Highstreet - Version 1.1.0

Version Notes

The new complete Highstreet extension

Download this release

Release Info

Developer Christian Apers
Extension Highstreet
Version 1.1.0
Comparing to
See all releases


Code changes from version 1.0.1 to 1.1.0

Files changed (25) hide show
  1. app/code/local/Highstreet/Hsapi/Helper/Config.php +0 -68
  2. app/code/local/Highstreet/Hsapi/Helper/Config/Api.php +77 -0
  3. app/code/local/Highstreet/Hsapi/Helper/Data.php +8 -2
  4. app/code/local/Highstreet/Hsapi/Helper/Encryption.php +67 -0
  5. app/code/local/Highstreet/Hsapi/Model/Categories.php +7 -22
  6. app/code/local/Highstreet/Hsapi/Model/Checkout.php +51 -30
  7. app/code/local/Highstreet/Hsapi/Model/CheckoutV2.php +802 -0
  8. app/code/local/Highstreet/Hsapi/Model/Images.php +5 -1
  9. app/code/local/Highstreet/Hsapi/Model/Observer.php +49 -6
  10. app/code/local/Highstreet/Hsapi/Model/Products.php +379 -284
  11. app/code/local/Highstreet/Hsapi/Model/System/Config/Environment.php +17 -0
  12. app/code/local/Highstreet/Hsapi/controllers/.DS_Store +0 -0
  13. app/code/local/Highstreet/Hsapi/controllers/CheckoutController.php +630 -0
  14. app/code/local/Highstreet/Hsapi/controllers/IndexController.php +177 -37
  15. app/code/local/Highstreet/Hsapi/etc/.DS_Store +0 -0
  16. app/code/local/Highstreet/Hsapi/etc/Configuration.json +0 -1
  17. app/code/local/Highstreet/Hsapi/etc/config.xml +44 -13
  18. app/code/local/Highstreet/Hsapi/etc/system.xml +103 -0
  19. app/code/local/Highstreet/SmartAppBanner/etc/config.xml +26 -0
  20. app/design/frontend/base/default/layout/highstreet/smart_app_banner.xml +12 -0
  21. app/design/frontend/base/default/template/highstreet/native_smart_app_banner.phtml +28 -0
  22. app/design/frontend/base/default/template/highstreet/smart_app_banner.phtml +91 -0
  23. app/etc/modules/Highstreet_SmartAppBanner.xml +14 -0
  24. app/locale/de_DE/Highstreet_Hsapi.csv +72 -0
  25. package.xml +6 -6
