Highstreet - Version 1.0.0

Version Notes

- First public release of the Highstreet Magento extension

Download this release

Release Info

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


Version 1.0.0

app/code/community/Technooze/Timage/Block/Data.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Technooze/Modules/magento-how-tos
4
+ * @package Technooze_Timage
5
+ * @author Damodar Bashyal
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+ class Technooze_Timage_Block_Data extends Mage_Core_Block_Template
9
+ {
10
+ }
app/code/community/Technooze/Timage/Helper/Data.php ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @category Technooze/Modules/magento-how-tos
4
+ * @package Technooze_Timage
5
+ * @author Damodar Bashyal
6
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
7
+ */
8
+ class Technooze_Timage_Helper_Data extends Mage_Core_Helper_Abstract
9
+ {
10
+ var
11
+ $width = null,
12
+ $height = null,
13
+ $rawImg = '',
14
+ $img = false,
15
+ $cacheDir = '',
16
+ $cachedImage = '',
17
+ $cachedImageUrl = '',
18
+ $ext = '',
19
+ $bgColor = array(255, 255, 255),
20
+ $imageObj = '',
21
+ $baseUrl = '',
22
+ $placeHolder = false;
23
+
24
+ public function init($img=false)
25
+ {
26
+ if($img)
27
+ {
28
+ $this->rawImg = $img;
29
+ }
30
+
31
+ if(empty($this->placeHolder))
32
+ {
33
+ $this->placeHolder = Mage::getDesign()->getSkinUrl('images/catalog/product/placeholder/image.jpg');
34
+ }
35
+ $this->imagePath($this->rawImg);
36
+
37
+ $this->imageObj = new Varien_Image($this->img, Varien_Image_Adapter::ADAPTER_GD2);
38
+
39
+ $path_parts = pathinfo($this->img);
40
+
41
+ $this->ext = $path_parts['extension'];
42
+
43
+ $this->cacheDir();
44
+
45
+ return $this;
46
+ }
47
+
48
+ public function resize($width=false, $height=false)
49
+ {
50
+ if($width)
51
+ {
52
+ $this->width = $width;
53
+ }
54
+
55
+ if($height)
56
+ {
57
+ $this->height = $height;
58
+ }
59
+
60
+ $this->cacheIt();
61
+
62
+ return $this->cachedImageUrl();
63
+ }
64
+
65
+ public function cachedImageUrl()
66
+ {
67
+ $img = str_replace(BP, '', $this->cachedImage);
68
+ $img = trim(str_replace('\\', '/', $img), '/');
69
+
70
+ return $this->baseUrl . $img;
71
+ }
72
+
73
+ public function cacheIt()
74
+ {
75
+ $this->cachedImage = $this->cacheDir . md5($this->img . $this->width . $this->height) . '.' .$this->ext;
76
+
77
+ if(file_exists($this->cachedImage))
78
+ {
79
+ return $this->cachedImage;
80
+ }
81
+
82
+ $this->resizer();
83
+ }
84
+
85
+ public function resizer()
86
+ {
87
+ try{
88
+ $this->imageObj->constrainOnly(true);
89
+ $this->imageObj->keepAspectRatio(true);
90
+ $this->imageObj->keepFrame(false);
91
+ $this->imageObj->keepTransparency(true);
92
+ $this->imageObj->backgroundColor($this->bgColor);
93
+ $this->imageObj->resize($this->width, $this->height);
94
+ $this->imageObj->save($this->cachedImage);
95
+ } catch(Exception $e){
96
+ return $e->getMessage();
97
+ }
98
+ }
99
+
100
+ public function imagePath($img='')
101
+ {
102
+ $this->baseUrl = str_replace('index.php/', '', Mage::getBaseUrl());
103
+ $img = str_replace($this->baseUrl, '', $img);
104
+ $img = trim(str_replace('/', DS, $img), DS);
105
+
106
+ $this->img = BP . DS . $img;
107
+
108
+ if((!file_exists($this->img) || !is_file($this->img)) && !empty($this->placeHolder))
109
+ {
110
+ $this->imagePath($this->placeHolder);
111
+ $this->placeHolder = false;
112
+ }
113
+ }
114
+
115
+ public function cacheDir()
116
+ {
117
+ $cache = BP . DS . 'media' . DS . 'catalog' . DS . 'cache' . DS;
118
+
119
+ if(!is_dir($cache))
120
+ {
121
+ mkdir($cache);
122
+ }
123
+ $this->cacheDir = $cache;
124
+ }
125
+
126
+ public function getOriginalSize() {
127
+
128
+ $width = $this->imageObj->getOriginalWidth();
129
+ $height = $this->imageObj->getOriginalHeight();
130
+
131
+ return array('width' => $width, 'height' => $height);
132
+
133
+ }
134
+ }
app/code/community/Technooze/Timage/etc/config.xml ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <!--
3
+ /**
4
+ * @category Technooze/Modules/magento-how-tos
5
+ * @package Technooze_Timage
6
+ * @author Damodar Bashyal
7
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
8
+ */
9
+ -->
10
+ <config>
11
+ <modules>
12
+ <Technooze_Timage>
13
+ <version>0.1.0</version>
14
+ </Technooze_Timage>
15
+ </modules>
16
+ <frontend>
17
+ <routers>
18
+ <timage>
19
+ <use>standard</use>
20
+ <args>
21
+ <module>Technooze_Timage</module>
22
+ <frontName>timage</frontName>
23
+ </args>
24
+ </timage>
25
+ </routers>
26
+ <layout>
27
+ <updates>
28
+ <timage>
29
+ <file>timage.xml</file>
30
+ </timage>
31
+ </updates>
32
+ </layout>
33
+ </frontend>
34
+ <global>
35
+ <helpers>
36
+ <timage>
37
+ <class>Technooze_Timage_Helper</class>
38
+ </timage>
39
+ </helpers>
40
+ <blocks>
41
+ <timage>
42
+ <class>Technooze_Timage_Block</class>
43
+ </timage>
44
+ </blocks>
45
+ </global>
46
+ </config>
app/code/local/Highstreet/.DS_Store ADDED
Binary file
app/code/local/Highstreet/Hsapi/.DS_Store ADDED
Binary file
app/code/local/Highstreet/Hsapi/Helper/.DS_Store ADDED
Binary file
app/code/local/Highstreet/Hsapi/Helper/Data.php ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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_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
17
+ */
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;
26
+ }
27
+
28
+ /**
29
+ * Check if request has a param && if param is an array bigger then 0
30
+ * @param $params
31
+ * @return bool
32
+ */
33
+ public function requestHasParams($params)
34
+ {
35
+ if(is_array($params) && count($params) > 0)
36
+ {
37
+ return true;
38
+ }
39
+ return false;
40
+ }
41
+
42
+
43
+ /**
44
+ * Returns the header for an image content type. Via the given source string param given it determines what the content type should be.
45
+ *
46
+ * @param string src url of the image
47
+ * @return string of the image type
48
+ */
49
+ public function imageHeaderStringForImage($src = false) {
50
+ if (!$src) {
51
+ return 'image';
52
+ }
53
+
54
+ $urlComponents = explode(".", $src);
55
+ $extension = strtolower($urlComponents[count($urlComponents)-1]);
56
+
57
+ if ($extension == 'jpeg' || $extension == 'jpg') {
58
+ return 'image/jpeg';
59
+ } else if ($extension == 'png') {
60
+ return 'image/png';
61
+ } else if ($extension == 'gif') {
62
+ return 'image/gif';
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Saves a search query to the search suggestion table
68
+ *
69
+ * @param string searchString, the search query string
70
+ * @param integer categoryId, the categoryId. When not filled in it will use the root category id
71
+ */
72
+ public function saveSearchSuggestion($searchString = false, $categoryId) {
73
+ if (!$searchString) {
74
+ return;
75
+ }
76
+
77
+ $catalogSearchSaveModel = Mage::getModel('catalogsearch/query');
78
+ try {
79
+ $catalogSearchSaveModel->setStoreId(Mage::app()->getStore()->getId());
80
+ $catalogSearchSaveModel->loadByQueryText($searchString);
81
+
82
+ $searchTermId = $catalogSearchSaveModel->getId();
83
+ if ($searchTermId) { // Search term already existed, update it
84
+ $popularity = $catalogSearchSaveModel->getData("popularity");
85
+ $catalogSearchSaveModel->setData("popularity", $popularity+1);
86
+
87
+ $catalogSearchSaveModel->save();
88
+ } else { // Search term did not yet exists, make a new one
89
+
90
+ // Check if there is an category ID set
91
+ if (empty($categoryId) || !is_numeric($categoryId)) {
92
+ $store = Mage::getModel('core/store')->load(Mage_Core_Model_App::DISTRO_STORE_ID);
93
+ $categoryId = $store->getRootCategoryId();
94
+ }
95
+
96
+ $data = array("query_text" => $searchString, "popularity" => 1);
97
+
98
+ if ($this->searchSuggestionTableHasHSAPIColumn()) {
99
+ $data["hsapi_category_id"] = $categoryId;
100
+ }
101
+
102
+ $catalogSearchSaveModel->addData($data);
103
+ $catalogSearchSaveModel->setIsProcessed(1);
104
+ $catalogSearchSaveModel->save();
105
+ }
106
+
107
+ } catch (Mage_Core_Exception $e) {
108
+ $this->_getSession()->addError($e->getMessage());
109
+ } catch (Exception $e) {
110
+ $this->_getSession()->addException($e,
111
+ Mage::helper('catalog')->__('An error occurred while saving the search query.')
112
+ );
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Returns a BOOL wether the "catalogsearch/query" table has the HSAPI category column
118
+ *
119
+ * @return bool
120
+ */
121
+ public function searchSuggestionTableHasHSAPIColumn() {
122
+ try {
123
+ $catalogSearchReadModel = Mage::getModel('catalogsearch/query')->getCollection();
124
+ $catalogSearchReadModel->addFieldToSelect('hsapi_category_id');
125
+ $catalogSearchReadModel->getSelect()->limit(1);
126
+ $catalogSearchReadModel->getData();
127
+ return TRUE;
128
+ } catch (Exception $e) {
129
+ return FALSE;
130
+ }
131
+
132
+ return FALSE;
133
+ }
134
+
135
+ }
136
+
137
+
138
+
app/code/local/Highstreet/Hsapi/Model/.DS_Store ADDED
Binary file
app/code/local/Highstreet/Hsapi/Model/Attributes.php ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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_Model_Attributes extends Mage_Core_Model_Abstract
11
+ {
12
+ /**
13
+ * Product entity type id
14
+ * We need to have it as a variable!
15
+ * As of mage_1.8.0 we have a different typeId
16
+ *
17
+ * @var int
18
+ */
19
+ protected $_entityTypeId;
20
+
21
+ /**
22
+ * Class constructor
23
+ * We are setting the entityTypeId on construct. In case we want
24
+ * attributes for other entityTypes in the future we have it prepared.
25
+ */
26
+ public function __construct()
27
+ {
28
+ $this->_entityTypeId = Mage::getModel('eav/entity')->setType('catalog_product')->getTypeId();
29
+ }
30
+
31
+
32
+ /**
33
+ * @return Mage_Catalog_Model_Product
34
+ */
35
+ public function getAttributes()
36
+ {
37
+ $response = $this->_extractResponse($this->_getAttributes());
38
+ return $response;
39
+ }
40
+
41
+
42
+ /**
43
+ * Return single attribute if found. else false
44
+ * @param null $code
45
+ * @return bool
46
+ */
47
+ public function getAttribute($code=null)
48
+ {
49
+ if(null != $code){
50
+ $response = $this->_extractResponse($this->_getAttribute($code));
51
+ return $response['attributes'][0];
52
+ }
53
+ return false;
54
+ }
55
+
56
+
57
+ /**
58
+ * Load attribute collection object
59
+ * @return Mage_Eav_Model_Resource_Entity_Attribute_Collection
60
+ */
61
+ private function _getAttributes()
62
+ {
63
+ $attributes = Mage::getResourceModel('eav/entity_attribute_collection')
64
+ ->setEntityTypeFilter($this->_entityTypeId)
65
+ ->addStoreLabel(Mage::app()->getStore()->getId())
66
+ ->addSetInfo(false) //no set data needed
67
+ ->getData();
68
+
69
+ return $attributes;
70
+ }
71
+
72
+
73
+ /**
74
+ * load single attribute by code
75
+ * @param null $code
76
+ * @return Mage_Eav_Model_Entity_Attribute
77
+ */
78
+ private function _getAttribute($code=null)
79
+ {
80
+ $attributes = Mage::getResourceModel('eav/entity_attribute_collection')
81
+ ->setEntityTypeFilter($this->_entityTypeId)
82
+ ->addStoreLabel(Mage::app()->getStore()->getId())
83
+ ->setCodeFilter($code)
84
+ ->addSetInfo(false) //no set data needed
85
+ ->getData();
86
+ return $attributes;
87
+ }
88
+
89
+
90
+ /**
91
+ * Extract the correct formatted array from the attribute data
92
+ * @param $attributes
93
+ * @return array|bool
94
+ */
95
+ private function _extractResponse($attributes)
96
+ {
97
+
98
+ if(!is_array($attributes)){
99
+ //throw Mage::exception('', '');
100
+ return false;
101
+ }
102
+
103
+ $response = array('attributes' => array());
104
+ foreach($attributes as $attribute)
105
+ {
106
+ $result = array();
107
+ $result['id'] = (int)$attribute['attribute_id'];
108
+ $result['code'] = $attribute['attribute_code'];
109
+ $result['title'] = strval($attribute['store_label']);
110
+ $result['type'] = $attribute['frontend_input'];
111
+
112
+ //Get the optionValues for this attribute
113
+ $result['options'] = $this->_getAttributeOptionValues(
114
+ $attribute['attribute_id'],
115
+ $attribute['default_value']
116
+ );
117
+
118
+ //we need to push to respond as a json array without index
119
+ array_push($response['attributes'], $result);
120
+ }
121
+ return $response;
122
+ }
123
+
124
+
125
+ /**
126
+ * Get the option values for the attribute
127
+ * @param null $attributeId
128
+ * @param int $defaultValue
129
+ *
130
+ * @return array
131
+ */
132
+ private function _getAttributeOptionValues($attributeId=null, $defaultValue = null)
133
+ {
134
+ $optionValues = array();
135
+ //We need to load the attribute to be able to use it get the options
136
+ $attribute = Mage::getModel('eav/entity_attribute')->load($attributeId);
137
+ $options = Mage::getModel('eav/entity_attribute_source_table')
138
+ ->setAttribute($attribute)
139
+ ->getAllOptions(false, false); //getAllOptions($withEmpty = true, $defaultValues = false)
140
+
141
+ foreach($options as $key => $option)
142
+ {
143
+ $opt = array();
144
+ $opt['title'] = strval($option['label']);
145
+ $opt['value'] = (int)$option['value'];
146
+ $opt['sort_hint'] = (int)$key;
147
+ $opt['is_default'] = (int)($option['value'] == $defaultValue) ? 1 : 0;
148
+
149
+ $optionValues[] = (object)$opt;
150
+ }
151
+ return $optionValues;
152
+ }
153
+
154
+
155
+ }
app/code/local/Highstreet/Hsapi/Model/Categories.php ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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_Model_Categories extends Mage_Core_Model_Abstract
11
+ {
12
+ const CATEGORY_MEDIA_PATH = "/media/catalog/category/";
13
+
14
+ /**
15
+ * Get category and children from id
16
+ * @param $categoryId
17
+ *
18
+ * @return bool
19
+ */
20
+ public function getCategories($categoryId)
21
+ {
22
+ $categoryObject = Mage::getModel('catalog/category')->load($categoryId);
23
+
24
+ $productCollection = $categoryObject->getProductCollection()
25
+ ->addAttributeToFilter('visibility', array('in' => array(
26
+ Mage_Catalog_Model_Product_Visibility::VISIBILITY_IN_CATALOG,
27
+ Mage_Catalog_Model_Product_Visibility::VISIBILITY_BOTH))
28
+ );
29
+
30
+ $name = $categoryObject->getData('name');
31
+ if (!empty($name)) {
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();
38
+ } else {
39
+ $imageUrl = '';
40
+ }
41
+ $category['image'] = $imageUrl;
42
+
43
+ $category['product_count'] = $productCollection->count();
44
+
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
+ }
74
+
75
+ return $category;
76
+ }
77
+ else {
78
+ return false;
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Gets the entire category tree. Can be filtered for a specific category with param categoryId
84
+ *
85
+ * @param integer categoryId, a categoryId which will filter the tree
86
+ * @return array Array of categories
87
+ */
88
+ public function getCategoryTree($categoryId = null) {
89
+ if ($categoryId == null) {
90
+ $categoryId = Mage::app()->getStore()->getRootCategoryId();
91
+ }
92
+
93
+ $categoryObject = Mage::getModel('catalog/category')->load($categoryId);
94
+ $children = $this->getChildrenCollectionForCategoryId($categoryId);
95
+
96
+ $productCollection = $categoryObject->getProductCollection()
97
+ ->addAttributeToFilter('visibility', array('in' => array(
98
+ Mage_Catalog_Model_Product_Visibility::VISIBILITY_IN_CATALOG,
99
+ Mage_Catalog_Model_Product_Visibility::VISIBILITY_BOTH))
100
+ );
101
+
102
+ $name = $categoryObject->getData('name');
103
+ if (!empty($name)) {
104
+ $category = array();
105
+ $category['id'] = $categoryId;
106
+ $category['title'] = $name;
107
+ $category['position'] = $categoryObject->getData('position');
108
+
109
+ if ($categoryObject->getImage()) {
110
+ $imageUrl = self::CATEGORY_MEDIA_PATH . $categoryObject->getImage();
111
+ } else {
112
+ $imageUrl = '';
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
+
134
+ if (is_array($childRepresentation)) {
135
+ array_push($category['children'], $childRepresentation);
136
+ }
137
+ }
138
+ }
139
+
140
+ return $category;
141
+ }
142
+ else {
143
+ return false;
144
+ }
145
+ }
146
+
147
+
148
+ /**
149
+ * Returns a category collection of children from the given category id
150
+ *
151
+ * @param integer categoryId, parent category ID for which children need to be get
152
+ * @return Mage_Catalog_Model_Resource_Category_Collection Category Collection
153
+ */
154
+ private function getChildrenCollectionForCategoryId($categoryId = null) {
155
+ if ($categoryId === null) {
156
+ return null;
157
+ }
158
+
159
+
160
+ $children = Mage::getModel('catalog/category')->getCollection()->setStoreId(Mage::app()->getStore()->getId());
161
+ $children->addAttributeToSelect(array('entity_id', 'name', 'image', 'level', 'include_in_menu','position')) // Only get nescecary attributes from the table
162
+ ->addAttributeToFilter('parent_id', $categoryId)
163
+ ->addAttributeToSort('position')
164
+ ->addAttributeToFilter('is_active', 1);
165
+
166
+
167
+ return $children;
168
+ }
169
+ }
app/code/local/Highstreet/Hsapi/Model/Checkout.php ADDED
@@ -0,0 +1,440 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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_Checkout extends Mage_Core_Model_Abstract
10
+ {
11
+ /**
12
+ * 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.
13
+ * The array should have the following format:
14
+ * {"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}]}
15
+ * With this format we will lateron be able to extend it for configurable products
16
+ *
17
+ * @param array An array of product SKU's to fill the cart
18
+ */
19
+ public function fillCartWithProductsAndQuantities($products = false) {
20
+ if (!$products) {
21
+ return;
22
+ }
23
+
24
+
25
+ $cart = Mage::getModel('checkout/cart');
26
+ $cart->init();
27
+ $cart->truncate(); // Reset cart everytime this function is called
28
+
29
+ foreach ($products as $key => $value) {
30
+ if (empty($value["id"])) {
31
+ continue;
32
+ }
33
+
34
+ try {
35
+ $product = $this->loadProduct($value["id"]);
36
+ } catch (Exception $e) {
37
+ continue;
38
+ }
39
+
40
+
41
+ if (empty($value["quantity"]) || !is_numeric($value["quantity"]) || $value["quantity"] === 0) {
42
+ continue; //skip this product
43
+ }
44
+
45
+ $quantity = $value["quantity"];
46
+
47
+ try {
48
+
49
+ $parent = $this->_getParentProduct($product);
50
+ if($parent) {
51
+ $configurations = $this->_getConfiguration($product,$parent);
52
+ $configurations = array('super_attribute' => $configurations);
53
+ $options = array_merge(array("qty" => $quantity),$configurations);
54
+ $cart->addProduct($parent,$options);
55
+ } else {
56
+ $cart->addProduct($product, array("qty" => $quantity));
57
+ }
58
+
59
+
60
+
61
+
62
+
63
+
64
+
65
+ } catch (Exception $e) {
66
+ continue;
67
+ }
68
+ }
69
+
70
+ $cart->save();
71
+ Mage::getSingleton('checkout/session')->setCartWasUpdated(true);
72
+ }
73
+
74
+
75
+ /**
76
+ * Can retrieve an existing quote, or create a new (temporary) quote with the given objects
77
+ * Purpose of this method is to return all products that exist in the cart, all shipping information and the totals
78
+ *
79
+ * @param array An array of product SKU's to fill the cart. Format identical to fillCartWithProductsAndQuantities
80
+ * @param quote_id (optional) The quote_id for which you would like to return the information
81
+ */
82
+ public function getQuoteWithProductsAndQuantities($products = false, $quote_id = -1) {
83
+ if ($products === false && $quote_id == -1) {
84
+ return;
85
+ }
86
+
87
+ $response = array();
88
+
89
+ Mage::getSingleton('checkout/session')->setQuoteId(null);
90
+
91
+
92
+ $quote = null;
93
+
94
+ if($quote_id == -1) {
95
+ $cart = Mage::getModel('checkout/cart');
96
+ $cart->init();
97
+ $cart->truncate(); // Reset cart everytime this function is called
98
+ $quote = $cart->getQuote();
99
+ } else {
100
+ $quote = Mage::getModel('sales/quote')->load($quote_id);
101
+ if(!$quote->getId()) {
102
+ return null;
103
+ }
104
+ }
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
+
117
+
118
+
119
+ }
120
+
121
+
122
+ //Helpers below
123
+
124
+ private function getProductsInQuote($quote,$products = null) {
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
137
+ foreach ($products as $key => $value) {
138
+ if (empty($value["id"])) {
139
+ continue;
140
+ }
141
+
142
+ try {
143
+ $product = $this->loadProduct($value["id"]);
144
+ } catch (Exception $e) {
145
+ $productInQuote["errorMessage"] = $e->getMessage();
146
+ $productInQuote["errorCode"] = 400; //something else went wrong
147
+ $productInQuote["quantity"] = 0;
148
+ }
149
+
150
+ $parent = $this->_getParentProduct($product);
151
+ if($parent)
152
+ $configurations = $this->_getConfiguration($product,$parent);
153
+
154
+
155
+
156
+ $requestedQuantity = $value["quantity"];
157
+
158
+
159
+
160
+
161
+ //Set up default response
162
+ $productInQuote = array();
163
+ $productInQuote["product_id"] = $value["id"];
164
+ $productInQuote["errorCode"] = 0;
165
+ $productInQuote["errorMessage"] = null;
166
+
167
+
168
+
169
+ try {
170
+
171
+ if (!$product) {
172
+ //product does not exist
173
+
174
+ $productInQuote["errorCode"] = 400;
175
+ $productInQuote["errorMessage"] = "The requested product does not exist";
176
+ $productInQuote["quantity"] = 0;
177
+
178
+
179
+ } else {
180
+
181
+
182
+ $itemInventory = Mage::getModel('cataloginventory/stock_item')->loadByProduct($product);
183
+ $quoteItem = $quote->getItemByProduct($product);
184
+
185
+
186
+ $actualQuantity = $requestedQuantity; //actual qty is what we are going to add
187
+
188
+ //adjust actual quantity if we are requesting more than in stock
189
+ $availableQuantity = $itemInventory->getQty();
190
+ $isInStock = $itemInventory->getIsInStock();
191
+ $isStockManaged = $itemInventory->getManageStock();
192
+ $backordersAllowed = $itemInventory->getBackorders();
193
+
194
+
195
+ if($isStockManaged) {
196
+
197
+ if(!$isInStock) {
198
+ $productInQuote["errorMessage"] = "Product is not in stock";
199
+ $productInQuote["errorCode"] = 101; //product cannot be added
200
+ $actualQuantity = 0;
201
+ } else {
202
+ //in stock, but should we cap it?
203
+ if(!$backordersAllowed && $requestedQuantity > $availableQuantity) {
204
+ $actualQuantity = $availableQuantity; //cap
205
+ $productInQuote["errorMessage"] = "Requested quantity is not available, added ".(int)$actualQuantity." instead of ".$requestedQuantity ." products with id ".$value["id"]." to the cart";
206
+ $productInQuote["errorCode"] = 102; //product can be added, but with a lower quantity
207
+ //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
208
+ //"Qty for Item's Status to Become Out of Stock" might be a negative integer
209
+ }
210
+
211
+ }
212
+
213
+ }
214
+
215
+ if($quoteItem) { //adjust existing entry
216
+ $quoteItem->setQty($actualQuantity);
217
+ } else { //or add new entry (but of course only when qty > 0)
218
+ 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
219
+ if($parent) {
220
+ $configurations = array('super_attribute' => $configurations);
221
+ $options = array_merge(array("qty" => $actualQuantity),$configurations);
222
+
223
+
224
+ $quoteItem = $quote->addProduct($parent,new Varien_Object($options));
225
+ } else {
226
+ $quoteItem = $quote->addProduct($product,new Varien_Object(array("qty" => $actualQuantity)));
227
+ }
228
+
229
+ //response output
230
+ $productInQuote = array_merge($this->getProductInQuoteResponse($quoteItem,$product),$productInQuote);
231
+ } else {
232
+ $productInQuote["quantity"] = 0;
233
+ }
234
+ }
235
+
236
+
237
+ if($quoteItem)
238
+ unset($quoteItems[$quoteItem->getId()]);
239
+
240
+
241
+
242
+ }
243
+
244
+
245
+ } catch (Exception $e) {
246
+ $productInQuote["errorMessage"] = $e->getMessage();
247
+ $productInQuote["errorCode"] = 400; //something else went wrong
248
+ $productInQuote["quantity"] = 0;
249
+
250
+ }
251
+
252
+
253
+
254
+ $responseQuote[] = $productInQuote;
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
+
268
+ return $responseQuote;
269
+ }
270
+
271
+ private function getProductInQuoteResponse($quoteItem = null, $product = null) {
272
+ if(!$quoteItem && !$product)
273
+ return null;
274
+ if(!$product)
275
+ $product = $quoteItem->getProduct();
276
+
277
+
278
+ $productInQuote = array();
279
+ $productInQuote["product_id"] = $product->getId();
280
+
281
+ if($quoteItem->getParentItem()) {
282
+ $quoteItem = $quoteItem->getParentItem();
283
+ $product = $quoteItem->getProduct();
284
+ }
285
+
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;
292
+ }
293
+
294
+ private function getQuoteTotals($quote) {
295
+
296
+
297
+ $quote->collectTotals()->save(); //required to fetch the totals
298
+
299
+ //Totals
300
+ $totals = $quote->getTotals(); //Total object
301
+ $subtotal = $totals["subtotal"]->getValue(); //Subtotal value
302
+ $grandtotal = $totals["grand_total"]->getValue(); //Grandtotal value
303
+
304
+ $discount = 0;
305
+ if(isset($totals['discount']) && $totals['discount']->getValue()) {
306
+ $discount = $totals['discount']->getValue(); //Discount value if applied
307
+ }
308
+ $tax = 0;
309
+ if(isset($totals['tax']) && $totals['tax']->getValue()) {
310
+ $tax = $totals['tax']->getValue(); //Tax value if present
311
+ }
312
+
313
+ $totalItemsInCart = 0;
314
+ foreach($quote->getAllItems() as $quoteItem) {
315
+ if(count($quoteItem->getChildren()) > 0)
316
+ continue;
317
+ $totalItemsInCart++;
318
+ }
319
+
320
+
321
+ $responseTotals = array();
322
+ $responseTotals["totalItemsInCart"] = $totalItemsInCart;
323
+ $responseTotals["subtotal"] = $subtotal;
324
+ $responseTotals["grandtotal"] = $grandtotal;
325
+ $responseTotals["discount"] = $discount;
326
+ $responseTotals["tax"] = $tax;
327
+
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
+
349
+ $chosenShippingMethod = $quoteShippingAddress->getShippingMethod();
350
+
351
+ if ($chosenShippingMethod === "") {
352
+ $chosenShippingMethod = null;
353
+ }
354
+
355
+ return $chosenShippingMethod;
356
+ }
357
+
358
+ private function getShippingMethods($quote, $selectedShippingMethod) {
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();
366
+
367
+ foreach ($groupedRates as $carrierCode => $rates ) {
368
+ foreach ($rates as $rate) {
369
+ $price = $rate->getPrice();
370
+ if ($rate->getCode() == $selectedShippingMethod) {
371
+ $quoteShippingAddress->setShippingMethod($selectedShippingMethod);
372
+ $quote->collectTotals()->save();
373
+
374
+ $price = $quoteShippingAddress->getShippingInclTax();
375
+ }
376
+
377
+ $responseRate = array();
378
+ $responseRate["carrier"] = $rate->getCarrier();
379
+ $responseRate["carrierTitle"] = $rate->getCarrierTitle();
380
+ $responseRate["carrierCode"] = $rate->getCode();
381
+
382
+ $responseRate["method"] = $rate->getMethod();
383
+ $responseRate["methodTitle"] = $rate->getMethodTitle();
384
+ $responseRate["methodDescription"] = $rate->getMethodDescription();
385
+ $responseRate["price"] = $price;
386
+ $responseCarriers[] = $responseRate;
387
+ }
388
+ }
389
+
390
+ return $responseCarriers;
391
+ }
392
+
393
+ private function loadProduct($productId = null) {
394
+ if(!$productId)
395
+ return null;
396
+
397
+ $productModel = Mage::getModel('catalog/product');
398
+ $product = $productModel->load($productId);
399
+ if (!$product->getId())
400
+ return null; //product does not exist
401
+
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) {
421
+ $configurations = array();
422
+ $conf = Mage::getModel('catalog/product_type_configurable')->setProduct($parent);
423
+
424
+ //build the configuration_attributes array
425
+ $configurableAttributes = $conf->getConfigurableAttributesAsArray($parent);
426
+
427
+ foreach($configurableAttributes as $attribute) {
428
+
429
+ $method = 'get' . uc_words($attribute['attribute_code'], '');
430
+ $attribute_value = $product->$method();
431
+ $attribute_id = $attribute['attribute_id'];
432
+ $configurations[$attribute_id] = $attribute_value;
433
+
434
+ }
435
+
436
+ return $configurations;
437
+ }
438
+
439
+
440
+ }
app/code/local/Highstreet/Hsapi/Model/Images.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Highstreet_HSAPI_module
4
+ *
5
+ * @package Highstreet_Hsapi
6
+ * @author Tim Wachter (tim@touchwonders.com)
7
+ * @copyright Copyright (c) 2014 Touchwonders (http://www.touchwonders.com/)
8
+ */
9
+ class Highstreet_Hsapi_Model_Images extends Mage_Core_Model_Abstract
10
+ {
11
+
12
+ public function getImage($src, $size) {
13
+ if (!$src) {
14
+ return null;
15
+ } else {
16
+ $image = Mage::helper('timage')->init($src);
17
+
18
+ if ($src[0] !== "/") {
19
+ $src = "/" . $src;
20
+ }
21
+
22
+ $imageUrl = Mage::getBaseDir() . $src;
23
+ if(!file_exists($imageUrl) ) {
24
+ return null;
25
+ }
26
+
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);
34
+ } else {
35
+ if ($originalSize['width'] >= $originalSize['height']) {
36
+ $image->resize($size, NULL);
37
+ } else {
38
+ $image->resize(NULL, $size);
39
+ }
40
+ }
41
+
42
+ $imageUrl = $image->cachedImage;
43
+ }
44
+ }
45
+
46
+ return $imageUrl;
47
+ }
48
+
49
+ }
app/code/local/Highstreet/Hsapi/Model/Observer.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ ?>
app/code/local/Highstreet/Hsapi/Model/Products.php ADDED
@@ -0,0 +1,880 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ 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;
20
+ const SPECIAL_PRICE_FROM_DATE_FALLBACK = "1970-01-01 00:00:00";
21
+
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);
71
+ }
72
+
73
+
74
+ // apply search
75
+ if ($searching) {
76
+
77
+ ////////
78
+ $_GET['q'] = $search; //this is the only to pass the search query
79
+ $query = Mage::helper('catalogsearch')->getQuery();
80
+ $query->setStoreId(Mage::app()->getStore()->getId());
81
+
82
+ //Code here inspired from ResultController.php
83
+ if ($query->getQueryText() != '') {
84
+ if (Mage::helper('catalogsearch')->isMinQueryLength()) {
85
+ $query->setId(0)
86
+ ->setIsActive(1)
87
+ ->setIsProcessed(1);
88
+ }
89
+ else {
90
+ if ($query->getId()) {
91
+ $query->setPopularity($query->getPopularity()+1);
92
+ }
93
+ else {
94
+ $query->setPopularity(1);
95
+ }
96
+ }
97
+ if (!Mage::helper('catalogsearch')->isMinQueryLength()) {
98
+ $query->prepare();
99
+ }
100
+ }
101
+ $query->save();
102
+
103
+
104
+ $catalogSearchModelCollection = Mage::getResourceModel('catalogsearch/fulltext_collection');
105
+
106
+ $catalogSearchModelCollection->addSearchFilter($search);
107
+
108
+ $collection = $catalogSearchModelCollection;
109
+ } else {
110
+
111
+ // initialize
112
+ $collection = Mage::getModel('catalog/product')->getCollection();
113
+ $collection->addStoreFilter();
114
+ Mage::getSingleton('catalog/product_status')->addVisibleFilterToCollection($collection);
115
+ Mage::getSingleton('catalog/product_visibility')->addVisibleInCatalogFilterToCollection($collection);
116
+
117
+ }
118
+
119
+
120
+
121
+
122
+ $categoryNotSet = false;
123
+
124
+ if (empty($categoryId)) {
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);
132
+ }
133
+
134
+ if (!empty($range)) {
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)) {
162
+ foreach ($filters as $filter) {
163
+ if (array_key_exists('attribute', $filter)) {
164
+ foreach ($filter as $operator => $condition) {
165
+ if ($operator != 'attribute') {
166
+ $collection->addAttributeToFilter(array(array('attribute' => $filter['attribute'], $operator => $condition)));
167
+ }
168
+ }
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)) {
179
+ foreach ($order as $orderCondition) {
180
+ $orderBy = explode(':', $orderCondition);
181
+ $collection->setOrder($orderBy[0], $orderBy[1]);
182
+ }
183
+ } else {
184
+ if($searching) {
185
+ $collection->setOrder('relevance', 'desc');
186
+ } else {
187
+
188
+ $sortKey = $category->getDefaultSortBy();
189
+ if (!$sortKey) {
190
+ $sortKey = Mage::getStoreConfig('catalog/frontend/default_sort_by');
191
+ }
192
+ $collection->setOrder($sortKey, 'asc');
193
+ }
194
+ }
195
+
196
+ // Add 'out of stock' filter, if preffered
197
+ if (!Mage::getStoreConfig('cataloginventory/options/show_out_of_stock')) {
198
+ Mage::getSingleton('cataloginventory/stock')->addInStockFilterToCollection($collection);
199
+
200
+ // Make a better fix for this. At this time this seems impossible, this doesn't work:
201
+ // $collection->addAttributeToFilter('is_salable', array('eq' => 1));
202
+ // Mage::getSingleton('cataloginventory/stock')->addInStockFilterToCollection($collection); // Another popular suggestion around the web, crashes the app for now and 'under water' does the same as l:276
203
+ // For now we look trough all the configurable products in the collection of a certain category and filter out the unneded products
204
+ $collectionConfigurable = Mage::getResourceModel('catalog/product_collection')->addAttributeToFilter('type_id', array('eq' => 'configurable'));
205
+ $collectionConfigurable->addCategoryFilter($category);
206
+
207
+ $outOfStockConfis = array();
208
+ foreach ($collectionConfigurable as $_configurableproduct) {
209
+ $product = Mage::getModel('catalog/product')->load($_configurableproduct->getId());
210
+ if (!$product->getData('is_salable')) {
211
+ $outOfStockConfis[] = $product->getId();
212
+ }
213
+ }
214
+
215
+ if (count($outOfStockConfis) > 0) {
216
+ $collection->addAttributeToFilter('entity_id', array('nin' => $outOfStockConfis));
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();
229
+ }
230
+ /**
231
+ * Format result array
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];
245
+ if ($rangeLength > count($products["products"])) {
246
+ $rangeLength = count($products["products"]);
247
+ }
248
+
249
+ $products['range'] = array("location" => $range[0], "length" => $rangeLength);
250
+
251
+ return $products;
252
+ }
253
+
254
+
255
+ /**
256
+ *
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
+
271
+ if (!$productIds) {
272
+ $products['product_count'] = 0;
273
+ $products['range'] = array("location" => 0, "length" => 0);
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
+
308
+ $outOfStockConfis = array();
309
+ foreach ($collectionConfigurable as $_configurableproduct) {
310
+ $product = Mage::getModel('catalog/product')->load($_configurableproduct->getId());
311
+ if (!$product->getData('is_salable')) {
312
+ $outOfStockConfis[] = $product->getId();
313
+ }
314
+ }
315
+
316
+ if (count($outOfStockConfis) > 0) {
317
+ $collection->addAttributeToFilter('entity_id', array('nin' => $outOfStockConfis));
318
+ }
319
+ }
320
+
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"])) {
332
+ $rangeLength = count($products["products"]);
333
+ }
334
+
335
+ $products['range'] = array("location" => $range[0], "length" => $rangeLength);
336
+
337
+ return $products;
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;
358
+ }
359
+
360
+
361
+ public function getStockInfo($productId = false) {
362
+ if(!$productId) {
363
+ return;
364
+ }
365
+ $product = Mage::getModel('catalog/product')->load($productId);
366
+
367
+ if(!$product->getId())
368
+ return;
369
+
370
+
371
+ $products = array();
372
+ $response = array();
373
+
374
+ if($product->getTypeId() == 'configurable') {
375
+ $conf = Mage::getModel('catalog/product_type_configurable')->setProduct($product);
376
+ $simple_collection = $conf->getUsedProductCollection()->addFilterByRequiredOptions();
377
+ $products = $simple_collection;
378
+ } else if($product->getTypeId() == 'simple'){
379
+ $products[] = $product;
380
+ } else {
381
+ return; //Other product types not supported yet
382
+ }
383
+
384
+
385
+ foreach($products as $simpleproduct)
386
+ $response[] = $this->_getStockInformationForProduct($simpleproduct);
387
+
388
+
389
+ return array_values($response);
390
+
391
+
392
+ }
393
+
394
+ /**
395
+ * Gets related products for a type and product id
396
+ *
397
+ * @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
398
+ * @param int productId, id used for base of related products
399
+ * @param string Attributes, comma seperated string of attributes
400
+ * @param string Child products attributes, comma seperated string of attributes for the child products
401
+ * @param string range, formatted range string
402
+ * @return array Array of product ids
403
+ *
404
+ */
405
+
406
+ public function getRelatedProducts($type, $productId = false, $attributes, $child_product_attributes, $range) {
407
+ if (!$productId) {
408
+ return;
409
+ }
410
+
411
+ if ($type == "cross-sell") {
412
+ $productIds = $this->getCrossSellProductIds($productId);
413
+ } else if ($type == "up-sell") {
414
+ $productIds = $this->getUpSellProductIds($productId);
415
+ } else {
416
+ $productIds = $this->getRelatedProductIds($productId);
417
+ }
418
+
419
+ return $this->getProductsFilteredByProductIds($productIds, $attributes, $child_product_attributes, $range);
420
+
421
+ }
422
+
423
+
424
+ /**
425
+ * Convenience functions
426
+ */
427
+
428
+ /**
429
+ *
430
+ * Get related product id's for a product id
431
+ *
432
+ * @param int productId, id used to filter related products
433
+ * @return array Array of product ids
434
+ *
435
+ */
436
+
437
+ public function getRelatedProductIds($productId = false) {
438
+ if (!$productId) {
439
+ return;
440
+ }
441
+
442
+ $productModel = Mage::getModel('catalog/product')->load($productId);
443
+ return $productModel->getRelatedProductIds();
444
+ }
445
+
446
+ /**
447
+ *
448
+ * Get cross sell product id's for a product id
449
+ *
450
+ * @param int productId, id used to filter cross sell products
451
+ * @return array Array of product ids
452
+ *
453
+ */
454
+
455
+ public function getCrossSellProductIds($productId = false) {
456
+ if (!$productId) {
457
+ return;
458
+ }
459
+
460
+ $productModel = Mage::getModel('catalog/product')->load($productId);
461
+ return $productModel->getCrossSellProductIds();
462
+ }
463
+
464
+ /**
465
+ *
466
+ * Get up sell product id's for a product id
467
+ *
468
+ * @param int productId, id used to filter up sell products
469
+ * @return array Array of product ids
470
+ *
471
+ */
472
+
473
+ public function getUpSellProductIds($productId = false) {
474
+ if (!$productId) {
475
+ return;
476
+ }
477
+
478
+ $productModel = Mage::getModel('catalog/product')->load($productId);
479
+ return $productModel->getUpSellProductIds();
480
+ }
481
+
482
+
483
+
484
+ /***********************************/
485
+ /**
486
+ * PRIVATE/protected FUNCTIONS
487
+ */
488
+ /***********************************/
489
+ /**
490
+ * Returns filters
491
+ *
492
+ * @param int $categoryId
493
+ * @return array
494
+ * @author Andrey Posudevsky
495
+ *
496
+ */
497
+ protected function getFilters($categoryId = false) {
498
+
499
+ if (!$categoryId) {
500
+ $categoryId = Mage::app()->getStore()->getRootCategoryId();
501
+ }
502
+
503
+ $layer = Mage::getModel('catalog/layer');
504
+
505
+ //$layered_nav = $this->getLayout()->createBlock('catalog/layer_view');
506
+ $layered_nav = Highstreet_Hsapi_IndexController::getLayout()->createBlock('catalog/layer_view');
507
+ $filters = $layered_nav->getFilters();
508
+ $category = Mage::getModel('catalog/category')->load($categoryId);
509
+ $layer->setCurrentCategory($category);
510
+ $attributes = $layer->getFilterableAttributes('price');
511
+ $resultFilters = array();
512
+ foreach ($attributes as $attribute) {
513
+
514
+ if ($attribute->getAttributeCode() == 'price') {
515
+ $filterBlockName = 'catalog/layer_filter_price';
516
+ } else {
517
+ $filterBlockName = 'catalog/layer_filter_attribute';
518
+ }
519
+ //$result = $this->getLayout()->createBlock($filterBlockName)->setLayer($layer)->setAttributeModel($attribute)->init();
520
+ $result = Highstreet_Hsapi_IndexController::getLayout()->createBlock($filterBlockName)->setLayer($layer)->setAttributeModel($attribute)->init();
521
+ $options = array();
522
+ foreach($result->getItems() as $option) {
523
+ $label = str_replace('<span class="price">', "", $option->getLabel());
524
+ $label = str_replace('</span>', "", $label);
525
+
526
+ $count = $option->getData('count');
527
+ array_push($options, array('filter' => $option->getValue(), 'label' => $label, 'product_count' => $count));
528
+ }
529
+ array_push($resultFilters, array($attribute->getAttributeCode() => $options));
530
+ }
531
+
532
+ return $resultFilters;
533
+ }
534
+
535
+ /**
536
+ *
537
+ * Gets attributes of a given product object.
538
+ *
539
+ * @param Mage_Catalog_Model_Product ResProduct, a product object
540
+ * @param string Attributes, an string of attributes to get for the product, comma delimited
541
+ * @param string Child_product_attributes, attributes for the child products, comma delimited
542
+ * @return array Array with information about the product, according to the Attributes array param
543
+ *
544
+ */
545
+
546
+ private function _getProductAttributes($resProduct = false, $attributes = nil, $child_product_attributes) {
547
+ if (!$resProduct) {
548
+ return null;
549
+ }
550
+
551
+
552
+ if(empty($attributes)) {
553
+ $attributes = $this->_getSystemAttributes();
554
+ } else {
555
+ $attributes = explode(',', $attributes);
556
+ }
557
+
558
+
559
+ // if attributes specified
560
+ if (!empty($attributes) && count($attributes) > 0) {
561
+ foreach ($attributes as $attribute) {
562
+
563
+ //always set final price to the special price field
564
+ if ($attribute === "special_price" || $attribute === "final_price") {
565
+ $product[$attribute] = $resProduct->getFinalPrice(1);
566
+
567
+ if ($product[$attribute] === false) {
568
+ $product[$attribute] = null;
569
+ }
570
+
571
+ continue;
572
+ }
573
+
574
+ // Translate this into an array of "translations" if we run into more problems
575
+ $fieldName = $attribute;
576
+ if ($attribute == "type") {
577
+ $attribute = "type_id";
578
+ $fieldName = "type";
579
+ }
580
+
581
+ if ($resProduct->getResource()->getAttribute($attribute)) {
582
+ $value = $resProduct->getResource()->getAttribute($attribute)->getFrontend()->getValue($resProduct);
583
+ $product[$fieldName] = $value;
584
+ }
585
+ }
586
+
587
+ }
588
+
589
+
590
+ $product['id'] = $resProduct->getId();
591
+
592
+
593
+ //We will deprecate special_from_date and special_to_date soon, but for now we make sure that the special price is always applicable
594
+ //this is correct because special_price always has the value of the finalprice
595
+ $product["special_from_date"] = self::SPECIAL_PRICE_FROM_DATE_FALLBACK;
596
+ $product["special_to_date"] = null;
597
+
598
+
599
+ if (array_key_exists("special_price", $product) && array_key_exists("price", $product) && $product["special_price"] >= $product["price"]) {
600
+ $product["special_price"] = null;
601
+ }
602
+
603
+
604
+
605
+
606
+ if (in_array("is_salable", $attributes)) {
607
+ $product["is_salable"] = $resProduct->getData('is_salable');
608
+ }
609
+
610
+
611
+
612
+ if(in_array('configurable_attributes',$attributes)){
613
+ $this->_addConfigurableAttributes = true;
614
+ }
615
+
616
+ if(in_array('child_products',$attributes)){
617
+ $this->_addConfigurations = true;
618
+ }
619
+
620
+ if($resProduct->getTypeId() == 'configurable'){
621
+ $conf = Mage::getModel('catalog/product_type_configurable')->setProduct($resProduct);
622
+
623
+ //build the configuration_attributes array
624
+ $configurableAttributes = $conf->getConfigurableAttributesAsArray($resProduct);
625
+
626
+ $tmpConfigurableAttributes = array();
627
+ foreach($configurableAttributes as $attribute)
628
+ {
629
+ array_push($tmpConfigurableAttributes,$attribute['attribute_code']);
630
+ //array_push($simpleCollectionAttributes,$attribute['attribute_code']);
631
+ }
632
+ if($this->_addConfigurableAttributes == true){
633
+ $product['configurable_attributes'] = $tmpConfigurableAttributes;
634
+ }
635
+
636
+ //build the configuration_attributes array if we want to display these
637
+ if($this->_addConfigurations == true){
638
+
639
+ $product['child_products'] = array();
640
+ $simple_collection = $conf->getUsedProductCollection()
641
+ ->addAttributeToSelect('*')
642
+ ->addFilterByRequiredOptions();
643
+
644
+ foreach($simple_collection as $simple_product){
645
+ if(!Mage::getStoreConfig('cataloginventory/options/show_out_of_stock')
646
+ && !$simple_product->isSaleable())
647
+ continue;
648
+
649
+ if(!$child_product_attributes)
650
+ $child_product_attributes = "entity_id,created_at,description,special_price,updated_at,image,sku,short_description,price,manufacturer";
651
+
652
+ $simpleProductRepresentation = $this->_getProductAttributes(Mage::getModel('catalog/product')->load($simple_product->getId()), $child_product_attributes);
653
+ $configuration = array();
654
+
655
+
656
+ foreach($tmpConfigurableAttributes as $attribute)
657
+ {
658
+ $method = 'get' . uc_words($attribute, '');
659
+ $configuration[$attribute] = $simple_product->$method();
660
+ }
661
+
662
+ $simpleProductRepresentation['configuration'] = $configuration;
663
+ array_push($product['child_products'],(object)$simpleProductRepresentation);
664
+ }
665
+ }
666
+
667
+ unset($tmpConfigurableAttributes);
668
+ }
669
+
670
+ $this->_convertProductDates($product);
671
+
672
+ $product = $this->_setImagePaths($product);
673
+
674
+ //media gallery
675
+ if(array_key_exists("media_gallery", $product)) {
676
+ $product["media_gallery"] = $this->_getMediaGalleryImagesForProductID($product["id"]);
677
+ }
678
+
679
+ return $product;
680
+ }
681
+
682
+
683
+ /**
684
+ * Converts product dates to timestamp
685
+ *
686
+ */
687
+ private function _convertProductDates(& $product) {
688
+
689
+ if (!empty($product['created_at'])) {
690
+ $product['created_at'] = strtotime($product['created_at']);
691
+ }
692
+ if (!empty($product['updated_at'])) {
693
+ $product['updated_at'] = strtotime($product['updated_at']);
694
+ }
695
+ if (!empty($product['special_from_date'])) {
696
+ $product['special_from_date'] = strtotime($product['special_from_date']);
697
+ }
698
+ if (!empty($product['special_to_date'])) {
699
+ $product['special_to_date'] = strtotime($product['special_to_date']);
700
+ }
701
+ if (!empty($product['news_from_date'])) {
702
+ $product['news_from_date'] = strtotime($product['news_from_date']);
703
+ }
704
+ if (!empty($product['news_to_date'])) {
705
+ $product['news_to_date'] = strtotime($product['news_to_date']);
706
+ }
707
+
708
+ }
709
+
710
+ /**
711
+ * Load media gallery in collection
712
+ */
713
+ private function _addMediaGalleryAttributeToCollection(Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Collection $_productCollection) {
714
+
715
+ if (count($_productCollection == 0))
716
+ return $_productCollection;
717
+
718
+ $_mediaGalleryAttributeId = Mage::getSingleton('eav/config')->getAttribute('catalog_product', 'media_gallery')->getAttributeId();
719
+ $_read = Mage::getSingleton('core/resource')->getConnection('catalog_read');
720
+
721
+ $_mediaGalleryData = $_read->fetchAll('
722
+ SELECT
723
+ main.entity_id, `main`.`value_id`, `main`.`value` AS `file`,
724
+ `value`.`label`, `value`.`position`, `value`.`disabled`, `default_value`.`label` AS `label_default`,
725
+ `default_value`.`position` AS `position_default`,
726
+ `default_value`.`disabled` AS `disabled_default`
727
+ FROM `catalog_product_entity_media_gallery` AS `main`
728
+ LEFT JOIN `catalog_product_entity_media_gallery_value` AS `value`
729
+ ON main.value_id=value.value_id AND value.store_id=' . Mage::app()->getStore()->getId() . '
730
+ LEFT JOIN `catalog_product_entity_media_gallery_value` AS `default_value`
731
+ ON main.value_id=default_value.value_id AND default_value.store_id=0
732
+ WHERE (
733
+ main.attribute_id = ' . $_read->quote($_mediaGalleryAttributeId) . ')
734
+ AND (main.entity_id IN (' . $_read->quote($_productCollection->getAllIds()) . '))
735
+ ORDER BY IF(value.position IS NULL, default_value.position, value.position) ASC
736
+ ');
737
+
738
+
739
+ $_mediaGalleryByProductId = array();
740
+ foreach ($_mediaGalleryData as $_galleryImage) {
741
+ $k = $_galleryImage['entity_id'];
742
+ unset($_galleryImage['entity_id']);
743
+ if (!isset($_mediaGalleryByProductId[$k])) {
744
+ $_mediaGalleryByProductId[$k] = array();
745
+ }
746
+ $_galleryImage['file'] = self::PRODUCTS_MEDIA_PATH . $_galleryImage['file'];
747
+ $_mediaGalleryByProductId[$k][] = $_galleryImage;
748
+ }
749
+ unset($_mediaGalleryData);
750
+
751
+ foreach ($_productCollection as &$_product) {
752
+ $_productId = $_product->getData('entity_id');
753
+ if (isset($_mediaGalleryByProductId[$_productId])) {
754
+ $_product->setData('media_gallery', array('images' => $_mediaGalleryByProductId[$_productId]));
755
+ }
756
+ }
757
+ unset($_mediaGalleryByProductId);
758
+
759
+ return $_productCollection;
760
+ }
761
+
762
+ /**
763
+ * Gets stock (voorraad) information about a certain product
764
+ *
765
+ * @param product A product
766
+ *
767
+ * @return array Array of stock data
768
+ */
769
+
770
+ private function _getStockInformationForProduct($product) {
771
+ $stock = Mage::getModel('cataloginventory/stock_item')->loadByProduct($product);
772
+ $stockinfo = array();
773
+
774
+ $stockinfo['product_entity_id'] = $product->getId();
775
+ $stockinfo['quantity'] = $stock->getQty();
776
+ $stockinfo['is_in_stock'] = (boolean)$stock->getIsInStock();
777
+ $stockinfo['min_sale_quantity'] = $stock->getMinSaleQty();
778
+ $stockinfo['max_sale_quantity'] = $stock->getMaxSaleQty();
779
+ $stockinfo['manage_stock'] = (boolean)$stock->getManageStock();
780
+ $stockinfo['backorders'] = (boolean)$stock->getBackorders();
781
+
782
+ $stockinfo['quantity_increments'] = (boolean)$stock->getQtyIncrements();
783
+ $stockinfo['quantity_increments_value'] = (int)$stock->getQtyIncrements();
784
+
785
+ return $stockinfo;
786
+ }
787
+
788
+ /**
789
+ * Gets system attributes. This function is used when there are no attributes give as a param in the URL of the API call
790
+ *
791
+ * @return array Array with attribute names
792
+ */
793
+
794
+ private function _getSystemAttributes() {
795
+
796
+ $entityTypeId = Mage::getModel('catalog/product')->getResource()->getEntityType()->getId();
797
+
798
+ // get only system attributes
799
+ $attributes = Mage::getResourceModel('eav/entity_attribute_collection')
800
+ ->setEntityTypeFilter($entityTypeId)
801
+ ->addFieldToFilter('main_table.is_user_defined', 0)
802
+ ->addFieldToFilter('additional_table.is_visible', 1);
803
+
804
+ $attributeNames = array();
805
+
806
+ foreach($attributes as $attribute) {
807
+ $attributeNames[] = $attribute->getName();
808
+ }
809
+
810
+ return $attributeNames;
811
+ }
812
+
813
+
814
+ /**
815
+ * Sets the image paths properly with the relative path.
816
+ *
817
+ * @param product Array with product information
818
+ * @return product Same product, but with formatted image uri's
819
+ */
820
+ private function _setImagePaths($product = false) {
821
+ if (!$product) {
822
+ return $product;
823
+ }
824
+
825
+
826
+ if (array_key_exists('image', $product) && !strstr($product['image'], self::PRODUCTS_MEDIA_PATH)) {
827
+ if($product['image'] != self::NO_IMAGE_PATH)
828
+ $product['image'] = self::PRODUCTS_MEDIA_PATH . $product['image'];
829
+ else
830
+ $product['image'] = null;
831
+ }
832
+
833
+ if (array_key_exists('thumbnail', $product) && !strstr($product['thumbnail'], self::PRODUCTS_MEDIA_PATH)) {
834
+ if($product['thumbnail'] != self::NO_IMAGE_PATH)
835
+ $product['thumbnail'] = self::PRODUCTS_MEDIA_PATH . $product['thumbnail'];
836
+ else
837
+ $product['thumbnail'] = null;
838
+ }
839
+
840
+ if (array_key_exists('small_image', $product) && !strstr($product['small_image'], self::PRODUCTS_MEDIA_PATH)) {
841
+ if($product['small_image'] != self::NO_IMAGE_PATH)
842
+ $product['small_image'] = self::PRODUCTS_MEDIA_PATH . $product['small_image'];
843
+ else
844
+ $product['small_image'] = null;
845
+ }
846
+
847
+ return $product;
848
+ }
849
+
850
+ /**
851
+ * Gets media gallery items for a given product id. Returns an array or media gallery items
852
+ *
853
+ * @param integer Product ID, ID of a product to get media gallery images for
854
+ * @return array Array of media gallery items
855
+ */
856
+
857
+ public function _getMediaGalleryImagesForProductID($productId = null) {
858
+ if (!$productId) {
859
+ return null;
860
+ }
861
+
862
+ $output = array();
863
+
864
+ foreach (Mage::getModel('catalog/product')->load($productId)->getMediaGalleryImages()->getItems() as $key => $value) {
865
+ $imageData = $value->getData();
866
+ if (array_key_exists('file', $imageData) && !strstr($imageData['file'], self::PRODUCTS_MEDIA_PATH)) {
867
+ $imageData['file'] = self::PRODUCTS_MEDIA_PATH . $imageData['file'];
868
+ }
869
+ unset($imageData["path"]);
870
+ unset($imageData["url"]);
871
+ unset($imageData["id"]);
872
+ $output[] = $imageData;
873
+ }
874
+
875
+ return array("images" => $output);
876
+ }
877
+
878
+
879
+
880
+ }
app/code/local/Highstreet/Hsapi/Model/SearchSuggestions.php ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Highstreet_HSAPI_module
4
+ *
5
+ * @package Highstreet_Hsapi
6
+ * @author Tim Wachter (tim@touchwonders.com)
7
+ * @copyright Copyright (c) 2014 Touchwonders (http://www.touchwonders.com/)
8
+ */
9
+ class Highstreet_Hsapi_Model_SearchSuggestions extends Mage_Core_Model_Abstract
10
+ {
11
+ /**
12
+ * Can take params for category, limit and search query. It manually takes those params from the URL
13
+ *
14
+ * @param integer Limit, limit for the results
15
+ * @param string Search, search query
16
+ * @param integer Category, limit results to category
17
+ * @return array Search suggestions
18
+ */
19
+ public function getSearchSuggestions($paramLimit, $paramSearch, $paramCategory) {
20
+ // Initialize search object
21
+ $searchModel= Mage::getModel('catalogsearch/query');
22
+
23
+ $searchCollection = $searchModel->getCollection();
24
+ $searchCollection->addOrder('popularity', 'DESC'); // Order on popularity
25
+
26
+ // Set limit
27
+ $limit = 10;
28
+ if (!empty($paramLimit)) {
29
+ $limit = $paramLimit;
30
+ }
31
+
32
+ // Limit data to search term
33
+ if (!empty($paramSearch)) {
34
+ $searchCollection->addFieldToFilter('query_text', array("like" => $paramSearch . "%"));
35
+ }
36
+
37
+ $searchCollection->addFieldToFilter('store_id', Mage::app()->getStore()->getId());
38
+
39
+
40
+ $searchCollection->addFieldToFilter('display_in_terms', 1);
41
+
42
+ // Get data
43
+ $searchCollection->getSelect()->limit($limit);
44
+
45
+ $searchSuggestionData = array();
46
+
47
+ foreach ($searchCollection as $item) {
48
+ $searchSuggestionData["search_suggestions"][]["search_term"] = (string)$item->getData("query_text");
49
+ }
50
+
51
+ if (count($searchSuggestionData["search_suggestions"]) < 1) {
52
+ $searchSuggestionData["search_suggestions"][] = "";
53
+ }
54
+
55
+ return $searchSuggestionData;
56
+ }
57
+
58
+ }
app/code/local/Highstreet/Hsapi/controllers/.DS_Store ADDED
Binary file
app/code/local/Highstreet/Hsapi/controllers/IndexController.php ADDED
@@ -0,0 +1,414 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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_IndexController extends Mage_Core_Controller_Front_Action
11
+ {
12
+
13
+ /**
14
+ * @return Highstreet_Hsapi_Helper_Data
15
+ */
16
+ private function _getHelper()
17
+ {
18
+ return Mage::helper('highstreet_hsapi/data');
19
+ }
20
+
21
+ /** Disable actions */
22
+ public function indexAction() {
23
+ return false;
24
+ }
25
+
26
+ /**
27
+ * Get CMS page call
28
+ *
29
+ * Examples:
30
+ * {url}?page_id=12
31
+ * {url}?content_key=home
32
+ * {url}?page_ids[title-1]=10&page_ids[title-2]=11&page_ids[title-3]=12
33
+ * {url}?content_keys[title-1]=home&content_keys[title-2]=home-2&content_keys[title-3]=home-3
34
+ *
35
+ */
36
+ public function pageAction() {
37
+
38
+ $pageId = Mage::app()->getRequest()->getParam('page_id');
39
+ $contentKey = Mage::app()->getRequest()->getParam('content_key');
40
+
41
+ $pageIds = Mage::app()->getRequest()->getParam('page_ids');
42
+ $contentKeys = Mage::app()->getRequest()->getParam('content_keys');
43
+
44
+ if (($pageId === null && $contentKey === null) &&
45
+ ($pageIds === null && $contentKeys === null)) {
46
+ $this->_JSONencodeAndRespond(array("title" => "Error", "content" => "No arguments found"));
47
+ } else if ($pageId !== null || $contentKey !== null) {
48
+ $page = Mage::getModel('cms/page');
49
+ $page->setStoreId(Mage::app()->getStore()->getId());
50
+ if ($pageId !== null) {
51
+ $page->load($pageId);
52
+ } else if ($contentKey !== null) {
53
+ $page->load($contentKey, 'identifier');
54
+ }
55
+ $this->_JSONencodeAndRespond(array("title" => $page->getData('title'), "content" => $page->getData('content')));
56
+ } else if ($pageIds !== null || $contentKeys !== null) {
57
+ $page = Mage::getModel('cms/page');
58
+ $page->setStoreId(Mage::app()->getStore()->getId());
59
+
60
+ $response = array();
61
+ if ($pageIds !== null) {
62
+ foreach ($pageIds as $key => $value) {
63
+ try {
64
+ $page->load($value, 'identifier');
65
+ $response[$key] = array("title" => $page->getData('title'), "content" => $page->getData('content'));
66
+ } catch (Exception $e) {
67
+ $response[$key] = array("title" => "Page not found", "content" => "");
68
+ }
69
+ }
70
+ } else if ($contentKeys !== null) {
71
+ foreach ($contentKeys as $key => $value) {
72
+ try {
73
+ $page->load($value, 'identifier');
74
+ $response[$key] = array("title" => $page->getData('title'), "contentKey" => $value, "content" => $page->getData('content'));
75
+ } catch (Exception $e) {
76
+ $response[$key] = array("title" => "Page not found", "content" => "");
77
+ }
78
+ }
79
+ }
80
+
81
+ $this->_JSONencodeAndRespond($response);
82
+ }
83
+ }
84
+
85
+ public function pingAction() {
86
+ $this->_JSONencodeAndRespond(array("OK"));
87
+ }
88
+
89
+ /**
90
+ * Categories
91
+ */
92
+ public function categoryAction()
93
+ {
94
+ return $this->categoriesAction();
95
+ }
96
+
97
+ /**
98
+ * Categories action
99
+ */
100
+ public function categoriesAction()
101
+ {
102
+ //Get categoryId and check
103
+ $categoryId = Mage::app()->getRequest()->getParam('id');
104
+
105
+ $categories = null;
106
+ $categoryModel = Mage::getModel('highstreet_hsapi/categories');
107
+
108
+ if ($categoryId === 'tree') {
109
+ $categories = $categoryModel->getCategoryTree();
110
+ } else if(!$categoryId || $categoryId !== '') {
111
+ $categories = $categoryModel->getCategories($categoryId);
112
+ }
113
+
114
+ if($categories == null) {
115
+ $this->_respondWith404();
116
+ return false;
117
+ }
118
+
119
+ $this->_JSONencodeAndRespond($categories);
120
+ }
121
+
122
+ /**
123
+ * Category Products action. (A proxy for Products)
124
+ */
125
+ public function categoryProductsAction()
126
+ {
127
+ return $this->productsAction();
128
+ }
129
+
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
+
162
+ public function cartAction() {
163
+ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
164
+ return $this->postCart();
165
+ }
166
+ else if ($_SERVER['REQUEST_METHOD'] === 'GET') {
167
+ return $this->getCart();
168
+ }
169
+
170
+ }
171
+
172
+
173
+ public function postCart() {
174
+ $requestObject = Mage::app()->getRequest();
175
+ $data = json_decode($requestObject->getParam('products'), true);
176
+
177
+ $checkoutModel = Mage::getModel('highstreet_hsapi/checkout');
178
+ $cart = $checkoutModel->getQuoteWithProductsAndQuantities($data["checkout"]);
179
+
180
+ $this->_JSONencodeAndRespond($cart);
181
+
182
+ }
183
+
184
+ public function getCart() {
185
+ $requestObject = Mage::app()->getRequest();
186
+ $quote_id = $requestObject->getParam('quote_id');
187
+ if(!$quote_id) {
188
+ $this->_respondWith404();
189
+ return false;
190
+ }
191
+
192
+ $checkoutModel = Mage::getModel('highstreet_hsapi/checkout');
193
+ $cart = $checkoutModel->getQuoteWithProductsAndQuantities(null,$quote_id);
194
+
195
+ $this->_JSONencodeAndRespond($cart);
196
+
197
+
198
+ }
199
+
200
+ /**
201
+ * Get single product
202
+ */
203
+ public function productAction()
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();
216
+ return false;
217
+ }
218
+
219
+ $this->_JSONencodeAndRespond($response);
220
+ }
221
+
222
+ /*
223
+ * Returns stock information for a given product
224
+ */
225
+ public function stockAction()
226
+ {
227
+ $requestObject = Mage::app()->getRequest();
228
+ $model = Mage::getModel('highstreet_hsapi/products');
229
+
230
+ $stockinfo = $model->getStockInfo($requestObject->getParam('id'));
231
+
232
+ if ($stockinfo == null) {
233
+ $this->_respondWith404();
234
+ return false;
235
+ }
236
+
237
+ $response = array();
238
+ $response["stock"] = $stockinfo;
239
+
240
+
241
+ $this->_JSONencodeAndRespond($response);
242
+ }
243
+
244
+ /**
245
+ * Get multiple products based on a category
246
+ */
247
+ public function productsAction()
248
+ {
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();
262
+ return false;
263
+ }
264
+
265
+ $this->_JSONencodeAndRespond($products);
266
+ }
267
+
268
+ /**
269
+ * Get a batch of products based on product ids
270
+ */
271
+
272
+ public function batchProductsAction()
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
+
289
+ /**
290
+ * Returns related / cross-sell / up-sell products
291
+ */
292
+ public function relatedProductsAction()
293
+ {
294
+ $requestObject = Mage::app()->getRequest();
295
+ $productsModel = Mage::getModel('highstreet_hsapi/products');
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();
305
+ return false;
306
+ }
307
+
308
+ $this->_JSONencodeAndRespond($response);
309
+ }
310
+
311
+ /**
312
+ * Media
313
+ */
314
+ public function imagesAction() {
315
+ $requestObject = $this->getRequest();
316
+
317
+ $imageUrl = Mage::getModel('highstreet_hsapi/images')->getImage(urldecode($requestObject->getParam('src')), $requestObject->getParam('size'));
318
+
319
+ if ($imageUrl === null || !file_exists($imageUrl)) {
320
+ $this->_respondWith404();
321
+ return false;
322
+ }
323
+
324
+ $this->getResponse()->setHeader('Content-Type', $this->_getHelper()->imageHeaderStringForImage($imageUrl), true);
325
+ $this->getResponse()->setBody(file_get_contents($imageUrl));
326
+ }
327
+
328
+
329
+ /**
330
+ * attributes
331
+ */
332
+ public function attributesAction()
333
+ {
334
+ $helper = $this->_getHelper();
335
+ $attributesModel = Mage::getModel('highstreet_hsapi/attributes');
336
+
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;
350
+ }
351
+
352
+ if($attributes == null){
353
+ $this->_respondWith404();
354
+ return false;
355
+ }
356
+
357
+ $this->_JSONencodeAndRespond($responseBody);
358
+ }
359
+
360
+ /**
361
+ * Returns search suggestions
362
+ * http://docs.test.touchwonders.com/highstreet/#api-Search-Search_suggestions
363
+ *
364
+ * @author Tim Wachter
365
+ *
366
+ */
367
+ public function searchsuggestionsAction() {
368
+ $requestObject = Mage::app()->getRequest();
369
+ $searchModel = Mage::getModel('highstreet_hsapi/searchSuggestions');
370
+
371
+ $response = $searchModel->getSearchSuggestions($requestObject->getParam('limit'), $requestObject->getParam('search'), $requestObject->getParam('category'));
372
+
373
+ if($response == null){
374
+ $this->_respondWith404();
375
+ return false;
376
+ }
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');
387
+ $this->getResponse()->sendHeaders();
388
+ return;
389
+ }
390
+
391
+ /**
392
+ * Sets the proper headers
393
+ */
394
+ private function _setHeader()
395
+ {
396
+ Mage::getSingleton('core/session')->setLastStoreCode(Mage::app()->getStore()->getCode());
397
+ header_remove('Pragma'); // removes 'no-cache' header
398
+ $this->getResponse()->setHeader('Content-Type','application/json', true);
399
+ }
400
+
401
+ /**
402
+ * Sets headers and body with proper JSON encoding
403
+ */
404
+ private function _JSONencodeAndRespond($data, $numeric_check = TRUE) {
405
+ //set response body
406
+ $this->_setHeader();
407
+ if ($numeric_check === FALSE) {
408
+ $this->getResponse()->setBody(json_encode($data));
409
+ } else {
410
+ $this->getResponse()->setBody(json_encode($data, JSON_NUMERIC_CHECK));
411
+ }
412
+ }
413
+
414
+ }
app/code/local/Highstreet/Hsapi/etc/.DS_Store ADDED
Binary file
app/code/local/Highstreet/Hsapi/etc/Configuration.json ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ {
2
+ "filters_categories": true,
3
+ }
app/code/local/Highstreet/Hsapi/etc/config.xml ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <!--
3
+ /**
4
+ * Highstreet_HSAPI_module
5
+ *
6
+ * @package Highstreet_Hsapi
7
+ * @author Tim Wachter (tim@touchwonders.com) ~ Touchwonders
8
+ * @copyright Copyright (c) 2013 Touchwonders b.v. (http://www.touchwonders.com/)
9
+ */
10
+ -->
11
+ <config>
12
+ <modules>
13
+ <Highstreet_Hsapi>
14
+ <version>1.0.0</version>
15
+ </Highstreet_Hsapi>
16
+ </modules>
17
+ <frontend>
18
+ <routers>
19
+ <hsapi>
20
+ <use>standard</use>
21
+ <args>
22
+ <module>Highstreet_Hsapi</module>
23
+ <frontName>hsapi</frontName>
24
+ </args>
25
+ </hsapi>
26
+ </routers>
27
+ <translate>
28
+ <modules>
29
+ <Highstreet_Hsapi>
30
+ <files>
31
+ <default>Highstreet_Hsapi.csv</default>
32
+ </files>
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>
46
+ <highstreet_hsapi>
47
+ <class>Highstreet_Hsapi_Model</class>
48
+ </highstreet_hsapi>
49
+ </models>
50
+ <helpers>
51
+ <highstreet_hsapi>
52
+ <class>Highstreet_Hsapi_Helper</class>
53
+ </highstreet_hsapi>
54
+ </helpers>
55
+ <blocks>
56
+ <highstreet_hsapi>
57
+ <class>Highstreet_Hsapi_Block</class>
58
+ </highstreet_hsapi>
59
+ </blocks>
60
+ <resources>
61
+ <highstreet_hsapi_setup>
62
+ <setup>
63
+ <module>Highstreet_Hsapi</module>
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>
72
+ <class>Highstreet_Hsapi_Model_Observer</class>
73
+ <method>mergeQuote</method>
74
+ </highstreet_hsapi_merge_quote>
75
+ </observers>
76
+ </sales_quote_merge_before>
77
+ </events>
78
+ </global>
79
+ </config>
app/etc/modules/Highstreet_Api.xml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+
3
+ <config>
4
+ <modules>
5
+ <Highstreet_Hsapi>
6
+ <active>true</active>
7
+ <codePool>local</codePool>
8
+ </Highstreet_Hsapi>
9
+ </modules>
10
+ </config>
app/etc/modules/Technooze_Timage.xml ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <!--
3
+ /**
4
+ * @category Technooze/Modules/magento-how-tos
5
+ * @package Technooze_Timage
6
+ * @author Damodar Bashyal
7
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
8
+ */
9
+ -->
10
+ <config>
11
+ <modules>
12
+ <Technooze_Timage>
13
+ <active>true</active>
14
+ <codePool>community</codePool>
15
+ </Technooze_Timage>
16
+ </modules>
17
+ </config>
app/locale/en_US/Highstreet_Hsapi.csv ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "Welcome", "Welcome"
2
+ "Billing address", "Billing address"
3
+ "Shipment", "Shipment"
4
+ "Payment", "Payment"
5
+ "Confirmation", "Confirmation"
6
+ "Log out", "Log out"
7
+ "Confirm and pay", "Confirm and pay"
8
+ "Your order is not final yet", "Your order is not final yet"
9
+ "You will continue to our secure payment page", "You will continue to our secure payment page"
10
+ "Hello!", "Hello!"
11
+ "Checkout as guest", "Checkout as guest"
12
+ "Log in", "Log in"
13
+ "hsapi_Register", "Register"
14
+ "Sign In", "Sign In"
15
+ "E-mail address", "E-mail address"
16
+ "ipad_Password", "Password"
17
+ "Forgot your password?", "Forgot your password?"
18
+ "Send", "Send"
19
+ "We have sent you an e-mail with which you can change your password.", "We have sent you an e-mail with which you can change your password."
20
+ "Create an account now", "Create an account now"
21
+ "and checkout faster next time", "and checkout faster next time"
22
+ "Please enter a valid e-mailaddress", "Please enter a valid e-mailaddress"
23
+ "Please enter a valid password", "Please enter a valid password"
24
+ "Repeat the password", "Repeat the password"
25
+ "First Name", "First Name"
26
+ "Last Name", "Last Name"
27
+ "Please enter a valid phone number.", "Please enter a valid phone number."
28
+ "Phone number", "Phone number"
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.", "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."
30
+ "Street", "Street"
31
+ "House number", "House number"
32
+ "Zip code", "Zip code"
33
+ "City", "City"
34
+ "Something went wrong", "Something went wrong"
35
+ "Please try again", "Please try again"
36
+ "How do you want to pay for your order?", "How do you want to pay for your order?"
37
+ "Please check your order.", "Please check your order."
38
+ "Please check the order overview. If this is correct, you can place your order.", "Please check the order overview. If this is correct, you can place your order."
39
+ "How do you want to receive your order?", "How do you want to receive your order?"
40
+ "Invoice address / delivery address", "Invoice address / delivery address"
41
+ "Edit", "Edit"
42
+ "Delivery method", "Delivery method"
43
+ "Payment method", "Payment method"
44
+ "You can continue to the next step when you have entered all fields", "You can continue to the next step when you have entered all fields"
45
+ "There has been an unknown error. Please try again later again later.", "There has been an unknown error. Please try again later again later."
46
+ "Thanks for shopping at ", "Thanks for shopping at "
47
+ ".<span class='hidden'>_ORDER_TEXT</span>", ".<span class='hidden'>_ORDER_TEXT</span>"
48
+ "When you tap 'Confirm and pay', you agree with ", "When you tap `Confirm and pay`, you agree with "
49
+ "the general terms", "the general terms"
50
+ " of ", " of "
51
+ "Ship to the same address", "Ship to the same address"
52
+ "Invoice address", "Invoice address"
53
+ "Delivery address", "Delivery address"
54
+ "Please make sure your passwords match.", "Please make sure your passwords match."
55
+ "Something went wrong while trying to get your address trough your zip code and house number. Please fill in your street and city manually.", "Er is iets fout gegaan met het ophalen van het adres via postcode en huisnummer. Vul het adres handmatig in."
56
+ "Street name is being retrieved", "Street name is being retrieved"
57
+ "City name is being retrieved", "City name is being retrieved"
58
+
59
+ "hsapi.index.couponTip", "Do you have a coupon code?<br />You can add your code on the<br />'payment' step."
60
+
61
+ "hsapi.addCouponAction.error.fatal", "An unexpected error has occurred, please try again later."
62
+ "hsapi.addCouponAction.error.invalid", "The coupon code you entered is invalid or has expired."
63
+ "hsapi.addCouponAction.error.length", "The coupon code you entered is too long, the maximum length is "
64
+ "hsapi.addCouponAction.success", "Kortingsbon '{coupon_code}' is succesvol toegepast. In je bestellijst kun je je korting bekijken."
65
+ "hsapi.addCouponAction.success.removed", "Kortingsbon '{coupon_code}' is succesvol verwijdert."
66
+
67
+ "hsapi.checkout.coupon.addTitle", "Add a <u>coupon code</u>"
68
+ "hsapi.checkout.coupon.form.title", "Here you can fill in your coupon code."
69
+ "hsapi.checkout.coupon.form.code", "Coupon code"
70
+ "hsapi.checkout.coupon.form.add", "Add"
71
+ "hsapi.checkout.coupon.form.remove", "Remove"
72
+ "hsapi.checkout.coupon.paymentTitle", "Choose one of the payment options"
73
+
74
+ "hsapi.checkout.payment.acceptAfterpayTermsText", "You have to accept the terms and conditions of AfterPay to use this payment method"
75
+
76
+ "hsapi.checkout.billing.error.email", "Please enter a valid e-mailaddress"
77
+
78
+
79
+ "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.", "The entered email address is already linked to an account. Please login instead or use a different email address for registration."
80
+
81
+ '"%s" is not a valid email address.', "hsapi.checkout.email.error"
82
+ '"%s" is not a valid hostname.', "hsapi.checkout.email.error"
83
+ '"%s" exceeds the allowed length.', "hsapi.checkout.email.error"
84
+ "'%value%' appears to be an IP address, but IP addresses are not allowed", "hsapi.checkout.email.error"
85
+ "'%value%' appears to be a DNS hostname but cannot match TLD against known list", "hsapi.checkout.email.error"
86
+ "'%value%' appears to be a DNS hostname but contains a dash in an invalid position", "hsapi.checkout.email.error"
87
+ "'%value%' appears to be a DNS hostname but cannot match against hostname schema for TLD '%tld%'", "hsapi.checkout.email.error"
88
+ "'%value%' appears to be a DNS hostname but cannot extract TLD part", "hsapi.checkout.email.error"
89
+ "'%value%' does not appear to be a valid local network name", "hsapi.checkout.email.error"
90
+ "'%value%' appears to be a local network name but local network names are not allowed", "hsapi.checkout.email.error"
91
+ "'%value%' appears to be a DNS hostname but the given punycode notation cannot be decoded", "hsapi.checkout.email.error"
92
+
93
+
app/locale/nl_NL/Highstreet_Hsapi.csv ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "Welcome", "Welkom"
2
+ "Billing address", "Factuuradres"
3
+ "Shipment", "Verzending"
4
+ "Payment", "Betaalwijze"
5
+ "Confirmation", "Bevestiging"
6
+ "Log out", "Log uit"
7
+ "Confirm and pay", "Bevestig en betaal"
8
+ "Your order is not final yet", "Je bestelling is nog niet definitief"
9
+ "You will continue to our secure payment page", "Je gaat door naar onze beveiligde betaalomgeving"
10
+ "Hello!", "Hallo!"
11
+ "Checkout as guest", "Reken af als gast"
12
+ "Log in", "Log in"
13
+ "hsapi_Register", "Registreer"
14
+ "Sign In", "Sign In"
15
+ "E-mail address", "E-mailadres"
16
+ "ipad_Password", "Wachtwoord"
17
+ "Forgot your password?", "Wachtwoord vergeten?"
18
+ "Send", "Verzenden"
19
+ "We have sent you an e-mail with which you can change your password.", "Er is een e-mail naar het door u opgegeven adres gestuurd waarmee u uw wachtwoord kunt veranderen."
20
+ "Create an account now", "Maak een account aan"
21
+ "and checkout faster next time", "en bestel de volgende keer makkelijker"
22
+ "Please enter a valid e-mailaddress", "Vul een geldig e-mail adres in"
23
+ "Please enter a valid password", "Vul een geldig wachtwoord in"
24
+ "Repeat the password", "Herhaal wachtwoord"
25
+ "First Name", "Voornaam"
26
+ "Last Name", "Achternaam"
27
+ "Please enter a valid phone number.", "Vul een geldig telefoonnummer in"
28
+ "Phone number", "Telefoonnummer"
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.", "We vragen om een telefoonnummer zodat we je indien nodig kunnen bellen over de bestelling. We zullen je telefoonnummer nooit gebruiken voor andere doeleinden."
30
+ "Street", "Straat"
31
+ "House number", "Huisnummer"
32
+ "Zip code", "Postcode"
33
+ "City", "Plaats"
34
+ "Something went wrong", "Er is iets mis gegaan"
35
+ "Please try again", "Probeer het opnieuw"
36
+ "How do you want to pay for your order?", "Hoe wil je je bestelling<br />betalen?"
37
+ "Please check your order.", "Klopt jouw bestelling?"
38
+ "Please check the order overview. If this is correct, you can place your order.", "Hier zie je een overzicht van de bestelling. Als dit helemaal klopt kun je de bestelling plaatsen."
39
+ "How do you want to receive your order?", "Hoe wil je je bestelling<br />ontvangen?"
40
+ "Invoice address / delivery address", "Factuuradres/Bezorgadres"
41
+ "Edit", "Wijzig"
42
+ "Delivery method", "Verzendoptie"
43
+ "Payment method", "Betaalwijze"
44
+ "You can continue to the next step when you have entered all fields", "Je kunt pas naar de volgende stap als je alle velden volledig hebt ingevuld"
45
+ "There has been an unknown error. Please try again later again later.", "Er heeft zich een onbekende fout voorgedaan. Probeer het later nog eens."
46
+ "Thanks for shopping at ", "Leuk dat je bij "
47
+ ".<span class='hidden'>_ORDER_TEXT</span>", " bestelt."
48
+ "When you tap 'Confirm and pay', you agree with ", "Als je op `Bevestig en betaal` tapt, ga je akkoord met "
49
+ "the general terms", "de algemene voorwaarden"
50
+ " of ", " van "
51
+ "Ship to the same address", "Bestelling verzenden naar hetzelfde adres"
52
+ "Invoice address", "Factuuradres"
53
+ "Delivery address", "Bezorgadres"
54
+ "Please make sure your passwords match.", "De ingevoerde wachtwoorden komen niet met elkaar overeen."
55
+ "Something went wrong while trying to get your address trough your zip code and house number. Please fill in your street and city manually.", "Er is iets fout gegaan met het ophalen van het adres via postcode en huisnummer. Vul het adres handmatig in."
56
+ "Street name is being retrieved", "Straatnaam wordt opgehaald"
57
+ "City name is being retrieved", "Plaatsnaam wordt opgehaald"
58
+
59
+ "hsapi.index.couponTip", "Heb je een tegoedbon?<br />Die kun je straks in de stap<br />'betaalwijze' invoeren."
60
+
61
+ "hsapi.addCouponAction.error.fatal", "Er heeft zich een onbekende fout voorgedaan, probleer het later nog eens."
62
+ "hsapi.addCouponAction.error.invalid", "De tegoedbon die je ingevoerd hebt is niet geldig."
63
+ "hsapi.addCouponAction.error.length", "De tegoedbon die je hebt ingevoerd is te lang, de maximale lengte is "
64
+ "hsapi.addCouponAction.success", "Tegoedbon '{coupon_code}' is succesvol toegepast. In je bestellijst kun je je korting bekijken."
65
+ "hsapi.addCouponAction.success.removed", "Tegoedbon '{coupon_code}' is succesvol verwijderd."
66
+
67
+ "hsapi.checkout.coupon.addTitle", "Voeg <u>tegoedbon</u> toe"
68
+ "hsapi.checkout.coupon.form.title", "Vul hier de code van je tegoedbon in."
69
+ "hsapi.checkout.coupon.form.code", "Tegoedbon"
70
+ "hsapi.checkout.coupon.form.add", "Voeg toe"
71
+ "hsapi.checkout.coupon.form.remove", "Verwijder"
72
+ "hsapi.checkout.coupon.paymentTitle", "Maak hier een keuze uit de volgende betaalopties"
73
+
74
+ "hsapi.checkout.payment.acceptAfterpayTermsText", "Je moet akkoord gaan met de betalingsvoorwaarden van AfterPay om gebruik te maken van deze betaalmethode"
75
+
76
+ "hsapi.checkout.billing.error.email", "Vul een geldig e-mailadres in"
77
+
78
+
79
+ "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.", "Het gebruikte e-mailadres is al gekoppeld aan een account. Log in met dit adres of gebruik een ander e-mailadres voor registratie."
80
+
81
+ '"%s" is not a valid email address.', "hsapi.checkout.email.error"
82
+ '"%s" is not a valid hostname.', "hsapi.checkout.email.error"
83
+ '"%s" exceeds the allowed length.', "hsapi.checkout.email.error"
84
+ "'%value%' appears to be an IP address, but IP addresses are not allowed", "hsapi.checkout.email.error"
85
+ "'%value%' appears to be a DNS hostname but cannot match TLD against known list", "hsapi.checkout.email.error"
86
+ "'%value%' appears to be a DNS hostname but contains a dash in an invalid position", "hsapi.checkout.email.error"
87
+ "'%value%' appears to be a DNS hostname but cannot match against hostname schema for TLD '%tld%'", "hsapi.checkout.email.error"
88
+ "'%value%' appears to be a DNS hostname but cannot extract TLD part", "hsapi.checkout.email.error"
89
+ "'%value%' does not appear to be a valid local network name", "hsapi.checkout.email.error"
90
+ "'%value%' appears to be a local network name but local network names are not allowed", "hsapi.checkout.email.error"
91
+ "'%value%' appears to be a DNS hostname but the given punycode notation cannot be decoded", "hsapi.checkout.email.error"
92
+
93
+
94
+
package.xml ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>Highstreet</name>
4
+ <version>1.0.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 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>- First public release of the Highstreet Magento 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>2014-08-08</date>
15
+ <time>08:09:01</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="Data.php" hash="94ba070797ec9925a331969b012be7f6"/><file name=".DS_Store" hash="194577a7e20bdcc7afbb718f502c134c"/></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="24942419f4fdd676ad8ca8b462c30a03"/><file name="SearchSuggestions.php" hash="17e0a008e2f90c0f5f6c5f7c3f010c9c"/><file name=".DS_Store" hash="194577a7e20bdcc7afbb718f502c134c"/></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="d35d719b384223addf201cf847ce6963"/><file name="config.xml" hash="b4f33b6ea6e8f24e7bacd85a48763cd4"/><file name=".DS_Store" hash="194577a7e20bdcc7afbb718f502c134c"/></dir><file name=".DS_Store" hash="bdce70b0d3d7ee6d49b036d8339f2acc"/></dir><file name=".DS_Store" hash="194577a7e20bdcc7afbb718f502c134c"/></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="1764bb5d414822ec79e1f39199f785b3"/></dir><dir name="nl_NL"><file name="Highstreet_Hsapi.csv" hash="4e3bab835cb6cd465bee52bebe393f67"/></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>