diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown index 4ea6804cf7e1f..10079683604ce 100644 --- a/CHANGELOG.markdown +++ b/CHANGELOG.markdown @@ -1,3 +1,34 @@ +Update as of 9/05/2012 +====================== +* Implemented encryption of the credit card name and expiration date for the payment method "Credit Card (saved)" +* Implemented console utility `dev/tools/migration/get_aliases_map.php`, which generates map file "M1 class alias" to "M2 class name" +* Implemented automatic data upgrades for replacing "M1 class aliases" to "M2 class names" in a database +* Implemented recursive `chmod` in the library class `Varien_Io_File` +* Improved verbosity of the library class `Magento_Shell` +* Migrated client-side translation mechanism to jQuery +* Performance tests: + * Improved assertion for number of created orders for the checkout performance testing scenario + * Reverted the feature of specifying PHP scenarios to be executed before and after a JMeter scenario + * Implemented validation for the number of created orders as a part of the JMeter scenario + * Implemented the "Admin Login" user activity as a separate file to be reused in the performance testing scenarios + * Implemented fixture of 100k customers for the performance tests + * Implemented fixture of 100k products for the performance tests + * Enhanced module `Mage_ImportExport` in order to utilize it for the fixture implementation + * Implemented back-end performance testing scenario, which covers Dashboard, Manage Products, Manage Customers pages +* Fixes: + * Fixed Magento console installer to enable write permission recursively to the `var` directory + * Fixed performance tests to enable write permission recursively to the `var` directory + * Fixed integration test `Mage_Adminhtml_Model_System_Config_Source_Admin_PageTest::testToOptionArray` to not produce "Warning: DOMDocument::loadHTML(): htmlParseEntityRef: expecting ';' in Entity" in the developer mode +* GitHub requests: + * [#43](https://github.com/magento/magento2/pull/43) -- implemented logging of executed setup files + * [#44](https://github.com/magento/magento2/pull/44) + * Implemented support of writing logs into wrappers (for example, `php://output`) + * Enforced a log writer model to be an instance of `Zend_Log_Writer_Stream` + * [#49](https://github.com/magento/magento2/pull/49) + * Fixed sorting of totals according to "before" and "after" properties + * Introduced `Magento_Data_Graph` library class and utilized it for finding cycles in "before" and "after" declarations + * Implemented tests for totals sorting including the ambiguous cases + Update as of 8/30/2012 ====================== * Fixes: diff --git a/app/Mage.php b/app/Mage.php index 3c97bd4894f73..8692c76a04916 100644 --- a/app/Mage.php +++ b/app/Mage.php @@ -99,6 +99,13 @@ final class Mage */ static private $_isInstalled; + /** + * Logger entities + * + * @var array + */ + static private $_loggers = array(); + /** * Magento edition constants */ @@ -172,6 +179,7 @@ public static function reset() self::$_isDownloader = false; self::$_isDeveloperMode = false; self::$_isInstalled = null; + self::$_loggers = array(); // do not reset $headersSentThrowsException } @@ -761,49 +769,64 @@ public static function log($message, $level = null, $file = '', $forceLog = fals return; } - static $loggers = array(); - $level = is_null($level) ? Zend_Log::DEBUG : $level; $file = empty($file) ? 'system.log' : $file; try { - if (!isset($loggers[$file])) { - $logDir = self::getBaseDir('var') . DS . 'log'; - $logFile = $logDir . DS . $file; - - if (!is_dir($logDir)) { - mkdir($logDir); - chmod($logDir, 0777); - } - - if (!file_exists($logFile)) { - file_put_contents($logFile, ''); - chmod($logFile, 0777); - } + if (!isset(self::$_loggers[$file])) { + $logFile = self::_expandLogFileName($file); $format = '%timestamp% %priorityName% (%priority%): %message%' . PHP_EOL; $formatter = new Zend_Log_Formatter_Simple($format); $writerModel = (string)self::getConfig()->getNode('global/log/core/writer_model'); - if (!self::$_app || !$writerModel) { - $writer = new Zend_Log_Writer_Stream($logFile); - } - else { - $writer = new $writerModel($logFile); + if (!self::$_app || !$writerModel || !is_subclass_of($writerModel, 'Zend_Log_Writer_Stream')) { + $writerModel = 'Zend_Log_Writer_Stream'; } + /** @var $writer Zend_Log_Writer_Stream */ + $writer = new $writerModel($logFile); $writer->setFormatter($formatter); - $loggers[$file] = new Zend_Log($writer); + self::$_loggers[$file] = new Zend_Log($writer); } if (is_array($message) || is_object($message)) { $message = print_r($message, true); } - $loggers[$file]->log($message, $level); + self::$_loggers[$file]->log($message, $level); } catch (Exception $e) { } } + /** + * Expand log file name to absolute path, if necessary + * + * @param string $file + * @return string + */ + protected static function _expandLogFileName($file) + { + /* + * Check whether a file is a wrapper + * @link http://www.php.net/manual/en/wrappers.php + */ + if (preg_match('#^[a-z][a-z0-9+.-]*\://#i', $file)) { + return $file; + } + $dir = self::getBaseDir('var') . DIRECTORY_SEPARATOR . 'log'; + $file = $dir . DIRECTORY_SEPARATOR . $file; + if (!is_dir($dir)) { + mkdir($dir); + chmod($dir, 0777); + } + if (!file_exists($file)) { + file_put_contents($file, ''); + chmod($file, 0777); + } + return $file; + } + + /** * Write exception to log * diff --git a/app/code/community/Phoenix/Moneybookers/view/adminhtml/activation.js b/app/code/community/Phoenix/Moneybookers/view/adminhtml/activation.js index fc30ee9a748c5..d8d846dd17b63 100644 --- a/app/code/community/Phoenix/Moneybookers/view/adminhtml/activation.js +++ b/app/code/community/Phoenix/Moneybookers/view/adminhtml/activation.js @@ -74,13 +74,7 @@ Moneybookers.prototype = { }, translate: function(text) { - try { - if(Translator){ - return Translator.translate(text); - } - } - catch(e){} - return text; + return jQuery.mage.__ ? jQuery.mage.__(text) : text; }, button: function () { diff --git a/app/code/core/Mage/Adminhtml/Helper/Media/Js.php b/app/code/core/Mage/Adminhtml/Helper/Media/Js.php index 523da5baca425..3e113020605a7 100644 --- a/app/code/core/Mage/Adminhtml/Helper/Media/Js.php +++ b/app/code/core/Mage/Adminhtml/Helper/Media/Js.php @@ -60,11 +60,7 @@ public function __construct() */ public function getTranslatorScript() { - $script = 'if (typeof(Translator) == \'undefined\') {' - . ' var Translator = new Translate('.$this->getTranslateJson().');' - . '} else {' - . ' Translator.add('.$this->getTranslateJson().');' - . '}'; + $script = '(function($) {$.mage.translate.add(' . $this->getTranslateJson() . ')})(jQuery);'; return $this->getScript($script); } diff --git a/app/code/core/Mage/Adminhtml/view/adminhtml/dataflow.xml b/app/code/core/Mage/Adminhtml/view/adminhtml/dataflow.xml index 547d6fc91383b..af3560e7c60ec 100644 --- a/app/code/core/Mage/Adminhtml/view/adminhtml/dataflow.xml +++ b/app/code/core/Mage/Adminhtml/view/adminhtml/dataflow.xml @@ -32,6 +32,8 @@ + jquery/jquery-1.7.1.min.js + mage/jquery-no-conflict.js prototype/prototype.js prototype/validation.js varien/js.js @@ -46,6 +48,8 @@ + jquery/jquery-1.7.1.min.js + mage/jquery-no-conflict.js prototype/prototype.js prototype/validation.js varien/js.js diff --git a/app/code/core/Mage/Adminhtml/view/adminhtml/main.xml b/app/code/core/Mage/Adminhtml/view/adminhtml/main.xml index 8e7fae386d95d..0e9f0f4166c9e 100644 --- a/app/code/core/Mage/Adminhtml/view/adminhtml/main.xml +++ b/app/code/core/Mage/Adminhtml/view/adminhtml/main.xml @@ -55,6 +55,8 @@ Supported layout update handles (special): Magento Admin + jquery/jquery-1.7.1.min.js + mage/jquery-no-conflict.js prototype/prototype.js mage/adminhtml/fix-extjs-defer.jscan_load_ext_js mage/adminhtml/fix-extjs-defer-before.jscan_load_ext_js diff --git a/app/code/core/Mage/Adminhtml/view/adminhtml/page/js/translate.phtml b/app/code/core/Mage/Adminhtml/view/adminhtml/page/js/translate.phtml index 99ebce66f7eda..c2646736b468a 100644 --- a/app/code/core/Mage/Adminhtml/view/adminhtml/page/js/translate.phtml +++ b/app/code/core/Mage/Adminhtml/view/adminhtml/page/js/translate.phtml @@ -63,5 +63,7 @@ $_data = array( ); ?> diff --git a/app/code/core/Mage/Adminhtml/view/adminhtml/promo/rules.js b/app/code/core/Mage/Adminhtml/view/adminhtml/promo/rules.js index 1b1ee89e24bf2..5c2116b8ddc51 100644 --- a/app/code/core/Mage/Adminhtml/view/adminhtml/promo/rules.js +++ b/app/code/core/Mage/Adminhtml/view/adminhtml/promo/rules.js @@ -295,7 +295,7 @@ VarienRulesForm.prototype = { var new_type = elem.value; var new_elem = document.createElement('LI'); new_elem.className = 'rule-param-wait'; - new_elem.innerHTML = Translator.translate('Please wait, loading...'); + new_elem.innerHTML = jQuery.mage.__('Please wait, loading...'); children_ul.insertBefore(new_elem, $(elem).up('li')); new Ajax.Request(this.newChildUrl, { diff --git a/app/code/core/Mage/Adminhtml/view/adminhtml/sales/order/create/scripts.js b/app/code/core/Mage/Adminhtml/view/adminhtml/sales/order/create/scripts.js index 39a42fd63cc44..0853018e49e47 100644 --- a/app/code/core/Mage/Adminhtml/view/adminhtml/sales/order/create/scripts.js +++ b/app/code/core/Mage/Adminhtml/view/adminhtml/sales/order/create/scripts.js @@ -60,7 +60,7 @@ AdminOrder.prototype = { } }); - var searchButton = new ControlButton(Translator.translate('Add Products')), + var searchButton = new ControlButton(jQuery.mage.__('Add Products')), searchAreaId = this.getAreaId('search'); searchButton.onClick = function() { $(searchAreaId).show(); diff --git a/app/code/core/Mage/Catalog/data/catalog_setup/data-upgrade-1.6.0.0.16-1.6.0.0.17.php b/app/code/core/Mage/Catalog/data/catalog_setup/data-upgrade-1.6.0.0.16-1.6.0.0.17.php new file mode 100644 index 0000000000000..b70409fbc1c66 --- /dev/null +++ b/app/code/core/Mage/Catalog/data/catalog_setup/data-upgrade-1.6.0.0.16-1.6.0.0.17.php @@ -0,0 +1,56 @@ +startSetup(); + +$attributeData = $this->getAttribute('catalog_category', 'custom_layout_update'); +$installer->appendClassAliasReplace('catalog_category_entity_text', 'value', + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_BLOCK, + Mage_Core_Model_Resource_Setup_Migration::FIELD_CONTENT_TYPE_XML, + array('value_id'), + 'attribute_id = ' . (int) $attributeData['attribute_id'] +); + +$attributeData = $this->getAttribute('catalog_product', 'custom_layout_update'); +$installer->appendClassAliasReplace('catalog_product_entity_text', 'value', + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_BLOCK, + Mage_Core_Model_Resource_Setup_Migration::FIELD_CONTENT_TYPE_XML, + array('value_id'), + 'attribute_id = ' . (int) $attributeData['attribute_id'] +); + +$installer->appendClassAliasReplace('catalog_eav_attribute', 'frontend_input_renderer', + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_BLOCK, + Mage_Core_Model_Resource_Setup_Migration::FIELD_CONTENT_TYPE_PLAIN, + array('attribute_id') +); +$installer->doUpdateClassAliases(); + +$installer->endSetup(); diff --git a/app/code/core/Mage/Catalog/etc/config.xml b/app/code/core/Mage/Catalog/etc/config.xml index f40dceda2a3bd..a8eee2143c2c8 100644 --- a/app/code/core/Mage/Catalog/etc/config.xml +++ b/app/code/core/Mage/Catalog/etc/config.xml @@ -28,7 +28,7 @@ - 1.6.0.0.16 + 1.6.0.0.17 true core diff --git a/app/code/core/Mage/CatalogRule/data/catalogrule_setup/data-upgrade-1.6.0.3-1.6.0.4.php b/app/code/core/Mage/CatalogRule/data/catalogrule_setup/data-upgrade-1.6.0.3-1.6.0.4.php new file mode 100644 index 0000000000000..0782b76065638 --- /dev/null +++ b/app/code/core/Mage/CatalogRule/data/catalogrule_setup/data-upgrade-1.6.0.3-1.6.0.4.php @@ -0,0 +1,44 @@ +startSetup(); + +$installer->appendClassAliasReplace('catalogrule', 'conditions_serialized', + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_MODEL, + Mage_Core_Model_Resource_Setup_Migration::FIELD_CONTENT_TYPE_SERIALIZED, + array('rule_id') +); +$installer->appendClassAliasReplace('catalogrule', 'actions_serialized', + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_MODEL, + Mage_Core_Model_Resource_Setup_Migration::FIELD_CONTENT_TYPE_SERIALIZED, + array('rule_id') +); + +$installer->doUpdateClassAliases(); + +$installer->endSetup(); diff --git a/app/code/core/Mage/CatalogRule/etc/config.xml b/app/code/core/Mage/CatalogRule/etc/config.xml index 87cd5eece37e6..8efee64544c39 100644 --- a/app/code/core/Mage/CatalogRule/etc/config.xml +++ b/app/code/core/Mage/CatalogRule/etc/config.xml @@ -28,7 +28,7 @@ - 1.6.0.3 + 1.6.0.4 true core diff --git a/app/code/core/Mage/Checkout/view/frontend/opcheckout.js b/app/code/core/Mage/Checkout/view/frontend/opcheckout.js index 115db07a0ae2d..b51b9d2d6f32f 100644 --- a/app/code/core/Mage/Checkout/view/frontend/opcheckout.js +++ b/app/code/core/Mage/Checkout/view/frontend/opcheckout.js @@ -136,7 +136,7 @@ Checkout.prototype = { this.gotoSection('billing'); } else{ - alert(Translator.translate('Please choose to register or to checkout as a guest').stripTags()); + alert(jQuery.mage.__('Please choose to register or to checkout as a guest').stripTags()); return false; } document.body.fire('login:setMethod', {method : this.method}); @@ -545,7 +545,7 @@ ShippingMethod.prototype = { validate: function() { var methods = document.getElementsByName('shipping_method'); if (methods.length==0) { - alert(Translator.translate('Your order cannot be completed at this time as there is no shipping methods available for it. Please make necessary changes in your shipping address.').stripTags()); + alert(jQuery.mage.__('Your order cannot be completed at this time as there is no shipping methods available for it. Please make necessary changes in your shipping address.').stripTags()); return false; } @@ -558,7 +558,7 @@ ShippingMethod.prototype = { return true; } } - alert(Translator.translate('Please specify shipping method.').stripTags()); + alert(jQuery.mage.__('Please specify shipping method.').stripTags()); return false; }, @@ -732,7 +732,7 @@ Payment.prototype = { } var methods = document.getElementsByName('payment[method]'); if (methods.length==0) { - alert(Translator.translate('Your order cannot be completed at this time as there is no payment methods available for it.').stripTags()); + alert(jQuery.mage.__('Your order cannot be completed at this time as there is no payment methods available for it.').stripTags()); return false; } for (var i=0; istartSetup(); + +$installer->appendClassAliasReplace('cms_block', 'content', + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_BLOCK, + Mage_Core_Model_Resource_Setup_Migration::FIELD_CONTENT_TYPE_WIKI, + array('block_id') +); +$installer->appendClassAliasReplace('cms_page', 'content', + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_BLOCK, + Mage_Core_Model_Resource_Setup_Migration::FIELD_CONTENT_TYPE_WIKI, + array('page_id') +); +$installer->appendClassAliasReplace('cms_page', 'layout_update_xml', + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_BLOCK, + Mage_Core_Model_Resource_Setup_Migration::FIELD_CONTENT_TYPE_XML, + array('page_id') +); +$installer->appendClassAliasReplace('cms_page', 'custom_layout_update_xml', + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_BLOCK, + Mage_Core_Model_Resource_Setup_Migration::FIELD_CONTENT_TYPE_XML, + array('page_id') +); + +$installer->doUpdateClassAliases(); + +$installer->endSetup(); + diff --git a/app/code/core/Mage/Cms/etc/config.xml b/app/code/core/Mage/Cms/etc/config.xml index 65962657874d4..3bafe519599b4 100644 --- a/app/code/core/Mage/Cms/etc/config.xml +++ b/app/code/core/Mage/Cms/etc/config.xml @@ -28,7 +28,7 @@ - 1.6.0.0.2 + 1.6.0.0.3 true core diff --git a/app/code/core/Mage/Core/Helper/Js.php b/app/code/core/Mage/Core/Helper/Js.php index a73a1ad9ce510..65e6a0d6bfb9a 100644 --- a/app/code/core/Mage/Core/Helper/Js.php +++ b/app/code/core/Mage/Core/Helper/Js.php @@ -72,7 +72,7 @@ public function getTranslateJson() */ public function getTranslatorScript() { - $script = 'var Translator = new Translate('.$this->getTranslateJson().');'; + $script = '(function($) {$.mage.translate.add(' . $this->getTranslateJson() . ')})(jQuery);'; return $this->getScript($script); } diff --git a/app/code/core/Mage/Core/Model/Resource/Setup.php b/app/code/core/Mage/Core/Model/Resource/Setup.php index ad1cac9748017..6148924f36c37 100644 --- a/app/code/core/Mage/Core/Model/Resource/Setup.php +++ b/app/code/core/Mage/Core/Model/Resource/Setup.php @@ -640,10 +640,13 @@ protected function _modifyResourceDb($actionType, $fromVersion, $toVersion) if ($result) { $this->_setResourceVersion($actionType, $file['toVersion']); + Mage::log($fileName); + } else { + Mage::log("Failed resource setup: {$fileName}"); } } catch (Exception $e) { printf('
%s
', print_r($e, true)); - throw Mage::exception('Mage_Core', Mage::helper('Mage_Core_Helper_Data')->__('Error in file: "%s" - %s', $fileName, $e->getMessage())); + throw new Magento_Exception(sprintf('Error in file: "%s" - %s', $fileName, $e->getMessage()), 0, $e); } $version = $file['toVersion']; $this->getConnection()->allowDdlCache(); diff --git a/app/code/core/Mage/Core/Model/Resource/Setup/Migration.php b/app/code/core/Mage/Core/Model/Resource/Setup/Migration.php new file mode 100644 index 0000000000000..3e2211a89d06f --- /dev/null +++ b/app/code/core/Mage/Core/Model/Resource/Setup/Migration.php @@ -0,0 +1,760 @@ +[a-z]+[_a-z\d]*?\/[a-z]+[_a-z\d]*?)::.*?$/sui'; + const WIKI_FIND_PATTERN + = '/{{(block|widget).*?type=\"(?P[a-z]+[_a-z\d]*?\/[a-z]+[_a-z\d]*?)\".*?}}/sui'; + const XML_FIND_PATTERN = '/[a-z]+[_a-z\d]*?\/[a-z]+[_a-z\d]*?)\".*?>/sui'; + const SERIALIZED_FIND_PATTERN = '#(?Ps:\d+:"(?P[a-z]+[_a-z\d]*?/[a-z]+[_a-z\d]*?)")#sui'; + const SERIALIZED_REPLACE_PATTERN = 's:%d:"%s"'; + /**#@-*/ + + /** + * Config key for path to aliases map file + */ + const CONFIG_KEY_PATH_TO_MAP_FILE = 'global/migration/path_to_aliases_map_file'; + + /** + * List of possible entity types sorted by possibility of usage + * + * @var array + */ + protected $_entityTypes = array(self::ENTITY_TYPE_MODEL, self::ENTITY_TYPE_BLOCK, self::ENTITY_TYPE_RESOURCE); + + /** + * Rows per page. To split processing data from tables + * + * @var int + */ + protected $_rowsPerPage = 100; + + /** + * Replace rules for tables + * + * [table name] => array( + * [field name] => array( + * 'entity_type' => [entity type] + * 'content_type' => [content type] + * 'additional_where' => [additional where] + * ) + * ) + * + * @var array + */ + protected $_replaceRules = array(); + + /** + * Aliases to classes map + * + * [entity type] => array( + * [alias] => [class name] + * ) + * + * @var array + */ + protected $_aliasesMap; + + /** + * Replacement regexps for specified content types + * + * @var array + */ + protected $_replacePatterns = array( + self::FIELD_CONTENT_TYPE_WIKI => self::WIKI_FIND_PATTERN, + self::FIELD_CONTENT_TYPE_XML => self::XML_FIND_PATTERN, + ); + + /** + * @var Mage_Core_Helper_Data + */ + protected $_coreHelper; + + /** + * Application root absolute path + * + * @var string + */ + protected $_baseDir; + + /** + * Path to map file from config + * + * @var string + */ + protected $_pathToMapFile; + + /** + * List of composite module names + * + * @var array + */ + protected $_compositeModules; + + /** + * Constructor + * + * @param string $resourceName + * @param array $data + */ + public function __construct($resourceName, array $data = array()) + { + if (!isset($data['resource_config']) + || !isset($data['connection_config']) + || !isset($data['module_config']) + || !isset($data['connection']) + ) { + parent::__construct($resourceName); + } else { + $this->_resourceName = $resourceName; + + if (isset($data['connection'])) { + $this->_conn = $data['connection']; + } + + $this->_initConfigs($data); + } + + if (isset($data['autoload'])) { + $this->_autoload = $data['autoload']; + } else { + $this->_autoload = Magento_Autoload::getInstance(); + } + + if (isset($data['core_helper'])) { + $this->_coreHelper = $data['core_helper']; + } else { + $this->_coreHelper = Mage::helper('Mage_Core_Helper_Data'); + } + + if (isset($data['base_dir'])) { + $this->_baseDir = $data['base_dir']; + } else { + $this->_baseDir = Mage::getBaseDir(); + } + + $this->_initAliasesMapConfiguration($data); + } + + /** + * Init configs + * + * @param array $data + */ + protected function _initConfigs(array $data = array()) + { + if (isset($data['resource_config'])) { + $this->_resourceConfig = $data['resource_config']; + } + + if (isset($data['connection_config'])) { + $this->_connectionConfig = $data['connection_config']; + } + + if (isset($data['module_config'])) { + $this->_moduleConfig = $data['module_config']; + } + } + + /** + * Init aliases map configuration + * + * @param array $data + */ + protected function _initAliasesMapConfiguration(array $data = array()) + { + if (isset($data['path_to_map_file'])) { + $this->_pathToMapFile = $data['path_to_map_file']; + } else { + $this->_pathToMapFile = Mage::getConfig()->getNode(self::CONFIG_KEY_PATH_TO_MAP_FILE); + } + + if (isset($data['aliases_map'])) { + $this->_aliasesMap = $data['aliases_map']; + } + } + + /** + * Add alias replace rule + * + * @param string $tableName name of table to replace aliases in + * @param string $fieldName name of table column to replace aliases in + * @param string $entityType entity type of alias + * @param string $fieldContentType type of field content where class alias is used + * @param array $primaryKeyFields row pk field(s) to update by + * @param string $additionalWhere additional where condition + * @return void + */ + public function appendClassAliasReplace($tableName, $fieldName, $entityType = '', + $fieldContentType = self::FIELD_CONTENT_TYPE_PLAIN, array $primaryKeyFields = array(), $additionalWhere = '' + ) { + if (!isset($this->_replaceRules[$tableName])) { + $this->_replaceRules[$tableName] = array(); + } + + if (!isset($this->_replaceRules[$tableName][$fieldName])) { + $this->_replaceRules[$tableName][$fieldName] = array( + 'entity_type' => $entityType, + 'content_type' => $fieldContentType, + 'pk_fields' => $primaryKeyFields, + 'additional_where' => $additionalWhere, + ); + } + } + + /** + * Start process of replacing aliases with class names using rules + */ + public function doUpdateClassAliases() + { + foreach ($this->_replaceRules as $tableName => $tableRules) { + $this->_updateClassAliasesInTable($tableName, $tableRules); + } + } + + /** + * Update class aliases in table + * + * @param string $tableName name of table to replace aliases in + * @param array $tableRules replacing rules for table + */ + protected function _updateClassAliasesInTable($tableName, array $tableRules) + { + foreach ($tableRules as $fieldName => $fieldRule) { + $pagesCount = ceil( + $this->_getRowsCount($tableName, $fieldName, $fieldRule['additional_where']) / $this->_rowsPerPage + ); + + for ($page = 1; $page <= $pagesCount; $page++) { + $this->_applyFieldRule($tableName, $fieldName, $fieldRule, $page); + } + } + } + + /** + * Get amount of rows for table column which should be processed + * + * @param string $tableName name of table to replace aliases in + * @param string $fieldName name of table column to replace aliases in + * @param string $additionalWhere additional where condition + * + * @return int + */ + protected function _getRowsCount($tableName, $fieldName, $additionalWhere = '') + { + $adapter = $this->getConnection(); + + $query = $adapter->select() + ->from($adapter->getTableName($tableName), array('rows_count' => new Zend_Db_Expr('COUNT(*)'))) + ->where($fieldName . ' IS NOT NULL'); + + if (!empty($additionalWhere)) { + $query->where($additionalWhere); + } + + return (int) $adapter->fetchOne($query); + } + + /** + * Replace aliases with class names in rows + * + * @param string $tableName name of table to replace aliases in + * @param string $fieldName name of table column to replace aliases in + * @param array $fieldRule + * @param int $currentPage + */ + protected function _applyFieldRule($tableName, $fieldName, array $fieldRule, $currentPage = 0) + { + $fieldsToSelect = array($fieldName); + if (!empty($fieldRule['pk_fields'])) { + $fieldsToSelect = array_merge($fieldsToSelect, $fieldRule['pk_fields']); + } + $tableData = $this->_getTableData($tableName, $fieldName, $fieldsToSelect, $fieldRule['additional_where'], + $currentPage + ); + + $fieldReplacements = array(); + foreach ($tableData as $rowData) { + $replacement = $this->_getReplacement($rowData[$fieldName], $fieldRule['content_type'], + $fieldRule['entity_type'] + ); + if ($replacement !== $rowData[$fieldName]) { + $fieldReplacement = array( + 'to' => $replacement + ); + if (empty($fieldRule['pk_fields'])) { + $fieldReplacement['where_fields'] = array($fieldName => $rowData[$fieldName]); + } else { + $fieldReplacement['where_fields'] = array(); + foreach ($fieldRule['pk_fields'] as $pkField) { + $fieldReplacement['where_fields'][$pkField] = $rowData[$pkField]; + } + } + $fieldReplacements[] = $fieldReplacement; + } + } + + $this->_updateRowsData($tableName, $fieldName, $fieldReplacements); + } + + /** + * Update rows data in database + * + * @param string $tableName + * @param string $fieldName + * @param array $fieldReplacements + */ + protected function _updateRowsData($tableName, $fieldName, array $fieldReplacements) + { + if (count($fieldReplacements) > 0) { + $adapter = $this->getConnection(); + + foreach ($fieldReplacements as $fieldReplacement) { + $where = array(); + foreach ($fieldReplacement['where_fields'] as $whereFieldName => $value) { + $where[$adapter->quoteIdentifier($whereFieldName) . ' = ?'] = $value; + } + $adapter->update( + $adapter->getTableName($tableName), + array($fieldName => $fieldReplacement['to']), + $where + ); + } + } + } + + /** + * Get data for table column which should be processed + * + * @param string $tableName name of table to replace aliases in + * @param string $fieldName name of table column to replace aliases in + * @param array $fieldsToSelect array of fields to select + * @param string $additionalWhere additional where condition + * @param int $currPage + * + * @return array + */ + protected function _getTableData($tableName, $fieldName, array $fieldsToSelect, $additionalWhere = '', + $currPage = 0 + ) { + $adapter = $this->getConnection(); + + $query = $adapter->select() + ->from($adapter->getTableName($tableName), $fieldsToSelect) + ->where($fieldName . ' IS NOT NULL'); + + if (!empty($additionalWhere)) { + $query->where($additionalWhere); + } + + if ($currPage) { + $query->limitPage($currPage, $this->_rowsPerPage); + } + + return $adapter->fetchAll($query); + } + + /** + * Get data with replaced aliases with class names + * + * @param string $data + * @param string $contentType type of data (field content) + * @param string $entityType entity type of alias + * + * @return string + */ + protected function _getReplacement($data, $contentType, $entityType = '') + { + switch ($contentType) { + case self::FIELD_CONTENT_TYPE_SERIALIZED: + $data = $this->_getAliasInSerializedStringReplacement($data, $entityType); + break; + // wiki and xml content types use the same replacement method + case self::FIELD_CONTENT_TYPE_WIKI: + case self::FIELD_CONTENT_TYPE_XML: + $data = $this->_getPatternReplacement($data, $contentType, $entityType); + break; + case self::FIELD_CONTENT_TYPE_PLAIN: + default: + $data = $this->_getModelReplacement($data, $entityType); + break; + } + + return $data; + } + + /** + * Get appropriate class name for alias + * + * @param string $alias + * @param string $entityType entity type of alias + * + * @return string + */ + protected function _getCorrespondingClassName($alias, $entityType = '') + { + if ($this->_isFactoryName($alias)) { + if ($className = $this->_getAliasFromMap($alias, $entityType)) { + return $className; + } + + list($module, $name) = $this->_getModuleName($alias); + + if (!empty($entityType)) { + $className = $this->_getClassName($module, $entityType, $name); + $properEntityType = $entityType; + } else { + // Try to find appropriate class name for all entity types + $className = ''; + $properEntityType = ''; + foreach ($this->_entityTypes as $entityType) { + if (empty($className)) { + $className = $this->_getClassName($module, $entityType, $name); + $properEntityType = $entityType; + } else { + // If was found more than one match - alias cannot be replaced + return ''; + } + } + } + $this->_pushToMap($properEntityType, $alias, $className); + return $className; + } + + return ''; + } + + /** + * Replacement for model alias and model alias with method + * + * @param string $data + * @param string $entityType + * @return string + */ + protected function _getModelReplacement($data, $entityType = '') + { + if (preg_match(self::PLAIN_FIND_PATTERN, $data, $matches)) { + $classAlias = $matches['alias']; + $className = $this->_getCorrespondingClassName($classAlias, $entityType); + if ($className) { + return str_replace($classAlias, $className, $data); + } + } + + $className = $this->_getCorrespondingClassName($data, $entityType); + if (!empty($className)) { + return $className; + } else { + return $data; + } + } + + /** + * Replaces class aliases using pattern + * + * @param string $data + * @param string $contentType + * @param string $entityType + * @return string|null + */ + protected function _getPatternReplacement($data, $contentType, $entityType = '') + { + if (!array_key_exists($contentType, $this->_replacePatterns)) { + return null; + } + + $replacements = array(); + $pattern = $this->_replacePatterns[$contentType]; + preg_match_all($pattern, $data, $matches, PREG_PATTERN_ORDER); + if (isset($matches['alias'])) { + $matches = array_unique($matches['alias']); + foreach ($matches as $classAlias) { + $className = $this->_getCorrespondingClassName($classAlias, $entityType); + if ($className) { + $replacements[$classAlias] = $className; + } + } + } + + foreach ($replacements as $classAlias => $className) { + $data = str_replace($classAlias, $className, $data); + } + + return $data; + } + + /** + * Generate class name + * + * @param string $module + * @param string $type + * @param string $name + * + * @return string + */ + protected function _getClassName($module, $type, $name = null) + { + $className = implode('_', array_map('ucfirst', explode('_', $module . '_' . $type . '_' . $name))); + + if (Magento_Autoload::getInstance()->classExists($className)) { + return $className; + } + + return ''; + } + + /** + * Whether the given class name is a factory name + * + * @param string $factoryName + * + * @return bool + */ + protected function _isFactoryName($factoryName) + { + return false !== strpos($factoryName, '/') || preg_match('/^[a-z\d]+(_[A-Za-z\d]+)?$/', $factoryName); + } + + /** + * Transform factory name into a pair of module and name + * + * @param string $factoryName + * + * @return array + */ + protected function _getModuleName($factoryName) + { + if (false !== strpos($factoryName, '/')) { + list($module, $name) = explode('/', $factoryName); + } else { + $module = $factoryName; + $name = false; + } + $compositeModuleName = $this->_getCompositeModuleName($module); + if (null !== $compositeModuleName) { + $module = $compositeModuleName; + } elseif (false === strpos($module, '_')) { + $module = "Mage_{$module}"; + } + return array($module, $name); + } + + /** + * Get composite module name by module alias + * + * @param $moduleAlias + * + * @return string|null + */ + protected function _getCompositeModuleName($moduleAlias) + { + if (null === $this->_compositeModules) { + $this->_compositeModules = static::getCompositeModules(); + } + if (array_key_exists($moduleAlias, $this->_compositeModules)) { + return $this->_compositeModules[$moduleAlias]; + } + return null; + } + + /** + * Search class by alias in map + * + * @param string $alias + * @param string $entityType + * + * @return string + */ + protected function _getAliasFromMap($alias, $entityType = '') + { + if ($map = $this->_getAliasesMap()) { + if (!empty($entityType) && isset($map[$entityType]) && !empty($map[$entityType][$alias])) { + return $map[$entityType][$alias]; + } else { + $className = ''; + foreach ($this->_entityTypes as $entityType) { + if (empty($className)) { + if (isset($map[$entityType]) && !empty($map[$entityType][$alias])) { + $className = $map[$entityType][$alias]; + } + } else { + return ''; + } + } + return $className; + } + } + + return ''; + } + + /** + * Store already generated class name for alias + * + * @param $entityType + * @param $alias + * @param $className + */ + protected function _pushToMap($entityType, $alias, $className) + { + // Load map from file if it wasn't loaded + $this->_getAliasesMap(); + + if (!isset($this->_aliasesMap[$entityType])) { + $this->_aliasesMap[$entityType] = array(); + } + + if (!isset($this->_aliasesMap[$entityType][$alias])) { + $this->_aliasesMap[$entityType][$alias] = $className; + } + } + + /** + * Retrieve aliases to classes map if exit + * + * @return array + */ + protected function _getAliasesMap() + { + if (is_null($this->_aliasesMap)) { + $this->_aliasesMap = array(); + + $map = $this->_loadMap($this->_pathToMapFile); + + if (!empty($map)) { + $this->_aliasesMap = $this->_coreHelper->jsonDecode($map); + } + } + + return $this->_aliasesMap; + } + + /** + * Load aliases to classes map from file + * + * @param string $pathToMapFile + * + * @return string + */ + protected function _loadMap($pathToMapFile) + { + $pathToMapFile = $this->_baseDir . DS . $pathToMapFile; + if (file_exists($pathToMapFile)) { + return file_get_contents($pathToMapFile); + } + + return ''; + } + + /** + * @param string $data + * @param string $entityType + * @return mixed + */ + protected function _getAliasInSerializedStringReplacement($data, $entityType = '') + { + $matches = $this->_parseSerializedString($data); + if (isset($matches['alias']) && count($matches['alias']) > 0) { + foreach ($matches['alias'] as $key => $alias) { + $className = $this->_getCorrespondingClassName($alias, $entityType); + + if (!empty($className)) { + $replaceString = sprintf(self::SERIALIZED_REPLACE_PATTERN, strlen($className), $className); + $data = str_replace($matches['string'][$key], $replaceString, $data); + } + } + } + + return $data; + } + + /** + * Parse class aliases from serialized string + * + * @param $string + * @return array + */ + protected function _parseSerializedString($string) + { + if ($string && preg_match_all(self::SERIALIZED_FIND_PATTERN, $string, $matches)) { + unset($matches[0], $matches[1], $matches[2]); + return $matches; + } else { + return array(); + } + } + + /** + * List of correspondence between composite module aliases and module names + * + * @static + * @return array + */ + public static function getCompositeModules() + { + return array( + 'adminnotification' => 'Mage_AdminNotification', + 'catalogindex' => 'Mage_CatalogIndex', + 'cataloginventory' => 'Mage_CatalogInventory', + 'catalogrule' => 'Mage_CatalogRule', + 'catalogsearch' => 'Mage_CatalogSearch', + 'currencysymbol' => 'Mage_CurrencySymbol', + 'giftmessage' => 'Mage_GiftMessage', + 'googleanalytics' => 'Mage_GoogleAnalytics', + 'googlebase' => 'Mage_GoogleBase', + 'googlecheckout' => 'Mage_GoogleCheckout', + 'importexport' => 'Mage_ImportExport', + 'paypaluk' => 'Mage_PaypalUk', + 'productalert' => 'Mage_ProductAlert', + 'salesrule' => 'Mage_SalesRule', + 'xmlconnect' => 'Mage_XmlConnect', + ); + } +} diff --git a/app/code/core/Mage/Core/data/core_setup/data-upgrade-1.6.0.3-1.6.0.4.php b/app/code/core/Mage/Core/data/core_setup/data-upgrade-1.6.0.3-1.6.0.4.php new file mode 100644 index 0000000000000..36e88c5accd4a --- /dev/null +++ b/app/code/core/Mage/Core/data/core_setup/data-upgrade-1.6.0.3-1.6.0.4.php @@ -0,0 +1,43 @@ +startSetup(); + +$installer->appendClassAliasReplace('core_config_data', 'value', + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_MODEL, + Mage_Core_Model_Resource_Setup_Migration::FIELD_CONTENT_TYPE_PLAIN, + array('config_id') +); +$installer->appendClassAliasReplace('core_layout_update', 'xml', + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_BLOCK, + Mage_Core_Model_Resource_Setup_Migration::FIELD_CONTENT_TYPE_XML, + array('layout_update_id') +); +$installer->doUpdateClassAliases(); + +$installer->endSetup(); diff --git a/app/code/core/Mage/Core/etc/config.xml b/app/code/core/Mage/Core/etc/config.xml index ed66936f955ce..32ca68e3a69d3 100644 --- a/app/code/core/Mage/Core/etc/config.xml +++ b/app/code/core/Mage/Core/etc/config.xml @@ -28,7 +28,7 @@ - 1.6.0.3 + 1.6.0.4 true core @@ -95,6 +95,9 @@ 1 + + app/etc/aliases_to_classes_map.json + diff --git a/dev/tests/performance/framework/tests/unit/testsuite/Magento/_files/scenario_with_scripts_before.php b/app/code/core/Mage/Customer/data/customer_setup/data-upgrade-1.6.2.0.1-1.6.2.0.2.php similarity index 62% rename from dev/tests/performance/framework/tests/unit/testsuite/Magento/_files/scenario_with_scripts_before.php rename to app/code/core/Mage/Customer/data/customer_setup/data-upgrade-1.6.2.0.1-1.6.2.0.2.php index 4b190a120f85a..1829ab6adc0fd 100644 --- a/dev/tests/performance/framework/tests/unit/testsuite/Magento/_files/scenario_with_scripts_before.php +++ b/app/code/core/Mage/Customer/data/customer_setup/data-upgrade-1.6.2.0.1-1.6.2.0.2.php @@ -18,11 +18,21 @@ * versions in the future. If you wish to customize Magento for your * needs please refer to http://www.magentocommerce.com for more information. * - * @category Magento - * @package performance_tests - * @subpackage unit_tests + * @category Mage + * @package Mage_Customer * @copyright Copyright (c) 2012 Magento Inc. (http://www.magentocommerce.com) * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) */ -// Nothing here, just file existence is needed +/** @var $installer Mage_Core_Model_Resource_Setup_Migration */ +$installer = Mage::getResourceModel('Mage_Core_Model_Resource_Setup_Migration', 'core_setup'); +$installer->startSetup(); + +$installer->appendClassAliasReplace('customer_eav_attribute', 'data_model', + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_MODEL, + Mage_Core_Model_Resource_Setup_Migration::FIELD_CONTENT_TYPE_PLAIN, + array('attribute_id') +); +$installer->doUpdateClassAliases(); + +$installer->endSetup(); diff --git a/app/code/core/Mage/Customer/etc/config.xml b/app/code/core/Mage/Customer/etc/config.xml index d2dade8baf3f1..d7af852d5e073 100644 --- a/app/code/core/Mage/Customer/etc/config.xml +++ b/app/code/core/Mage/Customer/etc/config.xml @@ -28,7 +28,7 @@ - 1.6.2.0.1 + 1.6.2.0.2 true core diff --git a/app/code/core/Mage/DesignEditor/view/frontend/js/change/layout.js b/app/code/core/Mage/DesignEditor/view/frontend/js/change/layout.js index 05e9a986c5a7b..7c970f34d4387 100644 --- a/app/code/core/Mage/DesignEditor/view/frontend/js/change/layout.js +++ b/app/code/core/Mage/DesignEditor/view/frontend/js/change/layout.js @@ -41,7 +41,7 @@ status: 'active', getType: function() { if (!this.type) { - throw Error(Translator.translate('Type of change is not defined')); + throw Error($.mage.__('Type of change is not defined')); } return this.type; }, @@ -50,13 +50,13 @@ }, undo: function() { if (this.status == 'undone') { - throw Error(Translator.translate("Can't undo change twice")); + throw Error($.mage.__("Can't undo change twice")); } alert('undo'); this.status = 'undone'; }, getTitle: function() { - throw Error(Translator.translate('Method "getTitle" is not implemented')); + throw Error($.mage.__('Method "getTitle" is not implemented')); }, setData: function(data) { this.data = data; @@ -65,10 +65,10 @@ return this.data; }, getPostData: function() { - throw Error(Translator.translate('Method "getTitle" is not implemented')); + throw Error($.mage.__('Method "getTitle" is not implemented')); }, setActionData: function() { - throw Error(Translator.translate('Method "getTitle" is not implemented')); + throw Error($.mage.__('Method "getTitle" is not implemented')); } }; } @@ -106,13 +106,13 @@ case ACTION_MOVE: if (data.origin.container == data.destination.container) { - title = Translator.translate('Block #block# sorted').replace('#block#', data.block); + title = $.mage.__('Block #block# sorted').replace('#block#', data.block); } else { - title = Translator.translate('Block #block# moved').replace('#block#', data.block); + title = $.mage.__('Block #block# moved').replace('#block#', data.block); } break; case ACTION_REMOVE: - title = Translator.translate('Block #block# removed').replace('#block#', data.block); + title = $.mage.__('Block #block# removed').replace('#block#', data.block); break; } return title; @@ -126,7 +126,7 @@ return this[ '_' + type + this._stringToTitleCase(action) ](data); break; default: - throw Error(Translator.translate('Invalid action "#action#"').replace('#action#', action)); + throw Error($.mage.__('Invalid action "#action#"').replace('#action#', action)); } }, /** @todo maybe we need to create global object for strings? */ @@ -229,7 +229,7 @@ var change = new fileChange(); break; default: - throw Error(Translator.translate('Invalid change type "#type#"').replace('#type#', type)); + throw Error($.mage.__('Invalid change type "#type#"').replace('#type#', type)); } return $.extend(new abstractChange(), change); } @@ -311,7 +311,7 @@ }, error: function(data) { _isSaveLocked = false; - throw Error(Translator.translate('Some problem with save action')); + throw Error($.mage.__('Some problem with save action')); } }); } diff --git a/app/code/core/Mage/DesignEditor/view/frontend/js/design_editor.js b/app/code/core/Mage/DesignEditor/view/frontend/js/design_editor.js index 420a06274660e..1a9b275e0fdbb 100644 --- a/app/code/core/Mage/DesignEditor/view/frontend/js/design_editor.js +++ b/app/code/core/Mage/DesignEditor/view/frontend/js/design_editor.js @@ -239,7 +239,7 @@ try { if (this._history.getItems().length == 0) { /** @todo temporary report */ - alert(Translator.translate('No changes found.')); + alert($.mage.__('No changes found.')); return false; } var data = this._preparePostItems(this._history.getItems()); @@ -255,7 +255,7 @@ try { if (this._history.getItems().length == 0) { /** @todo temporary report */ - alert(Translator.translate('No changes found.')); + alert($.mage.__('No changes found.')); return false; } var data = this._preparePostItems(this._history.getItems()); @@ -314,13 +314,13 @@ success: function(data) { if (data.error) { /** @todo add error validator */ - throw Error(Translator.translate('Some problem with save action')); + throw Error($.mage.__('Some problem with save action')); return; } postResult = data.success; }, error: function(data) { - throw Error(Translator.translate('Some problem with save action')); + throw Error($.mage.__('Some problem with save action')); } }); return postResult; diff --git a/app/code/core/Mage/Eav/data/eav_setup/data-upgrade-1.6.0.0-1.6.0.1.php b/app/code/core/Mage/Eav/data/eav_setup/data-upgrade-1.6.0.0-1.6.0.1.php new file mode 100644 index 0000000000000..121778192c738 --- /dev/null +++ b/app/code/core/Mage/Eav/data/eav_setup/data-upgrade-1.6.0.0-1.6.0.1.php @@ -0,0 +1,75 @@ +startSetup(); + +$installer->appendClassAliasReplace('eav_attribute', 'attribute_model', + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_MODEL, + Mage_Core_Model_Resource_Setup_Migration::FIELD_CONTENT_TYPE_PLAIN, + array('attribute_id') +); +$installer->appendClassAliasReplace('eav_attribute', 'backend_model', + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_MODEL, + Mage_Core_Model_Resource_Setup_Migration::FIELD_CONTENT_TYPE_PLAIN, + array('attribute_id') +); +$installer->appendClassAliasReplace('eav_attribute', 'frontend_model', + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_MODEL, + Mage_Core_Model_Resource_Setup_Migration::FIELD_CONTENT_TYPE_PLAIN, + array('attribute_id') +); +$installer->appendClassAliasReplace('eav_attribute', 'source_model', + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_MODEL, + Mage_Core_Model_Resource_Setup_Migration::FIELD_CONTENT_TYPE_PLAIN, + array('attribute_id') +); + +$installer->appendClassAliasReplace('eav_entity_type', 'entity_model', + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_MODEL, + Mage_Core_Model_Resource_Setup_Migration::FIELD_CONTENT_TYPE_PLAIN, + array('entity_type_id') +); +$installer->appendClassAliasReplace('eav_entity_type', 'attribute_model', + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_MODEL, + Mage_Core_Model_Resource_Setup_Migration::FIELD_CONTENT_TYPE_PLAIN, + array('entity_type_id') +); +$installer->appendClassAliasReplace('eav_entity_type', 'increment_model', + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_MODEL, + Mage_Core_Model_Resource_Setup_Migration::FIELD_CONTENT_TYPE_PLAIN, + array('entity_type_id') +); +$installer->appendClassAliasReplace('eav_entity_type', 'entity_attribute_collection', + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_RESOURCE, + Mage_Core_Model_Resource_Setup_Migration::FIELD_CONTENT_TYPE_PLAIN, + array('entity_type_id') +); + +$installer->doUpdateClassAliases(); + +$installer->endSetup(); diff --git a/app/code/core/Mage/Eav/etc/config.xml b/app/code/core/Mage/Eav/etc/config.xml index 851f17790aac7..fa50047159295 100644 --- a/app/code/core/Mage/Eav/etc/config.xml +++ b/app/code/core/Mage/Eav/etc/config.xml @@ -28,7 +28,7 @@ - 1.6.0.0 + 1.6.0.1 true core diff --git a/app/code/core/Mage/ImportExport/Model/Import.php b/app/code/core/Mage/ImportExport/Model/Import.php index e5b9ab1cac686..9b27c4114e71d 100644 --- a/app/code/core/Mage/ImportExport/Model/Import.php +++ b/app/code/core/Mage/ImportExport/Model/Import.php @@ -480,15 +480,14 @@ public function uploadSource() /** * Validates source file and returns validation result. * - * @param string $sourceFile Full path to source file + * @param Mage_ImportExport_Model_Import_Adapter_Abstract $source * @return bool */ - public function validateSource($sourceFile) + public function validateSource(Mage_ImportExport_Model_Import_Adapter_Abstract $source) { $this->addLogComment(Mage::helper('Mage_ImportExport_Helper_Data')->__('Begin data validation')); - $result = $this->_getEntityAdapter() - ->setSource($this->_getSourceAdapter($sourceFile)) - ->isDataValid(); + $adapter = $this->_getEntityAdapter()->setSource($source); + $result = $adapter->isDataValid(); $messages = $this->getOperationResultMessages($result); $this->addLogComment($messages); diff --git a/app/code/core/Mage/ImportExport/Model/Import/Adapter/Abstract.php b/app/code/core/Mage/ImportExport/Model/Import/Adapter/Abstract.php index c17ef9f99cef8..aead1691e984b 100644 --- a/app/code/core/Mage/ImportExport/Model/Import/Adapter/Abstract.php +++ b/app/code/core/Mage/ImportExport/Model/Import/Adapter/Abstract.php @@ -75,7 +75,7 @@ abstract class Mage_ImportExport_Model_Import_Adapter_Abstract implements Seekab * @throws Mage_Core_Exception * @return void */ - final public function __construct($source) + public function __construct($source) { if (!is_string($source)) { Mage::throwException(Mage::helper('Mage_ImportExport_Helper_Data')->__('Source file path must be a string')); diff --git a/app/code/core/Mage/ImportExport/controllers/Adminhtml/ImportController.php b/app/code/core/Mage/ImportExport/controllers/Adminhtml/ImportController.php index 9e712c2bf804b..ccca6a36f623f 100644 --- a/app/code/core/Mage/ImportExport/controllers/Adminhtml/ImportController.php +++ b/app/code/core/Mage/ImportExport/controllers/Adminhtml/ImportController.php @@ -141,8 +141,9 @@ public function validateAction() try { /** @var $import Mage_ImportExport_Model_Import */ - $import = Mage::getModel('Mage_ImportExport_Model_Import'); - $validationResult = $import->validateSource($import->setData($data)->uploadSource()); + $import = Mage::getModel('Mage_ImportExport_Model_Import')->setData($data); + $source = Mage_ImportExport_Model_Import_Adapter::findAdapterFor($import->uploadSource()); + $validationResult = $import->validateSource($source); if (!$import->getProcessedRowsCount()) { $resultBlock->addError($this->__('File does not contain data. Please upload another one')); diff --git a/app/code/core/Mage/Install/Model/Installer/Console.php b/app/code/core/Mage/Install/Model/Installer/Console.php index 83500524c6303..5a18c56c1fbec 100644 --- a/app/code/core/Mage/Install/Model/Installer/Console.php +++ b/app/code/core/Mage/Install/Model/Installer/Console.php @@ -327,8 +327,7 @@ public function install(array $options) /** * Change directories mode to be writable by apache user */ - @chmod('var/cache', 0777); - @chmod('var/session', 0777); + Varien_Io_File::chmodRecursive(Mage::getBaseDir('var'), 0777); return $encryptionKey; diff --git a/app/code/core/Mage/Install/view/install/page.phtml b/app/code/core/Mage/Install/view/install/page.phtml index af921d7d9bc93..39f4c99f8446d 100644 --- a/app/code/core/Mage/Install/view/install/page.phtml +++ b/app/code/core/Mage/Install/view/install/page.phtml @@ -38,6 +38,8 @@ + + diff --git a/app/code/core/Mage/Newsletter/data/newsletter_setup/data-upgrade-1.6.0.1-1.6.0.2.php b/app/code/core/Mage/Newsletter/data/newsletter_setup/data-upgrade-1.6.0.1-1.6.0.2.php new file mode 100644 index 0000000000000..ab53fbadfcf5d --- /dev/null +++ b/app/code/core/Mage/Newsletter/data/newsletter_setup/data-upgrade-1.6.0.1-1.6.0.2.php @@ -0,0 +1,49 @@ +startSetup(); + +$installer->appendClassAliasReplace('newsletter_template', 'template_text', + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_BLOCK, + Mage_Core_Model_Resource_Setup_Migration::FIELD_CONTENT_TYPE_WIKI, + array('template_id') +); +$installer->appendClassAliasReplace('newsletter_template', 'template_text_preprocessed', + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_BLOCK, + Mage_Core_Model_Resource_Setup_Migration::FIELD_CONTENT_TYPE_WIKI, + array('template_id') +); +$installer->appendClassAliasReplace('newsletter_queue', 'newsletter_text', + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_BLOCK, + Mage_Core_Model_Resource_Setup_Migration::FIELD_CONTENT_TYPE_WIKI, + array('queue_id') +); + +$installer->doUpdateClassAliases(); + +$installer->endSetup(); diff --git a/app/code/core/Mage/Newsletter/etc/config.xml b/app/code/core/Mage/Newsletter/etc/config.xml index cdedff065444a..405c2ec4aeb01 100644 --- a/app/code/core/Mage/Newsletter/etc/config.xml +++ b/app/code/core/Mage/Newsletter/etc/config.xml @@ -28,7 +28,7 @@ - 1.6.0.1 + 1.6.0.2 true core @@ -176,4 +176,4 @@ - \ No newline at end of file + diff --git a/app/code/core/Mage/Page/view/frontend/layout.xml b/app/code/core/Mage/Page/view/frontend/layout.xml index 118c2ed1ebf46..79adf27eead1d 100644 --- a/app/code/core/Mage/Page/view/frontend/layout.xml +++ b/app/code/core/Mage/Page/view/frontend/layout.xml @@ -35,6 +35,8 @@ Default layout, loads most of the pages + jquery/jquery-1.7.1.min.js + mage/jquery-no-conflict.js prototype/prototype.js lib/ccard.js prototype/validation.js @@ -92,6 +94,8 @@ Default layout, loads most of the pages + jquery/jquery-1.7.1.min.js + mage/jquery-no-conflict.js prototype/prototype.js mage/translate.js lib/ccard.js diff --git a/app/code/core/Mage/Payment/Model/Info.php b/app/code/core/Mage/Payment/Model/Info.php index 9e5ee6a9d4647..fd23dc398da6c 100644 --- a/app/code/core/Mage/Payment/Model/Info.php +++ b/app/code/core/Mage/Payment/Model/Info.php @@ -45,7 +45,7 @@ class Mage_Payment_Model_Info extends Mage_Core_Model_Abstract * * @param string $key * @param mixed $index - * @return unknown + * @return mixed */ public function getData($key='', $index=null) { diff --git a/app/code/core/Mage/Payment/etc/config.xml b/app/code/core/Mage/Payment/etc/config.xml index 25a5e52533dde..ee1c3c02d1429 100644 --- a/app/code/core/Mage/Payment/etc/config.xml +++ b/app/code/core/Mage/Payment/etc/config.xml @@ -153,7 +153,7 @@ - 1 + 0 AE,VI,MC,DI Mage_Payment_Model_Method_Ccsave pending diff --git a/app/code/core/Mage/Payment/etc/system.xml b/app/code/core/Mage/Payment/etc/system.xml index cba75207e7652..008e6d18b92ad 100644 --- a/app/code/core/Mage/Payment/etc/system.xml +++ b/app/code/core/Mage/Payment/etc/system.xml @@ -45,8 +45,9 @@ 1 1 - + + Enabling this method will store credit card information in the database, which may increase your security or compliance requirements. select Mage_Adminhtml_Model_System_Config_Source_Yesno 1 diff --git a/app/code/core/Mage/Sales/Model/Config/Ordered.php b/app/code/core/Mage/Sales/Model/Config/Ordered.php index bd89d332946a8..3f9a0e9f48ace 100644 --- a/app/code/core/Mage/Sales/Model/Config/Ordered.php +++ b/app/code/core/Mage/Sales/Model/Config/Ordered.php @@ -124,59 +124,56 @@ protected function _prepareConfigArray($code, $totalConfig) /** * Aggregate before/after information from all items and sort totals based on this data * + * @param array $config * @return array */ - protected function _getSortedCollectorCodes() + protected function _getSortedCollectorCodes(array $config) { - if (Mage::app()->useCache('config')) { - $cachedData = Mage::app()->loadCache($this->_collectorsCacheKey); - if ($cachedData) { - return unserialize($cachedData); - } - } - $configArray = $this->_modelsConfig; // invoke simple sorting if the first element contains the "sort_order" key - reset($configArray); - $element = current($configArray); + reset($config); + $element = current($config); if (isset($element['sort_order'])) { - uasort($configArray, array($this, '_compareSortOrder')); + uasort($config, array($this, '_compareSortOrder')); + $result = array_keys($config); } else { - foreach ($configArray as $code => $data) { - foreach ($data['before'] as $beforeCode) { - if (!isset($configArray[$beforeCode])) { + $result = array_keys($config); + // Move all totals with before specification in front of related total + foreach ($config as $code => &$data) { + foreach ($data['before'] as $positionCode) { + if (!isset($config[$positionCode])) { continue; } - $configArray[$code]['before'] = array_unique(array_merge( - $configArray[$code]['before'], $configArray[$beforeCode]['before'] - )); - $configArray[$beforeCode]['after'] = array_merge( - $configArray[$beforeCode]['after'], array($code), $data['after'] - ); - $configArray[$beforeCode]['after'] = array_unique($configArray[$beforeCode]['after']); - } - foreach ($data['after'] as $afterCode) { - if (!isset($configArray[$afterCode])) { - continue; + if (!in_array($code, $config[$positionCode]['after'], true)) { + // Also add additional after condition for related total, + // to keep it always after total with before value specified + $config[$positionCode]['after'][] = $code; + } + $currentPosition = array_search($code, $result, true); + $desiredPosition = array_search($positionCode, $result, true); + if ($currentPosition > $desiredPosition) { + // Only if current position is not corresponding to before condition + array_splice($result, $currentPosition, 1); // Removes existent + array_splice($result, $desiredPosition, 0, $code); // Add at new position } - $configArray[$code]['after'] = array_unique(array_merge( - $configArray[$code]['after'], $configArray[$afterCode]['after'] - )); - $configArray[$afterCode]['before'] = array_merge( - $configArray[$afterCode]['before'], array($code), $data['before'] - ); - $configArray[$afterCode]['before'] = array_unique($configArray[$afterCode]['before']); } } - uasort($configArray, array($this, '_compareTotals')); - } - $sortedCollectors = array_keys($configArray); - if (Mage::app()->useCache('config')) { - Mage::app()->saveCache(serialize($sortedCollectors), $this->_collectorsCacheKey, array( - Mage_Core_Model_Config::CACHE_TAG - ) - ); + // Sort out totals with after position specified + foreach ($config as $code => &$data) { + $maxAfter = null; + $currentPosition = array_search($code, $result, true); + + foreach ($data['after'] as $positionCode) { + $maxAfter = max($maxAfter, array_search($positionCode, $result, true)); + } + + if ($maxAfter !== null && $maxAfter > $currentPosition) { + // Moves only if it is in front of after total + array_splice($result, $maxAfter + 1, 0, $code); // Add at new position + array_splice($result, $currentPosition, 1); // Removes existent + } + } } - return $sortedCollectors; + return $result; } /** @@ -187,7 +184,27 @@ protected function _getSortedCollectorCodes() */ protected function _initCollectors() { - $sortedCodes = $this->_getSortedCollectorCodes(); + $useCache = Mage::app()->useCache('config'); + $sortedCodes = array(); + if ($useCache) { + $cachedData = Mage::app()->loadCache($this->_collectorsCacheKey); + if ($cachedData) { + $sortedCodes = unserialize($cachedData); + } + } + if (!$sortedCodes) { + try { + self::validateCollectorDeclarations($this->_modelsConfig); + } catch (Exception $e) { + Mage::logException($e); + } + $sortedCodes = $this->_getSortedCollectorCodes($this->_modelsConfig); + if ($useCache) { + Mage::app()->saveCache(serialize($sortedCodes), $this->_collectorsCacheKey, array( + Mage_Core_Model_Config::CACHE_TAG + )); + } + } foreach ($sortedCodes as $code) { $this->_collectors[$code] = $this->_models[$code]; } @@ -195,27 +212,6 @@ protected function _initCollectors() return $this; } - /** - * Callback that uses after/before for comparison - * - * @param array $a - * @param array $b - * @return int - */ - protected function _compareTotals($a, $b) - { - $aCode = $a['_code']; - $bCode = $b['_code']; - if (in_array($aCode, $b['after']) || in_array($bCode, $a['before'])) { - $res = -1; - } elseif (in_array($bCode, $a['after']) || in_array($aCode, $b['before'])) { - $res = 1; - } else { - $res = 0; - } - return $res; - } - /** * Callback that uses sort_order for comparison * @@ -237,4 +233,50 @@ protected function _compareSortOrder($a, $b) } return $res; } + + /** + * Validate specified configuration array as sales totals declaration + * + * If there are contradictions, the totals cannot be sorted correctly. Possible contradictions: + * - A relation between totals leads to cycles + * - Two relations combined lead to cycles + * + * @param array $config + * @throws Magento_Exception + */ + public static function validateCollectorDeclarations($config) + { + $before = self::_instantiateGraph($config, 'before'); + $after = self::_instantiateGraph($config, 'after'); + foreach ($after->getRelations(Magento_Data_Graph::INVERSE) as $from => $relations) { + foreach ($relations as $to) { + $before->addRelation($from, $to); + } + } + $cycle = $before->findCycle(); + if ($cycle) { + throw new Magento_Exception(sprintf( + 'Found cycle in sales total declarations: %s', implode(' -> ', $cycle) + )); + } + } + + /** + * Parse "config" array by specified key and instantiate a graph based on that + * + * @param array $config + * @param string $key + * @return Magento_Data_Graph + */ + private static function _instantiateGraph($config, $key) + { + $nodes = array_keys($config); + $graph = array(); + foreach ($config as $from => $row) { + foreach ($row[$key] as $to) { + $graph[] = array($from, $to); + } + } + return new Magento_Data_Graph($nodes, $graph); + } } diff --git a/app/code/core/Mage/Sales/Model/ConverterInterface.php b/app/code/core/Mage/Sales/Model/ConverterInterface.php new file mode 100644 index 0000000000000..fba249e13d8d6 --- /dev/null +++ b/app/code/core/Mage/Sales/Model/ConverterInterface.php @@ -0,0 +1,54 @@ + + */ +interface Mage_Sales_Model_ConverterInterface +{ + /** + * Decode data + * + * @param Mage_Core_Model_Abstract $object + * @param $filedName + * @return mixed + */ + public function decode(Mage_Core_Model_Abstract $object, $filedName); + + /** + * Encode data + * + * @param Mage_Core_Model_Abstract $object + * @param $filedName + * @return mixed + */ + public function encode(Mage_Core_Model_Abstract $object, $filedName); +} diff --git a/app/code/core/Mage/Sales/Model/Payment/Method/Converter.php b/app/code/core/Mage/Sales/Model/Payment/Method/Converter.php new file mode 100644 index 0000000000000..52bc7e008d32c --- /dev/null +++ b/app/code/core/Mage/Sales/Model/Payment/Method/Converter.php @@ -0,0 +1,111 @@ + + */ +class Mage_Sales_Model_Payment_Method_Converter +{ + /** + * List of fields that has to be encrypted + * Format: method_name => array(field1, field2, ... ) + * + * @var array + */ + protected $_encryptFields = array( + 'ccsave' => array( + 'cc_owner' => true, + 'cc_exp_year' => true, + 'cc_exp_month' => true, + ), + ); + + /** + * @var Mage_Core_Helper_Data + */ + protected $_encryptor; + + public function __construct(array $data = array()) + { + $this->_encryptor = isset($data['encryptor']) ? $data['encryptor'] : Mage::helper('Mage_Core_Helper_Data'); + } + + /** + * Check if specified field is encrypted + * + * @param Mage_Core_Model_Abstract $object + * @param string $filedName + * @return bool + */ + protected function _shouldBeEncrypted(Mage_Core_Model_Abstract $object, $filedName) + { + $method = $object->getData('method'); + return isset($this->_encryptFields[$method][$filedName]) && + $this->_encryptFields[$method][$filedName]; + } + + + /** + * Decode data + * + * @param Mage_Core_Model_Abstract $object + * @param string $filedName + * @return mixed + */ + public function decode(Mage_Core_Model_Abstract $object, $filedName) + { + $value = $object->getData($filedName); + + if ($this->_shouldBeEncrypted($object, $filedName)) { + $value = $this->_encryptor->decrypt($value); + } + + return $value; + } + + /** + * Encode data + * + * @param Mage_Core_Model_Abstract $object + * @param string $filedName + * @return mixed + */ + public function encode(Mage_Core_Model_Abstract $object, $filedName) + { + $value = $object->getData($filedName); + + if ($this->_shouldBeEncrypted($object, $filedName)) { + $value = $this->_encryptor->encrypt($value); + } + + return $value; + } +} diff --git a/app/code/core/Mage/Sales/Model/Quote/Payment.php b/app/code/core/Mage/Sales/Model/Quote/Payment.php index cfb9666fc0779..db530322a34e6 100644 --- a/app/code/core/Mage/Sales/Model/Quote/Payment.php +++ b/app/code/core/Mage/Sales/Model/Quote/Payment.php @@ -45,12 +45,6 @@ * @method Mage_Sales_Model_Quote_Payment setCcLast4(string $value) * @method string getCcCidEnc() * @method Mage_Sales_Model_Quote_Payment setCcCidEnc(string $value) - * @method string getCcOwner() - * @method Mage_Sales_Model_Quote_Payment setCcOwner(string $value) - * @method int getCcExpMonth() - * @method Mage_Sales_Model_Quote_Payment setCcExpMonth(int $value) - * @method int getCcExpYear() - * @method Mage_Sales_Model_Quote_Payment setCcExpYear(int $value) * @method string getCcSsOwner() * @method Mage_Sales_Model_Quote_Payment setCcSsOwner(string $value) * @method int getCcSsStartMonth() diff --git a/app/code/core/Mage/Sales/Model/Resource/Abstract.php b/app/code/core/Mage/Sales/Model/Resource/Abstract.php index 92f0533ea4756..c14f1712769f0 100755 --- a/app/code/core/Mage/Sales/Model/Resource/Abstract.php +++ b/app/code/core/Mage/Sales/Model/Resource/Abstract.php @@ -34,6 +34,13 @@ */ abstract class Mage_Sales_Model_Resource_Abstract extends Mage_Core_Model_Resource_Db_Abstract { + /** + * Data converter object + * + * @var Mage_Sales_Model_ConverterInterface + */ + protected $_converter = null; + /** * Prepare data for save * @@ -50,4 +57,65 @@ protected function _prepareDataForSave(Mage_Core_Model_Abstract $object) $data = parent::_prepareDataForSave($object); return $data; } + + /** + * Check if current model data should be converted + * + * @return bool + */ + protected function _shouldBeConverted() + { + return (null !== $this->_converter); + } + + + /** + * Perform actions before object save + * + * @param Mage_Core_Model_Abstract $object + * @return Mage_Sales_Model_Resource_Abstract + */ + protected function _beforeSave(Mage_Core_Model_Abstract $object) + { + parent::_beforeSave($object); + + if (true == $this->_shouldBeConverted()) { + foreach($object->getData() as $fieldName => $fieldValue) { + $object->setData($fieldName, $this->_converter->encode($object, $fieldName)); + } + } + return $this; + } + + /** + * Perform actions after object save + * + * @param Mage_Core_Model_Abstract $object + * @return Mage_Sales_Model_Resource_Abstract + */ + protected function _afterSave(Mage_Core_Model_Abstract $object) + { + if (true == $this->_shouldBeConverted()) { + foreach($object->getData() as $fieldName => $fieldValue) { + $object->setData($fieldName, $this->_converter->decode($object, $fieldName)); + } + } + return parent::_afterSave($object); + } + + /** + * Perform actions after object load + * + * @param Mage_Core_Model_Abstract $object + * @return Mage_Sales_Model_Resource_Abstract + */ + protected function _afterLoad(Mage_Core_Model_Abstract $object) + { + if (true == $this->_shouldBeConverted()) { + foreach($object->getData() as $fieldName => $fieldValue) { + $object->setData($fieldName, $this->_converter->decode($object, $fieldName)); + } + } + return parent::_afterLoad($object); + } } diff --git a/app/code/core/Mage/Sales/Model/Resource/Order/Payment.php b/app/code/core/Mage/Sales/Model/Resource/Order/Payment.php index afe2144ae7540..b4e33f2413661 100755 --- a/app/code/core/Mage/Sales/Model/Resource/Order/Payment.php +++ b/app/code/core/Mage/Sales/Model/Resource/Order/Payment.php @@ -56,6 +56,7 @@ class Mage_Sales_Model_Resource_Order_Payment extends Mage_Sales_Model_Resource_ */ protected function _construct() { + $this->_converter = Mage::getSingleton('Mage_Sales_Model_Payment_Method_Converter'); $this->_init('sales_flat_order_payment', 'entity_id'); } } diff --git a/app/code/core/Mage/Sales/Model/Resource/Order/Payment/Collection.php b/app/code/core/Mage/Sales/Model/Resource/Order/Payment/Collection.php index cec4dda9ddd48..4828348fb2905 100755 --- a/app/code/core/Mage/Sales/Model/Resource/Order/Payment/Collection.php +++ b/app/code/core/Mage/Sales/Model/Resource/Order/Payment/Collection.php @@ -67,6 +67,16 @@ protected function _afterLoad() foreach ($this->_items as $item) { $this->getResource()->unserializeFields($item); } + + /** @var Mage_Sales_Model_Order_Payment $item */ + foreach ($this->_items as $item) { + foreach ($item->getData() as $fieldName => $fieldValue) { + $item->setData($fieldName, + Mage::getSingleton('Mage_Sales_Model_Payment_Method_Converter')->decode($item, $fieldName) + ); + } + } + return parent::_afterLoad(); } } diff --git a/app/code/core/Mage/Sales/Model/Resource/Quote/Payment.php b/app/code/core/Mage/Sales/Model/Resource/Quote/Payment.php index c741ef65bc8f9..b3c5b7b57d719 100755 --- a/app/code/core/Mage/Sales/Model/Resource/Quote/Payment.php +++ b/app/code/core/Mage/Sales/Model/Resource/Quote/Payment.php @@ -49,6 +49,7 @@ class Mage_Sales_Model_Resource_Quote_Payment extends Mage_Sales_Model_Resource_ */ protected function _construct() { + $this->_converter = Mage::getSingleton('Mage_Sales_Model_Payment_Method_Converter'); $this->_init('sales_flat_quote_payment', 'payment_id'); } } diff --git a/app/code/core/Mage/Sales/Model/Resource/Quote/Payment/Collection.php b/app/code/core/Mage/Sales/Model/Resource/Quote/Payment/Collection.php index 2b2229db0549f..bf253f111d0a4 100755 --- a/app/code/core/Mage/Sales/Model/Resource/Quote/Payment/Collection.php +++ b/app/code/core/Mage/Sales/Model/Resource/Quote/Payment/Collection.php @@ -64,6 +64,16 @@ protected function _afterLoad() foreach ($this->_items as $item) { $this->getResource()->unserializeFields($item); } + + /** @var Mage_Sales_Model_Quote_Payment $item */ + foreach ($this->_items as $item) { + foreach ($item->getData() as $fieldName => $fieldValue) { + $item->setData($fieldName, + Mage::getSingleton('Mage_Sales_Model_Payment_Method_Converter')->decode($item, $fieldName) + ); + } + } + return parent::_afterLoad(); } } diff --git a/app/code/core/Mage/Sales/data/sales_setup/data-upgrade-1.6.0.8-1.6.0.9.php b/app/code/core/Mage/Sales/data/sales_setup/data-upgrade-1.6.0.8-1.6.0.9.php new file mode 100644 index 0000000000000..bbabdc3f8d9f3 --- /dev/null +++ b/app/code/core/Mage/Sales/data/sales_setup/data-upgrade-1.6.0.8-1.6.0.9.php @@ -0,0 +1,95 @@ +startSetup(); +$itemsPerPage = 1000; +$currentPosition = 0; + +/** Update sales order payment */ +do { + $select = $installer->getConnection() + ->select() + ->from( + $installer->getTable('sales_flat_order_payment'), + array('entity_id', 'cc_owner', 'cc_exp_month', 'cc_exp_year', 'method') + ) + ->where('method = ?', 'ccsave') + ->limit($itemsPerPage, $currentPosition); + + $orders = $select->query()->fetchAll(); + $currentPosition += $itemsPerPage; + + foreach ($orders as $order) { + $installer->getConnection() + ->update( + $installer->getTable('sales_flat_order_payment'), + array( + 'cc_exp_month' => $converter->encrypt($order['cc_exp_month']), + 'cc_exp_year' => $converter->encrypt($order['cc_exp_year']), + 'cc_owner' => $converter->encrypt($order['cc_owner']), + ), + array('entity_id = ?' => $order['entity_id']) + ); + } + +} while (count($orders) > 0); + +/** Update sales quote payment */ +$currentPosition = 0; +do { + $select = $installer->getConnection() + ->select() + ->from( + $installer->getTable('sales_flat_quote_payment'), + array('payment_id', 'cc_owner', 'cc_exp_month', 'cc_exp_year', 'method') + ) + ->where('method = ?', 'ccsave') + ->limit($itemsPerPage, $currentPosition); + + $quotes = $select->query()->fetchAll(); + $currentPosition += $itemsPerPage; + + foreach ($quotes as $quote) { + $installer->getConnection() + ->update( + $installer->getTable('sales_flat_quote_payment'), + array( + 'cc_exp_month' => $converter->encrypt($quote['cc_exp_month']), + 'cc_exp_year' => $converter->encrypt($quote['cc_exp_year']), + 'cc_owner' => $converter->encrypt($quote['cc_owner']), + ), + array('payment_id = ?' => $quote['payment_id']) + ); + } + +} while (count($quotes) > 0); + +$installer->endSetup(); diff --git a/app/code/core/Mage/Sales/etc/config.xml b/app/code/core/Mage/Sales/etc/config.xml index 16dc991b2a777..b0d62cf803580 100644 --- a/app/code/core/Mage/Sales/etc/config.xml +++ b/app/code/core/Mage/Sales/etc/config.xml @@ -28,7 +28,7 @@ - 1.6.0.8 + 1.6.0.9 true core diff --git a/app/code/core/Mage/Sales/sql/sales_setup/upgrade-1.6.0.8-1.6.0.9.php b/app/code/core/Mage/Sales/sql/sales_setup/upgrade-1.6.0.8-1.6.0.9.php new file mode 100644 index 0000000000000..acd65290334ad --- /dev/null +++ b/app/code/core/Mage/Sales/sql/sales_setup/upgrade-1.6.0.8-1.6.0.9.php @@ -0,0 +1,50 @@ +startSetup(); + +$installer->getConnection() + ->modifyColumn($installer->getTable('sales_flat_quote_payment'), 'cc_exp_year', + array( + 'type' => Varien_Db_Ddl_Table::TYPE_TEXT, + 'length' => 255, + 'nullable' => true, + 'default' => null, + 'comment' => 'Cc Exp Year' + ) + )->modifyColumn($installer->getTable('sales_flat_quote_payment'), 'cc_exp_month', + array( + 'type' => Varien_Db_Ddl_Table::TYPE_TEXT, + 'length' => 255, + 'nullable' => true, + 'default' => null, + 'comment' => 'Cc Exp Month' + ) + ); + +$installer->endSetup(); diff --git a/app/code/core/Mage/SalesRule/data/salesrule_setup/data-upgrade-1.6.0.3-1.6.0.4.php b/app/code/core/Mage/SalesRule/data/salesrule_setup/data-upgrade-1.6.0.3-1.6.0.4.php new file mode 100644 index 0000000000000..710278c4c36c7 --- /dev/null +++ b/app/code/core/Mage/SalesRule/data/salesrule_setup/data-upgrade-1.6.0.3-1.6.0.4.php @@ -0,0 +1,44 @@ +startSetup(); + +$installer->appendClassAliasReplace('salesrule', 'conditions_serialized', + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_MODEL, + Mage_Core_Model_Resource_Setup_Migration::FIELD_CONTENT_TYPE_SERIALIZED, + array('rule_id') +); +$installer->appendClassAliasReplace('salesrule', 'actions_serialized', + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_MODEL, + Mage_Core_Model_Resource_Setup_Migration::FIELD_CONTENT_TYPE_SERIALIZED, + array('rule_id') +); + +$installer->doUpdateClassAliases(); + +$installer->endSetup(); diff --git a/app/code/core/Mage/SalesRule/etc/config.xml b/app/code/core/Mage/SalesRule/etc/config.xml index 27f01f89d520c..b277f02bb2ef1 100644 --- a/app/code/core/Mage/SalesRule/etc/config.xml +++ b/app/code/core/Mage/SalesRule/etc/config.xml @@ -28,7 +28,7 @@ - 1.6.0.3 + 1.6.0.4 true core diff --git a/dev/tests/performance/testsuite/checkout_before.php b/app/code/core/Mage/Widget/data/widget_setup/data-upgrade-1.6.0.0-1.6.0.1.php similarity index 63% rename from dev/tests/performance/testsuite/checkout_before.php rename to app/code/core/Mage/Widget/data/widget_setup/data-upgrade-1.6.0.0-1.6.0.1.php index 5fa720081381f..1fa45b2bdb12f 100644 --- a/dev/tests/performance/testsuite/checkout_before.php +++ b/app/code/core/Mage/Widget/data/widget_setup/data-upgrade-1.6.0.0-1.6.0.1.php @@ -1,7 +1,5 @@ getSize(), PHP_EOL; +/** @var $installer Mage_Core_Model_Resource_Setup_Migration */ +$installer = Mage::getResourceModel('Mage_Core_Model_Resource_Setup_Migration', 'core_setup'); +$installer->startSetup(); + +$installer->appendClassAliasReplace('widget_instance', 'instance_type', + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_BLOCK, + Mage_Core_Model_Resource_Setup_Migration::FIELD_CONTENT_TYPE_PLAIN, + array('instance_id') +); +$installer->doUpdateClassAliases(); + +$installer->endSetup(); diff --git a/app/code/core/Mage/Widget/etc/config.xml b/app/code/core/Mage/Widget/etc/config.xml index 829311687fef8..360e7e7a7bf48 100644 --- a/app/code/core/Mage/Widget/etc/config.xml +++ b/app/code/core/Mage/Widget/etc/config.xml @@ -28,7 +28,7 @@ - 1.6.0.0 + 1.6.0.1 true core diff --git a/app/design/frontend/default/iphone/Mage_Page/layout.xml b/app/design/frontend/default/iphone/Mage_Page/layout.xml index ec8b066c308d1..73330be3b1c0a 100644 --- a/app/design/frontend/default/iphone/Mage_Page/layout.xml +++ b/app/design/frontend/default/iphone/Mage_Page/layout.xml @@ -34,6 +34,8 @@ Default layout, loads most of the pages + jquery/jquery-1.7.1.min.js + mage/jquery-no-conflict.js prototype/prototype.js lib/ccard.js prototype/validation.js @@ -95,6 +97,8 @@ Default layout, loads most of the pages + jquery/jquery-1.7.1.min.js + mage/jquery-no-conflict.js diff --git a/app/design/frontend/default/modern/Mage_Page/layout.xml b/app/design/frontend/default/modern/Mage_Page/layout.xml index 0adf9add7d3aa..c2b2709fed00e 100644 --- a/app/design/frontend/default/modern/Mage_Page/layout.xml +++ b/app/design/frontend/default/modern/Mage_Page/layout.xml @@ -35,6 +35,8 @@ Default layout, loads most of the pages + jquery/jquery-1.7.1.min.js + mage/jquery-no-conflict.js prototype/prototype.js lib/ccard.js prototype/validation.js @@ -91,6 +93,8 @@ Default layout, loads most of the pages + jquery/jquery-1.7.1.min.js + mage/jquery-no-conflict.js prototype/prototype.js mage/translate.js lib/ccard.js diff --git a/dev/tests/integration/testsuite/Mage/Adminhtml/Model/System/Config/Source/Admin/PageTest.php b/dev/tests/integration/testsuite/Mage/Adminhtml/Model/System/Config/Source/Admin/PageTest.php index d76d152a6d855..98084400c6df0 100644 --- a/dev/tests/integration/testsuite/Mage/Adminhtml/Model/System/Config/Source/Admin/PageTest.php +++ b/dev/tests/integration/testsuite/Mage/Adminhtml/Model/System/Config/Source/Admin/PageTest.php @@ -34,8 +34,7 @@ public function testToOptionArray() { $this->dispatch('backend/admin/system_config/edit/section/admin'); - $dom = new DomDocument(); - $dom->loadHTML($this->getResponse()->getBody()); + $dom = PHPUnit_Util_XML::load($this->getResponse()->getBody(), true); $select = $dom->getElementById('admin_startup_menu_item_id'); $this->assertNotEmpty($select, 'Startup Page select missed'); diff --git a/dev/tests/integration/testsuite/Mage/Core/Helper/JsTest.php b/dev/tests/integration/testsuite/Mage/Core/Helper/JsTest.php index 79c21acfceee4..765744fe454f1 100644 --- a/dev/tests/integration/testsuite/Mage/Core/Helper/JsTest.php +++ b/dev/tests/integration/testsuite/Mage/Core/Helper/JsTest.php @@ -52,9 +52,9 @@ public function testGetTranslateJson() public function testGetTranslatorScript() { $this->assertEquals( - '', + "", $this->_helper->getTranslatorScript() ); } diff --git a/dev/tests/integration/testsuite/Mage/ImportExport/Model/ImportTest.php b/dev/tests/integration/testsuite/Mage/ImportExport/Model/ImportTest.php index f6085e8251868..76918610126c8 100644 --- a/dev/tests/integration/testsuite/Mage/ImportExport/Model/ImportTest.php +++ b/dev/tests/integration/testsuite/Mage/ImportExport/Model/ImportTest.php @@ -103,23 +103,24 @@ public function testImportSource() $this->assertGreaterThan($existCustomersCount, $addedCustomers); } - /** - * @expectedException Mage_Core_Exception - * @expectedExceptionMessage Entity is unknown - */ - public function testGetEntityAdapterEntityIsNotSet() + public function testValidateSource() { - $this->_model->validateSource(''); + $this->_model->setEntity('catalog_product'); + $source = $this->getMockForAbstractClass('Mage_ImportExport_Model_Import_Adapter_Abstract', array(), '', false, + true, true, array('getColNames') + ); + $source->expects($this->any())->method('getColNames')->will($this->returnValue(array('sku'))); + $this->assertTrue($this->_model->validateSource($source)); } /** * @expectedException Mage_Core_Exception - * @expectedExceptionMessage Invalid entity + * @expectedExceptionMessage Entity is unknown */ - public function testGetEntityAdapterInvalidEntity() + public function testValidateSourceException() { - $this->_model->setEntity('invalid_entity_name'); - $this->_model->validateSource(''); + $source = $this->getMockForAbstractClass('Mage_ImportExport_Model_Import_Adapter_Abstract', array(), '', false); + $this->_model->validateSource($source); } public function testGetEntity() diff --git a/dev/tests/integration/testsuite/Mage/Install/controllers/WizardControllerTest.php b/dev/tests/integration/testsuite/Mage/Install/controllers/WizardControllerTest.php index a09768776a8af..22f33371ea16c 100644 --- a/dev/tests/integration/testsuite/Mage/Install/controllers/WizardControllerTest.php +++ b/dev/tests/integration/testsuite/Mage/Install/controllers/WizardControllerTest.php @@ -57,6 +57,9 @@ public function tearDown() parent::tearDown(); } + /** + * @magentoAppIsolation enabled + */ public function testPreDispatch() { $this->dispatch('install/index'); diff --git a/dev/tests/integration/testsuite/MageTest.php b/dev/tests/integration/testsuite/MageTest.php index 1d019823d10de..f485efb17e61e 100644 --- a/dev/tests/integration/testsuite/MageTest.php +++ b/dev/tests/integration/testsuite/MageTest.php @@ -32,6 +32,29 @@ public function testIsInstalled() $this->assertTrue(Mage::isInstalled()); } + /** + * @magentoConfigFixture current_store dev/log/active 1 + * @link http://us3.php.net/manual/en/wrappers.php + */ + public function testLogIntoWrapper() + { + $this->expectOutputRegex('/test/'); + Mage::log('test', null, 'php://output'); + } + + /** + * @magentoConfigFixture current_store dev/log/active 1 + * @magentoConfigFixture global/log/core/writer_model Zend_Log_Writer_Mail + */ + public function testLogUnsuppotedWrapper() + { + $logEntry = microtime(); + Mage::log($logEntry); + $logFile = Mage::getBaseDir('log') . '/system.log'; + $this->assertFileExists($logFile); + $this->assertContains($logEntry, file_get_contents($logFile)); + } + /** * @magentoAppIsolation enabled */ diff --git a/dev/tests/integration/testsuite/integrity/Mage/Sales/TotalDeclarationTest.php b/dev/tests/integration/testsuite/integrity/Mage/Sales/TotalDeclarationTest.php new file mode 100644 index 0000000000000..b1b3fc830f4cb --- /dev/null +++ b/dev/tests/integration/testsuite/integrity/Mage/Sales/TotalDeclarationTest.php @@ -0,0 +1,37 @@ +getNode('global/sales/quote/totals')->asCanonicalArray() as $key => $row) { + $config[$key] = array( + 'before' => empty($row['before']) ? array() : explode(',', $row['before']), + 'after' => empty($row['after']) ? array() : explode(',', $row['after']), + ); + } + Mage_Sales_Model_Config_Ordered::validateCollectorDeclarations($config); + } +} diff --git a/dev/tests/js/jsTestDriver.conf b/dev/tests/js/jsTestDriver.conf index b7236e52aa124..e5544bfede0d9 100644 --- a/dev/tests/js/jsTestDriver.conf +++ b/dev/tests/js/jsTestDriver.conf @@ -20,8 +20,10 @@ load: - ../../../pub/js/jquery/slimScroll/slimScroll.min.js - ../../../app/code/core/Mage/Page/view/frontend/js/cookies.js - ../../../app/code/core/Mage/DesignEditor/view/frontend/css/styles.css + - ../../../pub/js/mage/translate.js test: - ../../../dev/tests/js/testsuite/mage/localization/*.js - ../../../dev/tests/js/testsuite/mage/*.js - ../../../dev/tests/js/testsuite/mage/validation/*.js - - ../../../dev/tests/js/testsuite/mage/design_editor/*.js \ No newline at end of file + - ../../../dev/tests/js/testsuite/mage/design_editor/*.js + - ../../../dev/tests/js/testsuite/mage/translate/*.js \ No newline at end of file diff --git a/dev/tests/js/testsuite/mage/translate/translateTest.js b/dev/tests/js/testsuite/mage/translate/translateTest.js new file mode 100644 index 0000000000000..bece14e75fa0f --- /dev/null +++ b/dev/tests/js/testsuite/mage/translate/translateTest.js @@ -0,0 +1,67 @@ +/** + * Magento + * + * NOTICE OF LICENSE + * + * This source file is subject to the Academic Free License (AFL 3.0) + * that is bundled with this package in the file LICENSE_AFL.txt. + * It is also available through the world-wide-web at this URL: + * http://opensource.org/licenses/afl-3.0.php + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@magentocommerce.com so we can send you a copy immediately. + * + * DISCLAIMER + * + * Do not edit or add to this file if you wish to upgrade Magento to newer + * versions in the future. If you wish to customize Magento for your + * needs please refer to http://www.magentocommerce.com for more information. + * + * @category mage.translate + * @package test + * @copyright Copyright (c) 2012 Magento Inc. (http://www.magentocommerce.com) + * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) + */ +TranslateTest = TestCase('TranslateTest'); +TranslateTest.prototype.testTranslateExist = function() { + assertEquals(true, jQuery.mage.translate != undefined ? true : false); +}; +TranslateTest.prototype.testTranslationParametersOneArgument = function() { + jQuery.mage.translate.add('Hello World!'); + assertEquals( + 'Hello World!', + jQuery.mage.translate.translate('Hello World!')); +}; +TranslateTest.prototype.testTranslationParametersArray = function() { + jQuery.mage.translate.add(['Hello World!', 'Bonjour tout le monde!']); + assertEquals( + 'Hello World!', + jQuery.mage.translate.translate('Hello World!')); +}; +TranslateTest.prototype.testTranslationParametersObject = function() { + var translation = {'Hello World!': 'Bonjour tout le monde!'}; + jQuery.mage.translate.add(translation); + assertEquals( + translation['Hello World!'], + jQuery.mage.translate.translate('Hello World!')); + + translation = { + 'Hello World!': 'Hallo Welt!', + 'Some text with symbols!-+"%#*': 'Ein Text mit Symbolen!-+"%#*' + }; + jQuery.mage.translate.add(translation); + jQuery.each(translation, function(key) { + assertEquals(translation[key], jQuery.mage.translate.translate(key)); + }); +}; +TranslateTest.prototype.testTranslationParametersTwoArguments = function() { + jQuery.mage.translate.add('Hello World!', 'Bonjour tout le monde!'); + assertEquals( + 'Bonjour tout le monde!', + jQuery.mage.translate.translate('Hello World!')); +}; +TranslateTest.prototype.testTranslationAlias = function() { + var translation = {'Hello World!': 'Bonjour tout le monde!'}; + jQuery.mage.translate.add(translation); + assertEquals(translation['Hello World!'], jQuery.mage.__('Hello World!')); +}; diff --git a/dev/tests/performance/framework/Magento/Config.php b/dev/tests/performance/framework/Magento/Config.php index 23454226f8e14..f9feb530026e3 100644 --- a/dev/tests/performance/framework/Magento/Config.php +++ b/dev/tests/performance/framework/Magento/Config.php @@ -79,6 +79,7 @@ class Magento_Config * * @param array $configData * @param string $baseDir + * @throws InvalidArgumentException * @throws Magento_Exception */ public function __construct(array $configData, $baseDir) @@ -87,8 +88,7 @@ public function __construct(array $configData, $baseDir) if (!is_dir($baseDir)) { throw new Magento_Exception("Base directory '$baseDir' does not exist."); } - $baseDir = str_replace('\\', '/', realpath($baseDir)); - $this->_reportDir = $baseDir . '/' . $configData['report_dir']; + $this->_reportDir = $baseDir . DIRECTORY_SEPARATOR . $configData['report_dir']; $applicationOptions = $configData['application']; $this->_applicationUrlHost = $applicationOptions['url_host']; @@ -99,7 +99,19 @@ public function __construct(array $configData, $baseDir) $installConfig = $applicationOptions['installation']; $this->_installOptions = $installConfig['options']; if (isset($installConfig['fixture_files'])) { - $this->_fixtureFiles = glob($baseDir . '/' . $installConfig['fixture_files'], GLOB_BRACE); + if (!is_array($installConfig['fixture_files'])) { + throw new InvalidArgumentException( + "'application' => 'installation' => 'fixture_files' option must be array" + ); + } + $this->_fixtureFiles = array(); + foreach ($installConfig['fixture_files'] as $fixtureName) { + $fixtureFile = $baseDir . DIRECTORY_SEPARATOR . $fixtureName; + if (!file_exists($fixtureFile)) { + throw new Magento_Exception("Fixture '$fixtureName' doesn't exist in $baseDir"); + } + $this->_fixtureFiles[] = $fixtureFile; + } } } @@ -116,6 +128,7 @@ public function __construct(array $configData, $baseDir) * Expands scenario options and file paths glob to a list of scenarios * @param array $scenarios * @param string $baseDir + * @throws InvalidArgumentException * @throws Magento_Exception */ protected function _expandScenarios($scenarios, $baseDir) @@ -126,20 +139,23 @@ protected function _expandScenarios($scenarios, $baseDir) $scenarioParamsCommon = array(); } - $scenarioFilesPattern = $baseDir . '/' . $scenarios['files']; - $scenarioFiles = glob($scenarioFilesPattern, GLOB_BRACE); - if (!$scenarioFiles) { - throw new Magento_Exception("No scenario files match '$scenarioFilesPattern' pattern."); - } - foreach ($scenarioFiles as $oneScenarioFile) { - $oneScenarioFile = str_replace('\\', '/', realpath($oneScenarioFile)); - $oneScenarioName = substr($oneScenarioFile, strlen($baseDir) + 1); - if (isset($scenarios['scenario_params'][$oneScenarioName])) { - $oneScenarioParams = $scenarios['scenario_params'][$oneScenarioName]; - } else { - $oneScenarioParams = array(); + if (isset($scenarios['files'])) { + if (!is_array($scenarios['files'])) { + throw new InvalidArgumentException("'scenarios' => 'files' option must be array"); + } + foreach ($scenarios['files'] as $scenarioName) { + $scenarioFile = $baseDir . DIRECTORY_SEPARATOR . $scenarioName; + if (!file_exists($scenarioFile)) { + throw new Magento_Exception("Scenario '$scenarioName' doesn't exist in $baseDir"); + } + + if (isset($scenarios['scenario_params'][$scenarioName])) { + $oneScenarioParams = $scenarios['scenario_params'][$scenarioName]; + } else { + $oneScenarioParams = array(); + } + $this->_scenarios[$scenarioFile] = array_merge($scenarioParamsCommon, $oneScenarioParams); } - $this->_scenarios[$oneScenarioFile] = array_merge($scenarioParamsCommon, $oneScenarioParams); } } diff --git a/dev/tests/performance/framework/Magento/ImportExport/Fixture/Generator.php b/dev/tests/performance/framework/Magento/ImportExport/Fixture/Generator.php new file mode 100644 index 0000000000000..ac0c938b249a4 --- /dev/null +++ b/dev/tests/performance/framework/Magento/ImportExport/Fixture/Generator.php @@ -0,0 +1,98 @@ +_pattern = $rowPattern; + $this->_colNames = array_keys($rowPattern); + $this->_colQuantity = count($rowPattern); + foreach ($rowPattern as $key => $value) { + if (false !== strpos($value, '%s')) { + $this->_dynamicColumns[$key] = $value; + } + } + $this->_limit = (int)$limit; + } + + /** + * Whether limit of generated elements is reached (according to "Iterator" interface) + * + * @return bool + */ + public function valid() + { + return $this->_currentKey <= $this->_limit; + } + + /** + * Generate new element ("Iterator") + */ + public function next() + { + $this->_currentKey++; + $this->_currentRow = $this->_pattern; + foreach ($this->_dynamicColumns as $key => $pattern) { + $this->_currentRow[$key] = str_replace('%s', $this->_currentKey, $pattern); + } + } + + /** + * Generate first element ("Iterator") + */ + public function rewind() + { + $this->_currentKey = 0; + $this->next(); + } +} diff --git a/dev/tests/performance/framework/Magento/Installer.php b/dev/tests/performance/framework/Magento/Installer.php index f428081332ad8..b47eabbb01f75 100644 --- a/dev/tests/performance/framework/Magento/Installer.php +++ b/dev/tests/performance/framework/Magento/Installer.php @@ -76,6 +76,7 @@ public function install(array $options, array $fixtureFiles = array()) $this->_bootstrap(); $this->_applyFixtures($fixtureFiles); $this->_reindex(); + $this->_updateFilesystemPermissions(); } /** @@ -128,4 +129,12 @@ protected function _reindex() } } } + + /** + * Update permissions for `var` directory + */ + protected function _updateFilesystemPermissions() + { + Varien_Io_File::chmodRecursive(Mage::getBaseDir('var'), 0777); + } } diff --git a/dev/tests/performance/framework/Magento/Scenario.php b/dev/tests/performance/framework/Magento/Scenario.php index 87211f62c725e..1358203b6f928 100644 --- a/dev/tests/performance/framework/Magento/Scenario.php +++ b/dev/tests/performance/framework/Magento/Scenario.php @@ -108,9 +108,6 @@ public function run($scenarioFile, array $scenarioParams) )); } - // Run before-the-scenario PHP script (if exists) - $beforeOutput = $this->_runScenarioAdditionalScript($scenarioFile, 'before'); - // Dry run - just to warm-up the system $dryScenarioParams = array_merge($scenarioParams, array(self::PARAM_USERS => 1, self::PARAM_LOOPS => 2)); $this->_runScenario($scenarioFile, $dryScenarioParams); @@ -119,15 +116,6 @@ public function run($scenarioFile, array $scenarioParams) $fullScenarioParams = $scenarioParams + array(self::PARAM_USERS => 1, self::PARAM_LOOPS => 1); $reportFile = $this->_reportDir . DIRECTORY_SEPARATOR . basename($scenarioFile, '.jmx') . '.jtl'; $this->_runScenario($scenarioFile, $fullScenarioParams, $reportFile); - - // Run after-the-scenario PHP script (if exists) - $scenarioExecutions = $dryScenarioParams[self::PARAM_USERS] * $dryScenarioParams[self::PARAM_LOOPS] - + $fullScenarioParams[self::PARAM_USERS] * $fullScenarioParams[self::PARAM_LOOPS]; - $params = array( - 'beforeOutput' => $beforeOutput, - 'scenarioExecutions' => $scenarioExecutions, - ); - $this->_runScenarioAdditionalScript($scenarioFile, 'after', $params); } /** @@ -196,34 +184,4 @@ protected function _verifyReport($reportFile) throw new Magento_Exception(implode(PHP_EOL, $failureMessages)); } } - - /** - * Execute additional before/after scenario PHP script, if it exists - * - * @param string $scenarioFile - * @param string $suffix - * @param array $params - * @return null|string - * @throws Magento_Exception - */ - protected function _runScenarioAdditionalScript($scenarioFile, $suffix, $params = array()) - { - // Check existence of additional script - $pathinfo = pathinfo($scenarioFile); - $scriptFile = $pathinfo['dirname'] . DIRECTORY_SEPARATOR . "{$pathinfo['filename']}_{$suffix}.php"; - if (!file_exists($scriptFile)) { - return null; - } - - // Run script - $cmd = 'php %s'; - $execParams = array($scriptFile); - foreach ($params as $key => $value) { - $cmd .= " --{$key}=%s"; - $execParams[] = $value; - } - $output = $this->_shell->execute($cmd, $execParams); - - return $output; - } } diff --git a/dev/tests/performance/framework/tests/unit/phpunit.xml.dist b/dev/tests/performance/framework/tests/unit/phpunit.xml.dist index 99de2bd763d2e..4ff632bf94318 100644 --- a/dev/tests/performance/framework/tests/unit/phpunit.xml.dist +++ b/dev/tests/performance/framework/tests/unit/phpunit.xml.dist @@ -34,6 +34,7 @@ ../../../../../../lib + ../../../../../../app/code/core ../.. diff --git a/dev/tests/performance/framework/tests/unit/testsuite/Magento/ConfigTest.php b/dev/tests/performance/framework/tests/unit/testsuite/Magento/ConfigTest.php index 2c81b341bc89e..8be5813558d98 100644 --- a/dev/tests/performance/framework/tests/unit/testsuite/Magento/ConfigTest.php +++ b/dev/tests/performance/framework/tests/unit/testsuite/Magento/ConfigTest.php @@ -49,11 +49,17 @@ class Magento_ConfigTest extends PHPUnit_Framework_TestCase 'option1' => 'value 1', 'option2' => 'value 2', ), - 'fixture_files' => '{fixture}.php', + 'fixture_files' => array( + 'fixture.php', + ), ), ), 'scenario' => array( - 'files' => '*.jmx', + 'files' => array( + 'scenario.jmx', + 'scenario_error.jmx', + 'scenario_failure.jmx', + ), 'common_params' => array( 'param1' => 'value 1', 'param2' => 'value 2', @@ -69,7 +75,7 @@ class Magento_ConfigTest extends PHPUnit_Framework_TestCase protected function setUp() { - $this->_object = new Magento_Config($this->_sampleConfigData, __DIR__ . '/_files'); + $this->_object = new Magento_Config($this->_sampleConfigData, __DIR__ . DIRECTORY_SEPARATOR . '_files'); } protected function tearDown() @@ -81,11 +87,12 @@ protected function tearDown() * @dataProvider constructorExceptionDataProvider * @param array $configData * @param string $baseDir + * @param string $expectedException * @param string $expectedExceptionMsg */ - public function testConstructorException(array $configData, $baseDir, $expectedExceptionMsg) + public function testConstructorException(array $configData, $baseDir, $expectedException, $expectedExceptionMsg) { - $this->setExpectedException('Magento_Exception', $expectedExceptionMsg); + $this->setExpectedException($expectedException, $expectedExceptionMsg); new Magento_Config($configData, $baseDir); } @@ -98,12 +105,64 @@ public function constructorExceptionDataProvider() 'non-existing base dir' => array( $this->_sampleConfigData, 'non_existing_dir', + 'Magento_Exception', "Base directory 'non_existing_dir' does not exist", ), - 'no scenarios match pattern' => array( - array_merge($this->_sampleConfigData, array('scenario' => array('files' => 'non_existing_*.jmx'))), - __DIR__ . '/_files', - 'No scenario files match', + 'invalid fixtures format' => array( + array_merge( + $this->_sampleConfigData, array( + 'application' => array( + 'url_host' => '127.0.0.1', + 'url_path' => '/', + 'admin' => array( + 'frontname' => 'backend', + 'username' => 'admin', + 'password' => 'password1', + ), + 'installation' => array( + 'options' => array( + 'option1' => 'value 1', + 'option2' => 'value 2', + ), + 'fixture_files' => 'string_fixtures_*.php', + ), + ) + ) + ), + __DIR__ . DIRECTORY_SEPARATOR . '_files', + 'InvalidArgumentException', + "'application' => 'installation' => 'fixture_files' option must be array", + ), + 'non-existing fixture' => array( + array_merge_recursive( + $this->_sampleConfigData, + array('application' => + array( + 'installation' => array('fixture_files' => array('non_existing_fixture.php')), + ) + ) + ), + __DIR__ . DIRECTORY_SEPARATOR . '_files', + 'Magento_Exception', + "Fixture 'non_existing_fixture.php' doesn't exist", + ), + 'invalid scenarios format' => array( + array_merge( + $this->_sampleConfigData, + array('scenario' => array('files' => 'string_fixtures_*.jmx')) + ), + __DIR__ . DIRECTORY_SEPARATOR . '_files', + 'InvalidArgumentException', + "'scenarios' => 'files' option must be array", + ), + 'non-existing scenario' => array( + array_merge( + $this->_sampleConfigData, + array('scenario' => array('files' => array('non_existing_scenario.jmx'))) + ), + __DIR__ . DIRECTORY_SEPARATOR . '_files', + 'Magento_Exception', + "Scenario 'non_existing_scenario.jmx' doesn't exist", ), ); } @@ -136,21 +195,17 @@ public function testGetInstallOptions() public function testGetScenarios() { - $dir = str_replace('\\', '/', __DIR__ . '/_files'); + $dir = __DIR__ . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR; $expectedScenarios = array( - $dir . '/scenario.jmx' => array( + $dir . 'scenario.jmx' => array( 'param1' => 'value 1', 'param2' => 'overridden value 2', ), - $dir . '/scenario_error.jmx' => array( - 'param1' => 'value 1', - 'param2' => 'value 2', - ), - $dir . '/scenario_failure.jmx' => array( + $dir . 'scenario_error.jmx' => array( 'param1' => 'value 1', 'param2' => 'value 2', ), - $dir . '/scenario_with_scripts.jmx' => array( + $dir . 'scenario_failure.jmx' => array( 'param1' => 'value 1', 'param2' => 'value 2', ), @@ -163,13 +218,13 @@ public function testGetScenarios() public function testGetFixtureFiles() { - $expectedFixtureFile = str_replace('\\', '/', __DIR__ . '/_files/fixture.php'); + $expectedFixtureFile = __DIR__ . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'fixture.php'; $this->assertEquals(array($expectedFixtureFile), $this->_object->getFixtureFiles()); } public function testGetReportDir() { - $expectedReportDir = str_replace('\\', '/', __DIR__ . '/_files/report'); + $expectedReportDir = __DIR__ . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'report'; $this->assertEquals($expectedReportDir, $this->_object->getReportDir()); } diff --git a/dev/tests/performance/framework/tests/unit/testsuite/Magento/_files/scenario_with_scripts_after.php b/dev/tests/performance/framework/tests/unit/testsuite/Magento/ImportExport/Fixture/GeneratorTest.php similarity index 66% rename from dev/tests/performance/framework/tests/unit/testsuite/Magento/_files/scenario_with_scripts_after.php rename to dev/tests/performance/framework/tests/unit/testsuite/Magento/ImportExport/Fixture/GeneratorTest.php index 4b190a120f85a..682e3990b9e68 100644 --- a/dev/tests/performance/framework/tests/unit/testsuite/Magento/_files/scenario_with_scripts_after.php +++ b/dev/tests/performance/framework/tests/unit/testsuite/Magento/ImportExport/Fixture/GeneratorTest.php @@ -25,4 +25,18 @@ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) */ -// Nothing here, just file existence is needed +class Magento_ImportExport_Fixture_GeneratorTest extends PHPUnit_Framework_TestCase +{ + public function testIteratorInterface() + { + $model = new Magento_ImportExport_Fixture_Generator(array('id' => '%s', 'name' => 'Static'), 2); + $rows = array(); + foreach ($model as $row) { + $rows[] = $row; + } + $this->assertEquals(array( + array('id' => '1', 'name' => 'Static'), + array('id' => '2', 'name' => 'Static'), + ), $rows); + } +} diff --git a/dev/tests/performance/framework/tests/unit/testsuite/Magento/InstallerTest.php b/dev/tests/performance/framework/tests/unit/testsuite/Magento/InstallerTest.php index efeb703a76d1d..d5f2e549dc0c5 100644 --- a/dev/tests/performance/framework/tests/unit/testsuite/Magento/InstallerTest.php +++ b/dev/tests/performance/framework/tests/unit/testsuite/Magento/InstallerTest.php @@ -48,7 +48,7 @@ protected function setUp() $this->_installerScript = realpath(__DIR__ . '/_files/install_stub.php'); $this->_object = $this->getMock( 'Magento_Installer', - array('_bootstrap', '_reindex'), + array('_bootstrap', '_reindex', '_updateFilesystemPermissions'), array($this->_installerScript, $this->_shell) ); } diff --git a/dev/tests/performance/framework/tests/unit/testsuite/Magento/ScenarioTest.php b/dev/tests/performance/framework/tests/unit/testsuite/Magento/ScenarioTest.php index 6c79d7406258e..4c5f7f0d0c47a 100644 --- a/dev/tests/performance/framework/tests/unit/testsuite/Magento/ScenarioTest.php +++ b/dev/tests/performance/framework/tests/unit/testsuite/Magento/ScenarioTest.php @@ -135,31 +135,4 @@ public function runExceptionDataProvider() ), ); } - - public function testRunWithScripts() - { - $scenarioFile = realpath(__DIR__ . '/_files/scenario_with_scripts.jmx'); - $scriptBefore = realpath(__DIR__ . '/_files/scenario_with_scripts_before.php'); - $scriptAfter = realpath(__DIR__ . '/_files/scenario_with_scripts_after.php'); - - $this->_shell - ->expects($this->at(0)) - ->method('execute') - ->with('php %s', array($scriptBefore)) - ->will($this->returnValue('fixture output')); - - $this->_shell - ->expects($this->at(3)) - ->method('execute') - ->with( - 'php %s --beforeOutput=%s --scenarioExecutions=%s', - array( - $scriptAfter, - 'fixture output', - 4, // 2 warm-up executions + 2 users x 1 loop - ) - ); - - $this->_object->run($scenarioFile, $this->_scenarioParams); - } } diff --git a/dev/tests/performance/framework/tests/unit/testsuite/Magento/_files/scenario_with_scripts.jmx b/dev/tests/performance/framework/tests/unit/testsuite/Magento/_files/scenario_with_scripts.jmx deleted file mode 100644 index a48e5b549e6f8..0000000000000 --- a/dev/tests/performance/framework/tests/unit/testsuite/Magento/_files/scenario_with_scripts.jmx +++ /dev/null @@ -1,29 +0,0 @@ - - - diff --git a/dev/tests/performance/framework/tests/unit/testsuite/Magento/_files/scenario_with_scripts.jtl b/dev/tests/performance/framework/tests/unit/testsuite/Magento/_files/scenario_with_scripts.jtl deleted file mode 100644 index f21637327db28..0000000000000 --- a/dev/tests/performance/framework/tests/unit/testsuite/Magento/_files/scenario_with_scripts.jtl +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - Response Assertion - false - false - - - diff --git a/dev/tests/performance/testsuite/backend.jmx b/dev/tests/performance/testsuite/backend.jmx new file mode 100644 index 0000000000000..b5c7e26a5ebdd --- /dev/null +++ b/dev/tests/performance/testsuite/backend.jmx @@ -0,0 +1,204 @@ + + + + + + + false + false + + + + HOST + ${__P(host,localhost)} + = + + + ADMIN_PATH + ${__P(path,/)}${__P(admin_frontname,backend)}/ + = + + + ADMIN_USERNAME + ${__P(admin_username,admin)} + = + + + ADMIN_PASSWORD + ${__P(admin_password,123123q)} + = + + + products_number + ${__P(products_number,0)} + = + + + customers_number + ${__P(customers_number,0)} + = + + + users + ${__P(users,1)} + = + + + loops + ${__P(loops,100)} + = + + + + + + + + continue + + false + ${loops} + + ${users} + 1 + 1346113741000 + 1346113741000 + false + + + + + + reusable/admin_login.jmx + + + + + + + ${HOST} + + + + + + ${ADMIN_PATH}admin/catalog_product/ + GET + true + false + true + false + Java + false + + + + + + <h3 class="icon-head head-products">Manage Products</h3> + Total ${products_number} records found + + Assertion.response_data + false + 2 + + + + + + + + ${HOST} + + + + + UTF8 + ${ADMIN_PATH}admin/customer/ + GET + true + false + true + false + Java + false + + + + + + <h3 class="icon-head head-customer">Manage Customers</h3> + Total ${customers_number} records found + + Assertion.response_data + false + 2 + + + + + + true + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + true + false + false + false + false + false + 0 + true + + + + + + + + + diff --git a/dev/tests/performance/testsuite/checkout.jmx b/dev/tests/performance/testsuite/checkout.jmx index 5e961f434e1bc..5d88169503c1c 100644 --- a/dev/tests/performance/testsuite/checkout.jmx +++ b/dev/tests/performance/testsuite/checkout.jmx @@ -25,7 +25,7 @@ * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) */ --> - + @@ -35,12 +35,12 @@ HOST - ${__P(host,mage-dev.varien.com)} + ${__P(host,localhost)} = PATH - ${__P(path,/dev/performance/magento-two/)} + ${__P(path,/)} = @@ -58,12 +58,108 @@ ${__P(loops,1)} = + + ADMIN_PATH + ${__P(path,/)}${__P(admin_frontname,backend)}/ + = + + + ADMIN_USERNAME + ${__P(admin_username,admin)} + = + + + ADMIN_PASSWORD + ${__P(admin_password,123123q)} + = + - + + + + reusable/admin_login.jmx + + + + + + + + + + + http + + ${ADMIN_PATH}admin/sales_order/ + GET + true + false + true + false + Java + false + + + + + false + numOrders + Total (\d+) records found + $1$ + + 1 + numOrdersBefore + + + + + ^\d+$ + + Assertion.response_data + false + 1 + variable + numOrders + + + + + + stoptest + + false + 1 + + 1 + 1 + 1345662757000 + 1345662757000 + false + + + + + + + WorkBench + One Page Checkout + Reusable: Get number of orders + + + + + + + props.put("numOrdersBefore", vars.get('numOrders')); + javascript + + + + stopthread false @@ -78,7 +174,7 @@ - + @@ -94,9 +190,10 @@ false true false + Java false - + false @@ -140,7 +237,7 @@ - + @@ -178,9 +275,10 @@ false true false + Java false - + @@ -211,7 +309,7 @@ - + @@ -227,11 +325,12 @@ false true false + Java false - + - + @@ -255,9 +354,10 @@ false true false + Java false - + @@ -298,7 +398,7 @@ - + @@ -441,9 +541,10 @@ false true false + Java false - + @@ -484,7 +585,7 @@ - + @@ -500,9 +601,10 @@ false true false + Java false - + @@ -538,7 +640,7 @@ - + @@ -554,9 +656,10 @@ false true false + Java false - + @@ -580,7 +683,7 @@ - + @@ -604,9 +707,10 @@ false true false + Java false - + @@ -647,7 +751,7 @@ - + @@ -663,9 +767,10 @@ false true false + Java false - + @@ -685,7 +790,7 @@ - + @@ -709,9 +814,10 @@ false true false + Java false - + @@ -752,7 +858,7 @@ - + @@ -768,9 +874,10 @@ false true false + Java false - + @@ -790,7 +897,7 @@ - + @@ -814,9 +921,10 @@ false true false + Java false - + @@ -857,7 +965,7 @@ - + @@ -873,9 +981,10 @@ false true false + Java false - + @@ -889,7 +998,49 @@ - + + stoptest + + false + 1 + + 1 + 1 + 1345663815000 + 1345663815000 + false + + + + + + + WorkBench + One Page Checkout + Reusable: Get number of orders + + + + + + + var numAfter = vars.get('numOrders'); +if (numAfter !== null) { // The asserion is called for every sampler, but we want it to act only after orders number is retrieved + var numBefore = props.get('numOrdersBefore'); + var numCreated = numAfter - numBefore; + var numExpected = vars.get('USERS') * vars.get('LOOPS'); + + if (numCreated != numExpected) { + var message = 'Expected number of created orders: ' + numExpected + ', actual: ' + numCreated + ' (total before: ' + numBefore + ', total after: ' + numAfter + ')'; + AssertionResult.setFailureMessage(message); + AssertionResult.setFailure(true); + } +} + javascript + + + + false saveConfig @@ -932,6 +1083,7 @@ http + 4 diff --git a/dev/tests/performance/testsuite/checkout_after.php b/dev/tests/performance/testsuite/checkout_after.php deleted file mode 100644 index a5fd2f95291e0..0000000000000 --- a/dev/tests/performance/testsuite/checkout_after.php +++ /dev/null @@ -1,49 +0,0 @@ -getSize(); -$actualOrdersCreated = $numOrdersNow - $numOrdersBefore; - -// Compare number of new orders to the expected value -if ($expectedOrdersCreated != $actualOrdersCreated) { - echo "Failure: expected {$expectedOrdersCreated} new orders, while actually created {$actualOrdersCreated}"; - exit(1); -} - -echo "Verification successful, {$actualOrdersCreated} of {$expectedOrdersCreated} orders created"; diff --git a/dev/tests/performance/testsuite/fixtures/catalog_100k_products.php b/dev/tests/performance/testsuite/fixtures/catalog_100k_products.php new file mode 100644 index 0000000000000..b26810eb7a462 --- /dev/null +++ b/dev/tests/performance/testsuite/fixtures/catalog_100k_products.php @@ -0,0 +1,57 @@ + 'Default', + '_type' => Mage_Catalog_Model_Product_Type::TYPE_SIMPLE, + '_product_websites' => 'base', + 'name' => 'Product %s', + 'short_description' => 'Short desc %s', + 'weight' => 1, + 'description' => 'Description %s', + 'sku' => 'product_dynamic_%s', + 'price' => 10, + 'visibility' => Mage_Catalog_Model_Product_Visibility::VISIBILITY_BOTH, + 'status' => Mage_Catalog_Model_Product_Status::STATUS_ENABLED, + 'tax_class_id' => 0, + + // actually it saves without stock data, but by default system won't show on the frontend products out of stock + 'is_in_stock' => 1, + 'qty' => 100500, + 'use_config_min_qty' => '1', + 'use_config_backorders' => '1', + 'use_config_min_sale_qty' => '1', + 'use_config_max_sale_qty' => '1', + 'use_config_notify_stock_qty' => '1', + 'use_config_manage_stock' => '1', + 'use_config_qty_increments' => '1', + 'use_config_enable_qty_inc' => '1', + 'stock_id' => Mage_CatalogInventory_Model_Stock::DEFAULT_STOCK_ID, +); +$generator = new Magento_ImportExport_Fixture_Generator($pattern, 100000); +$import = new Mage_ImportExport_Model_Import(array('entity' => 'catalog_product', 'behavior' => 'append')); +// it is not obvious, but the validateSource() will actually save import queue data to DB +$import->validateSource($generator); +// this converts import queue into actual entities +$import->importSource(); diff --git a/dev/tests/performance/testsuite/fixtures/customer_100k_customers.php b/dev/tests/performance/testsuite/fixtures/customer_100k_customers.php new file mode 100644 index 0000000000000..9b471fef3aca2 --- /dev/null +++ b/dev/tests/performance/testsuite/fixtures/customer_100k_customers.php @@ -0,0 +1,74 @@ + 'user%s@example.com', + '_website' => 'base', + '_store' => '', + 'confirmation' => NULL, + 'created_at' => '30-08-2012 17:43', + 'created_in' => 'Default', + 'default_billing' => '1', + 'default_shipping' => '1', + 'disable_auto_group_change' => '0', + 'dob' => '12-10-1991', + 'firstname' => 'Firstname %s', + 'gender' => 'Male', + 'group_id' => '1', + 'lastname' => 'Lastname %s', + 'middlename' => '', + 'password_hash' => '', + 'prefix' => NULL, + 'reward_update_notification' => '1', + 'reward_warning_notification' => '1', + 'rp_token' => NULL, + 'rp_token_created_at' => NULL, + 'store_id' => '0', + 'suffix' => NULL, + 'taxvat' => NULL, + 'website_id' => '1', + 'password' => '123123q%s', + '_address_city' => 'Fayetteville', + '_address_company' => '', + '_address_country_id' => 'US', + '_address_fax' => '', + '_address_firstname' => 'Anthony', + '_address_lastname' => 'Nealy', + '_address_middlename' => '', + '_address_postcode' => '%s', + '_address_prefix' => '', + '_address_region' => 'Arkansas', + '_address_street' => '%s Freedom Blvd. #%s', + '_address_suffix' => '', + '_address_telephone' => '%s-%s-%s', + '_address_vat_id' => '', + '_address_default_billing_' => '1', + '_address_default_shipping_' => '1' +); +$generator = new Magento_ImportExport_Fixture_Generator($pattern, 100000); +$import = new Mage_ImportExport_Model_Import(array('entity' => 'customer_composite', 'behavior' => 'append')); +// it is not obvious, but the validateSource() will actually save import queue data to DB +$import->validateSource($generator); +// this converts import queue into actual entities +$import->importSource(); diff --git a/dev/tests/performance/testsuite/product_edit.jmx b/dev/tests/performance/testsuite/product_edit.jmx index 809a36dbb899a..cd321f5130538 100644 --- a/dev/tests/performance/testsuite/product_edit.jmx +++ b/dev/tests/performance/testsuite/product_edit.jmx @@ -88,103 +88,10 @@ - - - - - ${HOST} - - - - http - - ${ADMIN_PATH} - GET - true - false - true - false - Java - false - - - - - - Log in to Admin Panel - - Assertion.response_data - false - 2 - - - - false - form_key - <input name="form_key" type="hidden" value="([^'"]+)" /> - $1$ - - 1 - - - - - ^.+$ - - Assertion.response_data - false - 1 - variable - form_key - - - - - - - - false - ${ADMIN_USERNAME} - = - true - login[username] - - - false - ${ADMIN_PASSWORD} - = - true - login[password] - - - - ${HOST} - - - - http - - ${ADMIN_PATH} - POST - true - false - true - false - Java - false - - - - - - Logged in as ${ADMIN_USERNAME} - - Assertion.response_data - false - 2 - - - + + reusable/admin_login.jmx + + diff --git a/dev/tests/performance/testsuite/reusable/admin_login.jmx b/dev/tests/performance/testsuite/reusable/admin_login.jmx new file mode 100644 index 0000000000000..939997aac5cba --- /dev/null +++ b/dev/tests/performance/testsuite/reusable/admin_login.jmx @@ -0,0 +1,229 @@ + + + + + + Reusable scenario to log-in to admin backend. Needs: HOST, ADMIN_PATH, ADMIN_USERNAME, ADMIN_PASSWORD, Http Cookie Manager + false + false + + + + + + + + + + + + + ${HOST} + + + + http + + ${ADMIN_PATH} + GET + true + false + true + false + Java + false + + + + + + Log in to Admin Panel + + Assertion.response_data + false + 2 + + + + false + form_key + <input name="form_key" type="hidden" value="([^'"]+)" /> + $1$ + + 1 + + + + + ^.+$ + + Assertion.response_data + false + 1 + variable + form_key + + + + + + + + false + ${ADMIN_USERNAME} + = + true + login[username] + + + false + ${ADMIN_PASSWORD} + = + true + login[password] + + + + ${HOST} + + + + http + + ${ADMIN_PATH} + POST + true + false + true + false + Java + false + + + + + + Logged in as ${ADMIN_USERNAME} + + Assertion.response_data + false + 2 + + + + + + stopthread + + false + 1 + + 1 + 1 + 1304708488000 + 1304708488000 + false + + + Not included into exported fragment, just for purpose of testing this scenario + + + + + WorkBench + Admin - Login (Test Plan) + Admin - Login (Test Fragment) + + + + + + false + + + + + + HOST + ${__P(host,localhost)} + = + + + ADMIN_PATH + ${__P(path,/)}${__P(admin_frontname,backend)}/ + = + + + ADMIN_USERNAME + ${__P(admin_username,admin)} + = + + + ADMIN_PASSWORD + ${__P(admin_password,123123q)} + = + + + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + true + false + false + false + false + false + 0 + true + + + + + + + + diff --git a/dev/tests/static/testsuite/Php/_files/whitelist/core.txt b/dev/tests/static/testsuite/Php/_files/whitelist/core.txt index 667e040416a49..70dbef97cb694 100644 --- a/dev/tests/static/testsuite/Php/_files/whitelist/core.txt +++ b/dev/tests/static/testsuite/Php/_files/whitelist/core.txt @@ -18,6 +18,7 @@ app/code/core/Mage/Core/Model/Layout/Update.php app/code/core/Mage/Core/Model/Design/Fallback app/code/core/Mage/Core/Model/Design/Fallback.php app/code/core/Mage/Core/Model/Design/FallbackInterface.php +app/code/core/Mage/Core/Model/Resource/Setup/Migration.php app/code/core/Mage/DesignEditor app/code/core/Mage/ImportExport/Model/Import/Entity/CustomerComposite.php app/code/core/Mage/ImportExport/Model/Import/Entity/Product/Option.php diff --git a/dev/tests/unit/testsuite/Mage/Core/Model/Resource/Setup/MigrationTest.php b/dev/tests/unit/testsuite/Mage/Core/Model/Resource/Setup/MigrationTest.php new file mode 100644 index 0000000000000..1e97d7fb6342d --- /dev/null +++ b/dev/tests/unit/testsuite/Mage/Core/Model/Resource/Setup/MigrationTest.php @@ -0,0 +1,270 @@ +_actualUpdateResult); + unset($this->_actualWhere); + unset($this->_selectMock); + } + + /** + * Retrieve all necessary objects mocks which used inside customer storage + * + * @param int $tableRowsCount + * @param array $tableData + * @param array $aliasesMap + * + * @return array + */ + protected function _getModelDependencies($tableRowsCount = 0, $tableData = array(), $aliasesMap = array()) + { + $autoload = $this->getMock('Magento_Autoload', array('classExists'), array(), '', false); + $autoload->expects($this->any()) + ->method('classExists') + ->will($this->returnCallback(array($this, 'classExistCallback'))); + + $this->_selectMock = $this->getMock('Varien_Db_Select', array(), array(), '', false); + $this->_selectMock->expects($this->any()) + ->method('from') + ->will($this->returnSelf()); + $this->_selectMock->expects($this->any()) + ->method('where') + ->will($this->returnCallback(array($this, 'whereCallback'))); + + $adapterMock = $this->getMock('Varien_Db_Adapter_Pdo_Mysql', + array('select', 'update', 'fetchAll', 'fetchOne'), array(), '', false + ); + $adapterMock->expects($this->any()) + ->method('select') + ->will($this->returnValue($this->_selectMock)); + $adapterMock->expects($this->any()) + ->method('update') + ->will($this->returnCallback(array($this, 'updateCallback'))); + $adapterMock->expects($this->any()) + ->method('fetchAll') + ->will($this->returnValue($tableData)); + $adapterMock->expects($this->any()) + ->method('fetchOne') + ->will($this->returnValue($tableRowsCount)); + + return array( + 'resource_config' => 'not_used', + 'connection_config' => 'not_used', + 'module_config' => 'not_used', + 'base_dir' => 'not_used', + 'path_to_map_file' => 'not_used', + 'connection' => $adapterMock, + 'autoload' => $autoload, + 'core_helper' => new Mage_Core_Helper_Data(), + 'aliases_map' => $aliasesMap + ); + } + + /** + * Callback for Magento_Autoload::classExist + * + * @return bool + */ + public function classExistCallback() + { + return true; + } + + /** + * Callback for Varien_Db_Select::update + * + * @param string $table + * @param array $bind + * @param array $where + */ + public function updateCallback($table, array $bind, $where) + { + $fields = array_keys($bind); + $replacements = array_values($bind); + + $this->_actualUpdateResult[] = array( + 'table' => $table, + 'field' => $fields[0], + 'to' => $replacements[0], + 'from' => $where + ); + } + + /** + * Callback for Varien_Db_Select::where + * + * @param string $condition + * @return PHPUnit_Framework_MockObject_MockObject|Varien_Db_Select + */ + public function whereCallback($condition) + { + if (null === $this->_actualWhere) { + $this->_actualWhere = array(); + } + if (!empty($condition) && false === strpos($condition, ' IS NOT NULL') + && !in_array($condition, $this->_actualWhere) + ) { + $this->_actualWhere[] = $condition; + } + return $this->_selectMock; + } + + /** + * @covers Mage_Core_Model_Resource_Setup_Migration::appendClassAliasReplace + */ + public function testAppendClassAliasReplace() + { + $setupModel = new Mage_Core_Model_Resource_Setup_Migration('core_setup', $this->_getModelDependencies()); + + $setupModel->appendClassAliasReplace('tableName', 'fieldName', 'entityType', 'fieldContentType', + array('pk_field1', 'pk_field2'), 'additionalWhere' + ); + + $expectedRulesList = array ( + 'tableName' => array( + 'fieldName' => array( + 'entity_type' => 'entityType', + 'content_type' => 'fieldContentType', + 'pk_fields' => array('pk_field1', 'pk_field2'), + 'additional_where' => 'additionalWhere' + ) + ) + ); + + $this->assertAttributeEquals($expectedRulesList, '_replaceRules', $setupModel); + } + + /** + * @dataProvider updateClassAliasesDataProvider + * @covers Mage_Core_Model_Resource_Setup_Migration::doUpdateClassAliases + * @covers Mage_Core_Model_Resource_Setup_Migration::_updateClassAliasesInTable + * @covers Mage_Core_Model_Resource_Setup_Migration::_getRowsCount + * @covers Mage_Core_Model_Resource_Setup_Migration::_applyFieldRule + * @covers Mage_Core_Model_Resource_Setup_Migration::_updateRowsData + * @covers Mage_Core_Model_Resource_Setup_Migration::_getTableData + * @covers Mage_Core_Model_Resource_Setup_Migration::_getReplacement + * @covers Mage_Core_Model_Resource_Setup_Migration::_getCorrespondingClassName + * @covers Mage_Core_Model_Resource_Setup_Migration::_getModelReplacement + * @covers Mage_Core_Model_Resource_Setup_Migration::_getPatternReplacement + * @covers Mage_Core_Model_Resource_Setup_Migration::_getClassName + * @covers Mage_Core_Model_Resource_Setup_Migration::_isFactoryName + * @covers Mage_Core_Model_Resource_Setup_Migration::_getModuleName + * @covers Mage_Core_Model_Resource_Setup_Migration::_getCompositeModuleName + * @covers Mage_Core_Model_Resource_Setup_Migration::_getAliasFromMap + * @covers Mage_Core_Model_Resource_Setup_Migration::_pushToMap + * @covers Mage_Core_Model_Resource_Setup_Migration::_getAliasesMap + * @covers Mage_Core_Model_Resource_Setup_Migration::_getAliasInSerializedStringReplacement + * @covers Mage_Core_Model_Resource_Setup_Migration::_parseSerializedString + */ + public function testDoUpdateClassAliases($replaceRules, $tableData, $expected, $aliasesMap = array()) + { + $this->_actualUpdateResult = array(); + + $tableRowsCount = count($tableData); + + $setupModel = new Mage_Core_Model_Resource_Setup_Migration( + 'core_setup', + $this->_getModelDependencies($tableRowsCount, $tableData, $aliasesMap) + ); + + foreach ($replaceRules as $replaceRule) { + call_user_func_array(array($setupModel, 'appendClassAliasReplace'), $replaceRule); + } + + $setupModel->doUpdateClassAliases(); + + $this->assertEquals($expected['updates'], $this->_actualUpdateResult); + + if (isset($expected['where'])) { + $this->assertEquals($expected['where'], $this->_actualWhere); + } + + if (isset($expected['aliases_map'])) { + $this->assertAttributeEquals($expected['aliases_map'], '_aliasesMap', $setupModel); + } + } + + /** + * Data provider for updating class aliases + * + * @return array + */ + public function updateClassAliasesDataProvider() + { + return array( + 'plain text replace model' => include __DIR__ . '/_files/data_content_plain_model.php', + 'plain text replace resource' => include __DIR__ . '/_files/data_content_plain_resource.php', + 'plain text replace with pk field' => include __DIR__ . '/_files/data_content_plain_pk_fields.php', + 'xml replace' => include __DIR__ . '/_files/data_content_xml.php', + 'wiki markup replace' => include __DIR__ . '/_files/data_content_wiki.php', + 'serialized php replace' => include __DIR__ . '/_files/data_content_serialized.php', + ); + } + + /** + * @covers Mage_Core_Model_Resource_Setup_Migration::getCompositeModules + */ + public function testGetCompositeModules() + { + $compositeModules = Mage_Core_Model_Resource_Setup_Migration::getCompositeModules(); + $this->assertInternalType('array', $compositeModules); + $this->assertNotEmpty($compositeModules); + foreach ($compositeModules as $classAlias => $className) { + $this->assertInternalType('string', $classAlias); + $this->assertInternalType('string', $className); + $this->assertNotEmpty($classAlias); + $this->assertNotEmpty($className); + } + } +} diff --git a/dev/tests/unit/testsuite/Mage/Core/Model/Resource/Setup/_files/data_content_plain_model.php b/dev/tests/unit/testsuite/Mage/Core/Model/Resource/Setup/_files/data_content_plain_model.php new file mode 100644 index 0000000000000..95b03ff80e9f6 --- /dev/null +++ b/dev/tests/unit/testsuite/Mage/Core/Model/Resource/Setup/_files/data_content_plain_model.php @@ -0,0 +1,91 @@ + array( + array( + 'table', + 'field', + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_MODEL, + Mage_Core_Model_Resource_Setup_Migration::FIELD_CONTENT_TYPE_PLAIN, + ) + ), + '$tableData' => array( + array('field' => 'customer/customer'), + array('field' => 'customer/attribute_data_postcode'), + array('field' => 'customer/attribute_data_postcode::someMethod'), + array('field' => 'catalogSearch/session'), + array('field' => 'catalogSearch/session::someMethod'), + array('field' => 'Mage_Customer_Model_Customer'), + ), + '$expected' => array( + 'updates' => array( + array( + 'table' => 'table', + 'field' => 'field', + 'to' => 'Mage_Customer_Model_Customer_FROM_MAP', + 'from' => array('`field` = ?' => 'customer/customer') + ), + array( + 'table' => 'table', + 'field' => 'field', + 'to' => 'Mage_Customer_Model_Attribute_Data_Postcode', + 'from' => array('`field` = ?' => 'customer/attribute_data_postcode') + ), + array( + 'table' => 'table', + 'field' => 'field', + 'to' => 'Mage_Customer_Model_Attribute_Data_Postcode::someMethod', + 'from' => array('`field` = ?' => 'customer/attribute_data_postcode::someMethod') + ), + array( + 'table' => 'table', + 'field' => 'field', + 'to' => 'Mage_CatalogSearch_Model_Session', + 'from' => array('`field` = ?' => 'catalogSearch/session') + ), + array( + 'table' => 'table', + 'field' => 'field', + 'to' => 'Mage_CatalogSearch_Model_Session::someMethod', + 'from' => array('`field` = ?' => 'catalogSearch/session::someMethod') + ), + ), + 'aliases_map' => array( + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_MODEL => array( + 'customer/customer' => 'Mage_Customer_Model_Customer_FROM_MAP', + 'customer/attribute_data_postcode' => 'Mage_Customer_Model_Attribute_Data_Postcode', + 'catalogSearch/session' => 'Mage_CatalogSearch_Model_Session', + ), + ) + ), + '$aliasesMap' => array( + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_MODEL => array( + 'customer/customer' => 'Mage_Customer_Model_Customer_FROM_MAP' + ) + ) +); diff --git a/dev/tests/unit/testsuite/Mage/Core/Model/Resource/Setup/_files/data_content_plain_pk_fields.php b/dev/tests/unit/testsuite/Mage/Core/Model/Resource/Setup/_files/data_content_plain_pk_fields.php new file mode 100644 index 0000000000000..d359a24eb6f23 --- /dev/null +++ b/dev/tests/unit/testsuite/Mage/Core/Model/Resource/Setup/_files/data_content_plain_pk_fields.php @@ -0,0 +1,56 @@ + array( + array( + 'table', + 'collection', + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_RESOURCE, + Mage_Core_Model_Resource_Setup_Migration::FIELD_CONTENT_TYPE_PLAIN, + array('pk_field1', 'pk_field2') + ) + ), + '$tableData' => array( + array('collection' => 'customer/attribute_collection', 'pk_field1' => 'pk_value1', 'pk_field2' => 'pk_value2'), + ), + '$expected' => array( + 'updates' => array( + array( + 'table' => 'table', + 'field' => 'collection', + 'to' => 'Mage_Customer_Model_Resource_Attribute_Collection', + 'from' => array('`pk_field1` = ?' => 'pk_value1', '`pk_field2` = ?' => 'pk_value2') + ), + ), + 'aliases_map' => array( + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_RESOURCE => array( + 'customer/attribute_collection' => 'Mage_Customer_Model_Resource_Attribute_Collection' + ), + ) + ), +); diff --git a/dev/tests/unit/testsuite/Mage/Core/Model/Resource/Setup/_files/data_content_plain_resource.php b/dev/tests/unit/testsuite/Mage/Core/Model/Resource/Setup/_files/data_content_plain_resource.php new file mode 100644 index 0000000000000..7e2817ba23119 --- /dev/null +++ b/dev/tests/unit/testsuite/Mage/Core/Model/Resource/Setup/_files/data_content_plain_resource.php @@ -0,0 +1,60 @@ + array( + array( + 'table', + 'collection', + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_RESOURCE, + Mage_Core_Model_Resource_Setup_Migration::FIELD_CONTENT_TYPE_PLAIN, + array(), + 'flag = 1' + ) + ), + '$tableData' => array( + array('collection' => 'customer/attribute_collection'), + ), + '$expected' => array( + 'updates' => array( + array( + 'table' => 'table', + 'field' => 'collection', + 'to' => 'Mage_Customer_Model_Resource_Attribute_Collection', + 'from' => array('`collection` = ?' => 'customer/attribute_collection') + ), + ), + 'where' => array( + 'flag = 1' + ), + 'aliases_map' => array( + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_RESOURCE => array( + 'customer/attribute_collection' => 'Mage_Customer_Model_Resource_Attribute_Collection' + ), + ) + ), +); diff --git a/dev/tests/unit/testsuite/Mage/Core/Model/Resource/Setup/_files/data_content_serialized.php b/dev/tests/unit/testsuite/Mage/Core/Model/Resource/Setup/_files/data_content_serialized.php new file mode 100644 index 0000000000000..a0b6b3c4eda04 --- /dev/null +++ b/dev/tests/unit/testsuite/Mage/Core/Model/Resource/Setup/_files/data_content_serialized.php @@ -0,0 +1,64 @@ + array( + array( + 'table', + 'field', + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_MODEL, + Mage_Core_Model_Resource_Setup_Migration::FIELD_CONTENT_TYPE_SERIALIZED + ) + ), + '$tableData' => array( + array('field' => 'a:1:{s:5:"model";s:34:"catalogrule/rule_condition_combine";}'), + array('field' => 'a:1:{s:5:"model";s:21:"catalogSearch/session";}'), + array('field' => 'a:1:{s:5:"model";s:16:"some random text";}'), + ), + '$expected' => array( + 'updates' => array( + array( + 'table' => 'table', + 'field' => 'field', + 'to' => 'a:1:{s:5:"model";s:45:"Mage_CatalogRule_Model_Rule_Condition_Combine";}', + 'from' => array('`field` = ?' => 'a:1:{s:5:"model";s:34:"catalogrule/rule_condition_combine";}') + ), + array( + 'table' => 'table', + 'field' => 'field', + 'to' => 'a:1:{s:5:"model";s:32:"Mage_CatalogSearch_Model_Session";}', + 'from' => array('`field` = ?' => 'a:1:{s:5:"model";s:21:"catalogSearch/session";}') + ), + ), + 'aliases_map' => array( + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_MODEL => array( + 'catalogrule/rule_condition_combine' => 'Mage_CatalogRule_Model_Rule_Condition_Combine', + 'catalogSearch/session' => 'Mage_CatalogSearch_Model_Session', + ) + ) + ), +); diff --git a/dev/tests/unit/testsuite/Mage/Core/Model/Resource/Setup/_files/data_content_wiki.php b/dev/tests/unit/testsuite/Mage/Core/Model/Resource/Setup/_files/data_content_wiki.php new file mode 100644 index 0000000000000..f27b43c253c5a --- /dev/null +++ b/dev/tests/unit/testsuite/Mage/Core/Model/Resource/Setup/_files/data_content_wiki.php @@ -0,0 +1,64 @@ + array( + array( + 'table', + 'field', + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_BLOCK, + Mage_Core_Model_Resource_Setup_Migration::FIELD_CONTENT_TYPE_WIKI + ) + ), + '$tableData' => array( + array('field' => '

{{widget type="productalert/product_view"}}

'), + array('field' => '

{{widget type="catalogSearch/result"}}

'), + array('field' => '

Some HTML code

'), + ), + '$expected' => array( + 'updates' => array( + array( + 'table' => 'table', + 'field' => 'field', + 'to' => '

{{widget type="Mage_ProductAlert_Block_Product_View"}}

', + 'from' => array('`field` = ?' => '

{{widget type="productalert/product_view"}}

') + ), + array( + 'table' => 'table', + 'field' => 'field', + 'to' => '

{{widget type="Mage_CatalogSearch_Block_Result"}}

', + 'from' => array('`field` = ?' => '

{{widget type="catalogSearch/result"}}

') + ), + ), + 'aliases_map' => array( + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_BLOCK => array( + 'productalert/product_view' => 'Mage_ProductAlert_Block_Product_View', + 'catalogSearch/result' => 'Mage_CatalogSearch_Block_Result', + ) + ) + ), +); diff --git a/dev/tests/unit/testsuite/Mage/Core/Model/Resource/Setup/_files/data_content_xml.php b/dev/tests/unit/testsuite/Mage/Core/Model/Resource/Setup/_files/data_content_xml.php new file mode 100644 index 0000000000000..c78b5a0ad938d --- /dev/null +++ b/dev/tests/unit/testsuite/Mage/Core/Model/Resource/Setup/_files/data_content_xml.php @@ -0,0 +1,64 @@ + array( + array( + 'table', + 'field', + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_BLOCK, + Mage_Core_Model_Resource_Setup_Migration::FIELD_CONTENT_TYPE_XML, + ) + ), + '$tableData' => array( + array('field' => ''), + array('field' => ''), + array('field' => ''), + ), + '$expected' => array( + 'updates' => array( + array( + 'table' => 'table', + 'field' => 'field', + 'to' => '', + 'from' => array('`field` = ?' => '') + ), + array( + 'table' => 'table', + 'field' => 'field', + 'to' => '', + 'from' => array('`field` = ?' => '') + ), + ), + 'aliases_map' => array( + Mage_Core_Model_Resource_Setup_Migration::ENTITY_TYPE_BLOCK => array( + 'catalog/product_new' => 'Mage_Catalog_Block_Product_New', + 'catalogSearch/result' => 'Mage_CatalogSearch_Block_Result', + ) + ) + ), +); diff --git a/dev/tests/unit/testsuite/Mage/Sales/Model/Config/OrderTest.php b/dev/tests/unit/testsuite/Mage/Sales/Model/Config/OrderTest.php new file mode 100644 index 0000000000000..de6ec5e4dcea8 --- /dev/null +++ b/dev/tests/unit/testsuite/Mage/Sales/Model/Config/OrderTest.php @@ -0,0 +1,118 @@ +getMockForAbstractClass('Mage_Sales_Model_Config_Ordered'); + + $method = new ReflectionMethod($mock, '_getSortedCollectorCodes'); + $method->setAccessible(true); + $actualResult = $method->invoke($mock, $totalConfig); + $this->assertEquals($expectedResult, $actualResult); + } + + public function getSortedCollectorCodesDataProvider() + { + $ambiguousCases = self::ambiguousTotalsDataProvider(); + return array_merge(array( + 'core totals' => array( + require __DIR__ . '/_files/core_totals_config.php', + array( + 'nominal', 'subtotal', 'freeshipping', 'tax_subtotal', 'shipping', 'tax_shipping', 'discount', + 'tax', 'grand_total', 'msrp', 'weee', + ) + ), + 'custom totals' => array( + require __DIR__ . '/_files/custom_totals_config.php', + array( + 'nominal', 'own_subtotal', 'own_total1', 'own_total2', 'subtotal', 'freeshipping', 'tax_subtotal', + 'shipping', 'tax_shipping', 'discount', 'handling', 'handling_tax', 'tax', 'grand_total', 'msrp', + 'weee', + ) + ), + ), $ambiguousCases); + } + + /** + * @dataProvider ambiguousTotalsDataProvider + * @expectedException Magento_Exception + */ + public function testValidateCollectorDeclarations($config) + { + Mage_Sales_Model_Config_Ordered::validateCollectorDeclarations($config); + } + + public function ambiguousTotalsDataProvider() + { + return array( + '"before" ambiguity 1' => array( + array( + 'total_one' => array('before' => array('total_two'), 'after' => array()), + 'total_two' => array('before' => array('total_one'), 'after' => array()), + ), + array('total_one', 'total_two'), + ), + '"before" ambiguity 2' => array( + array( + 'total_two' => array('before' => array('total_one'), 'after' => array()), + 'total_one' => array('before' => array('total_two'), 'after' => array()), + ), + array('total_two', 'total_one'), + ), + '"after" ambiguity 1' => array( + array( + 'total_one' => array('before' => array(), 'after' => array('total_two')), + 'total_two' => array('before' => array(), 'after' => array('total_one')), + ), + array('total_one', 'total_two'), + ), + '"after" ambiguity 2' => array( + array( + 'total_two' => array('before' => array(), 'after' => array('total_one')), + 'total_one' => array('before' => array(), 'after' => array('total_two')), + ), + array('total_two', 'total_one'), + ), + 'combined contradiction to itself' => array( + array( + 'one' => array('before' => array('two'), 'after' => array('two')), + 'two' => array('before' => array(), 'after' => array()), + ), + array('one', 'two'), + ), + 'combined contradiction across declarations' => array( + array( + 'one' => array('before' => array('two'), 'after' => array()), + 'two' => array('before' => array(), 'after' => array('three')), + 'three' => array('before' => array(), 'after' => array('two')), + ), + array('one', 'two', 'three'), + ), + ); + } +} diff --git a/dev/tests/unit/testsuite/Mage/Sales/Model/Config/_files/core_totals_config.php b/dev/tests/unit/testsuite/Mage/Sales/Model/Config/_files/core_totals_config.php new file mode 100644 index 0000000000000..439cd2ccaa3f2 --- /dev/null +++ b/dev/tests/unit/testsuite/Mage/Sales/Model/Config/_files/core_totals_config.php @@ -0,0 +1,77 @@ + array( + 'before' => array('subtotal'), + 'after' => array(), + ), + 'subtotal' => array( + 'after' => array('nominal'), + 'before' => array('grand_total'), + ), + 'shipping' => array( + 'after' => array('subtotal', 'freeshipping', 'tax_subtotal'), + 'before' => array('grand_total'), + ), + 'grand_total' => array( + 'after' => array('subtotal'), + 'before' => array(), + ), + 'msrp' => array( + 'after' => array(), + 'before' => array(), + ), + // Totals declared in Mage_SalesRule + 'freeshipping' => array( + 'after' => array('subtotal'), + 'before' => array('tax_subtotal', 'shipping'), + ), + 'discount' => array( + 'after' => array('subtotal', 'shipping'), + 'before' => array('grand_total'), + ), + // Totals declared in Mage_Tax + 'tax_subtotal' => array( + 'after' => array('freeshipping'), + 'before' => array('tax', 'discount'), + ), + 'tax_shipping' => array( + 'after' => array('shipping'), + 'before' => array('tax', 'discount'), + ), + 'tax' => array( + 'after' => array('subtotal', 'shipping'), + 'before' => array('grand_total'), + ), + // Totals declared in Mage_Weee + 'weee' => array( + 'after' => array('subtotal', 'tax', 'discount', 'grand_total', 'shipping'), + 'before' => array(), + ), +); diff --git a/dev/tests/unit/testsuite/Mage/Sales/Model/Config/_files/custom_totals_config.php b/dev/tests/unit/testsuite/Mage/Sales/Model/Config/_files/custom_totals_config.php new file mode 100644 index 0000000000000..2213e330b8efe --- /dev/null +++ b/dev/tests/unit/testsuite/Mage/Sales/Model/Config/_files/custom_totals_config.php @@ -0,0 +1,51 @@ + array( + 'after' => array('shipping'), + 'before' => array('tax'), + ), + 'handling_tax' => array( + 'after' => array('tax_shipping'), + 'before' => array('tax'), + ), + 'own_subtotal' => array( + 'after' => array('nominal'), + 'before' => array('subtotal'), + ), + 'own_total1' => array( + 'after' => array('nominal'), + 'before' => array('subtotal'), + ), + 'own_total2' => array( + 'after' => array('nominal'), + 'before' => array('subtotal'), + ), +); +return $result; diff --git a/dev/tests/unit/testsuite/Magento/Data/GraphTest.php b/dev/tests/unit/testsuite/Magento/Data/GraphTest.php new file mode 100644 index 0000000000000..151f4caf7d6f2 --- /dev/null +++ b/dev/tests/unit/testsuite/Magento/Data/GraphTest.php @@ -0,0 +1,123 @@ + array( + array(1, 2, 2), array() + ), + 'self-link' => array( + array(1, 2), array(array(1, 2), array(2, 2)) + ), + 'broken reference "from"' => array( + array(1, 2), array(array(1, 2), array(3, 1)) + ), + 'broken reference "to"' => array( + array(1, 2), array(array(1, 2), array(1, 3)) + ), + ); + } + + /** + * Exceptions are covered by testConstructorError() + */ + public function testAddRelation() + { + $model = new Magento_Data_Graph(array(1, 2, 3), array(array(1, 2), array(2, 3))); + $this->assertEquals(array(1 => array(2 => 2), 2 => array(3 => 3)), $model->getRelations()); + $this->assertSame($model, $model->addRelation(3, 1)); + $this->assertEquals(array(1 => array(2 => 2), 2 => array(3 => 3), 3 => array(1 => 1)), $model->getRelations()); + } + + public function testGetRelations() + { + // directional case is covered by testAddRelation() + + // inverse + $model = new Magento_Data_Graph(array(1, 2, 3), array(array(1, 2), array(2, 3))); + $this->assertEquals( + array(2 => array(1 => 1), 3 => array(2 => 2)), $model->getRelations(Magento_Data_Graph::INVERSE) + ); + + // non-directional + $this->assertEquals( + array(1 => array(2 => 2), 2 => array(1 => 1, 3 => 3), 3 => array(2 => 2)), + $model->getRelations(Magento_Data_Graph::NON_DIRECTIONAL) + ); + } + + public function testFindCycle() + { + $nodes = array(1, 2, 3, 4); + $model = new Magento_Data_Graph($nodes, array( + array(1, 2), array(2, 3), array(3, 4), + )); + $this->assertEquals(array(), $model->findCycle()); + + $model = new Magento_Data_Graph($nodes, array( + array(1, 2), array(2, 3), array(3, 4), array(4, 2) + )); + $this->assertEquals(array(), $model->findCycle(1)); + $cycle = $model->findCycle(); + sort($cycle); + $this->assertEquals(array(2, 2, 3, 4), $cycle); + $this->assertEquals(array(3, 4, 2, 3), $model->findCycle(3)); + } + + public function testDfs() + { + $model = new Magento_Data_Graph(array(1, 2, 3, 4, 5), array(array(1, 2), array(2, 3), array(4, 5))); + + // directional + $this->assertEquals(array(1, 2, 3), $model->dfs(1, 3)); + $this->assertEquals(array(), $model->dfs(3, 1)); + $this->assertEquals(array(4, 5), $model->dfs(4, 5)); + $this->assertEquals(array(), $model->dfs(1, 5)); + + // inverse + $this->assertEquals(array(3, 2, 1), $model->dfs(3, 1, Magento_Data_Graph::INVERSE)); + + // non-directional + $model = new Magento_Data_Graph(array(1, 2, 3), array(array(2, 1), array(2, 3))); + $this->assertEquals(array(), $model->dfs(1, 3, Magento_Data_Graph::DIRECTIONAL)); + $this->assertEquals(array(), $model->dfs(3, 1, Magento_Data_Graph::INVERSE)); + $this->assertEquals(array(1, 2, 3), $model->dfs(1, 3, Magento_Data_Graph::NON_DIRECTIONAL)); + } +} diff --git a/dev/tests/unit/testsuite/Magento/ShellTest.php b/dev/tests/unit/testsuite/Magento/ShellTest.php index 281414d324545..5e564abbe1c6b 100644 --- a/dev/tests/unit/testsuite/Magento/ShellTest.php +++ b/dev/tests/unit/testsuite/Magento/ShellTest.php @@ -40,27 +40,36 @@ public function testGetSetVerbose() } /** - * @dataProvider executeDataProvider * @param string $phpCommand - * @param bool $isVerbose - * @param string $expectedOutput - * @param string $expectedResult + * @param string $expectedRaw + * @param string $expectedFull + * @dataProvider executeDataProvider */ - public function testExecute($phpCommand, $isVerbose, $expectedOutput, $expectedResult = '') + public function testExecute($phpCommand, $expectedRaw, $expectedFull) { - $this->expectOutputString($expectedOutput); - $shell = new Magento_Shell($isVerbose); - $actualResult = $shell->execute('php -r %s', array($phpCommand)); - $this->assertEquals($expectedResult, $actualResult); + // non-verbose + $shell = new Magento_Shell(); + $rawResult = $shell->execute('php -r %s', array($phpCommand), $fullResult); + $this->assertEquals($expectedRaw, $rawResult); + $this->assertEquals($expectedFull, $fullResult); + + // verbose + $this->expectOutputString($expectedFull); + $shell = new Magento_Shell(true); + $shell->execute('php -r %s', array($phpCommand)); } public function executeDataProvider() { + $quote = escapeshellarg('\'""'); + $quote = $quote[0]; return array( - 'capture STDOUT' => array('echo 27182;', false, '', '27182'), - 'print STDOUT' => array('echo 27182;', true, '27182' . PHP_EOL, '27182'), - 'capture STDERR' => array('fwrite(STDERR, 27182);', false, '', '27182'), - 'print STDERR' => array('fwrite(STDERR, 27182);', true, '27182' . PHP_EOL, '27182'), + 'STDOUT' => array('echo 27182;', array('27182'), + "php -r {$quote}echo 27182;{$quote} 2>&1" . PHP_EOL . '27182' . PHP_EOL . PHP_EOL + ), + 'STDERR' => array('fwrite(STDERR, 27183);', array('27183'), + "php -r {$quote}fwrite(STDERR, 27183);{$quote} 2>&1" . PHP_EOL . '27183' . PHP_EOL . PHP_EOL + ), ); } @@ -76,20 +85,19 @@ public function testExecuteFailure() } /** - * @dataProvider executeDataProvider * @param string $phpCommand - * @param bool $isVerbose - * @param string $expectedOutput - * @param string $expectedError + * @param string $expectedRaw + * @param string $expectedFull + * @dataProvider executeDataProvider */ - public function testExecuteFailureDetails($phpCommand, $isVerbose, $expectedOutput, $expectedError = '') + public function testExecuteFailureDetails($phpCommand, $expectedRaw, $expectedFull) { try { /* Force command to return non-zero exit code */ - $this->testExecute($phpCommand . ' exit(42);', $isVerbose, $expectedOutput); + $this->testExecute($phpCommand . ' exit(42);', $expectedRaw, $expectedFull); } catch (Magento_Exception $e) { $this->assertInstanceOf('Exception', $e->getPrevious()); - $this->assertEquals($expectedError, $e->getPrevious()->getMessage()); + $this->assertEquals(implode(PHP_EOL, $expectedRaw), $e->getPrevious()->getMessage()); $this->assertEquals(42, $e->getPrevious()->getCode()); } } diff --git a/dev/tests/unit/testsuite/Varien/Io/FileTest.php b/dev/tests/unit/testsuite/Varien/Io/FileTest.php new file mode 100644 index 0000000000000..abaf5d891cb1b --- /dev/null +++ b/dev/tests/unit/testsuite/Varien/Io/FileTest.php @@ -0,0 +1,108 @@ +_prepare(); + if (substr(PHP_OS, 0, 3) == 'WIN') { + $this->markTestSkipped("chmod may not work for Windows"); + } + $permsBefore = 0700; + $expected = 0777; + $this->assertEquals($permsBefore, fileperms($this->_dir) & $permsBefore, + "Wrong permissions set for " . $this->_dir); + $this->assertEquals($permsBefore, fileperms($this->_file) & $permsBefore, + "Wrong permissions set for " . $this->_file); + Varien_Io_File::chmodRecursive($this->_dir, $expected); + $this->assertEquals($expected, fileperms($this->_dir) & $expected, + "Directory permissions were changed incorrectly."); + $this->assertEquals($expected, fileperms($this->_file) & $expected, + "File permissions were changed incorrectly."); + } catch (Exception $e) { + } + + $this->_cleanup(); + if (isset($e)) { + throw $e; + } + } + + public function testRmdirRecursive() + { + try { + $this->_prepare(); + $this->assertFileExists($this->_file); + Varien_Io_File::rmdirRecursive($this->_dir); + $this->assertFileNotExists($this->_dir); + } catch (Exception $e) { + } + + $this->_cleanup(); + if (isset($e)) { + throw $e; + } + } + + /** + * Create files for tests + */ + protected function _prepare() + { + $this->_dir = __DIR__ . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'directory'; + $this->_file = $this->_dir . DIRECTORY_SEPARATOR . 'file.txt'; + @mkdir($this->_dir, 0700, true); + if (@touch($this->_file)) { + chmod($this->_file, 0700); + } + } + + /** + * Remove fixture files + */ + protected function _cleanup() + { + if (file_exists($this->_file)) { + @unlink($this->_file); + } + if (file_exists($this->_dir)) { + @rmdir($this->_dir); + } + } +} diff --git a/dev/tools/migration/aliases_map/composite_modules_ce.php b/dev/tools/migration/aliases_map/composite_modules_ce.php new file mode 100644 index 0000000000000..e0ddb48bcf530 --- /dev/null +++ b/dev/tools/migration/aliases_map/composite_modules_ce.php @@ -0,0 +1,36 @@ +./get_aliases_map.php -- [-ph] + Build Magento 1 Aliases To Magento 2 Classes Names. + Additional parameters: + -p path to code scope of magento instance + -h print usage + +USAGE +); + +$options = getopt('p:h'); + +if (isset($options['h'])) { + print USAGE; + exit(0); +} + +require_once realpath(dirname(dirname(dirname(__DIR__)))) . '/dev/tests/static/framework/bootstrap.php'; +require_once realpath(dirname(dirname(dirname(__DIR__)))) . '/lib/Zend/Json.php'; + +$magentoBaseDir = dirname(__DIR__) . '/../../'; +if (isset($options['p'])) { + $magentoBaseDir = $options['p']; +} + +$utilityFiles = new Utility_Files($magentoBaseDir); +$map = array(); +$compositeModules = getFilesCombinedArray(dirname(__FILE__) . '/aliases_map', '/^composite_modules_.*\.php$/'); +// PHP code +foreach ($utilityFiles->getPhpFiles(true, true, true, false) as $file) { + $content = file_get_contents($file); + $classes = Legacy_ClassesTest::collectPhpCodeClasses($content); + if ($classes) { + $factoryNames = array_filter($classes, 'isFactoryName'); + foreach ($factoryNames as $factoryName) { + list($module, $name) = getModuleName($factoryName, $compositeModules); + $patterns = array( + '::getModel(\'%s\'' => 'Model', + '::getSingleton(\'%s\'' => 'Model', + '::getResourceModel(\'%s\'' => 'Model_Resource', + '::getResourceSingleton(\'%s\'' => 'Model_Resource', + 'addBlock(\'%s\'' => 'Block', + 'createBlock(\'%s\'' => 'Block', + 'getBlockClassName(\'%s\'' => 'Block', + 'getBlockSingleton(\'%s\'' => 'Block' + ); + + foreach ($patterns as $pattern => $classType) { + if (isPatternExist($content, $pattern, $factoryName)) { + if (!isset($map[$classType])) { + $map[$classType] = array(); + } + + $map[$classType][$factoryName] = getClassName($module, $classType, $name); + } + } + } + } +} + +// layouts +$classType = 'Block'; +$layouts = $utilityFiles->getLayoutFiles(array(), false); +foreach ($layouts as $file) { + $xml = simplexml_load_file($file); + $classes = Utility_Classes::collectLayoutClasses($xml); + $factoryNames = array_filter($classes, 'isFactoryName'); + if (!$factoryNames) { + continue; + } + foreach ($factoryNames as $factoryName) { + list($module, $name) = getModuleName($factoryName, $compositeModules); + $map[$classType][$factoryName] = getClassName($module, $classType, $name); + } +} + +echo Zend_Json::prettyPrint(Zend_Json::encode($map)); + +/** + * Get combined array from similar files by pattern + * + * @param string $dirPath + * @param string $filePattern + * @return array + */ +function getFilesCombinedArray($dirPath, $filePattern) +{ + $result = array(); + $directoryIterator = new DirectoryIterator($dirPath); + $patternIterator = new RegexIterator($directoryIterator, $filePattern); + + foreach ($patternIterator as $fileInfo) { + $arrayFromFile = include_once($fileInfo->getPathname()); + $result = array_merge($result, $arrayFromFile); + } + return $result; +} + +/** + * Check if pattern exist in file content + * + * @param string $content + * @param string $pattern + * @param string $alias + * @return bool + */ +function isPatternExist($content, $pattern, $alias) +{ + $search = sprintf($pattern, $alias); + return strpos($content, $search) !== false; +} + +/** + * Build class name supported in magento 2 + * + * @param string $module + * @param string $type + * @param string $name + * @return string|bool + */ +function getClassName($module, $type, $name = null) +{ + if (empty($name)) { + if ('Helper' !== $type) { + return false; + } + $name = 'data'; + } + + return implode('_', array_map('ucfirst', explode('_', $module . '_' . $type . '_' . $name))); +} + +/** + * Whether the given class name is a factory name + * + * @param string $class + * @return bool + */ +function isFactoryName($class) +{ + return false !== strpos($class, '/') || preg_match('/^[a-z\d]+(_[A-Za-z\d]+)?$/', $class); +} + +/** + * Transform factory name into a pair of module and name + * + * @param string $factoryName + * @return array + */ +function getModuleName($factoryName, $compositeModules = array()) +{ + if (false !== strpos($factoryName, '/')) { + list($module, $name) = explode('/', $factoryName); + } else { + $module = $factoryName; + $name = false; + } + if (array_key_exists($module, $compositeModules)) { + $module = $compositeModules[$module]; + } elseif (false === strpos($module, '_')) { + $module = "Mage_{$module}"; + } + return array($module, $name); +} diff --git a/lib/Magento/Data/Graph.php b/lib/Magento/Data/Graph.php new file mode 100644 index 0000000000000..636f748cba6da --- /dev/null +++ b/lib/Magento/Data/Graph.php @@ -0,0 +1,195 @@ +_assertNode($node, false); + $this->_nodes[$node] = $node; + } + foreach ($relations as $pair) { + list($from, $to) = $pair; + $this->addRelation($from, $to); + } + } + + /** + * Set a relation between nodes + * + * @param string|int $from + * @param string|int $to + * @return Magento_Data_Graph + * @throws InvalidArgumentException + */ + public function addRelation($from, $to) + { + if ($from == $to) { + throw new InvalidArgumentException("Graph node '{$from}' is linked to itself."); + } + $this->_assertNode($from, true); + $this->_assertNode($to, true); + $this->_from[$from][$to] = $to; + $this->_to[$to][$from] = $from; + return $this; + } + + /** + * Export relations between nodes. Can return inverse relations + * + * @param int $mode + * @return array + * @throws InvalidArgumentException + */ + public function getRelations($mode = self::DIRECTIONAL) + { + switch ($mode) { + case self::DIRECTIONAL: + return $this->_from; + case self::INVERSE: + return $this->_to; + case self::NON_DIRECTIONAL: + $graph = $this->_from; + foreach ($this->_to as $idTo => $relations) { + foreach ($relations as $idFrom) { + $graph[$idTo][$idFrom] = $idFrom; + } + } + return $graph; + default: + throw new InvalidArgumentException("Unknown search mode: '{$mode}'"); + } + } + + /** + * Find a cycle in the graph + * + * Returns first found cycle + * Optionally may specify a node to return a cycle if it is in any + * + * @param string|int $node + * @return array + */ + public function findCycle($node = null) + { + $nodes = (null === $node) ? $this->_nodes : array($node); + $result = array(); + foreach ($nodes as $node) { + $result = $this->dfs($node, $node); + if ($result) { + break; + } + } + return $result; + } + + /** + * "Depth-first search" of a path between nodes + * + * Returns path as array of nodes or empty array if path does not exist. + * Only first found path is returned. It will be not necessary the shortest or optimal in any way. + * + * @param string|int $from + * @param string|int $to + * @param int $mode + * @return array + */ + public function dfs($from, $to, $mode = self::DIRECTIONAL) + { + $this->_assertNode($from, true); + $this->_assertNode($to, true); + return $this->_dfs($from, $to, $this->getRelations($mode)); + } + + /** + * Recursive sub-routine of dfs() + * + * @param string|int $from + * @param string|int $to + * @param array $graph + * @param array &$visited + * @param array $stack + * @return array + * @link http://en.wikipedia.org/wiki/Depth-first_search + */ + protected function _dfs($from, $to, $graph, &$visited = array(), $stack = array()) + { + $stack[] = $from; + $visited[$from] = $from; + if (isset($graph[$from][$to])) { + $stack[] = $to; + return $stack; + } + if (isset($graph[$from])) { + foreach ($graph[$from] as $node) { + if (!isset($visited[$node])) { + $result = $this->_dfs($node, $to, $graph, $visited, $stack); + if ($result) { + return $result; + } + } + } + } + return array(); + } + + /** + * Verify existence or non-existence of a node + * + * @param string|int $node + * @param bool $mustExist + * @throws InvalidArgumentException according to assertion rules + */ + protected function _assertNode($node, $mustExist) + { + if (isset($this->_nodes[$node])) { + if (!$mustExist) { + throw new InvalidArgumentException("Graph node '{$node}' already exists'."); + } + } else { + if ($mustExist) { + throw new InvalidArgumentException("Graph node '{$node}' does not exist."); + } + } + } +} diff --git a/lib/Magento/Shell.php b/lib/Magento/Shell.php index 43f7b8f841523..61cefd0d9c25e 100644 --- a/lib/Magento/Shell.php +++ b/lib/Magento/Shell.php @@ -73,23 +73,30 @@ public function getVerbose() * * @param string $command Command with optional argument markers '%s' * @param array $arguments Argument values to substitute markers with - * @return string - * @throws Magento_Exception + * @param string &$fullOutput A string to dump all actual output + * @return array raw output from exec() PHP-function (the second argument) + * @throws Magento_Exception if exit code is other than zero */ - public function execute($command, array $arguments = array()) + public function execute($command, array $arguments = array(), &$fullOutput = '') { $arguments = array_map('escapeshellarg', $arguments); - $command = vsprintf($command, $arguments); - /* Output errors to STDOUT instead of STDERR */ - exec("$command 2>&1", $output, $exitCode); - $output = implode(PHP_EOL, $output); + $rawCommand = vsprintf("{$command} 2>&1", $arguments); // Output errors to STDOUT instead of STDERR + $output = $rawCommand . PHP_EOL; + $fullOutput .= $output; if ($this->_isVerbose) { - echo $output . PHP_EOL; + echo $output; + } + exec($rawCommand, $rawOutput, $exitCode); + $rawOutputStr = implode(PHP_EOL, $rawOutput); + $output = $rawOutputStr . PHP_EOL . PHP_EOL; + $fullOutput .= $output; + if ($this->_isVerbose) { + echo $output; } if ($exitCode) { - $commandError = new Exception($output, $exitCode); + $commandError = new Exception($rawOutputStr, $exitCode); throw new Magento_Exception("Command `$command` returned non-zero exit code.", 0, $commandError); } - return $output; + return $rawOutput; } } diff --git a/lib/Varien/Data/Form/Element/Editor.php b/lib/Varien/Data/Form/Element/Editor.php index e6c219cc2ad33..3c09a52dafff4 100644 --- a/lib/Varien/Data/Form/Element/Editor.php +++ b/lib/Varien/Data/Form/Element/Editor.php @@ -107,10 +107,8 @@ public function getElementHtml() . $this->serialize($this->getHtmlAttributes()) . ' >' . $this->getEscapedValue() . '' . $js . '