app/code/local/Highstreet/Hsapi/Helper/Config.php DELETED
@@ -1,68 +0,0 @@
1
- <?php
2
- /**
3
- * Highstreet_HSAPI_module
4
- *
5
- * @package Highstreet_Hsapi
6
- * @author Tim Wachter (tim@touchwonders.com) ~ Touchwonders
7
- * @copyright Copyright (c) 2013 Touchwonders b.v. (http://www.touchwonders.com/)
8
- */
9
-
10
- class Highstreet_Hsapi_Helper_Config extends Mage_Core_Helper_Abstract
11
- {
12
- const HAS_COUPON_CODES_ENABLES = "has_coupon_codes_enabled";
13
- const FILTERS_CATEGORIES = "filters_categories";
14
- const ALWAYS_ADD_SIMPLE_PRODUCTS_TO_CART = "always_add_simple_products_to_cart";
15
- const CHECKOUT_URL = "checkout_url";
16
- const CHECKOUT_URL_FALLBACK = "checkout/onepage";
17
- const HAS_NEW_CHECKOUT = "new_checkout";
18
- const SHIPPING_METHODS_TEMPLATE_PATH = "shipping_methods_template_path";
19
- const SHIPPING_METHODS_TEMPLATE_PATH_FALLBACK = "highstreet/checkout/checkout/shipping_method/available.phtml";
20
-
21
-
22
- public function hasCouponCodeEnabled() {
23
- $configurations = $this->_getConfigurations();
24
- return $configurations[self::HAS_COUPON_CODES_ENABLES];
25
- }
26
-
27
- public function filtersCategories() {
28
- $configurations = $this->_getConfigurations();
29
- return $configurations[self::FILTERS_CATEGORIES];
30
- }
31
-
32
- public function alwaysAddSimpleProductsToCart() {
33
- $configurations = $this->_getConfigurations();
34
- $addSimpleProducts = $configurations[self::ALWAYS_ADD_SIMPLE_PRODUCTS_TO_CART];
35
- return ($addSimpleProducts === NULL) ? false : $addSimpleProducts;
36
- }
37
-
38
- public function checkoutUrl() {
39
- $configurations = $this->_getConfigurations();
40
- $checkoutUrl = $configurations[self::CHECKOUT_URL];
41
- return ($checkoutUrl === NULL) ? self::CHECKOUT_URL_FALLBACK : $checkoutUrl;
42
- }
43
-
44
- public function hasNewCheckout() {
45
- $configurations = $this->_getConfigurations();
46
- $hasNewCheckout = $configurations[self::HAS_NEW_CHECKOUT];
47
- return ($hasNewCheckout === NULL) ? false : $hasNewCheckout;
48
- }
49
-
50
- public function shippingMethodsTemplatePath() {
51
- $configurations = $this->_getConfigurations();
52
- $shippingMethodsTemplatePath = $configurations[self::SHIPPING_METHODS_TEMPLATE_PATH];
53
- return ($shippingMethodsTemplatePath === NULL) ? self::SHIPPING_METHODS_TEMPLATE_PATH_FALLBACK : $shippingMethodsTemplatePath;
54
- }
55
-
56
- /**
57
- * Loads the configuration JSON and returns an array of
58
- *
59
- * @return array Array of the settings
60
- */
61
- private function _getConfigurations() {
62
- $file = file_get_contents(Mage::getBaseDir('code') . "/local/Highstreet/Hsapi/etc/Configuration.json");
63
- return json_decode($file, true);
64
- }
65
- }
66
-
67
-
68
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/code/local/Highstreet/Hsapi/Helper/Config/Api.php ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Highstreet_HSAPI_module
4
+ *
5
+ * @package Highstreet_Hsapi
6
+ * @author Tim Wachter (tim@touchwonders.com) ~ Touchwonders
7
+ * @copyright Copyright (c) 2015 Touchwonders b.v. (http://www.touchwonders.com/)
8
+ */
9
+
10
+ class Highstreet_Hsapi_Helper_Config_Api extends Mage_Core_Helper_Abstract {
11
+ const MIDDLEWARE_URL_SCHEME = "http://";
12
+ const MIDDLEWARE_URL_ENVIRONMENT_STAGING = "api-dev";
13
+ const MIDDLEWARE_URL_ENVIRONMENT_PRODUCTION = "api";
14
+ const MIDDLEWARE_URL_HOST_PATH = "highstreetapp.com/hs-api/1.4";
15
+
16
+ public function alwaysAddSimpleProductsToCart() {
17
+ $alwaysAddSimpleProductsToCart = Mage::getStoreConfig('highstreet_hsapi/api/always_add_simple_products');
18
+ return ($alwaysAddSimpleProductsToCart === NULL) ? false : (bool)$alwaysAddSimpleProductsToCart;
19
+ }
20
+
21
+ public function shippingInCartDisabled() {
22
+ $shippingInCartDisabled = Mage::getStoreConfig('highstreet_hsapi/api/shipping_in_cart');
23
+ return ($shippingInCartDisabled === NULL) ? false : (bool)$shippingInCartDisabled;
24
+ }
25
+
26
+ public function storeIdentifier() {
27
+ $store_id = Mage::getStoreConfig('highstreet_hsapi/api/store_id');
28
+ return ($store_id === NULL) ? "" : $store_id;
29
+ }
30
+
31
+ public function environment() {
32
+ $environment = Mage::getStoreConfig('highstreet_hsapi/api/environment');
33
+ return ($environment === NULL) ? "staging" : $environment;
34
+ }
35
+
36
+ public function nativeSmartbannerActive() {
37
+ return (bool) Mage::getStoreConfig('highstreet_hsapi/api/smartbanner_native_active');
38
+ }
39
+
40
+ public function nativeSmartbannerAppId() {
41
+ $app_id = Mage::getStoreConfig('highstreet_hsapi/api/smartbanner_native_app_id');
42
+ return ($app_id === NULL) ? "" : $app_id;
43
+ }
44
+
45
+ public function nativeSmartbannerAppUrl() {
46
+ $app_url = Mage::getStoreConfig('highstreet_hsapi/api/smartbanner_native_app_url');
47
+ return ($app_url === NULL) ? "" : $app_url;
48
+ }
49
+
50
+ public function nativeSmartbannerAppName() {
51
+ $app_name = Mage::getStoreConfig('highstreet_hsapi/api/smartbanner_native_app_name');
52
+ return ($app_name === NULL) ? "" : $app_name;
53
+ }
54
+
55
+ public function middlewareUrl() {
56
+ if ($this->storeIdentifier() == "") {
57
+ return NULL;
58
+ }
59
+
60
+ $url = self::MIDDLEWARE_URL_SCHEME . $this->storeIdentifier();
61
+
62
+ if ($this->environment() === 'staging') {
63
+ $url .= '.' . self::MIDDLEWARE_URL_ENVIRONMENT_STAGING;
64
+ } else {
65
+ $url .= '.' . self::MIDDLEWARE_URL_ENVIRONMENT_PRODUCTION;
66
+ }
67
+
68
+ $url .= '.' . self::MIDDLEWARE_URL_HOST_PATH;
69
+
70
+
71
+ return $url;
72
+ }
73
+
74
+ public function shouldShowNativeSmartbanner() {
75
+ return ($this->nativeSmartbannerActive() && $this->nativeSmartbannerAppId() != "");
76
+ }
77
+ }
app/code/local/Highstreet/Hsapi/Helper/Data.php CHANGED
@@ -11,6 +11,7 @@ class Highstreet_Hsapi_Helper_Data extends Mage_Core_Helper_Abstract
11
  {
12
  /**
13
  * Return single request param. This returns the key as we have rest url
 
14
  * @param $params
15
  *
16
  * @return bool|int|string
@@ -18,8 +19,13 @@ class Highstreet_Hsapi_Helper_Data extends Mage_Core_Helper_Abstract
18
  public function extractRequestParam($params)
19
  {
20
  if($this->requestHasParams($params)){
21
- foreach($params as $key => $value){
22
- return $key;
 
 
 
 
 
23
  }
24
  }
25
  return false;
11
  {
12
  /**
13
  * Return single request param. This returns the key as we have rest url
14
+ * Ignores params with three underscores in them
15
  * @param $params
16
  *
17
  * @return bool|int|string
19
  public function extractRequestParam($params)
20
  {
21
  if($this->requestHasParams($params)){
22
+ foreach ($params as $key => $value) {
23
+ // Magento internal parameters use underscores to differentiate themselves
24
+ // if we want to pass a specific storefront we need to send the parameter '___store'
25
+ // this messes with the attributes call, so ignore these params
26
+ if (strstr($key, "___") === false) {
27
+ return $key;
28
+ }
29
  }
30
  }
31
  return false;
app/code/local/Highstreet/Hsapi/Helper/Encryption.php ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Highstreet_HSAPI_module
4
+ *
5
+ * @package Highstreet_Hsapi
6
+ * @author Tim Wachter (tim@touchwonders.com) ~ Touchwonders
7
+ * @copyright Copyright (c) 2015 Touchwonders b.v. (http://www.touchwonders.com/)
8
+ */
9
+
10
+ class Highstreet_Hsapi_Helper_Encryption extends Mage_Core_Helper_Abstract {
11
+ private $quoteIdHashSalt = "yw14dhejnr";
12
+ private $hmacEncryptionKey = "5JbqhKdBGtV8J4PH82cm5YDr5f8b4Rbk";
13
+
14
+ public function hashQuoteId($quoteId = 0) {
15
+ return md5($quoteId . $this->quoteIdHashSalt);
16
+ }
17
+
18
+ public function APISignatureStringIsValid() {
19
+ $givenAPISignature = $_SERVER['HTTP_X_API_SIGNATURE'];
20
+ $signatureString = $this->_getSignatureString();
21
+ $encryptedString = $this->_SHA256EncryptString($signatureString);
22
+
23
+ return ($givenAPISignature !== NULL && $givenAPISignature === $encryptedString);
24
+ }
25
+
26
+ private function _getSignatureString() {
27
+ $serverMethod = $_SERVER['REQUEST_METHOD'];
28
+ if (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') {
29
+ $serverScheme = 'https';
30
+ } else {
31
+ $serverScheme = 'http';
32
+ }
33
+ $serverHost = $_SERVER['HTTP_HOST'];
34
+ $serverPort = $_SERVER['SERVER_PORT'];
35
+ $serverUri = $_SERVER['REQUEST_URI'];
36
+
37
+ $urlComponents = explode('?', $serverUri);
38
+
39
+ $serverUri = $urlComponents[0];
40
+
41
+ $params = array();
42
+ if (count($urlComponents) >= 1) {
43
+ $params = $urlComponents[1];
44
+ parse_str($params, $params);
45
+ }
46
+
47
+ ksort($params);
48
+ foreach ($params as $key => $value) {
49
+ $paramString .= $key . '=' . urlencode($value);
50
+
51
+ if (array_search($key, array_keys($params)) < count($params)-1) {
52
+ $paramString .= '&';
53
+ }
54
+ }
55
+
56
+ $string = $serverMethod . $serverScheme . $serverHost . ':' . $serverPort . $serverUri . $paramString;
57
+
58
+ return $string;
59
+ }
60
+
61
+ private function _SHA256EncryptString($string) {
62
+ return hash_hmac('sha256', $string, $this->hmacEncryptionKey);
63
+ }
64
+ }
65
+
66
+
67
+
app/code/local/Highstreet/Hsapi/Model/Categories.php CHANGED
@@ -32,6 +32,7 @@ class Highstreet_Hsapi_Model_Categories extends Mage_Core_Model_Abstract
32
  $category = array();
33
  $category['id'] = $categoryId;
34
  $category['title'] = $categoryObject->getData('name');
 
35
 
36
  if ($categoryObject->getImage()) {
37
  $imageUrl = self::CATEGORY_MEDIA_PATH . $categoryObject->getImage();
@@ -45,29 +46,20 @@ class Highstreet_Hsapi_Model_Categories extends Mage_Core_Model_Abstract
45
  // category children
46
  $children = $this->getChildrenCollectionForCategoryId($categoryId);
47
 
48
- $config = Mage::helper('highstreet_hsapi/config');
49
- $filtersCategories = $config->filtersCategories();
50
-
51
  if ($children->count() > 0) {
52
  $category['children'] = array();
53
 
54
  foreach ($children as $child) {
55
- if ($filtersCategories) {
56
- if ($child->getData('level') == 2 && // Top Category Level
57
- $child->getData('include_in_menu') == 0) {
58
- continue;
59
- }
60
- }
61
-
62
  if ($child->getImage()) {
63
  $childImageUrl = self::CATEGORY_MEDIA_PATH . $child->getImage();
64
  } else {
65
  $childImageUrl = '';
66
  }
67
  array_push($category['children'], array(
68
- 'id' => $child->getData('entity_id'),
69
- 'title' => $child->getData('name'),
70
- 'image' => $childImageUrl,
 
71
  ));
72
  }
73
  }
@@ -113,21 +105,14 @@ class Highstreet_Hsapi_Model_Categories extends Mage_Core_Model_Abstract
113
  }
114
  $category['image'] = $imageUrl;
115
 
116
- $category['product_count'] = $productCollection->count();
117
 
118
- $config = Mage::helper('highstreet_hsapi/config');
119
- $filtersCategories = $config->filtersCategories();
120
 
121
  // category children
122
  $category['children'] = array();
123
  if ($children->count() > 0) {
124
  foreach ($children as $child) {
125
- if ($filtersCategories) {
126
- if ($child->getData('level') == 2 && // Top Category Level
127
- $child->getData('include_in_menu') == 0) {
128
- continue;
129
- }
130
- }
131
 
132
  $childRepresentation = $this->getCategoryTree($child->getData('entity_id'));
133
 
32
  $category = array();
33
  $category['id'] = $categoryId;
34
  $category['title'] = $categoryObject->getData('name');
35
+ $category['include_in_menu'] = (bool)$categoryObject->getData('include_in_menu');
36
 
37
  if ($categoryObject->getImage()) {
38
  $imageUrl = self::CATEGORY_MEDIA_PATH . $categoryObject->getImage();
46
  // category children
47
  $children = $this->getChildrenCollectionForCategoryId($categoryId);
48
 
 
 
 
49
  if ($children->count() > 0) {
50
  $category['children'] = array();
51
 
52
  foreach ($children as $child) {
 
 
 
 
 
 
 
53
  if ($child->getImage()) {
54
  $childImageUrl = self::CATEGORY_MEDIA_PATH . $child->getImage();
55
  } else {
56
  $childImageUrl = '';
57
  }
58
  array_push($category['children'], array(
59
+ 'id' => $child->getData('entity_id'),
60
+ 'title' => $child->getData('name'),
61
+ 'image' => $childImageUrl,
62
+ 'include_in_menu'=> (bool)$child->getData('include_in_menu')
63
  ));
64
  }
65
  }
105
  }
106
  $category['image'] = $imageUrl;
107
 
108
+ $category['include_in_menu'] = (bool)$categoryObject->getData('include_in_menu');
109
 
110
+ $category['product_count'] = $productCollection->count();
 
111
 
112
  // category children
113
  $category['children'] = array();
114
  if ($children->count() > 0) {
115
  foreach ($children as $child) {
 
 
 
 
 
 
116
 
117
  $childRepresentation = $this->getCategoryTree($child->getData('entity_id'));
118
 
app/code/local/Highstreet/Hsapi/Model/Checkout.php CHANGED
@@ -105,12 +105,24 @@ class Highstreet_Hsapi_Model_Checkout extends Mage_Core_Model_Abstract
105
 
106
  $response["quote"] = array_values($this->getProductsInQuote($quote,$products));
107
 
108
- $this->addAddressToQuoteIfNeeded($quote);
109
  //Shipping carries
110
  $response['selected_shipping_method'] = $this->getSelectedShippingMethod($quote);
111
- $response['shipping'] = array_values($this->getShippingMethods($quote, $response['selected_shipping_method']));
 
 
 
 
 
 
 
 
 
 
 
 
112
  $response["totals"] = $this->getQuoteTotals($quote);
113
 
 
114
 
115
  return $response;
116
 
@@ -125,12 +137,9 @@ class Highstreet_Hsapi_Model_Checkout extends Mage_Core_Model_Abstract
125
  $responseQuote = array();
126
 
127
 
128
-
129
- $quoteItems = array();
130
- foreach($quote->getAllItems() as $quoteItem) {
131
- $quoteItems[$quoteItem->getId()] = $quoteItem;
132
- }
133
-
134
 
135
 
136
  //loop through the requested products
@@ -234,8 +243,9 @@ class Highstreet_Hsapi_Model_Checkout extends Mage_Core_Model_Abstract
234
  }
235
 
236
 
237
- if($quoteItem)
238
- unset($quoteItems[$quoteItem->getId()]);
 
239
 
240
 
241
 
@@ -255,13 +265,23 @@ class Highstreet_Hsapi_Model_Checkout extends Mage_Core_Model_Abstract
255
 
256
 
257
  }
 
 
 
 
 
258
 
259
  foreach($quoteItems as $quoteItem) {
 
 
 
260
  if(count($quoteItem->getChildren()) > 0)
261
  continue;
262
 
263
  $productInQuote = $this->getProductInQuoteResponse($quoteItem);
264
  $responseQuote[] = $productInQuote;
 
 
265
  }
266
 
267
 
@@ -286,6 +306,11 @@ class Highstreet_Hsapi_Model_Checkout extends Mage_Core_Model_Abstract
286
  $quantity = $quoteItem ? $quoteItem->getQty() : 0;
287
 
288
  $productInQuote["finalPrice"] = $quantity > 0 ? $product->getFinalPrice($quantity) : $product->getFinalPrice();
 
 
 
 
 
289
  $productInQuote["quantity"] = $quantity;
290
 
291
  return $productInQuote;
@@ -328,21 +353,8 @@ class Highstreet_Hsapi_Model_Checkout extends Mage_Core_Model_Abstract
328
  return $responseTotals;
329
  }
330
 
331
- private function addAddressToQuoteIfNeeded(&$quote) {
332
- $address = $quote->getShippingAddress();
333
-
334
- if(!$address->getCountryId()) {
335
- $address->setCity("Utrecht")
336
- ->setCountryId("NL")
337
- ->setPostcode("3512NT")
338
- ->setCollectShippingRates(true);
339
- $quote->setShippingAddress($address);
340
- }
341
- }
342
-
343
  private function getSelectedShippingMethod($quote) {
344
  $quoteShippingAddress = $quote->getShippingAddress();
345
- $quoteShippingAddress->collectTotals(); //to make sure all available shipping methods are listed
346
 
347
  $quoteShippingAddress->collectShippingRates()->save(); //collect the rates
348
 
@@ -359,7 +371,7 @@ class Highstreet_Hsapi_Model_Checkout extends Mage_Core_Model_Abstract
359
  $responseCarriers = array();
360
 
361
  $quoteShippingAddress = $quote->getShippingAddress();
362
- $quoteShippingAddress->collectTotals(); //to make sure all available shipping methods are listed
363
 
364
  $quoteShippingAddress->collectShippingRates()->save(); //collect the rates
365
  $groupedRates = $quoteShippingAddress->getGroupedAllShippingRates();
@@ -402,19 +414,28 @@ class Highstreet_Hsapi_Model_Checkout extends Mage_Core_Model_Abstract
402
  return $product;
403
 
404
  }
405
-
406
  private function _getParentProduct($product) {
407
- $config = Mage::helper('highstreet_hsapi/config');
408
  if ($config->alwaysAddSimpleProductsToCart()) {
409
  return null;
410
  }
411
 
412
  $parentIds = Mage::getModel('catalog/product_type_configurable')->getParentIdsByChild($product->getId());
413
- $parent = null;
414
- if(isset($parentIds[0])){
415
- $parent = Mage::getModel('catalog/product')->load($parentIds[0]);
 
 
 
 
 
 
 
 
416
  }
417
- return $parent;
 
418
  }
419
 
420
  private function _getConfiguration($product,$parent) {
105
 
106
  $response["quote"] = array_values($this->getProductsInQuote($quote,$products));
107
 
 
108
  //Shipping carries
109
  $response['selected_shipping_method'] = $this->getSelectedShippingMethod($quote);
110
+
111
+ $config = Mage::helper('highstreet_hsapi/config_api');
112
+
113
+ //Some stores don't want to return the shipping methods in the cart (that is: when the selected_shipping_method is not set yet)
114
+ //e.g. PME doesn't want this for performance optimization, everytime the cart is openend the magento backend will connect to Paazl, which is quite expensive.
115
+ //Therefore they one return shipping info once the user acutally a has selected a shipping method.
116
+ //For PME we also know that shipping is always free, so the rewriter add 'price: 0' to the response.
117
+ if ($config->shippingInCartDisabled() && $response['selected_shipping_method'] === null) {
118
+ $response['shipping'] = array();
119
+ } else {
120
+ $response['shipping'] = array_values($this->getShippingMethods($quote, $response['selected_shipping_method']));
121
+ }
122
+
123
  $response["totals"] = $this->getQuoteTotals($quote);
124
 
125
+
126
 
127
  return $response;
128
 
137
  $responseQuote = array();
138
 
139
 
140
+ $quoteItemsAlreadyAdded = array();
141
+ $quoteItems = $quote->getAllItems();
142
+
 
 
 
143
 
144
 
145
  //loop through the requested products
243
  }
244
 
245
 
246
+ if($quoteItem) {
247
+ $quoteItemsAlreadyAdded[] = $quoteItem->getProduct()->getId();
248
+ }
249
 
250
 
251
 
265
 
266
 
267
  }
268
+
269
+ //explicit save for PME plugin and refetch quoteitems. For Amasty Free product plugin
270
+ $quote->collectTotals()->save();
271
+ $quoteItems = $quote->getAllItems();
272
+
273
 
274
  foreach($quoteItems as $quoteItem) {
275
+ if(in_array($quoteItem->getProduct()->getId(),$quoteItemsAlreadyAdded))
276
+ continue;
277
+
278
  if(count($quoteItem->getChildren()) > 0)
279
  continue;
280
 
281
  $productInQuote = $this->getProductInQuoteResponse($quoteItem);
282
  $responseQuote[] = $productInQuote;
283
+
284
+ $quoteItemsAlreadyAdded[] = $quoteItem->getProduct()->getId();
285
  }
286
 
287
 
306
  $quantity = $quoteItem ? $quoteItem->getQty() : 0;
307
 
308
  $productInQuote["finalPrice"] = $quantity > 0 ? $product->getFinalPrice($quantity) : $product->getFinalPrice();
309
+
310
+ //The custom price is set by the Amasty - Auto Add Promo Items extension. The extra free product has a custom price, and if it is set we should use that price.
311
+ //This price is already used (automatically) in the total calculations
312
+ if($quoteItem->getCustomPrice() !== null)
313
+ $productInQuote["finalPrice"] = $quoteItem->getCustomPrice();
314
  $productInQuote["quantity"] = $quantity;
315
 
316
  return $productInQuote;
353
  return $responseTotals;
354
  }
355
 
 
 
 
 
 
 
 
 
 
 
 
 
356
  private function getSelectedShippingMethod($quote) {
357
  $quoteShippingAddress = $quote->getShippingAddress();
 
358
 
359
  $quoteShippingAddress->collectShippingRates()->save(); //collect the rates
360
 
371
  $responseCarriers = array();
372
 
373
  $quoteShippingAddress = $quote->getShippingAddress();
374
+
375
 
376
  $quoteShippingAddress->collectShippingRates()->save(); //collect the rates
377
  $groupedRates = $quoteShippingAddress->getGroupedAllShippingRates();
414
  return $product;
415
 
416
  }
417
+
418
  private function _getParentProduct($product) {
419
+ $config = Mage::helper('highstreet_hsapi/config_api');
420
  if ($config->alwaysAddSimpleProductsToCart()) {
421
  return null;
422
  }
423
 
424
  $parentIds = Mage::getModel('catalog/product_type_configurable')->getParentIdsByChild($product->getId());
425
+
426
+ foreach ($parentIds as $value) {
427
+ $product = Mage::getModel('catalog/product')->load($value);
428
+
429
+ // Same check as in Products.php:656
430
+ // Checks if a parent product is enabled
431
+ if ($product && $product->getId() !== null &&
432
+ ($product->getData('status') == Mage_Catalog_Model_Product_Status::STATUS_ENABLED || $product->getStatus() == Mage_Catalog_Model_Product_Status::STATUS_ENABLED ||
433
+ $product->getData('status') == "Ingeschakeld" || $product->getStatus() == "Ingeschakeld")) {
434
+ return $product;
435
+ }
436
  }
437
+
438
+ return null;
439
  }
440
 
441
  private function _getConfiguration($product,$parent) {
app/code/local/Highstreet/Hsapi/Model/CheckoutV2.php ADDED
@@ -0,0 +1,802 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Highstreet_API_module
4
+ *
5
+ * @package Highstreet_Api
6
+ * @author Tim Wachter (tim@touchwonders.com)
7
+ * @copyright Copyright (c) 2014 Touchwonders (http://www.touchwonders.com/)
8
+ */
9
+ class Highstreet_Hsapi_Model_CheckoutV2 extends Mage_Core_Model_Abstract
10
+ {
11
+ private $_errorCodes = array();
12
+ private $_errorMessages = array();
13
+ private $_productIdsNotAdded = array();
14
+
15
+ /**
16
+ * Fills the current session with cart data. This session automatically gets set trough the Magento models, this also inserts the current data in the database e.d.
17
+ * The array should have the following format:
18
+ * {"products":[{"sku":"product_sku_1", "qty":5}, {"sku":"product_sku_2", "qty":9}, {"sku":"product_sku_3", "qty":32}, {"sku":"product_sku_4", "qty":3}, {"sku":"product_sku_5", "qty":1}]}
19
+ * With this format we will lateron be able to extend it for configurable products
20
+ *
21
+ * @param array An array of product SKU's to fill the cart
22
+ */
23
+ public function fillCartWithProductsAndQuantities($products = false) {
24
+ if (!$products) {
25
+ return;
26
+ }
27
+
28
+
29
+ Mage::getSingleton('checkout/session')->setQuoteId(null);
30
+
31
+
32
+ $cart = Mage::getModel('checkout/cart');
33
+ $cart->init();
34
+ $cart->truncate(); // Reset cart everytime this function is called
35
+ $quote = $cart->getQuote();
36
+
37
+
38
+ //add products
39
+ $this->getProductsInQuote($quote,$products,false);
40
+
41
+ $cart->save();
42
+ $quote->save();
43
+ Mage::getSingleton('checkout/session')->setCartWasUpdated(true);
44
+ }
45
+
46
+
47
+ /**
48
+ * Can retrieve an existing quote, or create a new (temporary) quote with the given objects
49
+ * Purpose of this method is to return all products that exist in the cart, all shipping information and the totals
50
+ *
51
+ * @param array An array of product SKU's to fill the cart. Format identical to fillCartWithProductsAndQuantities
52
+ * @param quote_id (optional) The quote_id for which you would like to return the information
53
+ */
54
+ public function getQuoteWithProductsAndQuantities($products = false, $quote_id = -1) {
55
+ if ($products === false && $quote_id == -1) {
56
+ return;
57
+ }
58
+
59
+ $response = array();
60
+
61
+ Mage::getSingleton('checkout/session')->setQuoteId(null);
62
+
63
+
64
+ $quote = null;
65
+
66
+ if($quote_id == -1) {
67
+ $cart = Mage::getModel('checkout/cart');
68
+ $cart->init();
69
+ $cart->truncate(); // Reset cart everytime this function is called
70
+ $quote = $cart->getQuote();
71
+ } else {
72
+ $quote = Mage::getModel('sales/quote')->load($quote_id);
73
+ if(!$quote->getId()) {
74
+ return null;
75
+ }
76
+ }
77
+
78
+ if($products) {
79
+ $this->getProductsInQuote($quote,$products);
80
+ }
81
+
82
+
83
+ //Shipping carries
84
+ $response['selected_shipping_method'] = $this->getSelectedShippingMethod($quote);
85
+
86
+
87
+
88
+ $config = Mage::helper('highstreet_hsapi/config_api');
89
+
90
+ //Some stores don't want to return the shipping methods in the cart (that is: when the selected_shipping_method is not set yet)
91
+ //e.g. PME doesn't want this for performance optimization, everytime the cart is openend the magento backend will connect to Paazl, which is quite expensive.
92
+ //Therefore they one return shipping info once the user acutally a has selected a shipping method.
93
+ //For PME we also know that shipping is always free, so the rewriter add 'price: 0' to the response.
94
+ if ($config->shippingInCartDisabled() && $response['selected_shipping_method'] === null) {
95
+ $response['shipping'] = array();
96
+ } else {
97
+ $response['shipping'] = array_values($this->getShippingMethods($quote, $response['selected_shipping_method']));
98
+ }
99
+
100
+
101
+
102
+ $response["totals"] = $this->getQuoteTotals($quote);
103
+
104
+ $quoteItems = $quote->getAllVisibleItems();
105
+
106
+ $responseQuote = array();
107
+
108
+ foreach($quoteItems as $quoteItem) {
109
+ $product_hash = $this->_getQuoteItemHash($quoteItem);
110
+ $responseQuote[] = array_merge($this->getProductInQuoteResponse($quoteItem),$this->_getErrorForProduct($product_hash));
111
+ }
112
+
113
+ foreach($this->_productIdsNotAdded as $product_hash) {
114
+ $responseQuote[] = array_merge(array("quantity" => 0,"hash" => $product_hash),$this->_getErrorForProduct($product_hash));
115
+
116
+ }
117
+
118
+ $response["quote"] = $responseQuote;
119
+
120
+
121
+ return $response;
122
+
123
+
124
+
125
+ }
126
+
127
+ /**
128
+ * Retrieves order information
129
+ *
130
+ * @param Object Order object
131
+ * @param string Status, overwrites the status if needed
132
+ * @param int Quote_id, fallback quote_id for error when the order object is empty
133
+ * @param bool Overwrite total due, needed for the `sales_order_invoice_pay` because here the total due is still filled in but the order is actually paid fully
134
+ * @return array Object with information about the order
135
+ */
136
+
137
+ public function getOrderInformationFromOrderObject ($order, $quote_id, $status = '') {
138
+ // We use 'loadByIdWithoutStore' instead of 'load' because if this event gets triggered by a status update from the admin backend the 'admin storefront' gets set, which doesn't have any quotes.
139
+ $quote = Mage::getModel('sales/quote')->loadByIdWithoutStore($order->getQuoteId());
140
+
141
+ if ($quote === false || $quote->getId() == false || $order === false || $order->getId() == false) {
142
+ return array("error" => 1, "state" => "quote not found", "quote_id" => $quote_id);
143
+ }
144
+
145
+ if ($status == '') {
146
+ if ($order->getData('total_due') < 1) {
147
+ $status = 'PAYMENT_SUCCESS';
148
+ } else {
149
+ $status = 'PAYMENT_DUE';
150
+ }
151
+ }
152
+
153
+ $response = array();
154
+
155
+ $response['order_id'] = $quote->getData('reserved_order_id');
156
+ $response['quote_id'] = $quote->getData('entity_id');
157
+ $response['error'] = 0;
158
+ $response['state'] = "success";
159
+ $response['remote_ip'] = $quote->getData('remote_ip');
160
+ $response['currency'] = $quote->getData('quote_currency_code');
161
+ $response['invoice_state'] = $order->getData('status');
162
+ $response['order_status'] = $status;
163
+
164
+
165
+ // Get totals
166
+ $totals = array();
167
+ $totals['total_due'] = $order->getData('total_due');
168
+ $totals['grand_total'] = $order->getData('grand_total');
169
+ $totals['discount_amount'] = $order->getData('discount_amount');
170
+ $totals['tax_amount'] = $order->getData('tax_amount');
171
+ $totals['shipping_amount'] = $order->getData('shipping_amount');
172
+ $response['totals'] = $totals;
173
+
174
+ $response['products'] = array();
175
+ foreach ($quote->getAllVisibleItems() as $product) {
176
+ array_push($response['products'], $this->getProductInQuoteResponse($product));
177
+ }
178
+
179
+ return $response;
180
+ }
181
+
182
+ /**
183
+ * Gives all the information to make the checkout work from the initial loading of the page.
184
+ */
185
+
186
+ public function getStartData() {
187
+ $customerSession = Mage::getSingleton('customer/session');
188
+ $customer = $customerSession->getCustomer();
189
+ $isLoggedIn = $customerSession->isLoggedIn();
190
+
191
+ $accountAddressFound = false;
192
+
193
+ $billingAddressData = array();
194
+ $shippingAddressData = array();
195
+
196
+ if ($isLoggedIn) {
197
+ $defaultBillingAddressId = $customer->getDefaultBilling();
198
+ $defaultShippingAddressId = $customer->getDefaultShipping();
199
+
200
+ if ($defaultBillingAddressId > 0) {
201
+ $accountAddressFound = true;
202
+
203
+ $billingAddress = $customer->getAddressById($defaultBillingAddressId);
204
+ $billingAddressData = $billingAddress->getData();
205
+ $billingAddressData["email"] = $customer->getEmail();
206
+
207
+ if ($defaultBillingAddressId != $defaultShippingAddressId) {
208
+ $shippingAddress = $customer->getAddressById($defaultShippingAddressId);
209
+ $shippingAddressData = $shippingAddress->getData();
210
+ }
211
+ }
212
+ }
213
+
214
+ $session = Mage::getSingleton('checkout/session');
215
+ $quote = $session->getQuote();
216
+
217
+ if (!$isLoggedIn || !$accountAddressFound) {
218
+ $billingAddress = $quote->getBillingAddress();
219
+ $billingAddressData = $billingAddress->getData();
220
+ $shippingAddress = $quote->getShippingAddress();
221
+ $shippingAddressData = $shippingAddress->getData();
222
+ }
223
+
224
+ $response = array();
225
+
226
+ $response["is_logged_in"] = Mage::getSingleton('customer/session')->isLoggedIn();
227
+ $response["quote_id"] = $quote->getEntityId();
228
+ $response["coupon_code"] = $quote->getCouponCode();
229
+
230
+ if ($quote->getItemsCount() > 0) {
231
+ $response["error"] = 0;
232
+ } else {
233
+ $response["error"] = -1;
234
+ }
235
+
236
+ if ($billingAddressData["firstname"] !== null) {
237
+ $billingAddressResponse = array();
238
+
239
+ $billingAddressResponse["email"] = $billingAddressData["email"];
240
+ $billingAddressResponse["firstname"] = $billingAddressData["firstname"];
241
+ $billingAddressResponse["lastname"] = $billingAddressData["lastname"];
242
+ $billingAddressResponse["telephone"] = (string) $billingAddressData["telephone"];
243
+ $billingAddressResponse["street"] = $billingAddress->getStreet();
244
+ $billingAddressResponse["postcode"] = $billingAddressData["postcode"];
245
+ $billingAddressResponse["city"] = $billingAddressData["city"];
246
+ $billingAddressResponse["country_id"] = $billingAddressData["country_id"];
247
+
248
+ $response["billing_address"] = $billingAddressResponse;
249
+
250
+ $shippingAddressResponse = array();
251
+ if ($shippingAddressData["firstname"] !== null) {
252
+ $shippingAddressResponse["firstname"] = $shippingAddressData["firstname"];
253
+ $shippingAddressResponse["lastname"] = $shippingAddressData["lastname"];
254
+ $shippingAddressResponse["telephone"] = (string) $shippingAddressData["telephone"];
255
+ $shippingAddressResponse["street"] = $shippingAddress->getStreet();
256
+ $shippingAddressResponse["postcode"] = $shippingAddressData["postcode"];
257
+ $shippingAddressResponse["city"] = $shippingAddressData["city"];
258
+ $shippingAddressResponse["country_id"] = $shippingAddressData["country_id"];
259
+
260
+ if (!$this->_billingAndShippingAddressesAreTheSame($response["billing_address"], $shippingAddressResponse)) {
261
+ $response["shipping_address"] = $shippingAddressResponse;
262
+ } else {
263
+ $response["shipping_address"] = array();
264
+ }
265
+ } else {
266
+ $response["shipping_address"] = array();
267
+ }
268
+ } else {
269
+ $response["billing_address"] = array();
270
+ $response["shipping_address"] = array();
271
+ }
272
+
273
+ return $response;
274
+ }
275
+
276
+
277
+
278
+ //Helpers below
279
+
280
+ private function getProductsInQuote($quote,$products = null,$capAmount = true) {
281
+ $responseQuote = array();
282
+
283
+
284
+
285
+
286
+ //loop through the requested products
287
+ foreach ($products as $key => $value) {
288
+ $product_id = $value["product_id"];
289
+ $product_hash = $this->_getQuoteItemRequestHash($value);
290
+
291
+ if (empty($product_id)) {
292
+ continue;
293
+ }
294
+
295
+
296
+ $errorMessage = null;
297
+
298
+ try {
299
+ $product = $this->loadProduct($product_id);
300
+ } catch (Exception $e) {
301
+ $errorMessage = $e->getMessage();
302
+ $product = null;
303
+ }
304
+
305
+ if ($product === null || !$product->getId()) {
306
+ $this->_reportErrorForProduct($product_hash,400,$errorMessage);
307
+ continue;
308
+ }
309
+
310
+ //input variables
311
+ $requestedQuantity = ($product->getStatus() != Mage_Catalog_Model_Product_Status::STATUS_ENABLED) ? 0 : $value["quantity"];
312
+ $configuration = array_key_exists("configuration", $value) ? $value["configuration"] : null;
313
+ $bundle_selections = array_key_exists("bundle_selections", $value) ? $value["bundle_selections"] : null;
314
+ $simple_product = null;
315
+ $quoteItem = null;
316
+
317
+ $quote_item_additional_info = null; //additional info that should be stored in the quote item
318
+
319
+ if($product->getTypeId() == 'simple') {
320
+ $simple_product = $product;
321
+ } else if($product->getTypeId() == 'configurable') {
322
+ $simple_product = $this->loadProduct($value['simple_product_id']);
323
+
324
+ //if 'alwaysAddSimpleProductsToCart' is set, then we add the simple product of a configurable product to the cart
325
+ $config = Mage::helper('highstreet_hsapi/config_api');
326
+ if($config->alwaysAddSimpleProductsToCart()) {
327
+ $quote_item_additional_info = array("parent_product_id" => $product_id);
328
+ $product = $simple_product;
329
+ }
330
+ }
331
+
332
+ if($simple_product !== null) {
333
+ $quoteItem = $quote->getItemByProduct($simple_product);
334
+ }
335
+
336
+
337
+ //Check for inventory (only for simple and configurable)
338
+
339
+ $actualQuantity = 0;
340
+ if($simple_product !== null && $capAmount) {
341
+
342
+ $itemInventory = Mage::getModel('cataloginventory/stock_item')->loadByProduct($simple_product);
343
+
344
+
345
+ $actualQuantity = $requestedQuantity; //actual qty is what we are going to add
346
+
347
+ //adjust actual quantity if we are requesting more than in stock
348
+ $availableQuantity = $itemInventory->getQty();
349
+ $isInStock = $itemInventory->getIsInStock();
350
+ $isStockManaged = $itemInventory->getManageStock();
351
+ $backordersAllowed = $itemInventory->getBackorders();
352
+
353
+
354
+ if($isStockManaged) {
355
+
356
+ if(!$isInStock) {
357
+ $this->_reportErrorForProduct($product_hash,101,"Product is not in stock");
358
+ $actualQuantity = 0;
359
+ } else {
360
+ //in stock, but should we cap it?
361
+ if(!$backordersAllowed && $requestedQuantity > $availableQuantity) {
362
+ $actualQuantity = $availableQuantity; //cap
363
+ $this->_reportErrorForProduct($product_hash,102,"Requested quantity is not available, added ".(int)$actualQuantity." instead of ".$requestedQuantity ." products with id ".$value["id"]." to the cart",false);
364
+ //product can be added, but with a lower quantity
365
+ //Note: even though the actualQuantity might be set to 0, we still do not return a 101, because a qty of 0 does not necessarily make a product out of stock
366
+ //"Qty for Item's Status to Become Out of Stock" might be a negative integer
367
+ }
368
+
369
+ }
370
+
371
+ }
372
+
373
+ } else {
374
+ $actualQuantity = $requestedQuantity;
375
+
376
+ }
377
+
378
+
379
+
380
+
381
+ //add to cart
382
+
383
+
384
+ try {
385
+
386
+
387
+ if($quoteItem) { //adjust existing entry
388
+
389
+ $quoteItem->setQty($actualQuantity);
390
+ } else { //or add new entry (but of course only when qty > 0)
391
+
392
+ if($actualQuantity > 0) { //do this check because the app might request a quantity of 0 for a product. If you call the function below with $actualQuantity = 0, it will still add one product to the cart
393
+
394
+
395
+ //simple
396
+ if($product->getTypeId() == 'simple') {
397
+ $quoteItem = $quote->addProduct($product,new Varien_Object(array("qty" => $actualQuantity)));
398
+ }
399
+
400
+ if($product->getTypeId() == 'configurable') {
401
+
402
+ $requestConfiguration = array();
403
+ foreach($configuration as $configuration_option) {
404
+ $attributeCode = $configuration_option['attribute_code'];
405
+ $attribute = Mage::getModel('eav/config')->getAttribute('catalog_product', $attributeCode);
406
+
407
+ $requestConfiguration[$attribute->getId()] = $configuration_option['attribute_value'];
408
+ }
409
+
410
+ $configurations = array('super_attribute' => $requestConfiguration);
411
+ $options = array_merge(array("qty" => $actualQuantity),$configurations);
412
+ $quoteItem = $quote->addProduct($product,new Varien_Object($options));
413
+
414
+ }
415
+
416
+ if($product->getTypeId() == 'bundle') {
417
+ $bundle_option = array();
418
+ $bundle_option_qty = array();
419
+
420
+ foreach($bundle_selections as $bundle_selection) {
421
+ $option_id = $bundle_selection["option_id"];
422
+ $bundle_option[$option_id] = $bundle_selection["selection_ids"];
423
+ if($bundle_selection["quantity"])
424
+ $bundle_option_qty[$option_id] = $bundle_selection["quantity"];
425
+
426
+ }
427
+
428
+ $options = array("qty" => $actualQuantity, "bundle_option" => $bundle_option,"bundle_option_qty" => $bundle_option_qty );
429
+
430
+
431
+ $quoteItem = $quote->addProduct($product,new Varien_Object($options));
432
+ }
433
+
434
+ $parentQuoteItem = $quoteItem->getParentItem();
435
+ if($parentQuoteItem) {
436
+ $quoteItem = $parentQuoteItem;
437
+ }
438
+
439
+ if($quote_item_additional_info) {
440
+ $quoteItem->setAdditionalData(json_encode($quote_item_additional_info));
441
+ }
442
+
443
+
444
+ } else {
445
+ if (!in_array($product_hash,$this->_productIdsNotAdded)) {
446
+ $this->_productIdsNotAdded[] = $product_hash;
447
+ }
448
+ }
449
+ }
450
+
451
+
452
+
453
+
454
+ } catch (Exception $e) {
455
+ $this->_reportErrorForProduct($product_hash,400,$e->getMessage());
456
+
457
+
458
+ }
459
+
460
+
461
+
462
+ }
463
+
464
+
465
+ }
466
+
467
+ private function _reportErrorForProduct($product_hash,$errorCode,$errorMessage,$notAdded = true) {
468
+ $this->_errorCodes[$product_hash] = $errorCode;
469
+ $this->_errorMessages[$product_hash] = $errorMessage;
470
+
471
+ if($notAdded) {
472
+ $this->_productIdsNotAdded[] = $product_hash;
473
+ }
474
+ }
475
+
476
+ private function _getErrorForProduct($product_hash) {
477
+ $response = array();
478
+ $response["errorCode"] = 0;
479
+ $response["errorMessage"] = null;
480
+
481
+ if (array_key_exists($product_hash, $this->_errorCodes)) {
482
+ $response["errorCode"] = $this->_errorCodes[$product_hash];
483
+ }
484
+
485
+ if (array_key_exists($product_hash, $this->_errorMessages)) {
486
+ $response["errorMessage"] = $this->_errorMessages[$product_hash];
487
+ }
488
+
489
+ return $response;
490
+ }
491
+
492
+
493
+
494
+ private function getProductInQuoteResponse($quoteItem = null) {
495
+
496
+ $product = $quoteItem->getProduct();
497
+ $parent_product_id = $this->_getQuoteItemParentProductId($quoteItem);
498
+
499
+
500
+ $productInQuote = array();
501
+ $productInQuote["product_id"] = $parent_product_id ? $parent_product_id : $product->getId();
502
+
503
+ $productInQuote["name"] = $product->getName();
504
+ $productInQuote["sku"] = $product->getSku();
505
+
506
+
507
+
508
+ $quantity = $quoteItem ? $quoteItem->getQty() : 0;
509
+
510
+ $productInQuote["finalPrice"] = $quoteItem->getPriceInclTax();
511
+ $productInQuote["tax"] = $quoteItem->getPriceInclTax() - $quoteItem->getPrice();
512
+
513
+ //The custom price is set by the Amasty - Auto Add Promo Items extension. The extra free product has a custom price, and if it is set we should use that price.
514
+ //This price is already used (automatically) in the total calculations
515
+ if($quoteItem->getCustomPrice() !== null) {
516
+ $productInQuote["finalPrice"] = $quoteItem->getCustomPrice();
517
+ }
518
+
519
+
520
+ $productInQuote["quantity"] = $quantity;
521
+
522
+
523
+ $productInQuote["hash"] = $this->_getQuoteItemHash($quoteItem);
524
+
525
+
526
+ return $productInQuote;
527
+ }
528
+
529
+ private function _getQuoteItemHash($quoteItem) {
530
+ $product = $quoteItem->getProduct();
531
+
532
+ $hash = null;
533
+
534
+ $parent_product_id = $this->_getQuoteItemParentProductId($quoteItem);
535
+
536
+
537
+ $type = $product->getTypeId();
538
+
539
+ if($type == 'simple') {
540
+ if($parent_product_id == null) {
541
+ $hash = $product->getId();
542
+ } else {
543
+ //actually a configurable product, but the child was added instead of the parent
544
+ $hash = $parent_product_id . ":" . $product->getId();
545
+ }
546
+ }
547
+
548
+ if($type == 'configurable') {
549
+ $children = $quoteItem->getChildren();
550
+ $childQuoteItem = current($children);
551
+ if ($childQuoteItem)
552
+ $hash = $product->getId() . ":" . $childQuoteItem->getProduct()->getId();
553
+ else
554
+ $hash = $product->getId();
555
+
556
+ }
557
+
558
+ if($type == 'bundle') {
559
+ $hash = $product->getId().":";
560
+
561
+ $options = $product->getTypeInstance(true)->getOrderOptions($product);
562
+
563
+ $bundle_option = $options["info_buyRequest"]["bundle_option"];
564
+ ksort($bundle_option);
565
+
566
+
567
+ $hashElements = array();
568
+
569
+ foreach($bundle_option as $option_id => $selection_ids) {
570
+
571
+ if (!is_array($selection_ids))
572
+ $selection_ids = array($selection_ids);
573
+
574
+
575
+ $quantity = round($options["info_buyRequest"]["bundle_option_qty"][$option_id]);
576
+
577
+ sort($selection_ids);
578
+ $selection_ids_string = implode(",",$selection_ids);
579
+ $hashElements[] = "($option_id,[$selection_ids_string],$quantity)";
580
+
581
+ }
582
+
583
+ $hash .= '['.implode(",",$hashElements).']';
584
+
585
+
586
+
587
+ }
588
+
589
+ if($parent_product_id) {
590
+ $type = 'configurable'; //the product is actually a configurable product
591
+ }
592
+
593
+ return $type."-".$hash;
594
+
595
+
596
+ }
597
+
598
+ private function _compareBundleSelection($a, $b) {
599
+ if ($a["option_id"] == $b["option_id"]) {
600
+ return 0;
601
+ }
602
+ return ($a["option_id"] < $b["option_id"]) ? -1 : 1;
603
+ }
604
+
605
+ private function _getQuoteItemRequestHash($quoteItemRequest) {
606
+
607
+ $hash = null;
608
+ $type = 'simple';
609
+ if(array_key_exists('simple_product_id', $quoteItemRequest)) {
610
+ $type = 'configurable';
611
+ }
612
+ if(array_key_exists('bundle_selections', $quoteItemRequest)) {
613
+ $type = 'bundle';
614
+ }
615
+
616
+ if($type == 'simple') {
617
+ $hash = $quoteItemRequest['product_id'];
618
+ }
619
+
620
+ if($type == 'configurable') {
621
+ $hash = $quoteItemRequest['product_id'] . ":" . $quoteItemRequest['simple_product_id'];
622
+ }
623
+
624
+ if($type == 'bundle') {
625
+ $hash = $quoteItemRequest['product_id'].":";
626
+
627
+
628
+ $hashElements = array();
629
+
630
+ $selections = $quoteItemRequest["bundle_selections"];
631
+
632
+
633
+ usort($selections,array($this,"_compareBundleSelection"));
634
+
635
+
636
+ foreach($selections as $bundle_selection) {
637
+ $option_id = $bundle_selection["option_id"];
638
+ $selection_ids = $bundle_selection["selection_ids"];
639
+ sort($selection_ids);
640
+ $quantity = $bundle_selection["quantity"];
641
+
642
+ $selection_ids_string = implode(",",$selection_ids);
643
+ $hashElements[] = "($option_id,[$selection_ids_string],$quantity)";
644
+ }
645
+
646
+
647
+ $hash .= '['.implode(",",$hashElements).']';
648
+
649
+
650
+
651
+ }
652
+
653
+ return $type."-".$hash;
654
+
655
+
656
+ }
657
+
658
+ private function _getQuoteItemParentProductId($quoteItem) {
659
+ $additionalInfo = $quoteItem->getAdditionalData();
660
+ if($additionalInfo) {
661
+ $json = json_decode($additionalInfo,true);
662
+ return $json['parent_product_id'];
663
+ }
664
+ return null;
665
+ }
666
+
667
+ private function getQuoteTotals($quote) {
668
+
669
+
670
+ $quote->save()->collectTotals(); //required to fetch the totals
671
+
672
+ //Totals
673
+ $totals = $quote->getTotals(); //Total object
674
+ $subtotal = $totals["subtotal"]->getValue(); //Subtotal value
675
+ $grandtotal = $totals["grand_total"]->getValue(); //Grandtotal value
676
+
677
+ $discount = 0;
678
+ if(isset($totals['discount']) && $totals['discount']->getValue()) {
679
+ $discount = $totals['discount']->getValue(); //Discount value if applied
680
+ }
681
+ $tax = 0;
682
+ if(isset($totals['tax']) && $totals['tax']->getValue()) {
683
+ $tax = $totals['tax']->getValue(); //Tax value if present
684
+ }
685
+
686
+ $totalItemsInCart = 0;
687
+ foreach($quote->getAllVisibleItems() as $quoteItem) {
688
+ $totalItemsInCart++;
689
+ }
690
+
691
+
692
+ $responseTotals = array();
693
+ $responseTotals["totalItemsInCart"] = $totalItemsInCart;
694
+ $responseTotals["subtotal"] = $subtotal;
695
+ $responseTotals["grandtotal"] = $grandtotal;
696
+ $responseTotals["discount"] = $discount;
697
+ $responseTotals["tax"] = $tax;
698
+
699
+ return $responseTotals;
700
+ }
701
+
702
+
703
+
704
+ private function getSelectedShippingMethod($quote) {
705
+ $quoteShippingAddress = $quote->getShippingAddress();
706
+ $quoteShippingAddress->collectTotals(); //to make sure all available shipping methods are listed
707
+
708
+ $quoteShippingAddress->collectShippingRates()->save(); //collect the rates
709
+
710
+ $chosenShippingMethod = $quoteShippingAddress->getShippingMethod();
711
+
712
+ if ($chosenShippingMethod === "") {
713
+ $chosenShippingMethod = null;
714
+ }
715
+
716
+ return $chosenShippingMethod;
717
+ }
718
+
719
+ private function getShippingMethods($quote, $selectedShippingMethod) {
720
+ $responseCarriers = array();
721
+
722
+ $quoteShippingAddress = $quote->getShippingAddress();
723
+ $quoteShippingAddress->collectTotals(); //to make sure all available shipping methods are listed
724
+
725
+ $quoteShippingAddress->collectShippingRates()->save(); //collect the rates
726
+ $groupedRates = $quoteShippingAddress->getGroupedAllShippingRates();
727
+
728
+ foreach ($groupedRates as $carrierCode => $rates ) {
729
+ foreach ($rates as $rate) {
730
+ $price = $rate->getPrice();
731
+ if ($rate->getCode() == $selectedShippingMethod) {
732
+ $quoteShippingAddress->setShippingMethod($selectedShippingMethod);
733
+ $quote->collectTotals()->save();
734
+
735
+ $price = $quoteShippingAddress->getShippingInclTax();
736
+ }
737
+
738
+ $responseRate = array();
739
+ $responseRate["carrier"] = $rate->getCarrier();
740
+ $responseRate["carrierTitle"] = $rate->getCarrierTitle();
741
+ $responseRate["carrierCode"] = $rate->getCode();
742
+
743
+ $responseRate["method"] = $rate->getMethod();
744
+ $responseRate["methodTitle"] = $rate->getMethodTitle();
745
+ $responseRate["methodDescription"] = $rate->getMethodDescription();
746
+ $responseRate["price"] = $price;
747
+ $responseCarriers[] = $responseRate;
748
+ }
749
+ }
750
+
751
+ return $responseCarriers;
752
+ }
753
+
754
+ private function loadProduct($productId = null) {
755
+ if(!$productId)
756
+ return null;
757
+
758
+ $productModel = Mage::getModel('catalog/product');
759
+ $product = $productModel->load($productId);
760
+ if (!$product->getId())
761
+ return null; //product does not exist
762
+
763
+ return $product;
764
+
765
+ }
766
+
767
+ private function _getParentProduct($product) {
768
+ $config = Mage::helper('highstreet_hsapi/config_api');
769
+ if ($config->alwaysAddSimpleProductsToCart()) {
770
+ return null;
771
+ }
772
+
773
+ $parentIds = Mage::getModel('catalog/product_type_configurable')->getParentIdsByChild($product->getId());
774
+ $parent = null;
775
+ if(isset($parentIds[0])){
776
+ $parent = Mage::getModel('catalog/product')->load($parentIds[0]);
777
+ }
778
+
779
+ if($parent->getId() === null)
780
+ return null;
781
+
782
+ return $parent;
783
+ }
784
+
785
+ /**
786
+ * Convenience method, compares 2 formatted address arrays
787
+ */
788
+ private function _billingAndShippingAddressesAreTheSame($billingAddressArray = array(), $shippingAddressArray = array()) {
789
+ if (count($billingAddressArray) == 0 || count($shippingAddressArray) == 0) {
790
+ return true;
791
+ }
792
+
793
+ return (($billingAddressArray["firstname"] == $shippingAddressArray["firstname"]) &&
794
+ ($billingAddressArray["lastname"] == $shippingAddressArray["lastname"]) &&
795
+ ($billingAddressArray["telephone"] == $shippingAddressArray["telephone"]) &&
796
+ ($billingAddressArray["street"] == $shippingAddressArray["street"]) &&
797
+ ($billingAddressArray["postcode"] == $shippingAddressArray["postcode"]) &&
798
+ ($billingAddressArray["city"] == $shippingAddressArray["city"]));
799
+ }
800
+
801
+
802
+ }
app/code/local/Highstreet/Hsapi/Model/Images.php CHANGED
@@ -27,7 +27,11 @@ class Highstreet_Hsapi_Model_Images extends Mage_Core_Model_Abstract
27
 
28
  $originalSize = $image->getOriginalSize();
29
  if ($size) {
30
- list($width, $height) = explode('x', $size);
 
 
 
 
31
 
32
  if (!empty($width) && !empty($height)) {
33
  $image->resize($width, $height);
27
 
28
  $originalSize = $image->getOriginalSize();
29
  if ($size) {
30
+
31
+ $explodedSize = explode('x', $size);
32
+ if (count($explodedSize) > 1) {
33
+ list($width, $height) = $explodedSize;
34
+ }
35
 
36
  if (!empty($width) && !empty($height)) {
37
  $image->resize($width, $height);
app/code/local/Highstreet/Hsapi/Model/Observer.php CHANGED
@@ -1,22 +1,65 @@
1
  <?php
2
- class Highstreet_Hsapi_Model_Observer extends Varien_Event_Observer
3
- {
 
4
  public function __construct()
5
  {
6
  }
 
7
  public function mergeQuote($observer)
8
  {
9
  $event = $observer->getEvent();
10
  $quote = $event->getQuote();
11
- exec("echo Quote".$quote->getId()." > /tmp/bla");
12
 
13
  foreach ($quote->getAllItems() as $item) {
14
  $quote->removeItem($item->getId());
15
  }
16
 
17
  }
18
-
19
 
20
- }
21
 
22
- ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  <?php
2
+
3
+ class Highstreet_Hsapi_Model_Observer {
4
+
5
  public function __construct()
6
  {
7
  }
8
+
9
  public function mergeQuote($observer)
10
  {
11
  $event = $observer->getEvent();
12
  $quote = $event->getQuote();
 
13
 
14
  foreach ($quote->getAllItems() as $item) {
15
  $quote->removeItem($item->getId());
16
  }
17
 
18
  }
 
19
 
 
20
 
21
+ public function salesOrderInvoicePay(Varien_Event_Observer $observer) {
22
+ $checkoutModel = Mage::getModel('highstreet_hsapi/checkoutV2');
23
+ $order = $observer->getEvent()->getInvoice()->getOrder();
24
+ $this->_communicateOrderEvent($order, '');
25
+ }
26
+
27
+
28
+ public function salesOrderInvoiceCancel(Varien_Event_Observer $observer) {
29
+ $checkoutModel = Mage::getModel('highstreet_hsapi/checkoutV2');
30
+ $order = $observer->getEvent()->getInvoice()->getOrder();
31
+ $this->_communicateOrderEvent($order, 'PAYMENT_CANCELED');
32
+ }
33
+
34
+
35
+ private function _communicateOrderEvent($order, $status = '') {
36
+ if ($order->getQuoteId() > 0) {
37
+ $encryptionHelper = Mage::helper('highstreet_hsapi/encryption');
38
+ $quoteIdHash = $encryptionHelper->hashQuoteId($order->getQuoteId());
39
+
40
+ $isHighstreetOrder = false;
41
+ foreach ($order->getStatusHistoryCollection(true) as $comment) {
42
+ if (strstr($comment->getData('comment'), $quoteIdHash) !== false) {
43
+ $isHighstreetOrder = true;
44
+ break;
45
+ }
46
+ }
47
+
48
+ $configHelper = Mage::helper('highstreet_hsapi/config_api');
49
+ $middleWareUrl = $configHelper->middlewareUrl();
50
+
51
+ if ($isHighstreetOrder && $middleWareUrl !== NULL) {
52
+ $checkoutModel = Mage::getModel('highstreet_hsapi/checkoutV2');
53
+ $data = $checkoutModel->getOrderInformationFromOrderObject($order, $order->getQuoteId(), $status);
54
+
55
+ $ch = curl_init($middleWareUrl . "/orders/" . $order->getQuoteId());
56
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
57
+ curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
58
+ curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5); // Time CURL takes to wait for a connection to our server, 0 is indefinitely
59
+ curl_setopt($ch, CURLOPT_TIMEOUT, 15); // Maximum time CURL takes to execute
60
+ curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data, JSON_NUMERIC_CHECK));
61
+ $output = curl_exec($ch);
62
+ }
63
+ }
64
+ }
65
+ }
app/code/local/Highstreet/Hsapi/Model/Products.php CHANGED
@@ -8,12 +8,6 @@
8
  */
9
  class Highstreet_Hsapi_Model_Products extends Mage_Core_Model_Abstract
10
  {
11
- /**
12
- * @var bool
13
- */
14
- private $_addConfigurableAttributes = false;
15
- private $_addConfigurations = false;
16
-
17
  const PRODUCTS_MEDIA_PATH = '/media/catalog/product';
18
  const NO_IMAGE_PATH = 'no_selection';
19
  const RANGE_FALLBACK_RANGE = 100;
@@ -22,49 +16,86 @@ class Highstreet_Hsapi_Model_Products extends Mage_Core_Model_Abstract
22
  /**
23
  * Gets a single product for a given productId and attributes
24
  *
25
- * @param integer ProductId, product id of the product to be gotten
26
- * @param string Attributes, string of attributes straight from the URL
 
 
27
  * @return array Product
28
  */
29
- public function getSingleProduct($productId = false, $attributes, $child_product_attributes)
30
  {
31
- if (!$productId) {
32
- return nil;
33
  }
34
-
35
- $product = Mage::getModel('catalog/product')->load($productId);
36
 
37
- return $this->_getProductAttributes($product, $attributes, $child_product_attributes);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  }
39
 
40
  /**
41
  * Gets products with attributes for given order, range, filters, search and categoryId
42
  *
43
- * @param string Attributes, string of attributes straight from the URL
44
- * @param string Child product attributes, string of attributes for the child products, comma sepperated
45
  * @param string Order, order for the products
46
  * @param string Range of products. Must formatted like "0,10" where 0 is the offset and 10 is the count
47
  * @param string Search string for filtering on keywords
48
  * @param integer CategoryId, category id of the category which will be used to filter
 
 
 
 
49
  * @return array Product
50
  */
51
- public function getProductsForResponse($attributes, $child_product_attributes, $order, $range, $filters, $search, $categoryId)
52
  {
53
- $addGalleryImages = false;
54
  $searching = !empty($search);
55
 
 
56
  // get attributes
57
- if (!empty($attributes)) {
58
- $attributesArray = explode(',', $attributes);
59
-
60
- if(in_array('configurable_attributes',$attributesArray)){
61
- $this->_addConfigurableAttributes = true;
62
- }
63
- if(in_array('configurations',$attributesArray)){
64
- $this->_addConfigurations = true;
65
- }
66
  }
67
 
 
 
68
  // get order
69
  if (!empty($order)) {
70
  $order = explode(',', $order);
@@ -125,7 +156,12 @@ class Highstreet_Hsapi_Model_Products extends Mage_Core_Model_Abstract
125
  $categoryId = Mage::app()->getStore()->getRootCategoryId();
126
  $categoryNotSet = true;
127
  }
 
128
  $category = Mage::getModel('catalog/category')->load($categoryId);
 
 
 
 
129
  // apply search
130
  if ($categoryId && !$categoryNotSet) {
131
  $collection->addCategoryFilter($category);
@@ -135,27 +171,14 @@ class Highstreet_Hsapi_Model_Products extends Mage_Core_Model_Abstract
135
  $range = explode(',', $range);
136
  }
137
 
138
- if (!empty($range)) {
139
- $collection->getSelect()->limit($range[1], $range[0]);
140
- } else {
141
- $collection->getSelect()->limit(self::RANGE_FALLBACK_RANGE);
142
- }
143
-
144
- // apply attributes
145
- if (!empty($attributesArray)) {
146
- foreach ($attributesArray as $attribute) {
147
- $collection->addAttributeToSelect($attribute);
148
- if ($attribute == 'media_gallery') {
149
- $addGalleryImages = true;
150
- }
151
- }
152
  }
153
- else {
154
 
155
- // select all attributes
156
- $collection->addAttributeToSelect('*');
157
- $addGalleryImages = true;
158
- }
159
 
160
  //apply filters
161
  if(!empty($filters)) {
@@ -169,10 +192,9 @@ class Highstreet_Hsapi_Model_Products extends Mage_Core_Model_Abstract
169
  }
170
  }
171
  }
172
-
173
 
174
- // Apply type filter, we only want Simple and Configurable products in our API
175
- $collection->addAttributeToFilter('type_id', array('simple', 'configurable'));
176
 
177
  // apply order
178
  if (!empty($order)) {
@@ -217,12 +239,6 @@ class Highstreet_Hsapi_Model_Products extends Mage_Core_Model_Abstract
217
  }
218
  }
219
 
220
-
221
- // Add media gallery to collection we cant do this in an earlier stage because it gives really strange results (filter not working, range not working!)
222
- if ($addGalleryImages) {
223
- $this->_addMediaGalleryAttributeToCollection($collection);
224
- }
225
-
226
  if (!isset($productCount)) {
227
  // get total product count
228
  $productCount = $collection->getSize();
@@ -232,13 +248,22 @@ class Highstreet_Hsapi_Model_Products extends Mage_Core_Model_Abstract
232
  */
233
  $products = array('products' => array());
234
 
235
- foreach($collection as $product) {
236
- array_push($products['products'], $this->_getProductAttributes($product, $attributes, $child_product_attributes));
 
 
 
 
 
 
 
237
  }
238
 
239
- $resultFilters = $this->getFilters($categoryId);
240
-
241
- $products['filters'] = $resultFilters;
 
 
242
  $products['product_count'] = $productCount;
243
 
244
  $rangeLength = $range[1];
@@ -257,14 +282,16 @@ class Highstreet_Hsapi_Model_Products extends Mage_Core_Model_Abstract
257
  * Gets products for a set of product id's
258
  *
259
  * @param array productIds, product id's to filter on
260
- * @param string Attributes, comma seperated string of attributes
261
- * @param string Child products attributes, comma seperated string of attributes for the child products
262
  * @param string range, formatted range string
 
 
 
263
  * @return array Array of products
264
  *
265
  */
266
 
267
- public function getProductsFilteredByProductIds($productIds = false, $attributes, $child_product_attributes, $range) {
268
 
269
  $products = array('products' => array());
270
 
@@ -274,34 +301,33 @@ class Highstreet_Hsapi_Model_Products extends Mage_Core_Model_Abstract
274
  return $products;
275
  }
276
 
 
277
  $collection = Mage::getModel('catalog/product')->getCollection()->addAttributeToFilter('entity_id', array('in' => $productIds));
278
 
279
  // get attributes
280
- if (!empty($attributes)) {
281
- $attributesArray = explode(',', $attributes);
282
  }
283
 
284
- // apply attributes
285
- if (!empty($attributes)) {
286
- foreach ($attributesArray as $attribute) {
287
- $collection->addAttributeToSelect($attribute);
288
- }
289
- } else {
290
- // select all attributes
291
- $collection->addAttributeToSelect('*');
292
- $this->_addMediaGalleryAttributeToCollection($collection);
293
- }
294
 
295
  if (!empty($range)) {
296
  $range = explode(',', $range);
297
- $collection->getSelect()->limit($range[1], $range[0]);
298
  }
299
 
 
 
 
 
 
 
300
  // Add 'out of stock' filter, if preffered
301
  if (!Mage::getStoreConfig('cataloginventory/options/show_out_of_stock')) {
302
  Mage::getSingleton('cataloginventory/stock')->addInStockFilterToCollection($collection);
303
 
304
- // For comments, see :285
305
  $collectionConfigurable = Mage::getResourceModel('catalog/product_collection')->addAttributeToFilter('type_id', array('eq' => 'configurable'));
306
  $collectionConfigurable->addAttributeToFilter('entity_id', array('in' => $productIds));
307
 
@@ -321,11 +347,15 @@ class Highstreet_Hsapi_Model_Products extends Mage_Core_Model_Abstract
321
  /**
322
  * Format result array
323
  */
324
- foreach($collection as $product) {
325
- array_push($products['products'], $this->_getProductAttributes($product, $attributes, $child_product_attributes));
 
 
 
 
326
  }
327
 
328
- $products['product_count'] = $collection->getSize();;
329
 
330
  $rangeLength = $range[1];
331
  if ($rangeLength > count($products["products"])) {
@@ -338,20 +368,19 @@ class Highstreet_Hsapi_Model_Products extends Mage_Core_Model_Abstract
338
  }
339
 
340
  /**
341
- * Gets a batch of products for a given comma sepperated productIds, attributes and child product attributes
342
  *
343
- * @param string Ids, product ids, comma sepperated
344
- * @param string Attributes, string of attributes, comma sepperated
345
- * @param string Child product attributes, string of attributes for the child products, comma sepperated
 
346
  * @return array Product
347
  */
348
 
349
- public function getBatchProducts($ids = false, $attributes, $child_product_attributes) {
350
- $idsArray = explode(',', $ids);
351
-
352
  $products = array();
353
- foreach ($idsArray as $value) {
354
- $products[] = $this->_getProductAttributes(Mage::getModel('catalog/product')->load($value), $attributes, $child_product_attributes);
355
  }
356
 
357
  return $products;
@@ -380,8 +409,11 @@ class Highstreet_Hsapi_Model_Products extends Mage_Core_Model_Abstract
380
  $products = $simple_collection;
381
  } else if($product->getTypeId() == 'simple'){
382
  $products[] = $product;
 
 
 
383
  } else {
384
- return; //Other product types not supported yet
385
  }
386
 
387
 
@@ -399,14 +431,16 @@ class Highstreet_Hsapi_Model_Products extends Mage_Core_Model_Abstract
399
  *
400
  * @param string Type, type of related products, can either be 'cross-sell', 'up-sell' or empty, in which case it will return 'regular' related products
401
  * @param int productId, id used for base of related products
402
- * @param string Attributes, comma seperated string of attributes
403
- * @param string Child products attributes, comma seperated string of attributes for the child products
404
  * @param string range, formatted range string
 
 
 
405
  * @return array Array of product ids
406
  *
407
  */
408
 
409
- public function getRelatedProducts($type, $productId = false, $attributes, $child_product_attributes, $range) {
410
  if (!$productId) {
411
  return;
412
  }
@@ -419,7 +453,7 @@ class Highstreet_Hsapi_Model_Products extends Mage_Core_Model_Abstract
419
  $productIds = $this->getRelatedProductIds($productId);
420
  }
421
 
422
- return $this->getProductsFilteredByProductIds($productIds, $attributes, $child_product_attributes, $range);
423
 
424
  }
425
 
@@ -505,31 +539,38 @@ class Highstreet_Hsapi_Model_Products extends Mage_Core_Model_Abstract
505
 
506
  $layer = Mage::getModel('catalog/layer');
507
 
508
- //$layered_nav = $this->getLayout()->createBlock('catalog/layer_view');
509
- $layered_nav = Highstreet_Hsapi_IndexController::getLayout()->createBlock('catalog/layer_view');
510
- $filters = $layered_nav->getFilters();
511
  $category = Mage::getModel('catalog/category')->load($categoryId);
512
  $layer->setCurrentCategory($category);
 
513
  $attributes = $layer->getFilterableAttributes('price');
514
  $resultFilters = array();
515
  foreach ($attributes as $attribute) {
516
-
517
  if ($attribute->getAttributeCode() == 'price') {
518
  $filterBlockName = 'catalog/layer_filter_price';
519
  } else {
520
  $filterBlockName = 'catalog/layer_filter_attribute';
521
  }
522
- //$result = $this->getLayout()->createBlock($filterBlockName)->setLayer($layer)->setAttributeModel($attribute)->init();
523
- $result = Highstreet_Hsapi_IndexController::getLayout()->createBlock($filterBlockName)->setLayer($layer)->setAttributeModel($attribute)->init();
524
  $options = array();
525
  foreach($result->getItems() as $option) {
526
- $label = str_replace('<span class="price">', "", $option->getLabel());
527
- $label = str_replace('</span>', "", $label);
528
 
529
  $count = $option->getData('count');
530
- array_push($options, array('filter' => $option->getValue(), 'label' => $label, 'product_count' => $count));
 
 
 
 
 
 
 
 
 
 
 
531
  }
532
- array_push($resultFilters, array($attribute->getAttributeCode() => $options));
533
  }
534
 
535
  return $resultFilters;
@@ -540,56 +581,176 @@ class Highstreet_Hsapi_Model_Products extends Mage_Core_Model_Abstract
540
  * Gets attributes of a given product object.
541
  *
542
  * @param Mage_Catalog_Model_Product ResProduct, a product object
543
- * @param string Attributes, an string of attributes to get for the product, comma delimited
544
- * @param string Child_product_attributes, attributes for the child products, comma delimited
 
545
  * @return array Array with information about the product, according to the Attributes array param
546
  *
547
  */
548
 
549
- private function _getProductAttributes($resProduct = false, $attributes = nil, $child_product_attributes) {
550
  if (!$resProduct) {
551
  return null;
552
  }
553
 
 
554
 
555
- if(empty($attributes)) {
556
- $attributes = $this->_getSystemAttributes();
557
- } else {
558
- $attributes = explode(',', $attributes);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
559
  }
560
-
561
 
562
- // if attributes specified
563
- if (!empty($attributes) && count($attributes) > 0) {
564
- foreach ($attributes as $attribute) {
 
 
 
 
565
 
566
- //always set final price to the special price field
567
- if ($attribute === "special_price" || $attribute === "final_price") {
568
- $product[$attribute] = $resProduct->getFinalPrice(1);
569
 
570
- if ($product[$attribute] === false) {
571
- $product[$attribute] = null;
572
- }
573
 
 
 
 
 
 
 
 
574
  continue;
575
  }
576
 
577
- // Translate this into an array of "translations" if we run into more problems
578
- $fieldName = $attribute;
579
- if ($attribute == "type") {
580
- $attribute = "type_id";
581
- $fieldName = "type";
 
 
 
 
582
  }
583
 
584
- if ($resProduct->getResource()->getAttribute($attribute)) {
585
- $value = $resProduct->getResource()->getAttribute($attribute)->getFrontend()->getValue($resProduct);
586
- $product[$fieldName] = $value;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
587
  }
588
  }
589
-
590
  }
591
 
592
-
593
  $product['id'] = $resProduct->getId();
594
 
595
 
@@ -603,87 +764,99 @@ class Highstreet_Hsapi_Model_Products extends Mage_Core_Model_Abstract
603
  $product["special_price"] = null;
604
  }
605
 
606
-
607
-
608
-
609
- if (in_array("is_salable", $attributes)) {
610
- $product["is_salable"] = $resProduct->getData('is_salable');
611
- }
612
-
613
-
614
-
615
- if(in_array('configurable_attributes',$attributes)){
616
- $this->_addConfigurableAttributes = true;
617
  }
618
 
619
- if(in_array('child_products',$attributes)){
620
- $this->_addConfigurations = true;
 
 
621
  }
622
 
623
- if($resProduct->getTypeId() == 'configurable'){
624
  $conf = Mage::getModel('catalog/product_type_configurable')->setProduct($resProduct);
625
 
626
  //build the configuration_attributes array
627
  $configurableAttributes = $conf->getConfigurableAttributesAsArray($resProduct);
628
 
629
  $tmpConfigurableAttributes = array();
630
- foreach($configurableAttributes as $attribute)
631
- {
632
  array_push($tmpConfigurableAttributes,$attribute['attribute_code']);
633
- //array_push($simpleCollectionAttributes,$attribute['attribute_code']);
634
- }
635
- if($this->_addConfigurableAttributes == true){
636
- $product['configurable_attributes'] = $tmpConfigurableAttributes;
637
  }
638
 
 
 
639
  //build the configuration_attributes array if we want to display these
640
- if($this->_addConfigurations == true){
 
 
 
641
 
642
- $product['child_products'] = array();
643
- $simple_collection = $conf->getUsedProductCollection()
644
- ->addAttributeToSelect('*')
645
- ->addFilterByRequiredOptions();
646
 
647
- foreach($simple_collection as $simple_product){
648
- if(!Mage::getStoreConfig('cataloginventory/options/show_out_of_stock')
649
- && !$simple_product->isSaleable())
650
- continue;
651
-
652
- if(!$child_product_attributes)
653
- $child_product_attributes = "entity_id,created_at,description,special_price,updated_at,image,sku,short_description,price,manufacturer";
654
 
655
- $resProduct = Mage::getModel('catalog/product')->load($simple_product->getId());
656
- if ($resProduct->getData('status') == false) {
657
- continue;
658
- }
 
 
 
659
 
660
- $simpleProductRepresentation = $this->_getProductAttributes($resProduct, $child_product_attributes);
661
- $configuration = array();
662
 
 
 
663
 
664
- foreach($tmpConfigurableAttributes as $attribute)
665
- {
666
- $method = 'get' . uc_words($attribute, '');
667
- $configuration[$attribute] = $simple_product->$method();
668
- }
 
 
 
 
 
 
 
 
 
 
 
 
669
 
670
- $simpleProductRepresentation['configuration'] = $configuration;
671
- array_push($product['child_products'],(object)$simpleProductRepresentation);
 
 
 
 
 
 
672
  }
 
673
  }
674
 
675
- unset($tmpConfigurableAttributes);
 
 
676
  }
677
 
678
  $this->_convertProductDates($product);
679
 
680
  $product = $this->_setImagePaths($product);
681
 
682
- //media gallery
683
- if(array_key_exists("media_gallery", $product)) {
684
- $product["media_gallery"] = $this->_getMediaGalleryImagesForProductID($product["id"]);
685
- }
686
-
687
  return $product;
688
  }
689
 
@@ -715,58 +888,6 @@ class Highstreet_Hsapi_Model_Products extends Mage_Core_Model_Abstract
715
 
716
  }
717
 
718
- /**
719
- * Load media gallery in collection
720
- */
721
- private function _addMediaGalleryAttributeToCollection(Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Collection $_productCollection) {
722
-
723
- if (count($_productCollection == 0))
724
- return $_productCollection;
725
-
726
- $_mediaGalleryAttributeId = Mage::getSingleton('eav/config')->getAttribute('catalog_product', 'media_gallery')->getAttributeId();
727
- $_read = Mage::getSingleton('core/resource')->getConnection('catalog_read');
728
-
729
- $_mediaGalleryData = $_read->fetchAll('
730
- SELECT
731
- main.entity_id, `main`.`value_id`, `main`.`value` AS `file`,
732
- `value`.`label`, `value`.`position`, `value`.`disabled`, `default_value`.`label` AS `label_default`,
733
- `default_value`.`position` AS `position_default`,
734
- `default_value`.`disabled` AS `disabled_default`
735
- FROM `catalog_product_entity_media_gallery` AS `main`
736
- LEFT JOIN `catalog_product_entity_media_gallery_value` AS `value`
737
- ON main.value_id=value.value_id AND value.store_id=' . Mage::app()->getStore()->getId() . '
738
- LEFT JOIN `catalog_product_entity_media_gallery_value` AS `default_value`
739
- ON main.value_id=default_value.value_id AND default_value.store_id=0
740
- WHERE (
741
- main.attribute_id = ' . $_read->quote($_mediaGalleryAttributeId) . ')
742
- AND (main.entity_id IN (' . $_read->quote($_productCollection->getAllIds()) . '))
743
- ORDER BY IF(value.position IS NULL, default_value.position, value.position) ASC
744
- ');
745
-
746
-
747
- $_mediaGalleryByProductId = array();
748
- foreach ($_mediaGalleryData as $_galleryImage) {
749
- $k = $_galleryImage['entity_id'];
750
- unset($_galleryImage['entity_id']);
751
- if (!isset($_mediaGalleryByProductId[$k])) {
752
- $_mediaGalleryByProductId[$k] = array();
753
- }
754
- $_galleryImage['file'] = self::PRODUCTS_MEDIA_PATH . $_galleryImage['file'];
755
- $_mediaGalleryByProductId[$k][] = $_galleryImage;
756
- }
757
- unset($_mediaGalleryData);
758
-
759
- foreach ($_productCollection as &$_product) {
760
- $_productId = $_product->getData('entity_id');
761
- if (isset($_mediaGalleryByProductId[$_productId])) {
762
- $_product->setData('media_gallery', array('images' => $_mediaGalleryByProductId[$_productId]));
763
- }
764
- }
765
- unset($_mediaGalleryByProductId);
766
-
767
- return $_productCollection;
768
- }
769
-
770
  /**
771
  * Gets stock (voorraad) information about a certain product
772
  *
@@ -793,32 +914,6 @@ class Highstreet_Hsapi_Model_Products extends Mage_Core_Model_Abstract
793
  return $stockinfo;
794
  }
795
 
796
- /**
797
- * Gets system attributes. This function is used when there are no attributes give as a param in the URL of the API call
798
- *
799
- * @return array Array with attribute names
800
- */
801
-
802
- private function _getSystemAttributes() {
803
-
804
- $entityTypeId = Mage::getModel('catalog/product')->getResource()->getEntityType()->getId();
805
-
806
- // get only system attributes
807
- $attributes = Mage::getResourceModel('eav/entity_attribute_collection')
808
- ->setEntityTypeFilter($entityTypeId)
809
- ->addFieldToFilter('main_table.is_user_defined', 0)
810
- ->addFieldToFilter('additional_table.is_visible', 1);
811
-
812
- $attributeNames = array();
813
-
814
- foreach($attributes as $attribute) {
815
- $attributeNames[] = $attribute->getName();
816
- }
817
-
818
- return $attributeNames;
819
- }
820
-
821
-
822
  /**
823
  * Sets the image paths properly with the relative path.
824
  *
@@ -829,29 +924,19 @@ class Highstreet_Hsapi_Model_Products extends Mage_Core_Model_Abstract
829
  if (!$product) {
830
  return $product;
831
  }
832
-
833
-
834
- if (array_key_exists('image', $product) && !strstr($product['image'], self::PRODUCTS_MEDIA_PATH)) {
835
- if($product['image'] != self::NO_IMAGE_PATH)
836
- $product['image'] = self::PRODUCTS_MEDIA_PATH . $product['image'];
837
- else
838
- $product['image'] = null;
839
- }
840
-
841
- if (array_key_exists('thumbnail', $product) && !strstr($product['thumbnail'], self::PRODUCTS_MEDIA_PATH)) {
842
- if($product['thumbnail'] != self::NO_IMAGE_PATH)
843
- $product['thumbnail'] = self::PRODUCTS_MEDIA_PATH . $product['thumbnail'];
844
- else
845
- $product['thumbnail'] = null;
846
- }
847
-
848
- if (array_key_exists('small_image', $product) && !strstr($product['small_image'], self::PRODUCTS_MEDIA_PATH)) {
849
- if($product['small_image'] != self::NO_IMAGE_PATH)
850
- $product['small_image'] = self::PRODUCTS_MEDIA_PATH . $product['small_image'];
851
- else
852
- $product['small_image'] = null;
853
  }
854
-
855
  return $product;
856
  }
857
 
@@ -880,9 +965,19 @@ class Highstreet_Hsapi_Model_Products extends Mage_Core_Model_Abstract
880
  $output[] = $imageData;
881
  }
882
 
883
- return array("images" => $output);
884
  }
885
 
886
-
 
 
 
 
 
 
 
 
 
 
887
 
888
  }
8
  */
9
  class Highstreet_Hsapi_Model_Products extends Mage_Core_Model_Abstract
10
  {
 
 
 
 
 
 
11
  const PRODUCTS_MEDIA_PATH = '/media/catalog/product';
12
  const NO_IMAGE_PATH = 'no_selection';
13
  const RANGE_FALLBACK_RANGE = 100;
16
  /**
17
  * Gets a single product for a given productId and attributes
18
  *
19
+ * @param object Product object for the product to be gotten
20
+ * @param string Additional Attributes, string of attributes straight from the URL
21
+ * @param bool include_configuration_details, weather to include child products in the product object and configurable attributes (For both configurable products and bundled products)
22
+ * @param bool include_media_gallery, weather to include the media gallery in the product object
23
  * @return array Product
24
  */
25
+ public function getSingleProduct($productObject = false, $additional_attributes, $include_configuration_details, $include_media_gallery)
26
  {
27
+ if (!$productObject) {
28
+ return null;
29
  }
 
 
30
 
31
+ return $this->_getProductAttributes($productObject, $additional_attributes, $include_configuration_details, $include_media_gallery);
32
+ }
33
+
34
+ public function productHasBeenModifiedSince($productObject = false, $since) {
35
+ if (!is_numeric($since)) {
36
+ if (($since = strtotime($since)) === false) {
37
+ return true; // String to time failed to convert, return the product as if it was modified
38
+ }
39
+ }
40
+
41
+ if (!$productObject) {
42
+ return false;
43
+ }
44
+
45
+ if (strtotime($productObject->getUpdatedAt()) >= $since) {
46
+ return true;
47
+ }
48
+
49
+ if ($productObject->getTypeId() == 'configurable') {
50
+ $conf = Mage::getModel('catalog/product_type_configurable')->setProduct($productObject);
51
+ $simple_collection = $conf->getUsedProductCollection()
52
+ ->addAttributeToSelect(array('updated_at'));
53
+
54
+ foreach ($simple_collection as $product) {
55
+ if (strtotime($product->getUpdatedAt()) >= $since) {
56
+ return true;
57
+ }
58
+ }
59
+ } elseif ($productObject->getTypeId() == 'bundle') {
60
+ $bundleProduct = Mage::getModel('bundle/product_type')->setProduct($productObject);
61
+ $bundleProducts = $bundleProduct->getSelectionsCollection($bundleProduct->getOptionsIds());
62
+
63
+ foreach ($bundleProducts as $product) {
64
+ if (strtotime($product->getUpdatedAt()) >= $since) {
65
+ return true;
66
+ }
67
+ }
68
+ }
69
+
70
+ return false;
71
  }
72
 
73
  /**
74
  * Gets products with attributes for given order, range, filters, search and categoryId
75
  *
76
+ * @param string Additional Attributes, string of attributes straight from the URL
 
77
  * @param string Order, order for the products
78
  * @param string Range of products. Must formatted like "0,10" where 0 is the offset and 10 is the count
79
  * @param string Search string for filtering on keywords
80
  * @param integer CategoryId, category id of the category which will be used to filter
81
+ * @param boolean Hide attributes, only returns product ID's (vastly improving the speed of the API)
82
+ * @param boolean Hide filters, hides filters if set to true
83
+ * @param bool include_configuration_details, weather to include child products in the product object and configurable attributes (For both configurable products and bundled products)
84
+ * @param bool include_media_gallery, weather to include the media gallery in the product object
85
  * @return array Product
86
  */
87
+ public function getProductsForResponse($additional_attributes, $order, $range, $filters, $search, $categoryId, $hideAttributes, $hideFilters, $include_configuration_details, $include_media_gallery)
88
  {
 
89
  $searching = !empty($search);
90
 
91
+ $attributesArray = array();
92
  // get attributes
93
+ if (!empty($additional_attributes)) {
94
+ $attributesArray = explode(',', $additional_attributes);
 
 
 
 
 
 
 
95
  }
96
 
97
+ $attributesArray = array_merge($attributesArray, $this->_getCoreAttributes());
98
+
99
  // get order
100
  if (!empty($order)) {
101
  $order = explode(',', $order);
156
  $categoryId = Mage::app()->getStore()->getRootCategoryId();
157
  $categoryNotSet = true;
158
  }
159
+
160
  $category = Mage::getModel('catalog/category')->load($categoryId);
161
+ if ($category->getId() === NULL) {
162
+ return array();
163
+ }
164
+
165
  // apply search
166
  if ($categoryId && !$categoryNotSet) {
167
  $collection->addCategoryFilter($category);
171
  $range = explode(',', $range);
172
  }
173
 
174
+ if (!is_array($range)) {
175
+ $range = array(0, self::RANGE_FALLBACK_RANGE);
 
 
 
 
 
 
 
 
 
 
 
 
176
  }
 
177
 
178
+ $collection->getSelect()->limit($range[1], $range[0]);
179
+
180
+ // apply attributes
181
+ $collection->addAttributeToSelect($attributesArray);
182
 
183
  //apply filters
184
  if(!empty($filters)) {
192
  }
193
  }
194
  }
 
195
 
196
+ // Apply type filter, we only want Simple and Configurable and Bundle products in our API
197
+ $collection->addAttributeToFilter('type_id', array('simple', 'configurable', 'bundle'));
198
 
199
  // apply order
200
  if (!empty($order)) {
239
  }
240
  }
241
 
 
 
 
 
 
 
242
  if (!isset($productCount)) {
243
  // get total product count
244
  $productCount = $collection->getSize();
248
  */
249
  $products = array('products' => array());
250
 
251
+ // If range requests no products to be returned, return no products. The limit() doesn't take 0 for an answer
252
+ if ($range[1] > 0) {
253
+ if (!$hideAttributes) {
254
+ foreach($collection as $product) {
255
+ array_push($products['products'], $this->_getProductAttributes($product, $additional_attributes, $include_configuration_details, $include_media_gallery));
256
+ }
257
+ } else {
258
+ $products['products'] = $collection->getAllIds($range[1], $range[0]);
259
+ }
260
  }
261
 
262
+ $products['filters'] = array();
263
+ if (!$hideFilters) {
264
+ $products['filters'] = $this->getFilters($categoryId);
265
+ }
266
+
267
  $products['product_count'] = $productCount;
268
 
269
  $rangeLength = $range[1];
282
  * Gets products for a set of product id's
283
  *
284
  * @param array productIds, product id's to filter on
285
+ * @param string additional_attributes, comma seperated string of attributes
 
286
  * @param string range, formatted range string
287
+ * @param boolean Hide Attributes, only returns product ID's (vastly improving the speed of the API)
288
+ * @param bool include_configuration_details, weather to include child products in the product object and configurable attributes (For both configurable products and bundled products)
289
+ * @param bool include_media_gallery, weather to include the media gallery in the product object
290
  * @return array Array of products
291
  *
292
  */
293
 
294
+ public function getProductsFilteredByProductIds($productIds = false, $additional_attributes, $range, $hideAttributes, $include_configuration_details, $include_media_gallery) {
295
 
296
  $products = array('products' => array());
297
 
301
  return $products;
302
  }
303
 
304
+
305
  $collection = Mage::getModel('catalog/product')->getCollection()->addAttributeToFilter('entity_id', array('in' => $productIds));
306
 
307
  // get attributes
308
+ if (!empty($additional_attributes)) {
309
+ $attributesArray = explode(',', $additional_attributes);
310
  }
311
 
312
+ $attributesArray = array_merge($attributesArray, $this->_getCoreAttributes());
313
+
314
+ $collection->addAttributeToSelect($attributesArray);
 
 
 
 
 
 
 
315
 
316
  if (!empty($range)) {
317
  $range = explode(',', $range);
 
318
  }
319
 
320
+ if (!is_array($range)) {
321
+ $range = array(0, self::RANGE_FALLBACK_RANGE);
322
+ }
323
+
324
+ $collection->getSelect()->limit($range[1], $range[0]);
325
+
326
  // Add 'out of stock' filter, if preffered
327
  if (!Mage::getStoreConfig('cataloginventory/options/show_out_of_stock')) {
328
  Mage::getSingleton('cataloginventory/stock')->addInStockFilterToCollection($collection);
329
 
330
+ // For comments, see :222
331
  $collectionConfigurable = Mage::getResourceModel('catalog/product_collection')->addAttributeToFilter('type_id', array('eq' => 'configurable'));
332
  $collectionConfigurable->addAttributeToFilter('entity_id', array('in' => $productIds));
333
 
347
  /**
348
  * Format result array
349
  */
350
+ if (!$hideAttributes) {
351
+ foreach($collection as $product) {
352
+ array_push($products['products'], $this->_getProductAttributes($product, $additional_attributes, $include_configuration_details, $include_media_gallery));
353
+ }
354
+ } else {
355
+ $products['products'] = $collection->getAllIds();
356
  }
357
 
358
+ $products['product_count'] = $collection->getSize();
359
 
360
  $rangeLength = $range[1];
361
  if ($rangeLength > count($products["products"])) {
368
  }
369
 
370
  /**
371
+ * Gets a batch of products for a given comma sepperated productIds and attributes
372
  *
373
+ * @param array ProductObjects, array of Magento product Objects
374
+ * @param string Additional Attributes, string of attributes, comma sepperated
375
+ * @param bool include_configuration_details, weather to include child products in the product object and configurable attributes (For both configurable products and bundled products)
376
+ * @param bool include_media_gallery, weather to include the media gallery in the product object
377
  * @return array Product
378
  */
379
 
380
+ public function getBatchProducts($productObjects, $additional_attributes, $include_configuration_details, $include_media_gallery) {
 
 
381
  $products = array();
382
+ foreach ($productObjects as $productObject) {
383
+ $products[] = $this->_getProductAttributes($productObject, $additional_attributes, $include_configuration_details, $include_media_gallery);
384
  }
385
 
386
  return $products;
409
  $products = $simple_collection;
410
  } else if($product->getTypeId() == 'simple'){
411
  $products[] = $product;
412
+ } else if($product->getTypeId() == 'bundle'){
413
+ $bundleProduct = Mage::getModel('bundle/product_type')->setProduct($product);
414
+ $products = $bundleProduct->getSelectionsCollection($bundleProduct->getOptionsIds());
415
  } else {
416
+ return;
417
  }
418
 
419
 
431
  *
432
  * @param string Type, type of related products, can either be 'cross-sell', 'up-sell' or empty, in which case it will return 'regular' related products
433
  * @param int productId, id used for base of related products
434
+ * @param string Additonal Attributes, comma seperated string of attributes
 
435
  * @param string range, formatted range string
436
+ * @param boolean Hide attributes, only return product ID's (vastly improving the speed of the API)
437
+ * @param bool include_configuration_details, weather to include child products in the product object and configurable attributes (For both configurable products and bundled products)
438
+ * @param bool include_media_gallery, weather to include the media gallery in the product object
439
  * @return array Array of product ids
440
  *
441
  */
442
 
443
+ public function getRelatedProducts($type, $productId = false, $additional_attributes, $range, $hideAttributes, $include_configuration_details, $include_media_gallery) {
444
  if (!$productId) {
445
  return;
446
  }
453
  $productIds = $this->getRelatedProductIds($productId);
454
  }
455
 
456
+ return $this->getProductsFilteredByProductIds($productIds, $additional_attributes, $range, $hideAttributes, $include_configuration_details, $include_media_gallery);
457
 
458
  }
459
 
539
 
540
  $layer = Mage::getModel('catalog/layer');
541
 
 
 
 
542
  $category = Mage::getModel('catalog/category')->load($categoryId);
543
  $layer->setCurrentCategory($category);
544
+ $controller = @Mage_Core_Controller_Front_Action::getLayout();
545
  $attributes = $layer->getFilterableAttributes('price');
546
  $resultFilters = array();
547
  foreach ($attributes as $attribute) {
 
548
  if ($attribute->getAttributeCode() == 'price') {
549
  $filterBlockName = 'catalog/layer_filter_price';
550
  } else {
551
  $filterBlockName = 'catalog/layer_filter_attribute';
552
  }
553
+
554
+ $result = $controller->createBlock($filterBlockName)->setLayer($layer)->setAttributeModel($attribute)->init();
555
  $options = array();
556
  foreach($result->getItems() as $option) {
557
+ $title = str_replace('<span class="price">', "", $option->getLabel());
558
+ $title = str_replace('</span>', "", $title);
559
 
560
  $count = $option->getData('count');
561
+ array_push($options, array('value' => $option->getValue(), 'title' => $title, 'product_count' => $count));
562
+ }
563
+
564
+ if (count($options) > 0) {
565
+ array_push($resultFilters,
566
+ array(
567
+ 'title' => $attribute->getData('frontend_label'),
568
+ 'type' => $attribute->getFrontendInput(),
569
+ 'code' => $attribute->getAttributeCode(),
570
+ 'options' => $options
571
+ )
572
+ );
573
  }
 
574
  }
575
 
576
  return $resultFilters;
581
  * Gets attributes of a given product object.
582
  *
583
  * @param Mage_Catalog_Model_Product ResProduct, a product object
584
+ * @param string Additional_attributes, an string of attributes to get for the product, comma delimited
585
+ * @param bool include_configuration_details, weather to include child products in the product object and configurable attributes (For both configurable products and bundled products). Default value is fale
586
+ * @param bool include_media_gallery, weather to include the media gallery in the product object. Default value is fale
587
  * @return array Array with information about the product, according to the Attributes array param
588
  *
589
  */
590
 
591
+ private function _getProductAttributes($resProduct = false, $additional_attributes = nil, $include_configuration_details = false, $include_media_gallery = false) {
592
  if (!$resProduct) {
593
  return null;
594
  }
595
 
596
+ $product = array();
597
 
598
+ $attributes = $this->_getCoreAttributes();
599
+
600
+ foreach ($attributes as $attribute) {
601
+ //always set final price to the special price field
602
+ if ($attribute === "special_price" || $attribute === "final_price") {
603
+ $product[$attribute] = $resProduct->getFinalPrice(1);
604
+
605
+ if ($product[$attribute] === false) {
606
+ $product[$attribute] = null;
607
+ }
608
+
609
+ continue;
610
+ }
611
+
612
+ if ($attribute === "is_salable") {
613
+ $product["is_salable"] = (bool)$resProduct->getData('is_salable');
614
+ continue;
615
+ }
616
+
617
+ // Translate this into an array of "translations" if we run into more problems
618
+ $fieldName = $attribute;
619
+ if ($attribute == "type") {
620
+ $attribute = "type_id";
621
+ $fieldName = "type";
622
+ }
623
+
624
+ if ($resProduct->getResource()->getAttribute($attribute)) {
625
+ $value = $resProduct->getResource()->getAttribute($attribute)->getFrontend()->getValue($resProduct);
626
+ $product[$fieldName] = $value;
627
+ }
628
  }
 
629
 
630
+ $product['images'] = array();
631
+ $product['images']['small_image'] = $product['small_image'];
632
+ $product['images']['image'] = $product['image'];
633
+ $product['images']['thumbnail'] = $product['thumbnail'];
634
+ unset($product['small_image']);
635
+ unset($product['image']);
636
+ unset($product['thumbnail']);
637
 
 
 
 
638
 
639
+ if (!empty($additional_attributes)) {
640
+ $additionalAttributesArray = explode(',', $additional_attributes);
641
+ }
642
 
643
+ $product['attribute_values'] = array(); // Make sure to always return an object for this key
644
+ // if additional attributes specified
645
+ if (!empty($additionalAttributesArray) && count($additionalAttributesArray) > 0) {
646
+ $attributesModel = Mage::getModel('highstreet_hsapi/attributes');
647
+
648
+ foreach ($additionalAttributesArray as $attribute) {
649
+ if ($attribute == "media_gallery") {
650
  continue;
651
  }
652
 
653
+ if ($attribute === "share_url") {
654
+ $additionalAttributeData = array();
655
+ $additionalAttributeData['title'] = "Share url";
656
+ $additionalAttributeData['code'] = "share_url";
657
+ $additionalAttributeData['type'] = "url";
658
+ $additionalAttributeData['inline_value'] = $resProduct->getProductUrl();
659
+ $product['attribute_values'][] = $additionalAttributeData;
660
+
661
+ continue;
662
  }
663
 
664
+ $attributeObject = $resProduct->getResource()->getAttribute($attribute);
665
+
666
+ if ($attributeObject !== false) {
667
+ $readableAttributeValue = $attributeObject->getFrontend()->getValue($resProduct); // 'frontend' value, human readable value
668
+
669
+ $attributesData = $attributesModel->getAttribute($attribute);
670
+
671
+ if ($attributesData['title'] == null ||
672
+ $attributesData['code'] == null ||
673
+ $attributeObject->getFrontendInput() == null) {
674
+ continue;
675
+ }
676
+
677
+ // Pre-make attribute object to be put into json
678
+ $additionalAttributeData = array();
679
+ $additionalAttributeData['title'] = $attributesData['title'];
680
+ $additionalAttributeData['code'] = $attributesData['code'];
681
+ $additionalAttributeData['type'] = $attributeObject->getFrontendInput();
682
+
683
+ // Switch statement from /app/code/core/Mage/Catalog/Model/Product/Attribute/Api.php:301
684
+ // Gets all attribute types and fill in the value field of the attribute object
685
+ switch ($attributesData['type']) {
686
+ case 'text':
687
+ case 'textarea':
688
+ case 'price':
689
+ $additionalAttributeData['inline_value'] = $readableAttributeValue;
690
+ break;
691
+ case 'date':
692
+ if ($readableAttributeValue == null) {
693
+ $additionalAttributeData['inline_value'] = null;
694
+ } else {
695
+ $additionalAttributeData['inline_value'] = strtotime($readableAttributeValue);
696
+ }
697
+
698
+ $additionalAttributeData['raw_value'] = $readableAttributeValue;
699
+ break;
700
+ case 'boolean':
701
+ $attributeMethod = "get" . uc_words($attribute);
702
+ $idAttributeValue = $resProduct->$attributeMethod();
703
+ $additionalAttributeData['raw_value'] = $readableAttributeValue;
704
+ $additionalAttributeData['inline_value'] = ($idAttributeValue == 1 ? true : false);
705
+ break;
706
+ case 'select':
707
+ case 'multiselect':
708
+ $hasFoundValue = false;
709
+ $additionalAttributeData['value'] = array();
710
+
711
+ $mutliSelectValues = $resProduct->getAttributeText($attribute); // Get values for multiselect type (array)
712
+
713
+ // Loop trough select options of attribute
714
+ foreach ($attributesData['options'] as $key => $value) {
715
+ if (($value->title === $readableAttributeValue && $attributesData['type'] === 'select') || // If attribute type is single select option, check title
716
+ ((is_array($mutliSelectValues) && in_array($value->title, $mutliSelectValues) || ($value->title === $mutliSelectValues)) &&
717
+ $attributesData['type'] === 'multiselect') // If attribute type is multi select option, check if value is in array of possible options or equal to the title
718
+ ) {
719
+ $attributeValueObject = array();
720
+ $attributeValueObject['id'] = $value->value;
721
+ $attributeValueObject['title'] = $value->title;
722
+ $attributeValueObject['sort_hint'] = $value->sort_hint;
723
+ $additionalAttributeData['value'][] = $attributeValueObject;
724
+ $hasFoundValue = true;
725
+
726
+ if ($attributesData['type'] === 'select') {
727
+ break; // single select option doesn't have to loop trough all possibilities
728
+ }
729
+ }
730
+ }
731
+
732
+ // If type is select and there is only one element, return the element as an object and not an array with 1 object
733
+ if ($attributesData['type'] == 'select' && count($additionalAttributeData['value']) == 1) {
734
+ $additionalAttributeData['value'] = $additionalAttributeData['value'][0];
735
+ }
736
+
737
+ // No value was found, make value field in attribute object null
738
+ if (!$hasFoundValue) {
739
+ $additionalAttributeData['value'] = null;
740
+ }
741
+ break;
742
+ default:
743
+ if ($readableAttributeValue != null) {
744
+ $additionalAttributeData['inline_value'] = $readableAttributeValue;
745
+ }
746
+ break;
747
+ }
748
+
749
+ $product['attribute_values'][] = $additionalAttributeData;
750
  }
751
  }
 
752
  }
753
 
 
754
  $product['id'] = $resProduct->getId();
755
 
756
 
764
  $product["special_price"] = null;
765
  }
766
 
767
+
768
+ if ($resProduct->getTypeId() == 'bundle') {
769
+ $product["price"] = Mage::getModel('bundle/product_price')->getTotalPrices($resProduct,'min',1);
 
 
 
 
 
 
 
 
770
  }
771
 
772
+
773
+ if ($include_media_gallery) {
774
+ $mediaGalleryValue = $this->_getMediaGalleryImagesForProductID($product["id"]);
775
+ $product['media_gallery'] = $mediaGalleryValue;
776
  }
777
 
778
+ if($resProduct->getTypeId() == 'configurable' && $include_configuration_details){
779
  $conf = Mage::getModel('catalog/product_type_configurable')->setProduct($resProduct);
780
 
781
  //build the configuration_attributes array
782
  $configurableAttributes = $conf->getConfigurableAttributesAsArray($resProduct);
783
 
784
  $tmpConfigurableAttributes = array();
785
+ foreach($configurableAttributes as $attribute) {
 
786
  array_push($tmpConfigurableAttributes,$attribute['attribute_code']);
 
 
 
 
787
  }
788
 
789
+ $product['configurable_attributes'] = $tmpConfigurableAttributes;
790
+
791
  //build the configuration_attributes array if we want to display these
792
+ $product['child_products'] = array();
793
+ $simple_collection = $conf->getUsedProductCollection()
794
+ ->addAttributeToSelect('*')
795
+ ->addFilterByRequiredOptions();
796
 
797
+ foreach($simple_collection as $simple_product){
798
+ if(!Mage::getStoreConfig('cataloginventory/options/show_out_of_stock')
799
+ && !$simple_product->isSaleable())
800
+ continue;
801
 
802
+ $resProduct = Mage::getModel('catalog/product')->load($simple_product->getId());
803
+ if ($resProduct->getData('status') == Mage_Catalog_Model_Product_Status::STATUS_DISABLED || $resProduct->getStatus() == Mage_Catalog_Model_Product_Status::STATUS_DISABLED ||
804
+ $resProduct->getData('status') == "Uitgeschakeld" || $resProduct->getStatus() == "Uitgeschakeld") {
805
+ continue;
806
+ }
 
 
807
 
808
+ $simpleProductAdditionalAttributesArray = $product['configurable_attributes']; // A configurable product always has a configuration so 'configurable_attributes' is always filled
809
+ if (!empty($additionalAttributesArray) && count($additionalAttributesArray) > 0) { // If we want to get additional attributes, merge them
810
+ $simpleProductAdditionalAttributesArray = array_merge($additionalAttributesArray, $simpleProductAdditionalAttributesArray);
811
+ }
812
+
813
+ $simpleProductAdditionalAttributesArray = array_unique($simpleProductAdditionalAttributesArray); // Make sure that we don't get multiple of the same attributes
814
+ $simpleProductAdditionalAttributesString = implode($simpleProductAdditionalAttributesArray, ',');
815
 
816
+ $simpleProductObject = $this->_getProductAttributes($resProduct, $simpleProductAdditionalAttributesString, $include_configuration_details, $include_media_gallery);
 
817
 
818
+ array_push($product['child_products'], (object)$simpleProductObject);
819
+ }
820
 
821
+ unset($tmpConfigurableAttributes);
822
+ }
823
+
824
+ if($resProduct->getTypeId() == 'bundle' && $include_configuration_details) {
825
+ $bundleProduct = Mage::getModel('bundle/product_type')->setProduct($resProduct);
826
+ $bundles = $bundleProduct->getOptionsCollection()->getData();
827
+ foreach($bundles as $bundle) {
828
+
829
+ $children = $bundleProduct->getSelectionsCollection(array($bundle['option_id']));
830
+ foreach($children as $child) {
831
+
832
+
833
+ $childRes['position'] = $child->getPosition();
834
+ $childRes['selection_id'] = $child->getSelectionId();
835
+ $childRes['selection_qty'] = $child->getSelectionQty();
836
+ $childRes['selection_can_change_qty'] = $child->getSelectionCanChangeQty();
837
+ $childRes['is_default'] = $child->getIsDefault();
838
 
839
+ //flinders-specific, but should not throw an error when not implemented
840
+ $childRes['selection_thumbnail'] = $child->getSelectionThumbnail();
841
+ $childRes['selection_modified_name'] = $child->getSelectionModifiedname();
842
+
843
+ $bundledProductAdditionalAttributesString = implode($additionalAttributesArray, ',');
844
+
845
+ $childRes['product'] = $this->_getProductAttributes($child, $bundledProductAdditionalAttributesString, $include_configuration_details, $include_media_gallery);
846
+ $bundle['children'][] = $childRes;
847
  }
848
+ $product['bundles'][] = $bundle;
849
  }
850
 
851
+
852
+
853
+
854
  }
855
 
856
  $this->_convertProductDates($product);
857
 
858
  $product = $this->_setImagePaths($product);
859
 
 
 
 
 
 
860
  return $product;
861
  }
862
 
888
 
889
  }
890
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
891
  /**
892
  * Gets stock (voorraad) information about a certain product
893
  *
914
  return $stockinfo;
915
  }
916
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
917
  /**
918
  * Sets the image paths properly with the relative path.
919
  *
924
  if (!$product) {
925
  return $product;
926
  }
927
+
928
+ foreach ($product['images'] as $key => $value) {
929
+ if (!strstr($value, self::PRODUCTS_MEDIA_PATH)) {
930
+ if($value != self::NO_IMAGE_PATH && $value != null) {
931
+ $value = self::PRODUCTS_MEDIA_PATH . $value;
932
+ } else {
933
+ $value = null;
934
+ }
935
+ }
936
+
937
+ $product['images'][$key] = $value;
 
 
 
 
 
 
 
 
 
 
938
  }
939
+
940
  return $product;
941
  }
942
 
965
  $output[] = $imageData;
966
  }
967
 
968
+ return $output;
969
  }
970
 
971
+ /**
972
+ * Returns an array of all core attributes
973
+ *
974
+ * @return array Array of attributes
975
+ */
976
+ private function _getCoreAttributes () {
977
+ return array("entity_id", "sku", "type", "created_at", "updated_at",
978
+ "name", "news_from_date", "news_to_date", "price",
979
+ "image", "small_image", "thumbnail",
980
+ "special_from_date", "special_to_date", "special_price", "is_salable");
981
+ }
982
 
983
  }
