shoppingfeeder - Version 1.2.0

Version Notes

This version has a number of major updates, including:
- Reducing payload on unchanged products
- Enhanced support for configurable products, providing child products as variants
- Updated attribute support to enable attribute mapping in ShoppingFeeder

Features include:
- One-click feed export to numerous channels
- Built-in order tracking
- Click & referral tracking
- Historical order importing for reporting and ROI analysis

Download this release

Release Info

Developer ShoppingFeeder
Extension shoppingfeeder
Version 1.2.0
Comparing to
See all releases


Version 1.2.0

app/code/community/ShoppingFeeder/Service/Block/Adminhtml/Service.php ADDED
@@ -0,0 +1,34 @@
1
+ <?php
2
+ /**
3
+ * Admin grid container
4
+ *
5
+ * @author ShoppingFeeder
6
+ */
7
+ class ShoppingFeeder_Service_Block_Adminhtml_Service extends
8
+ Mage_adminhtml_Block_Widget_Grid_Container
9
+ {
10
+ /**
11
+ * Block constructor
12
+ */
13
+ public function __construct()
14
+ {
15
+ $this->_blockGroup = 'shoppingfeeder_service';
16
+ $this->_controller = 'adminhtml_service';
17
+ $this->_headerText = Mage::helper('shoppingfeeder_service')->__('Manage Service');
18
+ parent::__construct();
19
+ if (Mage::helper('shoppingfeeder_service/Admin')->isActionAllowed('save'))
20
+ {
21
+ $this->_updateButton('add', 'label',
22
+ Mage::helper('shoppingfeeder_service')->__('Add New News'));
23
+ } else {
24
+ $this->_removeButton('add');
25
+ }
26
+ $this->addButton(
27
+ 'news_flush_images_cache',
28
+ array(
29
+ 'label' => Mage::helper('shoppingfeeder_service')->__('Flush Images Cache'),
30
+ 'onclick' => 'setLocation(\'' . $this->getUrl('*/*/flush') . '\')',
31
+ )
32
+ );
33
+ }
34
+ }
app/code/community/ShoppingFeeder/Service/Block/Service.php ADDED
@@ -0,0 +1,33 @@
1
+ <?php
2
+ /**
3
+ *
4
+ * ShoppingFeeder XML generation code
5
+ *
6
+ * @category ShoppingFeeder
7
+ * @package ShoppingFeeder_Service
8
+ * @copyright Copyright (c) 2014 Kevin Tucker (http://www.shoppingfeeder.com)
9
+ *
10
+ */
11
+
12
+ class ShoppingFeeder_Service_Block_Service extends Mage_Core_Block_Template
13
+ {
14
+
15
+ /**
16
+ * Block constructor
17
+ */
18
+ public function __construct()
19
+ {
20
+ parent::__construct();
21
+ }
22
+
23
+ /**
24
+ * Get the offers
25
+ *
26
+ * @return collection|null
27
+ */
28
+ public function getOffers()
29
+ {
30
+ return $this->_getData('offers');
31
+ }
32
+
33
+ }
app/code/community/ShoppingFeeder/Service/Controller/FrontAuth.php ADDED
@@ -0,0 +1,56 @@
1
+ <?php
2
+
3
+ class ShoppingFeeder_Service_Controller_FrontAuth extends Mage_Core_Controller_Front_Action
4
+ {
5
+ public function preDispatch()
6
+ {
7
+ parent::preDispatch();
8
+
9
+ //DEBUG
10
+ // return $this;
11
+
12
+ //check Auth
13
+ if (!function_exists('getallheaders'))
14
+ {
15
+ function getallheaders()
16
+ {
17
+ $headers = '';
18
+ foreach ($_SERVER as $name => $value)
19
+ {
20
+ if (substr($name, 0, 5) == 'HTTP_')
21
+ {
22
+ $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
23
+ }
24
+ }
25
+ return $headers;
26
+ }
27
+ }
28
+
29
+ $headers = getallheaders();
30
+
31
+ /** @var ShoppingFeeder_Service_Model_Auth $authModel */
32
+ $authModel = Mage::getModel('shoppingfeeder_service/auth');
33
+
34
+ $authResult = $authModel->auth(
35
+ $headers,
36
+ $this->getRequest()->getScheme(),
37
+ $this->getRequest()->getMethod()
38
+ );
39
+
40
+ if ($authResult !== true)
41
+ {
42
+ $responseData = array(
43
+ 'status' => 'fail',
44
+ 'data' => array (
45
+ 'message' => $authResult
46
+ )
47
+ );
48
+
49
+ header('Content-type: application/json; charset=UTF-8');
50
+ echo json_encode($responseData);
51
+ exit();
52
+ }
53
+
54
+ return $this;
55
+ }
56
+ }
app/code/community/ShoppingFeeder/Service/Helper/Data.php ADDED
@@ -0,0 +1,10 @@
1
+ <?php
2
+ /**
3
+ * Service Data Helper
4
+ *
5
+ * @author ShoppingFeeder
6
+ */
7
+ class ShoppingFeeder_Service_Helper_Data extends Mage_Core_Helper_Data
8
+ {
9
+
10
+ }
app/code/community/ShoppingFeeder/Service/Model/Auth.php ADDED
@@ -0,0 +1,103 @@
1
+ <?php
2
+
3
+ class ShoppingFeeder_Service_Model_Auth extends Mage_Core_Model_Abstract
4
+ {
5
+ const RANDOM_STRING = 'The mice all ate cheese together.';
6
+ const MAX_TIMEOUT = 300; //5 minutes
7
+
8
+ public function __construct()
9
+ {
10
+ $this->_init('shoppingfeeder_service/auth');
11
+ }
12
+
13
+ public static function auth(array $headers, $incomingScheme, $incomingMethod)
14
+ {
15
+ //check the API key
16
+ $incomingApiKey = '';
17
+ $incomingAuthTimestamp = strtotime('1970-00-00');
18
+ $incomingSignature = '';
19
+ foreach ($headers as $key => $value)
20
+ {
21
+ if (strtolower('X-SFApiKey') == strtolower($key))
22
+ {
23
+ $incomingApiKey = $value;
24
+ }
25
+ if (strtolower('X-SFTimestamp') == strtolower($key))
26
+ {
27
+ $incomingAuthTimestamp = $value;
28
+ }
29
+ if (strtolower('X-SFSignature') == strtolower($key))
30
+ {
31
+ $incomingSignature = $value;
32
+ }
33
+ }
34
+
35
+ //$sslInFront = Mage::getStoreConfig('web/secure/use_in_frontend');
36
+ //$useSsl = ($sslInFront == null) ? false : boolval($sslInFront);
37
+ $useSsl = false;
38
+
39
+ $apiKeys = self::getApiKeys();
40
+
41
+ if (!$useSsl || ($useSsl && $incomingScheme == 'https'))
42
+ {
43
+ //check the timestamp
44
+ if (time() - $incomingAuthTimestamp <= self::MAX_TIMEOUT)
45
+ {
46
+ $localApiKey = $apiKeys['api_key'];
47
+ if ($localApiKey == $incomingApiKey)
48
+ {
49
+ $localApiSecret = $apiKeys['api_secret'];
50
+
51
+ $stringToSign = $incomingMethod . "\n" .
52
+ $incomingAuthTimestamp . "\n" .
53
+ self::RANDOM_STRING;
54
+
55
+ if (function_exists('hash_hmac'))
56
+ {
57
+ $signature = hash_hmac('sha256', $stringToSign, $localApiSecret);
58
+ }
59
+ elseif (function_exists('mhash'))
60
+ {
61
+ $signature = bin2hex(mhash(MHASH_SHA256, $stringToSign, $localApiSecret));
62
+ }
63
+ else
64
+ {
65
+ return 'Authentication failed: Appropriate hashing function does not exist.';
66
+ }
67
+
68
+ if ($incomingSignature == $signature)
69
+ {
70
+ return true;
71
+ }
72
+ else
73
+ {
74
+ return 'Authentication failed: invalid credentials.';
75
+ }
76
+ }
77
+ else
78
+ {
79
+ return 'Authentication failed: invalid API key.';
80
+ }
81
+ }
82
+ else
83
+ {
84
+ return 'Authentication failed: timeout exceeded.';
85
+ }
86
+ }
87
+ else
88
+ {
89
+ return 'Authentication failed: non-secure connection';
90
+ }
91
+ }
92
+
93
+ public static function getApiKeys()
94
+ {
95
+ $localApiKey = Mage::getStoreConfig('shoppingfeeder/service/apikey');
96
+ $localApiSecret = Mage::getStoreConfig('shoppingfeeder/service/apisecret');
97
+
98
+ return array(
99
+ 'api_key' => $localApiKey,
100
+ 'api_secret' => $localApiSecret
101
+ );
102
+ }
103
+ }
app/code/community/ShoppingFeeder/Service/Model/Observer.php ADDED
@@ -0,0 +1,67 @@
1
+ <?php
2
+ class ShoppingFeeder_Service_Model_Observer extends Varien_Object
3
+ {
4
+ const SF_URL = 'https://www.shoppingfeeder.com/webhook/magento-orders/';
5
+ //const SF_URL = 'http://dev.shoppingfeeder.com/webhook/magento-orders/';
6
+
7
+ public function salesOrderPlaceAfter($observer)
8
+ {
9
+ try{
10
+ //if order tracking is set
11
+ //$sfEnabled = Mage::getStoreConfig('shoppingfeeder/service/enable');
12
+ $sfEnabled = true;
13
+ $sfTracking = Mage::getStoreConfig('shoppingfeeder/service/tracking');
14
+ if ($sfEnabled && $sfTracking)
15
+ {
16
+ /* @var Mage_Sales_Model_Order $order */
17
+ $order = $observer->getEvent()->getOrder();
18
+
19
+ Mage::log('salesOrderPlaceAfter Order ID: '.$order->getRealOrderId());
20
+
21
+ $this->_notifyShoppingFeeder($order);
22
+ }
23
+ }
24
+ catch (Exception $e)
25
+ {
26
+ Mage::log('_notifyShoppingFeeder Order ID: '.$order->getRealOrderId().' FAILED');
27
+ }
28
+ }
29
+
30
+ public function productView($observer)
31
+ {
32
+ try{
33
+ //here we're going to create the referral cookie for the visitor if they came from ShoppingFeeder
34
+ if (isset($_GET['SFDRREF']))
35
+ {
36
+ setcookie('SFDRREF', $_GET['SFDRREF'], time() + (60*60*24*30), '/');
37
+ $_COOKIE['SFDRREF']= $_GET['SFDRREF'];
38
+ }
39
+ }
40
+ catch (Exception $e)
41
+ {
42
+
43
+ }
44
+ }
45
+
46
+ protected function _notifyShoppingFeeder(Mage_Sales_Model_Order $order)
47
+ {
48
+ Mage::log('_notifyShoppingFeeder Order ID: '.$order->getRealOrderId());
49
+
50
+ //get API key value from admin settings
51
+ $apiKey = $sfTracking = Mage::getStoreConfig('shoppingfeeder/service/apikey');
52
+
53
+ $http = new Zend_Http_Client(self::SF_URL);
54
+
55
+ $http->setHeaders('X-SFApiKey', $apiKey);
56
+
57
+ $data = $order->toArray();
58
+ foreach ($order->getAllItems() as $lineItem)
59
+ {
60
+ $data['line_items'][] = $lineItem->toArray();
61
+ }
62
+ $data['landing_site_ref'] = isset($_COOKIE['SFDRREF']) ? $_COOKIE['SFDRREF'] : '';
63
+
64
+ $http->setRawData(Mage::helper('core')->jsonEncode($data));
65
+ $http->request(Zend_Http_Client::POST);
66
+ }
67
+ }
app/code/community/ShoppingFeeder/Service/Model/Offers.php ADDED
@@ -0,0 +1,366 @@
1
+ <?php
2
+
3
+ class ShoppingFeeder_Service_Model_Offers extends Mage_Core_Model_Abstract
4
+ {
5
+ public function __construct()
6
+ {
7
+ $this->_init('shoppingfeeder_service/offers');
8
+ }
9
+
10
+ private function hasParent($product)
11
+ {
12
+ $parents = Mage::getModel('catalog/product_type_configurable')->getParentIdsByChild($product->getId());
13
+ return !empty($parents);
14
+ }
15
+
16
+ private function getProductInfo(Mage_Catalog_Model_Product $product, Mage_Catalog_Model_Product $parent = null, $variantOptions = null, $lastUpdate = null)
17
+ {
18
+ /** @var Mage_Catalog_Model_Product_Type_Configurable $configModel */
19
+ $configModel = Mage::getModel('catalog/product_type_configurable');
20
+
21
+ $p = array();
22
+
23
+ $isVariant = !is_null($parent);
24
+
25
+ /**
26
+ * We only want to pull variants (children of configurable products) that are children, not as standalone products
27
+ */
28
+ //if this product's parent is visible in catalog and search, Mage_Catalog_Model_Product_Visibility::VISIBILITY_BOTH,
29
+ //we will find this product when we fetch all the children of this parent through a normal iteration, so return nothing
30
+ if (!$isVariant && $this->hasParent($product))
31
+ {
32
+ return array();
33
+ }
34
+
35
+ if ($isVariant)
36
+ {
37
+ $variant = $product;
38
+ $product = $parent;
39
+ }
40
+
41
+ $data = $product->getData();
42
+
43
+ /* @var Mage_CatalogInventory_Model_Stock_Item $stockItem */
44
+ $stockItem = Mage::getModel('cataloginventory/stock_item')->loadByProduct($product);
45
+
46
+ $attributes = $product->getAttributes();
47
+ $manufacturer = '';
48
+ $brand = '';
49
+
50
+ $usefulAttributes = array();
51
+
52
+ /**
53
+ * @var Mage_Eav_Model_Entity_Attribute_Abstract $attribute
54
+ */
55
+ // var_dump("");
56
+ // var_dump("");
57
+ // var_dump("");
58
+ foreach ($attributes as $attribute)
59
+ {
60
+ $attributeCode = $attribute->getAttributeCode();
61
+ $attributeLabel = $attribute->getData('frontend_label');
62
+ $value = $attribute->getFrontend()->getValue($product);
63
+
64
+ // var_dump($attributeCode. ' : '.print_r($value, true));
65
+ // var_dump($attributeLabel. ' : '.print_r($value, true));
66
+
67
+ if (preg_match('/^manufacturer#x2F;i', $attributeCode) || preg_match('/^manufacturer#x2F;i', $attributeLabel))
68
+ {
69
+ $manufacturer = $value;
70
+ }
71
+
72
+ if (preg_match('/^brand#x2F;i', $attributeCode) || preg_match('/^brand#x2F;i', $attributeLabel))
73
+ {
74
+ $brand = $value;
75
+ }
76
+
77
+ /*
78
+ if (preg_match('/age/i', $attributeCode) || preg_match('/age/i', $attributeLabel))
79
+ {
80
+ $usefulAttributes['age'] = $value;
81
+ }
82
+ if (preg_match('/color|colour/i', $attributeCode) || preg_match('/color|colour/i', $attributeLabel))
83
+ {
84
+ $usefulAttributes['colour'] = $value;
85
+ }
86
+ if (preg_match('/size/i', $attributeCode) || preg_match('/size/i', $attributeLabel))
87
+ {
88
+ $usefulAttributes['size'] = $value;
89
+ }
90
+ if (preg_match('/gender|sex/i', $attributeCode) || preg_match('/gender|sex/i', $attributeLabel))
91
+ {
92
+ $usefulAttributes['gender'] = $value;
93
+ }
94
+ if (preg_match('/material/i', $attributeCode) || preg_match('/material/i', $attributeLabel))
95
+ {
96
+ $usefulAttributes['material'] = $value;
97
+ }
98
+ if (preg_match('/pattern/i', $attributeCode) || preg_match('/pattern/i', $attributeLabel))
99
+ {
100
+ $usefulAttributes['pattern'] = $value;
101
+ }
102
+ */
103
+ $usefulAttributes[$attributeCode] = $value;
104
+ }
105
+ // exit();
106
+
107
+ //category path
108
+ $categories = $product->getCategoryIds();
109
+
110
+ $categoryPathsToEvaluate = array();
111
+ $maxDepth = 0;
112
+ $categoryPathToUse = '';
113
+
114
+ if (!empty($categories))
115
+ {
116
+ //we will get all the category paths and then use the most refined, deepest one
117
+ foreach ($categories as $rootCategoryId)
118
+ {
119
+ $depth = 0;
120
+ $category_path = '';
121
+
122
+ $mageCategoryPath = Mage::getModel('catalog/category')->load($rootCategoryId)->getPath();
123
+ $allCategoryIds = explode('/', $mageCategoryPath);
124
+ unset($allCategoryIds[0]);
125
+
126
+ $categoryPath = '';
127
+ /**
128
+ * @var Mage_Catalog_Model_Category $category
129
+ */
130
+ foreach ($allCategoryIds as $categoryId)
131
+ {
132
+ $depth++;
133
+ $category = Mage::getModel('catalog/category')->load($categoryId);
134
+ $category_name = $category->getName();
135
+ if ($category_name != 'Root Catalog' && $category_name != 'Default Category')
136
+ {
137
+ if (!empty($categoryPath))
138
+ {
139
+ $categoryPath.= ' > ';
140
+ }
141
+ $categoryPath.= $category_name;
142
+ }
143
+ }
144
+
145
+ $categoryPathsToEvaluate[$rootCategoryId]['path'] = $categoryPath;
146
+ $categoryPathsToEvaluate[$rootCategoryId]['depth'] = $depth;
147
+
148
+ if ($maxDepth < $depth)
149
+ {
150
+ $maxDepth = $depth;
151
+ $categoryPathToUse = $categoryPath;
152
+ }
153
+ }
154
+ }
155
+
156
+ if ($isVariant && isset($variant))
157
+ {
158
+ $p['internal_variant_id'] = $variant->getId();
159
+
160
+ $variantOptionsTitle = array();
161
+ $variantPrice = $variantOptions['basePrice'];
162
+
163
+ $urlHashParts = array();
164
+
165
+ // Collect options applicable to the configurable product
166
+ foreach ($variantOptions['refactoredOptions'][$variant->getId()] as $attributeCode => $option) {
167
+ $variantOptionsTitle[] = $option['value'];
168
+
169
+ //add these configured attributes to the set of parent's attributes
170
+ $usefulAttributes[$attributeCode] = $option['value'];
171
+
172
+ $variantPrice += $option['price'];
173
+
174
+ $urlHashParts[] = $option['attributeId'].'='.$option['valueId'];
175
+ }
176
+
177
+ $variantOptionsTitle = implode(' / ', $variantOptionsTitle);
178
+ $title = $data['name'] . ' - ' . $variantOptionsTitle;
179
+ $sku = $variant->getData('sku');
180
+ $price = $variantPrice;
181
+ $imageUrl = $p['image_url'] = Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_MEDIA).
182
+ 'catalog/product'.$variant->getImage();
183
+ $productUrl = $product->getProductUrl().'#'.implode('&', $urlHashParts);
184
+
185
+ // var_dump($variantOptionsTitle);
186
+ // var_dump($variantPrice);
187
+ // exit();
188
+ }
189
+ else
190
+ {
191
+ $p['internal_variant_id'] = '';
192
+ $title = $data['name'];
193
+ $sku = $data['sku'];
194
+ $price = $product->getPrice();
195
+ $imageUrl = $p['image_url'] = Mage::getBaseUrl(Mage_Core_Model_Store::URL_TYPE_MEDIA).
196
+ 'catalog/product'.$product->getImage();
197
+ $productUrl = $product->getProductUrl();
198
+ }
199
+
200
+ //if we have previously captured this product and it hasn't changed, don't send through full payload
201
+ $wasPreviouslyCaptured = !is_null($lastUpdate) && isset($usefulAttributes['updated_at']) && strtotime($usefulAttributes['updated_at']) < $lastUpdate;
202
+ if ($wasPreviouslyCaptured)
203
+ {
204
+ $p['internal_id'] = $product->getId();
205
+ $p['internal_update_time'] = $usefulAttributes['updated_at'];
206
+ }
207
+ else
208
+ {
209
+ $p['category'] = $categoryPathToUse;
210
+ $p['title'] = $title;
211
+ $p['brand'] = ($brand=='No') ? (($manufacturer == 'No') ? '' : $manufacturer) : $brand;
212
+ $p['manufacturer'] = ($manufacturer=='No') ? $brand : $manufacturer;
213
+ $p['mpn'] = isset($data['model']) ? $data['model'] : $data['sku'];
214
+ $p['internal_id'] = $product->getId();
215
+ $p['description'] = $data['description'];
216
+ $p['short_description'] = $data['short_description'];
217
+ $p['weight'] = isset($data['weight']) ? $data['weight'] : 0.00;
218
+ $p['sku'] = $sku;
219
+ $p['gtin'] = '';
220
+
221
+ //$priceModel = $product->getPriceModel();
222
+
223
+ $p['price'] = $price;// Mage::helper('checkout')->convertPrice($priceModel->getPrice($product), false);
224
+ $salePrice = $product->getSpecialPrice();// Mage::helper('checkout')->convertPrice($priceModel->getFinalPrice(null, $product), false);
225
+ $p['sale_price'] = '';
226
+ $p['sale_price_effective_date'] = '';
227
+ if ($salePrice != $p['price'])
228
+ {
229
+ $p['sale_price'] = $salePrice;
230
+ if ($product->getSpecialFromDate()!=null && $product->getSpecialToDate()!=null)
231
+ {
232
+ $p['sale_price_effective_date'] = date("c", strtotime($product->getSpecialFromDate())).'/'.date("c", strtotime($product->getSpecialToDate()));
233
+ }
234
+ }
235
+
236
+ $p['delivery_cost'] = 0.00;
237
+ $p['tax'] = 0.00;
238
+ $p['url'] = $productUrl;
239
+ $p['internal_update_time'] = isset($usefulAttributes['updated_at']) ? $usefulAttributes['updated_at'] : '';
240
+
241
+ $p['image_url'] = $imageUrl;
242
+ $p['image_modified_time'] = date("c", filemtime($product->getMediaConfig()->getMediaPath($product->getImage())));
243
+ $p['availability'] = ($stockItem->getIsInStock())?'in stock':'out of stock';
244
+ $p['quantity'] = $stockItem->getQty();
245
+ $p['condition'] = '';
246
+ $p['availability_date'] = '';
247
+ $p['attributes'] = $usefulAttributes;
248
+ $imageGallery = array();
249
+ foreach ($product->getMediaGalleryImages() as $image)
250
+ {
251
+ $galleryImage = array();
252
+ $galleryImage['url'] = $image['url'];
253
+ $galleryImage['image_modified_time'] = date("c", filemtime($image['path']));
254
+ $imageGallery[] = $galleryImage;
255
+ }
256
+ $p['extra_images'] = $imageGallery;
257
+ }
258
+
259
+ return $p;
260
+ }
261
+
262
+ public function getItems($page = null, $numPerPage = 1000, $lastUpdate = null)
263
+ {
264
+ /* @var Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Collection $collection */
265
+ $collection = Mage::getModel('catalog/product')->getCollection()
266
+ ->addAttributeToSelect('*')
267
+ ->addAttributeToFilter('status', Mage_Catalog_Model_Product_Status::STATUS_ENABLED)
268
+ ->addAttributeToFilter('visibility', Mage_Catalog_Model_Product_Visibility::VISIBILITY_BOTH);
269
+
270
+ if (!is_null($page))
271
+ {
272
+ $offset = ($page * $numPerPage) - $numPerPage;
273
+ $productIds = $collection->getAllIds($numPerPage, $offset);
274
+ }
275
+ else
276
+ {
277
+ $productIds = $collection->getAllIds();
278
+ }
279
+
280
+ $products = array();
281
+ foreach ($productIds as $productId)
282
+ {
283
+ Mage::getModel('catalog/product')->reset();
284
+ $product = Mage::getModel('catalog/product')->load($productId);
285
+
286
+ /**
287
+ * Get variants, if there are any
288
+ * If there are variants that are visible in the catalog, we will skip them when we iterate normally
289
+ */
290
+
291
+ //if we have a configurable product, capture the variants
292
+ if ($product->getTypeId() == 'configurable')
293
+ {
294
+ /** @var Mage_Catalog_Model_Product_Type_Configurable $configModel */
295
+ $configModel = Mage::getModel('catalog/product_type_configurable');
296
+
297
+ $variants = array_pop($configModel->getChildrenIds($product->getId()));
298
+
299
+ if (count($variants) > 0)
300
+ {
301
+ $parent = $product;
302
+
303
+ //get variant options
304
+ $layout = Mage::getSingleton('core/layout');
305
+ $block = $layout->createBlock('catalog/product_view_type_configurable');
306
+ $block->setProduct($parent);
307
+ $variantOptions = Mage::helper('core')->jsonDecode($block->getJsonConfig());
308
+
309
+ $variantAttributes = array();
310
+ foreach ($variantOptions['attributes'] as $attributeId => $options)
311
+ {
312
+ $code = $options['code'];
313
+ foreach ($options['options'] as $option)
314
+ {
315
+ $value = $option['label'];
316
+ $price = $option['price'];
317
+ $valueId = $option['id'];
318
+ foreach ($option['products'] as $productId)
319
+ {
320
+ $variantAttributes[$productId][$code]['value'] = $value;
321
+ $variantAttributes[$productId][$code]['price'] = $price;
322
+ $variantAttributes[$productId][$code]['valueId'] = $valueId;
323
+ $variantAttributes[$productId][$code]['attributeId'] = $attributeId;
324
+ }
325
+ }
326
+ }
327
+ $variantOptions['refactoredOptions'] = $variantAttributes;
328
+
329
+
330
+ foreach ($variants as $variantId)
331
+ {
332
+ /** @var Mage_Catalog_Model_Product $variant */
333
+ $variant = Mage::getModel('catalog/product')->load($variantId);
334
+
335
+ $productData = $this->getProductInfo($variant, $parent, $variantOptions, $lastUpdate);
336
+ if (!empty($productData))
337
+ {
338
+ $products[] = $productData;
339
+ }
340
+ }
341
+ }
342
+ }
343
+ else
344
+ {
345
+ $productData = $this->getProductInfo($product, null, null, $lastUpdate);
346
+ if (!empty($productData))
347
+ {
348
+ $products[] = $productData;
349
+ }
350
+ }
351
+ }
352
+
353
+ return $products;
354
+ }
355
+
356
+ public function getItem($itemId)
357
+ {
358
+ $products = array();
359
+
360
+ $product = Mage::getModel('catalog/product')->load($itemId);
361
+
362
+ $products[] = $this->getProductInfo($product);
363
+
364
+ return $products;
365
+ }
366
+ }
app/code/community/ShoppingFeeder/Service/Model/Orders.php ADDED
@@ -0,0 +1,75 @@
1
+ <?php
2
+
3
+ class ShoppingFeeder_Service_Model_Orders extends Mage_Core_Model_Abstract
4
+ {
5
+ public function __construct()
6
+ {
7
+ $this->_init('shoppingfeeder_service/orders');
8
+ }
9
+
10
+ private function getOrderInfo(Mage_Sales_Model_Order $order)
11
+ {
12
+ $data = array();
13
+
14
+ $orderData = $order->getData();
15
+
16
+ //normalise order data for ShoppingFeeder
17
+ $data['order_id'] = $orderData['entity_id'];
18
+ $data['order_date'] = $orderData['created_at'];
19
+ $data['store_order_number'] = $orderData['increment_id'];
20
+ $data['store_order_user_id'] = $orderData['customer_id'];
21
+ $data['store_order_currency'] = $orderData['order_currency_code'];
22
+ $data['store_total_price'] = $orderData['grand_total'];
23
+ $data['store_total_line_items_price'] = $orderData['subtotal_incl_tax'];
24
+ $data['store_total_tax'] = $orderData['tax_amount'];
25
+ $data['store_order_total_discount'] = $orderData['discount_amount'];
26
+ $data['store_shipping_price'] = $orderData['shipping_incl_tax'];
27
+
28
+ $lineItems = array();
29
+ foreach($order->getAllItems() as $item)
30
+ {
31
+ $lineItems[] = $item->toArray();
32
+ }
33
+ $data['line_items'] = $lineItems;
34
+
35
+ return $data;
36
+ }
37
+
38
+ public function getItems($page = null, $numPerPage = 1000)
39
+ {
40
+ /* @var Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Collection $collection */
41
+ $collection = Mage::getModel('sales/order')->getCollection()
42
+ ->addAttributeToSelect('*')
43
+ ->addAttributeToFilter('status', Mage_Sales_Model_Order::STATE_COMPLETE);
44
+
45
+ if (!is_null($page))
46
+ {
47
+ $offset = ($page * $numPerPage) - $numPerPage;
48
+ $orderIds = $collection->getAllIds($numPerPage, $offset);
49
+ }
50
+ else
51
+ {
52
+ $orderIds = $collection->getAllIds();
53
+ }
54
+
55
+ $orders = array();
56
+ /* @var Mage_Sales_Model_Order $order */
57
+ foreach ($orderIds as $orderId)
58
+ {
59
+ $order = Mage::getModel('sales/order')->load($orderId);
60
+
61
+ $orders[$order->getId()] = $this->getOrderInfo($order);
62
+ }
63
+ return $orders;
64
+ }
65
+
66
+ public function getItem($itemId)
67
+ {
68
+ $orders = array();
69
+
70
+ $order = Mage::getModel('sales/order')->load($itemId);
71
+ $orders[$order->getId()] = $this->getOrderInfo($order);
72
+
73
+ return $orders;
74
+ }
75
+ }
app/code/community/ShoppingFeeder/Service/controllers/AttributesController.php ADDED
@@ -0,0 +1,31 @@
1
+ <?php
2
+
3
+ //IMPORTANT - "Controller" directory is not autoloaded
4
+ require_once(Mage::getModuleDir('Controller','ShoppingFeeder_Service').DS.'Controller'.DS.'FrontAuth.php');
5
+
6
+ class ShoppingFeeder_Service_AttributesController extends ShoppingFeeder_Service_Controller_FrontAuth
7
+ {
8
+ public function indexAction()
9
+ {
10
+ set_time_limit(0);
11
+
12
+ $internalAttributes = Mage::getResourceModel('catalog/product_attribute_collection')
13
+ ->getItems();
14
+
15
+ $attributes = array();
16
+ foreach ($internalAttributes as $attribute){
17
+ $attributes[$attribute->getAttributecode()] = $attribute->getFrontendLabel();
18
+ }
19
+
20
+ $responseData = array(
21
+ 'status' => 'success',
22
+ 'data' => array(
23
+ 'attributes' => $attributes
24
+ )
25
+ );
26
+
27
+ header('Content-type: application/json; charset=UTF-8');
28
+ echo json_encode($responseData);
29
+ exit();
30
+ }
31
+ }
app/code/community/ShoppingFeeder/Service/controllers/FeedController.php ADDED
@@ -0,0 +1,98 @@
1
+ <?php
2
+
3
+ //IMPORTANT - "Controller" directory is not autoloaded
4
+ require_once(Mage::getModuleDir('Controller','ShoppingFeeder_Service').DS.'Controller'.DS.'FrontAuth.php');
5
+
6
+ class ShoppingFeeder_Service_FeedController extends ShoppingFeeder_Service_Controller_FrontAuth
7
+ {
8
+ public function indexAction()
9
+ {
10
+ set_time_limit(0);
11
+
12
+ /** @var ShoppingFeeder_Service_Model_Offers $offersModel */
13
+ $offersModel = Mage::getSingleton('shoppingfeeder_service/offers');
14
+
15
+ $page = $this->getRequest()->getParam('page', null);
16
+ $numPerPage = $this->getRequest()->getParam('num_per_page', 1000);
17
+ $offerId = $this->getRequest()->getParam('offer_id', null);
18
+ $lastUpdate = $this->getRequest()->getParam('last_update', null);
19
+
20
+ if (is_null($offerId))
21
+ {
22
+ $offers = $offersModel->getItems($page, $numPerPage, $lastUpdate);
23
+ }
24
+ else
25
+ {
26
+ $offers = $offersModel->getItem($offerId);
27
+ }
28
+
29
+ $responseData = array(
30
+ 'status' => 'success',
31
+ 'data' => array(
32
+ 'page' => $page,
33
+ 'num_per_page' => $numPerPage,
34
+ 'offers' => $offers
35
+ )
36
+ );
37
+
38
+ header('Content-type: application/json; charset=UTF-8');
39
+ echo json_encode($responseData);
40
+ exit();
41
+ }
42
+
43
+ /** Old XML version */
44
+ /*
45
+ public function indexAction()
46
+ {
47
+ set_time_limit(0);
48
+
49
+ $offersModel = Mage::getSingleton('shoppingfeeder_service/offers');
50
+
51
+ $page = $this->getRequest()->getParam('page', null);
52
+ $numPerPage = $this->getRequest()->getParam('num_per_page', 1000);
53
+
54
+ $offers = $offersModel->getItems($page, $numPerPage);
55
+ header('Content-type: text/xml; charset=UTF-8');
56
+ echo '<?xml version="1.0" encoding="UTF-8"?>';
57
+ echo "\n".'<offers>';
58
+ foreach ($offers as $offer) {
59
+ echo "\n".'<offer>';
60
+ echo "\n".'<category><![CDATA['.$offer['category'].']]></category>';
61
+ echo "\n".'<title><![CDATA['.$offer['title'].']]></title>';
62
+ echo "\n".'<brand><![CDATA['.$offer['manufacturer'].']]></brand>';
63
+ echo "\n".'<manufacturer><![CDATA['.$offer['manufacturer'].']]></manufacturer>';
64
+ echo "\n".'<mpn><![CDATA['.$offer['mpn'].']]></mpn>';
65
+ echo "\n".'<sku><![CDATA['.$offer['sku'].']]></sku>';
66
+ echo "\n".'<gtin>'.$offer['gtin'].'</gtin>';
67
+ echo "\n".'<weight>'.$offer['weight'].'</weight>';
68
+ echo "\n".'<internal_id><![CDATA['.$offer['internal_id'].']]></internal_id>';
69
+ echo "\n".'<internal_variant_id><![CDATA['.$offer['internal_variant_id'].']]></internal_variant_id>';
70
+ echo "\n".'<description><![CDATA['.$offer['description'].']]></description>';
71
+ echo "\n".'<price>'.$offer['price'].'</price>';
72
+ echo "\n".'<sale_price>'.$offer['sale_price'].'</sale_price>';
73
+ echo "\n".'<sale_price_effective_date>'.$offer['sale_price_effective_date'].'</sale_price_effective_date>';
74
+ echo "\n".'<delivery_cost>'.$offer['delivery_cost'].'</delivery_cost>';
75
+ echo "\n".'<tax>'.$offer['tax'].'</tax>';
76
+ echo "\n".'<url><![CDATA['.$offer['url'].']]></url>';
77
+ echo "\n".'<image_url><![CDATA['.$offer['image_url'].']]></image_url>';
78
+ echo "\n".'<quantity>'.$offer['quantity'].'</quantity>';
79
+ echo "\n".'<condition>'.$offer['condition'].'</condition>';
80
+ echo "\n".'<availability>'.$offer['availability'].'</availability>';
81
+ echo "\n".'<availability_date>'.$offer['availability_date'].'</availability_date>';
82
+ foreach ($offer['attributes'] as $attributeName => $attributeValue) {
83
+ echo "\n".' <attribute name="'.htmlspecialchars($attributeName).'"><![CDATA['.$attributeValue.']]></attribute>';
84
+ }
85
+ foreach ($offer['extra_images'] as $imageUrl){
86
+ echo "\n".' <additional_image_link><![CDATA['.$imageUrl.']]></additional_image_link>';
87
+ }
88
+ echo "\n".'</offer>';
89
+
90
+ flush();
91
+ ob_flush();
92
+ }
93
+ echo "\n".'</offers>';
94
+
95
+ exit();
96
+ }
97
+ */
98
+ }
app/code/community/ShoppingFeeder/Service/controllers/OrdersController.php ADDED
@@ -0,0 +1,74 @@
1
+ <?php
2
+
3
+ //IMPORTANT - "Controller" directory is not autoloaded
4
+ require_once(Mage::getModuleDir('Controller','ShoppingFeeder_Service').DS.'Controller'.DS.'FrontAuth.php');
5
+
6
+ class ShoppingFeeder_Service_OrdersController extends ShoppingFeeder_Service_Controller_FrontAuth
7
+ {
8
+ public function indexAction()
9
+ {
10
+ set_time_limit(0);
11
+
12
+ /** @var ShoppingFeeder_Service_Model_Orders $ordersModel */
13
+ $ordersModel = Mage::getSingleton('shoppingfeeder_service/orders');
14
+
15
+ $page = $this->getRequest()->getParam('page', null);
16
+ $numPerPage = $this->getRequest()->getParam('num_per_page', 1000);
17
+ $orderId = $this->getRequest()->getParam('order_id', null);
18
+
19
+ if (is_null($orderId))
20
+ {
21
+ $orders = $ordersModel->getItems($page, $numPerPage);
22
+ }
23
+ else
24
+ {
25
+ $orders = $ordersModel->getItem($orderId);
26
+ }
27
+
28
+ $responseData = array(
29
+ 'status' => 'success',
30
+ 'data' => array(
31
+ 'page' => $page,
32
+ 'num_per_page' => $numPerPage,
33
+ 'orders' => $orders
34
+ )
35
+ );
36
+
37
+ header('Content-type: application/json; charset=UTF-8');
38
+ echo json_encode($responseData);
39
+ exit();
40
+ }
41
+
42
+ // public function createAction()
43
+ // {
44
+ // /** @var ShoppingFeeder_Service_Model_Orders $ordersModel */
45
+ // $ordersModel = Mage::getSingleton('shoppingfeeder_service/orders');
46
+ //
47
+ // if ($this->getRequest()->getMethod() == Zend_Http_Client::POST)
48
+ // {
49
+ // //create the order
50
+ // $order = $ordersModel->create();
51
+ //
52
+ //
53
+ // $responseData = array(
54
+ // 'status' => 'success',
55
+ // 'data' => array(
56
+ // 'order' => $order
57
+ // )
58
+ // );
59
+ // }
60
+ // else
61
+ // {
62
+ // $responseData = array(
63
+ // 'status' => 'fail',
64
+ // 'data' => array(
65
+ // 'message' => 'HTTP method incorrect, must be POST'
66
+ // )
67
+ // );
68
+ // }
69
+ //
70
+ // header('Content-type: application/json; charset=UTF-8');
71
+ // echo json_encode($responseData);
72
+ // exit();
73
+ // }
74
+ }
app/code/community/ShoppingFeeder/Service/controllers/TestController.php ADDED
@@ -0,0 +1,274 @@
1
+ <?php
2
+
3
+ //IMPORTANT - "Controller" directory is not autoloaded
4
+ require_once(Mage::getModuleDir('Controller','ShoppingFeeder_Service').DS.'Controller'.DS.'FrontAuth.php');
5
+
6
+ class ShoppingFeeder_Service_TestController extends Mage_Core_Controller_Front_Action
7
+ {
8
+ public function indexAction()
9
+ {
10
+ if (!function_exists('getallheaders'))
11
+ {
12
+ function getallheaders()
13
+ {
14
+ $headers = '';
15
+ foreach ($_SERVER as $name => $value)
16
+ {
17
+ if (substr($name, 0, 5) == 'HTTP_')
18
+ {
19
+ $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
20
+ }
21
+ }
22
+ return $headers;
23
+ }
24
+ }
25
+
26
+ $requiresSsl = false;
27
+
28
+ try
29
+ {
30
+ /** @var ShoppingFeeder_Service_Model_Auth $authModel */
31
+ $authModel = Mage::getModel('shoppingfeeder_service/auth');
32
+ //check if this setup requires SSL
33
+ $sslInFront = Mage::getStoreConfig('web/secure/use_in_frontend');
34
+ $requiresSsl = ($sslInFront == null) ? false : $sslInFront;
35
+
36
+ $apiKeys = $authModel->getApiKeys();
37
+
38
+ if (!isset($apiKeys['api_key']) || empty($apiKeys['api_key']))
39
+ {
40
+ throw new Exception('API key not setup.');
41
+ }
42
+
43
+ if (!isset($apiKeys['api_secret']) || empty($apiKeys['api_secret']))
44
+ {
45
+ throw new Exception('API secret not setup.');
46
+ }
47
+
48
+ $headers = getallheaders();
49
+
50
+ $authResult = $authModel->auth(
51
+ $headers,
52
+ $this->getRequest()->getScheme(),
53
+ $this->getRequest()->getMethod()
54
+ );
55
+
56
+ if ($authResult === true)
57
+ {
58
+ set_time_limit(0);
59
+
60
+ $responseData = array(
61
+ 'status' => 'success',
62
+ 'data' => array(
63
+ 'message' => 'Authorization OK',
64
+ 'requires_ssl' => $requiresSsl
65
+ )
66
+ );
67
+ }
68
+ else
69
+ {
70
+ $responseData = array(
71
+ 'status' => 'fail',
72
+ 'data' => array (
73
+ 'message' => 'Authorization failed: ['.$authResult.']',
74
+ 'requires_ssl' => $requiresSsl
75
+ )
76
+ );
77
+ }
78
+ }
79
+ catch (Exception $e)
80
+ {
81
+ $responseData = array(
82
+ 'status' => 'fail',
83
+ 'data' => array (
84
+ 'message' => $e->getMessage(),
85
+ 'requires_ssl' => $requiresSsl
86
+ )
87
+ );
88
+ }
89
+
90
+ header('Content-type: application/json; charset=UTF-8');
91
+ echo json_encode($responseData);
92
+ exit();
93
+ }
94
+
95
+ public function debugAction()
96
+ {
97
+ if (function_exists('getallheaders'))
98
+ {
99
+ echo 'Function <b>getallheaders</b> <span style="color:green;">exists</span>'."<br>\n";
100
+ try
101
+ {
102
+ $headers = getallheaders();
103
+ echo 'Get headers succeeded: '.print_r($headers,true)."<br>\n";
104
+ }
105
+ catch (Exception $e)
106
+ {
107
+ echo 'Get headers failed: ['.$e->getMessage().']'."<br>\n";
108
+ }
109
+ }
110
+ else
111
+ {
112
+ try
113
+ {
114
+ function getallheaders()
115
+ {
116
+ $headers = '';
117
+ foreach ($_SERVER as $name => $value)
118
+ {
119
+ if (substr($name, 0, 5) == 'HTTP_')
120
+ {
121
+ $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
122
+ }
123
+ }
124
+ return $headers;
125
+ }
126
+ echo 'Function <b>getallheaders</b> created'."<br>\n";
127
+
128
+ try
129
+ {
130
+ $headers = getallheaders();
131
+ echo 'Get headers succeeded: '.print_r($headers,true)."<br>\n";
132
+ }
133
+ catch (Exception $e)
134
+ {
135
+ echo 'Get headers failed: ['.$e->getMessage().']'."<br>\n";
136
+ }
137
+ }
138
+ catch (Exception $e)
139
+ {
140
+ echo 'Function <b>getallheaders</b> could not be created'."<br>\n";
141
+ }
142
+ }
143
+
144
+ if (function_exists('hash_hmac'))
145
+ {
146
+ echo 'Function <b>hash_hmac</b> <span style="color:green;">exists</span>'."<br>\n";
147
+ }
148
+ else
149
+ {
150
+ echo 'Function <b>hash_hmac</b> <span style="color:red;">does not exist</span>'."<br>\n";
151
+ }
152
+
153
+ if (function_exists('mhash'))
154
+ {
155
+ echo 'Function <b>mhash</b> <span style="color:green;">exists</span>'."<br>\n";
156
+ }
157
+ else
158
+ {
159
+ echo 'Function <b>mhash</b> <span style="color:red;">does not exist</span>'."<br>\n";
160
+ }
161
+
162
+ try
163
+ {
164
+ /** @var ShoppingFeeder_Service_Model_Auth $authModel */
165
+ $authModel = Mage::getModel('shoppingfeeder_service/auth');
166
+ echo '$authModel successfully instantiated'."<br>\n";
167
+ }
168
+ catch (Exception $e)
169
+ {
170
+ echo '$authModel could not be instantiated: ['.$e->getMessage().']'."<br>\n";
171
+ }
172
+
173
+
174
+ if (isset($authModel))
175
+ {
176
+ try
177
+ {
178
+ $apiKeys = $authModel->getApiKeys();
179
+ //hide from view
180
+ unset($apiKeys['api_secret']);
181
+ echo 'API Keys successfully fetched: '.print_r($apiKeys, true)."<br>\n";
182
+
183
+ try
184
+ {
185
+ $headers = getallheaders();
186
+ $authResult = $authModel->auth(
187
+ $headers,
188
+ $this->getRequest()->getScheme(),
189
+ $this->getRequest()->getMethod()
190
+ );
191
+ echo '$authresult successfully called: '.print_r($authResult,true)."<br>\n";
192
+ }
193
+ catch (Exception $e)
194
+ {
195
+ echo '$authresult could not be called: ['.$e->getMessage().']'."<br>\n";
196
+ }
197
+ }
198
+ catch (Exception $e)
199
+ {
200
+ echo 'API Keys could not be fetched: ['.$e->getMessage().']'."<br>\n";
201
+ }
202
+ }
203
+
204
+ $offersModel = false;
205
+ try {
206
+ $offersModel = Mage::getSingleton('shoppingfeeder_service/offers');
207
+ echo '$offersModel <span style="color:green;">successfully instantiated</span>'."<br>\n";
208
+ }
209
+ catch (Exception $e)
210
+ {
211
+ echo '$offersModel <span style="color:red;">could not</span> be instantiated: ['.$e->getMessage().']'."<br>\n";
212
+
213
+ }
214
+
215
+ $page = 1;
216
+ $numPerPage = 1;
217
+ $offers = false;
218
+ if ($offersModel)
219
+ {
220
+ try {
221
+ $page = 1;
222
+ $numPerPage = 1;
223
+
224
+ $offers = $offersModel->getItems($page, $numPerPage);
225
+ echo 'Fetch 1 product successful'."<br>\n";
226
+ }
227
+ catch (Exception $e)
228
+ {
229
+ echo 'Fetch 1 product <span style="color:red;">NOT</span> successful: ['.$e->getMessage().']'."<br>\n";
230
+
231
+ try {
232
+ echo 'Trying to get $collection'."<br>\n";
233
+ $collection = Mage::getModel('catalog/product')->getCollection()
234
+ ->addAttributeToSelect('*')
235
+ ->addAttributeToFilter('status', Mage_Catalog_Model_Product_Status::STATUS_ENABLED)
236
+ ->addAttributeToFilter('visibility', 4);
237
+ echo '$collection <span style="color:green;">successfully instantiated</span>'."<br>\n";
238
+ }
239
+ catch (Exception $e)
240
+ {
241
+ echo '$collection <span style="color:red;">NOT</span> successful: ['.$e->getMessage().']'."<br>\n";
242
+ }
243
+ }
244
+ }
245
+
246
+ if ($offers)
247
+ {
248
+ try {
249
+ $responseData = array(
250
+ 'status' => 'success',
251
+ 'data' => array(
252
+ 'page' => $page,
253
+ 'num_per_page' => $numPerPage,
254
+ 'offers' => $offers
255
+ )
256
+ );
257
+
258
+ echo json_encode($responseData);
259
+ }
260
+ catch (Exception $e)
261
+ {
262
+ echo 'Could not output JSON: ['.$e->getMessage().']'."<br>\n";
263
+ }
264
+ }
265
+
266
+ exit();
267
+ }
268
+
269
+ public function debug2Action()
270
+ {
271
+ echo("DEBUG-DEBUG");
272
+ exit();
273
+ }
274
+ }
app/code/community/ShoppingFeeder/Service/controllers/VersionController.php ADDED
@@ -0,0 +1,23 @@
1
+ <?php
2
+
3
+ //IMPORTANT - "Controller" directory is not autoloaded
4
+ require_once(Mage::getModuleDir('Controller','ShoppingFeeder_Service').DS.'Controller'.DS.'FrontAuth.php');
5
+
6
+ class ShoppingFeeder_Service_VersionController extends Mage_Core_Controller_Front_Action
7
+ {
8
+ protected static $_version = '1.1.6';
9
+
10
+ public function indexAction()
11
+ {
12
+ $responseData = array(
13
+ 'status' => 'success',
14
+ 'data' => array(
15
+ 'version' => self::$_version
16
+ )
17
+ );
18
+
19
+ header('Content-type: application/json; charset=UTF-8');
20
+ echo json_encode($responseData);
21
+ exit();
22
+ }
23
+ }
app/code/community/ShoppingFeeder/Service/etc/config.xml ADDED
@@ -0,0 +1,119 @@
1
+ <?xml version="1.0"?>
2
+ <!--
3
+ /**
4
+ * Module config
5
+ *
6
+ * @author ShoppingFeeder
7
+ */
8
+ -->
9
+ <config>
10
+ <modules>
11
+ <ShoppingFeeder_Service>
12
+ <version>1.0.0</version>
13
+ </ShoppingFeeder_Service>
14
+ </modules>
15
+ <global>
16
+ <models>
17
+ <shoppingfeeder_service>
18
+ <class>ShoppingFeeder_Service_Model</class>
19
+ </shoppingfeeder_service>
20
+ </models>
21
+ <helpers>
22
+ <shoppingfeeder_service>
23
+ <class>ShoppingFeeder_Service_Helper</class>
24
+ </shoppingfeeder_service>
25
+ </helpers>
26
+ <blocks>
27
+ <shoppingfeeder_service>
28
+ <class>ShoppingFeeder_Service_Block</class>
29
+ </shoppingfeeder_service>
30
+ </blocks>
31
+ <resources>
32
+ <shoppingfeeder_setup>
33
+ <setup>
34
+ <module>ShoppingFeeder_Service</module>
35
+ <class>Mage_Core_Model_Resource_Setup</class>
36
+ </setup>
37
+ </shoppingfeeder_setup>
38
+ </resources>
39
+ <events>
40
+ <sales_order_place_after>
41
+ <observers>
42
+ <shoppingfeeder_notify_sales_order_place_after>
43
+ <class>shoppingfeeder_service/observer</class>
44
+ <method>salesOrderPlaceAfter</method>
45
+ </shoppingfeeder_notify_sales_order_place_after>
46
+ </observers>
47
+ </sales_order_place_after>
48
+ <!--<before_service_item_display>-->
49
+ <!--<observers>-->
50
+ <!--<shoppingfeeder_service>-->
51
+ <!--<class>shoppingfeeder_service/observer</class>-->
52
+ <!--<method>beforeServiceDisplayed</method>-->
53
+ <!--</shoppingfeeder_service>-->
54
+ <!--</observers>-->
55
+ <!--</before_service_item_display>-->
56
+ </events>
57
+ </global>
58
+ <frontend>
59
+ <routers>
60
+ <shoppingfeeder>
61
+ <use>standard</use>
62
+ <args>
63
+ <module>ShoppingFeeder_Service</module>
64
+ <frontName>shoppingfeeder</frontName>
65
+ </args>
66
+ </shoppingfeeder>
67
+ </routers>
68
+ <events>
69
+ <catalog_controller_product_view>
70
+ <observers>
71
+ <shoppingfeeder_notify_catalog_controller_product_view>
72
+ <class>shoppingfeeder_service/observer</class>
73
+ <method>productView</method>
74
+ </shoppingfeeder_notify_catalog_controller_product_view>
75
+ </observers>
76
+ </catalog_controller_product_view>
77
+ </events>
78
+ </frontend>
79
+ <admin>
80
+ <routers>
81
+ <adminhtml>
82
+ <args>
83
+ <modules>
84
+ <ShoppingFeeder_Service before="Mage_adminhtml">Shoppingfeeder_Service_Adminhtml</ShoppingFeeder_Service>
85
+ </modules>
86
+ </args>
87
+ </adminhtml>
88
+ </routers>
89
+ </admin>
90
+ <adminhtml>
91
+ <acl>
92
+ <resources>
93
+ <admin>
94
+ <children>
95
+ <system>
96
+ <children>
97
+ <config>
98
+ <children>
99
+ <shoppingfeeder>
100
+ <title>ShoppingFeeder Section</title>
101
+ </shoppingfeeder>
102
+ </children>
103
+ </config>
104
+ </children>
105
+ </system>
106
+ </children>
107
+ </admin>
108
+ </resources>
109
+ </acl>
110
+ </adminhtml>
111
+ <default>
112
+ <shoppingfeeder>
113
+ <service>
114
+ <enable>1</enable>
115
+ <tracking>1</tracking>
116
+ </service>
117
+ </shoppingfeeder>
118
+ </default>
119
+ </config>
app/code/community/ShoppingFeeder/Service/etc/system.xml ADDED
@@ -0,0 +1,74 @@
1
+ <?xml version="1.0"?>
2
+ <!--
3
+ /**
4
+ * Admin menu config
5
+ *
6
+ * @author ShoppingFeeder
7
+ */
8
+ -->
9
+ <config>
10
+ <sections>
11
+ <shoppingfeeder translate="label">
12
+ <class>separator-top</class>
13
+ <label>ShoppingFeeder</label>
14
+ <tab>catalog</tab>
15
+ <frontend_type>text</frontend_type>
16
+ <sort_order>500</sort_order>
17
+ <show_in_default>1</show_in_default>
18
+ <show_in_website>1</show_in_website>
19
+ <show_in_store>1</show_in_store>
20
+
21
+ <groups>
22
+ <service translate="label">
23
+ <label>Configuration</label>
24
+ <frontend_type>text</frontend_type>
25
+ <sort_order>10</sort_order>
26
+ <show_in_default>1</show_in_default>
27
+ <show_in_website>1</show_in_website>
28
+ <show_in_store>1</show_in_store>
29
+ <expanded>1</expanded>
30
+ <fields>
31
+ <!--<enable translate="label">-->
32
+ <!--<label>Enable ShoppingFeeder</label>-->
33
+ <!--<frontend_type>select</frontend_type>-->
34
+ <!--<source_model>adminhtml/system_config_source_yesno</source_model>-->
35
+ <!--<sort_order>1</sort_order>-->
36
+ <!--<show_in_default>1</show_in_default>-->
37
+ <!--<show_in_website>1</show_in_website>-->
38
+ <!--<show_in_store>1</show_in_store>-->
39
+ <!--</enable>-->
40
+ <apikey translate="label">
41
+ <label>ShoppingFeeder API Key</label>
42
+ <frontend_type>text</frontend_type>
43
+ <source_model>adminhtml/system_config_source_yesno</source_model>
44
+ <sort_order>1</sort_order>
45
+ <show_in_default>1</show_in_default>
46
+ <show_in_website>1</show_in_website>
47
+ <show_in_store>1</show_in_store>
48
+ <validate>required-entry</validate>
49
+ </apikey>
50
+ <apisecret translate="label">
51
+ <label>ShoppingFeeder API Secret</label>
52
+ <frontend_type>text</frontend_type>
53
+ <source_model>adminhtml/system_config_source_yesno</source_model>
54
+ <sort_order>1</sort_order>
55
+ <show_in_default>1</show_in_default>
56
+ <show_in_website>1</show_in_website>
57
+ <show_in_store>1</show_in_store>
58
+ <validate>required-entry</validate>
59
+ </apisecret>
60
+ <tracking translate="label">
61
+ <label>Enable Order Tracking</label>
62
+ <frontend_type>select</frontend_type>
63
+ <source_model>adminhtml/system_config_source_yesno</source_model>
64
+ <sort_order>2</sort_order>
65
+ <show_in_default>1</show_in_default>
66
+ <show_in_website>1</show_in_website>
67
+ <show_in_store>1</show_in_store>
68
+ </tracking>
69
+ </fields>
70
+ </service>
71
+ </groups>
72
+ </shoppingfeeder>
73
+ </sections>
74
+ </config>
app/etc/modules/ShoppingFeeder_Service.xml ADDED
@@ -0,0 +1,20 @@
1
+ <?xml version="1.0"?>
2
+ <!--
3
+ /**
4
+ * Module initial config
5
+ *
6
+ * @author ShoppingFeeder
7
+ */
8
+ -->
9
+ <config>
10
+ <modules>
11
+ <ShoppingFeeder_Service>
12
+ <active>true</active>
13
+ <codePool>community</codePool>
14
+ <depends>
15
+ <Mage_Core/>
16
+ <Mage_Adminhtml />
17
+ </depends>
18
+ </ShoppingFeeder_Service>
19
+ </modules>
20
+ </config>
package.xml ADDED
@@ -0,0 +1,35 @@
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>shoppingfeeder</name>
4
+ <version>1.2.0</version>
5
+ <stability>stable</stability>
6
+ <license>GNU General Public License (GPL)</license>
7
+ <channel>community</channel>
8
+ <extends/>
9
+ <summary>ShoppingFeeder allows you to manage product feeds for any kind of channel, and track everything.</summary>
10
+ <description>ShoppingFeeder allows you to manage product feeds for any kind of channel - and get a holistic view of traffic, conversions and ROI.&#xD;
11
+ &#xD;
12
+ &lt;b&gt;By far the best value extension out there!&lt;/b&gt;&#xD;
13
+ &#xD;
14
+ Use ShoppingFeeder to import your Shopify product catalogue, export it to numerous channels that have various feed specifications and requirements and then track all the clicks, referrals, conversions that result from the distribution of your product date.&#xD;
15
+ &#xD;
16
+ Export to Google, Shopping.com, Nextag, kelkoo, PriceCheck, Shopmania, Fruugo and more!&#xD;
17
+ &#xD;
18
+ To set up your ShoppingFeeder account and install this extension seamlessly, create an account at: &lt;a href="http://www.shoppingfeeder.com/register"&gt;http://www.shoppingfeeder.com/register&lt;/a&gt;</description>
19
+ <notes>This version has a number of major updates, including:&#xD;
20
+ - Reducing payload on unchanged products&#xD;
21
+ - Enhanced support for configurable products, providing child products as variants&#xD;
22
+ - Updated attribute support to enable attribute mapping in ShoppingFeeder&#xD;
23
+ &#xD;
24
+ Features include:&#xD;
25
+ - One-click feed export to numerous channels&#xD;
26
+ - Built-in order tracking&#xD;
27
+ - Click &amp; referral tracking&#xD;
28
+ - Historical order importing for reporting and ROI analysis</notes>
29
+ <authors><author><name>ShoppingFeeder</name><user>shoppingfeeder</user><email>support@shoppingfeeder.com</email></author></authors>
30
+ <date>2015-04-13</date>
31
+ <time>08:25:15</time>
32
+ <contents><target name="mageetc"><dir name="modules"><file name="ShoppingFeeder_Service.xml" hash="90b374109c2d1281ddf527f24fa7d914"/></dir></target><target name="magecommunity"><dir name="ShoppingFeeder"><dir name="Service"><dir name="Block"><dir name="Adminhtml"><file name="Service.php" hash="e94d54bc342dc2941753ad635c0454a3"/></dir><file name="Service.php" hash="48874f0e80ce70686b81eb5380e9f498"/></dir><dir name="Controller"><file name="FrontAuth.php" hash="2dc20fec996f1c02ad8b9374af432ec0"/></dir><dir name="Helper"><file name="Data.php" hash="17589bd08352e497806bef9884972300"/></dir><dir name="Model"><file name="Auth.php" hash="2cff17b0750ff70695b32524627b2aaf"/><file name="Observer.php" hash="79c5aa286d8bfacbe5b4c759b8b0c5f1"/><file name="Offers.php" hash="45ca08e63a13a46795d8e3025cfa47e7"/><file name="Orders.php" hash="682845c908726831937d648ec966db9f"/></dir><dir name="controllers"><file name="AttributesController.php" hash="163472c6ed07a2d9fea4c933a5b6f446"/><file name="FeedController.php" hash="6d469ec267e6e03c6d3a66821bd24848"/><file name="OrdersController.php" hash="07d47f30b9740c9dbb3cebbc54ca915f"/><file name="TestController.php" hash="77604f9f9660c4bcd8ad166160945c85"/><file name="VersionController.php" hash="af8303ea5297a3499f183ae1787f6d2b"/></dir><dir name="etc"><file name="config.xml" hash="51fe66a6abd05e684e66c8b90241a9dc"/><file name="system.xml" hash="9681c74fa8886143197932690616e2dd"/></dir></dir></dir></target></contents>
33
+ <compatible/>
34
+ <dependencies><required><php><min>5.1.0</min><max>6.0.0</max></php></required></dependencies>
35
+ </package>