Mage_Core_Modules - Version 1.9.3.0

Version Notes

1.9.3.0

Download this release

Release Info

Developer Magento Core Team
Extension Mage_Core_Modules
Version 1.9.3.0
Comparing to
See all releases


Code changes from version 1.9.2.4 to 1.9.3.0

Files changed (178) hide show
  1. .htaccess.sample +15 -0
  2. RELEASE_NOTES.txt +11 -1
  3. api.php +5 -2
  4. app/Mage.php +2 -2
  5. app/code/core/Mage/Admin/Model/User.php +3 -3
  6. app/code/core/Mage/Admin/etc/config.xml +1 -1
  7. app/code/core/Mage/Api/Model/Server/Handler/Abstract.php +47 -6
  8. app/code/core/Mage/Bundle/Block/Adminhtml/Catalog/Product/Edit/Tab/Attributes.php +1 -0
  9. app/code/core/Mage/Bundle/Block/Catalog/Product/View/Type/Bundle/Option.php +7 -2
  10. app/code/core/Mage/Catalog/Block/Product/Abstract.php +11 -6
  11. app/code/core/Mage/Catalog/Block/Product/List.php +2 -5
  12. app/code/core/Mage/Catalog/Block/Product/View.php +6 -61
  13. app/code/core/Mage/Catalog/Block/Product/View/Type/Configurable.php +33 -26
  14. app/code/core/Mage/Catalog/Helper/Image.php +8 -1
  15. app/code/core/Mage/Catalog/Helper/Product.php +40 -0
  16. app/code/core/Mage/Catalog/Helper/Product/Type/Composite.php +195 -0
  17. app/code/core/Mage/Catalog/Model/Product/Attribute/Backend/Groupprice/Abstract.php +6 -3
  18. app/code/core/Mage/Catalog/Model/Product/Attribute/Backend/Media.php +4 -1
  19. app/code/core/Mage/Catalog/Model/Product/Link/Api/V2.php +4 -4
  20. app/code/core/Mage/Catalog/Model/Resource/Eav/Attribute.php +1 -1
  21. app/code/core/Mage/Catalog/Model/Resource/Product/Attribute/Backend/Groupprice.php +13 -0
  22. app/code/core/Mage/Catalog/Model/Resource/Product/Indexer/Eav/Source.php +3 -3
  23. app/code/core/Mage/Catalog/Model/Resource/Product/Link/Product/Collection.php +48 -2
  24. app/code/core/Mage/Catalog/Model/Resource/Product/Type/Configurable/Attribute/Collection.php +11 -6
  25. app/code/core/Mage/Catalog/data/catalog_setup/data-upgrade-1.6.0.0.19.1.3-1.6.0.0.19.1.4.php +77 -0
  26. app/code/core/Mage/Catalog/etc/config.xml +2 -1
  27. app/code/core/Mage/Catalog/etc/system.xml +9 -0
  28. app/code/core/Mage/Catalog/sql/catalog_setup/upgrade-1.6.0.0.19.1.2-1.6.0.0.19.1.3.php +44 -0
  29. app/code/core/Mage/Catalog/sql/catalog_setup/upgrade-1.6.0.0.19.1.4-1.6.0.0.19.1.5.php +37 -0
  30. app/code/core/Mage/CatalogInventory/Model/Observer.php +1 -0
  31. app/code/core/Mage/CatalogInventory/Model/Stock/Item.php +21 -0
  32. app/code/core/Mage/CatalogRule/Model/Action/Index/Refresh.php +26 -4
  33. app/code/core/Mage/CatalogSearch/Model/Resource/Advanced.php +1 -1
  34. app/code/core/Mage/CatalogSearch/Model/Resource/Fulltext.php +40 -42
  35. app/code/core/Mage/CatalogSearch/Model/Resource/Fulltext/Collection.php +173 -13
  36. app/code/core/Mage/CatalogSearch/Model/Resource/Helper/Mysql4.php +16 -1
  37. app/code/core/Mage/Checkout/Model/Cart.php +9 -6
  38. app/code/core/Mage/Checkout/Model/Type/Multishipping.php +3 -0
  39. app/code/core/Mage/Checkout/Model/Type/Onepage.php +16 -0
  40. app/code/core/Mage/Checkout/controllers/CartController.php +26 -1
  41. app/code/core/Mage/Checkout/controllers/OnepageController.php +22 -10
  42. app/code/core/Mage/Cms/Block/Page.php +24 -2
  43. app/code/core/Mage/Cms/Helper/Wysiwyg/Images.php +2 -1
  44. app/code/core/Mage/Cms/Model/Wysiwyg/Images/Storage.php +5 -4
  45. app/code/core/Mage/ConfigurableSwatches/Block/Catalog/Product/List/Price.php +95 -0
  46. app/code/core/Mage/ConfigurableSwatches/Helper/Data.php +27 -1
  47. app/code/core/Mage/ConfigurableSwatches/Helper/List/Price.php +118 -0
  48. app/code/core/Mage/ConfigurableSwatches/Helper/Mediafallback.php +52 -10
  49. app/code/core/Mage/ConfigurableSwatches/Model/Observer.php +14 -7
  50. app/code/core/Mage/ConfigurableSwatches/Model/Resource/Catalog/Product/Attribute/Super/Collection.php +52 -33
  51. app/code/core/Mage/ConfigurableSwatches/etc/system.xml +9 -0
  52. app/code/core/Mage/Core/Block/Abstract.php +11 -1
  53. app/code/core/Mage/Core/Block/Template.php +1 -1
  54. app/code/core/Mage/Core/Controller/Varien/Action.php +1 -0
  55. app/code/core/Mage/Core/Helper/String.php +41 -1
  56. app/code/core/Mage/Core/Helper/Url.php +1 -1
  57. app/code/core/Mage/Core/Model/Config.php +3 -0
  58. app/code/core/Mage/Core/Model/Email/Queue.php +0 -2
  59. app/code/core/Mage/Core/Model/Email/Template.php +1 -0
  60. app/code/core/Mage/Core/Model/Email/Template/Abstract.php +15 -31
  61. app/code/core/Mage/Core/Model/Encryption.php +2 -2
  62. app/code/core/Mage/Core/Model/File/Storage/Abstract.php +3 -3
  63. app/code/core/Mage/Core/Model/File/Validator/AvailablePath.php +6 -6
  64. app/code/core/Mage/Core/Model/Input/Filter/MaliciousCode.php +7 -1
  65. app/code/core/Mage/Core/Model/Layout.php +1 -1
  66. app/code/core/Mage/Core/Model/Resource/Url/Rewrite.php +3 -3
  67. app/code/core/Mage/Core/Model/Resource/Variable/Collection.php +1 -1
  68. app/code/core/Mage/Core/Model/Session/Abstract/Varien.php +21 -0
  69. app/code/core/Mage/Core/etc/config.xml +8 -0
  70. app/code/core/Mage/Core/etc/jstranslator.xml +2 -2
  71. app/code/core/Mage/Core/etc/system.xml +1 -1
  72. app/code/core/Mage/Core/functions.php +35 -0
  73. app/code/core/Mage/Cron/Model/Schedule.php +5 -1
  74. app/code/core/Mage/Customer/Block/Address/Book.php +2 -1
  75. app/code/core/Mage/Customer/Helper/Data.php +37 -0
  76. app/code/core/Mage/Customer/Model/Customer.php +66 -8
  77. app/code/core/Mage/Customer/Model/Flowpassword.php +121 -0
  78. app/code/core/Mage/Customer/Model/Observer.php +13 -1
  79. app/code/core/Mage/Customer/Model/Resource/Flowpassword.php +44 -0
  80. app/code/core/Mage/Customer/Model/Resource/Flowpassword/Collection.php +44 -0
  81. app/code/core/Mage/Customer/controllers/AccountController.php +55 -25
  82. app/code/core/Mage/Customer/controllers/AddressController.php +3 -0
  83. app/code/core/Mage/Customer/data/customer_setup/data-upgrade-1.6.2.0.4-1.6.2.0.5.php +78 -0
  84. app/code/core/Mage/Customer/etc/config.xml +33 -2
  85. app/code/core/Mage/Customer/etc/system.xml +64 -1
  86. app/code/core/Mage/Customer/sql/customer_setup/upgrade-1.6.2.0.4-1.6.2.0.5.php +58 -0
  87. app/code/core/Mage/Dataflow/Model/Profile.php +14 -4
  88. app/code/core/Mage/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/Links.php +49 -21
  89. app/code/core/Mage/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/Samples.php +49 -24
  90. app/code/core/Mage/Downloadable/Helper/File.php +52 -612
  91. app/code/core/Mage/Eav/Block/Adminhtml/Attribute/Edit/Options/Abstract.php +2 -3
  92. app/code/core/Mage/Eav/Model/Entity/Abstract.php +10 -5
  93. app/code/core/Mage/Eav/Model/Entity/Attribute.php +2 -1
  94. app/code/core/Mage/Eav/Model/Entity/Attribute/Abstract.php +11 -2
  95. app/code/core/Mage/Eav/Model/Entity/Attribute/Source/Table.php +2 -2
  96. app/code/core/Mage/Eav/Model/Entity/Collection/Abstract.php +2 -0
  97. app/code/core/Mage/ImportExport/Helper/Data.php +13 -2
  98. app/code/core/Mage/ImportExport/Model/Export.php +47 -1
  99. app/code/core/Mage/ImportExport/Model/Export/Adapter/Abstract.php +17 -0
  100. app/code/core/Mage/ImportExport/Model/Export/Adapter/Csv.php +2 -0
  101. app/code/core/Mage/ImportExport/Model/Export/Entity/Abstract.php +58 -2
  102. app/code/core/Mage/ImportExport/Model/Export/Entity/Customer.php +312 -73
  103. app/code/core/Mage/ImportExport/Model/Export/Entity/Product.php +161 -88
  104. app/code/core/Mage/ImportExport/Model/Export/Entity/Product/Type/Abstract.php +1 -1
  105. app/code/core/Mage/ImportExport/Model/Import/Entity/Customer.php +62 -8
  106. app/code/core/Mage/ImportExport/Model/Import/Entity/Customer/Address.php +76 -36
  107. app/code/core/Mage/ImportExport/Model/Import/Entity/Product.php +118 -46
  108. app/code/core/Mage/ImportExport/Model/Import/Entity/Product/Type/Configurable.php +27 -11
  109. app/code/core/Mage/ImportExport/Model/Import/Uploader.php +1 -1
  110. app/code/core/Mage/ImportExport/controllers/Adminhtml/ExportController.php +4 -1
  111. app/code/core/Mage/ImportExport/etc/config.xml +3 -0
  112. app/code/core/Mage/ImportExport/etc/system.xml +19 -0
  113. app/code/core/Mage/Oauth/Model/Server.php +3 -3
  114. app/code/core/Mage/Paygate/Model/Authorizenet.php +9 -3
  115. app/code/core/Mage/Payment/Block/Info/Checkmo.php +8 -2
  116. app/code/core/Mage/Payment/Model/Method/Cc.php +1 -1
  117. app/code/core/Mage/Paypal/Model/Api/Nvp.php +2 -1
  118. app/code/core/Mage/Paypal/Model/Express/Checkout.php +23 -1
  119. app/code/core/Mage/Paypal/Model/Resource/Payment/Transaction.php +20 -0
  120. app/code/core/Mage/Persistent/Model/Persistent/Config.php +3 -1
  121. app/code/core/Mage/Reports/Model/Product/Index/Abstract.php +20 -2
  122. app/code/core/Mage/Reports/Model/Resource/Helper/Mysql4.php +29 -7
  123. app/code/core/Mage/Rss/Controller/Abstract.php +77 -0
  124. app/code/core/Mage/Rss/controllers/CatalogController.php +26 -38
  125. app/code/core/Mage/Rss/controllers/IndexController.php +2 -15
  126. app/code/core/Mage/Rss/controllers/OrderController.php +22 -18
  127. app/code/core/Mage/Rss/data/rss_setup/data-install-1.6.0.0.php +34 -0
  128. app/code/core/Mage/Rss/etc/config.xml +7 -3
  129. app/code/core/Mage/Rss/etc/system.xml +84 -5
  130. app/code/core/Mage/Sales/Helper/Guest.php +1 -1
  131. app/code/core/Mage/Sales/Model/Email/Template.php +1 -1
  132. app/code/core/Mage/Sales/Model/Order.php +17 -3
  133. app/code/core/Mage/Sales/Model/Order/Invoice/Total/Subtotal.php +4 -36
  134. app/code/core/Mage/Sales/Model/Quote/Address/Total/Subtotal.php +1 -1
  135. app/code/core/Mage/Sales/Model/Quote/Item.php +1 -1
  136. app/code/core/Mage/Sales/Model/Quote/Item/Abstract.php +4 -1
  137. app/code/core/Mage/Sales/Model/Resource/Order/Payment.php +24 -0
  138. app/code/core/Mage/Sales/Model/Resource/Order/Payment/Transaction.php +24 -0
  139. app/code/core/Mage/Sales/Model/Resource/Quote.php +2 -2
  140. app/code/core/Mage/Sales/Model/Resource/Quote/Payment.php +20 -0
  141. app/code/core/Mage/Sales/Model/Resource/Recurring/Profile.php +27 -0
  142. app/code/core/Mage/Sales/Model/Resource/Report/Bestsellers.php +3 -8
  143. app/code/core/Mage/Sales/Model/Resource/Report/Bestsellers/Collection.php +19 -6
  144. app/code/core/Mage/Sales/etc/config.xml +2 -1
  145. app/code/core/Mage/Sales/sql/sales_setup/upgrade-1.6.0.9-1.6.0.10.php +51 -0
  146. app/code/core/Mage/SalesRule/Model/Rule/Condition/Product.php +1 -1
  147. app/code/core/Mage/SalesRule/Model/Rule/Condition/Product/Combine.php +16 -1
  148. app/code/core/Mage/SalesRule/Model/Validator.php +182 -188
  149. app/code/core/Mage/SalesRule/etc/config.xml +2 -2
  150. app/code/core/Mage/Sitemap/Model/Sitemap.php +4 -2
  151. app/code/core/Mage/Tax/etc/config.xml +2 -2
  152. app/code/core/Mage/Uploader/Block/Abstract.php +247 -0
  153. app/code/core/Mage/Uploader/Block/Multiple.php +71 -0
  154. app/code/core/Mage/Uploader/Block/Single.php +52 -0
  155. app/code/core/Mage/Uploader/Helper/Data.php +35 -0
  156. app/code/core/Mage/Uploader/Helper/File.php +758 -0
  157. app/code/core/Mage/Uploader/Model/Config/Abstract.php +76 -0
  158. app/code/core/Mage/Uploader/Model/Config/Browsebutton.php +73 -0
  159. app/code/core/Mage/Uploader/Model/Config/Misc.php +54 -0
  160. app/code/core/Mage/Uploader/Model/Config/Uploader.php +128 -0
  161. app/code/core/Mage/Uploader/etc/config.xml +73 -0
  162. app/code/core/Mage/Uploader/etc/jstranslator.xml +45 -0
  163. app/code/core/Mage/Usa/Model/Shipping/Carrier/Dhl.php +4 -4
  164. app/code/core/Mage/Usa/Model/Shipping/Carrier/Dhl/International.php +18 -3
  165. app/code/core/Mage/Usa/Model/Shipping/Carrier/Fedex.php +3 -2
  166. app/code/core/Mage/Usa/Model/Shipping/Carrier/Ups.php +3 -3
  167. app/code/core/Mage/Usa/Model/Shipping/Carrier/Usps.php +6 -0
  168. app/code/core/Mage/Usa/etc/config.xml +5 -1
  169. app/code/core/Mage/Usa/etc/system.xml +27 -0
  170. app/code/core/Mage/Weee/Model/Observer.php +4 -1
  171. app/code/core/Mage/Wishlist/Block/Customer/Wishlist/Item/Options.php +6 -3
  172. app/code/core/Mage/Wishlist/Helper/Data.php +4 -1
  173. app/code/core/Mage/Wishlist/controllers/IndexController.php +3 -0
  174. app/code/core/Zend/Validate/Hostname.php +1684 -0
  175. app/etc/modules/Mage_All.xml +8 -1
  176. cron.php +1 -1
  177. errors/processor.php +21 -9
  178. package.xml +5 -5
.htaccess.sample CHANGED
@@ -127,6 +127,21 @@
127
 
128
  RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
129
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  ############################################
131
  ## always send 404 on missing files in these folders
132
 
127
 
128
  RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
129
 
130
+ <IfModule mod_setenvif.c>
131
+ <IfModule mod_headers.c>
132
+
133
+ ############################################
134
+ # X-Content-Type-Options: nosniff disable content-type sniffing on some browsers.
135
+ Header set X-Content-Type-Options: nosniff
136
+
137
+ ############################################
138
+ # This header forces to enables the Cross-site scripting (XSS) filter in browsers (if disabled)
139
+ BrowserMatch \bMSIE\s8 ie8
140
+ Header set X-XSS-Protection: "1; mode=block" env=!ie8
141
+
142
+ </IfModule>
143
+ </IfModule>
144
+
145
  ############################################
146
  ## always send 404 on missing files in these folders
147
 
RELEASE_NOTES.txt CHANGED
@@ -1,3 +1,13 @@
 
 
 
 
 
 
 
 
 
 
1
  ==== 1.9.2.4 ====
2
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -53,7 +63,7 @@
53
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
54
  ] NOTE: Current Release Notes are maintained at: [
55
  ] [
56
- ] http://www.magentocommerce.com/knowledge-base/entry/ce-19-later-release-notes [
57
  ] [
58
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
59
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1
+ ==== 1.9.3.0 ====
2
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
+ ] NOTE: Current Release Notes are maintained at: [
5
+ ] [
6
+ ] http://merch.docs.magento.com/ce/user_guide/magento/release-notes-ce-1.9.3.0.html [
7
+ ] [
8
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
9
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
10
+
11
  ==== 1.9.2.4 ====
12
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
13
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
63
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
64
  ] NOTE: Current Release Notes are maintained at: [
65
  ] [
66
+ ] http://devdocs.magento.com/guides/m1x/ce18-ee113/ce1.9_release-notes.html [
67
  ] [
68
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
69
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
api.php CHANGED
@@ -74,8 +74,11 @@ if (in_array($apiAlias, Mage_Api2_Model_Server::getApiTypes())) {
74
  } else {
75
  /* @var $server Mage_Api_Model_Server */
76
  $server = Mage::getSingleton('api/server');
77
- $adapterCode = $server->getAdapterCodeByAlias($apiAlias);
78
-
 
 
 
79
  // if no adapters found in aliases - find it by default, by code
80
  if (null === $adapterCode) {
81
  $adapterCode = $apiAlias;
74
  } else {
75
  /* @var $server Mage_Api_Model_Server */
76
  $server = Mage::getSingleton('api/server');
77
+ if (!$apiAlias) {
78
+ $adapterCode = 'default';
79
+ } else {
80
+ $adapterCode = $server->getAdapterCodeByAlias($apiAlias);
81
+ }
82
  // if no adapters found in aliases - find it by default, by code
83
  if (null === $adapterCode) {
84
  $adapterCode = $apiAlias;
app/Mage.php CHANGED
@@ -170,8 +170,8 @@ final class Mage
170
  return array(
171
  'major' => '1',
172
  'minor' => '9',
173
- 'revision' => '2',
174
- 'patch' => '4',
175
  'stability' => '',
176
  'number' => '',
177
  );
170
  return array(
171
  'major' => '1',
172
  'minor' => '9',
173
+ 'revision' => '3',
174
+ 'patch' => '0',
175
  'stability' => '',
176
  'number' => '',
177
  );
app/code/core/Mage/Admin/Model/User.php CHANGED
@@ -640,8 +640,8 @@ class Mage_Admin_Model_User extends Mage_Core_Model_Abstract
640
  return true;
641
  }
642
 
643
- $dayDifference = floor(($currentTimestamp - $tokenTimestamp) / (24 * 60 * 60));
644
- if ($dayDifference >= $tokenExpirationPeriod) {
645
  return true;
646
  }
647
 
@@ -665,7 +665,7 @@ class Mage_Admin_Model_User extends Mage_Core_Model_Abstract
665
  /**
666
  * Simple sql format date
667
  *
668
- * @param string $format
669
  * @return string
670
  */
671
  protected function _getDateNow($dayOnly = false)
640
  return true;
641
  }
642
 
643
+ $hoursDifference = floor(($currentTimestamp - $tokenTimestamp) / (60 * 60));
644
+ if ($hoursDifference >= $tokenExpirationPeriod) {
645
  return true;
646
  }
647
 
665
  /**
666
  * Simple sql format date
667
  *
668
+ * @param string | boolean $dayOnly
669
  * @return string
670
  */
671
  protected function _getDateNow($dayOnly = false)
app/code/core/Mage/Admin/etc/config.xml CHANGED
@@ -85,7 +85,7 @@
85
  <emails>
86
  <forgot_email_template>admin_emails_forgot_email_template</forgot_email_template>
87
  <forgot_email_identity>general</forgot_email_identity>
88
- <password_reset_link_expiration_period>1</password_reset_link_expiration_period>
89
  </emails>
90
  </admin>
91
  </default>
85
  <emails>
86
  <forgot_email_template>admin_emails_forgot_email_template</forgot_email_template>
87
  <forgot_email_identity>general</forgot_email_identity>
88
+ <password_reset_link_expiration_period>2</password_reset_link_expiration_period>
89
  </emails>
90
  </admin>
91
  </default>
app/code/core/Mage/Api/Model/Server/Handler/Abstract.php CHANGED
@@ -288,13 +288,15 @@ abstract class Mage_Api_Model_Server_Handler_Abstract
288
  }
289
 
290
  if (method_exists($model, $method)) {
 
291
  if (isset($methodInfo->arguments) && ((string)$methodInfo->arguments) == 'array') {
292
- return $model->$method((is_array($args) ? $args : array($args)));
293
  } elseif (!is_array($args)) {
294
- return $model->$method($args);
295
  } else {
296
- return call_user_func_array(array(&$model, $method), $args);
297
  }
 
298
  } else {
299
  throw new Mage_Api_Exception('resource_path_not_callable');
300
  }
@@ -401,13 +403,15 @@ abstract class Mage_Api_Model_Server_Handler_Abstract
401
  }
402
 
403
  if (method_exists($model, $method)) {
 
404
  if (isset($methodInfo->arguments) && ((string)$methodInfo->arguments) == 'array') {
405
- $result[] = $model->$method((is_array($args) ? $args : array($args)));
406
  } elseif (!is_array($args)) {
407
- $result[] = $model->$method($args);
408
  } else {
409
- $result[] = call_user_func_array(array(&$model, $method), $args);
410
  }
 
411
  } else {
412
  throw new Mage_Api_Exception('resource_path_not_callable');
413
  }
@@ -543,4 +547,41 @@ abstract class Mage_Api_Model_Server_Handler_Abstract
543
  $this->_startSession($sessionId);
544
  return array_values($this->_getConfig()->getFaults());
545
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
546
  } // Class Mage_Api_Model_Server_Handler_Abstract End
288
  }
289
 
290
  if (method_exists($model, $method)) {
291
+ $result = array();
292
  if (isset($methodInfo->arguments) && ((string)$methodInfo->arguments) == 'array') {
293
+ $result = $model->$method((is_array($args) ? $args : array($args)));
294
  } elseif (!is_array($args)) {
295
+ $result = $model->$method($args);
296
  } else {
297
+ $result = call_user_func_array(array(&$model, $method), $args);
298
  }
299
+ return $this->processingMethodResult($result);
300
  } else {
301
  throw new Mage_Api_Exception('resource_path_not_callable');
302
  }
403
  }
404
 
405
  if (method_exists($model, $method)) {
406
+ $callResult = array();
407
  if (isset($methodInfo->arguments) && ((string)$methodInfo->arguments) == 'array') {
408
+ $callResult = $model->$method((is_array($args) ? $args : array($args)));
409
  } elseif (!is_array($args)) {
410
+ $callResult = $model->$method($args);
411
  } else {
412
+ $callResult = call_user_func_array(array(&$model, $method), $args);
413
  }
414
+ $result[] = $this->processingMethodResult($callResult);
415
  } else {
416
  throw new Mage_Api_Exception('resource_path_not_callable');
417
  }
547
  $this->_startSession($sessionId);
548
  return array_values($this->_getConfig()->getFaults());
549
  }
550
+
551
+ /**
552
+ * Prepare Api data for XML exporting
553
+ * See allowed characters in XML:
554
+ * @link http://www.w3.org/TR/2000/REC-xml-20001006#NT-Char
555
+ *
556
+ * @param array $result
557
+ * @return mixed
558
+ */
559
+ public function processingMethodResult(array $result)
560
+ {
561
+ foreach ($result as &$row) {
562
+ if (!is_null($row) && !is_bool($row) && !is_numeric($row)) {
563
+ $row = $this->processingRow($row);
564
+ }
565
+ }
566
+ return $result;
567
+ }
568
+
569
+ /**
570
+ * Prepare Api row data for XML exporting
571
+ * Convert not allowed symbol to numeric character reference
572
+ *
573
+ * @param $row
574
+ * @return mixed
575
+ */
576
+ public function processingRow($row)
577
+ {
578
+ $row = preg_replace_callback(
579
+ '/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]/u',
580
+ function ($matches) {
581
+ return '&#' . Mage::helper('core/string')->uniOrd($matches[0]) . ';';
582
+ },
583
+ $row
584
+ );
585
+ return $row;
586
+ }
587
  } // Class Mage_Api_Model_Server_Handler_Abstract End
app/code/core/Mage/Bundle/Block/Adminhtml/Catalog/Product/Edit/Tab/Attributes.php CHANGED
@@ -121,6 +121,7 @@ class Mage_Bundle_Block_Adminhtml_Catalog_Product_Edit_Tab_Attributes
121
  $groupPrice->setRenderer(
122
  $this->getLayout()->createBlock('adminhtml/catalog_product_edit_tab_price_group')
123
  ->setPriceColumnHeader(Mage::helper('bundle')->__('Percent Discount'))
 
124
  ->setPriceValidation('validate-greater-than-zero validate-percents')
125
  );
126
  }
121
  $groupPrice->setRenderer(
122
  $this->getLayout()->createBlock('adminhtml/catalog_product_edit_tab_price_group')
123
  ->setPriceColumnHeader(Mage::helper('bundle')->__('Percent Discount'))
124
+ ->setIsPercent(true)
125
  ->setPriceValidation('validate-greater-than-zero validate-percents')
126
  );
127
  }
app/code/core/Mage/Bundle/Block/Catalog/Product/View/Type/Bundle/Option.php CHANGED
@@ -85,8 +85,13 @@ class Mage_Bundle_Block_Catalog_Product_View_Type_Bundle_Option extends Mage_Bun
85
  $_canChangeQty = $_default->getSelectionCanChangeQty();
86
  } elseif (!$inPreConfigured && $selectedOptions && is_numeric($selectedOptions)) {
87
  $selectedSelection = $_option->getSelectionById($selectedOptions);
88
- $_defaultQty = $selectedSelection->getSelectionQty() * 1;
89
- $_canChangeQty = $selectedSelection->getSelectionCanChangeQty();
 
 
 
 
 
90
  } elseif (!$this->_showSingle() || $inPreConfigured) {
91
  $_defaultQty = $this->_getSelectedQty();
92
  $_canChangeQty = (bool)$_defaultQty;
85
  $_canChangeQty = $_default->getSelectionCanChangeQty();
86
  } elseif (!$inPreConfigured && $selectedOptions && is_numeric($selectedOptions)) {
87
  $selectedSelection = $_option->getSelectionById($selectedOptions);
88
+ if ($selectedSelection) {
89
+ $_defaultQty = $selectedSelection->getSelectionQty() * 1;
90
+ $_canChangeQty = $selectedSelection->getSelectionCanChangeQty();
91
+ } else {
92
+ $_defaultQty = $_selections[0]->getSelectionQty() * 1;
93
+ $_canChangeQty = $_selections[0]->getSelectionCanChangeQty();
94
+ }
95
  } elseif (!$this->_showSingle() || $inPreConfigured) {
96
  $_defaultQty = $this->_getSelectedQty();
97
  $_canChangeQty = (bool)$_defaultQty;
app/code/core/Mage/Catalog/Block/Product/Abstract.php CHANGED
@@ -104,6 +104,16 @@ abstract class Mage_Catalog_Block_Product_Abstract extends Mage_Core_Block_Templ
104
  */
105
  protected $_mapRenderer = 'msrp';
106
 
 
 
 
 
 
 
 
 
 
 
107
  /**
108
  * Retrieve url for add product to cart
109
  * Will return product view page URL if product has required options
@@ -195,12 +205,7 @@ abstract class Mage_Catalog_Block_Product_Abstract extends Mage_Core_Block_Templ
195
  */
196
  public function getMinimalQty($product)
197
  {
198
- $stockItem = $product->getStockItem();
199
- if ($stockItem) {
200
- return ($stockItem->getMinSaleQty()
201
- && $stockItem->getMinSaleQty() > 0 ? $stockItem->getMinSaleQty() * 1 : null);
202
- }
203
- return null;
204
  }
205
 
206
  /**
104
  */
105
  protected $_mapRenderer = 'msrp';
106
 
107
+ /**
108
+ * Get catalog product helper
109
+ *
110
+ * @return Mage_Catalog_Helper_Product
111
+ */
112
+ public function getProductHelper()
113
+ {
114
+ return Mage::helper('catalog/product');
115
+ }
116
+
117
  /**
118
  * Retrieve url for add product to cart
119
  * Will return product view page URL if product has required options
205
  */
206
  public function getMinimalQty($product)
207
  {
208
+ return $this->getProductHelper()->getMinimalQty($product);
 
 
 
 
 
209
  }
210
 
211
  /**
app/code/core/Mage/Catalog/Block/Product/List.php CHANGED
@@ -62,16 +62,13 @@ class Mage_Catalog_Block_Product_List extends Mage_Catalog_Block_Product_Abstrac
62
  $this->setCategoryId(Mage::app()->getStore()->getRootCategoryId());
63
  }
64
 
65
- // if this is a product view page
66
  if (Mage::registry('product')) {
67
- // get collection of categories this product is associated with
68
  $categories = Mage::registry('product')->getCategoryCollection()
69
  ->setPage(1, 1)
70
  ->load();
71
- // if the product is associated with any category
72
  if ($categories->count()) {
73
- // show products from this category
74
- $this->setCategoryId(current($categories->getIterator()));
75
  }
76
  }
77
 
62
  $this->setCategoryId(Mage::app()->getStore()->getRootCategoryId());
63
  }
64
 
 
65
  if (Mage::registry('product')) {
66
+ /** @var Mage_Catalog_Model_Resource_Category_Collection $categories */
67
  $categories = Mage::registry('product')->getCategoryCollection()
68
  ->setPage(1, 1)
69
  ->load();
 
70
  if ($categories->count()) {
71
+ $this->setCategoryId($categories->getFirstItem()->getId());
 
72
  }
73
  }
74
 
app/code/core/Mage/Catalog/Block/Product/View.php CHANGED
@@ -141,62 +141,14 @@ class Mage_Catalog_Block_Product_View extends Mage_Catalog_Block_Product_Abstrac
141
  return Mage::helper('core')->jsonEncode($config);
142
  }
143
 
144
- $_request = Mage::getSingleton('tax/calculation')->getDefaultRateRequest();
145
  /* @var $product Mage_Catalog_Model_Product */
146
  $product = $this->getProduct();
147
- $_request->setProductClassId($product->getTaxClassId());
148
- $defaultTax = Mage::getSingleton('tax/calculation')->getRate($_request);
149
 
150
- $_request = Mage::getSingleton('tax/calculation')->getRateRequest();
151
- $_request->setProductClassId($product->getTaxClassId());
152
- $currentTax = Mage::getSingleton('tax/calculation')->getRate($_request);
153
-
154
- $_regularPrice = $product->getPrice();
155
- $_finalPrice = $product->getFinalPrice();
156
- if ($product->getTypeId() == Mage_Catalog_Model_Product_Type::TYPE_BUNDLE) {
157
- $_priceInclTax = Mage::helper('tax')->getPrice($product, $_finalPrice, true,
158
- null, null, null, null, null, false);
159
- $_priceExclTax = Mage::helper('tax')->getPrice($product, $_finalPrice, false,
160
- null, null, null, null, null, false);
161
- } else {
162
- $_priceInclTax = Mage::helper('tax')->getPrice($product, $_finalPrice, true);
163
- $_priceExclTax = Mage::helper('tax')->getPrice($product, $_finalPrice);
164
- }
165
- $_tierPrices = array();
166
- $_tierPricesInclTax = array();
167
- foreach ($product->getTierPrice() as $tierPrice) {
168
- $_tierPrices[] = Mage::helper('core')->currency(
169
- Mage::helper('tax')->getPrice($product, (float)$tierPrice['website_price'], false) - $_priceExclTax
170
- , false, false);
171
- $_tierPricesInclTax[] = Mage::helper('core')->currency(
172
- Mage::helper('tax')->getPrice($product, (float)$tierPrice['website_price'], true) - $_priceInclTax
173
- , false, false);
174
- }
175
- $config = array(
176
- 'productId' => $product->getId(),
177
- 'priceFormat' => Mage::app()->getLocale()->getJsPriceFormat(),
178
- 'includeTax' => Mage::helper('tax')->priceIncludesTax() ? 'true' : 'false',
179
- 'showIncludeTax' => Mage::helper('tax')->displayPriceIncludingTax(),
180
- 'showBothPrices' => Mage::helper('tax')->displayBothPrices(),
181
- 'productPrice' => Mage::helper('core')->currency($_finalPrice, false, false),
182
- 'productOldPrice' => Mage::helper('core')->currency($_regularPrice, false, false),
183
- 'priceInclTax' => Mage::helper('core')->currency($_priceInclTax, false, false),
184
- 'priceExclTax' => Mage::helper('core')->currency($_priceExclTax, false, false),
185
- /**
186
- * @var skipCalculate
187
- * @deprecated after 1.5.1.0
188
- */
189
- 'skipCalculate' => ($_priceExclTax != $_priceInclTax ? 0 : 1),
190
- 'defaultTax' => $defaultTax,
191
- 'currentTax' => $currentTax,
192
- 'idSuffix' => '_clone',
193
- 'oldPlusDisposition' => 0,
194
- 'plusDisposition' => 0,
195
- 'plusDispositionTax' => 0,
196
- 'oldMinusDisposition' => 0,
197
- 'minusDisposition' => 0,
198
- 'tierPrices' => $_tierPrices,
199
- 'tierPricesInclTax' => $_tierPricesInclTax,
200
  );
201
 
202
  $responseObject = new Varien_Object();
@@ -259,14 +211,7 @@ class Mage_Catalog_Block_Product_View extends Mage_Catalog_Block_Product_Abstrac
259
  $product = $this->getProduct();
260
  }
261
 
262
- $qty = $this->getMinimalQty($product);
263
- $config = $product->getPreconfiguredValues();
264
- $configQty = $config->getQty();
265
- if ($configQty > $qty) {
266
- $qty = $configQty;
267
- }
268
-
269
- return $qty;
270
  }
271
 
272
  /**
141
  return Mage::helper('core')->jsonEncode($config);
142
  }
143
 
 
144
  /* @var $product Mage_Catalog_Model_Product */
145
  $product = $this->getProduct();
 
 
146
 
147
+ /** @var Mage_Catalog_Helper_Product_Type_Composite $compositeProductHelper */
148
+ $compositeProductHelper = $this->helper('catalog/product_type_composite');
149
+ $config = array_merge(
150
+ $compositeProductHelper->prepareJsonGeneralConfig(),
151
+ $compositeProductHelper->prepareJsonProductConfig($product)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  );
153
 
154
  $responseObject = new Varien_Object();
211
  $product = $this->getProduct();
212
  }
213
 
214
+ return $this->getProductHelper()->getDefaultQty($product);
 
 
 
 
 
 
 
215
  }
216
 
217
  /**
app/code/core/Mage/Catalog/Block/Product/View/Type/Configurable.php CHANGED
@@ -48,6 +48,16 @@ class Mage_Catalog_Block_Product_View_Type_Configurable extends Mage_Catalog_Blo
48
  */
49
  protected $_resPrices = array();
50
 
 
 
 
 
 
 
 
 
 
 
51
  /**
52
  * Get allowed attributes
53
  *
@@ -91,7 +101,10 @@ class Mage_Catalog_Block_Product_View_Type_Configurable extends Mage_Catalog_Blo
91
  $allProducts = $this->getProduct()->getTypeInstance(true)
92
  ->getUsedProducts(null, $this->getProduct());
93
  foreach ($allProducts as $product) {
94
- if ($product->isSaleable() || $skipSaleableCheck) {
 
 
 
95
  $products[] = $product;
96
  }
97
  }
@@ -103,11 +116,12 @@ class Mage_Catalog_Block_Product_View_Type_Configurable extends Mage_Catalog_Blo
103
  /**
104
  * retrieve current store
105
  *
 
106
  * @return Mage_Core_Model_Store
107
  */
108
  public function getCurrentStore()
109
  {
110
- return Mage::app()->getStore();
111
  }
112
 
113
  /**
@@ -138,10 +152,10 @@ class Mage_Catalog_Block_Product_View_Type_Configurable extends Mage_Catalog_Blo
138
  $preconfiguredValues = $currentProduct->getPreconfiguredValues();
139
  $defaultValues = array();
140
  }
141
-
142
  foreach ($this->getAllowProducts() as $product) {
143
  $productId = $product->getId();
144
-
145
  foreach ($this->getAllowAttributes() as $attribute) {
146
  $productAttribute = $attribute->getProductAttribute();
147
  $productAttributeId = $productAttribute->getId();
@@ -189,7 +203,13 @@ class Mage_Catalog_Block_Product_View_Type_Configurable extends Mage_Catalog_Blo
189
  $configurablePrice = $currentProduct->getConfigurablePrice();
190
 
191
  if (isset($options[$attributeId][$value['value_index']])) {
192
- $productsIndex = $options[$attributeId][$value['value_index']];
 
 
 
 
 
 
193
  } else {
194
  $productsIndex = array();
195
  }
@@ -300,64 +320,51 @@ class Mage_Catalog_Block_Product_View_Type_Configurable extends Mage_Catalog_Blo
300
  /**
301
  * Calculation real price
302
  *
 
303
  * @param float $price
304
  * @param bool $isPercent
305
  * @return mixed
306
  */
307
  protected function _preparePrice($price, $isPercent = false)
308
  {
309
- if ($isPercent && !empty($price)) {
310
- $price = $this->getProduct()->getFinalPrice() * $price / 100;
311
- }
312
-
313
- return $this->_registerJsPrice($this->_convertPrice($price, true));
314
  }
315
 
316
  /**
317
  * Calculation price before special price
318
  *
 
319
  * @param float $price
320
  * @param bool $isPercent
321
  * @return mixed
322
  */
323
  protected function _prepareOldPrice($price, $isPercent = false)
324
  {
325
- if ($isPercent && !empty($price)) {
326
- $price = $this->getProduct()->getPrice() * $price / 100;
327
- }
328
-
329
- return $this->_registerJsPrice($this->_convertPrice($price, true));
330
  }
331
 
332
  /**
333
  * Replace ',' on '.' for js
334
  *
 
335
  * @param float $price
336
  * @return string
337
  */
338
  protected function _registerJsPrice($price)
339
  {
340
- return str_replace(',', '.', $price);
341
  }
342
 
343
  /**
344
  * Convert price from default currency to current currency
345
  *
 
346
  * @param float $price
347
  * @param boolean $round
348
  * @return float
349
  */
350
  protected function _convertPrice($price, $round = false)
351
  {
352
- if (empty($price)) {
353
- return 0;
354
- }
355
-
356
- $price = $this->getCurrentStore()->convertPrice($price);
357
- if ($round) {
358
- $price = $this->getCurrentStore()->roundPrice($price);
359
- }
360
-
361
- return $price;
362
  }
363
  }
48
  */
49
  protected $_resPrices = array();
50
 
51
+ /**
52
+ * Get helper for calculation purposes
53
+ *
54
+ * @return Mage_Catalog_Helper_Product_Type_Composite
55
+ */
56
+ protected function _getHelper()
57
+ {
58
+ return $this->helper('catalog/product_type_composite');
59
+ }
60
+
61
  /**
62
  * Get allowed attributes
63
  *
101
  $allProducts = $this->getProduct()->getTypeInstance(true)
102
  ->getUsedProducts(null, $this->getProduct());
103
  foreach ($allProducts as $product) {
104
+ if ($product->isSaleable()
105
+ || $skipSaleableCheck
106
+ || (!$product->getStockItem()->getIsInStock()
107
+ && Mage::helper('cataloginventory')->isShowOutOfStock())) {
108
  $products[] = $product;
109
  }
110
  }
116
  /**
117
  * retrieve current store
118
  *
119
+ * @deprecated
120
  * @return Mage_Core_Model_Store
121
  */
122
  public function getCurrentStore()
123
  {
124
+ return $this->_getHelper()->getCurrentStore();
125
  }
126
 
127
  /**
152
  $preconfiguredValues = $currentProduct->getPreconfiguredValues();
153
  $defaultValues = array();
154
  }
155
+ $productStock = array();
156
  foreach ($this->getAllowProducts() as $product) {
157
  $productId = $product->getId();
158
+ $productStock[$productId] = $product->getStockItem()->getIsInStock();
159
  foreach ($this->getAllowAttributes() as $attribute) {
160
  $productAttribute = $attribute->getProductAttribute();
161
  $productAttributeId = $productAttribute->getId();
203
  $configurablePrice = $currentProduct->getConfigurablePrice();
204
 
205
  if (isset($options[$attributeId][$value['value_index']])) {
206
+ $productsIndexOptions = $options[$attributeId][$value['value_index']];
207
+ $productsIndex = array();
208
+ foreach ($productsIndexOptions as $productIndex) {
209
+ if ($productStock[$productIndex]) {
210
+ $productsIndex[] = $productIndex;
211
+ }
212
+ }
213
  } else {
214
  $productsIndex = array();
215
  }
320
  /**
321
  * Calculation real price
322
  *
323
+ * @deprecated
324
  * @param float $price
325
  * @param bool $isPercent
326
  * @return mixed
327
  */
328
  protected function _preparePrice($price, $isPercent = false)
329
  {
330
+ return $this->_getHelper()->preparePrice($this->getProduct(), $price, $isPercent);
 
 
 
 
331
  }
332
 
333
  /**
334
  * Calculation price before special price
335
  *
336
+ * @deprecated
337
  * @param float $price
338
  * @param bool $isPercent
339
  * @return mixed
340
  */
341
  protected function _prepareOldPrice($price, $isPercent = false)
342
  {
343
+ return $this->_getHelper()->prepareOldPrice($this->getProduct(), $price, $isPercent);
 
 
 
 
344
  }
345
 
346
  /**
347
  * Replace ',' on '.' for js
348
  *
349
+ * @deprecated
350
  * @param float $price
351
  * @return string
352
  */
353
  protected function _registerJsPrice($price)
354
  {
355
+ return $this->_getHelper()->registerJsPrice($price);
356
  }
357
 
358
  /**
359
  * Convert price from default currency to current currency
360
  *
361
+ * @deprecated
362
  * @param float $price
363
  * @param boolean $round
364
  * @return float
365
  */
366
  protected function _convertPrice($price, $round = false)
367
  {
368
+ return $this->_getHelper()->convertPrice($price, $round);
 
 
 
 
 
 
 
 
 
369
  }
370
  }
app/code/core/Mage/Catalog/Helper/Image.php CHANGED
@@ -33,6 +33,7 @@ class Mage_Catalog_Helper_Image extends Mage_Core_Helper_Abstract
33
  {
34
  const XML_NODE_PRODUCT_BASE_IMAGE_WIDTH = 'catalog/product_image/base_width';
35
  const XML_NODE_PRODUCT_SMALL_IMAGE_WIDTH = 'catalog/product_image/small_width';
 
36
 
37
  /**
38
  * Current model
@@ -634,10 +635,16 @@ class Mage_Catalog_Helper_Image extends Mage_Core_Helper_Abstract
634
  * @throws Mage_Core_Exception
635
  */
636
  public function validateUploadFile($filePath) {
637
- if (!getimagesize($filePath)) {
 
 
638
  Mage::throwException($this->__('Disallowed file type.'));
639
  }
640
 
 
 
 
 
641
  $_processor = new Varien_Image($filePath);
642
  return $_processor->getMimeType() !== null;
643
  }
33
  {
34
  const XML_NODE_PRODUCT_BASE_IMAGE_WIDTH = 'catalog/product_image/base_width';
35
  const XML_NODE_PRODUCT_SMALL_IMAGE_WIDTH = 'catalog/product_image/small_width';
36
+ const XML_NODE_PRODUCT_MAX_DIMENSION = 'catalog/product_image/max_dimension';
37
 
38
  /**
39
  * Current model
635
  * @throws Mage_Core_Exception
636
  */
637
  public function validateUploadFile($filePath) {
638
+ $maxDimension = Mage::getStoreConfig(self::XML_NODE_PRODUCT_MAX_DIMENSION);
639
+ $imageInfo = getimagesize($filePath);
640
+ if (!$imageInfo) {
641
  Mage::throwException($this->__('Disallowed file type.'));
642
  }
643
 
644
+ if ($imageInfo[0] > $maxDimension || $imageInfo[1] > $maxDimension) {
645
+ Mage::throwException($this->__('Disalollowed file format.'));
646
+ }
647
+
648
  $_processor = new Varien_Image($filePath);
649
  return $_processor->getMimeType() !== null;
650
  }
app/code/core/Mage/Catalog/Helper/Product.php CHANGED
@@ -35,6 +35,8 @@ class Mage_Catalog_Helper_Product extends Mage_Core_Helper_Url
35
  const XML_PATH_PRODUCT_URL_USE_CATEGORY = 'catalog/seo/product_use_categories';
36
  const XML_PATH_USE_PRODUCT_CANONICAL_TAG = 'catalog/seo/product_canonical_tag';
37
 
 
 
38
  /**
39
  * Flag that shows if Magento has to check product to be saleable (enabled and/or inStock)
40
  *
@@ -485,4 +487,42 @@ class Mage_Catalog_Helper_Product extends Mage_Core_Helper_Url
485
  {
486
  return $this->_skipSaleableCheck;
487
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
488
  }
35
  const XML_PATH_PRODUCT_URL_USE_CATEGORY = 'catalog/seo/product_use_categories';
36
  const XML_PATH_USE_PRODUCT_CANONICAL_TAG = 'catalog/seo/product_canonical_tag';
37
 
38
+ const DEFAULT_QTY = 1;
39
+
40
  /**
41
  * Flag that shows if Magento has to check product to be saleable (enabled and/or inStock)
42
  *
487
  {
488
  return $this->_skipSaleableCheck;
489
  }
490
+
491
+ /**
492
+ * Gets minimal sales quantity
493
+ *
494
+ * @param Mage_Catalog_Model_Product $product
495
+ * @return int|null
496
+ */
497
+ public function getMinimalQty($product)
498
+ {
499
+ $stockItem = $product->getStockItem();
500
+ if ($stockItem && $stockItem->getMinSaleQty()) {
501
+ return $stockItem->getMinSaleQty() * 1;
502
+ }
503
+ return null;
504
+ }
505
+
506
+ /**
507
+ * Get default qty - either as preconfigured, or as 1.
508
+ * Also restricts it by minimal qty.
509
+ *
510
+ * @param Mage_Catalog_Model_Product $product
511
+ * @return int|float
512
+ */
513
+ public function getDefaultQty($product)
514
+ {
515
+ $qty = $this->getMinimalQty($product);
516
+ $configQty = $product->getPreconfiguredValues()->getQty();
517
+
518
+ if ($product->isConfigurable() || $configQty > $qty) {
519
+ $qty = $configQty;
520
+ }
521
+
522
+ if (is_null($qty)) {
523
+ $qty = self::DEFAULT_QTY;
524
+ }
525
+
526
+ return $qty;
527
+ }
528
  }
app/code/core/Mage/Catalog/Helper/Product/Type/Composite.php ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magento.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magento.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Catalog
23
+ * @copyright Copyright (c) 2006-2016 X.commerce, Inc. and affiliates (http://www.magento.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Helper for preparing properties for configurable product
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Catalog
32
+ * @author Magento Core Team <core@magentocommerce.com>
33
+ */
34
+ class Mage_Catalog_Helper_Product_Type_Composite extends Mage_Core_Helper_Abstract
35
+ {
36
+ /**
37
+ * Calculation real price
38
+ *
39
+ * @param Mage_Catalog_Model_Product $product
40
+ * @param float $price
41
+ * @param bool $isPercent
42
+ * @param null|int $storeId
43
+ * @return mixed
44
+ */
45
+ public function preparePrice($product, $price, $isPercent = false, $storeId = null)
46
+ {
47
+ if ($isPercent && !empty($price)) {
48
+ $price = $product->getFinalPrice() * $price / 100;
49
+ }
50
+
51
+ return $this->registerJsPrice($this->convertPrice($price, true, $storeId));
52
+ }
53
+
54
+ /**
55
+ * Calculation price before special price
56
+ *
57
+ * @param Mage_Catalog_Model_Product $product
58
+ * @param float $price
59
+ * @param bool $isPercent
60
+ * @param null|int $storeId
61
+ * @return mixed
62
+ */
63
+ public function prepareOldPrice($product, $price, $isPercent = false, $storeId = null)
64
+ {
65
+ if ($isPercent && !empty($price)) {
66
+ $price = $product->getPrice() * $price / 100;
67
+ }
68
+
69
+ return $this->registerJsPrice($this->convertPrice($price, true, $storeId));
70
+ }
71
+
72
+ /**
73
+ * Replace ',' on '.' for js
74
+ *
75
+ * @param float $price
76
+ * @return string
77
+ */
78
+ public function registerJsPrice($price)
79
+ {
80
+ return str_replace(',', '.', $price);
81
+ }
82
+
83
+ /**
84
+ * Convert price from default currency to current currency
85
+ *
86
+ * @param float $price
87
+ * @param boolean $round
88
+ * @param null|int $storeId
89
+ * @return float
90
+ */
91
+ public function convertPrice($price, $round = false, $storeId = null)
92
+ {
93
+ if (empty($price)) {
94
+ return 0;
95
+ }
96
+
97
+ $price = $this->getCurrentStore($storeId)->convertPrice($price);
98
+ if ($round) {
99
+ $price = $this->getCurrentStore($storeId)->roundPrice($price);
100
+ }
101
+
102
+ return $price;
103
+ }
104
+
105
+ /**
106
+ * Retrieve current store
107
+ *
108
+ * @param null $storeId
109
+ * @return Mage_Core_Model_Store
110
+ */
111
+ public function getCurrentStore($storeId = null)
112
+ {
113
+ return Mage::app()->getStore($storeId);
114
+ }
115
+
116
+ /**
117
+ * Prepare general params for product to be used in getJsonConfig()
118
+ * @see Mage_Catalog_Block_Product_View::getJsonConfig()
119
+ * @see Mage_ConfigurableSwatches_Block_Catalog_Product_List_Price::getJsonConfig()
120
+ *
121
+ * @return array
122
+ */
123
+ public function prepareJsonGeneralConfig()
124
+ {
125
+ return array(
126
+ 'priceFormat' => Mage::app()->getLocale()->getJsPriceFormat(),
127
+ 'includeTax' => Mage::helper('tax')->priceIncludesTax() ? 'true' : 'false',
128
+ 'showIncludeTax' => Mage::helper('tax')->displayPriceIncludingTax(),
129
+ 'showBothPrices' => Mage::helper('tax')->displayBothPrices(),
130
+ 'idSuffix' => '',
131
+ 'oldPlusDisposition' => 0,
132
+ 'plusDisposition' => 0,
133
+ 'plusDispositionTax' => 0,
134
+ 'oldMinusDisposition' => 0,
135
+ 'minusDisposition' => 0,
136
+ );
137
+ }
138
+
139
+
140
+
141
+ /**
142
+ * Prepare product specific params to be used in getJsonConfig()
143
+ * @see Mage_Catalog_Block_Product_View::getJsonConfig()
144
+ * @see Mage_ConfigurableSwatches_Block_Catalog_Product_List_Price::getJsonConfig()
145
+ *
146
+ * @param Mage_Catalog_Model_Product $product
147
+ * @return array
148
+ */
149
+ public function prepareJsonProductConfig($product)
150
+ {
151
+ $_request = Mage::getSingleton('tax/calculation')->getDefaultRateRequest();
152
+ $_request->setProductClassId($product->getTaxClassId());
153
+ $defaultTax = Mage::getSingleton('tax/calculation')->getRate($_request);
154
+
155
+ $_request = Mage::getSingleton('tax/calculation')->getRateRequest();
156
+ $_request->setProductClassId($product->getTaxClassId());
157
+ $currentTax = Mage::getSingleton('tax/calculation')->getRate($_request);
158
+
159
+ $_regularPrice = $product->getPrice();
160
+ $_finalPrice = $product->getFinalPrice();
161
+ if ($product->getTypeId() == Mage_Catalog_Model_Product_Type::TYPE_BUNDLE) {
162
+ $_priceInclTax = Mage::helper('tax')->getPrice($product, $_finalPrice, true,
163
+ null, null, null, null, null, false);
164
+ $_priceExclTax = Mage::helper('tax')->getPrice($product, $_finalPrice, false,
165
+ null, null, null, null, null, false);
166
+ } else {
167
+ $_priceInclTax = Mage::helper('tax')->getPrice($product, $_finalPrice, true);
168
+ $_priceExclTax = Mage::helper('tax')->getPrice($product, $_finalPrice);
169
+ }
170
+ $_tierPrices = array();
171
+ $_tierPricesInclTax = array();
172
+ foreach ($product->getTierPrice() as $tierPrice) {
173
+ $_tierPrices[] = Mage::helper('core')->currency(
174
+ Mage::helper('tax')->getPrice($product, (float)$tierPrice['website_price'], false) - $_priceExclTax
175
+ , false, false);
176
+ $_tierPricesInclTax[] = Mage::helper('core')->currency(
177
+ Mage::helper('tax')->getPrice($product, (float)$tierPrice['website_price'], true) - $_priceInclTax
178
+ , false, false);
179
+ }
180
+
181
+ return array(
182
+ 'productId' => $product->getId(),
183
+ 'productPrice' => Mage::helper('core')->currency($_finalPrice, false, false),
184
+ 'productOldPrice' => Mage::helper('core')->currency($_regularPrice, false, false),
185
+ 'priceInclTax' => Mage::helper('core')->currency($_priceInclTax, false, false),
186
+ 'priceExclTax' => Mage::helper('core')->currency($_priceExclTax, false, false),
187
+ 'skipCalculate' => ($_priceExclTax != $_priceInclTax ? 0 : 1),
188
+ 'defaultTax' => $defaultTax,
189
+ 'currentTax' => $currentTax,
190
+ 'tierPrices' => $_tierPrices,
191
+ 'tierPricesInclTax' => $_tierPricesInclTax,
192
+ 'swatchPrices' => $product->getSwatchPrices(),
193
+ );
194
+ }
195
+ }
app/code/core/Mage/Catalog/Model/Product/Attribute/Backend/Groupprice/Abstract.php CHANGED
@@ -227,6 +227,7 @@ abstract class Mage_Catalog_Model_Product_Attribute_Backend_Groupprice_Abstract
227
  $data = $this->_getResource()->loadPriceData($object->getId(), $websiteId);
228
  foreach ($data as $k => $v) {
229
  $data[$k]['website_price'] = $v['price'];
 
230
  if ($v['all_groups']) {
231
  $data[$k]['cust_group'] = Mage_Customer_Model_Group::CUST_GROUP_ALL;
232
  }
@@ -318,6 +319,7 @@ abstract class Mage_Catalog_Model_Product_Attribute_Backend_Groupprice_Abstract
318
  'all_groups' => $useForAllGroups ? 1 : 0,
319
  'customer_group_id' => $customerGroupId,
320
  'value' => $data['price'],
 
321
  ), $this->_getAdditionalUniqueFields($data));
322
  }
323
 
@@ -347,10 +349,11 @@ abstract class Mage_Catalog_Model_Product_Attribute_Backend_Groupprice_Abstract
347
 
348
  if (!empty($update)) {
349
  foreach ($update as $k => $v) {
350
- if ($old[$k]['price'] != $v['value']) {
351
  $price = new Varien_Object(array(
352
- 'value_id' => $old[$k]['price_id'],
353
- 'value' => $v['value']
 
354
  ));
355
  $this->_getResource()->savePriceData($price);
356
 
227
  $data = $this->_getResource()->loadPriceData($object->getId(), $websiteId);
228
  foreach ($data as $k => $v) {
229
  $data[$k]['website_price'] = $v['price'];
230
+ $data[$k]['is_percent'] = isset($v['is_percent']) ? isset($v['is_percent']) : 0;
231
  if ($v['all_groups']) {
232
  $data[$k]['cust_group'] = Mage_Customer_Model_Group::CUST_GROUP_ALL;
233
  }
319
  'all_groups' => $useForAllGroups ? 1 : 0,
320
  'customer_group_id' => $customerGroupId,
321
  'value' => $data['price'],
322
+ 'is_percent' => isset($data['is_percent']) ? $data['is_percent'] : 0,
323
  ), $this->_getAdditionalUniqueFields($data));
324
  }
325
 
349
 
350
  if (!empty($update)) {
351
  foreach ($update as $k => $v) {
352
+ if ($old[$k]['price'] != $v['value'] || $old[$k]['is_percent'] != $v['is_percent']) {
353
  $price = new Varien_Object(array(
354
+ 'value_id' => $old[$k]['price_id'],
355
+ 'value' => $v['value'],
356
+ 'is_percent' => $v['is_percent']
357
  ));
358
  $this->_getResource()->savePriceData($price);
359
 
app/code/core/Mage/Catalog/Model/Product/Attribute/Backend/Media.php CHANGED
@@ -643,8 +643,11 @@ class Mage_Catalog_Model_Product_Attribute_Backend_Media extends Mage_Eav_Model_
643
 
644
  } catch (Exception $e) {
645
  $file = $this->_getConfig()->getMediaPath($file);
 
646
  Mage::throwException(
647
- Mage::helper('catalog')->__('Failed to copy file %s. Please, delete media with non-existing images and try again.', $file)
 
 
648
  );
649
  }
650
 
643
 
644
  } catch (Exception $e) {
645
  $file = $this->_getConfig()->getMediaPath($file);
646
+ $io = new Varien_Io_File();
647
  Mage::throwException(
648
+ Mage::helper('catalog')->__(
649
+ 'Failed to copy file %s. Please, delete media with non-existing images and try again.',
650
+ $io->getFilteredPath($file))
651
  );
652
  }
653
 
app/code/core/Mage/Catalog/Model/Product/Link/Api/V2.php CHANGED
@@ -61,8 +61,8 @@ class Mage_Catalog_Model_Product_Link_Api_V2 extends Mage_Catalog_Model_Product_
61
 
62
  $links[(int)$linkedProductId] = array();
63
  foreach ($collection->getLinkModel()->getAttributes() as $attribute) {
64
- if (isset($data->$attribute['code'])) {
65
- $links[(int)$linkedProductId][$attribute['code']] = $data->$attribute['code'];
66
  }
67
  }
68
 
@@ -118,8 +118,8 @@ class Mage_Catalog_Model_Product_Link_Api_V2 extends Mage_Catalog_Model_Product_
118
  }
119
 
120
  foreach ($collection->getLinkModel()->getAttributes() as $attribute) {
121
- if (isset($data->$attribute['code'])) {
122
- $links[(int)$linkedProductId][$attribute['code']] = $data->$attribute['code'];
123
  }
124
  }
125
 
61
 
62
  $links[(int)$linkedProductId] = array();
63
  foreach ($collection->getLinkModel()->getAttributes() as $attribute) {
64
+ if (isset($data->{$attribute['code']})) {
65
+ $links[(int)$linkedProductId][$attribute['code']] = $data->{$attribute['code']};
66
  }
67
  }
68
 
118
  }
119
 
120
  foreach ($collection->getLinkModel()->getAttributes() as $attribute) {
121
+ if (isset($data->{$attribute['code']})) {
122
+ $links[(int)$linkedProductId][$attribute['code']] = $data->{$attribute['code']};
123
  }
124
  }
125
 
app/code/core/Mage/Catalog/Model/Resource/Eav/Attribute.php CHANGED
@@ -359,7 +359,7 @@ class Mage_Catalog_Model_Resource_Eav_Attribute extends Mage_Eav_Model_Entity_At
359
 
360
  if ($backendType == 'int' && $frontendInput == 'select') {
361
  return true;
362
- } else if ($backendType == 'varchar' && $frontendInput == 'multiselect') {
363
  return true;
364
  } else if ($backendType == 'decimal') {
365
  return true;
359
 
360
  if ($backendType == 'int' && $frontendInput == 'select') {
361
  return true;
362
+ } else if (($backendType == 'varchar' || $backendType == 'text') && $frontendInput == 'multiselect') {
363
  return true;
364
  } else if ($backendType == 'decimal') {
365
  return true;
app/code/core/Mage/Catalog/Model/Resource/Product/Attribute/Backend/Groupprice.php CHANGED
@@ -43,4 +43,17 @@ class Mage_Catalog_Model_Resource_Product_Attribute_Backend_Groupprice
43
  {
44
  $this->_init('catalog/product_attribute_group_price', 'value_id');
45
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  }
43
  {
44
  $this->_init('catalog/product_attribute_group_price', 'value_id');
45
  }
46
+
47
+ /**
48
+ * Add is_percent column
49
+ *
50
+ * @param array $columns
51
+ * @return array
52
+ */
53
+ protected function _loadPriceDataColumns($columns)
54
+ {
55
+ $columns = parent::_loadPriceDataColumns($columns);
56
+ $columns['is_percent'] = 'is_percent';
57
+ return $columns;
58
+ }
59
  }
app/code/core/Mage/Catalog/Model/Resource/Product/Indexer/Eav/Source.php CHANGED
@@ -61,7 +61,7 @@ class Mage_Catalog_Model_Resource_Product_Indexer_Eav_Source
61
  ->where($this->_getIndexableAttributesCondition());
62
 
63
  if ($multiSelect == true) {
64
- $select->where('ea.backend_type = ?', 'varchar')
65
  ->where('ea.frontend_input = ?', 'multiselect');
66
  } else {
67
  $select->where('ea.backend_type = ?', 'int')
@@ -203,14 +203,14 @@ class Mage_Catalog_Model_Resource_Product_Indexer_Eav_Source
203
  $productValueExpression = $adapter->getCheckSql('pvs.value_id > 0', 'pvs.value', 'pvd.value');
204
  $select = $adapter->select()
205
  ->from(
206
- array('pvd' => $this->getValueTable('catalog/product', 'varchar')),
207
  array('entity_id', 'attribute_id'))
208
  ->join(
209
  array('cs' => $this->getTable('core/store')),
210
  '',
211
  array('store_id'))
212
  ->joinLeft(
213
- array('pvs' => $this->getValueTable('catalog/product', 'varchar')),
214
  'pvs.entity_id = pvd.entity_id AND pvs.attribute_id = pvd.attribute_id'
215
  . ' AND pvs.store_id=cs.store_id',
216
  array('value' => $productValueExpression))
61
  ->where($this->_getIndexableAttributesCondition());
62
 
63
  if ($multiSelect == true) {
64
+ $select->where('ea.backend_type = ?', 'text')
65
  ->where('ea.frontend_input = ?', 'multiselect');
66
  } else {
67
  $select->where('ea.backend_type = ?', 'int')
203
  $productValueExpression = $adapter->getCheckSql('pvs.value_id > 0', 'pvs.value', 'pvd.value');
204
  $select = $adapter->select()
205
  ->from(
206
+ array('pvd' => $this->getValueTable('catalog/product', 'text')),
207
  array('entity_id', 'attribute_id'))
208
  ->join(
209
  array('cs' => $this->getTable('core/store')),
210
  '',
211
  array('store_id'))
212
  ->joinLeft(
213
+ array('pvs' => $this->getValueTable('catalog/product', 'text')),
214
  'pvs.entity_id = pvd.entity_id AND pvs.attribute_id = pvd.attribute_id'
215
  . ' AND pvs.store_id=cs.store_id',
216
  array('value' => $productValueExpression))
app/code/core/Mage/Catalog/Model/Resource/Product/Link/Product/Collection.php CHANGED
@@ -282,6 +282,19 @@ class Mage_Catalog_Model_Resource_Product_Link_Product_Collection extends Mage_C
282
  return $this;
283
  }
284
 
 
 
 
 
 
 
 
 
 
 
 
 
 
285
  /**
286
  * Join attributes
287
  *
@@ -294,10 +307,9 @@ class Mage_Catalog_Model_Resource_Product_Link_Product_Collection extends Mage_C
294
  }
295
  $attributes = $this->getLinkModel()->getAttributes();
296
 
297
- $attributesByType = array();
298
  foreach ($attributes as $attribute) {
299
  $table = $this->getLinkModel()->getAttributeTypeTable($attribute['type']);
300
- $alias = sprintf('link_attribute_%s_%s', $attribute['code'], $attribute['type']);
301
 
302
  $joinCondiotion = array(
303
  "{$alias}.link_id = links.link_id",
@@ -331,4 +343,38 @@ class Mage_Catalog_Model_Resource_Product_Link_Product_Collection extends Mage_C
331
  }
332
  return parent::setOrder($attribute, $dir);
333
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
334
  }
282
  return $this;
283
  }
284
 
285
+ /**
286
+ * Get table alias for link model attribute
287
+ *
288
+ * @param string $attributeCode
289
+ * @param string $attributeType
290
+ *
291
+ * @return string
292
+ */
293
+ protected function _getLinkAttributeTableAlias($attributeCode, $attributeType)
294
+ {
295
+ return sprintf('link_attribute_%s_%s', $attributeCode, $attributeType);
296
+ }
297
+
298
  /**
299
  * Join attributes
300
  *
307
  }
308
  $attributes = $this->getLinkModel()->getAttributes();
309
 
 
310
  foreach ($attributes as $attribute) {
311
  $table = $this->getLinkModel()->getAttributeTypeTable($attribute['type']);
312
+ $alias = $this->_getLinkAttributeTableAlias($attribute['code'], $attribute['type']);
313
 
314
  $joinCondiotion = array(
315
  "{$alias}.link_id = links.link_id",
343
  }
344
  return parent::setOrder($attribute, $dir);
345
  }
346
+
347
+ /**
348
+ * Add specific link model attribute to collection filter
349
+ *
350
+ * @param string $attributeCode
351
+ * @param array|null $condition
352
+ *
353
+ * @return Mage_Catalog_Model_Resource_Product_Link_Product_Collection
354
+ */
355
+ public function addLinkModelFieldToFilter($attributeCode, $condition = null)
356
+ {
357
+ if (!$this->getProduct() || !$this->getProduct()->getId()) {
358
+ return $this;
359
+ }
360
+
361
+ $attribute = null;
362
+ foreach ($this->getLinkModel()->getAttributes() as $attributeData) {
363
+ if ($attributeData['code'] == $attributeCode) {
364
+ $attribute = $attributeData;
365
+ break;
366
+ }
367
+ }
368
+
369
+ if (!$attribute) {
370
+ return $this;
371
+ }
372
+
373
+ $this->_hasLinkFilter = true;
374
+
375
+ $field = $this->_getLinkAttributeTableAlias($attribute['code'], $attribute['type']) . '.value';
376
+ $this->getSelect()->where($this->_getConditionSql($field, $condition));
377
+
378
+ return $this;
379
+ }
380
  }
app/code/core/Mage/Catalog/Model/Resource/Product/Type/Configurable/Attribute/Collection.php CHANGED
@@ -241,7 +241,7 @@ class Mage_Catalog_Model_Resource_Product_Type_Configurable_Attribute_Collection
241
  }
242
 
243
  $values = array();
244
-
245
  foreach ($this->_items as $item) {
246
  $productAttribute = $item->getProductAttribute();
247
  if (!($productAttribute instanceof Mage_Eav_Model_Entity_Attribute_Abstract)) {
@@ -251,7 +251,7 @@ class Mage_Catalog_Model_Resource_Product_Type_Configurable_Attribute_Collection
251
 
252
  $optionsByValue = array();
253
  foreach ($options as $option) {
254
- $optionsByValue[$option['value']] = $option['label'];
255
  }
256
 
257
  foreach ($this->getProduct()->getTypeInstance(true)
@@ -267,18 +267,23 @@ class Mage_Catalog_Model_Resource_Product_Type_Configurable_Attribute_Collection
267
  $values[$item->getId() . ':' . $optionValue] = array(
268
  'product_super_attribute_id' => $item->getId(),
269
  'value_index' => $optionValue,
270
- 'label' => $optionsByValue[$optionValue],
271
- 'default_label' => $optionsByValue[$optionValue],
272
- 'store_label' => $optionsByValue[$optionValue],
273
  'is_percent' => 0,
274
  'pricing_value' => null,
275
- 'use_default_value' => true
 
276
  );
277
  }
278
  }
279
  }
280
  }
281
 
 
 
 
 
282
  foreach ($pricings[0] as $pricing) {
283
  // Addding pricing to options
284
  $valueKey = $pricing['product_super_attribute_id'] . ':' . $pricing['value_index'];
241
  }
242
 
243
  $values = array();
244
+ $sortOrder = 1;
245
  foreach ($this->_items as $item) {
246
  $productAttribute = $item->getProductAttribute();
247
  if (!($productAttribute instanceof Mage_Eav_Model_Entity_Attribute_Abstract)) {
251
 
252
  $optionsByValue = array();
253
  foreach ($options as $option) {
254
+ $optionsByValue[$option['value']] = array('label' => $option['label'], 'order' => $sortOrder++);
255
  }
256
 
257
  foreach ($this->getProduct()->getTypeInstance(true)
267
  $values[$item->getId() . ':' . $optionValue] = array(
268
  'product_super_attribute_id' => $item->getId(),
269
  'value_index' => $optionValue,
270
+ 'label' => $optionsByValue[$optionValue]['label'],
271
+ 'default_label' => $optionsByValue[$optionValue]['label'],
272
+ 'store_label' => $optionsByValue[$optionValue]['label'],
273
  'is_percent' => 0,
274
  'pricing_value' => null,
275
+ 'use_default_value' => true,
276
+ 'order' => $optionsByValue[$optionValue]['order']
277
  );
278
  }
279
  }
280
  }
281
  }
282
 
283
+ uasort($values, function($a, $b) {
284
+ return $a['order'] - $b['order'];
285
+ });
286
+
287
  foreach ($pricings[0] as $pricing) {
288
  // Addding pricing to options
289
  $valueKey = $pricing['product_super_attribute_id'] . ':' . $pricing['value_index'];
app/code/core/Mage/Catalog/data/catalog_setup/data-upgrade-1.6.0.0.19.1.3-1.6.0.0.19.1.4.php ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magento.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magento.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Catalog
23
+ * @copyright Copyright (c) 2006-2016 X.commerce, Inc. and affiliates (http://www.magento.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+ /** @var $installer Mage_Catalog_Model_Resource_Setup */
27
+
28
+ $installer = $this;
29
+ $installer->startSetup();
30
+ $connection = $installer->getConnection();
31
+
32
+ $catalogProductEntityTypeId = Mage::getSingleton('eav/config')->getEntityType('catalog_product')->getEntityTypeId();
33
+
34
+ $attributes = Mage::getResourceModel('eav/entity_attribute_collection')
35
+ ->addFieldToFilter('frontend_input', 'multiselect')
36
+ ->addFieldToFilter('entity_type_id', $catalogProductEntityTypeId)
37
+ ->getItems();
38
+
39
+ foreach ($attributes as $attribute) {
40
+ $entityTypeId = $attribute->getEntityTypeId();
41
+ $attributeId = $attribute->getId();
42
+ $attributeTableOld = $installer->getAttributeTable($entityTypeId, $attributeId);
43
+
44
+ $installer->updateAttribute($entityTypeId, $attributeId, 'backend_type', 'text');
45
+
46
+ $attributeTableNew = $installer->getAttributeTable($entityTypeId, $attributeId);
47
+
48
+ if ($attributeTableOld != $attributeTableNew) {
49
+ $connection->disableTableKeys($attributeTableOld)
50
+ ->disableTableKeys($attributeTableNew);
51
+
52
+ $select = $connection->select()
53
+ ->from($attributeTableOld, array('entity_type_id', 'attribute_id', 'store_id', 'entity_id', 'value'))
54
+ ->where('entity_type_id = ?', $entityTypeId)
55
+ ->where('attribute_id = ?', $attributeId);
56
+
57
+ $query = $select->insertFromSelect($attributeTableNew,
58
+ array('entity_type_id', 'attribute_id', 'store_id', 'entity_id', 'value')
59
+ );
60
+
61
+ $connection->query($query);
62
+
63
+ $connection->delete($attributeTableOld,
64
+ $connection->quoteInto('entity_type_id = ?', $entityTypeId)
65
+ . $connection->quoteInto(' AND attribute_id = ?', $attributeId)
66
+ );
67
+
68
+ $connection->enableTableKeys($attributeTableOld)
69
+ ->enableTableKeys($attributeTableNew);
70
+ }
71
+ }
72
+
73
+ Mage::getModel('index/indexer')
74
+ ->getProcessByCode(Mage_Catalog_Helper_Product_Flat::CATALOG_FLAT_PROCESS_CODE)
75
+ ->changeStatus(Mage_Index_Model_Process::STATUS_REQUIRE_REINDEX);
76
+
77
+ $installer->endSetup();
app/code/core/Mage/Catalog/etc/config.xml CHANGED
@@ -28,7 +28,7 @@
28
  <config>
29
  <modules>
30
  <Mage_Catalog>
31
- <version>1.6.0.0.19.1.2</version>
32
  </Mage_Catalog>
33
  </modules>
34
  <admin>
@@ -807,6 +807,7 @@
807
  <product_image>
808
  <base_width>1800</base_width>
809
  <small_width>210</small_width>
 
810
  </product_image>
811
  <seo>
812
  <product_url_suffix>.html</product_url_suffix>
28
  <config>
29
  <modules>
30
  <Mage_Catalog>
31
+ <version>1.6.0.0.19.1.5</version>
32
  </Mage_Catalog>
33
  </modules>
34
  <admin>
807
  <product_image>
808
  <base_width>1800</base_width>
809
  <small_width>210</small_width>
810
+ <max_dimension>5000</max_dimension>
811
  </product_image>
812
  <seo>
813
  <product_url_suffix>.html</product_url_suffix>
app/code/core/Mage/Catalog/etc/system.xml CHANGED
@@ -211,6 +211,15 @@
211
  <show_in_website>1</show_in_website>
212
  <show_in_store>1</show_in_store>
213
  </small_width>
 
 
 
 
 
 
 
 
 
214
  </fields>
215
  </product_image>
216
  <placeholder translate="label">
211
  <show_in_website>1</show_in_website>
212
  <show_in_store>1</show_in_store>
213
  </small_width>
214
+ <max_dimension translate="label comment">
215
+ <label>Maximum resolution for upload image</label>
216
+ <comment>Maximum width and height resolutions for upload image</comment>
217
+ <frontend_type>text</frontend_type>
218
+ <sort_order>30</sort_order>
219
+ <show_in_default>1</show_in_default>
220
+ <show_in_website>1</show_in_website>
221
+ <show_in_store>1</show_in_store>
222
+ </max_dimension>
223
  </fields>
224
  </product_image>
225
  <placeholder translate="label">
app/code/core/Mage/Catalog/sql/catalog_setup/upgrade-1.6.0.0.19.1.2-1.6.0.0.19.1.3.php ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magento.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magento.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Catalog
23
+ * @copyright Copyright (c) 2006-2016 X.commerce, Inc. and affiliates (http://www.magento.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ $installer = $this;
28
+ /** @var $installer Mage_Catalog_Model_Resource_Setup */
29
+
30
+ $attribute = 'special_price';
31
+ $installer
32
+ ->updateAttribute(
33
+ Mage_Catalog_Model_Product::ENTITY,
34
+ 'special_price',
35
+ 'note',
36
+ NULL
37
+ )
38
+ ->updateAttribute(
39
+ Mage_Catalog_Model_Product::ENTITY,
40
+ 'special_price',
41
+ 'frontend_class',
42
+ 'validate-special-price'
43
+ )
44
+ ;
app/code/core/Mage/Catalog/sql/catalog_setup/upgrade-1.6.0.0.19.1.4-1.6.0.0.19.1.5.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magento.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magento.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Catalog
23
+ * @copyright Copyright (c) 2006-2016 X.commerce, Inc. and affiliates (http://www.magento.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /** @var $installer Mage_Catalog_Model_Resource_Setup */
28
+ $installer = $this;
29
+ $connection = $installer->getConnection();
30
+
31
+ $connection->addColumn($installer->getTable('catalog/product_attribute_group_price'), 'is_percent', array(
32
+ 'type' => Varien_Db_Ddl_Table::TYPE_SMALLINT,
33
+ 'unsigned' => true,
34
+ 'nullable' => false,
35
+ 'default' => '0',
36
+ 'comment' => 'Is Percent',
37
+ ));
app/code/core/Mage/CatalogInventory/Model/Observer.php CHANGED
@@ -384,6 +384,7 @@ class Mage_CatalogInventory_Model_Observer
384
  $stockItem = $option->getProduct()->getStockItem();
385
 
386
  if ($quoteItem->getProductType() == Mage_Catalog_Model_Product_Type::TYPE_CONFIGURABLE) {
 
387
  $stockItem->setProductName($quoteItem->getName());
388
  }
389
 
384
  $stockItem = $option->getProduct()->getStockItem();
385
 
386
  if ($quoteItem->getProductType() == Mage_Catalog_Model_Product_Type::TYPE_CONFIGURABLE) {
387
+ $stockItem->setParentItem($quoteItem);
388
  $stockItem->setProductName($quoteItem->getName());
389
  }
390
 
app/code/core/Mage/CatalogInventory/Model/Stock/Item.php CHANGED
@@ -525,6 +525,27 @@ class Mage_CatalogInventory_Model_Stock_Item extends Mage_Core_Model_Abstract
525
  $qty = Mage::app()->getLocale()->getNumber($qty);
526
  }
527
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
528
  /**
529
  * Check quantity type
530
  */
525
  $qty = Mage::app()->getLocale()->getNumber($qty);
526
  }
527
 
528
+ /**
529
+ * Check if child product assigned to parent
530
+ */
531
+ $parentItem = $this->getParentItem();
532
+ if ($this->getIsChildItem() && !empty($parentItem)) {
533
+ $typeInstance = $parentItem->getProduct()->getTypeInstance(true);
534
+ $requiredChildrenIds = $typeInstance->getChildrenIds($parentItem->getProductId(), true);
535
+ $childrenIds = array();
536
+ foreach ($requiredChildrenIds as $groupedChildrenIds) {
537
+ $childrenIds = array_merge($childrenIds, $groupedChildrenIds);
538
+ }
539
+ if (!in_array($this->getProductId(), $childrenIds)) {
540
+ $result->setHasError(true)
541
+ ->setMessage(Mage::helper('cataloginventory')
542
+ ->__('This product with current option is not available'))
543
+ ->setQuoteMessage(Mage::helper('cataloginventory')->__('Some of the products are not available'))
544
+ ->setQuoteMessageIndex('stock');
545
+ return $result;
546
+ }
547
+ }
548
+
549
  /**
550
  * Check quantity type
551
  */
app/code/core/Mage/CatalogRule/Model/Action/Index/Refresh.php CHANGED
@@ -319,8 +319,16 @@ class Mage_CatalogRule_Model_Action_Index_Refresh
319
  );
320
  $priceColumn = $this->_connection->getIfNullSql(
321
  $this->_connection->getIfNullSql(
322
- 'pg.value',
323
- 'pgd.value'
 
 
 
 
 
 
 
 
324
  ),
325
  'p.price'
326
  );
@@ -343,8 +351,22 @@ class Mage_CatalogRule_Model_Action_Index_Refresh
343
  );
344
  $priceColumn = $this->_connection->getIfNullSql(
345
  $this->_connection->getIfNullSql(
346
- 'pg.value',
347
- 'pgd.value'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
348
  ),
349
  $this->_connection->getIfNullSql(
350
  'p.value',
319
  );
320
  $priceColumn = $this->_connection->getIfNullSql(
321
  $this->_connection->getIfNullSql(
322
+ $this->_connection->getCheckSql(
323
+ 'pg.is_percent = 1',
324
+ 'p.price * (100 - pg.value)/100',
325
+ 'pg.value'
326
+ ),
327
+ $this->_connection->getCheckSql(
328
+ 'pgd.is_percent = 1',
329
+ 'p.price * (100 - pgd.value)/100',
330
+ 'pgd.value'
331
+ )
332
  ),
333
  'p.price'
334
  );
351
  );
352
  $priceColumn = $this->_connection->getIfNullSql(
353
  $this->_connection->getIfNullSql(
354
+ $this->_connection->getCheckSql(
355
+ 'pg.is_percent = 1',
356
+ $this->_connection->getIfNullSql(
357
+ 'p.value',
358
+ 'pd.value'
359
+ ) . ' * (100 - pg.value)/100',
360
+ 'pg.value'
361
+ ),
362
+ $this->_connection->getCheckSql(
363
+ 'pgd.is_percent = 1',
364
+ $this->_connection->getIfNullSql(
365
+ 'p.value',
366
+ 'pd.value'
367
+ ) . ' * (100 - pgd.value)/100',
368
+ 'pgd.value'
369
+ )
370
  ),
371
  $this->_connection->getIfNullSql(
372
  'p.value',
app/code/core/Mage/CatalogSearch/Model/Resource/Advanced.php CHANGED
@@ -84,7 +84,7 @@ class Mage_CatalogSearch_Model_Resource_Advanced extends Mage_Core_Model_Resourc
84
  if (is_array($value)) {
85
  if (!empty($value['from']) || !empty($value['to'])) { // range
86
  $condition = $value;
87
- } else if ($attribute->getBackendType() == 'varchar') { // multiselect
88
  $condition = array('in_set' => $value);
89
  } else if (!isset($value['from']) && !isset($value['to'])) { // select
90
  $condition = array('in' => $value);
84
  if (is_array($value)) {
85
  if (!empty($value['from']) || !empty($value['to'])) { // range
86
  $condition = $value;
87
+ } else if (in_array($attribute->getBackendType(), array('varchar', 'text'))) { // multiselect
88
  $condition = array('in_set' => $value);
89
  } else if (!isset($value['from']) && !isset($value['to'])) { // select
90
  $condition = array('in' => $value);
app/code/core/Mage/CatalogSearch/Model/Resource/Fulltext.php CHANGED
@@ -77,9 +77,10 @@ class Mage_CatalogSearch_Model_Resource_Fulltext extends Mage_Core_Model_Resourc
77
  */
78
  protected $_allowTableChanges = true;
79
 
80
-
81
-
82
-
 
83
 
84
  /**
85
  * Init resource model
@@ -298,12 +299,7 @@ class Mage_CatalogSearch_Model_Resource_Fulltext extends Mage_Core_Model_Resourc
298
  */
299
  public function resetSearchResults()
300
  {
301
- $adapter = $this->_getWriteAdapter();
302
- $adapter->update($this->getTable('catalogsearch/search_query'), array('is_processed' => 0));
303
- $adapter->delete($this->getTable('catalogsearch/result'));
304
-
305
  Mage::dispatchEvent('catalogsearch_reset_search_result');
306
-
307
  return $this;
308
  }
309
 
@@ -334,39 +330,38 @@ class Mage_CatalogSearch_Model_Resource_Fulltext extends Mage_Core_Model_Resourc
334
  public function prepareResult($object, $queryText, $query)
335
  {
336
  $adapter = $this->_getWriteAdapter();
337
- if (!$query->getIsProcessed()) {
338
- $searchType = $object->getSearchType($query->getStoreId());
339
-
340
- $preparedTerms = Mage::getResourceHelper('catalogsearch')
341
- ->prepareTerms($queryText, $query->getMaxQueryWords());
 
 
 
 
 
 
 
 
 
 
 
342
 
343
- $bind = array();
344
- $like = array();
345
- $likeCond = '';
346
- if ($searchType == Mage_CatalogSearch_Model_Fulltext::SEARCH_TYPE_LIKE
347
- || $searchType == Mage_CatalogSearch_Model_Fulltext::SEARCH_TYPE_COMBINE
348
- ) {
349
- $helper = Mage::getResourceHelper('core');
350
- $words = Mage::helper('core/string')->splitWords($queryText, true, $query->getMaxQueryWords());
351
- foreach ($words as $word) {
352
- $like[] = $helper->getCILike('s.data_index', $word, array('position' => 'any'));
353
- }
354
- if ($like) {
355
- $likeCond = '(' . join(' OR ', $like) . ')';
356
- }
357
  }
 
358
  $mainTableAlias = 's';
359
- $fields = array(
360
- 'query_id' => new Zend_Db_Expr($query->getId()),
361
- 'product_id',
362
- );
363
  $select = $adapter->select()
364
  ->from(array($mainTableAlias => $this->getMainTable()), $fields)
365
  ->joinInner(array('e' => $this->getTable('catalog/product')),
366
  'e.entity_id = s.product_id',
367
  array())
368
- ->where($mainTableAlias.'.store_id = ?', (int)$query->getStoreId());
369
 
 
370
  if ($searchType == Mage_CatalogSearch_Model_Fulltext::SEARCH_TYPE_FULLTEXT
371
  || $searchType == Mage_CatalogSearch_Model_Fulltext::SEARCH_TYPE_COMBINE
372
  ) {
@@ -374,11 +369,10 @@ class Mage_CatalogSearch_Model_Resource_Fulltext extends Mage_Core_Model_Resourc
374
  $where = Mage::getResourceHelper('catalogsearch')
375
  ->chooseFulltext($this->getMainTable(), $mainTableAlias, $select);
376
  }
377
-
378
  if ($likeCond != '' && $searchType == Mage_CatalogSearch_Model_Fulltext::SEARCH_TYPE_COMBINE) {
379
- $where .= ($where ? ' OR ' : '') . $likeCond;
380
  } elseif ($likeCond != '' && $searchType == Mage_CatalogSearch_Model_Fulltext::SEARCH_TYPE_LIKE) {
381
- $select->columns(array('relevance' => new Zend_Db_Expr(0)));
382
  $where = $likeCond;
383
  }
384
 
@@ -386,18 +380,22 @@ class Mage_CatalogSearch_Model_Resource_Fulltext extends Mage_Core_Model_Resourc
386
  $select->where($where);
387
  }
388
 
389
- $sql = $adapter->insertFromSelect($select,
390
- $this->getTable('catalogsearch/result'),
391
- array(),
392
- Varien_Db_Adapter_Interface::INSERT_ON_DUPLICATE);
393
- $adapter->query($sql, $bind);
394
-
395
- $query->setIsProcessed(1);
396
  }
397
 
398
  return $this;
399
  }
400
 
 
 
 
 
 
 
 
 
 
 
401
  /**
402
  * Retrieve EAV Config Singleton
403
  *
77
  */
78
  protected $_allowTableChanges = true;
79
 
80
+ /**
81
+ * @var array
82
+ */
83
+ protected $_foundData = array();
84
 
85
  /**
86
  * Init resource model
299
  */
300
  public function resetSearchResults()
301
  {
 
 
 
 
302
  Mage::dispatchEvent('catalogsearch_reset_search_result');
 
303
  return $this;
304
  }
305
 
330
  public function prepareResult($object, $queryText, $query)
331
  {
332
  $adapter = $this->_getWriteAdapter();
333
+ $searchType = $object->getSearchType($query->getStoreId());
334
+
335
+ $preparedTerms = Mage::getResourceHelper('catalogsearch')
336
+ ->prepareTerms($queryText, $query->getMaxQueryWords());
337
+
338
+ $bind = array();
339
+ $like = array();
340
+ $likeCond = '';
341
+ if ($searchType == Mage_CatalogSearch_Model_Fulltext::SEARCH_TYPE_LIKE
342
+ || $searchType == Mage_CatalogSearch_Model_Fulltext::SEARCH_TYPE_COMBINE
343
+ ) {
344
+ $helper = Mage::getResourceHelper('core');
345
+ $words = Mage::helper('core/string')->splitWords($queryText, true, $query->getMaxQueryWords());
346
+ foreach ($words as $word) {
347
+ $like[] = $helper->getCILike('s.data_index', $word, array('position' => 'any'));
348
+ }
349
 
350
+ if ($like) {
351
+ $likeCond = '(' . join(' OR ', $like) . ')';
 
 
 
 
 
 
 
 
 
 
 
 
352
  }
353
+
354
  $mainTableAlias = 's';
355
+ $fields = array('product_id');
356
+
 
 
357
  $select = $adapter->select()
358
  ->from(array($mainTableAlias => $this->getMainTable()), $fields)
359
  ->joinInner(array('e' => $this->getTable('catalog/product')),
360
  'e.entity_id = s.product_id',
361
  array())
362
+ ->where($mainTableAlias . '.store_id = ?', (int)$query->getStoreId());
363
 
364
+ $where = "";
365
  if ($searchType == Mage_CatalogSearch_Model_Fulltext::SEARCH_TYPE_FULLTEXT
366
  || $searchType == Mage_CatalogSearch_Model_Fulltext::SEARCH_TYPE_COMBINE
367
  ) {
369
  $where = Mage::getResourceHelper('catalogsearch')
370
  ->chooseFulltext($this->getMainTable(), $mainTableAlias, $select);
371
  }
 
372
  if ($likeCond != '' && $searchType == Mage_CatalogSearch_Model_Fulltext::SEARCH_TYPE_COMBINE) {
373
+ $where .= ($where ? ' OR ' : '') . $likeCond;
374
  } elseif ($likeCond != '' && $searchType == Mage_CatalogSearch_Model_Fulltext::SEARCH_TYPE_LIKE) {
375
+ $select->columns(array('relevance' => new Zend_Db_Expr(0)));
376
  $where = $likeCond;
377
  }
378
 
380
  $select->where($where);
381
  }
382
 
383
+ $this->_foundData = $adapter->fetchPairs($select, $bind);
 
 
 
 
 
 
384
  }
385
 
386
  return $this;
387
  }
388
 
389
+ /**
390
+ * Retrieve found data
391
+ *
392
+ * @return array
393
+ */
394
+ public function getFoundData()
395
+ {
396
+ return $this->_foundData;
397
+ }
398
+
399
  /**
400
  * Retrieve EAV Config Singleton
401
  *
app/code/core/Mage/CatalogSearch/Model/Resource/Fulltext/Collection.php CHANGED
@@ -34,6 +34,39 @@
34
  */
35
  class Mage_CatalogSearch_Model_Resource_Fulltext_Collection extends Mage_Catalog_Model_Resource_Product_Collection
36
  {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  /**
38
  * Retrieve query model object
39
  *
@@ -47,22 +80,101 @@ class Mage_CatalogSearch_Model_Resource_Fulltext_Collection extends Mage_Catalog
47
  /**
48
  * Add search query filter
49
  *
50
- * @param string $query
51
  * @return Mage_CatalogSearch_Model_Resource_Fulltext_Collection
52
  */
53
  public function addSearchFilter($query)
54
  {
55
- Mage::getSingleton('catalogsearch/fulltext')->prepareResult();
56
-
57
- $this->getSelect()->joinInner(
58
- array('search_result' => $this->getTable('catalogsearch/result')),
59
- $this->getConnection()->quoteInto(
60
- 'search_result.product_id=e.entity_id AND search_result.query_id=?',
61
- $this->_getQuery()->getId()
62
- ),
63
- array('relevance' => 'relevance')
64
- );
65
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  return $this;
67
  }
68
 
@@ -76,7 +188,8 @@ class Mage_CatalogSearch_Model_Resource_Fulltext_Collection extends Mage_Catalog
76
  public function setOrder($attribute, $dir = 'desc')
77
  {
78
  if ($attribute == 'relevance') {
79
- $this->getSelect()->order("relevance {$dir}");
 
80
  } else {
81
  parent::setOrder($attribute, $dir);
82
  }
@@ -84,7 +197,34 @@ class Mage_CatalogSearch_Model_Resource_Fulltext_Collection extends Mage_Catalog
84
  }
85
 
86
  /**
87
- * Stub method for campatibility with other search engines
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  *
89
  * @return Mage_CatalogSearch_Model_Resource_Fulltext_Collection
90
  */
@@ -92,4 +232,24 @@ class Mage_CatalogSearch_Model_Resource_Fulltext_Collection extends Mage_Catalog
92
  {
93
  return $this;
94
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  }
34
  */
35
  class Mage_CatalogSearch_Model_Resource_Fulltext_Collection extends Mage_Catalog_Model_Resource_Product_Collection
36
  {
37
+ /**
38
+ * Name for relevance order
39
+ */
40
+ const RELEVANCE_ORDER_NAME = 'relevance';
41
+
42
+ /**
43
+ * Found data
44
+ *
45
+ * @var array
46
+ */
47
+ protected $_foundData = null;
48
+
49
+ /**
50
+ * Sort order by relevance
51
+ *
52
+ * @var null
53
+ */
54
+ protected $_relevanceSortOrder = SORT_DESC;
55
+
56
+ /**
57
+ * Sort by relevance flag
58
+ *
59
+ * @var bool
60
+ */
61
+ protected $_sortByRelevance = false;
62
+
63
+ /**
64
+ * Is search filter applied flag
65
+ *
66
+ * @var bool
67
+ */
68
+ protected $_isSearchFiltersApplied = false;
69
+
70
  /**
71
  * Retrieve query model object
72
  *
80
  /**
81
  * Add search query filter
82
  *
83
+ * @param $query
84
  * @return Mage_CatalogSearch_Model_Resource_Fulltext_Collection
85
  */
86
  public function addSearchFilter($query)
87
  {
88
+ return $this;
89
+ }
 
 
 
 
 
 
 
 
90
 
91
+ /**
92
+ * Before load handler
93
+ *
94
+ * @return Mage_Catalog_Model_Resource_Product_Collection
95
+ */
96
+ protected function _beforeLoad()
97
+ {
98
+ if (!$this->_isSearchFiltersApplied) {
99
+ $this->_applySearchFilters();
100
+ }
101
+
102
+ return parent::_beforeLoad();
103
+ }
104
+
105
+ /**
106
+ * Get collection size
107
+ *
108
+ * @return int
109
+ */
110
+ public function getSize()
111
+ {
112
+ if (!$this->_isSearchFiltersApplied) {
113
+ $this->_applySearchFilters();
114
+ }
115
+
116
+ return parent::getSize();
117
+ }
118
+
119
+ /**
120
+ * Apply collection search filter
121
+ *
122
+ * @return Mage_CatalogSearch_Model_Resource_Fulltext_Collection
123
+ */
124
+ protected function _applySearchFilters()
125
+ {
126
+ $foundIds = $this->getFoundIds();
127
+ if (!empty($foundIds)) {
128
+ $this->addIdFilter($foundIds);
129
+ } else {
130
+ $this->getSelect()->orWhere('FALSE');
131
+ }
132
+ $this->_isSearchFiltersApplied = true;
133
+
134
+ return $this;
135
+ }
136
+
137
+ /**
138
+ * Get found products ids
139
+ *
140
+ * @return array
141
+ */
142
+ public function getFoundIds()
143
+ {
144
+ if (is_null($this->_foundData)) {
145
+ /** @var Mage_CatalogSearch_Model_Fulltext $preparedResult */
146
+ $preparedResult = Mage::getSingleton('catalogsearch/fulltext');
147
+ $preparedResult->prepareResult();
148
+ $this->_foundData = $preparedResult->getResource()->getFoundData();
149
+ }
150
+ if (isset($this->_orders[self::RELEVANCE_ORDER_NAME])) {
151
+ $this->_resortFoundDataByRelevance();
152
+ }
153
+ return array_keys($this->_foundData);
154
+ }
155
+
156
+ /**
157
+ * Resort found data by relevance
158
+ *
159
+ * @return Mage_CatalogSearch_Model_Resource_Fulltext_Collection
160
+ */
161
+ protected function _resortFoundDataByRelevance()
162
+ {
163
+ if (is_array($this->_foundData)) {
164
+ $data = array();
165
+ foreach ($this->_foundData as $id => $relevance) {
166
+ $this->_foundData[$id] = $relevance . '_' . $id;
167
+ }
168
+ natsort($this->_foundData);
169
+ if ($this->_relevanceSortOrder == SORT_DESC) {
170
+ $this->_foundData = array_reverse($this->_foundData);
171
+ }
172
+ foreach ($this->_foundData as $dataString) {
173
+ list ($relevance, $id) = explode('_', $dataString);
174
+ $data[$id] = $relevance;
175
+ }
176
+ $this->_foundData = $data;
177
+ }
178
  return $this;
179
  }
180
 
188
  public function setOrder($attribute, $dir = 'desc')
189
  {
190
  if ($attribute == 'relevance') {
191
+ $this->_relevanceSortOrder = ($dir == 'asc') ? SORT_ASC : SORT_DESC;
192
+ $this->addOrder(self::RELEVANCE_ORDER_NAME);
193
  } else {
194
  parent::setOrder($attribute, $dir);
195
  }
197
  }
198
 
199
  /**
200
+ * Add sorting by relevance to select
201
+ *
202
+ * @return Mage_CatalogSearch_Model_Resource_Fulltext_Collection
203
+ */
204
+ protected function _addRelevanceSorting()
205
+ {
206
+ $foundIds = $this->getFoundIds();
207
+ if (!$foundIds) {
208
+ return $this;
209
+ }
210
+
211
+ /** @var Mage_CatalogSearch_Model_Resource_Helper_Mysql4 $resourceHelper */
212
+ $resourceHelper = Mage::getResourceHelper('catalogsearch');
213
+ $this->_select->order(
214
+ new Zend_Db_Expr(
215
+ $resourceHelper->getFieldOrderExpression(
216
+ 'e.' . $this->getResource()->getIdFieldName(),
217
+ $foundIds
218
+ )
219
+ . ' ' . Zend_Db_Select::SQL_ASC
220
+ )
221
+ );
222
+
223
+ return $this;
224
+ }
225
+
226
+ /**
227
+ * Stub method for compatibility with other search engines
228
  *
229
  * @return Mage_CatalogSearch_Model_Resource_Fulltext_Collection
230
  */
232
  {
233
  return $this;
234
  }
235
+
236
+ /**
237
+ * Render sql select orders
238
+ *
239
+ * @return Varien_Data_Collection_Db
240
+ */
241
+ protected function _renderOrders()
242
+ {
243
+ if (!$this->_isOrdersRendered) {
244
+ foreach ($this->_orders as $attribute => $direction) {
245
+ if ($attribute == self::RELEVANCE_ORDER_NAME) {
246
+ $this->_addRelevanceSorting();
247
+ } else {
248
+ $this->addAttributeToSort($attribute, $direction);
249
+ }
250
+ }
251
+ $this->_isOrdersRendered = true;
252
+ }
253
+ return $this;
254
+ }
255
  }
app/code/core/Mage/CatalogSearch/Model/Resource/Helper/Mysql4.php CHANGED
@@ -52,6 +52,7 @@ class Mage_CatalogSearch_Model_Resource_Helper_Mysql4 extends Mage_Eav_Model_Res
52
  * Prepare Terms
53
  *
54
  * @param string $str The source string
 
55
  * @return array(0=>words, 1=>terms)
56
  */
57
  function prepareTerms($str, $maxWordLength = 0)
@@ -112,10 +113,24 @@ class Mage_CatalogSearch_Model_Resource_Helper_Mysql4 extends Mage_Eav_Model_Res
112
  *
113
  * @param mixed $table The table to insert data into.
114
  * @param array $data Column-value pairs or array of column-value pairs.
115
- * @param arrat $fields update fields pairs or values
116
  * @return int The number of affected rows.
117
  */
118
  public function insertOnDuplicate($table, array $data, array $fields = array()) {
119
  return $this->_getWriteAdapter()->insertOnDuplicate($table, $data, $fields);
120
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
  }
52
  * Prepare Terms
53
  *
54
  * @param string $str The source string
55
+ * @param int $maxWordLength
56
  * @return array(0=>words, 1=>terms)
57
  */
58
  function prepareTerms($str, $maxWordLength = 0)
113
  *
114
  * @param mixed $table The table to insert data into.
115
  * @param array $data Column-value pairs or array of column-value pairs.
116
+ * @param array $fields update fields pairs or values
117
  * @return int The number of affected rows.
118
  */
119
  public function insertOnDuplicate($table, array $data, array $fields = array()) {
120
  return $this->_getWriteAdapter()->insertOnDuplicate($table, $data, $fields);
121
  }
122
+
123
+ /**
124
+ * Get field expression for order by
125
+ *
126
+ * @param string $fieldName
127
+ * @param array $orderedIds
128
+ *
129
+ * @return string
130
+ */
131
+ public function getFieldOrderExpression($fieldName, array $orderedIds)
132
+ {
133
+ $fieldName = $this->_getWriteAdapter()->quoteIdentifier($fieldName);
134
+ return "FIELD({$fieldName}, {$this->_getReadAdapter()->quote($orderedIds)})";
135
+ }
136
  }
app/code/core/Mage/Checkout/Model/Cart.php CHANGED
@@ -229,10 +229,6 @@ class Mage_Checkout_Model_Cart extends Varien_Object implements Mage_Checkout_Mo
229
  $request = new Varien_Object($requestInfo);
230
  }
231
 
232
- if (!$request->hasQty()) {
233
- $request->setQty(1);
234
- }
235
-
236
  return $request;
237
  }
238
 
@@ -248,14 +244,21 @@ class Mage_Checkout_Model_Cart extends Varien_Object implements Mage_Checkout_Mo
248
  $product = $this->_getProduct($productInfo);
249
  $request = $this->_getProductRequest($requestInfo);
250
 
 
 
 
 
 
 
 
251
  $productId = $product->getId();
252
 
253
- if ($product->getStockItem()) {
254
  $minimumQty = $product->getStockItem()->getMinSaleQty();
255
  //If product was not found in cart and there is set minimal qty for it
256
  if ($minimumQty && $minimumQty > 0 && $request->getQty() < $minimumQty
257
  && !$this->getQuote()->hasProductId($productId)
258
- ){
259
  $request->setQty($minimumQty);
260
  }
261
  }
229
  $request = new Varien_Object($requestInfo);
230
  }
231
 
 
 
 
 
232
  return $request;
233
  }
234
 
244
  $product = $this->_getProduct($productInfo);
245
  $request = $this->_getProductRequest($requestInfo);
246
 
247
+ /** @var Mage_Catalog_Helper_Product $helper */
248
+ $helper = Mage::helper('catalog/product');
249
+
250
+ if (!$request->hasQty()) {
251
+ $request->setQty($helper->getDefaultQty($product));
252
+ }
253
+
254
  $productId = $product->getId();
255
 
256
+ if (!$product->isConfigurable() && $product->getStockItem()) {
257
  $minimumQty = $product->getStockItem()->getMinSaleQty();
258
  //If product was not found in cart and there is set minimal qty for it
259
  if ($minimumQty && $minimumQty > 0 && $request->getQty() < $minimumQty
260
  && !$this->getQuote()->hasProductId($productId)
261
+ ) {
262
  $request->setQty($minimumQty);
263
  }
264
  }
app/code/core/Mage/Checkout/Model/Type/Multishipping.php CHANGED
@@ -308,6 +308,9 @@ class Mage_Checkout_Model_Type_Multishipping extends Mage_Checkout_Model_Type_Ab
308
  if (!$quoteAddress = $this->getQuote()->getShippingAddressByCustomerAddressId($address->getId())) {
309
  $quoteAddress = Mage::getModel('sales/quote_address')->importCustomerAddress($address);
310
  $this->getQuote()->addShippingAddress($quoteAddress);
 
 
 
311
  }
312
 
313
  $quoteAddress = $this->getQuote()->getShippingAddressByCustomerAddressId($address->getId());
308
  if (!$quoteAddress = $this->getQuote()->getShippingAddressByCustomerAddressId($address->getId())) {
309
  $quoteAddress = Mage::getModel('sales/quote_address')->importCustomerAddress($address);
310
  $this->getQuote()->addShippingAddress($quoteAddress);
311
+ if ($couponCode = $this->getCheckoutSession()->getCartCouponCode()) {
312
+ $this->getQuote()->setCouponCode($couponCode);
313
+ }
314
  }
315
 
316
  $quoteAddress = $this->getQuote()->getShippingAddressByCustomerAddressId($address->getId());
app/code/core/Mage/Checkout/Model/Type/Onepage.php CHANGED
@@ -362,6 +362,7 @@ class Mage_Checkout_Model_Type_Onepage
362
  ->setShippingMethod($shippingMethod)
363
  ->setCollectShippingRates(true);
364
  $this->getCheckout()->setStepData('shipping', 'complete', true);
 
365
  break;
366
  }
367
  }
@@ -592,6 +593,8 @@ class Mage_Checkout_Model_Type_Onepage
592
  return array('error' => 1, 'message' => $validateRes);
593
  }
594
 
 
 
595
  $this->getQuote()->collectTotals()->save();
596
 
597
  $this->getCheckout()
@@ -946,4 +949,17 @@ class Mage_Checkout_Model_Type_Onepage
946
  }
947
  return $orderId;
948
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
949
  }
362
  ->setShippingMethod($shippingMethod)
363
  ->setCollectShippingRates(true);
364
  $this->getCheckout()->setStepData('shipping', 'complete', true);
365
+ $this->_setCartCouponCode();
366
  break;
367
  }
368
  }
593
  return array('error' => 1, 'message' => $validateRes);
594
  }
595
 
596
+ $this->_setCartCouponCode();
597
+
598
  $this->getQuote()->collectTotals()->save();
599
 
600
  $this->getCheckout()
949
  }
950
  return $orderId;
951
  }
952
+
953
+ /**
954
+ * Sets cart coupon code from checkout to quote
955
+ *
956
+ * @return $this
957
+ */
958
+ protected function _setCartCouponCode()
959
+ {
960
+ if ($couponCode = $this->getCheckout()->getCartCouponCode()) {
961
+ $this->getQuote()->setCouponCode($couponCode);
962
+ }
963
+ return $this;
964
+ }
965
  }
app/code/core/Mage/Checkout/controllers/CartController.php CHANGED
@@ -89,7 +89,10 @@ class Mage_Checkout_CartController extends Mage_Core_Controller_Front_Action
89
  ) {
90
  $this->getResponse()->setRedirect($backUrl);
91
  } else {
92
- if ((strtolower($this->getRequest()->getActionName()) == 'add') && !$this->getRequest()->getParam('in_cart')) {
 
 
 
93
  $this->_getSession()->setContinueShoppingUrl($this->_getRefererUrl());
94
  }
95
  $this->_redirect('checkout/cart');
@@ -141,6 +144,20 @@ class Mage_Checkout_CartController extends Mage_Core_Controller_Front_Action
141
  $cart = $this->_getCart();
142
  if ($cart->getQuote()->getItemsCount()) {
143
  $cart->init();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
  $cart->save();
145
 
146
  if (!$this->_getQuote()->validateMinimumAmount()) {
@@ -526,6 +543,13 @@ class Mage_Checkout_CartController extends Mage_Core_Controller_Front_Action
526
  ->setRegion($region)
527
  ->setCollectShippingRates(true);
528
  $this->_getQuote()->save();
 
 
 
 
 
 
 
529
  $this->_goBack();
530
  }
531
 
@@ -581,6 +605,7 @@ class Mage_Checkout_CartController extends Mage_Core_Controller_Front_Action
581
  $this->_getSession()->addSuccess(
582
  $this->__('Coupon code "%s" was applied.', Mage::helper('core')->escapeHtml($couponCode))
583
  );
 
584
  } else {
585
  $this->_getSession()->addError(
586
  $this->__('Coupon code "%s" is not valid.', Mage::helper('core')->escapeHtml($couponCode))
89
  ) {
90
  $this->getResponse()->setRedirect($backUrl);
91
  } else {
92
+ if (
93
+ (strtolower($this->getRequest()->getActionName()) == 'add')
94
+ && !$this->getRequest()->getParam('in_cart')
95
+ ) {
96
  $this->_getSession()->setContinueShoppingUrl($this->_getRefererUrl());
97
  }
98
  $this->_redirect('checkout/cart');
144
  $cart = $this->_getCart();
145
  if ($cart->getQuote()->getItemsCount()) {
146
  $cart->init();
147
+ if (
148
+ $cart->getQuote()->getShippingAddress()
149
+ && $this->_getSession()->getEstimatedShippingAddressData()
150
+ && $couponCode = $this->_getSession()->getCartCouponCode()
151
+ ) {
152
+ $estimatedSessionAddressData = $this->_getSession()->getEstimatedShippingAddressData();
153
+ $cart->getQuote()->getShippingAddress()
154
+ ->setCountryId($estimatedSessionAddressData['country_id'])
155
+ ->setCity($estimatedSessionAddressData['city'])
156
+ ->setPostcode($estimatedSessionAddressData['postcode'])
157
+ ->setRegionId($estimatedSessionAddressData['region_id'])
158
+ ->setRegion($estimatedSessionAddressData['region']);
159
+ $cart->getQuote()->setCouponCode($couponCode);
160
+ }
161
  $cart->save();
162
 
163
  if (!$this->_getQuote()->validateMinimumAmount()) {
543
  ->setRegion($region)
544
  ->setCollectShippingRates(true);
545
  $this->_getQuote()->save();
546
+ $this->_getSession()->setEstimatedShippingAddressData(array(
547
+ 'country_id' => $country,
548
+ 'postcode' => $postcode,
549
+ 'city' => $city,
550
+ 'region_id' => $regionId,
551
+ 'region' => $region
552
+ ));
553
  $this->_goBack();
554
  }
555
 
605
  $this->_getSession()->addSuccess(
606
  $this->__('Coupon code "%s" was applied.', Mage::helper('core')->escapeHtml($couponCode))
607
  );
608
+ $this->_getSession()->setCartCouponCode($couponCode);
609
  } else {
610
  $this->_getSession()->addError(
611
  $this->__('Coupon code "%s" is not valid.', Mage::helper('core')->escapeHtml($couponCode))
app/code/core/Mage/Checkout/controllers/OnepageController.php CHANGED
@@ -334,8 +334,7 @@ class Mage_Checkout_OnepageController extends Mage_Checkout_Controller_Action
334
  $address = $this->getOnepage()->getAddress($addressId);
335
 
336
  if (Mage::getSingleton('customer/session')->getCustomer()->getId() == $address->getCustomerId()) {
337
- $this->getResponse()->setHeader('Content-type', 'application/x-json');
338
- $this->getResponse()->setBody($address->toJson());
339
  } else {
340
  $this->getResponse()->setHeader('HTTP/1.1','403 Forbidden');
341
  }
@@ -353,7 +352,7 @@ class Mage_Checkout_OnepageController extends Mage_Checkout_Controller_Action
353
  if ($this->getRequest()->isPost()) {
354
  $method = $this->getRequest()->getPost('method');
355
  $result = $this->getOnepage()->saveCheckoutMethod($method);
356
- $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result));
357
  }
358
  }
359
 
@@ -395,7 +394,7 @@ class Mage_Checkout_OnepageController extends Mage_Checkout_Controller_Action
395
  }
396
  }
397
 
398
- $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result));
399
  }
400
  }
401
 
@@ -419,7 +418,7 @@ class Mage_Checkout_OnepageController extends Mage_Checkout_Controller_Action
419
  'html' => $this->_getShippingMethodsHtml()
420
  );
421
  }
422
- $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result));
423
  }
424
  }
425
 
@@ -442,7 +441,7 @@ class Mage_Checkout_OnepageController extends Mage_Checkout_Controller_Action
442
  'request' => $this->getRequest(),
443
  'quote' => $this->getOnepage()->getQuote()));
444
  $this->getOnepage()->getQuote()->collectTotals();
445
- $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result));
446
 
447
  $result['goto_section'] = 'payment';
448
  $result['update_section'] = array(
@@ -451,7 +450,7 @@ class Mage_Checkout_OnepageController extends Mage_Checkout_Controller_Action
451
  );
452
  }
453
  $this->getOnepage()->getQuote()->collectTotals()->save();
454
- $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result));
455
  }
456
  }
457
 
@@ -498,7 +497,7 @@ class Mage_Checkout_OnepageController extends Mage_Checkout_Controller_Action
498
  Mage::logException($e);
499
  $result['error'] = $this->__('Unable to set Payment Method.');
500
  }
501
- $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result));
502
  }
503
 
504
  /**
@@ -561,7 +560,7 @@ class Mage_Checkout_OnepageController extends Mage_Checkout_Controller_Action
561
  $result['success'] = false;
562
  $result['error'] = true;
563
  $result['error_messages'] = $this->__('Please agree to all the terms and conditions before placing the order.');
564
- $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result));
565
  return;
566
  }
567
  }
@@ -630,7 +629,7 @@ class Mage_Checkout_OnepageController extends Mage_Checkout_Controller_Action
630
  $result['redirect'] = $redirectUrl;
631
  }
632
 
633
- $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result));
634
  }
635
 
636
  /**
@@ -657,4 +656,17 @@ class Mage_Checkout_OnepageController extends Mage_Checkout_Controller_Action
657
  || Mage::helper('checkout')->isAllowedGuestCheckout($this->getOnepage()->getQuote())
658
  || !Mage::helper('checkout')->isCustomerMustBeLogged();
659
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
660
  }
334
  $address = $this->getOnepage()->getAddress($addressId);
335
 
336
  if (Mage::getSingleton('customer/session')->getCustomer()->getId() == $address->getCustomerId()) {
337
+ $this->_prepareDataJSON($address->toArray());
 
338
  } else {
339
  $this->getResponse()->setHeader('HTTP/1.1','403 Forbidden');
340
  }
352
  if ($this->getRequest()->isPost()) {
353
  $method = $this->getRequest()->getPost('method');
354
  $result = $this->getOnepage()->saveCheckoutMethod($method);
355
+ $this->_prepareDataJSON($result);
356
  }
357
  }
358
 
394
  }
395
  }
396
 
397
+ $this->_prepareDataJSON($result);
398
  }
399
  }
400
 
418
  'html' => $this->_getShippingMethodsHtml()
419
  );
420
  }
421
+ $this->_prepareDataJSON($result);
422
  }
423
  }
424
 
441
  'request' => $this->getRequest(),
442
  'quote' => $this->getOnepage()->getQuote()));
443
  $this->getOnepage()->getQuote()->collectTotals();
444
+ $this->_prepareDataJSON($result);
445
 
446
  $result['goto_section'] = 'payment';
447
  $result['update_section'] = array(
450
  );
451
  }
452
  $this->getOnepage()->getQuote()->collectTotals()->save();
453
+ $this->_prepareDataJSON($result);
454
  }
455
  }
456
 
497
  Mage::logException($e);
498
  $result['error'] = $this->__('Unable to set Payment Method.');
499
  }
500
+ $this->_prepareDataJSON($result);
501
  }
502
 
503
  /**
560
  $result['success'] = false;
561
  $result['error'] = true;
562
  $result['error_messages'] = $this->__('Please agree to all the terms and conditions before placing the order.');
563
+ $this->_prepareDataJSON($result);
564
  return;
565
  }
566
  }
629
  $result['redirect'] = $redirectUrl;
630
  }
631
 
632
+ $this->_prepareDataJSON($result);
633
  }
634
 
635
  /**
656
  || Mage::helper('checkout')->isAllowedGuestCheckout($this->getOnepage()->getQuote())
657
  || !Mage::helper('checkout')->isCustomerMustBeLogged();
658
  }
659
+
660
+ /**
661
+ * Prepare JSON formatted data for response to client
662
+ *
663
+ * @param $response
664
+ * @return Zend_Controller_Response_Abstract
665
+ */
666
+ protected function _prepareDataJSON($response)
667
+ {
668
+ $this->getResponse()->setHeader('Content-type', 'application/json', true);
669
+ return $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($response));
670
+ }
671
+
672
  }
app/code/core/Mage/Cms/Block/Page.php CHANGED
@@ -63,14 +63,28 @@ class Mage_Cms_Block_Page extends Mage_Core_Block_Abstract
63
  protected function _prepareLayout()
64
  {
65
  $page = $this->getPage();
 
66
 
67
  // show breadcrumbs
68
  if (Mage::getStoreConfig('web/default/show_cms_breadcrumbs')
69
  && ($breadcrumbs = $this->getLayout()->getBlock('breadcrumbs'))
70
  && ($page->getIdentifier()!==Mage::getStoreConfig('web/default/cms_home_page'))
71
  && ($page->getIdentifier()!==Mage::getStoreConfig('web/default/cms_no_route'))) {
72
- $breadcrumbs->addCrumb('home', array('label'=>Mage::helper('cms')->__('Home'), 'title'=>Mage::helper('cms')->__('Go to Home Page'), 'link'=>Mage::getBaseUrl()));
73
- $breadcrumbs->addCrumb('cms_page', array('label'=>$page->getTitle(), 'title'=>$page->getTitle()));
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  }
75
 
76
  $root = $this->getLayout()->getBlock('root');
@@ -85,6 +99,14 @@ class Mage_Cms_Block_Page extends Mage_Core_Block_Abstract
85
  $head->setDescription($page->getMetaDescription());
86
  }
87
 
 
 
 
 
 
 
 
 
88
  return parent::_prepareLayout();
89
  }
90
 
63
  protected function _prepareLayout()
64
  {
65
  $page = $this->getPage();
66
+ $breadcrumbsArray = array();
67
 
68
  // show breadcrumbs
69
  if (Mage::getStoreConfig('web/default/show_cms_breadcrumbs')
70
  && ($breadcrumbs = $this->getLayout()->getBlock('breadcrumbs'))
71
  && ($page->getIdentifier()!==Mage::getStoreConfig('web/default/cms_home_page'))
72
  && ($page->getIdentifier()!==Mage::getStoreConfig('web/default/cms_no_route'))) {
73
+ $breadcrumbsArray[] = array(
74
+ 'crumbName' => 'home',
75
+ 'crumbInfo' => array(
76
+ 'label' => Mage::helper('cms')->__('Home'),
77
+ 'title' => Mage::helper('cms')->__('Go to Home Page'),
78
+ 'link' => Mage::getBaseUrl()
79
+ )
80
+ );
81
+ $breadcrumbsArray[] = array(
82
+ 'crumbName' => 'cms_page',
83
+ 'crumbInfo' => array(
84
+ 'label' => $page->getTitle(),
85
+ 'title' => $page->getTitle()
86
+ )
87
+ );
88
  }
89
 
90
  $root = $this->getLayout()->getBlock('root');
99
  $head->setDescription($page->getMetaDescription());
100
  }
101
 
102
+ $breadcrumbsObject = new Varien_Object();
103
+ $breadcrumbsObject->setCrumbs($breadcrumbsArray);
104
+
105
+ Mage::dispatchEvent('cms_generate_breadcrumbs', array('breadcrumbs' => $breadcrumbsObject));
106
+
107
+ foreach ($breadcrumbsObject->getCrumbs() as $breadcrumbsItem) {
108
+ $breadcrumbs->addCrumb($breadcrumbsItem['crumbName'], $breadcrumbsItem['crumbInfo']);
109
+ }
110
  return parent::_prepareLayout();
111
  }
112
 
app/code/core/Mage/Cms/Helper/Wysiwyg/Images.php CHANGED
@@ -222,7 +222,8 @@ class Mage_Cms_Helper_Wysiwyg_Images extends Mage_Core_Helper_Abstract
222
  }
223
  $io = new Varien_Io_File();
224
  if (!$io->isWriteable($currentPath) && !$io->mkdir($currentPath)) {
225
- $message = Mage::helper('cms')->__('The directory %s is not writable by server.',$currentPath);
 
226
  Mage::throwException($message);
227
  }
228
  $this->_currentPath = $currentPath;
222
  }
223
  $io = new Varien_Io_File();
224
  if (!$io->isWriteable($currentPath) && !$io->mkdir($currentPath)) {
225
+ $message = Mage::helper('cms')->__('The directory %s is not writable by server.',
226
+ $io->getFilteredPath($currentPath));
227
  Mage::throwException($message);
228
  }
229
  $this->_currentPath = $currentPath;
app/code/core/Mage/Cms/Model/Wysiwyg/Images/Storage.php CHANGED
@@ -227,17 +227,18 @@ class Mage_Cms_Model_Wysiwyg_Images_Storage extends Varien_Object
227
  $rootCmp = rtrim($this->getHelper()->getStorageRoot(), DS);
228
  $pathCmp = rtrim($path, DS);
229
 
 
 
230
  if ($rootCmp == $pathCmp) {
231
- Mage::throwException(Mage::helper('cms')->__('Cannot delete root directory %s.', $path));
 
232
  }
233
 
234
- $io = new Varien_Io_File();
235
-
236
  if (Mage::helper('core/file_storage_database')->checkDbUsage()) {
237
  Mage::getModel('core/file_storage_directory_database')->deleteDirectory($path);
238
  }
239
  if (!$io->rmdir($path, true)) {
240
- Mage::throwException(Mage::helper('cms')->__('Cannot delete directory %s.', $path));
241
  }
242
 
243
  if (strpos($pathCmp, $rootCmp) === 0) {
227
  $rootCmp = rtrim($this->getHelper()->getStorageRoot(), DS);
228
  $pathCmp = rtrim($path, DS);
229
 
230
+ $io = new Varien_Io_File();
231
+
232
  if ($rootCmp == $pathCmp) {
233
+ Mage::throwException(Mage::helper('cms')->__('Cannot delete root directory %s.',
234
+ $io->getFilteredPath($path)));
235
  }
236
 
 
 
237
  if (Mage::helper('core/file_storage_database')->checkDbUsage()) {
238
  Mage::getModel('core/file_storage_directory_database')->deleteDirectory($path);
239
  }
240
  if (!$io->rmdir($path, true)) {
241
+ Mage::throwException(Mage::helper('cms')->__('Cannot delete directory %s.', $io->getFilteredPath($path)));
242
  }
243
 
244
  if (strpos($pathCmp, $rootCmp) === 0) {
app/code/core/Mage/ConfigurableSwatches/Block/Catalog/Product/List/Price.php ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magento.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magento.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_ConfigurableSwatches
23
+ * @copyright Copyright (c) 2006-2016 X.commerce, Inc. and affiliates (http://www.magento.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ class Mage_ConfigurableSwatches_Block_Catalog_Product_List_Price extends Mage_Core_Block_Template
28
+ {
29
+ /**
30
+ * @var string
31
+ */
32
+ protected $_template = 'configurableswatches/catalog/product/list/price/js.phtml';
33
+
34
+ /**
35
+ * Get target product IDs from product collection
36
+ * which was set on block
37
+ *
38
+ * @return Mage_Eav_Model_Entity_Collection_Abstract
39
+ */
40
+ protected function getProducts()
41
+ {
42
+ return $this->getProductCollection();
43
+ }
44
+
45
+ /**
46
+ * Get configuration for configurable swatches price change
47
+ *
48
+ * @return string
49
+ */
50
+ public function getJsonConfig()
51
+ {
52
+ /** @var Mage_Catalog_Helper_Product_Type_Composite $compositeProductHelper */
53
+ $compositeProductHelper = $this->helper('catalog/product_type_composite');
54
+
55
+ $config = array(
56
+ 'generalConfig' => $compositeProductHelper->prepareJsonGeneralConfig()
57
+ );
58
+ foreach ($this->getProducts() as $product) {
59
+ /** @var $product Mage_Catalog_Model_Product */
60
+ if (!$product->getSwatchPrices()) {
61
+ continue;
62
+ }
63
+
64
+ $config['products'][$product->getId()] = $compositeProductHelper->prepareJsonProductConfig($product);
65
+ $config['products'][$product->getId()]['swatchPrices'] = $product->getSwatchPrices();
66
+
67
+ $responseObject = new Varien_Object();
68
+ Mage::dispatchEvent('catalog_product_view_config', array(
69
+ 'response_object' => $responseObject,
70
+ 'product' => $product,
71
+ ));
72
+ if (is_array($responseObject->getAdditionalOptions())) {
73
+ foreach ($responseObject->getAdditionalOptions() as $option => $value) {
74
+ $config['products'][$product->getId()][$option] = $value;
75
+ }
76
+ }
77
+ }
78
+ return $this->helper('core')->jsonEncode($config);
79
+ }
80
+
81
+ /**
82
+ * Disable output if all preconditions doesn't meet
83
+ *
84
+ * @return string
85
+ */
86
+ protected function _toHtml()
87
+ {
88
+ if (!$this->helper('configurableswatches/list_price')->isEnabled()) {
89
+ return '';
90
+ }
91
+
92
+ return parent::_toHtml();
93
+ }
94
+
95
+ }
app/code/core/Mage/ConfigurableSwatches/Helper/Data.php CHANGED
@@ -92,7 +92,10 @@ class Mage_ConfigurableSwatches_Helper_Data extends Mage_Core_Helper_Abstract
92
  public function getSwatchAttributeIds()
93
  {
94
  if (is_null($this->_configAttributeIds)) {
95
- $this->_configAttributeIds = explode(',', Mage::getStoreConfig(self::CONFIG_PATH_SWATCH_ATTRIBUTES));
 
 
 
96
  }
97
  return $this->_configAttributeIds;
98
  }
@@ -111,4 +114,27 @@ class Mage_ConfigurableSwatches_Helper_Data extends Mage_Core_Helper_Abstract
111
  $configAttrs = $this->getSwatchAttributeIds();
112
  return in_array($attr, $configAttrs);
113
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
  }
92
  public function getSwatchAttributeIds()
93
  {
94
  if (is_null($this->_configAttributeIds)) {
95
+ $this->_configAttributeIds = array();
96
+ if (Mage::getStoreConfig(self::CONFIG_PATH_SWATCH_ATTRIBUTES)) {
97
+ $this->_configAttributeIds = explode(',', Mage::getStoreConfig(self::CONFIG_PATH_SWATCH_ATTRIBUTES));
98
+ }
99
  }
100
  return $this->_configAttributeIds;
101
  }
114
  $configAttrs = $this->getSwatchAttributeIds();
115
  return in_array($attr, $configAttrs);
116
  }
117
+
118
+ /**
119
+ * Get swatches product javascript
120
+ *
121
+ * @return string
122
+ */
123
+ public function getSwatchesProductJs()
124
+ {
125
+ /**
126
+ * @var $product Mage_Catalog_Model_Product
127
+ */
128
+ $product = Mage::registry('current_product');
129
+ if ($this->isEnabled() && $product) {
130
+ $configAttrs = $this->getSwatchAttributeIds();
131
+ $configurableAttributes = $product->getTypeInstance(true)->getConfigurableAttributesAsArray($product);
132
+ foreach ($configurableAttributes as $configurableAttribute) {
133
+ if (in_array($configurableAttribute['attribute_id'], $configAttrs)) {
134
+ return 'js/configurableswatches/swatches-product.js';
135
+ }
136
+ }
137
+ }
138
+ return '';
139
+ }
140
  }
app/code/core/Mage/ConfigurableSwatches/Helper/List/Price.php ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magento.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magento.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_ConfigurableSwatches
23
+ * @copyright Copyright (c) 2006-2016 X.commerce, Inc. and affiliates (http://www.magento.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Class implementing price change for swatches in product listing pages
29
+ */
30
+ class Mage_ConfigurableSwatches_Helper_List_Price extends Mage_Core_Helper_Abstract
31
+ {
32
+ /**
33
+ * Path to to check is it required to change prices
34
+ */
35
+ const XML_PATH_SWATCH_PRICE = 'configswatches/general/product_list_price_change';
36
+
37
+ /**
38
+ * Set swatch_price on products where swatch option_id is set
39
+ * Depends on following product data:
40
+ * - product must have children products attached and be configurable by type
41
+ *
42
+ * @param array $products
43
+ * @param int $storeId
44
+ * @return void
45
+ */
46
+ public function attachConfigurableProductChildrenPricesMapping(array $products, $storeId = null)
47
+ {
48
+ $listSwatchAttrId = Mage::helper('configurableswatches/productlist')->getSwatchAttributeId();
49
+ $result = array();
50
+
51
+ foreach ($products as $product) {
52
+ /** @var $product Mage_Catalog_Model_Product */
53
+ if ($product->getTypeId() !== Mage_Catalog_Model_Product_Type_Configurable::TYPE_CODE
54
+ && !is_array($product->getChildrenProducts())
55
+ ) {
56
+ continue;
57
+ }
58
+
59
+ /** @var Mage_Catalog_Model_Product_Type_Configurable $typeInstance */
60
+ $typeInstance = $product->getTypeInstance();
61
+ $allowedAttributes = $typeInstance->getConfigurableAttributeCollection($product);
62
+ foreach ($allowedAttributes as $attribute) {
63
+ /** @var $attribute Mage_Catalog_Model_Product_Type_Configurable_Attribute */
64
+ if ($attribute->getAttributeId() !== $listSwatchAttrId) {
65
+ continue;
66
+ }
67
+
68
+ foreach ($attribute->getPrices() as $attributePrice) {
69
+ $product->setConfigurablePrice(
70
+ $this->_getHelper()->preparePrice(
71
+ $product,
72
+ $attributePrice['pricing_value'],
73
+ $attributePrice['is_percent'],
74
+ $storeId
75
+ )
76
+ );
77
+ Mage::dispatchEvent(
78
+ 'catalog_product_type_configurable_price',
79
+ array('product' => $product)
80
+ );
81
+ $configurablePrice = $product->getConfigurablePrice();
82
+ $cofigurableSwatchesHelper = Mage::helper('configurableswatches');
83
+ $result[$cofigurableSwatchesHelper::normalizeKey($attributePrice['store_label'])] = array(
84
+ 'price' => $configurablePrice,
85
+ 'oldPrice' => $this->_getHelper()->prepareOldPrice(
86
+ $product,
87
+ $attributePrice['pricing_value'],
88
+ $attributePrice['is_percent'],
89
+ $storeId
90
+ ),
91
+ );
92
+ }
93
+ }
94
+ $product->setSwatchPrices($result);
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Get helper for calculation purposes
100
+ *
101
+ * @return Mage_Catalog_Helper_Product_Type_Composite
102
+ */
103
+ protected function _getHelper()
104
+ {
105
+ return Mage::helper('catalog/product_type_composite');
106
+ }
107
+
108
+ /**
109
+ * Check if option for swatches price change is enabled
110
+ *
111
+ * @return bool
112
+ */
113
+ public function isEnabled()
114
+ {
115
+ return Mage::getStoreConfigFlag(self::XML_PATH_SWATCH_PRICE);
116
+ }
117
+
118
+ }
app/code/core/Mage/ConfigurableSwatches/Helper/Mediafallback.php CHANGED
@@ -37,12 +37,39 @@ class Mage_ConfigurableSwatches_Helper_Mediafallback extends Mage_Core_Helper_Ab
37
  * - product must have children products attached
38
  *
39
  * @param array $parentProducts
 
40
  * @param $storeId
41
  * @return void
42
  */
43
  public function attachConfigurableProductChildrenAttributeMapping(array $parentProducts, $storeId)
44
  {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  $listSwatchAttr = Mage::helper('configurableswatches/productlist')->getSwatchAttribute();
 
 
 
 
 
 
 
 
 
 
46
 
47
  $parentProductIds = array();
48
  /* @var $parentProduct Mage_Catalog_Model_Product */
@@ -53,6 +80,7 @@ class Mage_ConfigurableSwatches_Helper_Mediafallback extends Mage_Core_Helper_Ab
53
  $configAttributes = Mage::getResourceModel('configurableswatches/catalog_product_attribute_super_collection')
54
  ->addParentProductsFilter($parentProductIds)
55
  ->attachEavAttributes()
 
56
  ->setStoreId($storeId)
57
  ;
58
 
@@ -61,9 +89,15 @@ class Mage_ConfigurableSwatches_Helper_Mediafallback extends Mage_Core_Helper_Ab
61
  $optionLabels += $attribute->getOptionLabels();
62
  }
63
 
 
 
 
 
 
64
  foreach ($parentProducts as $parentProduct) {
65
  $mapping = array();
66
  $listSwatchValues = array();
 
67
 
68
  /* @var $attribute Mage_Catalog_Model_Product_Type_Configurable_Attribute */
69
  foreach ($configAttributes as $attribute) {
@@ -74,8 +108,10 @@ class Mage_ConfigurableSwatches_Helper_Mediafallback extends Mage_Core_Helper_Ab
74
 
75
  foreach ($parentProduct->getChildrenProducts() as $childProduct) {
76
 
77
- // product has no value for attribute, we can't process it
78
- if (!$childProduct->hasData($attribute->getAttributeCode())) {
 
 
79
  continue;
80
  }
81
  $optionId = $childProduct->getData($attribute->getAttributeCode());
@@ -85,11 +121,6 @@ class Mage_ConfigurableSwatches_Helper_Mediafallback extends Mage_Core_Helper_Ab
85
  continue;
86
  }
87
 
88
- // normalize to all lower case before we start using them
89
- $optionLabels = array_map(function ($value) {
90
- return array_map('Mage_ConfigurableSwatches_Helper_Data::normalizeKey', $value);
91
- }, $optionLabels);
92
-
93
  // using default value as key unless store-specific label is present
94
  $optionLabel = $optionLabels[$optionId][0];
95
  if (isset($optionLabels[$optionId][$storeId])) {
@@ -110,7 +141,8 @@ class Mage_ConfigurableSwatches_Helper_Mediafallback extends Mage_Core_Helper_Ab
110
  if ($attribute->getAttributeId() == $listSwatchAttr->getAttributeId()
111
  && !in_array($mapping[$optionLabel]['label'], $listSwatchValues)
112
  ) {
113
- $listSwatchValues[$optionId] = $mapping[$optionLabel]['label'];
 
114
  }
115
  } // end looping child products
116
  } // end looping attributes
@@ -120,8 +152,13 @@ class Mage_ConfigurableSwatches_Helper_Mediafallback extends Mage_Core_Helper_Ab
120
  $mapping[$key]['product_ids'] = array_unique($mapping[$key]['product_ids']);
121
  }
122
 
 
 
 
 
123
  $parentProduct->setChildAttributeLabelMapping($mapping)
124
- ->setListSwatchAttrValues($listSwatchValues);
 
125
  } // end looping parent products
126
  }
127
 
@@ -201,7 +238,12 @@ class Mage_ConfigurableSwatches_Helper_Mediafallback extends Mage_Core_Helper_Ab
201
  /* @var $childProduct Mage_Catalog_Model_Product */
202
  if ($product->hasChildrenProducts()) {
203
  foreach ($product->getChildrenProducts() as $childProduct) {
204
- if ($image = $this->_resizeProductImage($childProduct, $imageType, $keepFrame)) {
 
 
 
 
 
205
  $imagesByType[$imageType][$childProduct->getId()] = $image;
206
  }
207
  }
37
  * - product must have children products attached
38
  *
39
  * @param array $parentProducts
40
+ * @deprecated use $this->attachProductChildrenAttributeMapping() instead
41
  * @param $storeId
42
  * @return void
43
  */
44
  public function attachConfigurableProductChildrenAttributeMapping(array $parentProducts, $storeId)
45
  {
46
+ return $this->attachProductChildrenAttributeMapping($parentProducts, $storeId);
47
+ }
48
+
49
+ /**
50
+ * Set child_attribute_label_mapping on products with attribute label -> product mapping
51
+ * Depends on following product data:
52
+ * - product must have children products attached
53
+ *
54
+ * @param array $parentProducts
55
+ * @param $storeId
56
+ * @param bool $onlyListAttributes
57
+ * @return void
58
+ */
59
+ public function attachProductChildrenAttributeMapping(array $parentProducts, $storeId, $onlyListAttributes = false)
60
+ {
61
+ /** @var $listSwatchAttr Mage_Eav_Model_Attribute */
62
  $listSwatchAttr = Mage::helper('configurableswatches/productlist')->getSwatchAttribute();
63
+ $swatchAttributeIds = array();
64
+ if (!$onlyListAttributes) {
65
+ $swatchAttributeIds = Mage::helper('configurableswatches')->getSwatchAttributeIds();
66
+ }
67
+ if ($listSwatchAttr->getId()) {
68
+ $swatchAttributeIds[] = $listSwatchAttr->getId();
69
+ }
70
+ if (empty($swatchAttributeIds)) {
71
+ return;
72
+ }
73
 
74
  $parentProductIds = array();
75
  /* @var $parentProduct Mage_Catalog_Model_Product */
80
  $configAttributes = Mage::getResourceModel('configurableswatches/catalog_product_attribute_super_collection')
81
  ->addParentProductsFilter($parentProductIds)
82
  ->attachEavAttributes()
83
+ ->addFieldToFilter('eav_attributes.attribute_id', array('in' => $swatchAttributeIds))
84
  ->setStoreId($storeId)
85
  ;
86
 
89
  $optionLabels += $attribute->getOptionLabels();
90
  }
91
 
92
+ // normalize to all lower case before we start using them
93
+ $optionLabels = array_map(function ($value) {
94
+ return array_map('Mage_ConfigurableSwatches_Helper_Data::normalizeKey', $value);
95
+ }, $optionLabels);
96
+
97
  foreach ($parentProducts as $parentProduct) {
98
  $mapping = array();
99
  $listSwatchValues = array();
100
+ $listSwatchStockValues = array();
101
 
102
  /* @var $attribute Mage_Catalog_Model_Product_Type_Configurable_Attribute */
103
  foreach ($configAttributes as $attribute) {
108
 
109
  foreach ($parentProduct->getChildrenProducts() as $childProduct) {
110
 
111
+ // product has no value for attribute or not available, we can't process it
112
+ $isInStock = $childProduct->getStockItem()->getIsInStock();
113
+ if (!$childProduct->hasData($attribute->getAttributeCode())
114
+ || (!$isInStock && !Mage::helper('cataloginventory')->isShowOutOfStock())) {
115
  continue;
116
  }
117
  $optionId = $childProduct->getData($attribute->getAttributeCode());
121
  continue;
122
  }
123
 
 
 
 
 
 
124
  // using default value as key unless store-specific label is present
125
  $optionLabel = $optionLabels[$optionId][0];
126
  if (isset($optionLabels[$optionId][$storeId])) {
141
  if ($attribute->getAttributeId() == $listSwatchAttr->getAttributeId()
142
  && !in_array($mapping[$optionLabel]['label'], $listSwatchValues)
143
  ) {
144
+ $listSwatchValues[$optionId] = $mapping[$optionLabel]['label'];
145
+ $listSwatchStockValues[$optionId] = $isInStock;
146
  }
147
  } // end looping child products
148
  } // end looping attributes
152
  $mapping[$key]['product_ids'] = array_unique($mapping[$key]['product_ids']);
153
  }
154
 
155
+ if (count($listSwatchValues)) {
156
+ $listSwatchValues = array_replace(array_intersect_key($optionLabels, $listSwatchValues),
157
+ $listSwatchValues);
158
+ }
159
  $parentProduct->setChildAttributeLabelMapping($mapping)
160
+ ->setListSwatchAttrValues($listSwatchValues)
161
+ ->setListSwatchAttrStockValues($listSwatchStockValues);
162
  } // end looping parent products
163
  }
164
 
238
  /* @var $childProduct Mage_Catalog_Model_Product */
239
  if ($product->hasChildrenProducts()) {
240
  foreach ($product->getChildrenProducts() as $childProduct) {
241
+ $image = $this->_resizeProductImage($childProduct, $imageType, $keepFrame);
242
+ if (!$image) {
243
+ $image = $this->_resizeProductImage($childProduct, 'image', $keepFrame);
244
+ }
245
+
246
+ if ($image) {
247
  $imagesByType[$imageType][$childProduct->getId()] = $image;
248
  }
249
  }
app/code/core/Mage/ConfigurableSwatches/Model/Observer.php CHANGED
@@ -37,8 +37,11 @@ class Mage_ConfigurableSwatches_Model_Observer extends Mage_Core_Model_Abstract
37
  return; // exit without loading swatch functionality
38
  }
39
 
40
- /* @var $helper Mage_ConfigurableSwatches_Helper_Mediafallback */
41
- $helper = Mage::helper('configurableswatches/mediafallback');
 
 
 
42
 
43
  /* @var $collection Mage_Catalog_Model_Resource_Product_Collection */
44
  $collection = $observer->getCollection();
@@ -51,15 +54,19 @@ class Mage_ConfigurableSwatches_Model_Observer extends Mage_Core_Model_Abstract
51
 
52
  $products = $collection->getItems();
53
 
54
- $helper->attachChildrenProducts($products, $collection->getStoreId());
55
 
56
- $helper->attachConfigurableProductChildrenAttributeMapping($products, $collection->getStoreId());
 
 
 
 
57
 
58
- $helper->attachGallerySetToCollection($products, $collection->getStoreId());
59
 
60
  /* @var $product Mage_Catalog_Model_Product */
61
  foreach ($products as $product) {
62
- $helper->groupMediaGalleryImages($product);
63
  Mage::helper('configurableswatches/productimg')
64
  ->indexProductImages($product, $product->getListSwatchAttrValues());
65
  }
@@ -90,7 +97,7 @@ class Mage_ConfigurableSwatches_Model_Observer extends Mage_Core_Model_Abstract
90
 
91
  $helper->groupMediaGalleryImages($product);
92
 
93
- $helper->attachConfigurableProductChildrenAttributeMapping(array($product), $product->getStoreId());
94
  }
95
 
96
  /**
37
  return; // exit without loading swatch functionality
38
  }
39
 
40
+ /* @var $mediaHelper Mage_ConfigurableSwatches_Helper_Mediafallback */
41
+ $mediaHelper = Mage::helper('configurableswatches/mediafallback');
42
+
43
+ /** @var $priceHelper Mage_ConfigurableSwatches_Helper_List_Price */
44
+ $priceHelper = Mage::helper('configurableswatches/list_price');
45
 
46
  /* @var $collection Mage_Catalog_Model_Resource_Product_Collection */
47
  $collection = $observer->getCollection();
54
 
55
  $products = $collection->getItems();
56
 
57
+ $mediaHelper->attachChildrenProducts($products, $collection->getStoreId());
58
 
59
+ $mediaHelper->attachProductChildrenAttributeMapping($products, $collection->getStoreId());
60
+
61
+ if ($priceHelper->isEnabled()) {
62
+ $priceHelper->attachConfigurableProductChildrenPricesMapping($products, $collection->getStoreId());
63
+ }
64
 
65
+ $mediaHelper->attachGallerySetToCollection($products, $collection->getStoreId());
66
 
67
  /* @var $product Mage_Catalog_Model_Product */
68
  foreach ($products as $product) {
69
+ $mediaHelper->groupMediaGalleryImages($product);
70
  Mage::helper('configurableswatches/productimg')
71
  ->indexProductImages($product, $product->getListSwatchAttrValues());
72
  }
97
 
98
  $helper->groupMediaGalleryImages($product);
99
 
100
+ $helper->attachProductChildrenAttributeMapping(array($product), $product->getStoreId(), false);
101
  }
102
 
103
  /**
app/code/core/Mage/ConfigurableSwatches/Model/Resource/Catalog/Product/Attribute/Super/Collection.php CHANGED
@@ -99,40 +99,59 @@ class Mage_ConfigurableSwatches_Model_Resource_Catalog_Product_Attribute_Super_C
99
  */
100
  protected function _loadOptionLabels()
101
  {
102
- if ($this->count()) {
103
- $select = $this->getConnection()->select()
104
- ->from(
105
- array('attr' => $this->getTable('catalog/product_super_attribute')),
106
- array(
107
- 'product_super_attribute_id' => 'attr.product_super_attribute_id',
108
- ))
109
- ->join(
110
- array('opt' => $this->getTable('eav/attribute_option')),
111
- 'opt.attribute_id = attr.attribute_id',
112
- array(
113
- 'attribute_id' => 'opt.attribute_id',
114
- 'option_id' => 'opt.option_id',
115
- ))
116
- ->join(
117
- array('lab' => $this->getTable('eav/attribute_option_value')),
118
- 'lab.option_id = opt.option_id',
119
- array(
120
- 'label' => 'lab.value',
121
- 'store_id' => 'lab.store_id',
122
- ))
123
- ->where('attr.product_super_attribute_id IN (?)', array_keys($this->_items))
124
- ;
125
-
126
- $result = $this->getConnection()->fetchAll($select);
127
- foreach ($result as $data) {
128
- $item = $this->getItemById($data['product_super_attribute_id']);
129
- if (!is_array($labels = $item->getOptionLabels())) {
130
- $labels = array();
131
- }
132
- $labels[$data['option_id']][$data['store_id']] = $data['label'];
133
- $item->setOptionLabels($labels);
134
- }
135
  }
136
  return $this;
137
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  }
99
  */
100
  protected function _loadOptionLabels()
101
  {
102
+ $labels = $this->_getOptionLabels();
103
+ foreach ($this->getItems() as $item) {
104
+ $item->setOptionLabels($labels);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
  }
106
  return $this;
107
  }
108
+
109
+ /**
110
+ * Get Option Labels
111
+ *
112
+ * @return array
113
+ */
114
+ protected function _getOptionLabels()
115
+ {
116
+ $attributeIds = $this->_getAttributeIds();
117
+
118
+ $select = $this->getConnection()->select();
119
+ $select->from(array('options' => $this->getTable('eav/attribute_option')))
120
+ ->join(
121
+ array('labels' => $this->getTable('eav/attribute_option_value')),
122
+ 'labels.option_id = options.option_id',
123
+ array(
124
+ 'label' => 'labels.value',
125
+ 'store_id' => 'labels.store_id',
126
+ )
127
+ )
128
+ ->where('options.attribute_id IN (?)', $attributeIds)
129
+ ->where(
130
+ 'labels.store_id IN (?)',
131
+ array(Mage_Catalog_Model_Abstract::DEFAULT_STORE_ID, $this->getStoreId())
132
+ );
133
+
134
+ $resultSet = $this->getConnection()->query($select);
135
+ $labels = array();
136
+ while ($option = $resultSet->fetch()) {
137
+ $labels[$option['option_id']][$option['store_id']] = $option['label'];
138
+ }
139
+ return $labels;
140
+ }
141
+
142
+ /**
143
+ * Get Attribute IDs
144
+ *
145
+ * @return array
146
+ */
147
+ protected function _getAttributeIds()
148
+ {
149
+ $attributeIds = array();
150
+ foreach ($this->getItems() as $item) {
151
+ $attributeIds[] = $item->getAttributeId();
152
+ }
153
+ $attributeIds = array_unique($attributeIds);
154
+
155
+ return $attributeIds;
156
+ }
157
  }
app/code/core/Mage/ConfigurableSwatches/etc/system.xml CHANGED
@@ -71,6 +71,15 @@
71
  <show_in_website>1</show_in_website>
72
  <show_in_store>1</show_in_store>
73
  </product_list_attribute>
 
 
 
 
 
 
 
 
 
74
  </fields>
75
  </general>
76
  <product_detail_dimensions translate="label comment" module="configurableswatches">
71
  <show_in_website>1</show_in_website>
72
  <show_in_store>1</show_in_store>
73
  </product_list_attribute>
74
+ <product_list_price_change translate="label" module="configurableswatches">
75
+ <label>Dynamic Price Change for Swatches in Product Listing</label>
76
+ <frontend_type>select</frontend_type>
77
+ <source_model>adminhtml/system_config_source_yesno</source_model>
78
+ <sort_order>40</sort_order>
79
+ <show_in_default>1</show_in_default>
80
+ <show_in_website>1</show_in_website>
81
+ <show_in_store>1</show_in_store>
82
+ </product_list_price_change>
83
  </fields>
84
  </general>
85
  <product_detail_dimensions translate="label comment" module="configurableswatches">
app/code/core/Mage/Core/Block/Abstract.php CHANGED
@@ -36,6 +36,10 @@
36
  */
37
  abstract class Mage_Core_Block_Abstract extends Varien_Object
38
  {
 
 
 
 
39
  /**
40
  * Cache group Tag
41
  */
@@ -1289,7 +1293,13 @@ abstract class Mage_Core_Block_Abstract extends Varien_Object
1289
  public function getCacheKey()
1290
  {
1291
  if ($this->hasData('cache_key')) {
1292
- return $this->getData('cache_key');
 
 
 
 
 
 
1293
  }
1294
  /**
1295
  * don't prevent recalculation by saving generated cache key
36
  */
37
  abstract class Mage_Core_Block_Abstract extends Varien_Object
38
  {
39
+ /**
40
+ * Prefix for cache key
41
+ */
42
+ const CACHE_KEY_PREFIX = 'BLOCK_';
43
  /**
44
  * Cache group Tag
45
  */
1293
  public function getCacheKey()
1294
  {
1295
  if ($this->hasData('cache_key')) {
1296
+ $cacheKey = $this->getData('cache_key');
1297
+ if (strpos($cacheKey, self::CACHE_KEY_PREFIX) !== 0) {
1298
+ $cacheKey = self::CACHE_KEY_PREFIX . $cacheKey;
1299
+ $this->setData('cache_key', $cacheKey);
1300
+ }
1301
+
1302
+ return $cacheKey;
1303
  }
1304
  /**
1305
  * don't prevent recalculation by saving generated cache key
app/code/core/Mage/Core/Block/Template.php CHANGED
@@ -212,7 +212,7 @@ class Mage_Core_Block_Template extends Mage_Core_Block_Abstract
212
 
213
  // EXTR_SKIP protects from overriding
214
  // already defined variables
215
- extract ($this->_viewVars, EXTR_SKIP);
216
  $do = $this->getDirectOutput();
217
 
218
  if (!$do) {
212
 
213
  // EXTR_SKIP protects from overriding
214
  // already defined variables
215
+ extract($this->_viewVars, EXTR_SKIP);
216
  $do = $this->getDirectOutput();
217
 
218
  if (!$do) {
app/code/core/Mage/Core/Controller/Varien/Action.php CHANGED
@@ -1054,6 +1054,7 @@ abstract class Mage_Core_Controller_Varien_Action
1054
  return $this;
1055
  }
1056
  if ($content['type'] == 'filename') {
 
1057
  $isFile = true;
1058
  $file = $content['value'];
1059
  $contentLength = filesize($file);
1054
  return $this;
1055
  }
1056
  if ($content['type'] == 'filename') {
1057
+ clearstatcache();
1058
  $isFile = true;
1059
  $file = $content['value'];
1060
  $contentLength = filesize($file);
app/code/core/Mage/Core/Helper/String.php CHANGED
@@ -172,6 +172,13 @@ class Mage_Core_Helper_String extends Mage_Core_Helper_Abstract
172
  // trim
173
  if ($trim) {
174
  $str = trim(preg_replace('/\s{2,}/siu', ' ', $str));
 
 
 
 
 
 
 
175
  }
176
  // do a usual str_split, but safe for our encoding
177
  if ((!$keepWords) || ($length < 2)) {
@@ -194,7 +201,14 @@ class Mage_Core_Helper_String extends Mage_Core_Helper_Abstract
194
  $space = ' ';
195
  $spaceLen = 1;
196
  }
197
- if (empty($result[$i])) {
 
 
 
 
 
 
 
198
  $currentLength = 0;
199
  $result[$i] = '';
200
  $space = '';
@@ -476,4 +490,30 @@ class Mage_Core_Helper_String extends Mage_Core_Helper_Abstract
476
  return $this->_arrayHelper;
477
  }
478
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
479
  }
172
  // trim
173
  if ($trim) {
174
  $str = trim(preg_replace('/\s{2,}/siu', ' ', $str));
175
+ /**
176
+ * In cases like:
177
+ * Mage::helper('core/string')->str_split('0 1 2 ', 2, false, true);
178
+ * the result array have elements with boolean "false" value.
179
+ * So it fixed by
180
+ */
181
+ $strlen = $this->strlen($str);
182
  }
183
  // do a usual str_split, but safe for our encoding
184
  if ((!$keepWords) || ($length < 2)) {
201
  $space = ' ';
202
  $spaceLen = 1;
203
  }
204
+ /**
205
+ * The empty($result[$i]) is not appropriate, because in case with empty("0") expression returns "true",
206
+ * so in cases when string have "0" symbol, the "0" will lost.
207
+ * Try Mage::helper('core/string')->str_split("0 aa", 2, true);
208
+ * Therefore the empty($result[$i]) expression
209
+ * replaced by !isset($result[$i]) || isset($result[$i]) && $result[$i] === ''
210
+ */
211
+ if (!isset($result[$i]) || isset($result[$i]) && $result[$i] === '') {
212
  $currentLength = 0;
213
  $result[$i] = '';
214
  $space = '';
490
  return $this->_arrayHelper;
491
  }
492
 
493
+ /**
494
+ * Unicode compatible ord() method
495
+ *
496
+ * @param string $c char to get value from
497
+ * @return integer
498
+ */
499
+ public function uniOrd($c)
500
+ {
501
+ $ord = 0;
502
+ $h = ord($c[0]);
503
+
504
+ if ($h <= 0x7F) {
505
+ $ord = $h;
506
+ } else if ($h < 0xC2) {
507
+ $ord = 0;
508
+ } else if ($h <= 0xDF) {
509
+ $ord = (($h & 0x1F) << 6 | (ord($c[1]) & 0x3F));
510
+ } else if ($h <= 0xEF) {
511
+ $ord = (($h & 0x0F) << 12 | (ord($c[1]) & 0x3F) << 6 | (ord($c[2]) & 0x3F));
512
+ } else if ($h <= 0xF4) {
513
+ $ord = (($h & 0x0F) << 18 | (ord($c[1]) & 0x3F) << 12 |
514
+ (ord($c[2]) & 0x3F) << 6 | (ord($c[3]) & 0x3F));
515
+ }
516
+
517
+ return $ord;
518
+ }
519
  }
app/code/core/Mage/Core/Helper/Url.php CHANGED
@@ -51,7 +51,7 @@ class Mage_Core_Helper_Url extends Mage_Core_Helper_Abstract
51
  $port = (in_array($port, $defaultPorts)) ? '' : ':' . $port;
52
  }
53
  $url = $request->getScheme() . '://' . $request->getHttpHost() . $port . $request->getServer('REQUEST_URI');
54
- return $url;
55
  // return $this->_getUrl('*/*/*', array('_current' => true, '_use_rewrite' => true));
56
  }
57
 
51
  $port = (in_array($port, $defaultPorts)) ? '' : ':' . $port;
52
  }
53
  $url = $request->getScheme() . '://' . $request->getHttpHost() . $port . $request->getServer('REQUEST_URI');
54
+ return $this->escapeUrl($url);
55
  // return $this->_getUrl('*/*/*', array('_current' => true, '_use_rewrite' => true));
56
  }
57
 
app/code/core/Mage/Core/Model/Config.php CHANGED
@@ -256,6 +256,9 @@ class Mage_Core_Model_Config extends Mage_Core_Model_Config_Base
256
  if ($cacheLoad) {
257
  return $this;
258
  }
 
 
 
259
  $this->loadModules();
260
  $this->loadDb();
261
  $this->saveCache();
256
  if ($cacheLoad) {
257
  return $this;
258
  }
259
+
260
+ $this->_useCache = false;
261
+
262
  $this->loadModules();
263
  $this->loadDb();
264
  $this->saveCache();
app/code/core/Mage/Core/Model/Email/Queue.php CHANGED
@@ -44,8 +44,6 @@
44
  *
45
  * @category Mage
46
  * @package Mage_Core
47
- * @copyright Copyright (c) 2011 Magento Inc. (http://www.magentocommerce.com)
48
- * @license http://www.magentocommerce.com/license/enterprise-edition
49
  */
50
  class Mage_Core_Model_Email_Queue extends Mage_Core_Model_Abstract
51
  {
44
  *
45
  * @category Mage
46
  * @package Mage_Core
 
 
47
  */
48
  class Mage_Core_Model_Email_Queue extends Mage_Core_Model_Abstract
49
  {
app/code/core/Mage/Core/Model/Email/Template.php CHANGED
@@ -407,6 +407,7 @@ class Mage_Core_Model_Email_Template extends Mage_Core_Model_Email_Template_Abst
407
  if ($this->hasQueue() && $this->getQueue() instanceof Mage_Core_Model_Email_Queue) {
408
  /** @var $emailQueue Mage_Core_Model_Email_Queue */
409
  $emailQueue = $this->getQueue();
 
410
  $emailQueue->setMessageBody($text);
411
  $emailQueue->setMessageParameters(array(
412
  'subject' => $subject,
407
  if ($this->hasQueue() && $this->getQueue() instanceof Mage_Core_Model_Email_Queue) {
408
  /** @var $emailQueue Mage_Core_Model_Email_Queue */
409
  $emailQueue = $this->getQueue();
410
+ $emailQueue->clearRecipients();
411
  $emailQueue->setMessageBody($text);
412
  $emailQueue->setMessageParameters(array(
413
  'subject' => $subject,
app/code/core/Mage/Core/Model/Email/Template/Abstract.php CHANGED
@@ -149,8 +149,7 @@ abstract class Mage_Core_Model_Email_Template_Abstract extends Mage_Core_Model_T
149
  protected function _addEmailVariables($variables, $storeId)
150
  {
151
  if (!isset($variables['store'])) {
152
- $store = Mage::app()->getStore($storeId);
153
- $variables['store'] = $store;
154
  }
155
  if (!isset($variables['logo_url'])) {
156
  $variables['logo_url'] = $this->_getLogoUrl($storeId);
@@ -158,35 +157,20 @@ abstract class Mage_Core_Model_Email_Template_Abstract extends Mage_Core_Model_T
158
  if (!isset($variables['logo_alt'])) {
159
  $variables['logo_alt'] = $this->_getLogoAlt($storeId);
160
  }
161
- if (!isset($variables['logo_width'])) {
162
- $variables['logo_width'] = Mage::getStoreConfig(
163
- self::XML_PATH_DESIGN_EMAIL_LOGO_WIDTH,
164
- $storeId
165
- );
166
- }
167
- if (!isset($variables['logo_height'])) {
168
- $variables['logo_height'] = Mage::getStoreConfig(
169
- self::XML_PATH_DESIGN_EMAIL_LOGO_HEIGHT,
170
- $storeId
171
- );
172
- }
173
- if (!isset($variables['store_phone'])) {
174
- $variables['store_phone'] = Mage::getStoreConfig(
175
- Mage_Core_Model_Store::XML_PATH_STORE_STORE_PHONE,
176
- $storeId
177
- );
178
- }
179
- if (!isset($variables['store_hours'])) {
180
- $variables['store_hours'] = Mage::getStoreConfig(
181
- Mage_Core_Model_Store::XML_PATH_STORE_STORE_HOURS,
182
- $storeId
183
- );
184
- }
185
- if (!isset($variables['store_email'])) {
186
- $variables['store_email'] = Mage::getStoreConfig(
187
- Mage_Customer_Helper_Data::XML_PATH_SUPPORT_EMAIL,
188
- $storeId
189
- );
190
  }
191
  // If template is text mode, don't include styles
192
  if (!$this->isPlain()) {
149
  protected function _addEmailVariables($variables, $storeId)
150
  {
151
  if (!isset($variables['store'])) {
152
+ $variables['store'] = Mage::app()->getStore($storeId);
 
153
  }
154
  if (!isset($variables['logo_url'])) {
155
  $variables['logo_url'] = $this->_getLogoUrl($storeId);
157
  if (!isset($variables['logo_alt'])) {
158
  $variables['logo_alt'] = $this->_getLogoAlt($storeId);
159
  }
160
+
161
+ $defaultValuesMap = array(
162
+ "logo_width" => self::XML_PATH_DESIGN_EMAIL_LOGO_WIDTH,
163
+ "logo_height" => self::XML_PATH_DESIGN_EMAIL_LOGO_HEIGHT,
164
+ "phone" => Mage_Core_Model_Store::XML_PATH_STORE_STORE_PHONE,
165
+ "store_phone" => Mage_Core_Model_Store::XML_PATH_STORE_STORE_PHONE,
166
+ "store_hours" => Mage_Core_Model_Store::XML_PATH_STORE_STORE_HOURS,
167
+ "store_email" => Mage_Customer_Helper_Data::XML_PATH_SUPPORT_EMAIL,
168
+ );
169
+
170
+ foreach ($defaultValuesMap as $variableName => $configValue) {
171
+ if (!isset($variables[$variableName])) {
172
+ $variables[$variableName] = Mage::getStoreConfig($configValue, $storeId);
173
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
  }
175
  // If template is text mode, don't include styles
176
  if (!$this->isPlain()) {
app/code/core/Mage/Core/Model/Encryption.php CHANGED
@@ -98,9 +98,9 @@ class Mage_Core_Model_Encryption
98
  $hashArr = explode(':', $hash);
99
  switch (count($hashArr)) {
100
  case 1:
101
- return $this->hash($password) === $hash;
102
  case 2:
103
- return $this->hash($hashArr[1] . $password) === $hashArr[0];
104
  }
105
  Mage::throwException('Invalid hash.');
106
  }
98
  $hashArr = explode(':', $hash);
99
  switch (count($hashArr)) {
100
  case 1:
101
+ return hash_equals($this->hash($password), $hash);
102
  case 2:
103
+ return hash_equals($this->hash($hashArr[1] . $password), $hashArr[0]);
104
  }
105
  Mage::throwException('Invalid hash.');
106
  }
app/code/core/Mage/Core/Model/File/Storage/Abstract.php CHANGED
@@ -74,12 +74,12 @@ abstract class Mage_Core_Model_File_Storage_Abstract extends Mage_Core_Model_Abs
74
  {
75
  $path = ltrim($path, '\\/');
76
  $fullPath = $this->getMediaBaseDirectory() . DS . $path;
77
-
78
  if (!file_exists($fullPath) || !is_file($fullPath)) {
79
- Mage::throwException(Mage::helper('core')->__('File %s does not exist', $fullPath));
80
  }
81
  if (!is_readable($fullPath)) {
82
- Mage::throwException(Mage::helper('core')->__('File %s is not readable', $fullPath));
83
  }
84
 
85
  $path = str_replace(array('/', '\\'), '/', $path);
74
  {
75
  $path = ltrim($path, '\\/');
76
  $fullPath = $this->getMediaBaseDirectory() . DS . $path;
77
+ $io = new Varien_Io_File();
78
  if (!file_exists($fullPath) || !is_file($fullPath)) {
79
+ Mage::throwException(Mage::helper('core')->__('File %s does not exist', $io->getFilteredPath($fullPath)));
80
  }
81
  if (!is_readable($fullPath)) {
82
+ Mage::throwException(Mage::helper('core')->__('File %s is not readable', $io->getFilteredPath($fullPath)));
83
  }
84
 
85
  $path = str_replace(array('/', '\\'), '/', $path);
app/code/core/Mage/Core/Model/File/Validator/AvailablePath.php CHANGED
@@ -92,7 +92,7 @@ class Mage_Core_Model_File_Validator_AvailablePath extends Zend_Validate_Abstrac
92
  /**
93
  * Initialize message templates with translating
94
  *
95
- * @return Mage_Adminhtml_Model_Core_File_Validator_SavePath_Available
96
  */
97
  protected function _initMessageTemplates()
98
  {
@@ -114,7 +114,7 @@ class Mage_Core_Model_File_Validator_AvailablePath extends Zend_Validate_Abstrac
114
  *
115
  * @param array $paths All paths masks types.
116
  * E.g.: array('available' => array(...), 'protected' => array(...))
117
- * @return Mage_Adminhtml_Model_Core_File_Validator_SavePath_Available
118
  */
119
  public function setPaths(array $paths)
120
  {
@@ -131,7 +131,7 @@ class Mage_Core_Model_File_Validator_AvailablePath extends Zend_Validate_Abstrac
131
  * Set protected paths masks
132
  *
133
  * @param array $paths
134
- * @return Mage_Adminhtml_Model_Core_File_Validator_SavePath_Available
135
  */
136
  public function setProtectedPaths(array $paths)
137
  {
@@ -143,7 +143,7 @@ class Mage_Core_Model_File_Validator_AvailablePath extends Zend_Validate_Abstrac
143
  * Add protected paths masks
144
  *
145
  * @param string|array $path
146
- * @return Mage_Adminhtml_Model_Core_File_Validator_SavePath_Available
147
  */
148
  public function addProtectedPath($path)
149
  {
@@ -169,7 +169,7 @@ class Mage_Core_Model_File_Validator_AvailablePath extends Zend_Validate_Abstrac
169
  * Set available paths masks
170
  *
171
  * @param array $paths
172
- * @return Mage_Adminhtml_Model_Core_File_Validator_SavePath_Available
173
  */
174
  public function setAvailablePaths(array $paths)
175
  {
@@ -181,7 +181,7 @@ class Mage_Core_Model_File_Validator_AvailablePath extends Zend_Validate_Abstrac
181
  * Add available paths mask
182
  *
183
  * @param string|array $path
184
- * @return Mage_Adminhtml_Model_Core_File_Validator_SavePath_Available
185
  */
186
  public function addAvailablePath($path)
187
  {
92
  /**
93
  * Initialize message templates with translating
94
  *
95
+ * @return Mage_Core_Model_File_Validator_AvailablePath
96
  */
97
  protected function _initMessageTemplates()
98
  {
114
  *
115
  * @param array $paths All paths masks types.
116
  * E.g.: array('available' => array(...), 'protected' => array(...))
117
+ * @return Mage_Core_Model_File_Validator_AvailablePath
118
  */
119
  public function setPaths(array $paths)
120
  {
131
  * Set protected paths masks
132
  *
133
  * @param array $paths
134
+ * @return Mage_Core_Model_File_Validator_AvailablePath
135
  */
136
  public function setProtectedPaths(array $paths)
137
  {
143
  * Add protected paths masks
144
  *
145
  * @param string|array $path
146
+ * @return Mage_Core_Model_File_Validator_AvailablePath
147
  */
148
  public function addProtectedPath($path)
149
  {
169
  * Set available paths masks
170
  *
171
  * @param array $paths
172
+ * @return Mage_Core_Model_File_Validator_AvailablePath
173
  */
174
  public function setAvailablePaths(array $paths)
175
  {
181
  * Add available paths mask
182
  *
183
  * @param string|array $path
184
+ * @return Mage_Core_Model_File_Validator_AvailablePath
185
  */
186
  public function addAvailablePath($path)
187
  {
app/code/core/Mage/Core/Model/Input/Filter/MaliciousCode.php CHANGED
@@ -65,7 +65,13 @@ class Mage_Core_Model_Input_Filter_MaliciousCode implements Zend_Filter_Interfac
65
  */
66
  public function filter($value)
67
  {
68
- return preg_replace($this->_expressions, '', $value);
 
 
 
 
 
 
69
  }
70
 
71
  /**
65
  */
66
  public function filter($value)
67
  {
68
+ $result = false;
69
+ do {
70
+ $subject = $result ? $result : $value;
71
+ $result = preg_replace($this->_expressions, '', $subject, -1, $count);
72
+ } while ($count !== 0);
73
+
74
+ return $result;
75
  }
76
 
77
  /**
app/code/core/Mage/Core/Model/Layout.php CHANGED
@@ -552,7 +552,7 @@ class Mage_Core_Model_Layout extends Varien_Simplexml_Config
552
  $out = '';
553
  if (!empty($this->_output)) {
554
  foreach ($this->_output as $callback) {
555
- $out .= $this->getBlock($callback[0])->$callback[1]();
556
  }
557
  }
558
 
552
  $out = '';
553
  if (!empty($this->_output)) {
554
  foreach ($this->_output as $callback) {
555
+ $out .= $this->getBlock($callback[0])->{$callback[1]}();
556
  }
557
  }
558
 
app/code/core/Mage/Core/Model/Resource/Url/Rewrite.php CHANGED
@@ -134,12 +134,12 @@ class Mage_Core_Model_Resource_Url_Rewrite extends Mage_Core_Model_Resource_Db_A
134
  public function loadByRequestPath(Mage_Core_Model_Url_Rewrite $object, $path)
135
  {
136
  if (!is_array($path)) {
137
- $path = array($path);
138
  }
139
 
140
  $pathBind = array();
141
  foreach ($path as $key => $url) {
142
- $pathBind['path' . $key] = $url;
143
  }
144
  // Form select
145
  $adapter = $this->_getReadAdapter();
@@ -151,7 +151,7 @@ class Mage_Core_Model_Resource_Url_Rewrite extends Mage_Core_Model_Resource_Db_A
151
  $items = $adapter->fetchAll($select, $pathBind);
152
 
153
  // Go through all found records and choose one with lowest penalty - earlier path in array, concrete store
154
- $mapPenalty = array_flip(array_values($path)); // we got mapping array(path => index), lower index - better
155
  $currentPenalty = null;
156
  $foundItem = null;
157
  foreach ($items as $item) {
134
  public function loadByRequestPath(Mage_Core_Model_Url_Rewrite $object, $path)
135
  {
136
  if (!is_array($path)) {
137
+ $path = array(strtolower($path));
138
  }
139
 
140
  $pathBind = array();
141
  foreach ($path as $key => $url) {
142
+ $pathBind['path' . $key] = strtolower($url);
143
  }
144
  // Form select
145
  $adapter = $this->_getReadAdapter();
151
  $items = $adapter->fetchAll($select, $pathBind);
152
 
153
  // Go through all found records and choose one with lowest penalty - earlier path in array, concrete store
154
+ $mapPenalty = array_change_key_case(array_flip(array_values($path))); // we got mapping array(path => index), lower index - better
155
  $currentPenalty = null;
156
  $foundItem = null;
157
  foreach ($items as $item) {
app/code/core/Mage/Core/Model/Resource/Variable/Collection.php CHANGED
@@ -84,7 +84,7 @@ class Mage_Core_Model_Resource_Variable_Collection extends Mage_Core_Model_Resou
84
  ->join(
85
  array('value_table' => $this->getTable('core/variable_value')),
86
  'value_table.variable_id = main_table.variable_id',
87
- array('value_table.value'));
88
  $this->addFieldToFilter('value_table.store_id', array('eq' => $this->getStoreId()));
89
  return $this;
90
  }
84
  ->join(
85
  array('value_table' => $this->getTable('core/variable_value')),
86
  'value_table.variable_id = main_table.variable_id',
87
+ array('value_table.plain_value', 'value_table.html_value'));
88
  $this->addFieldToFilter('value_table.store_id', array('eq' => $this->getStoreId()));
89
  return $this;
90
  }
app/code/core/Mage/Core/Model/Session/Abstract/Varien.php CHANGED
@@ -32,6 +32,7 @@ class Mage_Core_Model_Session_Abstract_Varien extends Varien_Object
32
  const VALIDATOR_HTTP_X_FORVARDED_FOR_KEY = 'http_x_forwarded_for';
33
  const VALIDATOR_HTTP_VIA_KEY = 'http_via';
34
  const VALIDATOR_REMOTE_ADDR_KEY = 'remote_addr';
 
35
  const SECURE_COOKIE_CHECK_KEY = '_secure_cookie_check';
36
 
37
  /**
@@ -377,6 +378,16 @@ class Mage_Core_Model_Session_Abstract_Varien extends Varien_Object
377
  return true;
378
  }
379
 
 
 
 
 
 
 
 
 
 
 
380
  /**
381
  * Retrieve skip User Agent validation strings (Flash etc)
382
  *
@@ -446,6 +457,14 @@ class Mage_Core_Model_Session_Abstract_Varien extends Varien_Object
446
  return false;
447
  }
448
 
 
 
 
 
 
 
 
 
449
  return true;
450
  }
451
 
@@ -479,6 +498,8 @@ class Mage_Core_Model_Session_Abstract_Varien extends Varien_Object
479
  $parts[self::VALIDATOR_HTTP_USER_AGENT_KEY] = (string)$_SERVER['HTTP_USER_AGENT'];
480
  }
481
 
 
 
482
  return $parts;
483
  }
484
 
32
  const VALIDATOR_HTTP_X_FORVARDED_FOR_KEY = 'http_x_forwarded_for';
33
  const VALIDATOR_HTTP_VIA_KEY = 'http_via';
34
  const VALIDATOR_REMOTE_ADDR_KEY = 'remote_addr';
35
+ const VALIDATOR_SESSION_EXPIRE_TIMESTAMP = 'session_expire_timestamp';
36
  const SECURE_COOKIE_CHECK_KEY = '_secure_cookie_check';
37
 
38
  /**
378
  return true;
379
  }
380
 
381
+ /**
382
+ * Use session expire timestamp in validator key
383
+ *
384
+ * @return bool
385
+ */
386
+ public function useValidateSessionExpire()
387
+ {
388
+ return true;
389
+ }
390
+
391
  /**
392
  * Retrieve skip User Agent validation strings (Flash etc)
393
  *
457
  return false;
458
  }
459
 
460
+ if ($this->useValidateSessionExpire()
461
+ && $sessionData[self::VALIDATOR_SESSION_EXPIRE_TIMESTAMP] < time() ) {
462
+ return false;
463
+ } else {
464
+ $this->_data[self::VALIDATOR_KEY][self::VALIDATOR_SESSION_EXPIRE_TIMESTAMP]
465
+ = $validatorData[self::VALIDATOR_SESSION_EXPIRE_TIMESTAMP];
466
+ }
467
+
468
  return true;
469
  }
470
 
498
  $parts[self::VALIDATOR_HTTP_USER_AGENT_KEY] = (string)$_SERVER['HTTP_USER_AGENT'];
499
  }
500
 
501
+ $parts[self::VALIDATOR_SESSION_EXPIRE_TIMESTAMP] = time() + $this->getCookie()->getLifetime();
502
+
503
  return $parts;
504
  }
505
 
app/code/core/Mage/Core/etc/config.xml CHANGED
@@ -456,6 +456,14 @@
456
  <public_files_valid_paths>
457
  <protected>
458
  <app>/app/*/*</app>
 
 
 
 
 
 
 
 
459
  </protected>
460
  </public_files_valid_paths>
461
  </file>
456
  <public_files_valid_paths>
457
  <protected>
458
  <app>/app/*/*</app>
459
+ <dev>/dev/*/*</dev>
460
+ <downloader>/downloader/*/*</downloader>
461
+ <errors>/errors/*/*</errors>
462
+ <includes>/includes/*/*</includes>
463
+ <js>/js/*/*</js>
464
+ <lib>/lib/*/*</lib>
465
+ <shell>/shell/*/*</shell>
466
+ <skin>/skin/*/*</skin>
467
  </protected>
468
  </public_files_valid_paths>
469
  </file>
app/code/core/Mage/Core/etc/jstranslator.xml CHANGED
@@ -82,7 +82,7 @@
82
  <message>Please use only visible characters and spaces.</message>
83
  </validate-email-sender>
84
  <validate-password translate="message" module="core">
85
- <message>Please enter 6 or more characters. Leading or trailing spaces will be ignored.</message>
86
  </validate-password>
87
  <validate-admin-password translate="message" module="core">
88
  <message>Please enter 7 or more characters. Password should contain both numeric and alphabetic characters.</message>
@@ -130,7 +130,7 @@
130
  <message>Please select State/Province.</message>
131
  </validate-state>
132
  <validate-new-password translate="message" module="core">
133
- <message>Please enter 6 or more characters. Leading or trailing spaces will be ignored.</message>
134
  </validate-new-password>
135
  <validate-greater-than-zero translate="message" module="core">
136
  <message>Please enter a number greater than 0 in this field.</message>
82
  <message>Please use only visible characters and spaces.</message>
83
  </validate-email-sender>
84
  <validate-password translate="message" module="core">
85
+ <message>Please enter 6 or more characters without leading or trailing spaces.</message>
86
  </validate-password>
87
  <validate-admin-password translate="message" module="core">
88
  <message>Please enter 7 or more characters. Password should contain both numeric and alphabetic characters.</message>
130
  <message>Please select State/Province.</message>
131
  </validate-state>
132
  <validate-new-password translate="message" module="core">
133
+ <message>Please enter 6 or more characters without leading or trailing spaces.</message>
134
  </validate-new-password>
135
  <validate-greater-than-zero translate="message" module="core">
136
  <message>Please enter a number greater than 0 in this field.</message>
app/code/core/Mage/Core/etc/system.xml CHANGED
@@ -1068,7 +1068,7 @@
1068
  <show_in_store>0</show_in_store>
1069
  </forgot_email_identity>
1070
  <password_reset_link_expiration_period translate="label comment">
1071
- <label>Recovery Link Expiration Period (days)</label>
1072
  <comment>Please enter a number 1 or greater in this field.</comment>
1073
  <frontend_type>text</frontend_type>
1074
  <validate>required-entry validate-digits validate-digits-range digits-range-1-</validate>
1068
  <show_in_store>0</show_in_store>
1069
  </forgot_email_identity>
1070
  <password_reset_link_expiration_period translate="label comment">
1071
+ <label>Recovery Link Expiration Period (hours)</label>
1072
  <comment>Please enter a number 1 or greater in this field.</comment>
1073
  <frontend_type>text</frontend_type>
1074
  <validate>required-entry validate-digits validate-digits-range digits-range-1-</validate>
app/code/core/Mage/Core/functions.php CHANGED
@@ -375,3 +375,38 @@ if ( !function_exists('sys_get_temp_dir') ) {
375
  }
376
  }
377
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
375
  }
376
  }
377
  }
378
+
379
+ if (!function_exists('hash_equals')) {
380
+ /**
381
+ * Compares two strings using the same time whether they're equal or not.
382
+ * A difference in length will leak
383
+ *
384
+ * @param string $known_string
385
+ * @param string $user_string
386
+ * @return boolean Returns true when the two strings are equal, false otherwise.
387
+ */
388
+ function hash_equals($known_string, $user_string)
389
+ {
390
+ $result = 0;
391
+
392
+ if (!is_string($known_string)) {
393
+ trigger_error("hash_equals(): Expected known_string to be a string", E_USER_WARNING);
394
+ return false;
395
+ }
396
+
397
+ if (!is_string($user_string)) {
398
+ trigger_error("hash_equals(): Expected user_string to be a string", E_USER_WARNING);
399
+ return false;
400
+ }
401
+
402
+ if (strlen($known_string) != strlen($user_string)) {
403
+ return false;
404
+ }
405
+
406
+ for ($i = 0; $i < strlen($known_string); $i++) {
407
+ $result |= (ord($known_string[$i]) ^ ord($user_string[$i]));
408
+ }
409
+
410
+ return 0 === $result;
411
+ }
412
+ }
app/code/core/Mage/Cron/Model/Schedule.php CHANGED
@@ -215,6 +215,10 @@ class Mage_Cron_Model_Schedule extends Mage_Core_Model_Abstract
215
  */
216
  public function tryLockJob($oldStatus = self::STATUS_PENDING)
217
  {
218
- return $this->_getResource()->trySetJobStatusAtomic($this->getId(), self::STATUS_RUNNING, $oldStatus);
 
 
 
 
219
  }
220
  }
215
  */
216
  public function tryLockJob($oldStatus = self::STATUS_PENDING)
217
  {
218
+ $result = $this->_getResource()->trySetJobStatusAtomic($this->getId(), self::STATUS_RUNNING, $oldStatus);
219
+ if ($result) {
220
+ $this->setStatus(self::STATUS_RUNNING);
221
+ }
222
+ return $result;
223
  }
224
  }
app/code/core/Mage/Customer/Block/Address/Book.php CHANGED
@@ -56,7 +56,8 @@ class Mage_Customer_Block_Address_Book extends Mage_Core_Block_Template
56
 
57
  public function getDeleteUrl()
58
  {
59
- return $this->getUrl('customer/address/delete');
 
60
  }
61
 
62
  public function getAddressEditUrl($address)
56
 
57
  public function getDeleteUrl()
58
  {
59
+ return $this->getUrl('customer/address/delete',
60
+ array(Mage_Core_Model_Url::FORM_KEY => Mage::getSingleton('core/session')->getFormKey()));
61
  }
62
 
63
  public function getAddressEditUrl($address)
app/code/core/Mage/Customer/Helper/Data.php CHANGED
@@ -85,6 +85,13 @@ class Mage_Customer_Helper_Data extends Mage_Core_Helper_Abstract
85
  const XML_PATH_CUSTOMER_REQUIRE_ADMIN_USER_TO_CHANGE_USER_PASSWORD
86
  = 'customer/password/require_admin_user_to_change_user_password';
87
 
 
 
 
 
 
 
 
88
  /**
89
  * VAT class constants
90
  */
@@ -483,6 +490,36 @@ class Mage_Customer_Helper_Data extends Mage_Core_Helper_Abstract
483
  return (int)Mage::getStoreConfig(Mage_Customer_Model_Group::XML_PATH_DEFAULT_ID, $store);
484
  }
485
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
486
  /**
487
  * Retrieve customer group ID based on his VAT number
488
  *
85
  const XML_PATH_CUSTOMER_REQUIRE_ADMIN_USER_TO_CHANGE_USER_PASSWORD
86
  = 'customer/password/require_admin_user_to_change_user_password';
87
 
88
+ /**
89
+ * Configuration path to password forgotten flow change
90
+ */
91
+ const XML_PATH_CUSTOMER_FORGOT_PASSWORD_FLOW_SECURE = 'admin/security/forgot_password_flow_secure';
92
+ const XML_PATH_CUSTOMER_FORGOT_PASSWORD_EMAIL_TIMES = 'admin/security/forgot_password_email_times';
93
+ const XML_PATH_CUSTOMER_FORGOT_PASSWORD_IP_TIMES = 'admin/security/forgot_password_ip_times';
94
+
95
  /**
96
  * VAT class constants
97
  */
490
  return (int)Mage::getStoreConfig(Mage_Customer_Model_Group::XML_PATH_DEFAULT_ID, $store);
491
  }
492
 
493
+ /**
494
+ * Retrieve forgot password flow secure type
495
+ *
496
+ * @return int
497
+ */
498
+ public function getCustomerForgotPasswordFlowSecure()
499
+ {
500
+ return (int)Mage::getStoreConfig(self::XML_PATH_CUSTOMER_FORGOT_PASSWORD_FLOW_SECURE);
501
+ }
502
+
503
+ /**
504
+ * Retrieve forgot password requests to times per 24 hours from 1 e-mail
505
+ *
506
+ * @return int
507
+ */
508
+ public function getCustomerForgotPasswordEmailTimes()
509
+ {
510
+ return (int)Mage::getStoreConfig(self::XML_PATH_CUSTOMER_FORGOT_PASSWORD_EMAIL_TIMES);
511
+ }
512
+
513
+ /**
514
+ * Retrieve forgot password requests to times per hour from 1 IP
515
+ *
516
+ * @return int
517
+ */
518
+ public function getCustomerForgotPasswordIpTimes()
519
+ {
520
+ return (int)Mage::getStoreConfig(self::XML_PATH_CUSTOMER_FORGOT_PASSWORD_IP_TIMES);
521
+ }
522
+
523
  /**
524
  * Retrieve customer group ID based on his VAT number
525
  *
app/code/core/Mage/Customer/Model/Customer.php CHANGED
@@ -46,6 +46,8 @@ class Mage_Customer_Model_Customer extends Mage_Core_Model_Abstract
46
  const XML_PATH_CONFIRM_EMAIL_TEMPLATE = 'customer/create_account/email_confirmation_template';
47
  const XML_PATH_CONFIRMED_EMAIL_TEMPLATE = 'customer/create_account/email_confirmed_template';
48
  const XML_PATH_GENERATE_HUMAN_FRIENDLY_ID = 'customer/create_account/generate_human_friendly_id';
 
 
49
  /**#@-*/
50
 
51
  /**#@+
@@ -66,6 +68,11 @@ class Mage_Customer_Model_Customer extends Mage_Core_Model_Abstract
66
 
67
  const CACHE_TAG = 'customer';
68
 
 
 
 
 
 
69
  /**
70
  * Model event prefix
71
  *
@@ -385,7 +392,7 @@ class Mage_Customer_Model_Customer extends Mage_Core_Model_Abstract
385
  public function hashPassword($password, $salt = null)
386
  {
387
  return $this->_getHelper('core')
388
- ->getHash($password, !is_null($salt) ? $salt : Mage_Admin_Model_User::HASH_SALT_LENGTH);
389
  }
390
 
391
  /**
@@ -585,10 +592,11 @@ class Mage_Customer_Model_Customer extends Mage_Core_Model_Abstract
585
  * @param string $type
586
  * @param string $backUrl
587
  * @param string $storeId
 
588
  * @throws Mage_Core_Exception
589
  * @return Mage_Customer_Model_Customer
590
  */
591
- public function sendNewAccountEmail($type = 'registered', $backUrl = '', $storeId = '0')
592
  {
593
  $types = array(
594
  'registered' => self::XML_PATH_REGISTER_EMAIL_TEMPLATE, // welcome email, when confirmation is disabled
@@ -603,8 +611,10 @@ class Mage_Customer_Model_Customer extends Mage_Core_Model_Abstract
603
  $storeId = $this->_getWebsiteStoreId($this->getSendemailStoreId());
604
  }
605
 
 
606
  $this->_sendEmailTemplate($types[$type], self::XML_PATH_REGISTER_EMAIL_IDENTITY,
607
  array('customer' => $this, 'back_url' => $backUrl), $storeId);
 
608
 
609
  return $this;
610
  }
@@ -655,6 +665,25 @@ class Mage_Customer_Model_Customer extends Mage_Core_Model_Abstract
655
  return $this;
656
  }
657
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
658
  /**
659
  * Send corresponding email template
660
  *
@@ -662,14 +691,16 @@ class Mage_Customer_Model_Customer extends Mage_Core_Model_Abstract
662
  * @param string $emailSender configuration path of email identity
663
  * @param array $templateParams
664
  * @param int|null $storeId
 
665
  * @return Mage_Customer_Model_Customer
666
  */
667
- protected function _sendEmailTemplate($template, $sender, $templateParams = array(), $storeId = null)
668
  {
 
669
  /** @var $mailer Mage_Core_Model_Email_Template_Mailer */
670
  $mailer = Mage::getModel('core/email_template_mailer');
671
  $emailInfo = Mage::getModel('core/email_info');
672
- $emailInfo->addTo($this->getEmail(), $this->getName());
673
  $mailer->addEmailInfo($emailInfo);
674
 
675
  // Set all required params and send emails
@@ -838,8 +869,9 @@ class Mage_Customer_Model_Customer extends Mage_Core_Model_Abstract
838
  if (!$this->getId() && !Zend_Validate::is($password , 'NotEmpty')) {
839
  $errors[] = Mage::helper('customer')->__('The password cannot be empty.');
840
  }
841
- if (strlen($password) && !Zend_Validate::is($password, 'StringLength', array(6))) {
842
- $errors[] = Mage::helper('customer')->__('The minimum password length is %s', 6);
 
843
  }
844
  $confirmation = $this->getPasswordConfirmation();
845
  if ($password != $confirmation) {
@@ -866,6 +898,32 @@ class Mage_Customer_Model_Customer extends Mage_Core_Model_Abstract
866
  return $errors;
867
  }
868
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
869
  /**
870
  * Import customer data from text array
871
  *
@@ -1339,8 +1397,8 @@ class Mage_Customer_Model_Customer extends Mage_Core_Model_Abstract
1339
  return true;
1340
  }
1341
 
1342
- $dayDifference = floor(($currentTimestamp - $tokenTimestamp) / (24 * 60 * 60));
1343
- if ($dayDifference >= $tokenExpirationPeriod) {
1344
  return true;
1345
  }
1346
 
46
  const XML_PATH_CONFIRM_EMAIL_TEMPLATE = 'customer/create_account/email_confirmation_template';
47
  const XML_PATH_CONFIRMED_EMAIL_TEMPLATE = 'customer/create_account/email_confirmed_template';
48
  const XML_PATH_GENERATE_HUMAN_FRIENDLY_ID = 'customer/create_account/generate_human_friendly_id';
49
+ const XML_PATH_CHANGED_PASSWORD_OR_EMAIL_TEMPLATE = 'customer/changed_account/password_or_email_template';
50
+ const XML_PATH_CHANGED_PASSWORD_OR_EMAIL_IDENTITY = 'customer/changed_account/password_or_email_identity';
51
  /**#@-*/
52
 
53
  /**#@+
68
 
69
  const CACHE_TAG = 'customer';
70
 
71
+ /**
72
+ * Minimum Password Length
73
+ */
74
+ const MINIMUM_PASSWORD_LENGTH = 6;
75
+
76
  /**
77
  * Model event prefix
78
  *
392
  public function hashPassword($password, $salt = null)
393
  {
394
  return $this->_getHelper('core')
395
+ ->getHash(trim($password), !is_null($salt) ? $salt : Mage_Admin_Model_User::HASH_SALT_LENGTH);
396
  }
397
 
398
  /**
592
  * @param string $type
593
  * @param string $backUrl
594
  * @param string $storeId
595
+ * @param string $password
596
  * @throws Mage_Core_Exception
597
  * @return Mage_Customer_Model_Customer
598
  */
599
+ public function sendNewAccountEmail($type = 'registered', $backUrl = '', $storeId = '0', $password = '')
600
  {
601
  $types = array(
602
  'registered' => self::XML_PATH_REGISTER_EMAIL_TEMPLATE, // welcome email, when confirmation is disabled
611
  $storeId = $this->_getWebsiteStoreId($this->getSendemailStoreId());
612
  }
613
 
614
+ $this->setPassword($password);
615
  $this->_sendEmailTemplate($types[$type], self::XML_PATH_REGISTER_EMAIL_IDENTITY,
616
  array('customer' => $this, 'back_url' => $backUrl), $storeId);
617
+ $this->cleanPasswordsValidationData();
618
 
619
  return $this;
620
  }
665
  return $this;
666
  }
667
 
668
+ /**
669
+ * Send info email about changed password or email
670
+ *
671
+ * @return Mage_Customer_Model_Customer
672
+ */
673
+ public function sendChangedPasswordOrEmail()
674
+ {
675
+ $storeId = $this->getStoreId();
676
+ if (!$storeId) {
677
+ $storeId = $this->_getWebsiteStoreId();
678
+ }
679
+
680
+ $this->_sendEmailTemplate(self::XML_PATH_CHANGED_PASSWORD_OR_EMAIL_TEMPLATE,
681
+ self::XML_PATH_CHANGED_PASSWORD_OR_EMAIL_IDENTITY,
682
+ array('customer' => $this), $storeId, $this->getOldEmail());
683
+
684
+ return $this;
685
+ }
686
+
687
  /**
688
  * Send corresponding email template
689
  *
691
  * @param string $emailSender configuration path of email identity
692
  * @param array $templateParams
693
  * @param int|null $storeId
694
+ * @param string|null $customerEmail
695
  * @return Mage_Customer_Model_Customer
696
  */
697
+ protected function _sendEmailTemplate($template, $sender, $templateParams = array(), $storeId = null, $customerEmail = null)
698
  {
699
+ $customerEmail = ($customerEmail) ? $customerEmail : $this->getEmail();
700
  /** @var $mailer Mage_Core_Model_Email_Template_Mailer */
701
  $mailer = Mage::getModel('core/email_template_mailer');
702
  $emailInfo = Mage::getModel('core/email_info');
703
+ $emailInfo->addTo($customerEmail, $this->getName());
704
  $mailer->addEmailInfo($emailInfo);
705
 
706
  // Set all required params and send emails
869
  if (!$this->getId() && !Zend_Validate::is($password , 'NotEmpty')) {
870
  $errors[] = Mage::helper('customer')->__('The password cannot be empty.');
871
  }
872
+ if (strlen($password) && !Zend_Validate::is($password, 'StringLength', array(self::MINIMUM_PASSWORD_LENGTH))) {
873
+ $errors[] = Mage::helper('customer')
874
+ ->__('The minimum password length is %s', self::MINIMUM_PASSWORD_LENGTH);
875
  }
876
  $confirmation = $this->getPasswordConfirmation();
877
  if ($password != $confirmation) {
898
  return $errors;
899
  }
900
 
901
+ /**
902
+ * Validate customer attribute values on password reset
903
+ * @return bool
904
+ */
905
+ public function validateResetPassword()
906
+ {
907
+ $errors = array();
908
+ $password = $this->getPassword();
909
+ if (!Zend_Validate::is($password, 'NotEmpty')) {
910
+ $errors[] = Mage::helper('customer')->__('The password cannot be empty.');
911
+ }
912
+ if (!Zend_Validate::is($password, 'StringLength', array(self::MINIMUM_PASSWORD_LENGTH))) {
913
+ $errors[] = Mage::helper('customer')
914
+ ->__('The minimum password length is %s', self::MINIMUM_PASSWORD_LENGTH);
915
+ }
916
+ $confirmation = $this->getPasswordConfirmation();
917
+ if ($password != $confirmation) {
918
+ $errors[] = Mage::helper('customer')->__('Please make sure your passwords match.');
919
+ }
920
+
921
+ if (empty($errors)) {
922
+ return true;
923
+ }
924
+ return $errors;
925
+ }
926
+
927
  /**
928
  * Import customer data from text array
929
  *
1397
  return true;
1398
  }
1399
 
1400
+ $hoursDifference = floor(($currentTimestamp - $tokenTimestamp) / (60 * 60));
1401
+ if ($hoursDifference >= $tokenExpirationPeriod) {
1402
  return true;
1403
  }
1404
 
app/code/core/Mage/Customer/Model/Flowpassword.php ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magento.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magento.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Customer
23
+ * @copyright Copyright (c) 2006-2016 X.commerce, Inc. and affiliates (http://www.magento.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+
28
+ /**
29
+ * Customer flow password info Model
30
+ *
31
+ * @category Mage
32
+ * @package Mage_Customer
33
+ * @author Magento Core Team <core@magentocommerce.com>
34
+ */
35
+ class Mage_Customer_Model_Flowpassword extends Mage_Core_Model_Abstract
36
+ {
37
+ protected function _construct()
38
+ {
39
+ $this->_init('customer/flowpassword');
40
+ }
41
+
42
+ /**
43
+ * Prepare data before save
44
+ *
45
+ * @return Mage_Core_Model_Abstract
46
+ */
47
+ protected function _beforeSave()
48
+ {
49
+ $this->_prepareData();
50
+ return parent::_beforeSave();
51
+ }
52
+
53
+ /**
54
+ * Prepare customer flow password data
55
+ *
56
+ * @return Mage_Customer_Model_Flowpassword
57
+ */
58
+ protected function _prepareData()
59
+ {
60
+ $validatorData = Mage::getSingleton('customer/session')->getValidatorData();
61
+ $this->setIp($validatorData[Mage_Customer_Model_Session::VALIDATOR_REMOTE_ADDR_KEY])
62
+ ->setRequestedDate(Mage::getModel('core/date')->date());
63
+ return $this;
64
+ }
65
+
66
+ /**
67
+ * Check forgot password requests to times per 24 hours from 1 e-mail
68
+ *
69
+ * @param string $email
70
+ * @return bool
71
+ */
72
+ public function checkCustomerForgotPasswordFlowEmail($email)
73
+ {
74
+ $helper = Mage::helper('customer');
75
+ $checkForgotPasswordFlowTypes = array(
76
+ Mage_Adminhtml_Model_System_Config_Source_Customer_Forgotpassword::FORGOTPASS_FLOW_IP_EMAIL,
77
+ Mage_Adminhtml_Model_System_Config_Source_Customer_Forgotpassword::FORGOTPASS_FLOW_EMAIL
78
+ );
79
+
80
+ if (in_array($helper->getCustomerForgotPasswordFlowSecure(), $checkForgotPasswordFlowTypes)) {
81
+ $forgotPassword = $this->getCollection()
82
+ ->addFieldToFilter('email', array('eq' => $email))
83
+ ->addFieldToFilter('requested_date',
84
+ array('gt' => Mage::getModel('core/date')->date(null, '-1 day')));
85
+
86
+ if ($forgotPassword->getSize() > $helper->getCustomerForgotPasswordEmailTimes()) {
87
+ return false;
88
+ }
89
+ }
90
+ return true;
91
+ }
92
+
93
+ /**
94
+ * Check forgot password requests to times per hour from 1 IP
95
+ *
96
+ * @return bool
97
+ */
98
+ public function checkCustomerForgotPasswordFlowIp()
99
+ {
100
+ $helper = Mage::helper('customer');
101
+ $validatorData = Mage::getSingleton('customer/session')->getValidatorData();
102
+ $remoteAddr = $validatorData[Mage_Customer_Model_Session::VALIDATOR_REMOTE_ADDR_KEY];
103
+ $checkForgotPasswordFlowTypes = array(
104
+ Mage_Adminhtml_Model_System_Config_Source_Customer_Forgotpassword::FORGOTPASS_FLOW_IP_EMAIL,
105
+ Mage_Adminhtml_Model_System_Config_Source_Customer_Forgotpassword::FORGOTPASS_FLOW_IP
106
+ );
107
+
108
+ if (in_array($helper->getCustomerForgotPasswordFlowSecure(), $checkForgotPasswordFlowTypes) && $remoteAddr) {
109
+ $forgotPassword = $this->getCollection()
110
+ ->addFieldToFilter('ip', array('eq' => $remoteAddr))
111
+ ->addFieldToFilter('requested_date',
112
+ array('gt' => Mage::getModel('core/date')->date(null, '-1 hour')));
113
+
114
+ if ($forgotPassword->getSize() > $helper->getCustomerForgotPasswordIpTimes()) {
115
+ return false;
116
+ }
117
+ }
118
+ return true;
119
+ }
120
+ }
121
+
app/code/core/Mage/Customer/Model/Observer.php CHANGED
@@ -141,7 +141,8 @@ class Mage_Customer_Model_Observer
141
  $customerAddress = $observer->getCustomerAddress();
142
  $customer = $customerAddress->getCustomer();
143
 
144
- if (!Mage::helper('customer/address')->isVatValidationEnabled($customer->getStore())
 
145
  || Mage::registry(self::VIV_PROCESSED_FLAG)
146
  || !$this->_canProcessAddress($customerAddress)
147
  ) {
@@ -218,4 +219,15 @@ class Mage_Customer_Model_Observer
218
  );
219
  $customer->save();
220
  }
 
 
 
 
 
 
 
 
 
 
 
221
  }
141
  $customerAddress = $observer->getCustomerAddress();
142
  $customer = $customerAddress->getCustomer();
143
 
144
+ $store = Mage::app()->getStore()->isAdmin() ? $customer->getStore() : null;
145
+ if (!Mage::helper('customer/address')->isVatValidationEnabled($store)
146
  || Mage::registry(self::VIV_PROCESSED_FLAG)
147
  || !$this->_canProcessAddress($customerAddress)
148
  ) {
219
  );
220
  $customer->save();
221
  }
222
+
223
+ /**
224
+ * Clear customer flow password table
225
+ *
226
+ */
227
+ public function deleteCustomerFlowPassword()
228
+ {
229
+ $connection = Mage::getSingleton('core/resource')->getConnection('write');
230
+ $condition = array('requested_date < ?' => Mage::getModel('core/date')->date(null, '-1 day'));
231
+ $connection->delete($connection->getTableName('customer_flowpassword'), $condition);
232
+ }
233
  }
app/code/core/Mage/Customer/Model/Resource/Flowpassword.php ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magento.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magento.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Customer
23
+ * @copyright Copyright (c) 2006-2016 X.commerce, Inc. and affiliates (http://www.magento.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+
28
+ /**
29
+ * Customer flow password info resource model
30
+ *
31
+ * @category Mage
32
+ * @package Mage_Customer
33
+ * @author Magento Core Team <core@magentocommerce.com>
34
+ */
35
+ class Mage_Customer_Model_Resource_Flowpassword extends Mage_Core_Model_Resource_Db_Abstract
36
+ {
37
+ /**
38
+ * Resource initialization
39
+ */
40
+ protected function _construct()
41
+ {
42
+ $this->_init('customer/flowpassword', 'flowpassword_id');
43
+ }
44
+ }
app/code/core/Mage/Customer/Model/Resource/Flowpassword/Collection.php ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magento.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magento.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Customer
23
+ * @copyright Copyright (c) 2006-2016 X.commerce, Inc. and affiliates (http://www.magento.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+
28
+ /**
29
+ * Customer flow password info collection
30
+ *
31
+ * @category Mage
32
+ * @package Mage_Customer
33
+ * @author Magento Core Team <core@magentocommerce.com>
34
+ */
35
+ class Mage_Customer_Model_Resource_Flowpassword_Collection extends Mage_Core_Model_Resource_Db_Collection_Abstract
36
+ {
37
+ /**
38
+ * Resource initialization
39
+ */
40
+ protected function _construct()
41
+ {
42
+ $this->_init('customer/flowpassword');
43
+ }
44
+ }
app/code/core/Mage/Customer/controllers/AccountController.php CHANGED
@@ -339,7 +339,8 @@ class Mage_Customer_AccountController extends Mage_Core_Controller_Front_Action
339
  $customer->sendNewAccountEmail(
340
  'confirmation',
341
  $session->getBeforeAuthUrl(),
342
- $store->getId()
 
343
  );
344
  $customerHelper = $this->_getHelper('customer');
345
  $session->addSuccess($this->__('Account confirmation is required. Please, check your email for the confirmation link. To resend the confirmation email please <a href="%s">click here</a>.',
@@ -571,7 +572,8 @@ class Mage_Customer_AccountController extends Mage_Core_Controller_Front_Action
571
  $customer->sendNewAccountEmail(
572
  $isJustConfirmed ? 'confirmed' : 'registered',
573
  '',
574
- Mage::app()->getStore()->getId()
 
575
  );
576
 
577
  $successUrl = $this->_getUrl('*/*/index', array('_secure' => true));
@@ -722,6 +724,25 @@ class Mage_Customer_AccountController extends Mage_Core_Controller_Front_Action
722
  {
723
  $email = (string) $this->getRequest()->getPost('email');
724
  if ($email) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
725
  if (!Zend_Validate::is($email, 'EmailAddress')) {
726
  $this->_getSession()->setForgottenEmail($email);
727
  $this->_getSession()->addError($this->__('Invalid email address.'));
@@ -825,7 +846,7 @@ class Mage_Customer_AccountController extends Mage_Core_Controller_Front_Action
825
 
826
  $customer->setPassword($password);
827
  $customer->setPasswordConfirmation($passwordConfirmation);
828
- $validationErrorMessages = $customer->validate();
829
  if (is_array($validationErrorMessages)) {
830
  $errorMessages = array_merge($errorMessages, $validationErrorMessages);
831
  }
@@ -927,7 +948,7 @@ class Mage_Customer_AccountController extends Mage_Core_Controller_Front_Action
927
  if ($this->getRequest()->isPost()) {
928
  /** @var $customer Mage_Customer_Model_Customer */
929
  $customer = $this->_getSession()->getCustomer();
930
-
931
  /** @var $customerForm Mage_Customer_Model_Form */
932
  $customerForm = $this->_getModel('customer/form');
933
  $customerForm->setFormCode('customer_account_edit')
@@ -943,32 +964,30 @@ class Mage_Customer_AccountController extends Mage_Core_Controller_Front_Action
943
  $customerForm->compactData($customerData);
944
  $errors = array();
945
 
 
 
 
 
 
 
 
 
946
  // If password change was requested then add it to common validation scheme
947
- if ($this->getRequest()->getParam('change_password')) {
948
- $currPass = $this->getRequest()->getPost('current_password');
 
949
  $newPass = $this->getRequest()->getPost('password');
950
  $confPass = $this->getRequest()->getPost('confirmation');
951
 
952
- $oldPass = $this->_getSession()->getCustomer()->getPasswordHash();
953
- if ( $this->_getHelper('core/string')->strpos($oldPass, ':')) {
954
- list($_salt, $salt) = explode(':', $oldPass);
955
- } else {
956
- $salt = false;
957
- }
958
-
959
- if ($customer->hashPassword($currPass, $salt) == $oldPass) {
960
- if (strlen($newPass)) {
961
- /**
962
- * Set entered password and its confirmation - they
963
- * will be validated later to match each other and be of right length
964
- */
965
- $customer->setPassword($newPass);
966
- $customer->setPasswordConfirmation($confPass);
967
- } else {
968
- $errors[] = $this->__('New password field cannot be empty.');
969
- }
970
  } else {
971
- $errors[] = $this->__('Invalid current password');
972
  }
973
  }
974
 
@@ -990,10 +1009,21 @@ class Mage_Customer_AccountController extends Mage_Core_Controller_Front_Action
990
 
991
  try {
992
  $customer->cleanPasswordsValidationData();
 
 
 
 
 
 
 
993
  $customer->save();
994
  $this->_getSession()->setCustomer($customer)
995
  ->addSuccess($this->__('The account information has been saved.'));
996
 
 
 
 
 
997
  $this->_redirect('customer/account');
998
  return;
999
  } catch (Mage_Core_Exception $e) {
339
  $customer->sendNewAccountEmail(
340
  'confirmation',
341
  $session->getBeforeAuthUrl(),
342
+ $store->getId(),
343
+ $this->getRequest()->getPost('password')
344
  );
345
  $customerHelper = $this->_getHelper('customer');
346
  $session->addSuccess($this->__('Account confirmation is required. Please, check your email for the confirmation link. To resend the confirmation email please <a href="%s">click here</a>.',
572
  $customer->sendNewAccountEmail(
573
  $isJustConfirmed ? 'confirmed' : 'registered',
574
  '',
575
+ Mage::app()->getStore()->getId(),
576
+ $this->getRequest()->getPost('password')
577
  );
578
 
579
  $successUrl = $this->_getUrl('*/*/index', array('_secure' => true));
724
  {
725
  $email = (string) $this->getRequest()->getPost('email');
726
  if ($email) {
727
+ /**
728
+ * @var $flowPassword Mage_Customer_Model_Flowpassword
729
+ */
730
+ $flowPassword = $this->_getModel('customer/flowpassword');
731
+ $flowPassword->setEmail($email)->save();
732
+
733
+ if (!$flowPassword->checkCustomerForgotPasswordFlowEmail($email)) {
734
+ $this->_getSession()
735
+ ->addError($this->__('You have exceeded requests to times per 24 hours from 1 e-mail.'));
736
+ $this->_redirect('*/*/forgotpassword');
737
+ return;
738
+ }
739
+
740
+ if (!$flowPassword->checkCustomerForgotPasswordFlowIp()) {
741
+ $this->_getSession()->addError($this->__('You have exceeded requests to times per hour from 1 IP.'));
742
+ $this->_redirect('*/*/forgotpassword');
743
+ return;
744
+ }
745
+
746
  if (!Zend_Validate::is($email, 'EmailAddress')) {
747
  $this->_getSession()->setForgottenEmail($email);
748
  $this->_getSession()->addError($this->__('Invalid email address.'));
846
 
847
  $customer->setPassword($password);
848
  $customer->setPasswordConfirmation($passwordConfirmation);
849
+ $validationErrorMessages = $customer->validateResetPassword();
850
  if (is_array($validationErrorMessages)) {
851
  $errorMessages = array_merge($errorMessages, $validationErrorMessages);
852
  }
948
  if ($this->getRequest()->isPost()) {
949
  /** @var $customer Mage_Customer_Model_Customer */
950
  $customer = $this->_getSession()->getCustomer();
951
+ $customer->setOldEmail($customer->getEmail());
952
  /** @var $customerForm Mage_Customer_Model_Form */
953
  $customerForm = $this->_getModel('customer/form');
954
  $customerForm->setFormCode('customer_account_edit')
964
  $customerForm->compactData($customerData);
965
  $errors = array();
966
 
967
+ if (!$customer->validatePassword($this->getRequest()->getPost('current_password'))) {
968
+ $errors[] = $this->__('Invalid current password');
969
+ }
970
+
971
+ // If email change was requested then set flag
972
+ $isChangeEmail = ($customer->getOldEmail() != $customer->getEmail()) ? true : false;
973
+ $customer->setIsChangeEmail($isChangeEmail);
974
+
975
  // If password change was requested then add it to common validation scheme
976
+ $customer->setIsChangePassword($this->getRequest()->getParam('change_password'));
977
+
978
+ if ($customer->getIsChangePassword()) {
979
  $newPass = $this->getRequest()->getPost('password');
980
  $confPass = $this->getRequest()->getPost('confirmation');
981
 
982
+ if (strlen($newPass)) {
983
+ /**
984
+ * Set entered password and its confirmation - they
985
+ * will be validated later to match each other and be of right length
986
+ */
987
+ $customer->setPassword($newPass);
988
+ $customer->setPasswordConfirmation($confPass);
 
 
 
 
 
 
 
 
 
 
 
989
  } else {
990
+ $errors[] = $this->__('New password field cannot be empty.');
991
  }
992
  }
993
 
1009
 
1010
  try {
1011
  $customer->cleanPasswordsValidationData();
1012
+
1013
+ // Reset all password reset tokens if all data was sufficient and correct on email change
1014
+ if ($customer->getIsChangeEmail()) {
1015
+ $customer->setRpToken(null);
1016
+ $customer->setRpTokenCreatedAt(null);
1017
+ }
1018
+
1019
  $customer->save();
1020
  $this->_getSession()->setCustomer($customer)
1021
  ->addSuccess($this->__('The account information has been saved.'));
1022
 
1023
+ if ($customer->getIsChangeEmail() || $customer->getIsChangePassword()) {
1024
+ $customer->sendChangedPasswordOrEmail();
1025
+ }
1026
+
1027
  $this->_redirect('customer/account');
1028
  return;
1029
  } catch (Mage_Core_Exception $e) {
app/code/core/Mage/Customer/controllers/AddressController.php CHANGED
@@ -163,6 +163,9 @@ class Mage_Customer_AddressController extends Mage_Core_Controller_Front_Action
163
 
164
  public function deleteAction()
165
  {
 
 
 
166
  $addressId = $this->getRequest()->getParam('id', false);
167
 
168
  if ($addressId) {
163
 
164
  public function deleteAction()
165
  {
166
+ if (!$this->_validateFormKey()) {
167
+ return $this->_redirect('*/*/');
168
+ }
169
  $addressId = $this->getRequest()->getParam('id', false);
170
 
171
  if ($addressId) {
app/code/core/Mage/Customer/data/customer_setup/data-upgrade-1.6.2.0.4-1.6.2.0.5.php ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magento.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magento.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Customer
23
+ * @copyright Copyright (c) 2006-2016 X.commerce, Inc. and affiliates (http://www.magento.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /** @var $installer Mage_Customer_Model_Entity_Setup */
28
+ $installer = $this;
29
+
30
+ $installer->startSetup();
31
+ $connection = $installer->getConnection();
32
+
33
+ $eavConfig = Mage::getSingleton('eav/config');
34
+ $customerEntityTypeId = $eavConfig->getEntityType('customer')->getEntityTypeId();
35
+ $customerAddressEntityTypeId = $eavConfig->getEntityType('customer_address')->getEntityTypeId();
36
+
37
+ $entityTypeIds = array($customerAddressEntityTypeId, $customerEntityTypeId);
38
+
39
+ $attributes = Mage::getResourceModel('eav/entity_attribute_collection')
40
+ ->addFieldToFilter('frontend_input', 'multiselect')
41
+ ->addFieldToFilter('entity_type_id', array('in' => $entityTypeIds))
42
+ ->getItems();
43
+
44
+ foreach ($attributes as $attribute) {
45
+ $entityTypeId = $attribute->getEntityTypeId();
46
+ $attributeId = $attribute->getId();
47
+ $attributeTableOld = $installer->getAttributeTable($entityTypeId, $attributeId);
48
+
49
+ $installer->updateAttribute($entityTypeId, $attributeId, 'backend_type', 'text');
50
+
51
+ $attributeTableNew = $installer->getAttributeTable($entityTypeId, $attributeId);
52
+
53
+ if ($attributeTableOld != $attributeTableNew) {
54
+ $connection->disableTableKeys($attributeTableOld)
55
+ ->disableTableKeys($attributeTableNew);
56
+
57
+ $select = $connection->select()
58
+ ->from($attributeTableOld, array('entity_type_id', 'attribute_id', 'entity_id', 'value'))
59
+ ->where('entity_type_id = ?', $entityTypeId)
60
+ ->where('attribute_id = ?', $attributeId);
61
+
62
+ $query = $select->insertFromSelect($attributeTableNew,
63
+ array('entity_type_id', 'attribute_id', 'entity_id', 'value')
64
+ );
65
+
66
+ $connection->query($query);
67
+
68
+ $connection->delete($attributeTableOld,
69
+ $connection->quoteInto('entity_type_id = ?', $entityTypeId)
70
+ . $connection->quoteInto(' AND attribute_id = ?', $attributeId)
71
+ );
72
+
73
+ $connection->enableTableKeys($attributeTableOld)
74
+ ->enableTableKeys($attributeTableNew);
75
+ }
76
+ }
77
+
78
+ $installer->endSetup();
app/code/core/Mage/Customer/etc/config.xml CHANGED
@@ -28,7 +28,7 @@
28
  <config>
29
  <modules>
30
  <Mage_Customer>
31
- <version>1.6.2.0.4</version>
32
  </Mage_Customer>
33
  </modules>
34
  <admin>
@@ -378,6 +378,9 @@
378
  <form_attribute>
379
  <table>customer_form_attribute</table>
380
  </form_attribute>
 
 
 
381
  </entities>
382
  </customer_resource>
383
  </models>
@@ -416,6 +419,11 @@
416
  <file>password_new.html</file>
417
  <type>html</type>
418
  </customer_password_remind_email_template>
 
 
 
 
 
419
  </email>
420
  </template>
421
  <events>
@@ -512,6 +520,10 @@
512
  <email_confirmed_template>customer_create_account_email_confirmed_template</email_confirmed_template>
513
  <vat_frontend_visibility>0</vat_frontend_visibility>
514
  </create_account>
 
 
 
 
515
  <default>
516
  <group>1</group>
517
  </default>
@@ -519,7 +531,7 @@
519
  <forgot_email_identity>support</forgot_email_identity>
520
  <forgot_email_template>customer_password_forgot_email_template</forgot_email_template>
521
  <remind_email_template>customer_password_remind_email_template</remind_email_template>
522
- <reset_link_expiration_period>1</reset_link_expiration_period>
523
  <require_admin_user_to_change_user_password>1</require_admin_user_to_change_user_password>
524
  </password>
525
  <address>
@@ -576,5 +588,24 @@ T: {{var telephone}}
576
  <js_template><![CDATA[#{prefix} #{firstname} #{middlename} #{lastname} #{suffix}<br/>#{company}<br/>#{street0}<br/>#{street1}<br/>#{street2}<br/>#{street3}<br/>#{city}, #{region}, #{postcode}<br/>#{country_id}<br/>T: #{telephone}<br/>F: #{fax}<br/>VAT: #{vat_id}]]></js_template>
577
  </address_templates>
578
  </customer>
 
 
 
 
 
 
 
579
  </default>
 
 
 
 
 
 
 
 
 
 
 
 
580
  </config>
28
  <config>
29
  <modules>
30
  <Mage_Customer>
31
+ <version>1.6.2.0.5</version>
32
  </Mage_Customer>
33
  </modules>
34
  <admin>
378
  <form_attribute>
379
  <table>customer_form_attribute</table>
380
  </form_attribute>
381
+ <flowpassword>
382
+ <table>customer_flowpassword</table>
383
+ </flowpassword>
384
  </entities>
385
  </customer_resource>
386
  </models>
419
  <file>password_new.html</file>
420
  <type>html</type>
421
  </customer_password_remind_email_template>
422
+ <customer_changed_account_password_or_email_template translate="label" module="customer">
423
+ <label>Changed Password or Email</label>
424
+ <file>password_or_email_changed.html</file>
425
+ <type>html</type>
426
+ </customer_changed_account_password_or_email_template>
427
  </email>
428
  </template>
429
  <events>
520
  <email_confirmed_template>customer_create_account_email_confirmed_template</email_confirmed_template>
521
  <vat_frontend_visibility>0</vat_frontend_visibility>
522
  </create_account>
523
+ <changed_account>
524
+ <password_or_email_identity>general</password_or_email_identity>
525
+ <password_or_email_template>customer_changed_account_password_or_email_template</password_or_email_template>
526
+ </changed_account>
527
  <default>
528
  <group>1</group>
529
  </default>
531
  <forgot_email_identity>support</forgot_email_identity>
532
  <forgot_email_template>customer_password_forgot_email_template</forgot_email_template>
533
  <remind_email_template>customer_password_remind_email_template</remind_email_template>
534
+ <reset_link_expiration_period>2</reset_link_expiration_period>
535
  <require_admin_user_to_change_user_password>1</require_admin_user_to_change_user_password>
536
  </password>
537
  <address>
588
  <js_template><![CDATA[#{prefix} #{firstname} #{middlename} #{lastname} #{suffix}<br/>#{company}<br/>#{street0}<br/>#{street1}<br/>#{street2}<br/>#{street3}<br/>#{city}, #{region}, #{postcode}<br/>#{country_id}<br/>T: #{telephone}<br/>F: #{fax}<br/>VAT: #{vat_id}]]></js_template>
589
  </address_templates>
590
  </customer>
591
+ <admin>
592
+ <security>
593
+ <forgot_password_flow_secure>1</forgot_password_flow_secure>
594
+ <forgot_password_email_times>5</forgot_password_email_times>
595
+ <forgot_password_ip_times>5</forgot_password_ip_times>
596
+ </security>
597
+ </admin>
598
  </default>
599
+ <crontab>
600
+ <jobs>
601
+ <customer_flowpassword>
602
+ <schedule>
603
+ <cron_expr>0 0 1 * *</cron_expr>
604
+ </schedule>
605
+ <run>
606
+ <model>mage_customer/observer::deleteCustomerFlowPassword</model>
607
+ </run>
608
+ </customer_flowpassword>
609
+ </jobs>
610
+ </crontab>
611
  </config>
app/code/core/Mage/Customer/etc/system.xml CHANGED
@@ -250,6 +250,34 @@
250
  </generate_human_friendly_id>
251
  </fields>
252
  </create_account>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
  <password translate="label">
254
  <label>Password Options</label>
255
  <frontend_type>text</frontend_type>
@@ -286,7 +314,7 @@
286
  <show_in_store>1</show_in_store>
287
  </forgot_email_identity>
288
  <reset_link_expiration_period translate="label comment">
289
- <label>Recovery Link Expiration Period (days)</label>
290
  <comment>Please enter a number 1 or greater in this field.</comment>
291
  <frontend_type>text</frontend_type>
292
  <validate>required-entry validate-digits validate-digits-range digits-range-1-</validate>
@@ -492,5 +520,40 @@
492
  </store_information>
493
  </groups>
494
  </general>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
495
  </sections>
496
  </config>
250
  </generate_human_friendly_id>
251
  </fields>
252
  </create_account>
253
+ <changed_account translate="label">
254
+ <label>Change Account Data</label>
255
+ <frontend_type>text</frontend_type>
256
+ <sort_order>25</sort_order>
257
+ <show_in_default>1</show_in_default>
258
+ <show_in_website>1</show_in_website>
259
+ <show_in_store>1</show_in_store>
260
+ <fields>
261
+ <password_or_email_identity translate="label">
262
+ <label>Email Sender</label>
263
+ <frontend_type>select</frontend_type>
264
+ <source_model>adminhtml/system_config_source_email_identity</source_model>
265
+ <sort_order>10</sort_order>
266
+ <show_in_default>1</show_in_default>
267
+ <show_in_website>1</show_in_website>
268
+ <show_in_store>1</show_in_store>
269
+ </password_or_email_identity>
270
+ <password_or_email_template translate="label">
271
+ <label>Changed Email or Password Email Template</label>
272
+ <frontend_type>select</frontend_type>
273
+ <source_model>adminhtml/system_config_source_email_template</source_model>
274
+ <sort_order>20</sort_order>
275
+ <show_in_default>1</show_in_default>
276
+ <show_in_website>1</show_in_website>
277
+ <show_in_store>1</show_in_store>
278
+ </password_or_email_template>
279
+ </fields>
280
+ </changed_account>
281
  <password translate="label">
282
  <label>Password Options</label>
283
  <frontend_type>text</frontend_type>
314
  <show_in_store>1</show_in_store>
315
  </forgot_email_identity>
316
  <reset_link_expiration_period translate="label comment">
317
+ <label>Recovery Link Expiration Period (hours)</label>
318
  <comment>Please enter a number 1 or greater in this field.</comment>
319
  <frontend_type>text</frontend_type>
320
  <validate>required-entry validate-digits validate-digits-range digits-range-1-</validate>
520
  </store_information>
521
  </groups>
522
  </general>
523
+ <admin>
524
+ <groups>
525
+ <security>
526
+ <fields>
527
+ <forgot_password_flow_secure translate="label">
528
+ <label>Forgot password flow secure</label>
529
+ <frontend_type>select</frontend_type>
530
+ <source_model>adminhtml/system_config_source_customer_forgotpassword</source_model>
531
+ <sort_order>140</sort_order>
532
+ <show_in_default>1</show_in_default>
533
+ <show_in_website>1</show_in_website>
534
+ <show_in_store>1</show_in_store>
535
+ </forgot_password_flow_secure>
536
+ <forgot_password_ip_times translate="label comment">
537
+ <label>Forgot password requests to times per hour from 1 IP</label>
538
+ <frontend_type>text</frontend_type>
539
+ <sort_order>150</sort_order>
540
+ <show_in_default>1</show_in_default>
541
+ <show_in_website>1</show_in_website>
542
+ <show_in_store>1</show_in_store>
543
+ <depends><forgot_password_flow_secure separator=",">1,2</forgot_password_flow_secure></depends>
544
+ </forgot_password_ip_times>
545
+ <forgot_password_email_times translate="label">
546
+ <label>Forgot password requests to times per 24 hours from 1 e-mail</label>
547
+ <frontend_type>text</frontend_type>
548
+ <sort_order>160</sort_order>
549
+ <show_in_default>1</show_in_default>
550
+ <show_in_website>1</show_in_website>
551
+ <show_in_store>1</show_in_store>
552
+ <depends><forgot_password_flow_secure separator=",">1,3</forgot_password_flow_secure></depends>
553
+ </forgot_password_email_times>
554
+ </fields>
555
+ </security>
556
+ </groups>
557
+ </admin>
558
  </sections>
559
  </config>
app/code/core/Mage/Customer/sql/customer_setup/upgrade-1.6.2.0.4-1.6.2.0.5.php ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magento.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magento.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Customer
23
+ * @copyright Copyright (c) 2006-2016 X.commerce, Inc. and affiliates (http://www.magento.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /* @var $installer Mage_Customer_Model_Entity_Setup */
28
+ $installer = $this;
29
+ $installer->startSetup();
30
+
31
+ $table = $installer->getConnection()
32
+ ->newTable($installer->getTable('customer/flowpassword'))
33
+ ->addColumn('flowpassword_id', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array(
34
+ 'identity' => true,
35
+ 'unsigned' => true,
36
+ 'nullable' => false,
37
+ 'primary' => true,
38
+ ), 'Flow password Id')
39
+ ->addColumn('ip', Varien_Db_Ddl_Table::TYPE_VARCHAR, 50, array(
40
+ 'nullable' => false,
41
+ ), 'User IP')
42
+ ->addColumn('email', Varien_Db_Ddl_Table::TYPE_VARCHAR, 255, array(
43
+ 'nullable' => false,
44
+ ), 'Requested email for change')
45
+ ->addColumn('requested_date', Varien_Db_Ddl_Table::TYPE_VARCHAR, 255, array(
46
+ 'nullable' => false,
47
+ 'default' => '0000-00-00 00:00:00',
48
+ ), 'Requested date for change')
49
+ ->addIndex($installer->getIdxName('customer/flowpassword', array('email')),
50
+ array('email'))
51
+ ->addIndex($installer->getIdxName('customer/flowpassword', array('ip')),
52
+ array('ip'))
53
+ ->addIndex($installer->getIdxName('customer/flowpassword', array('requested_date')),
54
+ array('requested_date'))
55
+ ->setComment('Customer flow password');
56
+ $installer->getConnection()->createTable($table);
57
+
58
+ $installer->endSetup();
app/code/core/Mage/Dataflow/Model/Profile.php CHANGED
@@ -64,10 +64,14 @@ class Mage_Dataflow_Model_Profile extends Mage_Core_Model_Abstract
64
 
65
  protected function _afterLoad()
66
  {
 
67
  if (is_string($this->getGuiData())) {
68
- $guiData = unserialize($this->getGuiData());
69
- } else {
70
- $guiData = '';
 
 
 
71
  }
72
  $this->setGuiData($guiData);
73
 
@@ -127,7 +131,13 @@ class Mage_Dataflow_Model_Profile extends Mage_Core_Model_Abstract
127
  protected function _afterSave()
128
  {
129
  if (is_string($this->getGuiData())) {
130
- $this->setGuiData(unserialize($this->getGuiData()));
 
 
 
 
 
 
131
  }
132
 
133
  $profileHistory = Mage::getModel('dataflow/profile_history');
64
 
65
  protected function _afterLoad()
66
  {
67
+ $guiData = '';
68
  if (is_string($this->getGuiData())) {
69
+ try {
70
+ $guiData = Mage::helper('core/unserializeArray')
71
+ ->unserialize($this->getGuiData());
72
+ } catch (Exception $e) {
73
+ Mage::logException($e);
74
+ }
75
  }
76
  $this->setGuiData($guiData);
77
 
131
  protected function _afterSave()
132
  {
133
  if (is_string($this->getGuiData())) {
134
+ try {
135
+ $guiData = Mage::helper('core/unserializeArray')
136
+ ->unserialize($this->getGuiData());
137
+ $this->setGuiData($guiData);
138
+ } catch (Exception $e) {
139
+ Mage::logException($e);
140
+ }
141
  }
142
 
143
  $profileHistory = Mage::getModel('dataflow/profile_history');
app/code/core/Mage/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/Links.php CHANGED
@@ -32,7 +32,7 @@
32
  * @author Magento Core Team <core@magentocommerce.com>
33
  */
34
  class Mage_Downloadable_Block_Adminhtml_Catalog_Product_Edit_Tab_Downloadable_Links
35
- extends Mage_Adminhtml_Block_Template
36
  {
37
  /**
38
  * Purchased Separately Attribute cache
@@ -242,6 +242,7 @@ class Mage_Downloadable_Block_Adminhtml_Catalog_Product_Edit_Tab_Downloadable_Li
242
  */
243
  protected function _prepareLayout()
244
  {
 
245
  $this->setChild(
246
  'upload_button',
247
  $this->getLayout()->createBlock('adminhtml/widget_button')->addData(array(
@@ -251,6 +252,10 @@ class Mage_Downloadable_Block_Adminhtml_Catalog_Product_Edit_Tab_Downloadable_Li
251
  'onclick' => 'Downloadable.massUploadByType(\'links\');Downloadable.massUploadByType(\'linkssample\')'
252
  ))
253
  );
 
 
 
 
254
  }
255
 
256
  /**
@@ -270,33 +275,56 @@ class Mage_Downloadable_Block_Adminhtml_Catalog_Product_Edit_Tab_Downloadable_Li
270
  */
271
  public function getConfigJson($type='links')
272
  {
273
- $this->getConfig()->setUrl(Mage::getModel('adminhtml/url')->addSessionParam()
274
- ->getUrl('*/downloadable_file/upload', array('type' => $type, '_secure' => true)));
275
- $this->getConfig()->setParams(array('form_key' => $this->getFormKey()));
276
- $this->getConfig()->setFileField($type);
277
- $this->getConfig()->setFilters(array(
278
- 'all' => array(
279
- 'label' => Mage::helper('adminhtml')->__('All Files'),
280
- 'files' => array('*.*')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
281
  )
282
- ));
283
- $this->getConfig()->setReplaceBrowseWithRemove(true);
284
- $this->getConfig()->setWidth('32');
285
- $this->getConfig()->setHideUploadButton(true);
286
- return Mage::helper('core')->jsonEncode($this->getConfig()->getData());
287
  }
288
 
 
289
  /**
290
- * Retrive config object
 
 
 
 
 
 
 
 
 
 
 
 
291
  *
292
- * @return Varien_Config
 
293
  */
294
  public function getConfig()
295
  {
296
- if(is_null($this->_config)) {
297
- $this->_config = new Varien_Object();
298
- }
299
-
300
- return $this->_config;
301
  }
302
  }
32
  * @author Magento Core Team <core@magentocommerce.com>
33
  */
34
  class Mage_Downloadable_Block_Adminhtml_Catalog_Product_Edit_Tab_Downloadable_Links
35
+ extends Mage_Uploader_Block_Single
36
  {
37
  /**
38
  * Purchased Separately Attribute cache
242
  */
243
  protected function _prepareLayout()
244
  {
245
+ parent::_prepareLayout();
246
  $this->setChild(
247
  'upload_button',
248
  $this->getLayout()->createBlock('adminhtml/widget_button')->addData(array(
252
  'onclick' => 'Downloadable.massUploadByType(\'links\');Downloadable.massUploadByType(\'linkssample\')'
253
  ))
254
  );
255
+ $this->_addElementIdsMapping(array(
256
+ 'container' => $this->getHtmlId() . '-new',
257
+ 'delete' => $this->getHtmlId() . '-delete'
258
+ ));
259
  }
260
 
261
  /**
275
  */
276
  public function getConfigJson($type='links')
277
  {
278
+
279
+ $this->getUploaderConfig()
280
+ ->setFileParameterName($type)
281
+ ->setTarget(
282
+ Mage::getModel('adminhtml/url')
283
+ ->addSessionParam()
284
+ ->getUrl('*/downloadable_file/upload', array('type' => $type, '_secure' => true))
285
+ );
286
+ $this->getMiscConfig()
287
+ ->setReplaceBrowseWithRemove(true)
288
+ ;
289
+ return Mage::helper('core')->jsonEncode(parent::getJsonConfig());
290
+ }
291
+
292
+ /**
293
+ * @return string
294
+ */
295
+ public function getBrowseButtonHtml($type = '')
296
+ {
297
+ return $this->getChild('browse_button')
298
+ // Workaround for IE9
299
+ ->setBeforeHtml(
300
+ '<div style="display:inline-block; " id="downloadable_link_{{id}}_' . $type . 'file-browse">'
301
  )
302
+ ->setAfterHtml('</div>')
303
+ ->setId('downloadable_link_{{id}}_' . $type . 'file-browse_button')
304
+ ->toHtml();
 
 
305
  }
306
 
307
+
308
  /**
309
+ * @return string
310
+ */
311
+ public function getDeleteButtonHtml($type = '')
312
+ {
313
+ return $this->getChild('delete_button')
314
+ ->setLabel('')
315
+ ->setId('downloadable_link_{{id}}_' . $type . 'file-delete')
316
+ ->setStyle('display:none; width:31px;')
317
+ ->toHtml();
318
+ }
319
+
320
+ /**
321
+ * Retrieve config object
322
  *
323
+ * @deprecated
324
+ * @return $this
325
  */
326
  public function getConfig()
327
  {
328
+ return $this;
 
 
 
 
329
  }
330
  }
app/code/core/Mage/Downloadable/Block/Adminhtml/Catalog/Product/Edit/Tab/Downloadable/Samples.php CHANGED
@@ -32,7 +32,7 @@
32
  * @author Magento Core Team <core@magentocommerce.com>
33
  */
34
  class Mage_Downloadable_Block_Adminhtml_Catalog_Product_Edit_Tab_Downloadable_Samples
35
- extends Mage_Adminhtml_Block_Widget
36
  {
37
  /**
38
  * Class constructor
@@ -148,6 +148,7 @@ class Mage_Downloadable_Block_Adminhtml_Catalog_Product_Edit_Tab_Downloadable_Sa
148
  */
149
  protected function _prepareLayout()
150
  {
 
151
  $this->setChild(
152
  'upload_button',
153
  $this->getLayout()->createBlock('adminhtml/widget_button')
@@ -158,6 +159,11 @@ class Mage_Downloadable_Block_Adminhtml_Catalog_Product_Edit_Tab_Downloadable_Sa
158
  'onclick' => 'Downloadable.massUploadByType(\'samples\')'
159
  ))
160
  );
 
 
 
 
 
161
  }
162
 
163
  /**
@@ -171,40 +177,59 @@ class Mage_Downloadable_Block_Adminhtml_Catalog_Product_Edit_Tab_Downloadable_Sa
171
  }
172
 
173
  /**
174
- * Retrive config json
175
  *
176
  * @return string
177
  */
178
  public function getConfigJson()
179
  {
180
- $this->getConfig()->setUrl(Mage::getModel('adminhtml/url')
181
- ->addSessionParam()
182
- ->getUrl('*/downloadable_file/upload', array('type' => 'samples', '_secure' => true)));
183
- $this->getConfig()->setParams(array('form_key' => $this->getFormKey()));
184
- $this->getConfig()->setFileField('samples');
185
- $this->getConfig()->setFilters(array(
186
- 'all' => array(
187
- 'label' => Mage::helper('adminhtml')->__('All Files'),
188
- 'files' => array('*.*')
189
- )
190
- ));
191
- $this->getConfig()->setReplaceBrowseWithRemove(true);
192
- $this->getConfig()->setWidth('32');
193
- $this->getConfig()->setHideUploadButton(true);
194
- return Mage::helper('core')->jsonEncode($this->getConfig()->getData());
195
  }
196
 
197
  /**
198
- * Retrive config object
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
  *
200
- * @return Varien_Config
 
201
  */
202
  public function getConfig()
203
  {
204
- if(is_null($this->_config)) {
205
- $this->_config = new Varien_Object();
206
- }
207
-
208
- return $this->_config;
209
  }
210
  }
32
  * @author Magento Core Team <core@magentocommerce.com>
33
  */
34
  class Mage_Downloadable_Block_Adminhtml_Catalog_Product_Edit_Tab_Downloadable_Samples
35
+ extends Mage_Uploader_Block_Single
36
  {
37
  /**
38
  * Class constructor
148
  */
149
  protected function _prepareLayout()
150
  {
151
+ parent::_prepareLayout();
152
  $this->setChild(
153
  'upload_button',
154
  $this->getLayout()->createBlock('adminhtml/widget_button')
159
  'onclick' => 'Downloadable.massUploadByType(\'samples\')'
160
  ))
161
  );
162
+
163
+ $this->_addElementIdsMapping(array(
164
+ 'container' => $this->getHtmlId() . '-new',
165
+ 'delete' => $this->getHtmlId() . '-delete'
166
+ ));
167
  }
168
 
169
  /**
177
  }
178
 
179
  /**
180
+ * Retrieve config json
181
  *
182
  * @return string
183
  */
184
  public function getConfigJson()
185
  {
186
+ $this->getUploaderConfig()
187
+ ->setFileParameterName('samples')
188
+ ->setTarget(
189
+ Mage::getModel('adminhtml/url')
190
+ ->addSessionParam()
191
+ ->getUrl('*/downloadable_file/upload', array('type' => 'samples', '_secure' => true))
192
+ );
193
+ $this->getMiscConfig()
194
+ ->setReplaceBrowseWithRemove(true)
195
+ ;
196
+ return Mage::helper('core')->jsonEncode(parent::getJsonConfig());
 
 
 
 
197
  }
198
 
199
  /**
200
+ * @return string
201
+ */
202
+ public function getBrowseButtonHtml()
203
+ {
204
+ return $this->getChild('browse_button')
205
+ // Workaround for IE9
206
+ ->setBeforeHtml('<div style="display:inline-block; " id="downloadable_sample_{{id}}_file-browse">')
207
+ ->setAfterHtml('</div>')
208
+ ->setId('downloadable_sample_{{id}}_file-browse_button')
209
+ ->toHtml();
210
+ }
211
+
212
+
213
+ /**
214
+ * @return string
215
+ */
216
+ public function getDeleteButtonHtml()
217
+ {
218
+ return $this->getChild('delete_button')
219
+ ->setLabel('')
220
+ ->setId('downloadable_sample_{{id}}_file-delete')
221
+ ->setStyle('display:none; width:31px;')
222
+ ->toHtml();
223
+ }
224
+
225
+ /**
226
+ * Retrieve config object
227
  *
228
+ * @deprecated
229
+ * @return $this
230
  */
231
  public function getConfig()
232
  {
233
+ return $this;
 
 
 
 
234
  }
235
  }
app/code/core/Mage/Downloadable/Helper/File.php CHANGED
@@ -33,15 +33,35 @@
33
  */
34
  class Mage_Downloadable_Helper_File extends Mage_Core_Helper_Abstract
35
  {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  public function __construct()
37
  {
38
- $nodes = Mage::getConfig()->getNode('global/mime/types');
39
- if ($nodes) {
40
- $nodes = (array)$nodes;
41
- foreach ($nodes as $key => $value) {
42
- self::$_mimeTypes[$key] = $value;
43
- }
 
 
 
 
44
  }
 
 
45
  }
46
 
47
  /**
@@ -152,628 +172,48 @@ class Mage_Downloadable_Helper_File extends Mage_Core_Helper_Abstract
152
  return $file;
153
  }
154
 
 
 
 
 
 
 
155
  public function getFileType($filePath)
156
  {
157
  $ext = substr($filePath, strrpos($filePath, '.')+1);
158
  return $this->_getFileTypeByExt($ext);
159
  }
160
 
 
 
 
 
 
 
 
161
  protected function _getFileTypeByExt($ext)
162
  {
163
- $type = 'x' . $ext;
164
- if (isset(self::$_mimeTypes[$type])) {
165
- return self::$_mimeTypes[$type];
166
- }
167
- return 'application/octet-stream';
168
  }
169
 
 
 
 
 
 
170
  public function getAllFileTypes()
171
  {
172
- return array_values(self::getAllMineTypes());
173
  }
174
 
 
 
 
 
 
175
  public function getAllMineTypes()
176
  {
177
- return self::$_mimeTypes;
178
  }
179
 
180
- protected static $_mimeTypes =
181
- array(
182
- 'x123' => 'application/vnd.lotus-1-2-3',
183
- 'x3dml' => 'text/vnd.in3d.3dml',
184
- 'x3g2' => 'video/3gpp2',
185
- 'x3gp' => 'video/3gpp',
186
- 'xace' => 'application/x-ace-compressed',
187
- 'xacu' => 'application/vnd.acucobol',
188
- 'xaep' => 'application/vnd.audiograph',
189
- 'xai' => 'application/postscript',
190
- 'xaif' => 'audio/x-aiff',
191
-
192
- 'xaifc' => 'audio/x-aiff',
193
- 'xaiff' => 'audio/x-aiff',
194
- 'xami' => 'application/vnd.amiga.ami',
195
- 'xapr' => 'application/vnd.lotus-approach',
196
- 'xasf' => 'video/x-ms-asf',
197
- 'xaso' => 'application/vnd.accpac.simply.aso',
198
- 'xasx' => 'video/x-ms-asf',
199
- 'xatom' => 'application/atom+xml',
200
- 'xatomcat' => 'application/atomcat+xml',
201
-
202
- 'xatomsvc' => 'application/atomsvc+xml',
203
- 'xatx' => 'application/vnd.antix.game-component',
204
- 'xau' => 'audio/basic',
205
- 'xavi' => 'video/x-msvideo',
206
- 'xbat' => 'application/x-msdownload',
207
- 'xbcpio' => 'application/x-bcpio',
208
- 'xbdm' => 'application/vnd.syncml.dm+wbxml',
209
- 'xbh2' => 'application/vnd.fujitsu.oasysprs',
210
- 'xbmi' => 'application/vnd.bmi',
211
-
212
- 'xbmp' => 'image/bmp',
213
- 'xbox' => 'application/vnd.previewsystems.box',
214
- 'xboz' => 'application/x-bzip2',
215
- 'xbtif' => 'image/prs.btif',
216
- 'xbz' => 'application/x-bzip',
217
- 'xbz2' => 'application/x-bzip2',
218
- 'xcab' => 'application/vnd.ms-cab-compressed',
219
- 'xccxml' => 'application/ccxml+xml',
220
- 'xcdbcmsg' => 'application/vnd.contact.cmsg',
221
-
222
- 'xcdkey' => 'application/vnd.mediastation.cdkey',
223
- 'xcdx' => 'chemical/x-cdx',
224
- 'xcdxml' => 'application/vnd.chemdraw+xml',
225
- 'xcdy' => 'application/vnd.cinderella',
226
- 'xcer' => 'application/pkix-cert',
227
- 'xcgm' => 'image/cgm',
228
- 'xchat' => 'application/x-chat',
229
- 'xchm' => 'application/vnd.ms-htmlhelp',
230
- 'xchrt' => 'application/vnd.kde.kchart',
231
-
232
- 'xcif' => 'chemical/x-cif',
233
- 'xcii' => 'application/vnd.anser-web-certificate-issue-initiation',
234
- 'xcil' => 'application/vnd.ms-artgalry',
235
- 'xcla' => 'application/vnd.claymore',
236
- 'xclkk' => 'application/vnd.crick.clicker.keyboard',
237
- 'xclkp' => 'application/vnd.crick.clicker.palette',
238
- 'xclkt' => 'application/vnd.crick.clicker.template',
239
- 'xclkw' => 'application/vnd.crick.clicker.wordbank',
240
- 'xclkx' => 'application/vnd.crick.clicker',
241
-
242
- 'xclp' => 'application/x-msclip',
243
- 'xcmc' => 'application/vnd.cosmocaller',
244
- 'xcmdf' => 'chemical/x-cmdf',
245
- 'xcml' => 'chemical/x-cml',
246
- 'xcmp' => 'application/vnd.yellowriver-custom-menu',
247
- 'xcmx' => 'image/x-cmx',
248
- 'xcom' => 'application/x-msdownload',
249
- 'xconf' => 'text/plain',
250
- 'xcpio' => 'application/x-cpio',
251
-
252
- 'xcpt' => 'application/mac-compactpro',
253
- 'xcrd' => 'application/x-mscardfile',
254
- 'xcrl' => 'application/pkix-crl',
255
- 'xcrt' => 'application/x-x509-ca-cert',
256
- 'xcsh' => 'application/x-csh',
257
- 'xcsml' => 'chemical/x-csml',
258
- 'xcss' => 'text/css',
259
- 'xcsv' => 'text/csv',
260
- 'xcurl' => 'application/vnd.curl',
261
-
262
- 'xcww' => 'application/prs.cww',
263
- 'xdaf' => 'application/vnd.mobius.daf',
264
- 'xdavmount' => 'application/davmount+xml',
265
- 'xdd2' => 'application/vnd.oma.dd2+xml',
266
- 'xddd' => 'application/vnd.fujixerox.ddd',
267
- 'xdef' => 'text/plain',
268
- 'xder' => 'application/x-x509-ca-cert',
269
- 'xdfac' => 'application/vnd.dreamfactory',
270
- 'xdis' => 'application/vnd.mobius.dis',
271
-
272
- 'xdjv' => 'image/vnd.djvu',
273
- 'xdjvu' => 'image/vnd.djvu',
274
- 'xdll' => 'application/x-msdownload',
275
- 'xdna' => 'application/vnd.dna',
276
- 'xdoc' => 'application/msword',
277
- 'xdot' => 'application/msword',
278
- 'xdp' => 'application/vnd.osgi.dp',
279
- 'xdpg' => 'application/vnd.dpgraph',
280
- 'xdsc' => 'text/prs.lines.tag',
281
-
282
- 'xdtd' => 'application/xml-dtd',
283
- 'xdvi' => 'application/x-dvi',
284
- 'xdwf' => 'model/vnd.dwf',
285
- 'xdwg' => 'image/vnd.dwg',
286
- 'xdxf' => 'image/vnd.dxf',
287
- 'xdxp' => 'application/vnd.spotfire.dxp',
288
- 'xecelp4800' => 'audio/vnd.nuera.ecelp4800',
289
- 'xecelp7470' => 'audio/vnd.nuera.ecelp7470',
290
- 'xecelp9600' => 'audio/vnd.nuera.ecelp9600',
291
-
292
- 'xecma' => 'application/ecmascript',
293
- 'xedm' => 'application/vnd.novadigm.edm',
294
- 'xedx' => 'application/vnd.novadigm.edx',
295
- 'xefif' => 'application/vnd.picsel',
296
- 'xei6' => 'application/vnd.pg.osasli',
297
- 'xeml' => 'message/rfc822',
298
- 'xeol' => 'audio/vnd.digital-winds',
299
- 'xeot' => 'application/vnd.ms-fontobject',
300
- 'xeps' => 'application/postscript',
301
-
302
- 'xesf' => 'application/vnd.epson.esf',
303
- 'xetx' => 'text/x-setext',
304
- 'xexe' => 'application/x-msdownload',
305
- 'xext' => 'application/vnd.novadigm.ext',
306
- 'xez' => 'application/andrew-inset',
307
- 'xez2' => 'application/vnd.ezpix-album',
308
- 'xez3' => 'application/vnd.ezpix-package',
309
- 'xfbs' => 'image/vnd.fastbidsheet',
310
- 'xfdf' => 'application/vnd.fdf',
311
-
312
- 'xfe_launch' => 'application/vnd.denovo.fcselayout-link',
313
- 'xfg5' => 'application/vnd.fujitsu.oasysgp',
314
- 'xfli' => 'video/x-fli',
315
- 'xflo' => 'application/vnd.micrografx.flo',
316
- 'xflw' => 'application/vnd.kde.kivio',
317
- 'xflx' => 'text/vnd.fmi.flexstor',
318
- 'xfly' => 'text/vnd.fly',
319
- 'xfnc' => 'application/vnd.frogans.fnc',
320
- 'xfpx' => 'image/vnd.fpx',
321
-
322
- 'xfsc' => 'application/vnd.fsc.weblaunch',
323
- 'xfst' => 'image/vnd.fst',
324
- 'xftc' => 'application/vnd.fluxtime.clip',
325
- 'xfti' => 'application/vnd.anser-web-funds-transfer-initiation',
326
- 'xfvt' => 'video/vnd.fvt',
327
- 'xfzs' => 'application/vnd.fuzzysheet',
328
- 'xg3' => 'image/g3fax',
329
- 'xgac' => 'application/vnd.groove-account',
330
- 'xgdl' => 'model/vnd.gdl',
331
-
332
- 'xghf' => 'application/vnd.groove-help',
333
- 'xgif' => 'image/gif',
334
- 'xgim' => 'application/vnd.groove-identity-message',
335
- 'xgph' => 'application/vnd.flographit',
336
- 'xgram' => 'application/srgs',
337
- 'xgrv' => 'application/vnd.groove-injector',
338
- 'xgrxml' => 'application/srgs+xml',
339
- 'xgtar' => 'application/x-gtar',
340
- 'xgtm' => 'application/vnd.groove-tool-message',
341
-
342
- 'xgtw' => 'model/vnd.gtw',
343
- 'xh261' => 'video/h261',
344
- 'xh263' => 'video/h263',
345
- 'xh264' => 'video/h264',
346
- 'xhbci' => 'application/vnd.hbci',
347
- 'xhdf' => 'application/x-hdf',
348
- 'xhlp' => 'application/winhlp',
349
- 'xhpgl' => 'application/vnd.hp-hpgl',
350
- 'xhpid' => 'application/vnd.hp-hpid',
351
-
352
- 'xhps' => 'application/vnd.hp-hps',
353
- 'xhqx' => 'application/mac-binhex40',
354
- 'xhtke' => 'application/vnd.kenameaapp',
355
- 'xhtm' => 'text/html',
356
- 'xhtml' => 'text/html',
357
- 'xhvd' => 'application/vnd.yamaha.hv-dic',
358
- 'xhvp' => 'application/vnd.yamaha.hv-voice',
359
- 'xhvs' => 'application/vnd.yamaha.hv-script',
360
- 'xice' => '#x-conference/x-cooltalk',
361
-
362
- 'xico' => 'image/x-icon',
363
- 'xics' => 'text/calendar',
364
- 'xief' => 'image/ief',
365
- 'xifb' => 'text/calendar',
366
- 'xifm' => 'application/vnd.shana.informed.formdata',
367
- 'xigl' => 'application/vnd.igloader',
368
- 'xigx' => 'application/vnd.micrografx.igx',
369
- 'xiif' => 'application/vnd.shana.informed.interchange',
370
- 'ximp' => 'application/vnd.accpac.simply.imp',
371
-
372
- 'xims' => 'application/vnd.ms-ims',
373
- 'xin' => 'text/plain',
374
- 'xipk' => 'application/vnd.shana.informed.package',
375
- 'xirm' => 'application/vnd.ibm.rights-management',
376
- 'xirp' => 'application/vnd.irepository.package+xml',
377
- 'xitp' => 'application/vnd.shana.informed.formtemplate',
378
- 'xivp' => 'application/vnd.immervision-ivp',
379
- 'xivu' => 'application/vnd.immervision-ivu',
380
- 'xjad' => 'text/vnd.sun.j2me.app-descriptor',
381
-
382
- 'xjam' => 'application/vnd.jam',
383
- 'xjava' => 'text/x-java-source',
384
- 'xjisp' => 'application/vnd.jisp',
385
- 'xjlt' => 'application/vnd.hp-jlyt',
386
- 'xjoda' => 'application/vnd.joost.joda-archive',
387
- 'xjpe' => 'image/jpeg',
388
- 'xjpeg' => 'image/jpeg',
389
- 'xjpg' => 'image/jpeg',
390
- 'xjpgm' => 'video/jpm',
391
-
392
- 'xjpgv' => 'video/jpeg',
393
- 'xjpm' => 'video/jpm',
394
- 'xjs' => 'application/javascript',
395
- 'xjson' => 'application/json',
396
- 'xkar' => 'audio/midi',
397
- 'xkarbon' => 'application/vnd.kde.karbon',
398
- 'xkfo' => 'application/vnd.kde.kformula',
399
- 'xkia' => 'application/vnd.kidspiration',
400
- 'xkml' => 'application/vnd.google-earth.kml+xml',
401
-
402
- 'xkmz' => 'application/vnd.google-earth.kmz',
403
- 'xkon' => 'application/vnd.kde.kontour',
404
- 'xksp' => 'application/vnd.kde.kspread',
405
- 'xlatex' => 'application/x-latex',
406
- 'xlbd' => 'application/vnd.llamagraphics.life-balance.desktop',
407
- 'xlbe' => 'application/vnd.llamagraphics.life-balance.exchange+xml',
408
- 'xles' => 'application/vnd.hhe.lesson-player',
409
- 'xlist' => 'text/plain',
410
- 'xlog' => 'text/plain',
411
-
412
- 'xlrm' => 'application/vnd.ms-lrm',
413
- 'xltf' => 'application/vnd.frogans.ltf',
414
- 'xlvp' => 'audio/vnd.lucent.voice',
415
- 'xlwp' => 'application/vnd.lotus-wordpro',
416
- 'xm13' => 'application/x-msmediaview',
417
- 'xm14' => 'application/x-msmediaview',
418
- 'xm1v' => 'video/mpeg',
419
- 'xm2a' => 'audio/mpeg',
420
- 'xm3a' => 'audio/mpeg',
421
-
422
- 'xm3u' => 'audio/x-mpegurl',
423
- 'xm4u' => 'video/vnd.mpegurl',
424
- 'xmag' => 'application/vnd.ecowin.chart',
425
- 'xmathml' => 'application/mathml+xml',
426
- 'xmbk' => 'application/vnd.mobius.mbk',
427
- 'xmbox' => 'application/mbox',
428
- 'xmc1' => 'application/vnd.medcalcdata',
429
- 'xmcd' => 'application/vnd.mcd',
430
- 'xmdb' => 'application/x-msaccess',
431
-
432
- 'xmdi' => 'image/vnd.ms-modi',
433
- 'xmesh' => 'model/mesh',
434
- 'xmfm' => 'application/vnd.mfmp',
435
- 'xmgz' => 'application/vnd.proteus.magazine',
436
- 'xmid' => 'audio/midi',
437
- 'xmidi' => 'audio/midi',
438
- 'xmif' => 'application/vnd.mif',
439
- 'xmime' => 'message/rfc822',
440
- 'xmj2' => 'video/mj2',
441
-
442
- 'xmjp2' => 'video/mj2',
443
- 'xmlp' => 'application/vnd.dolby.mlp',
444
- 'xmmd' => 'application/vnd.chipnuts.karaoke-mmd',
445
- 'xmmf' => 'application/vnd.smaf',
446
- 'xmmr' => 'image/vnd.fujixerox.edmics-mmr',
447
- 'xmny' => 'application/x-msmoney',
448
- 'xmov' => 'video/quicktime',
449
- 'xmovie' => 'video/x-sgi-movie',
450
- 'xmp2' => 'audio/mpeg',
451
-
452
- 'xmp2a' => 'audio/mpeg',
453
- 'xmp3' => 'audio/mpeg',
454
- 'xmp4' => 'video/mp4',
455
- 'xmp4a' => 'audio/mp4',
456
- 'xmp4s' => 'application/mp4',
457
- 'xmp4v' => 'video/mp4',
458
- 'xmpc' => 'application/vnd.mophun.certificate',
459
- 'xmpe' => 'video/mpeg',
460
- 'xmpeg' => 'video/mpeg',
461
-
462
- 'xmpg' => 'video/mpeg',
463
- 'xmpg4' => 'video/mp4',
464
- 'xmpga' => 'audio/mpeg',
465
- 'xmpkg' => 'application/vnd.apple.installer+xml',
466
- 'xmpm' => 'application/vnd.blueice.multipass',
467
- 'xmpn' => 'application/vnd.mophun.application',
468
- 'xmpp' => 'application/vnd.ms-project',
469
- 'xmpt' => 'application/vnd.ms-project',
470
- 'xmpy' => 'application/vnd.ibm.minipay',
471
-
472
- 'xmqy' => 'application/vnd.mobius.mqy',
473
- 'xmrc' => 'application/marc',
474
- 'xmscml' => 'application/mediaservercontrol+xml',
475
- 'xmseq' => 'application/vnd.mseq',
476
- 'xmsf' => 'application/vnd.epson.msf',
477
- 'xmsh' => 'model/mesh',
478
- 'xmsi' => 'application/x-msdownload',
479
- 'xmsl' => 'application/vnd.mobius.msl',
480
- 'xmsty' => 'application/vnd.muvee.style',
481
-
482
- 'xmts' => 'model/vnd.mts',
483
- 'xmus' => 'application/vnd.musician',
484
- 'xmvb' => 'application/x-msmediaview',
485
- 'xmwf' => 'application/vnd.mfer',
486
- 'xmxf' => 'application/mxf',
487
- 'xmxl' => 'application/vnd.recordare.musicxml',
488
- 'xmxml' => 'application/xv+xml',
489
- 'xmxs' => 'application/vnd.triscape.mxs',
490
- 'xmxu' => 'video/vnd.mpegurl',
491
-
492
- 'xn-gage' => 'application/vnd.nokia.n-gage.symbian.install',
493
- 'xngdat' => 'application/vnd.nokia.n-gage.data',
494
- 'xnlu' => 'application/vnd.neurolanguage.nlu',
495
- 'xnml' => 'application/vnd.enliven',
496
- 'xnnd' => 'application/vnd.noblenet-directory',
497
- 'xnns' => 'application/vnd.noblenet-sealer',
498
- 'xnnw' => 'application/vnd.noblenet-web',
499
- 'xnpx' => 'image/vnd.net-fpx',
500
- 'xnsf' => 'application/vnd.lotus-notes',
501
-
502
- 'xoa2' => 'application/vnd.fujitsu.oasys2',
503
- 'xoa3' => 'application/vnd.fujitsu.oasys3',
504
- 'xoas' => 'application/vnd.fujitsu.oasys',
505
- 'xobd' => 'application/x-msbinder',
506
- 'xoda' => 'application/oda',
507
- 'xodc' => 'application/vnd.oasis.opendocument.chart',
508
- 'xodf' => 'application/vnd.oasis.opendocument.formula',
509
- 'xodg' => 'application/vnd.oasis.opendocument.graphics',
510
- 'xodi' => 'application/vnd.oasis.opendocument.image',
511
-
512
- 'xodp' => 'application/vnd.oasis.opendocument.presentation',
513
- 'xods' => 'application/vnd.oasis.opendocument.spreadsheet',
514
- 'xodt' => 'application/vnd.oasis.opendocument.text',
515
- 'xogg' => 'application/ogg',
516
- 'xoprc' => 'application/vnd.palm',
517
- 'xorg' => 'application/vnd.lotus-organizer',
518
- 'xotc' => 'application/vnd.oasis.opendocument.chart-template',
519
- 'xotf' => 'application/vnd.oasis.opendocument.formula-template',
520
- 'xotg' => 'application/vnd.oasis.opendocument.graphics-template',
521
-
522
- 'xoth' => 'application/vnd.oasis.opendocument.text-web',
523
- 'xoti' => 'application/vnd.oasis.opendocument.image-template',
524
- 'xotm' => 'application/vnd.oasis.opendocument.text-master',
525
- 'xots' => 'application/vnd.oasis.opendocument.spreadsheet-template',
526
- 'xott' => 'application/vnd.oasis.opendocument.text-template',
527
- 'xoxt' => 'application/vnd.openofficeorg.extension',
528
- 'xp10' => 'application/pkcs10',
529
- 'xp7r' => 'application/x-pkcs7-certreqresp',
530
- 'xp7s' => 'application/pkcs7-signature',
531
-
532
- 'xpbd' => 'application/vnd.powerbuilder6',
533
- 'xpbm' => 'image/x-portable-bitmap',
534
- 'xpcl' => 'application/vnd.hp-pcl',
535
- 'xpclxl' => 'application/vnd.hp-pclxl',
536
- 'xpct' => 'image/x-pict',
537
- 'xpcx' => 'image/x-pcx',
538
- 'xpdb' => 'chemical/x-pdb',
539
- 'xpdf' => 'application/pdf',
540
- 'xpfr' => 'application/font-tdpfr',
541
-
542
- 'xpgm' => 'image/x-portable-graymap',
543
- 'xpgn' => 'application/x-chess-pgn',
544
- 'xpgp' => 'application/pgp-encrypted',
545
- 'xpic' => 'image/x-pict',
546
- 'xpki' => 'application/pkixcmp',
547
- 'xpkipath' => 'application/pkix-pkipath',
548
- 'xplb' => 'application/vnd.3gpp.pic-bw-large',
549
- 'xplc' => 'application/vnd.mobius.plc',
550
- 'xplf' => 'application/vnd.pocketlearn',
551
-
552
- 'xpls' => 'application/pls+xml',
553
- 'xpml' => 'application/vnd.ctc-posml',
554
- 'xpng' => 'image/png',
555
- 'xpnm' => 'image/x-portable-anymap',
556
- 'xportpkg' => 'application/vnd.macports.portpkg',
557
- 'xpot' => 'application/vnd.ms-powerpoint',
558
- 'xppd' => 'application/vnd.cups-ppd',
559
- 'xppm' => 'image/x-portable-pixmap',
560
- 'xpps' => 'application/vnd.ms-powerpoint',
561
-
562
- 'xppt' => 'application/vnd.ms-powerpoint',
563
- 'xpqa' => 'application/vnd.palm',
564
- 'xprc' => 'application/vnd.palm',
565
- 'xpre' => 'application/vnd.lotus-freelance',
566
- 'xprf' => 'application/pics-rules',
567
- 'xps' => 'application/postscript',
568
- 'xpsb' => 'application/vnd.3gpp.pic-bw-small',
569
- 'xpsd' => 'image/vnd.adobe.photoshop',
570
- 'xptid' => 'application/vnd.pvi.ptid1',
571
-
572
- 'xpub' => 'application/x-mspublisher',
573
- 'xpvb' => 'application/vnd.3gpp.pic-bw-var',
574
- 'xpwn' => 'application/vnd.3m.post-it-notes',
575
- 'xqam' => 'application/vnd.epson.quickanime',
576
- 'xqbo' => 'application/vnd.intu.qbo',
577
- 'xqfx' => 'application/vnd.intu.qfx',
578
- 'xqps' => 'application/vnd.publishare-delta-tree',
579
- 'xqt' => 'video/quicktime',
580
- 'xra' => 'audio/x-pn-realaudio',
581
-
582
- 'xram' => 'audio/x-pn-realaudio',
583
- 'xrar' => 'application/x-rar-compressed',
584
- 'xras' => 'image/x-cmu-raster',
585
- 'xrcprofile' => 'application/vnd.ipunplugged.rcprofile',
586
- 'xrdf' => 'application/rdf+xml',
587
- 'xrdz' => 'application/vnd.data-vision.rdz',
588
- 'xrep' => 'application/vnd.businessobjects',
589
- 'xrgb' => 'image/x-rgb',
590
- 'xrif' => 'application/reginfo+xml',
591
-
592
- 'xrl' => 'application/resource-lists+xml',
593
- 'xrlc' => 'image/vnd.fujixerox.edmics-rlc',
594
- 'xrm' => 'application/vnd.rn-realmedia',
595
- 'xrmi' => 'audio/midi',
596
- 'xrmp' => 'audio/x-pn-realaudio-plugin',
597
- 'xrms' => 'application/vnd.jcp.javame.midlet-rms',
598
- 'xrnc' => 'application/relax-ng-compact-syntax',
599
- 'xrpss' => 'application/vnd.nokia.radio-presets',
600
- 'xrpst' => 'application/vnd.nokia.radio-preset',
601
-
602
- 'xrq' => 'application/sparql-query',
603
- 'xrs' => 'application/rls-services+xml',
604
- 'xrsd' => 'application/rsd+xml',
605
- 'xrss' => 'application/rss+xml',
606
- 'xrtf' => 'application/rtf',
607
- 'xrtx' => 'text/richtext',
608
- 'xsaf' => 'application/vnd.yamaha.smaf-audio',
609
- 'xsbml' => 'application/sbml+xml',
610
- 'xsc' => 'application/vnd.ibm.secure-container',
611
-
612
- 'xscd' => 'application/x-msschedule',
613
- 'xscm' => 'application/vnd.lotus-screencam',
614
- 'xscq' => 'application/scvp-cv-request',
615
- 'xscs' => 'application/scvp-cv-response',
616
- 'xsdp' => 'application/sdp',
617
- 'xsee' => 'application/vnd.seemail',
618
- 'xsema' => 'application/vnd.sema',
619
- 'xsemd' => 'application/vnd.semd',
620
- 'xsemf' => 'application/vnd.semf',
621
-
622
- 'xsetpay' => 'application/set-payment-initiation',
623
- 'xsetreg' => 'application/set-registration-initiation',
624
- 'xsfs' => 'application/vnd.spotfire.sfs',
625
- 'xsgm' => 'text/sgml',
626
- 'xsgml' => 'text/sgml',
627
- 'xsh' => 'application/x-sh',
628
- 'xshar' => 'application/x-shar',
629
- 'xshf' => 'application/shf+xml',
630
- 'xsilo' => 'model/mesh',
631
-
632
- 'xsit' => 'application/x-stuffit',
633
- 'xsitx' => 'application/x-stuffitx',
634
- 'xslt' => 'application/vnd.epson.salt',
635
- 'xsnd' => 'audio/basic',
636
- 'xspf' => 'application/vnd.yamaha.smaf-phrase',
637
- 'xspl' => 'application/x-futuresplash',
638
- 'xspot' => 'text/vnd.in3d.spot',
639
- 'xspp' => 'application/scvp-vp-response',
640
- 'xspq' => 'application/scvp-vp-request',
641
-
642
- 'xsrc' => 'application/x-wais-source',
643
- 'xsrx' => 'application/sparql-results+xml',
644
- 'xssf' => 'application/vnd.epson.ssf',
645
- 'xssml' => 'application/ssml+xml',
646
- 'xstf' => 'application/vnd.wt.stf',
647
- 'xstk' => 'application/hyperstudio',
648
- 'xstr' => 'application/vnd.pg.format',
649
- 'xsus' => 'application/vnd.sus-calendar',
650
- 'xsusp' => 'application/vnd.sus-calendar',
651
-
652
- 'xsv4cpio' => 'application/x-sv4cpio',
653
- 'xsv4crc' => 'application/x-sv4crc',
654
- 'xsvd' => 'application/vnd.svd',
655
- 'xswf' => 'application/x-shockwave-flash',
656
- 'xtao' => 'application/vnd.tao.intent-module-archive',
657
- 'xtar' => 'application/x-tar',
658
- 'xtcap' => 'application/vnd.3gpp2.tcap',
659
- 'xtcl' => 'application/x-tcl',
660
- 'xtex' => 'application/x-tex',
661
-
662
- 'xtext' => 'text/plain',
663
- 'xtif' => 'image/tiff',
664
- 'xtiff' => 'image/tiff',
665
- 'xtmo' => 'application/vnd.tmobile-livetv',
666
- 'xtorrent' => 'application/x-bittorrent',
667
- 'xtpl' => 'application/vnd.groove-tool-template',
668
- 'xtpt' => 'application/vnd.trid.tpt',
669
- 'xtra' => 'application/vnd.trueapp',
670
- 'xtrm' => 'application/x-msterminal',
671
-
672
- 'xtsv' => 'text/tab-separated-values',
673
- 'xtxd' => 'application/vnd.genomatix.tuxedo',
674
- 'xtxf' => 'application/vnd.mobius.txf',
675
- 'xtxt' => 'text/plain',
676
- 'xumj' => 'application/vnd.umajin',
677
- 'xunityweb' => 'application/vnd.unity',
678
- 'xuoml' => 'application/vnd.uoml+xml',
679
- 'xuri' => 'text/uri-list',
680
- 'xuris' => 'text/uri-list',
681
-
682
- 'xurls' => 'text/uri-list',
683
- 'xustar' => 'application/x-ustar',
684
- 'xutz' => 'application/vnd.uiq.theme',
685
- 'xuu' => 'text/x-uuencode',
686
- 'xvcd' => 'application/x-cdlink',
687
- 'xvcf' => 'text/x-vcard',
688
- 'xvcg' => 'application/vnd.groove-vcard',
689
- 'xvcs' => 'text/x-vcalendar',
690
- 'xvcx' => 'application/vnd.vcx',
691
-
692
- 'xvis' => 'application/vnd.visionary',
693
- 'xviv' => 'video/vnd.vivo',
694
- 'xvrml' => 'model/vrml',
695
- 'xvsd' => 'application/vnd.visio',
696
- 'xvsf' => 'application/vnd.vsf',
697
- 'xvss' => 'application/vnd.visio',
698
- 'xvst' => 'application/vnd.visio',
699
- 'xvsw' => 'application/vnd.visio',
700
- 'xvtu' => 'model/vnd.vtu',
701
-
702
- 'xvxml' => 'application/voicexml+xml',
703
- 'xwav' => 'audio/x-wav',
704
- 'xwax' => 'audio/x-ms-wax',
705
- 'xwbmp' => 'image/vnd.wap.wbmp',
706
- 'xwbs' => 'application/vnd.criticaltools.wbs+xml',
707
- 'xwbxml' => 'application/vnd.wap.wbxml',
708
- 'xwcm' => 'application/vnd.ms-works',
709
- 'xwdb' => 'application/vnd.ms-works',
710
- 'xwks' => 'application/vnd.ms-works',
711
-
712
- 'xwm' => 'video/x-ms-wm',
713
- 'xwma' => 'audio/x-ms-wma',
714
- 'xwmd' => 'application/x-ms-wmd',
715
- 'xwmf' => 'application/x-msmetafile',
716
- 'xwml' => 'text/vnd.wap.wml',
717
- 'xwmlc' => 'application/vnd.wap.wmlc',
718
- 'xwmls' => 'text/vnd.wap.wmlscript',
719
- 'xwmlsc' => 'application/vnd.wap.wmlscriptc',
720
- 'xwmv' => 'video/x-ms-wmv',
721
-
722
- 'xwmx' => 'video/x-ms-wmx',
723
- 'xwmz' => 'application/x-ms-wmz',
724
- 'xwpd' => 'application/vnd.wordperfect',
725
- 'xwpl' => 'application/vnd.ms-wpl',
726
- 'xwps' => 'application/vnd.ms-works',
727
- 'xwqd' => 'application/vnd.wqd',
728
- 'xwri' => 'application/x-mswrite',
729
- 'xwrl' => 'model/vrml',
730
- 'xwsdl' => 'application/wsdl+xml',
731
-
732
- 'xwspolicy' => 'application/wspolicy+xml',
733
- 'xwtb' => 'application/vnd.webturbo',
734
- 'xwvx' => 'video/x-ms-wvx',
735
- 'xx3d' => 'application/vnd.hzn-3d-crossword',
736
- 'xxar' => 'application/vnd.xara',
737
- 'xxbd' => 'application/vnd.fujixerox.docuworks.binder',
738
- 'xxbm' => 'image/x-xbitmap',
739
- 'xxdm' => 'application/vnd.syncml.dm+xml',
740
- 'xxdp' => 'application/vnd.adobe.xdp+xml',
741
-
742
- 'xxdw' => 'application/vnd.fujixerox.docuworks',
743
- 'xxenc' => 'application/xenc+xml',
744
- 'xxfdf' => 'application/vnd.adobe.xfdf',
745
- 'xxfdl' => 'application/vnd.xfdl',
746
- 'xxht' => 'application/xhtml+xml',
747
- 'xxhtml' => 'application/xhtml+xml',
748
- 'xxhvml' => 'application/xv+xml',
749
- 'xxif' => 'image/vnd.xiff',
750
- 'xxla' => 'application/vnd.ms-excel',
751
-
752
- 'xxlc' => 'application/vnd.ms-excel',
753
- 'xxlm' => 'application/vnd.ms-excel',
754
- 'xxls' => 'application/vnd.ms-excel',
755
- 'xxlt' => 'application/vnd.ms-excel',
756
- 'xxlw' => 'application/vnd.ms-excel',
757
- 'xxml' => 'application/xml',
758
- 'xxo' => 'application/vnd.olpc-sugar',
759
- 'xxop' => 'application/xop+xml',
760
- 'xxpm' => 'image/x-xpixmap',
761
-
762
- 'xxpr' => 'application/vnd.is-xpr',
763
- 'xxps' => 'application/vnd.ms-xpsdocument',
764
- 'xxsl' => 'application/xml',
765
- 'xxslt' => 'application/xslt+xml',
766
- 'xxsm' => 'application/vnd.syncml+xml',
767
- 'xxspf' => 'application/xspf+xml',
768
- 'xxul' => 'application/vnd.mozilla.xul+xml',
769
- 'xxvm' => 'application/xv+xml',
770
- 'xxvml' => 'application/xv+xml',
771
-
772
- 'xxwd' => 'image/x-xwindowdump',
773
- 'xxyz' => 'chemical/x-xyz',
774
- 'xzaz' => 'application/vnd.zzazz.deck+xml',
775
- 'xzip' => 'application/zip',
776
- 'xzmm' => 'application/vnd.handheld-entertainment+xml',
777
- 'xodt' => 'application/x-vnd.oasis.opendocument.spreadsheet'
778
- );
779
  }
33
  */
34
  class Mage_Downloadable_Helper_File extends Mage_Core_Helper_Abstract
35
  {
36
+ /**
37
+ * @see Mage_Uploader_Helper_File::getMimeTypes
38
+ * @var array
39
+ */
40
+ protected $_mimeTypes;
41
+
42
+ /**
43
+ * @var Mage_Uploader_Helper_File
44
+ */
45
+ protected $_fileHelper;
46
+
47
+ /**
48
+ * Populate self::_mimeTypes array with values that set in config or pre-defined
49
+ */
50
  public function __construct()
51
  {
52
+ $this->_mimeTypes = $this->_getFileHelper()->getMimeTypes();
53
+ }
54
+
55
+ /**
56
+ * @return Mage_Uploader_Helper_File
57
+ */
58
+ protected function _getFileHelper()
59
+ {
60
+ if (!$this->_fileHelper) {
61
+ $this->_fileHelper = Mage::helper('uploader/file');
62
  }
63
+
64
+ return $this->_fileHelper;
65
  }
66
 
67
  /**
172
  return $file;
173
  }
174
 
175
+ /**
176
+ * Get MIME type for $filePath
177
+ *
178
+ * @param $filePath
179
+ * @return string
180
+ */
181
  public function getFileType($filePath)
182
  {
183
  $ext = substr($filePath, strrpos($filePath, '.')+1);
184
  return $this->_getFileTypeByExt($ext);
185
  }
186
 
187
+ /**
188
+ * Get MIME type by file extension
189
+ *
190
+ * @param $ext
191
+ * @return string
192
+ * @deprecated
193
+ */
194
  protected function _getFileTypeByExt($ext)
195
  {
196
+ return $this->_getFileHelper()->getMimeTypeByExtension($ext);
 
 
 
 
197
  }
198
 
199
+ /**
200
+ * Get all MIME types
201
+ *
202
+ * @return array
203
+ */
204
  public function getAllFileTypes()
205
  {
206
+ return array_values($this->getAllMineTypes());
207
  }
208
 
209
+ /**
210
+ * Get list of all MIME types
211
+ *
212
+ * @return array
213
+ */
214
  public function getAllMineTypes()
215
  {
216
+ return $this->_mimeTypes;
217
  }
218
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
219
  }
app/code/core/Mage/Eav/Block/Adminhtml/Attribute/Edit/Options/Abstract.php CHANGED
@@ -170,12 +170,11 @@ abstract class Mage_Eav_Block_Adminhtml_Attribute_Edit_Options_Abstract extends
170
  public function getLabelValues()
171
  {
172
  $values = array();
173
- $values[0] = $this->getAttributeObject()->getFrontend()->getLabel();
174
- // it can be array and cause bug
175
  $frontendLabel = $this->getAttributeObject()->getFrontend()->getLabel();
176
  if (is_array($frontendLabel)) {
177
- $frontendLabel = array_shift($frontendLabel);
178
  }
 
179
  $storeLabels = $this->getAttributeObject()->getStoreLabels();
180
  foreach ($this->getStores() as $store) {
181
  if ($store->getId() != 0) {
170
  public function getLabelValues()
171
  {
172
  $values = array();
 
 
173
  $frontendLabel = $this->getAttributeObject()->getFrontend()->getLabel();
174
  if (is_array($frontendLabel)) {
175
+ return $frontendLabel;
176
  }
177
+ $values[0] = $frontendLabel;
178
  $storeLabels = $this->getAttributeObject()->getStoreLabels();
179
  foreach ($this->getStores() as $store) {
180
  if ($store->getId() != 0) {
app/code/core/Mage/Eav/Model/Entity/Abstract.php CHANGED
@@ -808,13 +808,13 @@ abstract class Mage_Eav_Model_Entity_Abstract extends Mage_Core_Model_Resource_A
808
  *
809
  * @see Mage_Eav_Model_Entity_Abstract::getAttribute for $attribute format
810
  * @param integer|string|Mage_Eav_Model_Entity_Attribute_Abstract $attribute
 
811
  * @return boolean
812
  */
813
  public function isAttributeStatic($attribute)
814
  {
815
- $attrInstance = $this->getAttribute($attribute);
816
- $attrBackendStatic = $attrInstance->getBackend()->isStatic();
817
- return $attrInstance && $attrBackendStatic;
818
  }
819
 
820
  /**
@@ -1309,9 +1309,8 @@ abstract class Mage_Eav_Model_Entity_Abstract extends Mage_Core_Model_Resource_A
1309
  $this->_attributeValuesToSave = array();
1310
  $this->_attributeValuesToDelete = array();
1311
 
1312
- extract($saveData);
1313
  /**
1314
- * Import variables into the current symbol table from save data array
1315
  *
1316
  * @see Mage_Eav_Model_Entity_Attribute_Abstract::_collectSaveData()
1317
  *
@@ -1321,6 +1320,12 @@ abstract class Mage_Eav_Model_Entity_Abstract extends Mage_Core_Model_Resource_A
1321
  * @var array $update
1322
  * @var array $delete
1323
  */
 
 
 
 
 
 
1324
  $adapter = $this->_getWriteAdapter();
1325
  $insertEntity = true;
1326
  $entityTable = $this->getEntityTable();
808
  *
809
  * @see Mage_Eav_Model_Entity_Abstract::getAttribute for $attribute format
810
  * @param integer|string|Mage_Eav_Model_Entity_Attribute_Abstract $attribute
811
+ *
812
  * @return boolean
813
  */
814
  public function isAttributeStatic($attribute)
815
  {
816
+ $attrInstance = $this->getAttribute($attribute);
817
+ return $attrInstance && $attrInstance->getBackend()->isStatic();
 
818
  }
819
 
820
  /**
1309
  $this->_attributeValuesToSave = array();
1310
  $this->_attributeValuesToDelete = array();
1311
 
 
1312
  /**
1313
+ * Import variables from save data array
1314
  *
1315
  * @see Mage_Eav_Model_Entity_Attribute_Abstract::_collectSaveData()
1316
  *
1320
  * @var array $update
1321
  * @var array $delete
1322
  */
1323
+ $newObject = $saveData['newObject'];
1324
+ $entityRow = $saveData['entityRow'];
1325
+ $insert = $saveData['insert'];
1326
+ $update = $saveData['update'];
1327
+ $delete = $saveData['delete'];
1328
+
1329
  $adapter = $this->_getWriteAdapter();
1330
  $insertEntity = true;
1331
  $entityTable = $this->getEntityTable();
app/code/core/Mage/Eav/Model/Entity/Attribute.php CHANGED
@@ -225,12 +225,12 @@ class Mage_Eav_Model_Entity_Attribute extends Mage_Eav_Model_Entity_Attribute_Ab
225
  case 'text':
226
  case 'gallery':
227
  case 'media_image':
228
- case 'multiselect':
229
  $field = 'varchar';
230
  break;
231
 
232
  case 'image':
233
  case 'textarea':
 
234
  $field = 'text';
235
  break;
236
 
@@ -272,6 +272,7 @@ class Mage_Eav_Model_Entity_Attribute extends Mage_Eav_Model_Entity_Attribute_Ab
272
  case 'text':
273
  case 'price':
274
  case 'image':
 
275
  $field = 'default_value_text';
276
  break;
277
 
225
  case 'text':
226
  case 'gallery':
227
  case 'media_image':
 
228
  $field = 'varchar';
229
  break;
230
 
231
  case 'image':
232
  case 'textarea':
233
+ case 'multiselect':
234
  $field = 'text';
235
  break;
236
 
272
  case 'text':
273
  case 'price':
274
  case 'image':
275
+ case 'weight':
276
  $field = 'default_value_text';
277
  break;
278
 
app/code/core/Mage/Eav/Model/Entity/Attribute/Abstract.php CHANGED
@@ -383,7 +383,10 @@ abstract class Mage_Eav_Model_Entity_Attribute_Abstract extends Mage_Core_Model_
383
  $source = Mage::getModel($this->getSourceModel());
384
  if (!$source) {
385
  throw Mage::exception('Mage_Eav',
386
- Mage::helper('eav')->__('Source model "%s" not found for attribute "%s"',$this->getSourceModel(), $this->getAttributeCode())
 
 
 
387
  );
388
  }
389
  $this->_source = $source->setAttribute($this);
@@ -628,8 +631,14 @@ abstract class Mage_Eav_Model_Entity_Attribute_Abstract extends Mage_Core_Model_
628
  break;
629
  }
630
  $prop = $describe[$this->getAttributeCode()];
 
 
 
 
 
 
631
  $columns[$this->getAttributeCode()] = array(
632
- 'type' => $prop['DATA_TYPE'] . ($prop['LENGTH'] ? "({$prop['LENGTH']})" : ""),
633
  'unsigned' => $prop['UNSIGNED'] ? true: false,
634
  'is_null' => $prop['NULLABLE'],
635
  'default' => $prop['DEFAULT'],
383
  $source = Mage::getModel($this->getSourceModel());
384
  if (!$source) {
385
  throw Mage::exception('Mage_Eav',
386
+ Mage::helper('eav')->__('Source model "%s" not found for attribute "%s"',
387
+ $this->getSourceModel(),
388
+ $this->getAttributeCode()
389
+ )
390
  );
391
  }
392
  $this->_source = $source->setAttribute($this);
631
  break;
632
  }
633
  $prop = $describe[$this->getAttributeCode()];
634
+ $type = $prop['DATA_TYPE'];
635
+ if (isset($prop['PRECISION']) && isset($prop['SCALE'])) {
636
+ $type .= "({$prop['PRECISION']},{$prop['SCALE']})";
637
+ } else {
638
+ $type .= (isset($prop['LENGTH']) && $prop['LENGTH']) ? "({$prop['LENGTH']})" : "";
639
+ }
640
  $columns[$this->getAttributeCode()] = array(
641
+ 'type' => $type,
642
  'unsigned' => $prop['UNSIGNED'] ? true: false,
643
  'is_null' => $prop['NULLABLE'],
644
  'default' => $prop['DEFAULT'],
app/code/core/Mage/Eav/Model/Entity/Attribute/Source/Table.php CHANGED
@@ -152,7 +152,7 @@ class Mage_Eav_Model_Entity_Attribute_Source_Table extends Mage_Eav_Model_Entity
152
 
153
  if (Mage::helper('core')->useDbCompatibleMode()) {
154
  $columns[$attributeCode] = array(
155
- 'type' => $isMulti ? 'varchar(255)' : 'int',
156
  'unsigned' => false,
157
  'is_null' => true,
158
  'default' => null,
@@ -171,7 +171,7 @@ class Mage_Eav_Model_Entity_Attribute_Source_Table extends Mage_Eav_Model_Entity
171
  $type = ($isMulti) ? Varien_Db_Ddl_Table::TYPE_TEXT : Varien_Db_Ddl_Table::TYPE_INTEGER;
172
  $columns[$attributeCode] = array(
173
  'type' => $type,
174
- 'length' => $isMulti ? '255' : null,
175
  'unsigned' => false,
176
  'nullable' => true,
177
  'default' => null,
152
 
153
  if (Mage::helper('core')->useDbCompatibleMode()) {
154
  $columns[$attributeCode] = array(
155
+ 'type' => $isMulti ? 'text' : 'int',
156
  'unsigned' => false,
157
  'is_null' => true,
158
  'default' => null,
171
  $type = ($isMulti) ? Varien_Db_Ddl_Table::TYPE_TEXT : Varien_Db_Ddl_Table::TYPE_INTEGER;
172
  $columns[$attributeCode] = array(
173
  'type' => $type,
174
+ 'length' => $isMulti ? '65535' : null,
175
  'unsigned' => false,
176
  'nullable' => true,
177
  'default' => null,
app/code/core/Mage/Eav/Model/Entity/Collection/Abstract.php CHANGED
@@ -918,6 +918,7 @@ abstract class Mage_Eav_Model_Entity_Collection_Abstract extends Varien_Data_Col
918
  /**
919
  * Retrive all ids sql
920
  *
 
921
  * @return array
922
  */
923
  public function getAllIdsSql()
@@ -1409,6 +1410,7 @@ abstract class Mage_Eav_Model_Entity_Collection_Abstract extends Varien_Data_Col
1409
  foreach ($attribute as $attr) {
1410
  parent::setOrder($attr, $dir);
1411
  }
 
1412
  }
1413
  return parent::setOrder($attribute, $dir);
1414
  }
918
  /**
919
  * Retrive all ids sql
920
  *
921
+ * @deprecated
922
  * @return array
923
  */
924
  public function getAllIdsSql()
1410
  foreach ($attribute as $attr) {
1411
  parent::setOrder($attr, $dir);
1412
  }
1413
+ return $this;
1414
  }
1415
  return parent::setOrder($attribute, $dir);
1416
  }
app/code/core/Mage/ImportExport/Helper/Data.php CHANGED
@@ -36,8 +36,9 @@ class Mage_ImportExport_Helper_Data extends Mage_Core_Helper_Data
36
  /**
37
  * XML path for config data
38
  */
39
- const XML_PATH_EXPORT_LOCAL_VALID_PATH = 'general/file/importexport_local_valid_paths';
40
- const XML_PATH_BUNCH_SIZE = 'general/file/bunch_size';
 
41
 
42
  /**
43
  * Maximum size of uploaded files.
@@ -69,4 +70,14 @@ class Mage_ImportExport_Helper_Data extends Mage_Core_Helper_Data
69
  {
70
  return (int)Mage::getStoreConfig(self::XML_PATH_BUNCH_SIZE);
71
  }
 
 
 
 
 
 
 
 
 
 
72
  }
36
  /**
37
  * XML path for config data
38
  */
39
+ const XML_PATH_EXPORT_LOCAL_VALID_PATH = 'general/file/importexport_local_valid_paths';
40
+ const XML_PATH_BUNCH_SIZE = 'general/file/bunch_size';
41
+ const XML_PATH_IMPORT_CONFIGURABLE_PAGE_SIZE = 'system/import_csv/configurable_page_size';
42
 
43
  /**
44
  * Maximum size of uploaded files.
70
  {
71
  return (int)Mage::getStoreConfig(self::XML_PATH_BUNCH_SIZE);
72
  }
73
+
74
+ /**
75
+ * Get page size for import configurable products
76
+ *
77
+ * @return int
78
+ */
79
+ public function getImportConfigurablePageSize()
80
+ {
81
+ return (int)Mage::getStoreConfig(self::XML_PATH_IMPORT_CONFIGURABLE_PAGE_SIZE);
82
+ }
83
  }
app/code/core/Mage/ImportExport/Model/Export.php CHANGED
@@ -136,7 +136,9 @@ class Mage_ImportExport_Model_Export extends Mage_ImportExport_Model_Abstract
136
  }
137
 
138
  /**
139
- * Export data.
 
 
140
  *
141
  * @throws Mage_Core_Exception
142
  * @return string
@@ -168,6 +170,50 @@ class Mage_ImportExport_Model_Export extends Mage_ImportExport_Model_Abstract
168
  }
169
  }
170
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
  /**
172
  * Clean up already loaded attribute collection.
173
  *
136
  }
137
 
138
  /**
139
+ * Export data and return contents of temporary file.
140
+ *
141
+ * @deprecated after ver 1.9.2.4 use $this->exportFile() instead
142
  *
143
  * @throws Mage_Core_Exception
144
  * @return string
170
  }
171
  }
172
 
173
+ /**
174
+ * Export data and return temporary file through array.
175
+ *
176
+ * This method will return following array:
177
+ *
178
+ * array(
179
+ * 'rows' => count of written rows,
180
+ * 'value' => path to created file,
181
+ * 'type' => 'file'
182
+ * )
183
+ *
184
+ * @throws Mage_Core_Exception
185
+ * @return array
186
+ */
187
+ public function exportFile()
188
+ {
189
+ if (isset($this->_data[self::FILTER_ELEMENT_GROUP])) {
190
+ $this->addLogComment(Mage::helper('importexport')->__('Begin export of %s', $this->getEntity()));
191
+ $result = $this->_getEntityAdapter()
192
+ ->setWriter($this->_getWriter())
193
+ ->exportFile();
194
+
195
+ if (isset($result['rows'])) {
196
+ if (!$result['rows']) {
197
+ Mage::throwException(
198
+ Mage::helper('importexport')->__('There is no data for export')
199
+ );
200
+ }
201
+ if ($result['rows']) {
202
+ $this->addLogComment(array(
203
+ Mage::helper('importexport')->__('Exported %s rows.', $result['rows']),
204
+ Mage::helper('importexport')->__('Export has been done.')
205
+ ));
206
+ }
207
+ }
208
+
209
+ return $result;
210
+ } else {
211
+ Mage::throwException(
212
+ Mage::helper('importexport')->__('No filter data provided')
213
+ );
214
+ }
215
+ }
216
+
217
  /**
218
  * Clean up already loaded attribute collection.
219
  *
app/code/core/Mage/ImportExport/Model/Export/Adapter/Abstract.php CHANGED
@@ -47,6 +47,13 @@ abstract class Mage_ImportExport_Model_Export_Adapter_Abstract
47
  */
48
  protected $_headerCols = null;
49
 
 
 
 
 
 
 
 
50
  /**
51
  * Adapter object constructor.
52
  *
@@ -124,6 +131,16 @@ abstract class Mage_ImportExport_Model_Export_Adapter_Abstract
124
  return '';
125
  }
126
 
 
 
 
 
 
 
 
 
 
 
127
  /**
128
  * Set column names.
129
  *
47
  */
48
  protected $_headerCols = null;
49
 
50
+ /**
51
+ * Count of rows
52
+ *
53
+ * @var int
54
+ */
55
+ protected $_rowsCount = 0;
56
+
57
  /**
58
  * Adapter object constructor.
59
  *
131
  return '';
132
  }
133
 
134
+ /**
135
+ * Get count of wrote lines
136
+ *
137
+ * @return int
138
+ */
139
+ public function getRowsCount()
140
+ {
141
+ return $this->_rowsCount;
142
+ }
143
+
144
  /**
145
  * Set column names.
146
  *
app/code/core/Mage/ImportExport/Model/Export/Adapter/Csv.php CHANGED
@@ -125,6 +125,8 @@ class Mage_ImportExport_Model_Export_Adapter_Csv extends Mage_ImportExport_Model
125
  $this->_enclosure
126
  );
127
 
 
 
128
  return $this;
129
  }
130
 
125
  $this->_enclosure
126
  );
127
 
128
+ $this->_rowsCount++;
129
+
130
  return $this;
131
  }
132
 
app/code/core/Mage/ImportExport/Model/Export/Entity/Abstract.php CHANGED
@@ -153,6 +153,27 @@ abstract class Mage_ImportExport_Model_Export_Entity_Abstract
153
  */
154
  protected $_writer;
155
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
  /**
157
  * Constructor.
158
  *
@@ -165,6 +186,20 @@ abstract class Mage_ImportExport_Model_Export_Entity_Abstract
165
  $this->_connection = Mage::getSingleton('core/resource')->getConnection('write');
166
  }
167
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
  /**
169
  * Initialize stores hash.
170
  *
@@ -173,9 +208,11 @@ abstract class Mage_ImportExport_Model_Export_Entity_Abstract
173
  protected function _initStores()
174
  {
175
  foreach (Mage::app()->getStores(true) as $store) {
176
- $this->_storeIdToCode[$store->getId()] = $store->getCode();
 
177
  }
178
  ksort($this->_storeIdToCode); // to ensure that 'admin' store (ID is zero) goes first
 
179
 
180
  return $this;
181
  }
@@ -319,10 +356,28 @@ abstract class Mage_ImportExport_Model_Export_Entity_Abstract
319
  /**
320
  * Export process.
321
  *
 
 
322
  * @return string
323
  */
324
  abstract public function export();
325
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
326
  /**
327
  * Clean up attribute collection.
328
  *
@@ -367,7 +422,8 @@ abstract class Mage_ImportExport_Model_Export_Entity_Abstract
367
 
368
  try {
369
  foreach ($attribute->getSource()->getAllOptions(false) as $option) {
370
- foreach (is_array($option['value']) ? $option['value'] : array($option) as $innerOption) {
 
371
  if (strlen($innerOption['value'])) { // skip ' -- Please Select -- ' option
372
  $options[$innerOption['value']] = $innerOption[$index];
373
  }
153
  */
154
  protected $_writer;
155
 
156
+ /**
157
+ * Array of pairs store ID to its code.
158
+ *
159
+ * @var array
160
+ */
161
+ protected $_storeIdToCode = array();
162
+
163
+ /**
164
+ * Store Id-to-website
165
+ *
166
+ * @var array
167
+ */
168
+ protected $_storeIdToWebsiteId = array();
169
+
170
+ /**
171
+ * Website ID-to-code.
172
+ *
173
+ * @var array
174
+ */
175
+ protected $_websiteIdToCode = array();
176
+
177
  /**
178
  * Constructor.
179
  *
186
  $this->_connection = Mage::getSingleton('core/resource')->getConnection('write');
187
  }
188
 
189
+ /**
190
+ * Initialize website values.
191
+ *
192
+ * @return Mage_ImportExport_Model_Export_Entity_Customer
193
+ */
194
+ protected function _initWebsites()
195
+ {
196
+ /** @var $website Mage_Core_Model_Website */
197
+ foreach (Mage::app()->getWebsites(true) as $website) {
198
+ $this->_websiteIdToCode[$website->getId()] = $website->getCode();
199
+ }
200
+ return $this;
201
+ }
202
+
203
  /**
204
  * Initialize stores hash.
205
  *
208
  protected function _initStores()
209
  {
210
  foreach (Mage::app()->getStores(true) as $store) {
211
+ $this->_storeIdToCode[$store->getId()] = $store->getCode();
212
+ $this->_storeIdToWebsiteId[$store->getId()] = $store->getWebsiteId();
213
  }
214
  ksort($this->_storeIdToCode); // to ensure that 'admin' store (ID is zero) goes first
215
+ sort($this->_storeIdToWebsiteId);
216
 
217
  return $this;
218
  }
356
  /**
357
  * Export process.
358
  *
359
+ * @deprecated after ver 1.9.2.4 use $this->exportFile() instead
360
+ *
361
  * @return string
362
  */
363
  abstract public function export();
364
 
365
+ /**
366
+ * Export data and return temporary file through array.
367
+ *
368
+ * This method will return following array:
369
+ *
370
+ * array(
371
+ * 'rows' => count of written rows,
372
+ * 'value' => path to created file,
373
+ * 'type' => 'file'
374
+ * )
375
+ *
376
+ * @throws Mage_Core_Exception
377
+ * @return array
378
+ */
379
+ abstract function exportFile();
380
+
381
  /**
382
  * Clean up attribute collection.
383
  *
422
 
423
  try {
424
  foreach ($attribute->getSource()->getAllOptions(false) as $option) {
425
+ $innerOptions = is_array($option['value']) ? $option['value'] : array($option);
426
+ foreach ($innerOptions as $innerOption) {
427
  if (strlen($innerOption['value'])) { // skip ' -- Please Select -- ' option
428
  $options[$innerOption['value']] = $innerOption[$index];
429
  }
app/code/core/Mage/ImportExport/Model/Export/Entity/Customer.php CHANGED
@@ -75,20 +75,6 @@ class Mage_ImportExport_Model_Export_Entity_Customer extends Mage_ImportExport_M
75
  */
76
  protected $_permanentAttributes = array(self::COL_EMAIL, self::COL_WEBSITE, self::COL_STORE);
77
 
78
- /**
79
- * Array of pairs store ID to its code.
80
- *
81
- * @var array
82
- */
83
- protected $_storeIdToCode = array();
84
-
85
- /**
86
- * Website ID-to-code.
87
- *
88
- * @var array
89
- */
90
- protected $_websiteIdToCode = array();
91
-
92
  /**
93
  * Constructor.
94
  *
@@ -132,51 +118,97 @@ class Mage_ImportExport_Model_Export_Entity_Customer extends Mage_ImportExport_M
132
  }
133
 
134
  /**
135
- * Export process.
 
 
136
  *
137
  * @return string
138
  */
139
  public function export()
140
  {
141
- $collection = $this->_prepareEntityCollection(Mage::getResourceModel('customer/customer_collection'));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
  $validAttrCodes = $this->_getExportAttrCodes();
143
  $writer = $this->getWriter();
144
  $defaultAddrMap = Mage_ImportExport_Model_Import_Entity_Customer_Address::getDefaultAddressAttrMapping();
145
 
146
  // prepare address data
147
- $addrAttributes = array();
148
- $addrColNames = array();
149
- $customerAddrs = array();
150
-
151
- foreach (Mage::getResourceModel('customer/address_attribute_collection')
152
- ->addSystemHiddenFilter()
153
- ->addExcludeHiddenFrontendFilter() as $attribute) {
154
- $options = array();
155
- $attrCode = $attribute->getAttributeCode();
156
 
157
- if ($attribute->usesSource() && 'country_id' != $attrCode) {
158
- foreach ($attribute->getSource()->getAllOptions(false) as $option) {
159
- foreach (is_array($option['value']) ? $option['value'] : array($option) as $innerOption) {
160
- if (strlen($innerOption['value'])) { // skip ' -- Please Select -- ' option
161
- $options[$innerOption['value']] = $innerOption['label'];
162
- }
163
- }
164
- }
165
- }
166
- $addrAttributes[$attrCode] = $options;
167
  $addrColNames[] = Mage_ImportExport_Model_Import_Entity_Customer_Address::getColNameForAttrCode($attrCode);
168
  }
169
  foreach (Mage::getResourceModel('customer/address_collection')->addAttributeToSelect('*') as $address) {
170
  $addrRow = array();
171
 
172
- foreach ($addrAttributes as $attrCode => $attrValues) {
 
 
 
 
173
  if (null !== $address->getData($attrCode)) {
174
- $value = $address->getData($attrCode);
 
 
 
 
175
 
176
- if ($attrValues) {
 
 
 
 
 
 
 
 
177
  $value = $attrValues[$value];
178
  }
179
- $column = Mage_ImportExport_Model_Import_Entity_Customer_Address::getColNameForAttrCode($attrCode);
180
  $addrRow[$column] = $value;
181
  }
182
  }
@@ -189,49 +221,108 @@ class Mage_ImportExport_Model_Export_Entity_Customer extends Mage_ImportExport_M
189
  array('password'), $addrColNames,
190
  array_keys($defaultAddrMap)
191
  ));
192
- foreach ($collection as $itemId => $item) { // go through all customers
193
- $row = array();
 
 
 
 
 
 
 
194
 
195
- // go through all valid attribute codes
196
- foreach ($validAttrCodes as $attrCode) {
197
- $attrValue = $item->getData($attrCode);
198
 
199
- if (isset($this->_attributeValues[$attrCode])
200
- && isset($this->_attributeValues[$attrCode][$attrValue])
201
- ) {
202
- $attrValue = $this->_attributeValues[$attrCode][$attrValue];
203
- }
204
- if (null !== $attrValue) {
205
- $row[$attrCode] = $attrValue;
206
- }
 
 
207
  }
208
- $row[self::COL_WEBSITE] = $this->_websiteIdToCode[$item['website_id']];
209
- $row[self::COL_STORE] = $this->_storeIdToCode[$item['store_id']];
210
 
211
- // addresses injection
212
- $defaultAddrs = array();
 
 
 
213
 
214
- foreach ($defaultAddrMap as $colName => $addrAttrCode) {
215
- if (!empty($item[$addrAttrCode])) {
216
- $defaultAddrs[$item[$addrAttrCode]][] = $colName;
217
- }
218
- }
219
- if (isset($customerAddrs[$itemId])) {
220
- while (($addrRow = each($customerAddrs[$itemId]))) {
221
- if (isset($defaultAddrs[$addrRow['key']])) {
222
- foreach ($defaultAddrs[$addrRow['key']] as $colName) {
223
- $row[$colName] = 1;
224
- }
225
  }
226
- $writer->writeRow(array_merge($row, $addrRow['value']));
227
 
228
- $row = array();
 
 
 
229
  }
230
- } else {
231
- $writer->writeRow($row);
232
  }
233
  }
234
- return $writer->getContents();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
235
  }
236
 
237
  /**
@@ -247,7 +338,7 @@ class Mage_ImportExport_Model_Export_Entity_Customer extends Mage_ImportExport_M
247
  $data = $this->_attributeOverrides[$attribute->getAttributeCode()];
248
 
249
  if (isset($data['options_method']) && method_exists($this, $data['options_method'])) {
250
- $data['filter_options'] = $this->$data['options_method']();
251
  }
252
  $attribute->addData($data);
253
  }
@@ -274,4 +365,152 @@ class Mage_ImportExport_Model_Export_Entity_Customer extends Mage_ImportExport_M
274
  {
275
  return 'customer';
276
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
277
  }
75
  */
76
  protected $_permanentAttributes = array(self::COL_EMAIL, self::COL_WEBSITE, self::COL_STORE);
77
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  /**
79
  * Constructor.
80
  *
118
  }
119
 
120
  /**
121
+ * Export process and return contents of temporary file
122
+ *
123
+ * @deprecated after ver 1.9.2.4 use $this->exportFile() instead
124
  *
125
  * @return string
126
  */
127
  public function export()
128
  {
129
+ $this->_prepareExport();
130
+
131
+ return $this->getWriter()->getContents();
132
+ }
133
+
134
+ /**
135
+ * Export process and return temporary file through array
136
+ *
137
+ * This method will return following array:
138
+ *
139
+ * array(
140
+ * 'rows' => count of written rows,
141
+ * 'value' => path to created file
142
+ * )
143
+ *
144
+ * @return array
145
+ */
146
+ public function exportFile()
147
+ {
148
+ $this->_prepareExport();
149
+
150
+ $writer = $this->getWriter();
151
+
152
+ return array(
153
+ 'rows' => $writer->getRowsCount(),
154
+ 'value' => $writer->getDestination()
155
+ );
156
+ }
157
+
158
+ /**
159
+ * Prepare data for export and write its to temporary file through writer.
160
+ *
161
+ * @return void
162
+ */
163
+ protected function _prepareExport()
164
+ {
165
+ $collection = $this->_prepareEntityCollection(Mage::getResourceModel('customer/customer_collection'));
166
  $validAttrCodes = $this->_getExportAttrCodes();
167
  $writer = $this->getWriter();
168
  $defaultAddrMap = Mage_ImportExport_Model_Import_Entity_Customer_Address::getDefaultAddressAttrMapping();
169
 
170
  // prepare address data
171
+ $allAddressAttributeOptions = array();
172
+ $addrColNames = array();
173
+ $customerAddrs = array();
174
+ $addressAttributeCollection = Mage::getResourceModel('customer/address_attribute_collection')
175
+ ->addSystemHiddenFilter()
176
+ ->addExcludeHiddenFrontendFilter();
177
+ $addressAttributes = array();
178
+ $addrAttributeMultiSelect = array();
179
+ $customerAttributeMultiSelect = array();
180
 
181
+ foreach ($addressAttributeCollection as $attribute) {
182
+ $attrCode = $attribute->getAttributeCode();
183
+ $allAddressAttributeOptions[$attrCode] = $this->_getAddressAttributeOptions($attribute);
 
 
 
 
 
 
 
184
  $addrColNames[] = Mage_ImportExport_Model_Import_Entity_Customer_Address::getColNameForAttrCode($attrCode);
185
  }
186
  foreach (Mage::getResourceModel('customer/address_collection')->addAttributeToSelect('*') as $address) {
187
  $addrRow = array();
188
 
189
+ if (empty($addressAttributes)) {
190
+ $addressAttributes = $address->getAttributes();
191
+ }
192
+ foreach ($allAddressAttributeOptions as $attrCode => $attrValues) {
193
+ $column = Mage_ImportExport_Model_Import_Entity_Customer_Address::getColNameForAttrCode($attrCode);
194
  if (null !== $address->getData($attrCode)) {
195
+ if (!isset($addressAttributes[$attrCode])) {
196
+ $addressAttributes = array_merge($addressAttributes, $address->getAttributes());
197
+ }
198
+ $addressAttribute = $addressAttributes[$attrCode];
199
+ $value = $address->getData($attrCode);
200
 
201
+ if ($addressAttribute->getFrontendInput() == 'multiselect') {
202
+ $optionIds = explode(',', $value);
203
+ $optionTexts = array();
204
+ foreach ($optionIds as $optionId) {
205
+ $optionText = $addressAttribute->getSource()->getOptionText($optionId);
206
+ $optionTexts[$optionId] = $optionText;
207
+ }
208
+ $addrAttributeMultiSelect[$address['parent_id']][$address->getId()][$column] = $optionTexts;
209
+ } elseif ($attrValues) {
210
  $value = $attrValues[$value];
211
  }
 
212
  $addrRow[$column] = $value;
213
  }
214
  }
221
  array('password'), $addrColNames,
222
  array_keys($defaultAddrMap)
223
  ));
224
+ foreach ($collection as $customerId => $customer) {
225
+ $customerAddress = array();
226
+ if (isset($customerAddrs[$customerId])) {
227
+ $customerAddress = $customerAddrs[$customerId];
228
+ }
229
+ $addressMultiselect= array();
230
+ if (isset($addrAttributeMultiSelect[$customerId])) {
231
+ $addressMultiselect = $addrAttributeMultiSelect[$customerId];
232
+ }
233
 
234
+ $row = $this->_prepareExportRow($customer, $customerAttributeMultiSelect);
235
+ $defaultAddrs = $this->_prepareDefaultAddress($customer);
 
236
 
237
+ $addrRow = array();
238
+ $currentAddressId = 0;
239
+ if (isset($customerAddrs[$customerId])) {
240
+ list($addressId, $addrRow) = $this->_getNextAddressRow($customerAddress);
241
+ $row = $this->_addDefaultAddressFields($defaultAddrs, $addressId, $row);
242
+ $addrRow = $this->_addNextAddressOptions($addressMultiselect, $addressId, $addrRow);
243
+ $currentAddressId = $addressId;
244
+ }
245
+ foreach ($customerAttributeMultiSelect as $column => &$multiSelectOptions) {
246
+ $row[$column] = array_shift($multiSelectOptions);
247
  }
248
+ $writeRow = array_merge($row, $addrRow);
249
+ $writer->writeRow($writeRow);
250
 
251
+ $additionalRowsCount = $this->_getAdditionalRowsCount($customerAddress,
252
+ $addressMultiselect, $customerAttributeMultiSelect);
253
+ if ($additionalRowsCount) {
254
+ for ($i = 0; $i < $additionalRowsCount; $i++) {
255
+ $writeRow = array();
256
 
257
+ foreach ($customerAttributeMultiSelect as $column => &$multiSelectOptions) {
258
+ $writeRow[$column] = array_shift($multiSelectOptions);
259
+ }
260
+ if (!$this->_isExistMultiSelectOptions($addressMultiselect, $currentAddressId)) {
261
+ list($addressId, $addrRow) = $this->_getNextAddressRow($customerAddress);
262
+ $currentAddressId = $addressId;
263
+ $addrRow = $this->_addNextAddressOptions($addressMultiselect, $currentAddressId, $addrRow);
264
+ } else {
265
+ $addrRow = array();
266
+ $addrRow = $this->_addNextAddressOptions($addressMultiselect, $currentAddressId, $addrRow);
 
267
  }
 
268
 
269
+ if ($addrRow) {
270
+ $writeRow = array_merge($writeRow, $addrRow);
271
+ }
272
+ $writer->writeRow($writeRow);
273
  }
 
 
274
  }
275
  }
276
+ }
277
+
278
+ /**
279
+ * Get Additional Rows Count
280
+ *
281
+ * @param array $customerAddress
282
+ * @param array $addrMultiSelect
283
+ * @param array $customerMultiSelect
284
+ * @return int
285
+ */
286
+ protected function _getAdditionalRowsCount($customerAddress, $addrMultiSelect, $customerMultiSelect)
287
+ {
288
+ $additionalRowsCount = count($customerAddress);
289
+ $addressRowCount = 0;
290
+ $allAddressRowCount = array();
291
+
292
+ foreach ($addrMultiSelect as $addressId => $addressAttributeOptions) {
293
+ foreach ($addressAttributeOptions as $options) {
294
+ $addressRowCount = max(count($options), $addressRowCount);
295
+ $allAddressRowCount[$addressId] = $addressRowCount;
296
+ }
297
+ $addressRowCount = 0;
298
+ }
299
+
300
+ $additionalRowsCount = max(array_sum($allAddressRowCount), $additionalRowsCount);
301
+
302
+ foreach ($customerMultiSelect as $options) {
303
+ $additionalRowsCount = max(count($options), $additionalRowsCount);
304
+ }
305
+
306
+ return $additionalRowsCount;
307
+ }
308
+
309
+ /**
310
+ * Get Next Address Row
311
+ *
312
+ * @param array $customerAddress
313
+ * @return array
314
+ */
315
+ protected function _getNextAddressRow(&$customerAddress)
316
+ {
317
+ if (!empty($customerAddress)) {
318
+ reset($customerAddress);
319
+ $addressId = key($customerAddress);
320
+ $addressRow = current($customerAddress);
321
+ unset($customerAddress[$addressId]);
322
+
323
+ return array($addressId, $addressRow);
324
+ }
325
+ return array(null, null);
326
  }
327
 
328
  /**
338
  $data = $this->_attributeOverrides[$attribute->getAttributeCode()];
339
 
340
  if (isset($data['options_method']) && method_exists($this, $data['options_method'])) {
341
+ $data['filter_options'] = $this->{$data['options_method']}();
342
  }
343
  $attribute->addData($data);
344
  }
365
  {
366
  return 'customer';
367
  }
368
+
369
+ /**
370
+ * Get Address Attributes
371
+ *
372
+ * @param $attribute
373
+ * @return array
374
+ */
375
+ protected function _getAddressAttributeOptions($attribute)
376
+ {
377
+ $options = array();
378
+ $attrCode = $attribute->getAttributeCode();
379
+
380
+ if ($attribute->usesSource() && 'country_id' != $attrCode) {
381
+ foreach ($attribute->getSource()->getAllOptions(false) as $option) {
382
+ $innerOptions = is_array($option['value']) ? $option['value'] : array($option);
383
+ foreach ($innerOptions as $innerOption) {
384
+ // skip ' -- Please Select -- ' option
385
+ if (strlen($innerOption['value'])) {
386
+ $options[$innerOption['value']] = $innerOption['label'];
387
+ }
388
+ }
389
+ }
390
+ }
391
+ return $options;
392
+ }
393
+
394
+ /**
395
+ * Prepare Export Row
396
+ *
397
+ * @param Mage_Customer_Model_Customer $customer
398
+ * @param array $attributeMultiSelect
399
+ * @return array
400
+ */
401
+ protected function _prepareExportRow($customer, &$attributeMultiSelect)
402
+ {
403
+ $row = array();
404
+ $validAttrCodes = $this->_getExportAttrCodes();
405
+
406
+ // go through all valid attribute codes
407
+ foreach ($validAttrCodes as $attrCode) {
408
+ $attribute = $customer->getAttribute($attrCode);
409
+ $attrValue = $customer->getData($attrCode);
410
+
411
+ if ($attribute && $attribute->getFrontendInput() == 'multiselect') {
412
+ $optionText = (array)$attribute->getSource()->getOptionText($attrValue);
413
+ if ($optionText) {
414
+ $attributeMultiSelect[$attrCode] = $optionText;
415
+ $attrValue = null;
416
+ }
417
+ } elseif (isset($this->_attributeValues[$attrCode])
418
+ && isset($this->_attributeValues[$attrCode][$attrValue])
419
+ ) {
420
+ $attrValue = $this->_attributeValues[$attrCode][$attrValue];
421
+ }
422
+ if (null !== $attrValue) {
423
+ $row[$attrCode] = $attrValue;
424
+ }
425
+ }
426
+ $row[self::COL_WEBSITE] = $this->_websiteIdToCode[$customer['website_id']];
427
+ $row[self::COL_STORE] = $this->_storeIdToCode[$customer['store_id']];
428
+
429
+ return $row;
430
+ }
431
+
432
+ /**
433
+ * Prepare Default Address
434
+ *
435
+ * @param Mage_Customer_Model_Customer $customer
436
+ * @return array
437
+ */
438
+ protected function _prepareDefaultAddress($customer)
439
+ {
440
+ $defaultAddrMap = Mage_ImportExport_Model_Import_Entity_Customer_Address::getDefaultAddressAttrMapping();
441
+ $defaultAddrs = array();
442
+
443
+ foreach ($defaultAddrMap as $colName => $addrAttrCode) {
444
+ if (!empty($customer[$addrAttrCode])) {
445
+ $defaultAddrs[$customer[$addrAttrCode]][] = $colName;
446
+ }
447
+ }
448
+ return $defaultAddrs;
449
+ }
450
+
451
+ /**
452
+ * Add default fields to row
453
+ *
454
+ * @param $defaultAddrs
455
+ * @param $addressId
456
+ * @param $row
457
+ * @return mixed
458
+ */
459
+ protected function _addDefaultAddressFields($defaultAddrs, $addressId, $row)
460
+ {
461
+ if (isset($defaultAddrs[$addressId])) {
462
+ foreach ($defaultAddrs[$addressId] as $colName) {
463
+ $row[$colName] = 1;
464
+ }
465
+ return $row;
466
+ }
467
+ return $row;
468
+ }
469
+
470
+ /**
471
+ * Get Next Address MultiSelect option
472
+ *
473
+ * @param array $addrAttributeMultiSelect
474
+ * @param int $addressId
475
+ * @param array $addrRow
476
+ * @return array
477
+ */
478
+ protected function _addNextAddressOptions(&$addrAttributeMultiSelect, $addressId, $addrRow)
479
+ {
480
+ if (!isset($addrAttributeMultiSelect[$addressId])) {
481
+ return $addrRow;
482
+ }
483
+ $addrMultiSelectOption = &$addrAttributeMultiSelect[$addressId];
484
+ if (is_array($addrMultiSelectOption)) {
485
+ foreach ($addrMultiSelectOption as $column => &$options) {
486
+ $addrRow[$column] = array_shift($options);
487
+ }
488
+ }
489
+ return $addrRow;
490
+ }
491
+
492
+ /**
493
+ * Check if exist MultiSelect Options
494
+ *
495
+ * @param array $addrAttributeMultiSelect
496
+ * @param int $addressId
497
+ * @return bool
498
+ */
499
+ protected function _isExistMultiSelectOptions($addrAttributeMultiSelect, $addressId)
500
+ {
501
+ $result = false;
502
+ if (!isset($addrAttributeMultiSelect[$addressId])) {
503
+ return $result;
504
+ }
505
+ $addrMultiSelectOption = $addrAttributeMultiSelect[$addressId];
506
+ if (is_array($addrMultiSelectOption)) {
507
+ foreach ($addrMultiSelectOption as $option) {
508
+ if (!empty($option)) {
509
+ $result = true;
510
+ break;
511
+ }
512
+ }
513
+ }
514
+ return $result;
515
+ }
516
  }
app/code/core/Mage/ImportExport/Model/Export/Entity/Product.php CHANGED
@@ -102,24 +102,18 @@ class Mage_ImportExport_Model_Export_Entity_Product extends Mage_ImportExport_Mo
102
  protected $_productTypeModels = array();
103
 
104
  /**
105
- * Array of pairs store ID to its code.
106
  *
107
  * @var array
108
  */
109
- protected $_storeIdToCode = array();
110
 
111
  /**
112
- * Website ID-to-code.
113
  *
114
  * @var array
115
  */
116
- protected $_websiteIdToCode = array();
117
-
118
- /**
119
- * Attribute types
120
- * @var array
121
- */
122
- protected $_attributeTypes = array();
123
 
124
  /**
125
  * Constructor.
@@ -546,11 +540,49 @@ class Mage_ImportExport_Model_Export_Entity_Product extends Mage_ImportExport_Mo
546
  }
547
 
548
  /**
549
- * Export process.
 
 
550
  *
551
  * @return string
552
  */
553
  public function export()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
554
  {
555
  //Execution time may be very long
556
  set_time_limit(0);
@@ -637,7 +669,41 @@ class Mage_ImportExport_Model_Export_Entity_Product extends Mage_ImportExport_Mo
637
  $this->_attributeValues[$attrCode],
638
  array_flip($attrValue)
639
  );
640
- $rowMultiselects[$itemId][$attrCode] = $attrValue;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
641
  } else if (isset($this->_attributeValues[$attrCode][$attrValue])) {
642
  $attrValue = $this->_attributeValues[$attrCode][$attrValue];
643
  } else {
@@ -737,6 +803,7 @@ class Mage_ImportExport_Model_Export_Entity_Product extends Mage_ImportExport_Mo
737
  );
738
 
739
  foreach ($this->_storeIdToCode as $storeId => &$storeCode) {
 
740
  $options = Mage::getResourceModel('catalog/product_option_collection')
741
  ->reset()
742
  ->addTitleToResult($storeId)
@@ -769,7 +836,7 @@ class Mage_ImportExport_Model_Export_Entity_Product extends Mage_ImportExport_Mo
769
  }
770
  $values = $option->getValues();
771
  if ($values) {
772
- $firstValue = array_shift($values);
773
  $priceType = $firstValue['price_type'] == 'percent' ? '%' : '';
774
 
775
  if ($defaultStoreId == $storeId) {
@@ -788,8 +855,14 @@ class Mage_ImportExport_Model_Export_Entity_Product extends Mage_ImportExport_Mo
788
  $row['_custom_option_store'] = $this->_storeIdToCode[$storeId];
789
  }
790
  $customOptionsDataPre[$productId][$optionId][] = $row;
 
791
  }
792
  foreach ($values as $value) {
 
 
 
 
 
793
  $row = array();
794
  $valuePriceType = $value['price_type'] == 'percent' ? '%' : '';
795
 
@@ -798,7 +871,7 @@ class Mage_ImportExport_Model_Export_Entity_Product extends Mage_ImportExport_Mo
798
  $row['_custom_option_row_price'] = $value['price'] . $valuePriceType;
799
  $row['_custom_option_row_sku'] = $value['sku'];
800
  $row['_custom_option_row_sort'] = $value['sort_order'];
801
- } elseif ($value['title'] != $customOptions[0]['_custom_option_row_title']) {
802
  $row['_custom_option_row_title'] = $value['title'];
803
  }
804
  if ($row) {
@@ -905,94 +978,93 @@ class Mage_ImportExport_Model_Export_Entity_Product extends Mage_ImportExport_Mo
905
  if (!empty($configurableData[$productId])) {
906
  $dataRow = array_merge($dataRow, array_shift($configurableData[$productId]));
907
  }
908
- if(!empty($rowMultiselects[$productId])) {
909
- foreach ($rowMultiselects[$productId] as $attrKey => $attrVal) {
910
- if (!empty($rowMultiselects[$productId][$attrKey])) {
911
- $dataRow[$attrKey] = array_shift($rowMultiselects[$productId][$attrKey]);
912
  }
913
  }
914
  }
915
 
916
  $writer->writeRow($dataRow);
917
- }
918
- // calculate largest links block
919
- $largestLinks = 0;
920
 
921
- if (isset($linksRows[$productId])) {
922
- $linksRowsKeys = array_keys($linksRows[$productId]);
923
- foreach ($linksRowsKeys as $linksRowsKey) {
924
- $largestLinks = max($largestLinks, count($linksRows[$productId][$linksRowsKey]));
 
925
  }
926
- }
927
- $additionalRowsCount = max(
928
- count($rowCategories[$productId]),
929
- count($rowWebsites[$productId]),
930
- $largestLinks
931
- );
932
- if (!empty($rowTierPrices[$productId])) {
933
- $additionalRowsCount = max($additionalRowsCount, count($rowTierPrices[$productId]));
934
- }
935
- if (!empty($rowGroupPrices[$productId])) {
936
- $additionalRowsCount = max($additionalRowsCount, count($rowGroupPrices[$productId]));
937
- }
938
- if (!empty($mediaGalery[$productId])) {
939
- $additionalRowsCount = max($additionalRowsCount, count($mediaGalery[$productId]));
940
- }
941
- if (!empty($customOptionsData[$productId])) {
942
- $additionalRowsCount = max($additionalRowsCount, count($customOptionsData[$productId]));
943
- }
944
- if (!empty($configurableData[$productId])) {
945
- $additionalRowsCount = max($additionalRowsCount, count($configurableData[$productId]));
946
- }
947
- if (!empty($rowMultiselects[$productId])) {
948
- foreach($rowMultiselects[$productId] as $attributes) {
949
- $additionalRowsCount = max($additionalRowsCount, count($attributes));
950
  }
951
- }
952
-
953
- if ($additionalRowsCount) {
954
- for ($i = 0; $i < $additionalRowsCount; $i++) {
955
- $dataRow = array();
956
-
957
- $this->_updateDataWithCategoryColumns($dataRow, $rowCategories, $productId);
958
- if ($rowWebsites[$productId]) {
959
- $dataRow['_product_websites'] = $this
960
- ->_websiteIdToCode[array_shift($rowWebsites[$productId])];
961
- }
962
- if (!empty($rowTierPrices[$productId])) {
963
- $dataRow = array_merge($dataRow, array_shift($rowTierPrices[$productId]));
964
- }
965
- if (!empty($rowGroupPrices[$productId])) {
966
- $dataRow = array_merge($dataRow, array_shift($rowGroupPrices[$productId]));
967
- }
968
- if (!empty($mediaGalery[$productId])) {
969
- $dataRow = array_merge($dataRow, array_shift($mediaGalery[$productId]));
970
  }
971
- foreach ($linkIdColPrefix as $linkId => &$colPrefix) {
972
- if (!empty($linksRows[$productId][$linkId])) {
973
- $linkData = array_shift($linksRows[$productId][$linkId]);
974
- $dataRow[$colPrefix . 'position'] = $linkData['position'];
975
- $dataRow[$colPrefix . 'sku'] = $linkData['sku'];
976
-
977
- if (null !== $linkData['default_qty']) {
978
- $dataRow[$colPrefix . 'default_qty'] = $linkData['default_qty'];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
979
  }
980
  }
981
- }
982
- if (!empty($customOptionsData[$productId])) {
983
- $dataRow = array_merge($dataRow, array_shift($customOptionsData[$productId]));
984
- }
985
- if (!empty($configurableData[$productId])) {
986
- $dataRow = array_merge($dataRow, array_shift($configurableData[$productId]));
987
- }
988
- if(!empty($rowMultiselects[$productId])) {
989
- foreach($rowMultiselects[$productId] as $attrKey=>$attrVal) {
990
- if(!empty($rowMultiselects[$productId][$attrKey])) {
991
- $dataRow[$attrKey] = array_shift($rowMultiselects[$productId][$attrKey]);
992
  }
993
  }
 
994
  }
995
- $writer->writeRow($dataRow);
996
  }
997
  }
998
  }
@@ -1058,6 +1130,7 @@ class Mage_ImportExport_Model_Export_Entity_Product extends Mage_ImportExport_Mo
1058
  $this->_attributeValues[$attribute->getAttributeCode()] = $this->getAttributeOptions($attribute);
1059
  $this->_attributeTypes[$attribute->getAttributeCode()] =
1060
  Mage_ImportExport_Model_Import::getAttributeType($attribute);
 
1061
  }
1062
  return $this;
1063
  }
102
  protected $_productTypeModels = array();
103
 
104
  /**
105
+ * Attribute types
106
  *
107
  * @var array
108
  */
109
+ protected $_attributeTypes = array();
110
 
111
  /**
112
+ * Attribute scopes
113
  *
114
  * @var array
115
  */
116
+ protected $_attributeScopes = array();
 
 
 
 
 
 
117
 
118
  /**
119
  * Constructor.
540
  }
541
 
542
  /**
543
+ * Export process and return contents of temporary file.
544
+ *
545
+ * @deprecated after ver 1.9.2.4 use $this->exportFile() instead
546
  *
547
  * @return string
548
  */
549
  public function export()
550
+ {
551
+ $this->_prepareExport();
552
+
553
+ return $this->getWriter()->getContents();
554
+ }
555
+
556
+ /**
557
+ * Export process and return temporary file through array.
558
+ *
559
+ * This method will return following array:
560
+ *
561
+ * array(
562
+ * 'rows' => count of written rows,
563
+ * 'value' => path to created file
564
+ * )
565
+ *
566
+ * @return array
567
+ */
568
+ public function exportFile()
569
+ {
570
+ $this->_prepareExport();
571
+
572
+ $writer = $this->getWriter();
573
+
574
+ return array(
575
+ 'rows' => $writer->getRowsCount(),
576
+ 'value' => $writer->getDestination()
577
+ );
578
+ }
579
+
580
+ /**
581
+ * Prepare data for export.
582
+ *
583
+ * @return void
584
+ */
585
+ protected function _prepareExport()
586
  {
587
  //Execution time may be very long
588
  set_time_limit(0);
669
  $this->_attributeValues[$attrCode],
670
  array_flip($attrValue)
671
  );
672
+
673
+ switch ($this->_attributeScopes[$attrCode]) {
674
+ case Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_STORE:
675
+ if (isset($rowMultiselects[$itemId][0][$attrCode])
676
+ && $attrValue == $rowMultiselects[$itemId][0][$attrCode]
677
+ ) {
678
+ $attrValue = null;
679
+ }
680
+ break;
681
+
682
+ case Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_GLOBAL:
683
+ if ($storeId != $defaultStoreId) {
684
+ $attrValue = null;
685
+ }
686
+ break;
687
+
688
+ case Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_WEBSITE:
689
+ $websiteId = $this->_storeIdToWebsiteId[$storeId];
690
+ $websiteStoreId = array_search($websiteId, $this->_storeIdToWebsiteId);
691
+ if ((isset($rowMultiselects[$itemId][$websiteStoreId][$attrCode])
692
+ && $attrValue == $rowMultiselects[$itemId][$websiteStoreId][$attrCode])
693
+ || $attrValue == $rowMultiselects[$itemId][0][$attrCode]
694
+ ) {
695
+ $attrValue = null;
696
+ }
697
+ break;
698
+
699
+ default:
700
+ break;
701
+ }
702
+
703
+ if ($attrValue) {
704
+ $rowMultiselects[$itemId][$storeId][$attrCode] = $attrValue;
705
+ $rowIsEmpty = false;
706
+ }
707
  } else if (isset($this->_attributeValues[$attrCode][$attrValue])) {
708
  $attrValue = $this->_attributeValues[$attrCode][$attrValue];
709
  } else {
803
  );
804
 
805
  foreach ($this->_storeIdToCode as $storeId => &$storeCode) {
806
+ $skip = false;
807
  $options = Mage::getResourceModel('catalog/product_option_collection')
808
  ->reset()
809
  ->addTitleToResult($storeId)
836
  }
837
  $values = $option->getValues();
838
  if ($values) {
839
+ $firstValue = reset($values);
840
  $priceType = $firstValue['price_type'] == 'percent' ? '%' : '';
841
 
842
  if ($defaultStoreId == $storeId) {
855
  $row['_custom_option_store'] = $this->_storeIdToCode[$storeId];
856
  }
857
  $customOptionsDataPre[$productId][$optionId][] = $row;
858
+ $skip = true;
859
  }
860
  foreach ($values as $value) {
861
+ if ($skip) {
862
+ $skip = false;
863
+ continue;
864
+ }
865
+
866
  $row = array();
867
  $valuePriceType = $value['price_type'] == 'percent' ? '%' : '';
868
 
871
  $row['_custom_option_row_price'] = $value['price'] . $valuePriceType;
872
  $row['_custom_option_row_sku'] = $value['sku'];
873
  $row['_custom_option_row_sort'] = $value['sort_order'];
874
+ } else {
875
  $row['_custom_option_row_title'] = $value['title'];
876
  }
877
  if ($row) {
978
  if (!empty($configurableData[$productId])) {
979
  $dataRow = array_merge($dataRow, array_shift($configurableData[$productId]));
980
  }
981
+ if(!empty($rowMultiselects[$productId][$storeId])) {
982
+ foreach ($rowMultiselects[$productId][$storeId] as $attrKey => $attrVal) {
983
+ if (isset($rowMultiselects[$productId][$storeId][$attrKey])) {
984
+ $dataRow[$attrKey] = array_shift($rowMultiselects[$productId][$storeId][$attrKey]);
985
  }
986
  }
987
  }
988
 
989
  $writer->writeRow($dataRow);
990
+ // calculate largest links block
991
+ $largestLinks = 0;
 
992
 
993
+ if (isset($linksRows[$productId])) {
994
+ $linksRowsKeys = array_keys($linksRows[$productId]);
995
+ foreach ($linksRowsKeys as $linksRowsKey) {
996
+ $largestLinks = max($largestLinks, count($linksRows[$productId][$linksRowsKey]));
997
+ }
998
  }
999
+ $additionalRowsCount = max(
1000
+ count($rowCategories[$productId]),
1001
+ count($rowWebsites[$productId]),
1002
+ $largestLinks
1003
+ );
1004
+ if (!empty($rowTierPrices[$productId])) {
1005
+ $additionalRowsCount = max($additionalRowsCount, count($rowTierPrices[$productId]));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1006
  }
1007
+ if (!empty($rowGroupPrices[$productId])) {
1008
+ $additionalRowsCount = max($additionalRowsCount, count($rowGroupPrices[$productId]));
1009
+ }
1010
+ if (!empty($mediaGalery[$productId])) {
1011
+ $additionalRowsCount = max($additionalRowsCount, count($mediaGalery[$productId]));
1012
+ }
1013
+ if (!empty($customOptionsData[$productId])) {
1014
+ $additionalRowsCount = max($additionalRowsCount, count($customOptionsData[$productId]));
1015
+ }
1016
+ if (!empty($configurableData[$productId])) {
1017
+ $additionalRowsCount = max($additionalRowsCount, count($configurableData[$productId]));
1018
+ }
1019
+ if (!empty($rowMultiselects[$productId][$storeId])) {
1020
+ foreach($rowMultiselects[$productId][$storeId] as $attributes) {
1021
+ $additionalRowsCount = max($additionalRowsCount, count($attributes));
 
 
 
 
1022
  }
1023
+ }
1024
+ if ($additionalRowsCount) {
1025
+ for ($i = 0; $i < $additionalRowsCount; $i++) {
1026
+ $dataRow = array();
1027
+
1028
+ $this->_updateDataWithCategoryColumns($dataRow, $rowCategories, $productId);
1029
+ if ($rowWebsites[$productId]) {
1030
+ $dataRow['_product_websites'] = $this
1031
+ ->_websiteIdToCode[array_shift($rowWebsites[$productId])];
1032
+ }
1033
+ if (!empty($rowTierPrices[$productId])) {
1034
+ $dataRow = array_merge($dataRow, array_shift($rowTierPrices[$productId]));
1035
+ }
1036
+ if (!empty($rowGroupPrices[$productId])) {
1037
+ $dataRow = array_merge($dataRow, array_shift($rowGroupPrices[$productId]));
1038
+ }
1039
+ if (!empty($mediaGalery[$productId])) {
1040
+ $dataRow = array_merge($dataRow, array_shift($mediaGalery[$productId]));
1041
+ }
1042
+ foreach ($linkIdColPrefix as $linkId => &$colPrefix) {
1043
+ if (!empty($linksRows[$productId][$linkId])) {
1044
+ $linkData = array_shift($linksRows[$productId][$linkId]);
1045
+ $dataRow[$colPrefix . 'position'] = $linkData['position'];
1046
+ $dataRow[$colPrefix . 'sku'] = $linkData['sku'];
1047
+
1048
+ if (null !== $linkData['default_qty']) {
1049
+ $dataRow[$colPrefix . 'default_qty'] = $linkData['default_qty'];
1050
+ }
1051
  }
1052
  }
1053
+ if (!empty($customOptionsData[$productId])) {
1054
+ $dataRow = array_merge($dataRow, array_shift($customOptionsData[$productId]));
1055
+ }
1056
+ if (!empty($configurableData[$productId])) {
1057
+ $dataRow = array_merge($dataRow, array_shift($configurableData[$productId]));
1058
+ }
1059
+ if(!empty($rowMultiselects[$productId][$storeId])) {
1060
+ foreach($rowMultiselects[$productId][$storeId] as $attrKey=>$attrVal) {
1061
+ if(isset($rowMultiselects[$productId][$storeId][$attrKey])) {
1062
+ $dataRow[$attrKey] = array_shift($rowMultiselects[$productId][$storeId][$attrKey]);
1063
+ }
1064
  }
1065
  }
1066
+ $writer->writeRow($dataRow);
1067
  }
 
1068
  }
1069
  }
1070
  }
1130
  $this->_attributeValues[$attribute->getAttributeCode()] = $this->getAttributeOptions($attribute);
1131
  $this->_attributeTypes[$attribute->getAttributeCode()] =
1132
  Mage_ImportExport_Model_Import::getAttributeType($attribute);
1133
+ $this->_attributeScopes[$attribute->getAttributeCode()] = $attribute->getIsGlobal();
1134
  }
1135
  return $this;
1136
  }
app/code/core/Mage/ImportExport/Model/Export/Entity/Product/Type/Abstract.php CHANGED
@@ -96,7 +96,7 @@ abstract class Mage_ImportExport_Model_Export_Entity_Product_Type_Abstract
96
  $data = $this->_attributeOverrides[$attribute->getAttributeCode()];
97
 
98
  if (isset($data['options_method']) && method_exists($this, $data['options_method'])) {
99
- $data['filter_options'] = $this->$data['options_method']();
100
  }
101
  $attribute->addData($data);
102
 
96
  $data = $this->_attributeOverrides[$attribute->getAttributeCode()];
97
 
98
  if (isset($data['options_method']) && method_exists($this, $data['options_method'])) {
99
+ $data['filter_options'] = $this->{$data['options_method']}();
100
  }
101
  $attribute->addData($data);
102
 
app/code/core/Mage/ImportExport/Model/Import/Entity/Customer.php CHANGED
@@ -43,6 +43,7 @@ class Mage_ImportExport_Model_Import_Entity_Customer extends Mage_ImportExport_M
43
  */
44
  const SCOPE_DEFAULT = 1;
45
  const SCOPE_ADDRESS = -1;
 
46
 
47
  /**
48
  * Permanent column names.
@@ -50,9 +51,10 @@ class Mage_ImportExport_Model_Import_Entity_Customer extends Mage_ImportExport_M
50
  * Names that begins with underscore is not an attribute. This name convention is for
51
  * to avoid interference with same attribute name.
52
  */
53
- const COL_EMAIL = 'email';
54
- const COL_WEBSITE = '_website';
55
- const COL_STORE = '_store';
 
56
 
57
  /**
58
  * Error codes.
@@ -95,6 +97,13 @@ class Mage_ImportExport_Model_Import_Entity_Customer extends Mage_ImportExport_M
95
  */
96
  protected $_attributes = array();
97
 
 
 
 
 
 
 
 
98
  /**
99
  * Customer account sharing. TRUE - is global, FALSE - is per website.
100
  *
@@ -276,7 +285,7 @@ class Mage_ImportExport_Model_Import_Entity_Customer extends Mage_ImportExport_M
276
  {
277
  $collection = Mage::getResourceModel('customer/attribute_collection')->addSystemHiddenFilterWithPasswordHash();
278
  foreach ($collection as $attribute) {
279
- $this->_attributes[$attribute->getAttributeCode()] = array(
280
  'id' => $attribute->getId(),
281
  'is_required' => $attribute->getIsRequired(),
282
  'is_static' => $attribute->isStatic(),
@@ -284,6 +293,10 @@ class Mage_ImportExport_Model_Import_Entity_Customer extends Mage_ImportExport_M
284
  'type' => Mage_ImportExport_Model_Import::getAttributeType($attribute),
285
  'options' => $this->getAttributeOptions($attribute)
286
  );
 
 
 
 
287
  }
288
  return $this;
289
  }
@@ -363,6 +376,7 @@ class Mage_ImportExport_Model_Import_Entity_Customer extends Mage_ImportExport_M
363
  $nextEntityId = Mage::getResourceHelper('importexport')->getNextAutoincrement($table);
364
  $passId = $resource->getAttribute('password_hash')->getId();
365
  $passTable = $resource->getAttribute('password_hash')->getBackend()->getTable();
 
366
 
367
  while ($bunch = $this->_dataSourceModel->getNextBunch()) {
368
  $entityRowsIn = array();
@@ -415,6 +429,11 @@ class Mage_ImportExport_Model_Import_Entity_Customer extends Mage_ImportExport_M
415
  $value = $attrParams['options'][strtolower($value)];
416
  } elseif ('datetime' == $attrParams['type']) {
417
  $value = gmstrftime($strftimeFormat, strtotime($value));
 
 
 
 
 
418
  } elseif ($backModel) {
419
  $attribute->getBackend()->beforeSave($resource->setData($attrCode, $value));
420
  $value = $resource->getData($attrCode);
@@ -429,6 +448,24 @@ class Mage_ImportExport_Model_Import_Entity_Customer extends Mage_ImportExport_M
429
  if (isset($rowData['password']) && strlen($rowData['password'])) {
430
  $attributes[$passTable][$entityId][$passId] = $resource->hashPassword($rowData['password']);
431
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
432
  }
433
  }
434
  $this->_saveCustomerEntity($entityRowsIn, $entityRowsUp)->_saveCustomerAttributes($attributes);
@@ -521,7 +558,22 @@ class Mage_ImportExport_Model_Import_Entity_Customer extends Mage_ImportExport_M
521
  */
522
  public function getRowScope(array $rowData)
523
  {
524
- return strlen(trim($rowData[self::COL_EMAIL])) ? self::SCOPE_DEFAULT : self::SCOPE_ADDRESS;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
525
  }
526
 
527
  /**
@@ -607,15 +659,17 @@ class Mage_ImportExport_Model_Import_Entity_Customer extends Mage_ImportExport_M
607
  if (isset($this->_invalidRows[$rowNum])) {
608
  $email = false; // mark row as invalid for next address rows
609
  }
610
- } else {
611
  if (null === $email) { // first row is not SCOPE_DEFAULT
612
  $this->addRowError(self::ERROR_EMAIL_IS_EMPTY, $rowNum);
613
  } elseif (false === $email) { // SCOPE_DEFAULT row is invalid
614
  $this->addRowError(self::ERROR_ROW_IS_ORPHAN, $rowNum);
615
  }
616
  }
617
- // validate row data by address entity
618
- $this->_addressEntity->validateRow($rowData, $rowNum);
 
 
619
 
620
  return !isset($this->_invalidRows[$rowNum]);
621
  }
43
  */
44
  const SCOPE_DEFAULT = 1;
45
  const SCOPE_ADDRESS = -1;
46
+ const SCOPE_OPTIONS = 2;
47
 
48
  /**
49
  * Permanent column names.
51
  * Names that begins with underscore is not an attribute. This name convention is for
52
  * to avoid interference with same attribute name.
53
  */
54
+ const COL_EMAIL = 'email';
55
+ const COL_WEBSITE = '_website';
56
+ const COL_STORE = '_store';
57
+ const COL_POSTCODE = '_address_postcode';
58
 
59
  /**
60
  * Error codes.
97
  */
98
  protected $_attributes = array();
99
 
100
+ /**
101
+ * MultiSelect Attributes
102
+ *
103
+ * @var array
104
+ */
105
+ protected $_multiSelectAttributes = array();
106
+
107
  /**
108
  * Customer account sharing. TRUE - is global, FALSE - is per website.
109
  *
285
  {
286
  $collection = Mage::getResourceModel('customer/attribute_collection')->addSystemHiddenFilterWithPasswordHash();
287
  foreach ($collection as $attribute) {
288
+ $attributeArray = array(
289
  'id' => $attribute->getId(),
290
  'is_required' => $attribute->getIsRequired(),
291
  'is_static' => $attribute->isStatic(),
293
  'type' => Mage_ImportExport_Model_Import::getAttributeType($attribute),
294
  'options' => $this->getAttributeOptions($attribute)
295
  );
296
+ $this->_attributes[$attribute->getAttributeCode()] = $attributeArray;
297
+ if (Mage_ImportExport_Model_Import::getAttributeType($attribute) == 'multiselect') {
298
+ $this->_multiSelectAttributes[$attribute->getAttributeCode()] = $attributeArray;
299
+ }
300
  }
301
  return $this;
302
  }
376
  $nextEntityId = Mage::getResourceHelper('importexport')->getNextAutoincrement($table);
377
  $passId = $resource->getAttribute('password_hash')->getId();
378
  $passTable = $resource->getAttribute('password_hash')->getBackend()->getTable();
379
+ $multiSelect = array();
380
 
381
  while ($bunch = $this->_dataSourceModel->getNextBunch()) {
382
  $entityRowsIn = array();
429
  $value = $attrParams['options'][strtolower($value)];
430
  } elseif ('datetime' == $attrParams['type']) {
431
  $value = gmstrftime($strftimeFormat, strtotime($value));
432
+ } elseif ('multiselect' == $attrParams['type']) {
433
+ $value = (array)$attrParams['options'][strtolower($value)];
434
+ $attribute->getBackend()->beforeSave($resource->setData($attrCode, $value));
435
+ $value = $resource->getData($attrCode);
436
+ $multiSelect[$entityId][] = $value;
437
  } elseif ($backModel) {
438
  $attribute->getBackend()->beforeSave($resource->setData($attrCode, $value));
439
  $value = $resource->getData($attrCode);
448
  if (isset($rowData['password']) && strlen($rowData['password'])) {
449
  $attributes[$passTable][$entityId][$passId] = $resource->hashPassword($rowData['password']);
450
  }
451
+ } elseif (self::SCOPE_OPTIONS == $this->getRowScope($rowData)) {
452
+ foreach (array_intersect_key($rowData, $this->_attributes) as $attrCode => $value) {
453
+ $attribute = $resource->getAttribute($attrCode);
454
+ $attrParams = $this->_attributes[$attrCode];
455
+ if ($attrParams['type'] == 'multiselect') {
456
+ if (!isset($attrParams['options'][strtolower($value)])) {
457
+ continue;
458
+ }
459
+ $value = $attrParams['options'][strtolower($value)];
460
+ if (isset($multiSelect[$entityId])) {
461
+ $multiSelect[$entityId][] = $value;
462
+ $value = $multiSelect[$entityId];
463
+ }
464
+ $attribute->getBackend()->beforeSave($resource->setData($attrCode, $value));
465
+ $value = $resource->getData($attrCode);
466
+ $attributes[$attribute->getBackend()->getTable()][$entityId][$attrParams['id']] = $value;
467
+ }
468
+ }
469
  }
470
  }
471
  $this->_saveCustomerEntity($entityRowsIn, $entityRowsUp)->_saveCustomerAttributes($attributes);
558
  */
559
  public function getRowScope(array $rowData)
560
  {
561
+ $foundOptions = false;
562
+ foreach ($this->_multiSelectAttributes as $attrCode => $attribute) {
563
+ if ($rowData[$attrCode]) {
564
+ $foundOptions = true;
565
+ }
566
+ }
567
+
568
+ $scope = self::SCOPE_OPTIONS;
569
+ if (strlen(trim($rowData[self::COL_EMAIL]))) {
570
+ $scope = self::SCOPE_DEFAULT;
571
+ } elseif ($foundOptions) {
572
+ $scope = self::SCOPE_OPTIONS;
573
+ } elseif (strlen(trim($rowData[self::COL_POSTCODE]))) {
574
+ $scope = self::SCOPE_ADDRESS;
575
+ }
576
+ return $scope;
577
  }
578
 
579
  /**
659
  if (isset($this->_invalidRows[$rowNum])) {
660
  $email = false; // mark row as invalid for next address rows
661
  }
662
+ } elseif (self::SCOPE_OPTIONS != $rowScope) {
663
  if (null === $email) { // first row is not SCOPE_DEFAULT
664
  $this->addRowError(self::ERROR_EMAIL_IS_EMPTY, $rowNum);
665
  } elseif (false === $email) { // SCOPE_DEFAULT row is invalid
666
  $this->addRowError(self::ERROR_ROW_IS_ORPHAN, $rowNum);
667
  }
668
  }
669
+
670
+ if ($rowScope != self::SCOPE_OPTIONS) {
671
+ $this->_addressEntity->validateRow($rowData, $rowNum);
672
+ }
673
 
674
  return !isset($this->_invalidRows[$rowNum]);
675
  }
app/code/core/Mage/ImportExport/Model/Import/Entity/Customer/Address.php CHANGED
@@ -175,6 +175,7 @@ class Mage_ImportExport_Model_Import_Entity_Customer_Address extends Mage_Import
175
  $regionIdTable = $regionIdAttr->getBackend()->getTable();
176
  $regionIdAttrId = $regionIdAttr->getId();
177
  $isAppendMode = Mage_ImportExport_Model_Import::BEHAVIOR_APPEND == $this->_customer->getBehavior();
 
178
 
179
  while ($bunch = $this->_dataSourceModel->getNextBunch()) {
180
  $entityRows = array();
@@ -182,15 +183,17 @@ class Mage_ImportExport_Model_Import_Entity_Customer_Address extends Mage_Import
182
  $defaults = array(); // customer default addresses (billing/shipping) data
183
 
184
  foreach ($bunch as $rowNum => $rowData) {
185
- if (!empty($rowData[Mage_ImportExport_Model_Import_Entity_Customer::COL_EMAIL])
186
- && !empty($rowData[Mage_ImportExport_Model_Import_Entity_Customer::COL_WEBSITE])
187
- ) {
188
  $customerId = $this->_customer->getCustomerId(
189
  $rowData[Mage_ImportExport_Model_Import_Entity_Customer::COL_EMAIL],
190
  $rowData[Mage_ImportExport_Model_Import_Entity_Customer::COL_WEBSITE]
191
  );
192
  }
193
- if (!$customerId || !$this->_isRowWithAddress($rowData) || !$this->validateRow($rowData, $rowNum)) {
 
 
 
194
  continue;
195
  }
196
 
@@ -205,6 +208,9 @@ class Mage_ImportExport_Model_Import_Entity_Customer_Address extends Mage_Import
205
  $value = $attrParams['options'][strtolower($rowData[$attrAlias])];
206
  } elseif ('datetime' == $attrParams['type']) {
207
  $value = gmstrftime($strftimeFormat, strtotime($rowData[$attrAlias]));
 
 
 
208
  } else {
209
  $value = $rowData[$attrAlias];
210
  }
@@ -220,40 +226,56 @@ class Mage_ImportExport_Model_Import_Entity_Customer_Address extends Mage_Import
220
 
221
  $entityId = $nextEntityId++;
222
 
223
- // entity table data
224
- $entityRows[] = array(
225
- 'entity_id' => $entityId,
226
- 'entity_type_id' => $this->_entityTypeId,
227
- 'parent_id' => $customerId,
228
- 'created_at' => now(),
229
- 'updated_at' => now()
230
- );
231
- // attribute values
232
- foreach ($this->_attributes as $attrAlias => $attrParams) {
233
- if (isset($addressAttributes[$attrParams['id']])) {
234
- $attributes[$attrParams['table']][$entityId][$attrParams['id']]
235
- = $addressAttributes[$attrParams['id']];
 
 
 
 
236
  }
237
- }
238
- // customer default addresses
239
- foreach (self::getDefaultAddressAttrMapping() as $colName => $customerAttrCode) {
240
- if (!empty($rowData[$colName])) {
241
- $attribute = $customer->getAttribute($customerAttrCode);
242
- $defaults[$attribute->getBackend()->getTable()][$customerId][$attribute->getId()] = $entityId;
 
243
  }
244
- }
245
- // let's try to find region ID
246
- if (!empty($rowData[$regionColName])) {
247
- $countryNormalized = strtolower($rowData[$countryColName]);
248
- $regionNormalized = strtolower($rowData[$regionColName]);
249
-
250
- if (isset($this->_countryRegions[$countryNormalized][$regionNormalized])) {
251
- $regionId = $this->_countryRegions[$countryNormalized][$regionNormalized];
252
- $attributes[$regionIdTable][$entityId][$regionIdAttrId] = $regionId;
253
- // set 'region' attribute value as default name
254
- $tbl = $this->_attributes[$regionColName]['table'];
255
- $regionColNameId = $this->_attributes[$regionColName]['id'];
256
- $attributes[$tbl][$entityId][$regionColNameId] = $this->_regions[$regionId];
 
 
 
 
 
 
 
 
 
 
 
257
  }
258
  }
259
  }
@@ -486,4 +508,22 @@ class Mage_ImportExport_Model_Import_Entity_Customer_Address extends Mage_Import
486
  }
487
  return $rowIsValid;
488
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
489
  }
175
  $regionIdTable = $regionIdAttr->getBackend()->getTable();
176
  $regionIdAttrId = $regionIdAttr->getId();
177
  $isAppendMode = Mage_ImportExport_Model_Import::BEHAVIOR_APPEND == $this->_customer->getBehavior();
178
+ $multiSelect = array();
179
 
180
  while ($bunch = $this->_dataSourceModel->getNextBunch()) {
181
  $entityRows = array();
183
  $defaults = array(); // customer default addresses (billing/shipping) data
184
 
185
  foreach ($bunch as $rowNum => $rowData) {
186
+ $rowScope = $this->_getRowScope($rowData);
187
+ if ($rowScope == Mage_ImportExport_Model_Import_Entity_Customer::SCOPE_DEFAULT) {
 
188
  $customerId = $this->_customer->getCustomerId(
189
  $rowData[Mage_ImportExport_Model_Import_Entity_Customer::COL_EMAIL],
190
  $rowData[Mage_ImportExport_Model_Import_Entity_Customer::COL_WEBSITE]
191
  );
192
  }
193
+ if ($rowScope != Mage_ImportExport_Model_Import_Entity_Customer::SCOPE_OPTIONS) {
194
+ $multiSelect = array();
195
+ }
196
+ if (!$customerId) {
197
  continue;
198
  }
199
 
208
  $value = $attrParams['options'][strtolower($rowData[$attrAlias])];
209
  } elseif ('datetime' == $attrParams['type']) {
210
  $value = gmstrftime($strftimeFormat, strtotime($rowData[$attrAlias]));
211
+ } elseif ('multiselect' == $attrParams['type']) {
212
+ $value = $attrParams['options'][strtolower($rowData[$attrAlias])];
213
+ $multiSelect[$attrParams['id']][] = $value;
214
  } else {
215
  $value = $rowData[$attrAlias];
216
  }
226
 
227
  $entityId = $nextEntityId++;
228
 
229
+ if ($rowScope == Mage_ImportExport_Model_Import_Entity_Customer::SCOPE_DEFAULT
230
+ || $rowScope == Mage_ImportExport_Model_Import_Entity_Customer::SCOPE_ADDRESS
231
+ ) {
232
+ // entity table data
233
+ $entityRows[] = array(
234
+ 'entity_id' => $entityId,
235
+ 'entity_type_id' => $this->_entityTypeId,
236
+ 'parent_id' => $customerId,
237
+ 'created_at' => now(),
238
+ 'updated_at' => now()
239
+ );
240
+ // attribute values
241
+ foreach ($this->_attributes as $attrAlias => $attrParams) {
242
+ if (isset($addressAttributes[$attrParams['id']])) {
243
+ $attributes[$attrParams['table']][$entityId][$attrParams['id']]
244
+ = $addressAttributes[$attrParams['id']];
245
+ }
246
  }
247
+ // customer default addresses
248
+ foreach (self::getDefaultAddressAttrMapping() as $colName => $customerAttrCode) {
249
+ if (!empty($rowData[$colName])) {
250
+ $attribute = $customer->getAttribute($customerAttrCode);
251
+ $backendTable = $attribute->getBackend()->getTable();
252
+ $defaults[$backendTable][$customerId][$attribute->getId()] = $entityId;
253
+ }
254
  }
255
+ // let's try to find region ID
256
+ if (!empty($rowData[$regionColName])) {
257
+ $countryNormalized = strtolower($rowData[$countryColName]);
258
+ $regionNormalized = strtolower($rowData[$regionColName]);
259
+
260
+ if (isset($this->_countryRegions[$countryNormalized][$regionNormalized])) {
261
+ $regionId = $this->_countryRegions[$countryNormalized][$regionNormalized];
262
+ $attributes[$regionIdTable][$entityId][$regionIdAttrId] = $regionId;
263
+ // set 'region' attribute value as default name
264
+ $tbl = $this->_attributes[$regionColName]['table'];
265
+ $regionColNameId = $this->_attributes[$regionColName]['id'];
266
+ $attributes[$tbl][$entityId][$regionColNameId] = $this->_regions[$regionId];
267
+ }
268
+ }
269
+ } else {
270
+ foreach (array_intersect_key($rowData, $this->_attributes) as $attrCode => $value) {
271
+ $attrParams = $this->_attributes[$attrCode];
272
+ if ($attrParams['type'] == 'multiselect') {
273
+ $value = '';
274
+ if (isset($multiSelect[$attrParams['id']])) {
275
+ $value = implode(',', $multiSelect[$attrParams['id']]);
276
+ }
277
+ $attributes[$this->_attributes[$attrCode]['table']][$entityId][$attrParams['id']] = $value;
278
+ }
279
  }
280
  }
281
  }
508
  }
509
  return $rowIsValid;
510
  }
511
+
512
+ /**
513
+ * Get current scope
514
+ *
515
+ * @param $rowData
516
+ * @return int
517
+ */
518
+ protected function _getRowScope($rowData)
519
+ {
520
+ if (strlen(trim($rowData[Mage_ImportExport_Model_Import_Entity_Customer::COL_EMAIL]))) {
521
+ $scope = Mage_ImportExport_Model_Import_Entity_Customer::SCOPE_DEFAULT;
522
+ } elseif (strlen(trim($rowData[Mage_ImportExport_Model_Import_Entity_Customer::COL_POSTCODE]))) {
523
+ $scope = Mage_ImportExport_Model_Import_Entity_Customer::SCOPE_ADDRESS;
524
+ } else {
525
+ $scope = Mage_ImportExport_Model_Import_Entity_Customer::SCOPE_OPTIONS;
526
+ }
527
+ return $scope;
528
+ }
529
  }
app/code/core/Mage/ImportExport/Model/Import/Entity/Product.php CHANGED
@@ -422,6 +422,13 @@ class Mage_ImportExport_Model_Import_Entity_Product extends Mage_ImportExport_Mo
422
  */
423
  protected $_fileUploader;
424
 
 
 
 
 
 
 
 
425
  /**
426
  * Constructor.
427
  *
@@ -823,6 +830,9 @@ class Mage_ImportExport_Model_Import_Entity_Product extends Mage_ImportExport_Mo
823
  );
824
 
825
  $alreadyUsedProductIds = array();
 
 
 
826
  while ($bunch = $this->_dataSourceModel->getNextBunch()) {
827
  $customOptions = array(
828
  'product_id' => array(),
@@ -833,7 +843,8 @@ class Mage_ImportExport_Model_Import_Entity_Product extends Mage_ImportExport_Mo
833
  $typeTitleTable => array(),
834
  $typeValueTable => array()
835
  );
836
-
 
837
  foreach ($bunch as $rowNum => $rowData) {
838
  $this->_filterRowData($rowData);
839
  if (!$this->isRowAllowedToImport($rowData, $rowNum)) {
@@ -844,6 +855,13 @@ class Mage_ImportExport_Model_Import_Entity_Product extends Mage_ImportExport_Mo
844
  } elseif (!isset($productId)) {
845
  continue;
846
  }
 
 
 
 
 
 
 
847
  if (!empty($rowData['_custom_option_store'])) {
848
  if (!isset($this->_storeCodeToId[$rowData['_custom_option_store']])) {
849
  continue;
@@ -920,41 +938,66 @@ class Mage_ImportExport_Model_Import_Entity_Product extends Mage_ImportExport_Mo
920
  }
921
  $prevOptionId = $nextOptionId++; // increment option id, but preserve value for $typeValueTable
922
  }
923
- if ($typeSpecific[$type] === true && !empty($rowData['_custom_option_row_title'])
924
- && empty($rowData['_custom_option_store'])) {
925
- // complex CO option row
926
- $customOptions[$typeValueTable][$prevOptionId][] = array(
927
- 'option_type_id' => $nextValueId,
928
- 'sort_order' => empty($rowData['_custom_option_row_sort'])
929
- ? 0 : abs($rowData['_custom_option_row_sort']),
930
- 'sku' => !empty($rowData['_custom_option_row_sku'])
931
- ? $rowData['_custom_option_row_sku'] : ''
932
- );
933
- if (!isset($customOptions[$typeTitleTable][$nextValueId][0])) { // ensure default title is set
934
- $customOptions[$typeTitleTable][$nextValueId][0] = $rowData['_custom_option_row_title'];
935
- }
936
- $customOptions[$typeTitleTable][$nextValueId][$storeId] = $rowData['_custom_option_row_title'];
937
 
938
- if (!empty($rowData['_custom_option_row_price'])) {
939
- $typePriceRow = array(
940
- 'price' => (float) rtrim($rowData['_custom_option_row_price'], '%'),
941
- 'price_type' => 'fixed'
 
 
 
 
 
942
  );
943
- if ('%' == substr($rowData['_custom_option_row_price'], -1)) {
944
- $typePriceRow['price_type'] = 'percent';
945
  }
946
- if ($priceIsGlobal) {
947
- $customOptions[$typePriceTable][$nextValueId][0] = $typePriceRow;
948
- } else {
949
- // ensure default price is set
950
- if (!isset($customOptions[$typePriceTable][$nextValueId][0])) {
 
 
 
 
 
 
951
  $customOptions[$typePriceTable][$nextValueId][0] = $typePriceRow;
 
 
 
 
 
 
952
  }
953
- $customOptions[$typePriceTable][$nextValueId][$storeId] = $typePriceRow;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
954
  }
955
  }
956
- $nextValueId++;
957
  }
 
958
  if (!empty($rowData['_custom_option_title'])) {
959
  if (!isset($customOptions[$titleTable][$prevOptionId][0])) { // ensure default title is set
960
  $customOptions[$titleTable][$prevOptionId][0] = $rowData['_custom_option_title'];
@@ -1105,13 +1148,13 @@ class Mage_ImportExport_Model_Import_Entity_Product extends Mage_ImportExport_Mo
1105
  $sku = $rowData[self::COL_SKU];
1106
  }
1107
  foreach ($this->_linkNameToId as $linkName => $linkId) {
1108
- $productId = $this->_newSku[$sku]['entity_id'];
1109
- $productIds[] = $productId;
1110
  if (isset($rowData[$linkName . 'sku'])) {
1111
- $linkedSku = $rowData[$linkName . 'sku'];
 
 
1112
 
1113
  if ((isset($this->_newSku[$linkedSku]) || isset($this->_oldSku[$linkedSku]))
1114
- && $linkedSku != $sku) {
1115
  if (isset($this->_newSku[$linkedSku])) {
1116
  $linkedId = $this->_newSku[$linkedSku]['entity_id'];
1117
  } else {
@@ -1189,19 +1232,21 @@ class Mage_ImportExport_Model_Import_Entity_Product extends Mage_ImportExport_Mo
1189
  );
1190
  }
1191
 
1192
- /*
1193
- If the store based values are not provided for a particular store,
1194
- we default to the default scope values.
1195
- In this case, remove all the existing store based values stored in the table.
1196
- */
1197
- $where = $this->_connection->quoteInto('store_id NOT IN (?)', array_keys($storeValues)) .
1198
- $this->_connection->quoteInto(' AND attribute_id = ?', $attributeId) .
1199
- $this->_connection->quoteInto(' AND entity_id = ?', $productId) .
1200
- $this->_connection->quoteInto(' AND entity_type_id = ?', $this->_entityTypeId);
1201
-
1202
- $this->_connection->delete(
1203
- $tableName, $where
1204
- );
 
 
1205
  }
1206
  }
1207
  $this->_connection->insertOnDuplicate($tableName, $tableData, array('value'));
@@ -1404,7 +1449,7 @@ class Mage_ImportExport_Model_Import_Entity_Product extends Mage_ImportExport_Mo
1404
  if (!is_null($productType)) {
1405
  $previousType = $productType;
1406
  }
1407
- if (!is_null($rowData[self::COL_ATTR_SET])) {
1408
  $previousAttributeSet = $rowData[Mage_ImportExport_Model_Import_Entity_Product::COL_ATTR_SET];
1409
  }
1410
  if (self::SCOPE_NULL == $rowScope) {
@@ -1511,6 +1556,8 @@ class Mage_ImportExport_Model_Import_Entity_Product extends Mage_ImportExport_Mo
1511
  // check website defaults already set
1512
  if (!isset($attributes[$attrTable][$rowSku][$attrId][$rowStore])) {
1513
  $storeIds = $this->_storeIdToWebsiteStoreIds[$rowStore];
 
 
1514
  }
1515
  } elseif (self::SCOPE_STORE == $attribute->getIsGlobal()) {
1516
  $storeIds = array($rowStore);
@@ -2136,4 +2183,29 @@ class Mage_ImportExport_Model_Import_Entity_Product extends Mage_ImportExport_Mo
2136
  }
2137
  return $productIds;
2138
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2139
  }
422
  */
423
  protected $_fileUploader;
424
 
425
+ /**
426
+ * url_key attribute id
427
+ *
428
+ * @var int
429
+ */
430
+ protected $_urlKeyAttributeId;
431
+
432
  /**
433
  * Constructor.
434
  *
830
  );
831
 
832
  $alreadyUsedProductIds = array();
833
+ $lastStoreId = null;
834
+ $lastProductId = null;
835
+ $currentValueId = null;
836
  while ($bunch = $this->_dataSourceModel->getNextBunch()) {
837
  $customOptions = array(
838
  'product_id' => array(),
843
  $typeTitleTable => array(),
844
  $typeValueTable => array()
845
  );
846
+ $flagNewOption = true;
847
+ $firstKeyOption = null;
848
  foreach ($bunch as $rowNum => $rowData) {
849
  $this->_filterRowData($rowData);
850
  if (!$this->isRowAllowedToImport($rowData, $rowNum)) {
855
  } elseif (!isset($productId)) {
856
  continue;
857
  }
858
+
859
+ if ($lastProductId != $productId) {
860
+ $lastStoreId = Mage_Catalog_Model_Abstract::DEFAULT_STORE_ID;
861
+ $currentValueId = null;
862
+ $lastProductId = $productId;
863
+ }
864
+
865
  if (!empty($rowData['_custom_option_store'])) {
866
  if (!isset($this->_storeCodeToId[$rowData['_custom_option_store']])) {
867
  continue;
938
  }
939
  $prevOptionId = $nextOptionId++; // increment option id, but preserve value for $typeValueTable
940
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
941
 
942
+ if ($typeSpecific[$type] === true && !empty($rowData['_custom_option_row_title'])) {
943
+ if (empty($rowData['_custom_option_store'])) {
944
+ // complex CO option row
945
+ $customOptions[$typeValueTable][$prevOptionId][] = array(
946
+ 'option_type_id' => $nextValueId,
947
+ 'sort_order' => empty($rowData['_custom_option_row_sort'])
948
+ ? 0 : abs($rowData['_custom_option_row_sort']),
949
+ 'sku' => !empty($rowData['_custom_option_row_sku'])
950
+ ? $rowData['_custom_option_row_sku'] : ''
951
  );
952
+ if (!isset($customOptions[$typeTitleTable][$nextValueId][0])) { // ensure default title is set
953
+ $customOptions[$typeTitleTable][$nextValueId][0] = $rowData['_custom_option_row_title'];
954
  }
955
+ $customOptions[$typeTitleTable][$nextValueId][$storeId] = $rowData['_custom_option_row_title'];
956
+
957
+ if (!empty($rowData['_custom_option_row_price'])) {
958
+ $typePriceRow = array(
959
+ 'price' => (float) rtrim($rowData['_custom_option_row_price'], '%'),
960
+ 'price_type' => 'fixed'
961
+ );
962
+ if ('%' == substr($rowData['_custom_option_row_price'], -1)) {
963
+ $typePriceRow['price_type'] = 'percent';
964
+ }
965
+ if ($priceIsGlobal) {
966
  $customOptions[$typePriceTable][$nextValueId][0] = $typePriceRow;
967
+ } else {
968
+ // ensure default price is set
969
+ if (!isset($customOptions[$typePriceTable][$nextValueId][0])) {
970
+ $customOptions[$typePriceTable][$nextValueId][0] = $typePriceRow;
971
+ }
972
+ $customOptions[$typePriceTable][$nextValueId][$storeId] = $typePriceRow;
973
  }
974
+ }
975
+ if ($flagNewOption) {
976
+ $firstKeyOption = $nextValueId;
977
+ $flagNewOption = false;
978
+ }
979
+ $nextValueId++;
980
+ } else {
981
+ $flagNewOption = true;
982
+ if ($lastStoreId != $storeId) {
983
+ if (!$firstKeyOption) {
984
+ reset($customOptions[$typeTitleTable]);
985
+ $firstKeyOption = key($customOptions[$typeTitleTable]);
986
+ }
987
+ $currentValueId = $firstKeyOption;
988
+ $lastStoreId = $storeId;
989
+ } else {
990
+ $currentValueId++;
991
+ }
992
+
993
+ $defaultValue = $customOptions[$typeTitleTable][$currentValueId][0];
994
+ if ($defaultValue != $rowData['_custom_option_row_title']) {
995
+ $customOptions[$typeTitleTable][$currentValueId][$storeId]
996
+ = $rowData['_custom_option_row_title'];
997
  }
998
  }
 
999
  }
1000
+
1001
  if (!empty($rowData['_custom_option_title'])) {
1002
  if (!isset($customOptions[$titleTable][$prevOptionId][0])) { // ensure default title is set
1003
  $customOptions[$titleTable][$prevOptionId][0] = $rowData['_custom_option_title'];
1148
  $sku = $rowData[self::COL_SKU];
1149
  }
1150
  foreach ($this->_linkNameToId as $linkName => $linkId) {
 
 
1151
  if (isset($rowData[$linkName . 'sku'])) {
1152
+ $productId = $this->_newSku[$sku]['entity_id'];
1153
+ $productIds[] = $productId;
1154
+ $linkedSku = $rowData[$linkName . 'sku'];
1155
 
1156
  if ((isset($this->_newSku[$linkedSku]) || isset($this->_oldSku[$linkedSku]))
1157
+ && $linkedSku != $sku) {
1158
  if (isset($this->_newSku[$linkedSku])) {
1159
  $linkedId = $this->_newSku[$linkedSku]['entity_id'];
1160
  } else {
1232
  );
1233
  }
1234
 
1235
+ if ($attributeId == $this->_getUrlKeyAttributeId()) {
1236
+ /*
1237
+ If the store based values are not provided for a particular store,
1238
+ we default to the default scope values.
1239
+ In this case, remove all the existing store based values stored in the table.
1240
+ */
1241
+ $where = $this->_connection->quoteInto('store_id NOT IN (?)', array_keys($storeValues)) .
1242
+ $this->_connection->quoteInto(' AND attribute_id = ?', $attributeId) .
1243
+ $this->_connection->quoteInto(' AND entity_id = ?', $productId) .
1244
+ $this->_connection->quoteInto(' AND entity_type_id = ?', $this->_entityTypeId);
1245
+
1246
+ $this->_connection->delete(
1247
+ $tableName, $where
1248
+ );
1249
+ }
1250
  }
1251
  }
1252
  $this->_connection->insertOnDuplicate($tableName, $tableData, array('value'));
1449
  if (!is_null($productType)) {
1450
  $previousType = $productType;
1451
  }
1452
+ if (isset($rowData[self::COL_ATTR_SET]) && !is_null($rowData[self::COL_ATTR_SET])) {
1453
  $previousAttributeSet = $rowData[Mage_ImportExport_Model_Import_Entity_Product::COL_ATTR_SET];
1454
  }
1455
  if (self::SCOPE_NULL == $rowScope) {
1556
  // check website defaults already set
1557
  if (!isset($attributes[$attrTable][$rowSku][$attrId][$rowStore])) {
1558
  $storeIds = $this->_storeIdToWebsiteStoreIds[$rowStore];
1559
+ } else {
1560
+ $storeIds = array($rowStore);
1561
  }
1562
  } elseif (self::SCOPE_STORE == $attribute->getIsGlobal()) {
1563
  $storeIds = array($rowStore);
2183
  }
2184
  return $productIds;
2185
  }
2186
+
2187
+ /**
2188
+ * Get product url_key attribute id
2189
+ *
2190
+ * @return null|int
2191
+ */
2192
+ protected function _getUrlKeyAttributeId()
2193
+ {
2194
+ if ($this->_urlKeyAttributeId === null) {
2195
+ $adapter = $this->getConnection();
2196
+ $resource = $this->getResourceModel('eav/entity_attribute');
2197
+
2198
+ $select = $adapter->select()
2199
+ ->from(
2200
+ $resource->getMainTable(),
2201
+ array('attribute_id')
2202
+ )
2203
+ ->where('attribute_code = ?', 'url_key')
2204
+ ->where('entity_type_id = ?', $this->_entityTypeId);
2205
+
2206
+ $this->_urlKeyAttributeId = $adapter->fetchOne($select);
2207
+ }
2208
+
2209
+ return $this->_urlKeyAttributeId;
2210
+ }
2211
  }
app/code/core/Mage/ImportExport/Model/Import/Entity/Product/Type/Configurable.php CHANGED
@@ -230,18 +230,34 @@ class Mage_ImportExport_Model_Import_Entity_Product_Type_Configurable
230
  ->getNode('global/catalog/product/type/configurable/allow_product_types')->children() as $type) {
231
  $allowProductTypes[] = $type->getName();
232
  }
233
- foreach (Mage::getResourceModel('catalog/product_collection')
 
234
  ->addFieldToFilter('type_id', $allowProductTypes)
235
- ->addAttributeToSelect(array_keys($this->_superAttributes)) as $product) {
236
- $attrSetName = $attrSetIdToName[$product->getAttributeSetId()];
237
-
238
- $data = array_intersect_key(
239
- $product->getData(),
240
- $this->_superAttributes
241
- );
242
- foreach ($data as $attrCode => $value) {
243
- $attrId = $this->_superAttributes[$attrCode]['id'];
244
- $this->_skuSuperAttributeValues[$attrSetName][$product->getId()][$attrId] = $value;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
245
  }
246
  }
247
  }
230
  ->getNode('global/catalog/product/type/configurable/allow_product_types')->children() as $type) {
231
  $allowProductTypes[] = $type->getName();
232
  }
233
+ /** @var Mage_Catalog_Model_Resource_Product_Collection $collection */
234
+ $collection = Mage::getResourceModel('catalog/product_collection')
235
  ->addFieldToFilter('type_id', $allowProductTypes)
236
+ ->addAttributeToSelect(array_keys($this->_superAttributes));
237
+
238
+ $collectionSize = $collection->getSize();
239
+ if ($collectionSize) {
240
+ $configPageSize = Mage::helper('importexport')->getImportConfigurablePageSize();
241
+ $pageSize = ($configPageSize > 0) ? $configPageSize : $collectionSize;
242
+ $page = 0;
243
+ $collection->setPageSize($pageSize);
244
+ while ($pageSize * $page < $collectionSize) {
245
+ $page++;
246
+ $collection->setCurPage($page);
247
+
248
+ foreach ($collection as $product) {
249
+ $attrSetName = $attrSetIdToName[$product->getAttributeSetId()];
250
+
251
+ $data = array_intersect_key(
252
+ $product->getData(),
253
+ $this->_superAttributes
254
+ );
255
+ foreach ($data as $attrCode => $value) {
256
+ $attrId = $this->_superAttributes[$attrCode]['id'];
257
+ $this->_skuSuperAttributeValues[$attrSetName][$product->getId()][$attrId] = $value;
258
+ }
259
+ }
260
+ $collection->clear();
261
  }
262
  }
263
  }
app/code/core/Mage/ImportExport/Model/Import/Uploader.php CHANGED
@@ -132,7 +132,7 @@ class Mage_ImportExport_Model_Import_Uploader extends Mage_Core_Model_File_Uploa
132
  //run validate callbacks
133
  foreach ($this->_validateCallbacks as $params) {
134
  if (is_object($params['object']) && method_exists($params['object'], $params['method'])) {
135
- $params['object']->$params['method']($filePath);
136
  }
137
  }
138
  }
132
  //run validate callbacks
133
  foreach ($this->_validateCallbacks as $params) {
134
  if (is_object($params['object']) && method_exists($params['object'], $params['method'])) {
135
+ $params['object']->{$params['method']}($filePath);
136
  }
137
  }
138
  }
app/code/core/Mage/ImportExport/controllers/Adminhtml/ExportController.php CHANGED
@@ -81,9 +81,12 @@ class Mage_ImportExport_Adminhtml_ExportController extends Mage_Adminhtml_Contro
81
  $model = Mage::getModel('importexport/export');
82
  $model->setData($this->getRequest()->getParams());
83
 
 
 
 
84
  return $this->_prepareDownloadResponse(
85
  $model->getFileName(),
86
- $model->export(),
87
  $model->getContentType()
88
  );
89
  } catch (Mage_Core_Exception $e) {
81
  $model = Mage::getModel('importexport/export');
82
  $model->setData($this->getRequest()->getParams());
83
 
84
+ $result = $model->exportFile();
85
+ $result['type'] = 'filename';
86
+
87
  return $this->_prepareDownloadResponse(
88
  $model->getFileName(),
89
+ $result,
90
  $model->getContentType()
91
  );
92
  } catch (Mage_Core_Exception $e) {
app/code/core/Mage/ImportExport/etc/config.xml CHANGED
@@ -139,6 +139,9 @@
139
  <export_csv>
140
  <escaping>1</escaping>
141
  </export_csv>
 
 
 
142
  </system>
143
  <general>
144
  <file>
139
  <export_csv>
140
  <escaping>1</escaping>
141
  </export_csv>
142
+ <import_csv>
143
+ <configurable_page_size>1000</configurable_page_size>
144
+ </import_csv>
145
  </system>
146
  <general>
147
  <file>
app/code/core/Mage/ImportExport/etc/system.xml CHANGED
@@ -48,6 +48,25 @@
48
  </escaping>
49
  </fields>
50
  </export_csv>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  </groups>
52
  </system>
53
  </sections>
48
  </escaping>
49
  </fields>
50
  </export_csv>
51
+ <import_csv translate="label">
52
+ <label>Import CSV</label>
53
+ <show_in_default>1</show_in_default>
54
+ <show_in_website>1</show_in_website>
55
+ <show_in_store>1</show_in_store>
56
+ <sort_order>500</sort_order>
57
+ <fields>
58
+ <configurable_page_size translate="label,comment">
59
+ <label>Page size for import configurable products</label>
60
+ <frontend_type>text</frontend_type>
61
+ <validate>validate-number</validate>
62
+ <sort_order>1</sort_order>
63
+ <show_in_default>1</show_in_default>
64
+ <show_in_website>0</show_in_website>
65
+ <show_in_store>0</show_in_store>
66
+ <comment>This option will be ignore if it set less than 1.</comment>
67
+ </configurable_page_size>
68
+ </fields>
69
+ </import_csv>
70
  </groups>
71
  </system>
72
  </sections>
app/code/core/Mage/Oauth/Model/Server.php CHANGED
@@ -328,10 +328,10 @@ class Mage_Oauth_Model_Server
328
  if (self::REQUEST_TOKEN == $this->_requestType) {
329
  $this->_validateVerifierParam();
330
 
331
- if ($this->_token->getVerifier() != $this->_protocolParams['oauth_verifier']) {
332
  $this->_throwException('', self::ERR_VERIFIER_INVALID);
333
  }
334
- if ($this->_token->getConsumerId() != $this->_consumer->getId()) {
335
  $this->_throwException('', self::ERR_TOKEN_REJECTED);
336
  }
337
  if (Mage_Oauth_Model_Token::TYPE_REQUEST != $this->_token->getType()) {
@@ -544,7 +544,7 @@ class Mage_Oauth_Model_Server
544
  $this->_request->getScheme() . '://' . $this->_request->getHttpHost() . $this->_request->getRequestUri()
545
  );
546
 
547
- if ($calculatedSign != $this->_protocolParams['oauth_signature']) {
548
  $this->_throwException('', self::ERR_SIGNATURE_INVALID);
549
  }
550
  }
328
  if (self::REQUEST_TOKEN == $this->_requestType) {
329
  $this->_validateVerifierParam();
330
 
331
+ if (!hash_equals($this->_token->getVerifier(), $this->_protocolParams['oauth_verifier'])) {
332
  $this->_throwException('', self::ERR_VERIFIER_INVALID);
333
  }
334
+ if (!hash_equals($this->_token->getConsumerId(), $this->_consumer->getId())) {
335
  $this->_throwException('', self::ERR_TOKEN_REJECTED);
336
  }
337
  if (Mage_Oauth_Model_Token::TYPE_REQUEST != $this->_token->getType()) {
544
  $this->_request->getScheme() . '://' . $this->_request->getHttpHost() . $this->_request->getRequestUri()
545
  );
546
 
547
+ if (!hash_equals($calculatedSign, $this->_protocolParams['oauth_signature'])) {
548
  $this->_throwException('', self::ERR_SIGNATURE_INVALID);
549
  }
550
  }
app/code/core/Mage/Paygate/Model/Authorizenet.php CHANGED
@@ -1273,8 +1273,10 @@ class Mage_Paygate_Model_Authorizenet extends Mage_Payment_Model_Method_Cc
1273
  $uri = $this->getConfigData('cgi_url');
1274
  $client->setUri($uri ? $uri : self::CGI_URL);
1275
  $client->setConfig(array(
1276
- 'maxredirects'=>0,
1277
- 'timeout'=>30,
 
 
1278
  //'ssltransport' => 'tcp',
1279
  ));
1280
  foreach ($request->getData() as $key => $value) {
@@ -1543,7 +1545,11 @@ class Mage_Paygate_Model_Authorizenet extends Mage_Payment_Model_Method_Cc
1543
  $uri = $this->getConfigData('cgi_url_td');
1544
  $uri = $uri ? $uri : self::CGI_URL_TD;
1545
  $client->setUri($uri);
1546
- $client->setConfig(array('timeout'=>45));
 
 
 
 
1547
  $client->setHeaders(array('Content-Type: text/xml'));
1548
  $client->setMethod(Zend_Http_Client::POST);
1549
  $client->setRawData($requestBody);
1273
  $uri = $this->getConfigData('cgi_url');
1274
  $client->setUri($uri ? $uri : self::CGI_URL);
1275
  $client->setConfig(array(
1276
+ 'maxredirects' => 0,
1277
+ 'timeout' => 30,
1278
+ 'verifyhost' => 2,
1279
+ 'verifypeer' => true,
1280
  //'ssltransport' => 'tcp',
1281
  ));
1282
  foreach ($request->getData() as $key => $value) {
1545
  $uri = $this->getConfigData('cgi_url_td');
1546
  $uri = $uri ? $uri : self::CGI_URL_TD;
1547
  $client->setUri($uri);
1548
+ $client->setConfig(array(
1549
+ 'timeout' => 45,
1550
+ 'verifyhost' => 2,
1551
+ 'verifypeer' => true,
1552
+ ));
1553
  $client->setHeaders(array('Content-Type: text/xml'));
1554
  $client->setMethod(Zend_Http_Client::POST);
1555
  $client->setRawData($requestBody);
app/code/core/Mage/Payment/Block/Info/Checkmo.php CHANGED
@@ -70,7 +70,13 @@ class Mage_Payment_Block_Info_Checkmo extends Mage_Payment_Block_Info
70
  */
71
  protected function _convertAdditionalData()
72
  {
73
- $details = @unserialize($this->getInfo()->getAdditionalData());
 
 
 
 
 
 
74
  if (is_array($details)) {
75
  $this->_payableTo = isset($details['payable_to']) ? (string) $details['payable_to'] : '';
76
  $this->_mailingAddress = isset($details['mailing_address']) ? (string) $details['mailing_address'] : '';
@@ -80,7 +86,7 @@ class Mage_Payment_Block_Info_Checkmo extends Mage_Payment_Block_Info
80
  }
81
  return $this;
82
  }
83
-
84
  public function toPdf()
85
  {
86
  $this->setTemplate('payment/info/pdf/checkmo.phtml');
70
  */
71
  protected function _convertAdditionalData()
72
  {
73
+ $details = false;
74
+ try {
75
+ $details = Mage::helper('core/unserializeArray')
76
+ ->unserialize($this->getInfo()->getAdditionalData());
77
+ } catch (Exception $e) {
78
+ Mage::logException($e);
79
+ }
80
  if (is_array($details)) {
81
  $this->_payableTo = isset($details['payable_to']) ? (string) $details['payable_to'] : '';
82
  $this->_mailingAddress = isset($details['mailing_address']) ? (string) $details['mailing_address'] : '';
86
  }
87
  return $this;
88
  }
89
+
90
  public function toPdf()
91
  {
92
  $this->setTemplate('payment/info/pdf/checkmo.phtml');
app/code/core/Mage/Payment/Model/Method/Cc.php CHANGED
@@ -122,7 +122,7 @@ class Mage_Payment_Model_Method_Cc extends Mage_Payment_Model_Method_Abstract
122
  // Visa
123
  'VI' => '/^4[0-9]{12}([0-9]{3})?$/',
124
  // Master Card
125
- 'MC' => '/^5[1-5][0-9]{14}$/',
126
  // American Express
127
  'AE' => '/^3[47][0-9]{13}$/',
128
  // Discover Network
122
  // Visa
123
  'VI' => '/^4[0-9]{12}([0-9]{3})?$/',
124
  // Master Card
125
+ 'MC' => '/^(5[1-5][0-9]{14}|2(22[1-9][0-9]{12}|2[3-9][0-9]{13}|[3-6][0-9]{14}|7[0-1][0-9]{13}|720[0-9]{12}))$/',
126
  // American Express
127
  'AE' => '/^3[47][0-9]{13}$/',
128
  // Discover Network
app/code/core/Mage/Paypal/Model/Api/Nvp.php CHANGED
@@ -520,7 +520,8 @@ class Mage_Paypal_Model_Api_Nvp extends Mage_Paypal_Model_Api_Abstract
520
  * @var array
521
  */
522
  protected $_requiredResponseParams = array(
523
- self::DO_DIRECT_PAYMENT => array('ACK', 'CORRELATIONID', 'AMT')
 
524
  );
525
 
526
  /**
520
  * @var array
521
  */
522
  protected $_requiredResponseParams = array(
523
+ self::DO_DIRECT_PAYMENT => array('ACK', 'CORRELATIONID', 'AMT'),
524
+ self::DO_EXPRESS_CHECKOUT_PAYMENT => array('ACK', 'CORRELATIONID', 'AMT'),
525
  );
526
 
527
  /**
app/code/core/Mage/Paypal/Model/Express/Checkout.php CHANGED
@@ -947,7 +947,7 @@ class Mage_Paypal_Model_Express_Checkout
947
  $shipping = $quote->isVirtual() ? null : $quote->getShippingAddress();
948
 
949
  $customerId = $this->_lookupCustomerId();
950
- if ($customerId) {
951
  $this->getCustomerSession()->loginById($customerId);
952
  return $this->_prepareCustomerQuote();
953
  }
@@ -1063,4 +1063,26 @@ class Mage_Paypal_Model_Express_Checkout
1063
  {
1064
  return $this->_customerSession;
1065
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1066
  }
947
  $shipping = $quote->isVirtual() ? null : $quote->getShippingAddress();
948
 
949
  $customerId = $this->_lookupCustomerId();
950
+ if ($customerId && !$this->_customerEmailExists($quote->getCustomerEmail())) {
951
  $this->getCustomerSession()->loginById($customerId);
952
  return $this->_prepareCustomerQuote();
953
  }
1063
  {
1064
  return $this->_customerSession;
1065
  }
1066
+
1067
+ /**
1068
+ * Check if customer email exists
1069
+ *
1070
+ * @param string $email
1071
+ * @return bool
1072
+ */
1073
+ protected function _customerEmailExists($email)
1074
+ {
1075
+ $result = false;
1076
+ $customer = Mage::getModel('customer/customer');
1077
+ $websiteId = Mage::app()->getStore()->getWebsiteId();
1078
+ if (!is_null($websiteId)) {
1079
+ $customer->setWebsiteId($websiteId);
1080
+ }
1081
+ $customer->loadByEmail($email);
1082
+ if (!is_null($customer->getId())) {
1083
+ $result = true;
1084
+ }
1085
+
1086
+ return $result;
1087
+ }
1088
  }
app/code/core/Mage/Paypal/Model/Resource/Payment/Transaction.php CHANGED
@@ -52,6 +52,26 @@ class Mage_Paypal_Model_Resource_Payment_Transaction extends Mage_Core_Model_Res
52
  $this->_init('paypal/payment_transaction', 'transaction_id');
53
  }
54
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  /**
56
  * Load the transaction object by specified txn_id
57
  *
52
  $this->_init('paypal/payment_transaction', 'transaction_id');
53
  }
54
 
55
+ /**
56
+ * @see Mage_Core_Model_Resource_Abstract::_unserializeField()
57
+ */
58
+ protected function _unserializeField(Varien_Object $object, $field, $defaultValue = null)
59
+ {
60
+ $value = $object->getData($field);
61
+ if (empty($value)) {
62
+ $object->setData($field, $defaultValue);
63
+ } elseif (!is_array($value) && !is_object($value)) {
64
+ $unserializedValue = false;
65
+ try {
66
+ $unserializedValue = Mage::helper('core/unserializeArray')
67
+ ->unserialize($value);
68
+ } catch (Exception $e) {
69
+ Mage::logException($e);
70
+ }
71
+ $object->setData($field, $unserializedValue);
72
+ }
73
+ }
74
+
75
  /**
76
  * Load the transaction object by specified txn_id
77
  *
app/code/core/Mage/Persistent/Model/Persistent/Config.php CHANGED
@@ -71,7 +71,9 @@ class Mage_Persistent_Model_Persistent_Config
71
  if (is_null($this->_xmlConfig)) {
72
  $filePath = $this->_configFilePath;
73
  if (!is_file($filePath) || !is_readable($filePath)) {
74
- Mage::throwException(Mage::helper('persistent')->__('Cannot load configuration from file %s.', $filePath));
 
 
75
  }
76
  $xml = file_get_contents($filePath);
77
  $this->_xmlConfig = new Varien_Simplexml_Element($xml);
71
  if (is_null($this->_xmlConfig)) {
72
  $filePath = $this->_configFilePath;
73
  if (!is_file($filePath) || !is_readable($filePath)) {
74
+ $io = new Varien_Io_File();
75
+ Mage::throwException(Mage::helper('persistent')->__('Cannot load configuration from file %s.',
76
+ $io->getFilteredPath($filePath)));
77
  }
78
  $xml = file_get_contents($filePath);
79
  $this->_xmlConfig = new Varien_Simplexml_Element($xml);
app/code/core/Mage/Reports/Model/Product/Index/Abstract.php CHANGED
@@ -65,7 +65,16 @@ abstract class Mage_Reports_Model_Product_Index_Abstract extends Mage_Core_Model
65
  // Thanks to new performance tweaks it is possible to switch off visitor logging
66
  // This check is needed to make sure report record has either visitor id or customer id
67
  if ($this->hasVisitorId() || $this->hasCustomerId()) {
68
- parent::save();
 
 
 
 
 
 
 
 
 
69
  }
70
 
71
  return $this;
@@ -223,7 +232,16 @@ abstract class Mage_Reports_Model_Product_Index_Abstract extends Mage_Core_Model
223
  */
224
  public function registerIds($productIds)
225
  {
226
- $this->_getResource()->registerIds($this, $productIds);
 
 
 
 
 
 
 
 
 
227
  $this->_getSession()->unsData($this->_countCacheKey);
228
  return $this;
229
  }
65
  // Thanks to new performance tweaks it is possible to switch off visitor logging
66
  // This check is needed to make sure report record has either visitor id or customer id
67
  if ($this->hasVisitorId() || $this->hasCustomerId()) {
68
+ try {
69
+ parent::save();
70
+ } catch (Exception $exception) {
71
+ if ($this->hasCustomerId()) {
72
+ $this->updateCustomerFromVisitor();
73
+ parent::save();
74
+ } else {
75
+ Mage::logException($exception);
76
+ }
77
+ }
78
  }
79
 
80
  return $this;
232
  */
233
  public function registerIds($productIds)
234
  {
235
+ try {
236
+ $this->_getResource()->registerIds($this, $productIds);
237
+ } catch (Exception $exception) {
238
+ if ($this->hasCustomerId()) {
239
+ $this->updateCustomerFromVisitor();
240
+ $this->_getResource()->registerIds($this, $productIds);
241
+ } else {
242
+ Mage::logException($exception);
243
+ }
244
+ }
245
  $this->_getSession()->unsData($this->_countCacheKey);
246
  return $this;
247
  }
app/code/core/Mage/Reports/Model/Resource/Helper/Mysql4.php CHANGED
@@ -77,22 +77,44 @@ class Mage_Reports_Model_Resource_Helper_Mysql4 extends Mage_Core_Model_Resource
77
  }
78
 
79
  $columns = array(
80
- 'period' => 't.period',
81
- 'store_id' => 't.store_id',
82
- 'product_id' => 't.product_id',
83
- 'product_name' => 't.product_name',
84
- 'product_price' => 't.product_price',
85
  );
86
 
87
  if ($type == 'day') {
88
  $columns['id'] = 't.id'; // to speed-up insert on duplicate key update
89
  }
90
 
 
 
 
 
 
91
  $cols = array_keys($columns);
92
  $cols['total_qty'] = new Zend_Db_Expr('SUM(t.' . $column . ')');
 
93
  $periodSubSelect->from(array('t' => $mainTable), $cols)
94
- ->group(array('t.store_id', $periodCol, 't.product_id'))
95
- ->order(array('t.store_id', $periodCol, 'total_qty DESC'));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
 
97
  $cols = $columns;
98
  $cols[$column] = 't.total_qty';
77
  }
78
 
79
  $columns = array(
80
+ 'period' => 't.period',
81
+ 'store_id' => 't.store_id',
82
+ 'product_id' => 't.product_id',
83
+ 'product_name' => 't.product_name',
84
+ 'product_price' => 't.product_price',
85
  );
86
 
87
  if ($type == 'day') {
88
  $columns['id'] = 't.id'; // to speed-up insert on duplicate key update
89
  }
90
 
91
+ if ($column == 'qty_ordered')
92
+ {
93
+ $columns['product_type_id'] = 't.product_type_id';
94
+ }
95
+
96
  $cols = array_keys($columns);
97
  $cols['total_qty'] = new Zend_Db_Expr('SUM(t.' . $column . ')');
98
+
99
  $periodSubSelect->from(array('t' => $mainTable), $cols)
100
+ ->group(array('t.store_id', $periodCol, 't.product_id'));
101
+
102
+ if ($column == 'qty_ordered') {
103
+ $productTypesInExpr = $adapter->quoteInto(
104
+ 't.product_type_id IN (?)',
105
+ Mage_Catalog_Model_Product_Type::getCompositeTypes()
106
+ );
107
+ $periodSubSelect->order(
108
+ array(
109
+ 't.store_id',
110
+ $periodCol,
111
+ $adapter->getCheckSql($productTypesInExpr, 1, 0),
112
+ 'total_qty DESC'
113
+ )
114
+ );
115
+ } else {
116
+ $periodSubSelect->order(array('t.store_id', $periodCol, 'total_qty DESC'));
117
+ }
118
 
119
  $cols = $columns;
120
  $cols[$column] = 't.total_qty';
app/code/core/Mage/Rss/Controller/Abstract.php ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magento.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magento.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Rss
23
+ * @copyright Copyright (c) 2006-2016 X.commerce, Inc. and affiliates (http://www.magento.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+ /**
28
+ * Rss abstract controller
29
+ *
30
+ * @category Mage
31
+ * @package Mage_Rss
32
+ * @author Magento Core Team <core@magentocommerce.com>
33
+ */
34
+ class Mage_Rss_Controller_Abstract extends Mage_Core_Controller_Front_Action
35
+ {
36
+ /**
37
+ * Check feed enabled in config
38
+ *
39
+ * @param string $code
40
+ * @return boolean
41
+ */
42
+ protected function isFeedEnable($code)
43
+ {
44
+ return $this->_getHelper('rss')->isRssEnabled()
45
+ && Mage::getStoreConfig('rss/'. $code);
46
+ }
47
+
48
+ /**
49
+ * Do check feed enabled and prepare response
50
+ *
51
+ * @param string $code
52
+ * @return boolean
53
+ */
54
+ protected function checkFeedEnable($code)
55
+ {
56
+ if ($this->isFeedEnable($code)) {
57
+ $this->getResponse()->setHeader('Content-type', 'text/xml; charset=UTF-8');
58
+ return true;
59
+ } else {
60
+ $this->getResponse()->setHeader('HTTP/1.1', '404 Not Found');
61
+ $this->getResponse()->setHeader('Status', '404 File not found');
62
+ $this->_forward('nofeed', 'index', 'rss');
63
+ return false;
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Retrieve helper instance
69
+ *
70
+ * @param string $name
71
+ * @return Mage_Core_Helper_Abstract
72
+ */
73
+ protected function _getHelper($name)
74
+ {
75
+ return Mage::helper($name);
76
+ }
77
+ }
app/code/core/Mage/Rss/controllers/CatalogController.php CHANGED
@@ -32,55 +32,41 @@
32
  * @author Magento Core Team <core@magentocommerce.com>
33
  */
34
 
35
- class Mage_Rss_CatalogController extends Mage_Core_Controller_Front_Action
36
  {
37
- protected function isFeedEnable($code)
38
- {
39
- return Mage::getStoreConfig('rss/catalog/'.$code);
40
- }
41
-
42
- protected function checkFeedEnable($code)
43
- {
44
- if ($this->isFeedEnable($code)) {
45
- $this->getResponse()->setHeader('Content-type', 'text/xml; charset=UTF-8');
46
- return true;
47
- } else {
48
- $this->getResponse()->setHeader('HTTP/1.1','404 Not Found');
49
- $this->getResponse()->setHeader('Status','404 File not found');
50
- $this->_forward('nofeed','index','rss');
51
- return false;
52
- }
53
- }
54
-
55
  public function newAction()
56
  {
57
- $this->checkFeedEnable('new');
58
- $this->loadLayout(false);
59
- $this->renderLayout();
 
60
  }
61
 
62
  public function specialAction()
63
  {
64
- $this->checkFeedEnable('special');
65
- $this->loadLayout(false);
66
- $this->renderLayout();
 
67
  }
68
 
69
  public function salesruleAction()
70
  {
71
- $this->checkFeedEnable('salesrule');
72
- $this->loadLayout(false);
73
- $this->renderLayout();
 
74
  }
75
 
76
  public function tagAction()
77
  {
78
- if ($this->checkFeedEnable('tag')) {
79
  $tagName = urldecode($this->getRequest()->getParam('tagName'));
80
  $tagModel = Mage::getModel('tag/tag');
81
  $tagModel->loadByName($tagName);
82
  if ($tagModel->getId() && $tagModel->getStatus()==$tagModel->getApprovedStatus()) {
83
  Mage::register('tag_model', $tagModel);
 
84
  $this->loadLayout(false);
85
  $this->renderLayout();
86
  return;
@@ -91,21 +77,23 @@ class Mage_Rss_CatalogController extends Mage_Core_Controller_Front_Action
91
 
92
  public function notifystockAction()
93
  {
94
- $this->getResponse()->setHeader('Content-type', 'text/xml; charset=UTF-8');
95
- $this->loadLayout(false);
96
- $this->renderLayout();
 
97
  }
98
 
99
  public function reviewAction()
100
  {
101
- $this->getResponse()->setHeader('Content-type', 'text/xml; charset=UTF-8');
102
- $this->loadLayout(false);
103
- $this->renderLayout();
 
104
  }
105
 
106
  public function categoryAction()
107
  {
108
- if ($this->checkFeedEnable('category')) {
109
  $this->loadLayout(false);
110
  $this->renderLayout();
111
  }
@@ -119,11 +107,11 @@ class Mage_Rss_CatalogController extends Mage_Core_Controller_Front_Action
119
  public function preDispatch()
120
  {
121
  $action = strtolower($this->getRequest()->getActionName());
122
- if ($action == 'notifystock') {
123
  $this->_currentArea = 'adminhtml';
124
  Mage::helper('rss')->authAdmin('catalog/products');
125
  }
126
- if ($action == 'review') {
127
  $this->_currentArea = 'adminhtml';
128
  Mage::helper('rss')->authAdmin('catalog/reviews_ratings');
129
  }
32
  * @author Magento Core Team <core@magentocommerce.com>
33
  */
34
 
35
+ class Mage_Rss_CatalogController extends Mage_Rss_Controller_Abstract
36
  {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  public function newAction()
38
  {
39
+ if ($this->checkFeedEnable('catalog/new')) {
40
+ $this->loadLayout(false);
41
+ $this->renderLayout();
42
+ }
43
  }
44
 
45
  public function specialAction()
46
  {
47
+ if ($this->checkFeedEnable('catalog/special')) {
48
+ $this->loadLayout(false);
49
+ $this->renderLayout();
50
+ }
51
  }
52
 
53
  public function salesruleAction()
54
  {
55
+ if ($this->checkFeedEnable('catalog/salesrule')) {
56
+ $this->loadLayout(false);
57
+ $this->renderLayout();
58
+ }
59
  }
60
 
61
  public function tagAction()
62
  {
63
+ if ($this->isFeedEnable('catalog/tag')) {
64
  $tagName = urldecode($this->getRequest()->getParam('tagName'));
65
  $tagModel = Mage::getModel('tag/tag');
66
  $tagModel->loadByName($tagName);
67
  if ($tagModel->getId() && $tagModel->getStatus()==$tagModel->getApprovedStatus()) {
68
  Mage::register('tag_model', $tagModel);
69
+ $this->getResponse()->setHeader('Content-type', 'text/xml; charset=UTF-8');
70
  $this->loadLayout(false);
71
  $this->renderLayout();
72
  return;
77
 
78
  public function notifystockAction()
79
  {
80
+ if ($this->checkFeedEnable('catalog/notifystock')) {
81
+ $this->loadLayout(false);
82
+ $this->renderLayout();
83
+ }
84
  }
85
 
86
  public function reviewAction()
87
  {
88
+ if ($this->checkFeedEnable('catalog/review')) {
89
+ $this->loadLayout(false);
90
+ $this->renderLayout();
91
+ }
92
  }
93
 
94
  public function categoryAction()
95
  {
96
+ if ($this->checkFeedEnable('catalog/category')) {
97
  $this->loadLayout(false);
98
  $this->renderLayout();
99
  }
107
  public function preDispatch()
108
  {
109
  $action = strtolower($this->getRequest()->getActionName());
110
+ if ($action == 'notifystock' && $this->isFeedEnable('catalog/notifystock')) {
111
  $this->_currentArea = 'adminhtml';
112
  Mage::helper('rss')->authAdmin('catalog/products');
113
  }
114
+ if ($action == 'review' && $this->isFeedEnable('catalog/review')) {
115
  $this->_currentArea = 'adminhtml';
116
  Mage::helper('rss')->authAdmin('catalog/reviews_ratings');
117
  }
app/code/core/Mage/Rss/controllers/IndexController.php CHANGED
@@ -30,7 +30,7 @@
30
  * @file IndexController.php
31
  * @author Magento Core Team <core@magentocommerce.com>
32
  */
33
- class Mage_Rss_IndexController extends Mage_Core_Controller_Front_Action
34
  {
35
  /**
36
  * Current wishlist
@@ -80,9 +80,7 @@ class Mage_Rss_IndexController extends Mage_Core_Controller_Front_Action
80
  */
81
  public function wishlistAction()
82
  {
83
- if (!Mage::getStoreConfig('rss/wishlist/active')) {
84
- $this->getResponse()->setHeader('HTTP/1.1','404 Not Found');
85
- $this->getResponse()->setHeader('Status','404 File not found');
86
  $this->_forward('nofeed','index','rss');
87
  return;
88
  }
@@ -156,15 +154,4 @@ class Mage_Rss_IndexController extends Mage_Core_Controller_Front_Action
156
 
157
  return $this->_customer;
158
  }
159
-
160
- /**
161
- * Retrieve helper instance
162
- *
163
- * @param string $name
164
- * @return Mage_Core_Helper_Abstract
165
- */
166
- protected function _getHelper($name)
167
- {
168
- return Mage::helper($name);
169
- }
170
  }
30
  * @file IndexController.php
31
  * @author Magento Core Team <core@magentocommerce.com>
32
  */
33
+ class Mage_Rss_IndexController extends Mage_Rss_Controller_Abstract
34
  {
35
  /**
36
  * Current wishlist
80
  */
81
  public function wishlistAction()
82
  {
83
+ if (!$this->isFeedEnable('wishlist/active')) {
 
 
84
  $this->_forward('nofeed','index','rss');
85
  return;
86
  }
154
 
155
  return $this->_customer;
156
  }
 
 
 
 
 
 
 
 
 
 
 
157
  }
app/code/core/Mage/Rss/controllers/OrderController.php CHANGED
@@ -32,23 +32,25 @@
32
  * @author Magento Core Team <core@magentocommerce.com>
33
  */
34
 
35
- class Mage_Rss_OrderController extends Mage_Core_Controller_Front_Action
36
  {
37
  public function newAction()
38
  {
39
- $this->getResponse()->setHeader('Content-type', 'text/xml; charset=UTF-8');
40
- $this->loadLayout(false);
41
- $this->renderLayout();
 
42
  }
43
 
44
  public function customerAction()
45
  {
46
- if (Mage::app()->getStore()->isCurrentlySecure()) {
47
- $this->getResponse()->setHeader('Content-type', 'text/xml; charset=UTF-8');
48
- Mage::helper('rss')->authFrontend();
49
- } else {
50
- $this->_redirect('rss/order/customer', array('_secure'=>true));
51
- return $this;
 
52
  }
53
  }
54
 
@@ -57,13 +59,15 @@ class Mage_Rss_OrderController extends Mage_Core_Controller_Front_Action
57
  */
58
  public function statusAction()
59
  {
60
- $order = Mage::helper('rss/order')->getOrderByStatusUrlKey((string)$this->getRequest()->getParam('data'));
61
- if (!is_null($order)) {
62
- Mage::register('current_order', $order);
63
- $this->getResponse()->setHeader('Content-type', 'text/xml; charset=UTF-8');
64
- $this->loadLayout(false);
65
- $this->renderLayout();
66
- return;
 
 
67
  }
68
  $this->_forward('nofeed', 'index', 'rss');
69
  }
@@ -76,7 +80,7 @@ class Mage_Rss_OrderController extends Mage_Core_Controller_Front_Action
76
  public function preDispatch()
77
  {
78
  $action = strtolower($this->getRequest()->getActionName());
79
- if ($action == 'new') {
80
  $this->_currentArea = 'adminhtml';
81
  Mage::helper('rss')->authAdmin('sales/order');
82
  }
32
  * @author Magento Core Team <core@magentocommerce.com>
33
  */
34
 
35
+ class Mage_Rss_OrderController extends Mage_Rss_Controller_Abstract
36
  {
37
  public function newAction()
38
  {
39
+ if ($this->checkFeedEnable('order/new')) {
40
+ $this->loadLayout(false);
41
+ $this->renderLayout();
42
+ }
43
  }
44
 
45
  public function customerAction()
46
  {
47
+ if ($this->checkFeedEnable('order/customer')) {
48
+ if (Mage::app()->getStore()->isCurrentlySecure()) {
49
+ Mage::helper('rss')->authFrontend();
50
+ } else {
51
+ $this->_redirect('rss/order/customer', array('_secure'=>true));
52
+ return $this;
53
+ }
54
  }
55
  }
56
 
59
  */
60
  public function statusAction()
61
  {
62
+ if ($this->isFeedEnable('order/status_notified')) {
63
+ $order = Mage::helper('rss/order')->getOrderByStatusUrlKey((string)$this->getRequest()->getParam('data'));
64
+ if (!is_null($order)) {
65
+ Mage::register('current_order', $order);
66
+ $this->getResponse()->setHeader('Content-type', 'text/xml; charset=UTF-8');
67
+ $this->loadLayout(false);
68
+ $this->renderLayout();
69
+ return;
70
+ }
71
  }
72
  $this->_forward('nofeed', 'index', 'rss');
73
  }
80
  public function preDispatch()
81
  {
82
  $action = strtolower($this->getRequest()->getActionName());
83
+ if ($action == 'new' && $this->isFeedEnable('order/new')) {
84
  $this->_currentArea = 'adminhtml';
85
  Mage::helper('rss')->authAdmin('sales/order');
86
  }
app/code/core/Mage/Rss/data/rss_setup/data-install-1.6.0.0.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magento.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magento.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Rss
23
+ * @copyright Copyright (c) 2006-2016 X.commerce, Inc. and affiliates (http://www.magento.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+
28
+ /* @var $installer Mage_Core_Model_Resource_Setup */
29
+
30
+ $installer = $this;
31
+
32
+ $this->deleteConfigData(Mage_Rss_Helper_Data::XML_PATH_RSS_ACTIVE);
33
+
34
+ $installer->endSetup();
app/code/core/Mage/Rss/etc/config.xml CHANGED
@@ -47,10 +47,14 @@
47
  <class>Mage_Rss_Block</class>
48
  </rss>
49
  </blocks>
 
 
 
 
 
 
 
50
  </global>
51
- <admin>
52
-
53
- </admin>
54
  <adminhtml>
55
  <translate>
56
  <modules>
47
  <class>Mage_Rss_Block</class>
48
  </rss>
49
  </blocks>
50
+ <resources>
51
+ <rss_setup>
52
+ <setup>
53
+ <module>Mage_Rss</module>
54
+ </setup>
55
+ </rss_setup>
56
+ </resources>
57
  </global>
 
 
 
58
  <adminhtml>
59
  <translate>
60
  <modules>
app/code/core/Mage/Rss/etc/system.xml CHANGED
@@ -56,7 +56,6 @@
56
  </active>
57
  </fields>
58
  </config>
59
-
60
  <wishlist translate="label">
61
  <label>Wishlist</label>
62
  <frontend_type>text</frontend_type>
@@ -76,7 +75,6 @@
76
  </active>
77
  </fields>
78
  </wishlist>
79
-
80
  <catalog translate="label">
81
  <label>Catalog</label>
82
  <frontend_type>text</frontend_type>
@@ -130,10 +128,27 @@
130
  <show_in_website>1</show_in_website>
131
  <show_in_store>1</show_in_store>
132
  </category>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
  </fields>
134
  </catalog>
135
-
136
- <order>
137
  <label>Order</label>
138
  <frontend_type>text</frontend_type>
139
  <sort_order>4</sort_order>
@@ -150,9 +165,73 @@
150
  <show_in_website>1</show_in_website>
151
  <show_in_store>1</show_in_store>
152
  </status_notified>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
  </fields>
154
  </order>
155
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
  </groups>
157
  </rss>
158
  </sections>
56
  </active>
57
  </fields>
58
  </config>
 
59
  <wishlist translate="label">
60
  <label>Wishlist</label>
61
  <frontend_type>text</frontend_type>
75
  </active>
76
  </fields>
77
  </wishlist>
 
78
  <catalog translate="label">
79
  <label>Catalog</label>
80
  <frontend_type>text</frontend_type>
128
  <show_in_website>1</show_in_website>
129
  <show_in_store>1</show_in_store>
130
  </category>
131
+ <notifystock translate="label">
132
+ <label>Stock Notification</label>
133
+ <frontend_type>select</frontend_type>
134
+ <source_model>adminhtml/system_config_source_enabledisable</source_model>
135
+ <sort_order>15</sort_order>
136
+ <show_in_default>1</show_in_default>
137
+ <show_in_website>1</show_in_website>
138
+ <show_in_store>1</show_in_store>
139
+ </notifystock>
140
+ <review translate="label">
141
+ <label>Review Notification</label>
142
+ <frontend_type>select</frontend_type>
143
+ <source_model>adminhtml/system_config_source_enabledisable</source_model>
144
+ <sort_order>16</sort_order>
145
+ <show_in_default>1</show_in_default>
146
+ <show_in_website>1</show_in_website>
147
+ <show_in_store>1</show_in_store>
148
+ </review>
149
  </fields>
150
  </catalog>
151
+ <order translate="label">
 
152
  <label>Order</label>
153
  <frontend_type>text</frontend_type>
154
  <sort_order>4</sort_order>
165
  <show_in_website>1</show_in_website>
166
  <show_in_store>1</show_in_store>
167
  </status_notified>
168
+ <new translate="label">
169
+ <label>New Order Notification</label>
170
+ <frontend_type>select</frontend_type>
171
+ <source_model>adminhtml/system_config_source_enabledisable</source_model>
172
+ <sort_order>20</sort_order>
173
+ <show_in_default>1</show_in_default>
174
+ <show_in_website>1</show_in_website>
175
+ <show_in_store>1</show_in_store>
176
+ </new>
177
+ <customer translate="label">
178
+ <label>Customer Order Notification</label>
179
+ <frontend_type>select</frontend_type>
180
+ <source_model>adminhtml/system_config_source_enabledisable</source_model>
181
+ <sort_order>30</sort_order>
182
+ <show_in_default>1</show_in_default>
183
+ <show_in_website>1</show_in_website>
184
+ <show_in_store>1</show_in_store>
185
+ </customer>
186
  </fields>
187
  </order>
188
+ <admin_catalog translate="label">
189
+ <label>Admin Catalog</label>
190
+ <frontend_type>text</frontend_type>
191
+ <sort_order>5</sort_order>
192
+ <show_in_default>1</show_in_default>
193
+ <show_in_website>1</show_in_website>
194
+ <show_in_store>1</show_in_store>
195
+ <fields>
196
+ <review translate="label">
197
+ <label>Review Notification</label>
198
+ <frontend_type>select</frontend_type>
199
+ <source_model>adminhtml/system_config_source_enabledisable</source_model>
200
+ <sort_order>10</sort_order>
201
+ <show_in_default>1</show_in_default>
202
+ <show_in_website>1</show_in_website>
203
+ <show_in_store>1</show_in_store>
204
+ </review>
205
+ <notifystock translate="label">
206
+ <label>Stock Notification</label>
207
+ <frontend_type>select</frontend_type>
208
+ <source_model>adminhtml/system_config_source_enabledisable</source_model>
209
+ <sort_order>20</sort_order>
210
+ <show_in_default>1</show_in_default>
211
+ <show_in_website>1</show_in_website>
212
+ <show_in_store>1</show_in_store>
213
+ </notifystock>
214
+ </fields>
215
+ </admin_catalog>
216
+ <admin_order translate="label">
217
+ <label>Admin Order</label>
218
+ <frontend_type>text</frontend_type>
219
+ <sort_order>6</sort_order>
220
+ <show_in_default>1</show_in_default>
221
+ <show_in_website>1</show_in_website>
222
+ <show_in_store>1</show_in_store>
223
+ <fields>
224
+ <new translate="label">
225
+ <label>New Order Notification</label>
226
+ <frontend_type>select</frontend_type>
227
+ <source_model>adminhtml/system_config_source_enabledisable</source_model>
228
+ <sort_order>10</sort_order>
229
+ <show_in_default>1</show_in_default>
230
+ <show_in_website>1</show_in_website>
231
+ <show_in_store>1</show_in_store>
232
+ </new>
233
+ </fields>
234
+ </admin_order>
235
  </groups>
236
  </rss>
237
  </sections>
app/code/core/Mage/Sales/Helper/Guest.php CHANGED
@@ -81,7 +81,7 @@ class Mage_Sales_Helper_Guest extends Mage_Core_Helper_Data
81
  $billingAddress = $order->getBillingAddress();
82
  if ((strtolower($lastName) != strtolower($billingAddress->getLastname()))
83
  || ($type == 'email'
84
- && strtolower($email) != strtolower($billingAddress->getEmail()))
85
  || ($type == 'zip'
86
  && (strtolower($zip) != strtolower($billingAddress->getPostcode())))
87
  ) {
81
  $billingAddress = $order->getBillingAddress();
82
  if ((strtolower($lastName) != strtolower($billingAddress->getLastname()))
83
  || ($type == 'email'
84
+ && strtolower($email) != strtolower($order->getCustomerEmail()))
85
  || ($type == 'zip'
86
  && (strtolower($zip) != strtolower($billingAddress->getPostcode())))
87
  ) {
app/code/core/Mage/Sales/Model/Email/Template.php CHANGED
@@ -33,7 +33,7 @@ class Mage_Sales_Model_Email_Template extends Mage_Core_Model_Email_Template
33
  if (!$filename) {
34
  return '';
35
  }
36
- extract($variables);
37
  ob_start();
38
  include $filename;
39
  return ob_get_clean();
33
  if (!$filename) {
34
  return '';
35
  }
36
+ extract($variables, EXTR_SKIP);
37
  ob_start();
38
  include $filename;
39
  return ob_get_clean();
app/code/core/Mage/Sales/Model/Order.php CHANGED
@@ -1255,7 +1255,11 @@ class Mage_Sales_Model_Order extends Mage_Sales_Model_Abstract
1255
  if (!$asObject) {
1256
  return $shippingMethod;
1257
  } else {
1258
- list($carrierCode, $method) = explode('_', $shippingMethod, 2);
 
 
 
 
1259
  return new Varien_Object(array(
1260
  'carrier_code' => $carrierCode,
1261
  'method' => $method
@@ -2021,7 +2025,12 @@ class Mage_Sales_Model_Order extends Mage_Sales_Model_Abstract
2021
  */
2022
  public function hasShipments()
2023
  {
2024
- return $this->getShipmentsCollection()->count();
 
 
 
 
 
2025
  }
2026
 
2027
  /**
@@ -2031,7 +2040,12 @@ class Mage_Sales_Model_Order extends Mage_Sales_Model_Abstract
2031
  */
2032
  public function hasCreditmemos()
2033
  {
2034
- return $this->getCreditmemosCollection()->count();
 
 
 
 
 
2035
  }
2036
 
2037
 
1255
  if (!$asObject) {
1256
  return $shippingMethod;
1257
  } else {
1258
+ $segments = explode('_', $shippingMethod, 2);
1259
+ if (!isset($segments[1])) {
1260
+ $segments[1] = $segments[0];
1261
+ }
1262
+ list($carrierCode, $method) = $segments;
1263
  return new Varien_Object(array(
1264
  'carrier_code' => $carrierCode,
1265
  'method' => $method
2025
  */
2026
  public function hasShipments()
2027
  {
2028
+ $result = false;
2029
+ $shipmentsCollection = $this->getShipmentsCollection();
2030
+ if ($shipmentsCollection) {
2031
+ $result = (bool)$shipmentsCollection->count();
2032
+ }
2033
+ return $result;
2034
  }
2035
 
2036
  /**
2040
  */
2041
  public function hasCreditmemos()
2042
  {
2043
+ $result = false;
2044
+ $creditmemosCollection = $this->getCreditmemosCollection();
2045
+ if ($creditmemosCollection) {
2046
+ $result = (bool)$creditmemosCollection->count();
2047
+ }
2048
+ return $result;
2049
  }
2050
 
2051
 
app/code/core/Mage/Sales/Model/Order/Invoice/Total/Subtotal.php CHANGED
@@ -40,9 +40,6 @@ class Mage_Sales_Model_Order_Invoice_Total_Subtotal extends Mage_Sales_Model_Ord
40
  $subtotalInclTax= 0;
41
  $baseSubtotalInclTax = 0;
42
 
43
- $totalWeeeDiscount = 0;
44
- $totalBaseWeeeDiscount = 0;
45
-
46
  $order = $invoice->getOrder();
47
 
48
  foreach ($invoice->getAllItems() as $item) {
@@ -52,50 +49,21 @@ class Mage_Sales_Model_Order_Invoice_Total_Subtotal extends Mage_Sales_Model_Ord
52
 
53
  $item->calcRowTotal();
54
 
55
- $subtotal += $item->getRowTotal();
56
- $baseSubtotal += $item->getBaseRowTotal();
57
- $subtotalInclTax+= $item->getRowTotalInclTax();
58
- $baseSubtotalInclTax += $item->getBaseRowTotalInclTax();
59
- $totalWeeeDiscount += $item->getOrderItem()->getDiscountAppliedForWeeeTax();
60
- $totalBaseWeeeDiscount += $item->getOrderItem()->getBaseDiscountAppliedForWeeeTax();
61
  }
62
 
63
  $allowedSubtotal = $order->getSubtotal() - $order->getSubtotalInvoiced();
64
  $baseAllowedSubtotal = $order->getBaseSubtotal() - $order->getBaseSubtotalInvoiced();
65
- $allowedSubtotalInclTax = $allowedSubtotal + $order->getHiddenTaxAmount() + $totalWeeeDiscount
66
- + $order->getTaxAmount() - $order->getTaxInvoiced() - $order->getHiddenTaxInvoiced();
67
- $baseAllowedSubtotalInclTax = $baseAllowedSubtotal + $order->getBaseHiddenTaxAmount() + $totalBaseWeeeDiscount
68
- + $order->getBaseTaxAmount() - $order->getBaseTaxInvoiced() - $order->getBaseHiddenTaxInvoiced();
69
-
70
- /**
71
- * Check if shipping tax calculation is included to current invoice.
72
- */
73
- $includeShippingTax = true;
74
- foreach ($invoice->getOrder()->getInvoiceCollection() as $previousInvoice) {
75
- if ($previousInvoice->getShippingAmount() && !$previousInvoice->isCanceled()) {
76
- $includeShippingTax = false;
77
- break;
78
- }
79
- }
80
-
81
- if ($includeShippingTax) {
82
- $allowedSubtotalInclTax -= $order->getShippingTaxAmount();
83
- $baseAllowedSubtotalInclTax -= $order->getBaseShippingTaxAmount();
84
- } else {
85
- $allowedSubtotalInclTax += $order->getShippingHiddenTaxAmount();
86
- $baseAllowedSubtotalInclTax += $order->getBaseShippingHiddenTaxAmount();
87
- }
88
 
89
  if ($invoice->isLast()) {
90
  $subtotal = $allowedSubtotal;
91
  $baseSubtotal = $baseAllowedSubtotal;
92
- $subtotalInclTax = $allowedSubtotalInclTax;
93
- $baseSubtotalInclTax = $baseAllowedSubtotalInclTax;
94
  } else {
95
  $subtotal = min($allowedSubtotal, $subtotal);
96
  $baseSubtotal = min($baseAllowedSubtotal, $baseSubtotal);
97
- $subtotalInclTax = min($allowedSubtotalInclTax, $subtotalInclTax);
98
- $baseSubtotalInclTax = min($baseAllowedSubtotalInclTax, $baseSubtotalInclTax);
99
  }
100
 
101
  $invoice->setSubtotal($subtotal);
40
  $subtotalInclTax= 0;
41
  $baseSubtotalInclTax = 0;
42
 
 
 
 
43
  $order = $invoice->getOrder();
44
 
45
  foreach ($invoice->getAllItems() as $item) {
49
 
50
  $item->calcRowTotal();
51
 
52
+ $subtotal += $item->getRowTotal();
53
+ $baseSubtotal += $item->getBaseRowTotal();
54
+ $subtotalInclTax += $item->getRowTotalInclTax() + $item->getWeeeTaxAppliedRowAmount();
55
+ $baseSubtotalInclTax += $item->getBaseRowTotalInclTax() + $item->getBaseWeeeTaxAppliedRowAmount();
 
 
56
  }
57
 
58
  $allowedSubtotal = $order->getSubtotal() - $order->getSubtotalInvoiced();
59
  $baseAllowedSubtotal = $order->getBaseSubtotal() - $order->getBaseSubtotalInvoiced();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
 
61
  if ($invoice->isLast()) {
62
  $subtotal = $allowedSubtotal;
63
  $baseSubtotal = $baseAllowedSubtotal;
 
 
64
  } else {
65
  $subtotal = min($allowedSubtotal, $subtotal);
66
  $baseSubtotal = min($baseAllowedSubtotal, $baseSubtotal);
 
 
67
  }
68
 
69
  $invoice->setSubtotal($subtotal);
app/code/core/Mage/Sales/Model/Quote/Address/Total/Subtotal.php CHANGED
@@ -111,7 +111,7 @@ class Mage_Sales_Model_Quote_Address_Total_Subtotal extends Mage_Sales_Model_Quo
111
  $item->setPrice($finalPrice)
112
  ->setBaseOriginalPrice($finalPrice);
113
  $item->calcRowTotal();
114
- } else if (!$quoteItem->getParentItem()) {
115
  $finalPrice = $product->getFinalPrice($quoteItem->getQty());
116
  $item->setPrice($finalPrice)
117
  ->setBaseOriginalPrice($finalPrice);
111
  $item->setPrice($finalPrice)
112
  ->setBaseOriginalPrice($finalPrice);
113
  $item->calcRowTotal();
114
+ } else if (!$quoteItem->getParentItem() && !$item->getHasError()) {
115
  $finalPrice = $product->getFinalPrice($quoteItem->getQty());
116
  $item->setPrice($finalPrice)
117
  ->setBaseOriginalPrice($finalPrice);
app/code/core/Mage/Sales/Model/Quote/Item.php CHANGED
@@ -507,7 +507,7 @@ class Mage_Sales_Model_Quote_Item extends Mage_Sales_Model_Quote_Item_Abstract
507
  $itemOptionValue = $_itemOptionValue;
508
  $optionValue = $_optionValue;
509
  // looks like it does not break bundle selection qty
510
- foreach (array('qty', 'uenc', 'form_key') as $key) {
511
  unset($itemOptionValue[$key], $optionValue[$key]);
512
  }
513
  }
507
  $itemOptionValue = $_itemOptionValue;
508
  $optionValue = $_optionValue;
509
  // looks like it does not break bundle selection qty
510
+ foreach (array('qty', 'uenc', 'form_key', 'item', 'original_qty') as $key) {
511
  unset($itemOptionValue[$key], $optionValue[$key]);
512
  }
513
  }
app/code/core/Mage/Sales/Model/Quote/Item/Abstract.php CHANGED
@@ -130,7 +130,10 @@ abstract class Mage_Sales_Model_Quote_Item_Abstract extends Mage_Core_Model_Abst
130
  {
131
  if ($parentItem) {
132
  $this->_parentItem = $parentItem;
133
- $parentItem->addChild($this);
 
 
 
134
  }
135
  return $this;
136
  }
130
  {
131
  if ($parentItem) {
132
  $this->_parentItem = $parentItem;
133
+ // Prevent duplication of children in those are already set
134
+ if (!in_array($this, $parentItem->getChildren())) {
135
+ $parentItem->addChild($this);
136
+ }
137
  }
138
  return $this;
139
  }
app/code/core/Mage/Sales/Model/Resource/Order/Payment.php CHANGED
@@ -58,4 +58,28 @@ class Mage_Sales_Model_Resource_Order_Payment extends Mage_Sales_Model_Resource_
58
  {
59
  $this->_init('sales/order_payment', 'entity_id');
60
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  }
58
  {
59
  $this->_init('sales/order_payment', 'entity_id');
60
  }
61
+
62
+ /**
63
+ * Unserialize Varien_Object field in an object
64
+ *
65
+ * @param Mage_Core_Model_Abstract $object
66
+ * @param string $field
67
+ * @param mixed $defaultValue
68
+ */
69
+ protected function _unserializeField(Varien_Object $object, $field, $defaultValue = null)
70
+ {
71
+ $value = $object->getData($field);
72
+ if (empty($value)) {
73
+ $object->setData($field, $defaultValue);
74
+ } elseif (!is_array($value) && !is_object($value)) {
75
+ $unserializedValue = false;
76
+ try {
77
+ $unserializedValue = Mage::helper('core/unserializeArray')
78
+ ->unserialize($value);
79
+ } catch (Exception $e) {
80
+ Mage::logException($e);
81
+ }
82
+ $object->setData($field, $unserializedValue);
83
+ }
84
+ }
85
  }
app/code/core/Mage/Sales/Model/Resource/Order/Payment/Transaction.php CHANGED
@@ -52,6 +52,30 @@ class Mage_Sales_Model_Resource_Order_Payment_Transaction extends Mage_Sales_Mod
52
  $this->_init('sales/payment_transaction', 'transaction_id');
53
  }
54
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  /**
56
  * Update transactions in database using provided transaction as parent for them
57
  * have to repeat the business logic to avoid accidental injection of wrong transactions