app/code/local/Highstreet/Hsapi/Model/System/Config/Environment.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Highstreet_Hsapi_Model_System_Config_Environment {
4
+ public function toOptionArray()
5
+ {
6
+ return array(
7
+ array(
8
+ 'value' => 'staging',
9
+ 'label' => 'Staging',
10
+ ),
11
+ array(
12
+ 'value' => 'production',
13
+ 'label' => 'Production',
14
+ ),
15
+ );
16
+ }
17
+ }
app/code/local/Highstreet/Hsapi/controllers/.DS_Store DELETED
Binary file
app/code/local/Highstreet/Hsapi/controllers/CheckoutController.php ADDED
@@ -0,0 +1,630 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Highstreet_HSAPI_module
4
+ *
5
+ * @package Highstreet_Hsapi
6
+ * @author Tim Wachter (tim@touchwonders.com) ~ Touchwonders
7
+ * @copyright Copyright (c) 2014 Touchwonders b.v. (http://www.touchwonders.com/)
8
+ */
9
+
10
+ class Highstreet_Hsapi_CheckoutController extends Mage_Core_Controller_Front_Action
11
+ {
12
+ /**
13
+ * Coupon constants
14
+ **/
15
+ const COUPON_SUCCESS_REPLACE = "{coupon_code}";
16
+ const COUPON_CODE_FATAL = -1;
17
+ const COUPON_CODE_ERROR = 1;
18
+ const COUPON_CODE_SUCCESS = 0;
19
+ const COUPON_CODE_MAX_LENGTH = 255;
20
+
21
+
22
+ public function indexAction() {
23
+ return false;
24
+ }
25
+
26
+
27
+
28
+
29
+ /**
30
+ * Gives all the information to make the checkout work from the initial loading of the page.
31
+ */
32
+ public function startAction() {
33
+ $checkoutModel = Mage::getModel('highstreet_hsapi/checkoutV2');
34
+
35
+ $this->_JSONencodeAndRespond($checkoutModel->getStartData());
36
+ }
37
+
38
+ /**
39
+ * Countries action. Returns the available countries for the checkout
40
+ */
41
+ public function countriesAction() {
42
+ $countryCollection = Mage::getSingleton('directory/country')->getResourceCollection()->loadByStore();
43
+ $options = $countryCollection->toOptionArray();
44
+ $returnOptions = array();
45
+ foreach ($options as $key => $value) {
46
+ $value['value'] = trim($value['value']);
47
+ $value['label'] = trim($value['label']);
48
+ if (empty($value['value']) === false &&
49
+ empty($value['label']) === false) {
50
+ $returnOptions[] = array("name" => $value['label'], "code" => $value['value']);
51
+ }
52
+ }
53
+
54
+ $this->_JSONencodeAndRespond($returnOptions);
55
+ }
56
+
57
+ /**
58
+ * Logout action
59
+ */
60
+ public function logoutAction() {
61
+ Mage::getSingleton('customer/session')->logout();
62
+ $this->_JSONencodeAndRespond(array("OK"));
63
+ }
64
+
65
+ /**
66
+ * Logs the user in. Gets the parameters "email" and "password" from POST
67
+ *
68
+ * @author Tim Wachter
69
+ *
70
+ */
71
+ public function loginAction() {
72
+ $session = Mage::getSingleton('customer/session');
73
+
74
+ $success = false;
75
+ $message = "";
76
+
77
+ $requestObject = Mage::app()->getRequest();
78
+
79
+ $loginArray = $requestObject->getParam('login');
80
+
81
+ $email = $loginArray["username"];
82
+ $password = $loginArray["password"];
83
+
84
+ if ($session->isLoggedIn()) {
85
+ $success = false;
86
+ $message = "Je bent al ingelogd.";
87
+ } else {
88
+ try {
89
+ if ($session->login($email, $password)) {
90
+ $success = true;
91
+ $message = "Je bent succesvol ingelogd.";
92
+ }
93
+ } catch (Mage_Core_Exception $e) {
94
+ switch ($e->getCode()) {
95
+ case Mage_Customer_Model_Customer::EXCEPTION_EMAIL_NOT_CONFIRMED: { // E-mail not confirmed
96
+ $success = false;
97
+ $message = "Je account is nog niet geactiveerd. Je moet je account activeren voordat je kunt inloggen.";
98
+ break;
99
+ }
100
+ case Mage_Customer_Model_Customer::EXCEPTION_INVALID_EMAIL_OR_PASSWORD: { // E-mail or password wrong
101
+ $success = false;
102
+ $message = "De combinatie van het ingegeven e-mailadres en wachtwoord is onjuist.";
103
+ break;
104
+ }
105
+ default: {
106
+ $success = false;
107
+ $message = "Er heeft zich een onbekende fout voorgedaan. Probeer het later nog eens.";
108
+ break;
109
+ }
110
+ }
111
+ } catch (Exception $e) {
112
+ $success = false;
113
+ $message = "Er heeft zich een onbekende fout voorgedaan. Probeer het later nog eens.";
114
+ }
115
+ }
116
+
117
+ $response = array();
118
+ $response["success"] = $success;
119
+ $response["message"] = $message;
120
+
121
+ $this->_JSONencodeAndRespond($response);
122
+ }
123
+
124
+ /**
125
+ * Re-used from Mage_Checkout_OnepageController:348
126
+ */
127
+ public function saveMethodAction() {
128
+ $method = $this->getRequest()->getPost('method');
129
+ $result = $this->getOnepage()->saveCheckoutMethod($method);
130
+ $this->_JSONencodeAndRespond($result);
131
+ }
132
+
133
+ /**
134
+ * Largely re-used from Mage_Checkout_OnepageController:363
135
+ */
136
+ public function saveBillingAction() {
137
+ $data = $this->getRequest()->getPost('billing', array());
138
+ $customerAddressId = $this->getRequest()->getPost('billing_address_id', false);
139
+
140
+ if (isset($data['email'])) {
141
+ $data['email'] = trim($data['email']);
142
+ }
143
+ $result = $this->getOnepage()->saveBilling($data, $customerAddressId);
144
+
145
+ if (!isset($result['error'])) {
146
+ if (isset($data['use_for_shipping']) && $data['use_for_shipping'] == 1) {
147
+ $result['goto_section'] = 'shipping_method';
148
+
149
+ $result['update_section'] = array(
150
+ 'name' => 'shipping-method',
151
+ 'data' => $this->_getShippingMethods()
152
+ );
153
+
154
+ $result['allow_sections'] = array('shipping');
155
+ $result['duplicateBillingInfo'] = 'true';
156
+ } else {
157
+ $result['goto_section'] = 'shipping';
158
+ }
159
+ }
160
+
161
+ $this->_JSONencodeAndRespond($result);
162
+ }
163
+
164
+ /**
165
+ * Largely re-used from Mage_Checkout_OnepageController:405
166
+ */
167
+ public function saveShippingAction()
168
+ {
169
+ $data = $this->getRequest()->getPost('shipping', array());
170
+ $customerAddressId = $this->getRequest()->getPost('shipping_address_id', false);
171
+ $result = $this->getOnepage()->saveShipping($data, $customerAddressId);
172
+
173
+ if (!isset($result['error'])) {
174
+ $result['goto_section'] = 'shipping_method';
175
+ $result['update_section'] = array(
176
+ 'name' => 'shipping-method',
177
+ 'data' => $this->_getShippingMethods()
178
+ );
179
+ }
180
+ $this->_JSONencodeAndRespond($result);
181
+ }
182
+
183
+ /**
184
+ * Largely re-used from Mage_Checkout_OnepageController:429
185
+ */
186
+ public function saveShippingMethodAction()
187
+ {
188
+ $data = $this->getRequest()->getPost('shipping_method', '');
189
+ $result = $this->getOnepage()->saveShippingMethod($data);
190
+ // $result will contain error data if shipping method is empty
191
+ if (!$result) {
192
+ Mage::dispatchEvent(
193
+ 'checkout_controller_onepage_save_shipping_method',
194
+ array(
195
+ 'request' => $this->getRequest(),
196
+ 'quote' => $this->getOnepage()->getQuote()));
197
+ $this->getOnepage()->getQuote()->collectTotals();
198
+ $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result));
199
+
200
+ $paymentMethods = array();
201
+
202
+
203
+ $result['goto_section'] = 'payment';
204
+ $result['update_section'] = array(
205
+ 'name' => 'payment-method',
206
+ 'data' => $this->_getPaymentMethods()
207
+ );
208
+ }
209
+ $this->getOnepage()->getQuote()->collectTotals()->save();
210
+ $this->_JSONencodeAndRespond($result);
211
+ }
212
+
213
+ /**
214
+ * Adds a coupon to the quote.
215
+ * Code is partially duplicated from couponPostAction in the Mage_Checkout_CartController
216
+ *
217
+ * @author Tim Wachter
218
+ *
219
+ */
220
+ public function addCouponAction() {
221
+ $quote = Mage::getModel('checkout/cart')->getQuote();
222
+
223
+ // Shopping cart is empty
224
+ // The checkout should fire a restart
225
+ if (!$quote->getItemsCount()) {
226
+ $this->_JSONencodeAndRespond(array("error" => self::COUPON_CODE_FATAL, "message" => $this->__('hsapi.addCouponAction.error.fatal')));
227
+ return;
228
+ }
229
+
230
+ $couponCode = (string) $this->getRequest()->getParam('coupon_code');
231
+ if ($this->getRequest()->getParam('remove') == 1) {
232
+ $couponCode = '';
233
+ }
234
+ $oldCouponCode = $quote->getCouponCode();
235
+
236
+ // No coupon code given
237
+ if (!strlen($couponCode) && !strlen($oldCouponCode)) {
238
+ $this->_JSONencodeAndRespond(array("error" => self::COUPON_CODE_ERROR, "message" => $this->__('hsapi.addCouponAction.error.invalid')));
239
+ return;
240
+ }
241
+
242
+ try {
243
+ $codeLength = strlen($couponCode);
244
+
245
+ if ($codeLength >= self::COUPON_CODE_MAX_LENGTH) {
246
+ $this->_JSONencodeAndRespond(array("error" => self::COUPON_CODE_ERROR, "message" => $this->__('hsapi.addCouponAction.error.length') . self::COUPON_CODE_MAX_LENGTH));
247
+ return;
248
+ }
249
+
250
+ $quote->setCouponCode($couponCode)
251
+ ->collectTotals() // Makes sure that the totals are recalculated with the new discount. Without this the coupon simply gets added, but not calculated in the price
252
+ ->save();
253
+
254
+ if ($codeLength) {
255
+ if ($couponCode == $quote->getCouponCode()) { // Code was successfully added
256
+ $message = str_replace(self::COUPON_SUCCESS_REPLACE, $this->getRequest()->getParam('coupon_code'), $this->__('hsapi.addCouponAction.success'));
257
+ $this->_JSONencodeAndRespond(array("error" => self::COUPON_CODE_SUCCESS, "message" => $message));
258
+ return;
259
+ } else { // Code was not valid
260
+ $this->_JSONencodeAndRespond(array("error" => self::COUPON_CODE_ERROR, "message" => $this->__('hsapi.addCouponAction.error.invalid')));
261
+ return;
262
+ }
263
+ } else {
264
+ $message = str_replace(self::COUPON_SUCCESS_REPLACE, $this->getRequest()->getParam('coupon_code'), $this->__('hsapi.addCouponAction.success.removed'));
265
+ $this->_JSONencodeAndRespond(array("error" => self::COUPON_CODE_SUCCESS, "message" => $message));
266
+ return;
267
+ }
268
+
269
+ } catch (Mage_Core_Exception $e) {
270
+ $this->_JSONencodeAndRespond(array("error" => self::COUPON_CODE_FATAL, "message" => $this->__('hsapi.addCouponAction.error.fatal')));
271
+ return;
272
+ } catch (Exception $e) {
273
+ $this->_JSONencodeAndRespond(array("error" => self::COUPON_CODE_FATAL, "message" => $this->__('hsapi.addCouponAction.error.fatal')));
274
+ return;
275
+ }
276
+
277
+ $this->_JSONencodeAndRespond(array("error" => self::COUPON_CODE_FATAL, "message" => $this->__('hsapi.addCouponAction.error.fatal')));
278
+ return;
279
+ }
280
+
281
+ /**
282
+ * Largely re-used from Mage_Checkout_OnepageController:463
283
+ */
284
+ public function savePaymentAction()
285
+ {
286
+ try {
287
+ $data = $this->getRequest()->getPost('payment', array());
288
+ $result = $this->getOnepage()->savePayment($data);
289
+
290
+ // get section and redirect data
291
+ $redirectUrl = $this->getOnepage()->getQuote()->getPayment()->getCheckoutRedirectUrl();
292
+ if (empty($result['error']) && !$redirectUrl) {
293
+ $result['goto_section'] = 'review';
294
+ $result['update_section'] = array(
295
+ 'name' => 'review',
296
+ 'data' => $this->_getReviewData()
297
+ );
298
+ }
299
+ if ($redirectUrl) {
300
+ $result['redirect'] = $redirectUrl;
301
+ }
302
+ } catch (Mage_Payment_Exception $e) {
303
+ if ($e->getFields()) {
304
+ $result['fields'] = $e->getFields();
305
+ }
306
+ $result['error'] = $e->getMessage();
307
+ } catch (Mage_Core_Exception $e) {
308
+ $result['error'] = $e->getMessage();
309
+ } catch (Exception $e) {
310
+ Mage::logException($e);
311
+ $result['error'] = $this->__('Unable to set Payment Method.');
312
+ }
313
+ $this->_JSONencodeAndRespond($result, false);
314
+ }
315
+
316
+ /**
317
+ * Largely re-used from Mage_Checkout_OnepageController:543
318
+ */
319
+ public function saveOrderAction() {
320
+ $result = array();
321
+ try {
322
+ $requiredAgreements = Mage::helper('checkout')->getRequiredAgreementIds();
323
+ if ($requiredAgreements) {
324
+ $postedAgreements = array_keys($this->getRequest()->getPost('agreement', array()));
325
+ $diff = array_diff($requiredAgreements, $postedAgreements);
326
+ if ($diff) {
327
+ $result['success'] = false;
328
+ $result['error'] = true;
329
+ $result['error_messages'] = $this->__('Please agree to all the terms and conditions before placing the order.');
330
+ $this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result));
331
+ return;
332
+ }
333
+ }
334
+
335
+ if ($data = $this->getRequest()->getPost('payment', false)) {
336
+ $this->getOnepage()->getQuote()->getPayment()->importData($data);
337
+ }
338
+ $this->getOnepage()->saveOrder();
339
+
340
+ // In the saveOrder function (^^^) in the file /app/code/core/Mage/Checkout/Model/Type/Onepage.php:767 the order is finalized and saved.
341
+ // On line 796 the "LastSuccessQuoteId" value is set in the session of the user
342
+ // We can read this value to create a hash from it and insert it in the comment.
343
+ // Later on we re-use this hash to check if a specific order was a Highstreet order
344
+ // On line 823 the "LastRealOderId" value is set in the session of the user
345
+ // We can read this value to get the order ID and insert a comment in the order.
346
+ // This comment in the order is absolutely crucial for the order tracking of Highstreet.
347
+
348
+ try {
349
+ $checkoutSession = Mage::getSingleton('checkout/session');
350
+ $orderId = $checkoutSession->getLastRealOrderId();
351
+ $quoteId = $checkoutSession->getLastSuccessQuoteId();
352
+ if ($orderId > 0 && $quoteId > 0) {
353
+ $encryptionHelper = Mage::helper('highstreet_hsapi/encryption');
354
+ $quoteIdHash = $encryptionHelper->hashQuoteId($quoteId);
355
+
356
+ $order = Mage::getModel('sales/order')->loadByIncrementId($orderId);
357
+ $order->addStatusHistoryComment('Order made via the Highstreet app. Quote hash:' . $quoteIdHash)
358
+ ->setIsVisibleOnFront(false)
359
+ ->setIsCustomerNotified(false);
360
+ $order->save();
361
+ }
362
+ } catch (Exception $e) {}
363
+
364
+ $redirectUrl = $this->getOnepage()->getCheckout()->getRedirectUrl();
365
+ $result['success'] = true;
366
+ $result['error'] = false;
367
+ } catch (Mage_Payment_Model_Info_Exception $e) {
368
+ $message = $e->getMessage();
369
+ if (!empty($message)) {
370
+ $result['error_messages'] = $message;
371
+ }
372
+ $result['goto_section'] = 'payment';
373
+ $result['update_section'] = array(
374
+ 'name' => 'payment-method',
375
+ 'html' => $this->_getPaymentMethodsHtml()
376
+ );
377
+ } catch (Mage_Core_Exception $e) {
378
+ Mage::logException($e);
379
+ Mage::helper('checkout')->sendPaymentFailedEmail($this->getOnepage()->getQuote(), $e->getMessage());
380
+ $result['success'] = false;
381
+ $result['error'] = true;
382
+ $result['error_messages'] = $e->getMessage();
383
+
384
+ $gotoSection = $this->getOnepage()->getCheckout()->getGotoSection();
385
+ if ($gotoSection) {
386
+ $result['goto_section'] = $gotoSection;
387
+ $this->getOnepage()->getCheckout()->setGotoSection(null);
388
+ }
389
+ $updateSection = $this->getOnepage()->getCheckout()->getUpdateSection();
390
+ if ($updateSection) {
391
+ if (isset($this->_sectionUpdateFunctions[$updateSection])) {
392
+ $updateSectionFunction = $this->_sectionUpdateFunctions[$updateSection];
393
+ $result['update_section'] = array(
394
+ 'name' => $updateSection,
395
+ 'html' => $this->$updateSectionFunction()
396
+ );
397
+ }
398
+ $this->getOnepage()->getCheckout()->setUpdateSection(null);
399
+ }
400
+ } catch (Exception $e) {
401
+ Mage::logException($e);
402
+ Mage::helper('checkout')->sendPaymentFailedEmail($this->getOnepage()->getQuote(), $e->getMessage());
403
+ $result['success'] = false;
404
+ $result['error'] = true;
405
+ $result['error_messages'] = $this->__('There was an error processing your order. Please contact us or try again later.');
406
+ }
407
+ $this->getOnepage()->getQuote()->save();
408
+ /**
409
+ * when there is redirect to third party, we don't want to save order yet.
410
+ * we will save the order in return action.
411
+ */
412
+ if (isset($redirectUrl)) {
413
+ $result['redirect'] = $redirectUrl;
414
+ }
415
+
416
+ $this->_JSONencodeAndRespond($result);
417
+ }
418
+
419
+ private function getOnepage() {
420
+ return Mage::getSingleton('checkout/type_onepage');
421
+ }
422
+
423
+ private function _getShippingMethods() {
424
+ $shippingMethods = array();
425
+
426
+ $shouldGetPostNL = true;
427
+ $postNlOptions = array();
428
+ try {
429
+ $quote = Mage::getSingleton('checkout/session')->getQuote(); // Get quote for filled in address data
430
+ $shippingAddressData = $quote->getShippingAddress()->getData();
431
+
432
+ $cif = Mage::getModel('postnl_deliveryoptions/cif');
433
+ if ($cif) {
434
+ $postNlOptions = $cif->setStoreId(Mage::app()->getStore()->getId())
435
+ ->getDeliveryTimeframes(array(
436
+ 'postcode' => str_replace(" ", "", $shippingAddressData["postcode"]), // Postcode
437
+ 'housenumber' => ereg_replace("[^0-9]", "", $shippingAddressData["street"]), // Extract housenumber from street field
438
+ 'deliveryDate' => date('d-m-Y', strtotime('+ 1 day')), // Set delivery day to tomorrow
439
+ ));
440
+ } else {
441
+ $shouldGetPostNL = false;
442
+ }
443
+ } catch (Exception $e) {
444
+ $shouldGetPostNL = false;
445
+ }
446
+
447
+ $quote = Mage::getSingleton('checkout/cart')->getQuote();
448
+ foreach ($quote->getShippingAddress()->getGroupedAllShippingRates() as $_rates) {
449
+ foreach ($_rates as $_rate){
450
+ $checked = false;
451
+
452
+ $shippingMethod;
453
+ try {
454
+ $shippingMethod = $quote->getShippingAddress()->getShippingMethod();
455
+ } catch (Exception $e) {}
456
+
457
+ if (!empty($shippingMethod) && $_rate->getCode() === $shippingMethod) {
458
+ $checked = true;
459
+ }
460
+
461
+ $shippingMethod = array();
462
+ $shippingMethod['title'] = $_rate->getMethodTitle();
463
+ $shippingMethod['price'] = $_rate->getData('price');
464
+ $shippingMethod['code'] = $_rate->getCode();
465
+ $shippingMethod['checked'] = $checked;
466
+
467
+ if (strstr($_rate->getCode(), "postnl") !== false) {
468
+ $shippingMethod['sub_options'] = $postNlOptions;
469
+ }
470
+
471
+ $shippingMethods[] = $shippingMethod;
472
+
473
+ }
474
+ }
475
+
476
+ return $shippingMethods;
477
+ }
478
+
479
+ private function _getPaymentMethods() {
480
+ $paymentMethods = array();
481
+
482
+ $model = new Mage_Checkout_Block_Onepage_Payment_Methods();
483
+
484
+ $quote = Mage::getSingleton('checkout/cart')->getQuote();
485
+ $selectedPaymentMethod;
486
+ try {
487
+ $selectedPaymentMethod = $quote->getPayment()->getData('method');
488
+ } catch (Exception $e) {}
489
+
490
+ foreach ($model->getMethods() as $method) {
491
+ $object = array();
492
+ $code = $method->getCode();
493
+
494
+ $methodTitle = $method->getTitle();
495
+ if ($code == "paypal_express") { // PayPal. Has logo and strange label text, override
496
+ $methodTitle = "PayPal";
497
+ }
498
+
499
+ $checked = false;
500
+
501
+ if (!empty($selectedPaymentMethod) && $selectedPaymentMethod == $code) {
502
+ $checked = true;
503
+ }
504
+
505
+ $object["title"] = $methodTitle;
506
+ $object["code"] = $code;
507
+ $object["checked"] = $checked;
508
+
509
+ if ($code === "buckaroo3extended_ideal") {
510
+ $session = Mage::getSingleton('checkout/session');
511
+ $sessionValue = $session->getData('buckaroo3extended_ideal_BPE_Issuer');
512
+ $buckarooIdealModel = new TIG_Buckaroo3Extended_Block_PaymentMethods_Ideal_Checkout_Form();
513
+ $issuerList = $buckarooIdealModel->getIssuerList();
514
+
515
+ foreach ($issuerList as $issuer => $issuerDetails) {
516
+ $option = array();
517
+ $optionChecked = false;
518
+ if (!empty($sessionValue) && array_key_exists($sessionValue, $issuerList)) {
519
+ if ($issuer == $sessionValue) {
520
+ $optionChecked = true;
521
+ }
522
+ }
523
+
524
+ $option["checked"] = $optionChecked;
525
+ $option["title"] = $issuerDetails['name'];
526
+ $option["code"] = $issuer;
527
+ $option["image"] = $issuerDetails['logo'];
528
+ $object["sub_options"][] = $option;
529
+ }
530
+ }
531
+
532
+ $paymentMethods[] = $object;
533
+ }
534
+
535
+ return $paymentMethods;
536
+ }
537
+
538
+ private function _getReviewData() {
539
+ $session = Mage::getSingleton('checkout/session');
540
+ $quote = $session->getQuote();
541
+ $billingAddress = $quote->getBillingAddress();
542
+ $shippingAddress = $quote->getShippingAddress();
543
+ $billingAddressData = $billingAddress->getData();
544
+ $shippingAddressData = $shippingAddress->getData();
545
+
546
+ $response = array();
547
+
548
+ // Billing address information
549
+ $billing_address = array();
550
+ $billing_address["email"] = $quote->getCustomerEmail();
551
+ $billing_address["firstname"] = $billingAddressData["firstname"];
552
+ $billing_address["lastname"] = $billingAddressData["lastname"];
553
+ $billing_address["telephone"] = (string) $billingAddressData["telephone"];
554
+ $billing_address["street"] = $billingAddressData["street"];
555
+ $billing_address["postcode"] = $billingAddressData["postcode"];
556
+ $billing_address["city"] = $billingAddressData["city"];
557
+
558
+ $response["billing_address"] = $billing_address;
559
+
560
+ // Shipping address information
561
+ $shipping_address = array();
562
+ $shipping_address["firstname"] = $shippingAddressData["firstname"];
563
+ $shipping_address["lastname"] = $shippingAddressData["lastname"];
564
+ $shipping_address["telephone"] = (string) $shippingAddressData["telephone"];
565
+ $shipping_address["street"] = $shippingAddressData["street"];
566
+ $shipping_address["postcode"] = $shippingAddressData["postcode"];
567
+ $shipping_address["city"] = $shippingAddressData["city"];
568
+
569
+ if (!$this->_billingAndShippingAddressesAreTheSame($response["billing_address"], $shipping_address)) {
570
+ $response["shipping_address"] = $shipping_address;
571
+ } else {
572
+ $response["shipping_address"] = array();
573
+ }
574
+
575
+
576
+ // Shipping method information
577
+ $response["shipping_method"]["name"] = $shippingAddress->getShippingDescription();
578
+
579
+ // Payment method information
580
+ $response["payment_method"]["name"] = $quote->getPayment()->getMethodInstance()->getTitle();
581
+ $response["payment_method"]["coupon_code"] = $quote->getCouponCode();
582
+
583
+
584
+ return $response;
585
+ }
586
+
587
+ /**
588
+ * Conveinience method, compares 2 formatted address arrays
589
+ */
590
+ private function _billingAndShippingAddressesAreTheSame($billingAddressArray = array(), $shippingAddressArray = array()) {
591
+ if (count($billingAddressArray) == 0 || count($shippingAddressArray) == 0) {
592
+ return true;
593
+ }
594
+
595
+ if ($billingAddressArray["firstname"] !== $shippingAddressArray["firstname"] ||
596
+ $billingAddressArray["lastname"] !== $shippingAddressArray["lastname"] ||
597
+ $billingAddressArray["telephone"] !== $shippingAddressArray["telephone"] ||
598
+ $billingAddressArray["street"] !== $shippingAddressArray["street"] ||
599
+ $billingAddressArray["postcode"] !== $shippingAddressArray["postcode"] ||
600
+ $billingAddressArray["city"] !== $shippingAddressArray["city"]) {
601
+ return false;
602
+ } else {
603
+ return true;
604
+ }
605
+ }
606
+
607
+ /**
608
+ * Sets the proper headers
609
+ */
610
+ private function _setHeader()
611
+ {
612
+ Mage::getSingleton('core/session')->setLastStoreCode(Mage::app()->getStore()->getCode());
613
+ header_remove('Pragma'); // removes 'no-cache' header
614
+ $this->getResponse()->setHeader('Content-Type','application/json', true);
615
+ }
616
+
617
+ /**
618
+ * Sets headers and body with proper JSON encoding
619
+ */
620
+ private function _JSONencodeAndRespond($data, $numericCheck = true) {
621
+ //set response body
622
+ $this->_setHeader();
623
+ if ($numericCheck) {
624
+ $this->getResponse()->setBody(json_encode($data, JSON_NUMERIC_CHECK));
625
+ } else {
626
+ $this->getResponse()->setBody(json_encode($data));
627
+ }
628
+
629
+ }
630
+ }
app/code/local/Highstreet/Hsapi/controllers/IndexController.php CHANGED
@@ -83,7 +83,24 @@ class Highstreet_Hsapi_IndexController extends Mage_Core_Controller_Front_Action
83
  }
84
 
85
  public function pingAction() {
86
- $this->_JSONencodeAndRespond(array("OK"));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  }
88
 
89
  /**
@@ -130,32 +147,36 @@ class Highstreet_Hsapi_IndexController extends Mage_Core_Controller_Front_Action
130
  public function checkoutAction() {
131
  $requestObject = Mage::app()->getRequest();
132
  $data = json_decode($requestObject->getParam('products'), true);
 
 
 
133
 
134
- $checkoutModel = Mage::getModel('highstreet_hsapi/checkout');
135
- $checkoutModel->fillCartWithProductsAndQuantities($data["checkout"]);
 
136
 
137
- $locale = Mage::app()->getLocale()->getLocaleCode();
138
-
139
  $country = $requestObject->getParam('country');
140
- $country = preg_replace("/[^a-zA-Z]/", "", $country);
141
- $country = strtoupper($country);
 
142
 
143
- if ($country === "") { // No country given in URL, fallback on store country from locale
144
- $locale = Mage::app()->getLocale()->getLocaleCode();
145
- $country = strtoupper(substr($locale, strpos($locale, "_")+1));
 
 
146
  }
147
 
148
- Mage::getSingleton('core/session')->setHSCheckoutCountry($country);
149
 
150
  $urlOptions = array();
151
  if (!empty($_SERVER['HTTPS'])) { // Server is not HTTPS
152
  $urlOptions['_secure'] = TRUE;
153
  }
154
 
155
- $config = Mage::helper('highstreet_hsapi/config');
156
- $checkoutUrl = $config->checkoutUrl();
157
 
158
- Mage::app()->getFrontController()->getResponse()->setRedirect(Mage::getUrl($checkoutUrl, $urlOptions));
159
  }
160
 
161
 
@@ -169,6 +190,17 @@ class Highstreet_Hsapi_IndexController extends Mage_Core_Controller_Front_Action
169
 
170
  }
171
 
 
 
 
 
 
 
 
 
 
 
 
172
 
173
  public function postCart() {
174
  $requestObject = Mage::app()->getRequest();
@@ -181,6 +213,17 @@ class Highstreet_Hsapi_IndexController extends Mage_Core_Controller_Front_Action
181
 
182
  }
183
 
 
 
 
 
 
 
 
 
 
 
 
184
  public function getCart() {
185
  $requestObject = Mage::app()->getRequest();
186
  $quote_id = $requestObject->getParam('quote_id');
@@ -195,6 +238,22 @@ class Highstreet_Hsapi_IndexController extends Mage_Core_Controller_Front_Action
195
  $this->_JSONencodeAndRespond($cart);
196
 
197
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
198
  }
199
 
200
  /**
@@ -204,12 +263,26 @@ class Highstreet_Hsapi_IndexController extends Mage_Core_Controller_Front_Action
204
  {
205
  $requestObject = Mage::app()->getRequest();
206
  $model = Mage::getModel('highstreet_hsapi/products');
207
-
208
- $id = $requestObject->getParam('id');
209
- $attributes = $requestObject->getParam('attributes');
210
- $child_product_attributes = $requestObject->getParam('child_product_attributes');
211
 
212
- $response = $model->getSingleProduct($id, $attributes,$child_product_attributes);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
 
214
  if ($response == null) {
215
  $this->_respondWith404();
@@ -249,13 +322,16 @@ class Highstreet_Hsapi_IndexController extends Mage_Core_Controller_Front_Action
249
  $requestObject = Mage::app()->getRequest();
250
  $model = Mage::getModel('highstreet_hsapi/products');
251
 
252
- $products = $model->getProductsForResponse($requestObject->getParam('attributes'),
253
- $requestObject->getParam('child_product_attributes'),
254
  $requestObject->getParam('order'),
255
  $requestObject->getParam('range'),
256
  $requestObject->getParam('filter'),
257
  $requestObject->getParam('search'),
258
- $requestObject->getParam('id'));
 
 
 
 
259
 
260
  if ($products == null) {
261
  $this->_respondWith404();
@@ -273,16 +349,35 @@ class Highstreet_Hsapi_IndexController extends Mage_Core_Controller_Front_Action
273
  {
274
  $requestObject = Mage::app()->getRequest();
275
  $model = Mage::getModel('highstreet_hsapi/products');
276
-
277
- $products = $model->getBatchProducts($requestObject->getParam('ids'),
278
- $requestObject->getParam('attributes'),
279
- $requestObject->getParam('child_product_attributes'));
280
 
281
- if ($products == null) {
282
- $this->_respondWith404();
283
- return false;
 
 
 
284
  }
285
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
286
  $this->_JSONencodeAndRespond($products);
287
  }
288
 
@@ -296,9 +391,11 @@ class Highstreet_Hsapi_IndexController extends Mage_Core_Controller_Front_Action
296
 
297
  $response = $productsModel->getRelatedProducts($requestObject->getParam('type'),
298
  $requestObject->getParam('id'),
299
- $requestObject->getParam('attributes'),
300
- $requestObject->getParam('child_product_attributes'),
301
- $requestObject->getParam('range'));
 
 
302
 
303
  if ($response == null) {
304
  $this->_respondWith404();
@@ -337,13 +434,11 @@ class Highstreet_Hsapi_IndexController extends Mage_Core_Controller_Front_Action
337
  /** @var $params */
338
  $params = $helper->extractRequestParam(Mage::app()->getRequest()->getParams());
339
 
340
- if(is_string($params))
341
- {
342
  //get Single Attribute
343
  $attributes = $attributesModel->getAttribute($params);
344
  $responseBody = $attributes;
345
- }
346
- else{
347
  //Get all attributes
348
  $attributes = $attributesModel->getAttributes();
349
  $responseBody = $attributes;
@@ -377,10 +472,48 @@ class Highstreet_Hsapi_IndexController extends Mage_Core_Controller_Front_Action
377
 
378
  $this->_JSONencodeAndRespond($response, FALSE);
379
  }
380
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
381
  /**
382
  * Header and http functions
383
  */
 
 
 
 
 
 
 
384
  private function _respondWith404()
385
  {
386
  $this->getResponse()->setHeader('HTTP/1.1','404 Not Found');
@@ -388,6 +521,13 @@ class Highstreet_Hsapi_IndexController extends Mage_Core_Controller_Front_Action
388
  return;
389
  }
390
 
 
 
 
 
 
 
 
391
  /**
392
  * Sets the proper headers
393
  */
83
  }
84
 
85
  public function pingAction() {
86
+ $configApi = Mage::helper('highstreet_hsapi/config_api');
87
+ $this->_JSONencodeAndRespond("OK");
88
+ }
89
+
90
+ public function infoAction() {
91
+ $encryptionHelper = Mage::helper('highstreet_hsapi/encryption');
92
+ if (!$encryptionHelper->APISignatureStringIsValid()) {
93
+ $this->_respondWith401();
94
+ return;
95
+ }
96
+
97
+ $configApi = Mage::helper('highstreet_hsapi/config_api');
98
+ $this->_JSONencodeAndRespond(array("status" => "OK",
99
+ "identifer" => $configApi->storeIdentifier(),
100
+ "version" => (string)Mage::getConfig()->getNode()->modules->Highstreet_Hsapi->version,
101
+ "magento_version" => (string)Mage::getVersion(),
102
+ "environment" => $configApi->environment(),
103
+ "storefront" => Mage::app()->getStore()->getCode()));
104
  }
105
 
106
  /**
147
  public function checkoutAction() {
148
  $requestObject = Mage::app()->getRequest();
149
  $data = json_decode($requestObject->getParam('products'), true);
150
+ $country = $requestObject->getParam('country');
151
+ return $this->_postCheckout($data["checkout"],$country);
152
+ }
153
 
154
+ public function checkout_v2Action() {
155
+ $requestObject = Mage::app()->getRequest();
156
+ $data = json_decode($requestObject->getParam('items'), true);
157
 
 
 
158
  $country = $requestObject->getParam('country');
159
+ return $this->_postCheckout($data,$country,true);
160
+
161
+ }
162
 
163
+ private function _postCheckout($items,$country = null,$v2 = false) {
164
+ if($v2) {
165
+ $checkoutModel = Mage::getModel('highstreet_hsapi/checkoutV2');
166
+ } else {
167
+ $checkoutModel = Mage::getModel('highstreet_hsapi/checkout');
168
  }
169
 
170
+ $checkoutModel->fillCartWithProductsAndQuantities($items);
171
 
172
  $urlOptions = array();
173
  if (!empty($_SERVER['HTTPS'])) { // Server is not HTTPS
174
  $urlOptions['_secure'] = TRUE;
175
  }
176
 
177
+ $checkoutModel = Mage::getModel('highstreet_hsapi/checkoutV2');
 
178
 
179
+ $this->_JSONencodeAndRespond($checkoutModel->getStartData());
180
  }
181
 
182
 
190
 
191
  }
192
 
193
+ public function cart_v2Action() {
194
+ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
195
+ return $this->postCart_v2();
196
+ }
197
+ else if ($_SERVER['REQUEST_METHOD'] === 'GET') {
198
+ return $this->getCart_v2();
199
+ }
200
+
201
+ }
202
+
203
+
204
 
205
  public function postCart() {
206
  $requestObject = Mage::app()->getRequest();
213
 
214
  }
215
 
216
+ public function postCart_v2() {
217
+ $requestObject = Mage::app()->getRequest();
218
+ $data = json_decode($requestObject->getParam('items'), true);
219
+
220
+ $checkoutModel = Mage::getModel('highstreet_hsapi/checkoutV2');
221
+ $cart = $checkoutModel->getQuoteWithProductsAndQuantities($data);
222
+
223
+ $this->_JSONencodeAndRespond($cart);
224
+
225
+ }
226
+
227
  public function getCart() {
228
  $requestObject = Mage::app()->getRequest();
229
  $quote_id = $requestObject->getParam('quote_id');
238
  $this->_JSONencodeAndRespond($cart);
239
 
240
 
241
+ }
242
+
243
+ public function getCart_v2() {
244
+ $requestObject = Mage::app()->getRequest();
245
+ $quote_id = $requestObject->getParam('quote_id');
246
+ if(!$quote_id) {
247
+ $this->_respondWith404();
248
+ return false;
249
+ }
250
+
251
+ $checkoutModel = Mage::getModel('highstreet_hsapi/checkoutV2');
252
+ $cart = $checkoutModel->getQuoteWithProductsAndQuantities(null,$quote_id);
253
+
254
+ $this->_JSONencodeAndRespond($cart);
255
+
256
+
257
  }
258
 
259
  /**
263
  {
264
  $requestObject = Mage::app()->getRequest();
265
  $model = Mage::getModel('highstreet_hsapi/products');
 
 
 
 
266
 
267
+ $productObject = Mage::getModel('catalog/product')->load($requestObject->getParam('id'));
268
+
269
+ if (!$productObject->getId()) {
270
+ $this->_respondWith404();
271
+ return false;
272
+ }
273
+
274
+ if (isset($_SERVER["HTTP_IF_MODIFIED_SINCE"]) && $_SERVER["HTTP_IF_MODIFIED_SINCE"] !== NULL) {
275
+ if (!$model->productHasBeenModifiedSince($productObject, $_SERVER["HTTP_IF_MODIFIED_SINCE"])) {
276
+ $this->_respondWith304();
277
+ return false;
278
+ }
279
+ }
280
+
281
+ $response = $model->getSingleProduct($productObject,
282
+ $requestObject->getParam('additional_attributes'),
283
+ $requestObject->getParam('include_configuration_details'),
284
+ $requestObject->getParam('include_media_gallery'));
285
+
286
 
287
  if ($response == null) {
288
  $this->_respondWith404();
322
  $requestObject = Mage::app()->getRequest();
323
  $model = Mage::getModel('highstreet_hsapi/products');
324
 
325
+ $products = $model->getProductsForResponse($requestObject->getParam('additional_attributes'),
 
326
  $requestObject->getParam('order'),
327
  $requestObject->getParam('range'),
328
  $requestObject->getParam('filter'),
329
  $requestObject->getParam('search'),
330
+ $requestObject->getParam('id'),
331
+ $requestObject->getParam('hide_attributes'),
332
+ $requestObject->getParam('hide_filters'),
333
+ $requestObject->getParam('include_configuration_details'),
334
+ $requestObject->getParam('include_media_gallery'));
335
 
336
  if ($products == null) {
337
  $this->_respondWith404();
349
  {
350
  $requestObject = Mage::app()->getRequest();
351
  $model = Mage::getModel('highstreet_hsapi/products');
 
 
 
 
352
 
353
+ $idsArray = explode(',', $requestObject->getParam('ids'));
354
+ $productObjects = array();
355
+
356
+ foreach ($idsArray as $value) {
357
+ $productObject = Mage::getModel('catalog/product')->load($value);
358
+ $productObjects[] = $productObject;
359
  }
360
 
361
+ if (isset($_SERVER["HTTP_IF_MODIFIED_SINCE"]) && $_SERVER["HTTP_IF_MODIFIED_SINCE"] !== NULL) {
362
+ $productHasChanged = false;
363
+
364
+ foreach ($productObjects as $productObject) {
365
+ if ($model->productHasBeenModifiedSince($productObject, $_SERVER["HTTP_IF_MODIFIED_SINCE"])) {
366
+ $productHasChanged = true;
367
+ }
368
+ }
369
+
370
+ if (!$productHasChanged) {
371
+ $this->_respondWith304();
372
+ return false;
373
+ }
374
+ }
375
+
376
+ $products = $model->getBatchProducts($productObjects,
377
+ $requestObject->getParam('additional_attributes'),
378
+ $requestObject->getParam('include_configuration_details'),
379
+ $requestObject->getParam('include_media_gallery'));
380
+
381
  $this->_JSONencodeAndRespond($products);
382
  }
383
 
391
 
392
  $response = $productsModel->getRelatedProducts($requestObject->getParam('type'),
393
  $requestObject->getParam('id'),
394
+ $requestObject->getParam('additional_attributes'),
395
+ $requestObject->getParam('range'),
396
+ $requestObject->getParam('hide_attributes'),
397
+ $requestObject->getParam('include_configuration_details'),
398
+ $requestObject->getParam('include_media_gallery'));
399
 
400
  if ($response == null) {
401
  $this->_respondWith404();
434
  /** @var $params */
435
  $params = $helper->extractRequestParam(Mage::app()->getRequest()->getParams());
436
 
437
+ if (is_string($params)) {
 
438
  //get Single Attribute
439
  $attributes = $attributesModel->getAttribute($params);
440
  $responseBody = $attributes;
441
+ } else {
 
442
  //Get all attributes
443
  $attributes = $attributesModel->getAttributes();
444
  $responseBody = $attributes;
472
 
473
  $this->_JSONencodeAndRespond($response, FALSE);
474
  }
475
+
476
+ public function getOrderAction () {
477
+ $encryptionHelper = Mage::helper('highstreet_hsapi/encryption');
478
+ if (!$encryptionHelper->APISignatureStringIsValid()) {
479
+ $this->_respondWith401();
480
+ return;
481
+ }
482
+
483
+ $requestObject = Mage::app()->getRequest();
484
+
485
+ $quoteIds = array();
486
+ if ($requestObject->getParam('quote_id') != null) {
487
+ $quoteIds[] = $requestObject->getParam('quote_id');
488
+ }
489
+
490
+ if ($requestObject->getParam('quote_ids') != null) {
491
+ $quoteIds = array_merge($quoteIds, explode(',', $requestObject->getParam('quote_ids')));
492
+ }
493
+
494
+ $checkoutModel = Mage::getModel('highstreet_hsapi/checkoutV2');
495
+
496
+ $response = array();
497
+ foreach ($quoteIds as $quoteId) {
498
+ $quote = Mage::getModel('sales/quote')->loadByIdWithoutStore($quoteId);
499
+ $order = Mage::getModel('sales/order')->loadByIncrementId($quote->getData('reserved_order_id'));
500
+
501
+ $response[] = $checkoutModel->getOrderInformationFromOrderObject($order, $quoteId);
502
+ }
503
+
504
+ $this->_JSONencodeAndRespond($response);
505
+ }
506
+
507
  /**
508
  * Header and http functions
509
  */
510
+ private function _respondWith304()
511
+ {
512
+ $this->getResponse()->setHeader('HTTP/1.1','304 Not Modified');
513
+ $this->getResponse()->sendHeaders();
514
+ return;
515
+ }
516
+
517
  private function _respondWith404()
518
  {
519
  $this->getResponse()->setHeader('HTTP/1.1','404 Not Found');
521
  return;
522
  }
523
 
524
+ private function _respondWith401()
525
+ {
526
+ $this->getResponse()->setHeader('HTTP/1.1','401 Unauthorized');
527
+ $this->getResponse()->sendHeaders();
528
+ return;
529
+ }
530
+
531
  /**
532
  * Sets the proper headers
533
  */
app/code/local/Highstreet/Hsapi/etc/.DS_Store DELETED
Binary file
app/code/local/Highstreet/Hsapi/etc/Configuration.json DELETED
@@ -1 +0,0 @@
1
- {}
 
app/code/local/Highstreet/Hsapi/etc/config.xml CHANGED
@@ -11,7 +11,7 @@
11
  <config>
12
  <modules>
13
  <Highstreet_Hsapi>
14
- <version>1.0.0</version>
15
  </Highstreet_Hsapi>
16
  </modules>
17
  <frontend>
@@ -33,13 +33,7 @@
33
  </Highstreet_Hsapi>
34
  </modules>
35
  </translate>
36
- <layout>
37
- <updates>
38
- <hsapi>
39
- <file>highstreet/checkout.xml</file>
40
- </hsapi>
41
- </updates>
42
- </layout>
43
  </frontend>
44
  <global>
45
  <models>
@@ -64,8 +58,8 @@
64
  </setup>
65
  </highstreet_hsapi_setup>
66
  </resources>
67
- <events>
68
- <sales_quote_merge_before>
69
  <observers>
70
  <highstreet_hsapi_merge_quote>
71
  <type>singleton</type>
@@ -73,7 +67,44 @@
73
  <method>mergeQuote</method>
74
  </highstreet_hsapi_merge_quote>
75
  </observers>
76
- </sales_quote_merge_before>
77
- </events>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  </global>
79
- </config>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  <config>
12
  <modules>
13
  <Highstreet_Hsapi>
14
+ <version>1.4.3</version>
15
  </Highstreet_Hsapi>
16
  </modules>
17
  <frontend>
33
  </Highstreet_Hsapi>
34
  </modules>
35
  </translate>
36
+
 
 
 
 
 
 
37
  </frontend>
38
  <global>
39
  <models>
58
  </setup>
59
  </highstreet_hsapi_setup>
60
  </resources>
61
+ <events>
62
+ <sales_quote_merge_before>
63
  <observers>
64
  <highstreet_hsapi_merge_quote>
65
  <type>singleton</type>
67
  <method>mergeQuote</method>
68
  </highstreet_hsapi_merge_quote>
69
  </observers>
70
+ </sales_quote_merge_before>
71
+ <sales_order_invoice_pay>
72
+ <observers>
73
+ <highstreet_hsapi_sales_order_invoice_pay>
74
+ <class>Highstreet_Hsapi_Model_Observer</class>
75
+ <method>salesOrderInvoicePay</method>
76
+ </highstreet_hsapi_sales_order_invoice_pay>
77
+ </observers>
78
+ </sales_order_invoice_pay>
79
+ <sales_order_invoice_cancel>
80
+ <observers>
81
+ <highstreet_hsapi_sales_order_invoice_cancel>
82
+ <class>Highstreet_Hsapi_Model_Observer</class>
83
+ <method>salesOrderInvoiceCancel</method>
84
+ </highstreet_hsapi_sales_order_invoice_cancel>
85
+ </observers>
86
+ </sales_order_invoice_cancel>
87
+ </events>
88
  </global>
89
+ <adminhtml>
90
+ <acl>
91
+ <resources>
92
+ <admin>
93
+ <children>
94
+ <system>
95
+ <children>
96
+ <config>
97
+ <children>
98
+ <highstreet_hsapi>
99
+ <title>Highstreet</title>
100
+ </highstreet_hsapi>
101
+ </children>
102
+ </config>
103
+ </children>
104
+ </system>
105
+ </children>
106
+ </admin>
107
+ </resources>
108
+ </acl>
109
+ </adminhtml>
110
+ </config>
app/code/local/Highstreet/Hsapi/etc/system.xml ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <sections>
4
+ <highstreet_hsapi module="highstreet_hsapi">
5
+ <label>Highstreet</label>
6
+ <class>highstreet-section</class>
7
+ <tab>service</tab>
8
+ <frontend_type>text</frontend_type>
9
+ <sort_order>1000</sort_order>
10
+ <show_in_default>1</show_in_default>
11
+ <show_in_website>1</show_in_website>
12
+ <show_in_store>1</show_in_store>
13
+ <groups>
14
+ <api>
15
+ <label>Configuration</label>
16
+ <frontend_type>text</frontend_type>
17
+ <sort_order>20</sort_order>
18
+ <show_in_default>1</show_in_default>
19
+ <show_in_website>1</show_in_website>
20
+ <show_in_store>1</show_in_store>
21
+ <fields>
22
+ <store_id>
23
+ <label>Store identifier</label>
24
+ <comment>Your store identifier, e.g. your brand name</comment>
25
+ <frontend_type>text</frontend_type>
26
+ <sort_order>10</sort_order>
27
+ <show_in_default>1</show_in_default>
28
+ <show_in_website>1</show_in_website>
29
+ <show_in_store>1</show_in_store>
30
+ </store_id>
31
+ <environment>
32
+ <label>Environment</label>
33
+ <comment></comment>
34
+ <frontend_type>select</frontend_type>
35
+ <source_model>highstreet_hsapi/system_config_environment</source_model>
36
+ <sort_order>20</sort_order>
37
+ <show_in_default>1</show_in_default>
38
+ <show_in_website>1</show_in_website>
39
+ <show_in_store>1</show_in_store>
40
+ </environment>
41
+ <shipping_in_cart translate="label">
42
+ <label>Hide shipping methods in cart API call</label>
43
+ <comment></comment>
44
+ <frontend_type>select</frontend_type>
45
+ <source_model>adminhtml/system_config_source_yesno</source_model>
46
+ <sort_order>30</sort_order>
47
+ <show_in_default>1</show_in_default>
48
+ <show_in_website>1</show_in_website>
49
+ <show_in_store>1</show_in_store>
50
+ </shipping_in_cart>
51
+ <always_add_simple_products translate="label">
52
+ <label>Always add simple products to the cart</label>
53
+ <comment></comment>
54
+ <frontend_type>select</frontend_type>
55
+ <source_model>adminhtml/system_config_source_yesno</source_model>
56
+ <sort_order>40</sort_order>
57
+ <show_in_default>1</show_in_default>
58
+ <show_in_website>1</show_in_website>
59
+ <show_in_store>1</show_in_store>
60
+ </always_add_simple_products>
61
+ <smartbanner_native_active translate="label">
62
+ <label>Enable the native Apple smart banner</label>
63
+ <comment></comment>
64
+ <frontend_type>select</frontend_type>
65
+ <source_model>adminhtml/system_config_source_yesno</source_model>
66
+ <sort_order>50</sort_order>
67
+ <show_in_default>1</show_in_default>
68
+ <show_in_website>1</show_in_website>
69
+ <show_in_store>1</show_in_store>
70
+ </smartbanner_native_active>
71
+ <smartbanner_native_app_id translate="label">
72
+ <label>App store id</label>
73
+ <comment></comment>
74
+ <frontend_type>text</frontend_type>
75
+ <sort_order>60</sort_order>
76
+ <show_in_default>1</show_in_default>
77
+ <show_in_website>1</show_in_website>
78
+ <show_in_store>1</show_in_store>
79
+ </smartbanner_native_app_id>
80
+ <smartbanner_native_app_url translate="label">
81
+ <label>Url scheme for universal links</label>
82
+ <comment></comment>
83
+ <frontend_type>text</frontend_type>
84
+ <sort_order>70</sort_order>
85
+ <show_in_default>1</show_in_default>
86
+ <show_in_website>1</show_in_website>
87
+ <show_in_store>1</show_in_store>
88
+ </smartbanner_native_app_url>
89
+ <smartbanner_native_app_name translate="label">
90
+ <label>App name used to display in iOS search results</label>
91
+ <comment></comment>
92
+ <frontend_type>text</frontend_type>
93
+ <sort_order>80</sort_order>
94
+ <show_in_default>1</show_in_default>
95
+ <show_in_website>1</show_in_website>
96
+ <show_in_store>1</show_in_store>
97
+ </smartbanner_native_app_name>
98
+ </fields>
99
+ </api>
100
+ </groups>
101
+ </highstreet_hsapi>
102
+ </sections>
103
+ </config>
app/code/local/Highstreet/SmartAppBanner/etc/config.xml ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+
3
+ <config>
4
+
5
+ <modules>
6
+
7
+ <Highstreet_SmartAppBanner>
8
+ <version>1.3.1</version>
9
+ </Highstreet_SmartAppBanner>
10
+
11
+ </modules>
12
+
13
+
14
+ <frontend>
15
+
16
+ <layout>
17
+ <updates>
18
+ <Highstreet_SmartAppBanner>
19
+ <file>highstreet/smart_app_banner.xml</file>
20
+ </Highstreet_SmartAppBanner>
21
+ </updates>
22
+ </layout>
23
+
24
+ </frontend>
25
+
26
+ </config>
app/design/frontend/base/default/layout/highstreet/smart_app_banner.xml ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+
3
+ <layout version="1.2.0">
4
+ <default>
5
+ <reference name="head">
6
+ <block type="core/template" name="native_smart_app_banner" template="highstreet/native_smart_app_banner.phtml" />
7
+ </reference>
8
+ <reference name="before_body_end">
9
+ <block type="page/html_header" name="smart_app_banner" template="highstreet/smart_app_banner.phtml" />
10
+ </reference>
11
+ </default>
12
+ </layout>
app/design/frontend/base/default/template/highstreet/native_smart_app_banner.phtml ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $configHelper = Mage::helper('highstreet_hsapi/config_api');
4
+
5
+ if ($configHelper->nativeSmartbannerAppUrl() != "") {
6
+ $deeplink = $configHelper->nativeSmartbannerAppUrl() . "://";
7
+
8
+ $category = Mage::registry('current_category');
9
+ $deeplink .= $category ? "categories/" . $category->getId() . "/" : "";
10
+
11
+ $product = Mage::registry('current_product');
12
+ $deeplink .= $product ? "products/" . $product->getId() . "/" : "";
13
+
14
+ $searchQuery = Mage::app()->getRequest()->getParam('q');
15
+ $deeplink .= $searchQuery ? "search/" . $searchQuery . "/" : "";
16
+
17
+ if ($configHelper->shouldShowNativeSmartbanner()) {
18
+ $metaTag = '<meta name="apple-itunes-app" content="app-id=' . $configHelper->nativeSmartbannerAppId();
19
+ $metaTag .= ', app-argument=' . $deeplink;
20
+ $metaTag .= '"/>';
21
+
22
+ echo $metaTag;
23
+ }
24
+
25
+ echo '<meta property="al:ios:app_name" content="' . $configHelper->nativeSmartbannerAppName() . '">';
26
+ echo '<meta property="al:ios:app_store_id" content="' . $configHelper->nativeSmartbannerAppId() . '">';
27
+ echo '<meta property="al:ios:url" content="' . $deeplink . '">';
28
+ }
app/design/frontend/base/default/template/highstreet/smart_app_banner.phtml ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Template for smart-app-banner
4
+ */
5
+
6
+ $website = Mage::app()->getWebsite()->getCode();
7
+
8
+ $configHelper = Mage::helper('highstreet_hsapi/config_api');
9
+
10
+ $id = $configHelper->storeIdentifier();
11
+
12
+ if ($id == "") { // No id set == no smartbanner
13
+ return;
14
+ }
15
+
16
+ // Request config
17
+ $config = new stdClass();
18
+
19
+ $config->store = Mage::app()->getStore()->getCode();
20
+ $config->host = $_SERVER['HTTP_HOST'];
21
+
22
+ $request = $this->getRequest();
23
+ $config->module = $request ? strtolower($request->getModuleName()) : '';
24
+ $config->controller = $request ? strtolower($request->getControllerName()) : '';
25
+
26
+ $config->locale = Mage::app()->getLocale()->getLocaleCode();
27
+
28
+
29
+
30
+ // Content config
31
+ $content = new stdClass();
32
+
33
+ $category = Mage::registry('current_category');
34
+ $content->category = $category ? $category->getId() : null;
35
+
36
+ $product = Mage::registry('current_product');
37
+ $content->product = $product ? $product->getId() : null;
38
+
39
+ $content->query = Mage::app()->getRequest()->getParam('q', null);
40
+
41
+ $content->version = Mage::getConfig()->getModuleConfig('Highstreet_SmartAppBanner')->version;
42
+
43
+
44
+ // Don't show on checkout
45
+ $module_blacklist = array( 'checkout', 'onestepcheckout', 'hsapi' );
46
+ $controller_blacklist = array( 'checkout_cart' );
47
+
48
+ if ( in_array($config->module, $module_blacklist)
49
+ || in_array($config->controller, $controller_blacklist) ) {
50
+
51
+ return;
52
+
53
+ }
54
+
55
+ // Construct javascript url
56
+ $configHelper = Mage::helper('highstreet_hsapi/config_api');
57
+ $middleWareUrl = $configHelper->middlewareUrl();
58
+ $base_url = $middleWareUrl . "/smartbanner";
59
+ $key = md5('dY63*23xOi'. $configHelper->storeIdentifier());
60
+ $params = base64_url_encode(encrypt(json_encode($config), $key));
61
+
62
+ $js_url = "${base_url}/${params}";
63
+
64
+ /**
65
+ * Returns an encrypted & utf8-encoded string
66
+ */
67
+ function encrypt($pure_string, $encryption_key) {
68
+ $iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB);
69
+ $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
70
+ $encrypted_string = mcrypt_encrypt(MCRYPT_BLOWFISH, $encryption_key, utf8_encode($pure_string), MCRYPT_MODE_ECB, $iv);
71
+ return trim($encrypted_string);
72
+ }
73
+
74
+ /**
75
+ * Returns base64 url-safe encoded string
76
+ */
77
+ function base64_url_encode($input) {
78
+ return strtr(base64_encode($input), '+/=', '-_~');
79
+ }
80
+
81
+
82
+ ?>
83
+
84
+ <script type="text/javascript">
85
+ //<![CDATA[
86
+ var smartAppBannerContent = <?php echo json_encode($content); ?>;
87
+ //]]>
88
+ </script>
89
+
90
+ <script src="<?php echo $js_url; ?>" defer="true" ></script>
91
+
app/etc/modules/Highstreet_SmartAppBanner.xml ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <config>
3
+ <modules>
4
+ <Highstreet_SmartAppBanner>
5
+
6
+ <!-- Whether our module is active: true or false -->
7
+ <active>true</active>
8
+
9
+ <!-- Which code pool to use: core, community or local -->
10
+ <codePool>local</codePool>
11
+
12
+ </Highstreet_SmartAppBanner>
13
+ </modules>
14
+ </config>
app/locale/de_DE/Highstreet_Hsapi.csv ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "Welcome","Willkommen"
2
+ "Billing address","Adresse"
3
+ "Shipment","Versand"
4
+ "Payment","Zahlung"
5
+ "Confirmation","Bestätigung"
6
+ "Log out","Ausloggen"
7
+ "Confirm and pay","Bestätigen & bezahlen"
8
+ "Your order is not final yet","Ihre Bestellung ist noch nicht definitiv"
9
+ "You will continue to our secure payment page","Sie werden in unsere gesicherte Zahlungsumgebung weitergeleitet"
10
+ "Hello!","Hallo!"
11
+ "Checkout as guest","Als Gast bezahlen"
12
+ "Log in","Einloggen"
13
+ "hsapi_Register","Registrieren"
14
+ "Sign In","Anmelden"
15
+ "E-mail address","E-Mail-Adresse"
16
+ "ipad_Password","Passwort"
17
+ "Forgot your password?","Passwort vergessen?"
18
+ "Send","Senden"
19
+ "We have sent you an e-mail with which you can change your password.","Eine E-Mail, mit der Sie Ihr Passwort ändern können, wurde an die von Ihnen angegebene Adresse gesendet."
20
+ "Create an account now","Account erstellen"
21
+ "and checkout faster next time","und beim nächsten Mal noch einfacher bestellen"
22
+ "Please enter a valid e-mailaddress","Eine gültige E-Mailadresse eingeben"
23
+ "Please enter a valid password","Ein gültiges Passwort eingeben"
24
+ "Repeat the password","Passwort wiederholen"
25
+ "First Name","Vorname"
26
+ "Last Name","Nachname"
27
+ "Please enter a valid phone number.","Eine gültige Telefonnummer angeben"
28
+ "Phone number","Telefonnummer"
29
+ "We ask for your phone number so that we can call you about your order if need be. We will never use your telephone number for other purposes.","Wir bitten Sie um die Angabe Ihrer Telefonnummer, sodass wir Sie wegen Ihrer Bestellung anrufen können, falls nötig. Wir werden Ihre Telefonnummer niemals für andere Zwecke verwenden."
30
+ "Street","Straße"
31
+ "House number","Hausnummer"
32
+ "Zip code","PLZ"
33
+ "City","Ort"
34
+ "Something went wrong","Etwas ist schief gegangen"
35
+ "Please try again","Noch einmal versuchen"
36
+ "How do you want to pay for your order?","Wie möchten Sie Ihre Bestellung <br /> bezahlen?"
37
+ "Please check your order.","Ist Ihre Bestellung richtig?"
38
+ "Please check the order overview. If this is correct, you can place your order.","Hier sehen Sie eine Übersicht Ihrer Bestellung. Wenn alles richtig ist, können Sie die Bestellung absenden."
39
+ "How do you want to receive your order?","Wie möchten Sie Ihre Bestellung <br /> erhalten?"
40
+ "Invoice address / delivery address","Rechnungsadresse / Lieferadresse"
41
+ "Edit","Ändern"
42
+ "Delivery method","Versandoption"
43
+ "Payment method","Zahlungsweise"
44
+ "You can continue to the next step when you have entered all fields","Sie können erst zum nächsten Schritt gehen, wenn Sie alle Felder ausgefüllt haben"
45
+ "There has been an unknown error. Please try again later again later.","Ein unbekannter Fehler ist aufgetreten. Versuchen Sie es später noch einmal."
46
+ "Thanks for shopping at ","Schön, dass Sie bei "
47
+ ".<span class='hidden'>_ORDER_TEXT</span>"," bestellen."
48
+ "When you tap 'Confirm and pay', you agree with ","Wenn Sie auf ""Bestätigen und bezahlen"" klicken, akzeptieren Sie "
49
+ "the general terms","die allgemeinen Geschäftsbedingungen"
50
+ " of "," von "
51
+ "Ship to the same address","Bestellung an die gleiche Adresse senden"
52
+ "Invoice address","Rechnungsadresse"
53
+ "Delivery address","Lieferadresse"
54
+ "Please make sure your passwords match.","Die eingegebenen Passwörter stimmen nicht überein."
55
+
56
+
57
+ "hsapi.index.couponTip","Haben Sie einen Gutschein? <br /> Diesen können Sie später im Schritt <br /> ""Zahlung"" einlösen."
58
+
59
+ "hsapi.addCouponAction.error.fatal","Ein unbekannter Fehler ist aufgetreten. Versuchen Sie es später noch einmal."
60
+ "hsapi.addCouponAction.error.invalid","Der Gutschein, den Sie eingegeben haben, ist nicht gültig."
61
+ "hsapi.addCouponAction.error.length","Der Gutschein, den Sie eingegeben haben, ist zu lang, die maximale Länge beträgt "
62
+ "hsapi.addCouponAction.success","Gutschein '{coupon_code}' wurde erfolgreich eingelöst. Sie können Ihren Rabatt in der Bestellliste ansehen."
63
+ "hsapi.addCouponAction.success.removed","Gutschein '{coupon_code}' wurde erfolgreich gelöscht."
64
+
65
+ "hsapi.checkout.coupon.addTitle","<u> Gutschein </u> hinzufügen"
66
+ "hsapi.checkout.coupon.form.title","Geben Sie hier den Gutscheincode ein."
67
+ "hsapi.checkout.coupon.form.code","Gutschein"
68
+ "hsapi.checkout.coupon.form.add","Hinzufügen"
69
+ "hsapi.checkout.coupon.form.remove","Entfernen"
70
+ "hsapi.checkout.coupon.paymentTitle","Wählen Sie aus den folgenden Zahlungsmöglichkeiten"
71
+
72
+ "There is already a customer registered using this email address. Please login using this email address or enter a different email address to register your account.","Die angegebene E-Mailadresse ist schon an einen Account gekoppelt. Loggen Sie sich mit dieser Adresse ein oder verwenden Sie eine andere E-Mailadresse für die Registrierung."
package.xml CHANGED
@@ -1,19 +1,19 @@
1
  <?xml version="1.0"?>
2
  <package>
3
  <name>Highstreet</name>
4
- <version>1.0.1</version>
5
  <stability>stable</stability>
6
  <license>-</license>
7
  <channel>community</channel>
8
  <extends/>
9
- <summary>Highstreet Magento Extension. This extension allows the Highstreet iPad app to connect to the store. </summary>
10
  <description>The extension provides read-only access to the catalog (products and categories) by providing a simple API for the app. &#xD;
11
  This extension does not modify or affect the Magento installation, nor does it change any catalog data.</description>
12
- <notes>- Corrected an issue which caused the categories API to fail</notes>
13
  <authors><author><name>Christian Apers</name><user>Christian</user><email>Christian@Touchwonders.com</email></author><author><name>Tim Wachter</name><user>Tim</user><email>Tim@Touchwonders.com</email></author></authors>
14
- <date>2014-08-13</date>
15
- <time>07:40:08</time>
16
- <contents><target name="magecommunity"><dir name="Technooze"><dir name="Timage"><dir name="Block"><file name="Data.php" hash="02f8402df49abe64716e01746055330e"/></dir><dir name="Helper"><file name="Data.php" hash="4ade060d79bd29a91a524fce9e61e49b"/><file name="Data.php" hash="4ade060d79bd29a91a524fce9e61e49b"/></dir><dir name="etc"><file name="config.xml" hash="ff5675c31f16c03674ae87f19964a59d"/><file name="config.xml" hash="ff5675c31f16c03674ae87f19964a59d"/></dir></dir></dir></target><target name="magelocal"><dir name="Highstreet"><dir name="Hsapi"><dir name="Helper"><file name="Config.php" hash="2ec387f4de4f0869ffb73667db9cbab0"/><file name="Data.php" hash="94ba070797ec9925a331969b012be7f6"/></dir><dir name="Model"><file name="Attributes.php" hash="6ad75fe439bfc6993ff741b2ae452150"/><file name="Categories.php" hash="79c3135fe7d99bf8f2cc1776bf792cb0"/><file name="Checkout.php" hash="b6c4606464818bf9cb4cceb766105c2e"/><file name="Images.php" hash="32d65360440199910c05b1e087c1cee2"/><file name="Observer.php" hash="3797806da488c928c0cbc543ce98763c"/><file name="Products.php" hash="1255dcbc9a9cb6e968c69e8cec48f246"/><file name="SearchSuggestions.php" hash="17e0a008e2f90c0f5f6c5f7c3f010c9c"/></dir><dir name="controllers"><file name="IndexController.php" hash="79797b7b4fbc842a4c63dfaef03ac7f0"/><file name=".DS_Store" hash="194577a7e20bdcc7afbb718f502c134c"/></dir><dir name="etc"><file name="Configuration.json" hash="99914b932bd37a50b983c5e7c90ae93b"/><file name="config.xml" hash="b4f33b6ea6e8f24e7bacd85a48763cd4"/><file name=".DS_Store" hash="194577a7e20bdcc7afbb718f502c134c"/></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Highstreet_Api.xml" hash="97e586a7ef0a274d4f0fda883c646f31"/><file name="Technooze_Timage.xml" hash="bec7a12e3028d25b3a28c6eafe1878b4"/><file name="Highstreet_SmartAppBanner.xml" hash=""/></dir></target><target name="magelocale"><dir><dir name="en_US"><file name="Highstreet_Hsapi.csv" hash="0246609251f607c5f4119ba56db14c6e"/></dir><dir name="nl_NL"><file name="Highstreet_Hsapi.csv" hash="a2f582408d85509c3de30d26f090fa9a"/></dir></dir></target></contents>
17
  <compatible/>
18
  <dependencies><required><php><min>5.0.0.0</min><max>6.0.0.0</max></php><extension><name>gd</name><min>2.0</min><max>3.0</max></extension></required></dependencies>
19
  </package>
1
  <?xml version="1.0"?>
2
  <package>
3
  <name>Highstreet</name>
4
+ <version>1.1.0</version>
5
  <stability>stable</stability>
6
  <license>-</license>
7
  <channel>community</channel>
8
  <extends/>
9
+ <summary>Highstreet Magento Extension. This extension allows the Highstreet iPad and iPhone apps to connect to the store. </summary>
10
  <description>The extension provides read-only access to the catalog (products and categories) by providing a simple API for the app. &#xD;
11
  This extension does not modify or affect the Magento installation, nor does it change any catalog data.</description>
12
+ <notes>The new complete Highstreet extension</notes>
13
  <authors><author><name>Christian Apers</name><user>Christian</user><email>Christian@Touchwonders.com</email></author><author><name>Tim Wachter</name><user>Tim</user><email>Tim@Touchwonders.com</email></author></authors>
14
+ <date>2015-09-14</date>
15
+ <time>15:47:30</time>
16
+ <contents><target name="magecommunity"><dir name="Technooze"><dir name="Timage"><dir name="Block"><file name="Data.php" hash="02f8402df49abe64716e01746055330e"/></dir><dir name="Helper"><file name="Data.php" hash="4ade060d79bd29a91a524fce9e61e49b"/><file name="Data.php" hash="4ade060d79bd29a91a524fce9e61e49b"/></dir><dir name="etc"><file name="config.xml" hash="ff5675c31f16c03674ae87f19964a59d"/><file name="config.xml" hash="ff5675c31f16c03674ae87f19964a59d"/></dir></dir></dir></target><target name="magelocal"><dir name="Highstreet"><dir name="Hsapi"><dir name="Helper"><dir name="Config"><file name="Api.php" hash="04ab6b698c0f21c467f9d80f4d649f74"/></dir><file name="Data.php" hash="cec9e38fe3e3cf06d544c013e52e9cc8"/><file name="Encryption.php" hash="a482d2a12ac584966d9f066bb1f5a892"/></dir><dir name="Model"><file name="Attributes.php" hash="6ad75fe439bfc6993ff741b2ae452150"/><file name="Categories.php" hash="e7f051c672b32a3913f2b5fe60e74378"/><file name="Checkout.php" hash="00a2912a581a9fa4ea0e9b98177860ee"/><file name="CheckoutV2.php" hash="4a462bd5ca20cea19d0eecf0cc61817c"/><file name="Images.php" hash="b26334f765cfd1f5b59c3509901ea8b1"/><file name="Observer.php" hash="e787c2c519232a1c93182df212e505c8"/><file name="Products.php" hash="6c2720b3f7bc327127955434e42ff9eb"/><file name="SearchSuggestions.php" hash="17e0a008e2f90c0f5f6c5f7c3f010c9c"/><dir name="System"><dir name="Config"><file name="Environment.php" hash="5ef07d78cf67bc47be1a64e54ff643ec"/></dir></dir></dir><dir name="controllers"><file name="CheckoutController.php" hash="c967c0075dae43e230d3f8dbc858d0db"/><file name="IndexController.php" hash="c4c697e4645f49d9b92fb7193a79eea8"/></dir><dir name="etc"><file name="config.xml" hash="64eb9de832bf0ef2de232d2af51d51bd"/><file name="system.xml" hash="3481c560e663170a96df5a09a766d03e"/></dir></dir><dir name="SmartAppBanner"><dir name="etc"><file name="config.xml" hash="aff217b03485fdf69874f5f0975c1cfe"/></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Highstreet_Api.xml" hash="97e586a7ef0a274d4f0fda883c646f31"/><file name="Technooze_Timage.xml" hash="bec7a12e3028d25b3a28c6eafe1878b4"/><file name="Highstreet_SmartAppBanner.xml" hash="c739b442a3907d7d7414d0abfd0efaf2"/></dir></target><target name="magedesign"><dir name="frontend"><dir name="base"><dir name="default"><dir name="layout"><dir name="highstreet"><file name="smart_app_banner.xml" hash="ed1ad1737439e221c8ca1e0daa0ccd1b"/></dir></dir><dir name="template"><dir name="highstreet"><file name="native_smart_app_banner.phtml" hash="915e6d1d2fbe97baea707c5be21d4cfe"/><file name="smart_app_banner.phtml" hash="0faa81b53d95a57583e1c1747ac9cd96"/></dir></dir></dir></dir></dir></target><target name="magelocale"><dir><dir name="de_DE"><file name="Highstreet_Hsapi.csv" hash="bd1c428e4da644cf5a39d3493ad142e4"/></dir><dir name="en_US"><file name="Highstreet_Hsapi.csv" hash="0246609251f607c5f4119ba56db14c6e"/></dir><dir name="nl_NL"><file name="Highstreet_Hsapi.csv" hash="a2f582408d85509c3de30d26f090fa9a"/></dir></dir></target></contents>
17
  <compatible/>
18
  <dependencies><required><php><min>5.0.0.0</min><max>6.0.0.0</max></php><extension><name>gd</name><min>2.0</min><max>3.0</max></extension></required></dependencies>
19
  </package>