CommerceStack_Recommender - Version 2.2.0

Version Notes

Please contact help@commercestack.com with any feature requests, bugs, issues, or questions.

Download this release

Release Info

Developer CommerceStack
Extension CommerceStack_Recommender
Version 2.2.0
Comparing to
See all releases


Code changes from version 2.1.3 to 2.2.0

app/code/community/CommerceStack/CsApiClient/etc/config.xml CHANGED
@@ -19,7 +19,7 @@
19
  <api>
20
  <base_uri>https://api.commercestack.com/</base_uri>
21
  <!--<base_uri>http://127.0.0.1/recommender/public/</base_uri>-->
22
- <create_account_uri>account/</create_account_uri>
23
  </api>
24
  </csapiclient>
25
  </default>
19
  <api>
20
  <base_uri>https://api.commercestack.com/</base_uri>
21
  <!--<base_uri>http://127.0.0.1/recommender/public/</base_uri>-->
22
+ <!--<create_account_uri>account/</create_account_uri>-->
23
  </api>
24
  </csapiclient>
25
  </default>
app/code/community/CommerceStack/Recommender/.DS_Store ADDED
Binary file
app/code/community/CommerceStack/Recommender/Block/Cart/Crosssell.php CHANGED
@@ -12,6 +12,7 @@ class CommerceStack_Recommender_Block_Cart_Crosssell extends Mage_Checkout_Block
12
  ->{$linkSource}()
13
  ->getProductCollection()
14
  ->setStoreId(Mage::app()->getStore()->getId())
 
15
  ->addStoreFilter();
16
  $this->_addProductAttributesAndPrices($collection);
17
 
@@ -133,6 +134,7 @@ class CommerceStack_Recommender_Block_Cart_Crosssell extends Mage_Checkout_Block
133
  $randCollection->addStoreFilter();
134
  $randCollection->setPage(1, $numRecsToGet);
135
  $randCollection->addIdFilter(array_merge($unionLinkedItemCollection->getAllIds(), $this->_getCartProductIds()), true);
 
136
 
137
  Mage::getSingleton('catalog/product_visibility')->addVisibleInCatalogFilterToCollection($randCollection);
138
 
12
  ->{$linkSource}()
13
  ->getProductCollection()
14
  ->setStoreId(Mage::app()->getStore()->getId())
15
+ //->addAttributeToFilter('discontinued', 0) // uncomment to filter by attribute
16
  ->addStoreFilter();
17
  $this->_addProductAttributesAndPrices($collection);
18
 
134
  $randCollection->addStoreFilter();
135
  $randCollection->setPage(1, $numRecsToGet);
136
  $randCollection->addIdFilter(array_merge($unionLinkedItemCollection->getAllIds(), $this->_getCartProductIds()), true);
137
+ //$randCollection->addAttributeToFilter('discontinued', 0); // uncomment to filter by attribute
138
 
139
  Mage::getSingleton('catalog/product_visibility')->addVisibleInCatalogFilterToCollection($randCollection);
140
 
app/code/community/CommerceStack/Recommender/Block/Product/List/Related.php CHANGED
@@ -61,6 +61,7 @@ class CommerceStack_Recommender_Block_Product_List_Related extends Mage_Catalog_
61
  ->addAttributeToSelect('required_options')
62
  ->setGroupBy()
63
  ->setPositionOrder()
 
64
  ->addStoreFilter();
65
 
66
  $linkedItemCollection->getSelect()->limit($numRecsToGet);
@@ -81,6 +82,10 @@ class CommerceStack_Recommender_Block_Product_List_Related extends Mage_Catalog_
81
  $this->_addProductAttributesAndPrices($linkedItemCollection);
82
 
83
  Mage::getSingleton('catalog/product_visibility')->addVisibleInCatalogFilterToCollection($linkedItemCollection);
 
 
 
 
84
 
85
  $linkedItemCollection->load();
86
 
@@ -119,8 +124,13 @@ class CommerceStack_Recommender_Block_Product_List_Related extends Mage_Catalog_
119
  $randCollection->addStoreFilter();
120
  $randCollection->setPage(1, $numRecsToGet);
121
  $randCollection->addIdFilter(array_merge($unionLinkedItemCollection->getAllIds(), array($product->getId())), true);
 
122
 
123
  Mage::getSingleton('catalog/product_visibility')->addVisibleInCatalogFilterToCollection($randCollection);
 
 
 
 
124
 
125
  if($useCategoryFilter)
126
  {
61
  ->addAttributeToSelect('required_options')
62
  ->setGroupBy()
63
  ->setPositionOrder()
64
+ //->addAttributeToFilter('discontinued', 0) // uncomment to filter by attribute
65
  ->addStoreFilter();
66
 
67
  $linkedItemCollection->getSelect()->limit($numRecsToGet);
82
  $this->_addProductAttributesAndPrices($linkedItemCollection);
83
 
84
  Mage::getSingleton('catalog/product_visibility')->addVisibleInCatalogFilterToCollection($linkedItemCollection);
85
+ /**
86
+ Mage::getSingleton('catalog/product_status')->addSaleableFilterToCollection($collection);
87
+ Mage::getSingleton('cataloginventory/stock')->addInStockFilterToCollection($collection);
88
+ */
89
 
90
  $linkedItemCollection->load();
91
 
124
  $randCollection->addStoreFilter();
125
  $randCollection->setPage(1, $numRecsToGet);
126
  $randCollection->addIdFilter(array_merge($unionLinkedItemCollection->getAllIds(), array($product->getId())), true);
127
+ //$randCollection->addAttributeToFilter('discontinued', 0); // uncomment to filter by attribute
128
 
129
  Mage::getSingleton('catalog/product_visibility')->addVisibleInCatalogFilterToCollection($randCollection);
130
+ /**
131
+ Mage::getSingleton('catalog/product_status')->addSaleableFilterToCollection($collection);
132
+ Mage::getSingleton('cataloginventory/stock')->addInStockFilterToCollection($collection);
133
+ */
134
 
135
  if($useCategoryFilter)
136
  {
app/code/community/CommerceStack/Recommender/Block/Product/List/Upsell.php CHANGED
@@ -80,6 +80,7 @@ class CommerceStack_Recommender_Block_Product_List_Upsell extends Mage_Catalog_B
80
  ->addStoreFilter();
81
  }
82
 
 
83
  $linkedItemCollection->getSelect()->limit($numRecsToGet);
84
 
85
  if($useCategoryFilter)
@@ -98,6 +99,10 @@ class CommerceStack_Recommender_Block_Product_List_Upsell extends Mage_Catalog_B
98
  $this->_addProductAttributesAndPrices($linkedItemCollection);
99
 
100
  Mage::getSingleton('catalog/product_visibility')->addVisibleInCatalogFilterToCollection($linkedItemCollection);
 
 
 
 
101
 
102
  $linkedItemCollection->load();
103
 
@@ -133,9 +138,14 @@ class CommerceStack_Recommender_Block_Product_List_Upsell extends Mage_Catalog_B
133
  $randCollection->getSelect()->order('rand()');
134
  $randCollection->addStoreFilter();
135
  $randCollection->setPage(1, $numRecsToGet);
 
136
  $randCollection->addIdFilter(array_merge($unionLinkedItemCollection->getAllIds(), array($product->getId())), true);
137
 
138
  Mage::getSingleton('catalog/product_visibility')->addVisibleInCatalogFilterToCollection($randCollection);
 
 
 
 
139
 
140
  if($useCategoryFilter)
141
  {
80
  ->addStoreFilter();
81
  }
82
 
83
+ //$linkedItemCollection->addAttributeToFilter('discontinued', 0); // uncomment to filter by attribute
84
  $linkedItemCollection->getSelect()->limit($numRecsToGet);
85
 
86
  if($useCategoryFilter)
99
  $this->_addProductAttributesAndPrices($linkedItemCollection);
100
 
101
  Mage::getSingleton('catalog/product_visibility')->addVisibleInCatalogFilterToCollection($linkedItemCollection);
102
+ /**
103
+ Mage::getSingleton('catalog/product_status')->addSaleableFilterToCollection($collection);
104
+ Mage::getSingleton('cataloginventory/stock')->addInStockFilterToCollection($collection);
105
+ */
106
 
107
  $linkedItemCollection->load();
108
 
138
  $randCollection->getSelect()->order('rand()');
139
  $randCollection->addStoreFilter();
140
  $randCollection->setPage(1, $numRecsToGet);
141
+ //$randCollection->addAttributeToFilter('discontinued', 0); // uncomment to filter by attribute
142
  $randCollection->addIdFilter(array_merge($unionLinkedItemCollection->getAllIds(), array($product->getId())), true);
143
 
144
  Mage::getSingleton('catalog/product_visibility')->addVisibleInCatalogFilterToCollection($randCollection);
145
+ /**
146
+ Mage::getSingleton('catalog/product_status')->addSaleableFilterToCollection($collection);
147
+ Mage::getSingleton('cataloginventory/stock')->addInStockFilterToCollection($collection);
148
+ */
149
 
150
  if($useCategoryFilter)
151
  {
app/code/community/CommerceStack/Recommender/Helper/Data.php CHANGED
@@ -3,6 +3,8 @@ class CommerceStack_Recommender_Helper_Data extends Mage_Core_Helper_Abstract
3
  {
4
  protected $_totalTasks;
5
  protected $_currentTask;
 
 
6
 
7
  protected function _authenticate($apiUser, $apiSecret)
8
  {
@@ -41,6 +43,40 @@ class CommerceStack_Recommender_Helper_Data extends Mage_Core_Helper_Abstract
41
  );
42
  }
43
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  public function getTableUpdateAsXml($apiUser, $apiSecret, $columns, $table, $primaryKey, $lastRecordId, $rootName, $chunkSize, $where = NULL)
45
  {
46
  if(!$this->_authenticate($apiUser, $apiSecret))
@@ -61,6 +97,17 @@ class CommerceStack_Recommender_Helper_Data extends Mage_Core_Helper_Abstract
61
  $xml .= "<{$rootName}s>\n";
62
  $xml .= "<commercestack_recommender_version>{$this->_getRecommenderVersion()}</commercestack_recommender_version>\n";
63
 
 
 
 
 
 
 
 
 
 
 
 
64
  // We cannot rely on XMLWriter being available so we construct the XML manually
65
  foreach ($result as $row)
66
  {
@@ -95,7 +142,7 @@ class CommerceStack_Recommender_Helper_Data extends Mage_Core_Helper_Abstract
95
  return $maxId;
96
  }
97
 
98
- public function getUpdateXml($rootName)
99
  {
100
  $server = $this->getServer();
101
 
@@ -147,5 +194,42 @@ class CommerceStack_Recommender_Helper_Data extends Mage_Core_Helper_Abstract
147
 
148
  return $tableName;
149
  }
150
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  }
3
  {
4
  protected $_totalTasks;
5
  protected $_currentTask;
6
+ protected $_totalFrames;
7
+ protected $_currentFrame;
8
 
9
  protected function _authenticate($apiUser, $apiSecret)
10
  {
43
  );
44
  }
45
 
46
+ // Driver for getTableUpdateAsXml when we are doing a client driven update
47
+ public function postUpdate($apiUser, $apiSecret, $columns, $table, $primaryKey, $rootName, $chunkSize, $where = NULL)
48
+ {
49
+ // Get last server record ID from server
50
+ $lastServerRecordId = (int)$this->getFromServer($rootName); // If this fails there's no point in continuing. Let exception bubble up
51
+
52
+ // Get count of records we'll need to send
53
+ $connection = Mage::getSingleton('core/resource')->getConnection('core_read');
54
+ $sql = "SELECT COUNT($primaryKey) FROM $table WHERE $primaryKey > $lastServerRecordId";
55
+ if(!is_null($where)) $sql .= " AND $where";
56
+ $result = $connection->query($sql);
57
+ $totalRecordCount = $result->fetchColumn();
58
+ if($totalRecordCount <= 0) return;
59
+
60
+ $chunkSize == 0 ? $this->_totalFrames = 1 : $this->_totalFrames = ceil($totalRecordCount/$chunkSize);
61
+
62
+ $this->_currentFrame = 1;
63
+ while($this->_currentFrame <= $this->_totalFrames)
64
+ {
65
+ $tableUpdateXml = $this->getTableUpdateAsXml($apiUser, $apiSecret, $columns, $table, $primaryKey, $lastServerRecordId, $rootName, $chunkSize, $where);
66
+
67
+ /** @var $server CommerceStack_CsApiClient_Model_Server */
68
+ $server = $this->getServer();
69
+ $server->post("{$rootName}/", /*. "?XDEBUG_SESSION_START=PHPSTORM",*/ $tableUpdateXml); // let exceptions bubble up
70
+
71
+ if($this->_currentFrame != $this->_totalFrames)
72
+ {
73
+ $lastServerRecordId = $this->getFromServer($rootName);
74
+ }
75
+
76
+ $this->_currentFrame++;
77
+ }
78
+ }
79
+
80
  public function getTableUpdateAsXml($apiUser, $apiSecret, $columns, $table, $primaryKey, $lastRecordId, $rootName, $chunkSize, $where = NULL)
81
  {
82
  if(!$this->_authenticate($apiUser, $apiSecret))
97
  $xml .= "<{$rootName}s>\n";
98
  $xml .= "<commercestack_recommender_version>{$this->_getRecommenderVersion()}</commercestack_recommender_version>\n";
99
 
100
+ // If this is a client driven update we need to update the server of progress
101
+ if(!is_null($this->_currentTask))
102
+ $xml .= "<current_task>{$this->_currentTask}</current_task>\n";
103
+ if(!is_null($this->_totalTasks))
104
+ $xml .= "<total_tasks>{$this->_totalTasks}</total_tasks>\n";
105
+ if(!is_null($this->_currentFrame))
106
+ $xml .= "<current_frame>{$this->_currentFrame}</current_frame>\n";
107
+ if(!is_null($this->_totalFrames))
108
+ $xml .= "<total_frames>{$this->_totalFrames}</total_frames>\n";
109
+ // END server progress update for client driven update
110
+
111
  // We cannot rely on XMLWriter being available so we construct the XML manually
112
  foreach ($result as $row)
113
  {
142
  return $maxId;
143
  }
144
 
145
+ public function getFromServer($rootName)
146
  {
147
  $server = $this->getServer();
148
 
194
 
195
  return $tableName;
196
  }
197
+
198
+ public function setTotalTasks($totalTasks)
199
+ {
200
+ $this->_totalTasks = $totalTasks;
201
+ }
202
+
203
+ public function setCurrentTask($curTask)
204
+ {
205
+ $this->_currentTask = $curTask;
206
+ }
207
+
208
+ public function setClientStatus($clientStatus)
209
+ {
210
+ $server = $this->getServer();
211
+
212
+ try
213
+ {
214
+ $server->post('status', $clientStatus);
215
+ }
216
+ catch(Exception $e)
217
+ {
218
+ $this->reportException($e);
219
+ }
220
+ }
221
+
222
+ public function prepareNewTransfer($rootName)
223
+ {
224
+ $server = $this->getServer();
225
+
226
+ try
227
+ {
228
+ $server->post($rootName, 'BeginTransfer');
229
+ }
230
+ catch(Exception $e)
231
+ {
232
+ $this->reportException($e);
233
+ }
234
+ }
235
  }
app/code/community/CommerceStack/Recommender/Model/.DS_Store ADDED
Binary file
app/code/community/CommerceStack/Recommender/Model/Product/Link.php CHANGED
@@ -3,7 +3,7 @@
3
  class CommerceStack_Recommender_Model_Product_Link extends Mage_Catalog_Model_Product_Link
4
  {
5
  const LINK_SOURCE_MANUAL = 1;
6
- const LINK_SOURCE_IGNITE = 2;
7
 
8
  protected $_recTypes = array(self::LINK_TYPE_CROSSSELL => 'marketbasket', self::LINK_TYPE_RELATED => 'alsoviewed');
9
  protected $_linkSource = self::LINK_SOURCE_MANUAL;
@@ -17,7 +17,7 @@ class CommerceStack_Recommender_Model_Product_Link extends Mage_Catalog_Model_Pr
17
  {
18
  $this->_linkType = $linkType;
19
  $this->_rootName = $rootName;
20
- $xml = $dataHelper->getUpdateXml($rootName);
21
 
22
  if($xml != '')
23
  {
@@ -53,7 +53,7 @@ class CommerceStack_Recommender_Model_Product_Link extends Mage_Catalog_Model_Pr
53
 
54
  public function useLinkSourceCommerceStack()
55
  {
56
- $this->_linkSource = self::LINK_SOURCE_IGNITE;
57
  return $this;
58
  }
59
 
@@ -65,7 +65,7 @@ class CommerceStack_Recommender_Model_Product_Link extends Mage_Catalog_Model_Pr
65
 
66
  public function isLinkSourceCommerceStack()
67
  {
68
- if($this->_linkSource == self::LINK_SOURCE_IGNITE) return true;
69
  return false;
70
  }
71
 
3
  class CommerceStack_Recommender_Model_Product_Link extends Mage_Catalog_Model_Product_Link
4
  {
5
  const LINK_SOURCE_MANUAL = 1;
6
+ const LINK_SOURCE_COMMERCESTACK = 2;
7
 
8
  protected $_recTypes = array(self::LINK_TYPE_CROSSSELL => 'marketbasket', self::LINK_TYPE_RELATED => 'alsoviewed');
9
  protected $_linkSource = self::LINK_SOURCE_MANUAL;
17
  {
18
  $this->_linkType = $linkType;
19
  $this->_rootName = $rootName;
20
+ $xml = $dataHelper->getFromServer($rootName);
21
 
22
  if($xml != '')
23
  {
53
 
54
  public function useLinkSourceCommerceStack()
55
  {
56
+ $this->_linkSource = self::LINK_SOURCE_COMMERCESTACK;
57
  return $this;
58
  }
59
 
65
 
66
  public function isLinkSourceCommerceStack()
67
  {
68
+ if($this->_linkSource == self::LINK_SOURCE_COMMERCESTACK) return true;
69
  return false;
70
  }
71
 
app/code/community/CommerceStack/Recommender/Model/Resource/.DS_Store ADDED
Binary file
app/code/community/CommerceStack/Recommender/Model/Resource/Eav/.DS_Store ADDED
Binary file
app/code/community/CommerceStack/Recommender/Model/Resource/Eav/Mysql4/.DS_Store ADDED
Binary file
app/code/community/CommerceStack/Recommender/Model/Resource/Eav/Mysql4/Product/.DS_Store ADDED
Binary file
app/code/community/CommerceStack/Recommender/Model/Resource/Eav/Mysql4/Product/Link/.DS_Store ADDED
Binary file
app/code/community/CommerceStack/Recommender/controllers/IndexController.php CHANGED
@@ -1,6 +1,15 @@
1
  <?php
2
  class CommerceStack_Recommender_IndexController extends Mage_Core_Controller_Front_Action
3
  {
 
 
 
 
 
 
 
 
 
4
  public function requestUpdateAction()
5
  {
6
  /** @var $dataHelper CommerceStack_Recommender_Helper_Data */
@@ -40,7 +49,7 @@ class CommerceStack_Recommender_IndexController extends Mage_Core_Controller_Fro
40
  $xml = $dataHelper->getTableUpdateAsXml(
41
  $this->getRequest()->getParam('api_user'),
42
  $this->getRequest()->getParam('api_secret'),
43
- 'item_id, order_id, parent_item_id, store_id, created_at, product_id, price',
44
  $dataHelper->getTableNameSafe('sales/order_item'),
45
  'item_id',
46
  $this->getRequest()->getParam('max_server_id'),
@@ -81,7 +90,7 @@ class CommerceStack_Recommender_IndexController extends Mage_Core_Controller_Fro
81
  $xml = $dataHelper->getTableUpdateAsXml(
82
  $this->getRequest()->getParam('api_user'),
83
  $this->getRequest()->getParam('api_secret'),
84
- 'log_id, visitor_id, customer_id, store_id, login_at, logout_at',
85
  $dataHelper->getTableNameSafe('log/customer'),
86
  'log_id',
87
  $this->getRequest()->getParam('max_server_id'),
@@ -122,7 +131,7 @@ class CommerceStack_Recommender_IndexController extends Mage_Core_Controller_Fro
122
  $xml = $dataHelper->getTableUpdateAsXml(
123
  $this->getRequest()->getParam('api_user'),
124
  $this->getRequest()->getParam('api_secret'),
125
- 'url_id, url',
126
  $dataHelper->getTableNameSafe('log/url_info_table'),
127
  'url_id',
128
  $this->getRequest()->getParam('max_server_id'),
@@ -163,7 +172,7 @@ class CommerceStack_Recommender_IndexController extends Mage_Core_Controller_Fro
163
  $xml = $dataHelper->getTableUpdateAsXml(
164
  $this->getRequest()->getParam('api_user'),
165
  $this->getRequest()->getParam('api_secret'),
166
- 'url_id, visitor_id, visit_time',
167
  $dataHelper->getTableNameSafe('log/url_table'),
168
  'url_id',
169
  $this->getRequest()->getParam('max_server_id'),
@@ -205,7 +214,7 @@ class CommerceStack_Recommender_IndexController extends Mage_Core_Controller_Fro
205
  $xml = $dataHelper->getTableUpdateAsXml(
206
  $this->getRequest()->getParam('api_user'),
207
  $this->getRequest()->getParam('api_secret'),
208
- 'url_rewrite_id AS id, store_id, target_path, request_path, product_id',
209
  $dataHelper->getTableNameSafe('core/url_rewrite'),
210
  'url_rewrite_id',
211
  $this->getRequest()->getParam('max_server_id'),
@@ -248,7 +257,7 @@ class CommerceStack_Recommender_IndexController extends Mage_Core_Controller_Fro
248
  $xml = $dataHelper->getTableUpdateAsXml(
249
  $this->getRequest()->getParam('api_user'),
250
  $this->getRequest()->getParam('api_secret'),
251
- 'v.value_id as id, v.entity_id as product_id, v.value as url_path',
252
  (string)Mage::getConfig()->getTablePrefix() . 'catalog_product_entity_varchar v, ' . $dataHelper->getTableNameSafe('eav/attribute') . ' eav',
253
  'v.value_id',
254
  $this->getRequest()->getParam('max_server_id'),
@@ -287,4 +296,100 @@ class CommerceStack_Recommender_IndexController extends Mage_Core_Controller_Fro
287
  $productLinks = Mage::getModel('recommender/product_link');
288
  $productLinks->updateFromXml($xml, 1); // Mage_Catalog_Model_Product_Link::LINK_TYPE_RELATED. PHP 5.2.x doesn't allow accessing class const
289
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
290
  }
1
  <?php
2
  class CommerceStack_Recommender_IndexController extends Mage_Core_Controller_Front_Action
3
  {
4
+ private static $_columns = array(
5
+ 'orderitem' => 'item_id, order_id, parent_item_id, store_id, created_at, product_id, price',
6
+ 'logcustomer' => 'log_id, visitor_id, customer_id, store_id, login_at, logout_at',
7
+ 'logurlinfo' => 'url_id, url',
8
+ 'logurl' => 'url_id, visitor_id, visit_time',
9
+ 'urlrewrite' => 'url_rewrite_id AS id, store_id, target_path, request_path, product_id',
10
+ 'producturl' => 'v.value_id as id, v.entity_id as product_id, v.value as url_path'
11
+ );
12
+
13
  public function requestUpdateAction()
14
  {
15
  /** @var $dataHelper CommerceStack_Recommender_Helper_Data */
49
  $xml = $dataHelper->getTableUpdateAsXml(
50
  $this->getRequest()->getParam('api_user'),
51
  $this->getRequest()->getParam('api_secret'),
52
+ self::$_columns['orderitem'],
53
  $dataHelper->getTableNameSafe('sales/order_item'),
54
  'item_id',
55
  $this->getRequest()->getParam('max_server_id'),
90
  $xml = $dataHelper->getTableUpdateAsXml(
91
  $this->getRequest()->getParam('api_user'),
92
  $this->getRequest()->getParam('api_secret'),
93
+ self::$_columns['logcustomer'],
94
  $dataHelper->getTableNameSafe('log/customer'),
95
  'log_id',
96
  $this->getRequest()->getParam('max_server_id'),
131
  $xml = $dataHelper->getTableUpdateAsXml(
132
  $this->getRequest()->getParam('api_user'),
133
  $this->getRequest()->getParam('api_secret'),
134
+ self::$_columns['logurlinfo'],
135
  $dataHelper->getTableNameSafe('log/url_info_table'),
136
  'url_id',
137
  $this->getRequest()->getParam('max_server_id'),
172
  $xml = $dataHelper->getTableUpdateAsXml(
173
  $this->getRequest()->getParam('api_user'),
174
  $this->getRequest()->getParam('api_secret'),
175
+ self::$_columns['logurl'],
176
  $dataHelper->getTableNameSafe('log/url_table'),
177
  'url_id',
178
  $this->getRequest()->getParam('max_server_id'),
214
  $xml = $dataHelper->getTableUpdateAsXml(
215
  $this->getRequest()->getParam('api_user'),
216
  $this->getRequest()->getParam('api_secret'),
217
+ self::$_columns['urlrewrite'],
218
  $dataHelper->getTableNameSafe('core/url_rewrite'),
219
  'url_rewrite_id',
220
  $this->getRequest()->getParam('max_server_id'),
257
  $xml = $dataHelper->getTableUpdateAsXml(
258
  $this->getRequest()->getParam('api_user'),
259
  $this->getRequest()->getParam('api_secret'),
260
+ self::$_columns['producturl'],
261
  (string)Mage::getConfig()->getTablePrefix() . 'catalog_product_entity_varchar v, ' . $dataHelper->getTableNameSafe('eav/attribute') . ' eav',
262
  'v.value_id',
263
  $this->getRequest()->getParam('max_server_id'),
296
  $productLinks = Mage::getModel('recommender/product_link');
297
  $productLinks->updateFromXml($xml, 1); // Mage_Catalog_Model_Product_Link::LINK_TYPE_RELATED. PHP 5.2.x doesn't allow accessing class const
298
  }
299
+
300
+ public function updateAction()
301
+ {
302
+ session_write_close(); // prevent other requests from blocking during update because of locked session file
303
+ ini_set('memory_limit', '512M');
304
+ set_time_limit(7200);
305
+ $dataHelper = Mage::helper('recommender');
306
+ $currentTask = 1;
307
+
308
+ try
309
+ {
310
+ $apiUser = $this->getRequest()->getParam('api_user');
311
+ $apiSecret = $this->getRequest()->getParam('api_secret');
312
+
313
+ $dataHelper->setClientStatus('transferring_client_push');
314
+ $dataHelper->setTotalTasks(7);
315
+
316
+ $dataHelper->setCurrentTask($currentTask);
317
+ $dataHelper->postUpdate($apiUser, $apiSecret,
318
+ self::$_columns['orderitem'],
319
+ $dataHelper->getTableNameSafe('sales/order_item'),
320
+ 'item_id',
321
+ 'orderitem',
322
+ 10000
323
+ );
324
+
325
+ $currentTask++;
326
+
327
+ $dataHelper->setCurrentTask($currentTask);
328
+ $dataHelper->postUpdate($apiUser, $apiSecret,
329
+ self::$_columns['logcustomer'],
330
+ $dataHelper->getTableNameSafe('log/customer'),
331
+ 'log_id',
332
+ 'logcustomer',
333
+ 10000);
334
+
335
+ $currentTask++;
336
+
337
+ $dataHelper->setCurrentTask($currentTask);
338
+ $dataHelper->postUpdate($apiUser, $apiSecret,
339
+ self::$_columns['logurlinfo'],
340
+ $dataHelper->getTableNameSafe('log/url_info_table'),
341
+ 'url_id',
342
+ 'logurlinfo',
343
+ 10000);
344
+
345
+ $currentTask++;
346
+
347
+ $dataHelper->setCurrentTask($currentTask);
348
+ $dataHelper->postUpdate($apiUser, $apiSecret,
349
+ self::$_columns['logurl'],
350
+ $dataHelper->getTableNameSafe('log/url_table'),
351
+ 'url_id',
352
+ 'logurl',
353
+ 10000);
354
+
355
+ $currentTask++;
356
+
357
+ $dataHelper->setCurrentTask($currentTask);
358
+ $dataHelper->prepareNewTransfer('urlrewrite'); // urlrewrite requires a full table update
359
+ $dataHelper->postUpdate($apiUser, $apiSecret,
360
+ self::$_columns['urlrewrite'],
361
+ $dataHelper->getTableNameSafe('core/url_rewrite'),
362
+ 'url_rewrite_id',
363
+ 'urlrewrite',
364
+ 10000, // must be 0. Server always returns 0 as last record id so chunking is not possible
365
+ "product_id IS NOT null"
366
+ );
367
+
368
+ $currentTask++;
369
+
370
+ $dataHelper->setCurrentTask($currentTask);
371
+ $dataHelper->prepareNewTransfer('producturl'); // urlrewrite requires a full table update
372
+ $dataHelper->postUpdate($apiUser, $apiSecret,
373
+ self::$_columns['producturl'],
374
+ (string)Mage::getConfig()->getTablePrefix() . 'catalog_product_entity_varchar v, ' . $dataHelper->getTableNameSafe('eav/attribute') . ' eav',
375
+ 'v.value_id',
376
+ 'producturl',
377
+ 10000,
378
+ "v.attribute_id = eav.attribute_id AND eav.attribute_code='url_path'"
379
+ );
380
+
381
+ $currentTask++;
382
+
383
+ $dataHelper->setCurrentTask($currentTask);
384
+ $productLinks = Mage::getModel('recommender/product_link');
385
+ $productLinks->update();
386
+
387
+ $dataHelper->setClientStatus('complete');
388
+ $this->getResponse()->setBody("");
389
+ }
390
+ catch(Exception $e)
391
+ {
392
+ $dataHelper->reportException($e);
393
+ }
394
+ }
395
  }
app/code/community/CommerceStack/Recommender/etc/config.xml CHANGED
@@ -2,7 +2,7 @@
2
  <config>
3
  <modules>
4
  <CommerceStack_Recommender>
5
- <version>2.1.3</version>
6
  </CommerceStack_Recommender>
7
  </modules>
8
 
@@ -82,7 +82,7 @@
82
  </recommender_read>
83
  </resources>
84
  </global>
85
- <!--
86
  <admin>
87
  <routers>
88
  <recommender>
@@ -94,7 +94,7 @@
94
  </recommender>
95
  </routers>
96
  </admin>
97
- -->
98
  <frontend>
99
  <routers>
100
  <recommender>
2
  <config>
3
  <modules>
4
  <CommerceStack_Recommender>
5
+ <version>2.2.0</version>
6
  </CommerceStack_Recommender>
7
  </modules>
8
 
82
  </recommender_read>
83
  </resources>
84
  </global>
85
+
86
  <admin>
87
  <routers>
88
  <recommender>
94
  </recommender>
95
  </routers>
96
  </admin>
97
+
98
  <frontend>
99
  <routers>
100
  <recommender>
js/commercestack/adminhtml/recommender.js CHANGED
@@ -11,11 +11,13 @@ jQuery.noConflict();
11
 
12
  var commercestackBaseUrl = 'https://api.commercestack.com/';
13
  //var commercestackBaseUrl = 'http://127.0.0.1/recommender/public/';
14
- var commercestackStatusUrl = commercestackBaseUrl + 'status?';
15
  var commercestackJobUrl = commercestackBaseUrl + 'job';
16
  var commercestackStatusAuthUrl = null;
17
  var commercestackLastStatus = null;
18
- var commercestackPe;
 
 
19
  document.observe("dom:loaded", function() {
20
  $('row_recommender_relatedproducts_update').hide();
21
  $('row_recommender_relatedproducts_cancel').hide();
@@ -32,7 +34,7 @@ document.observe("dom:loaded", function() {
32
  }
33
  else
34
  {
35
- generateAuthUrls(apikey.apiUser, apikey.apiSecret);
36
  getStatus();
37
  }
38
 
@@ -44,10 +46,10 @@ document.observe("dom:loaded", function() {
44
  }, 5);
45
  });
46
 
47
- function generateAuthUrls(apiUser, apiSecret)
48
  {
49
- commercestackStatusAuthUrl = commercestackStatusUrl + 'api_user=' + apiUser + '&api_secret=' + apiSecret;
50
-
51
  }
52
 
53
  function getStatus()
@@ -58,6 +60,10 @@ function getStatus()
58
  // update button, the CommerceStack Magento module will request a new API key which the AJAX UI status elements
59
  // needs to function. Because these two processes cannot communicate directly, we periodically ping the CommerceStack Magento module
60
  // here to get that information when it becomes available.
 
 
 
 
61
  var apiKeyUrl = $('recommender_account_apikeyurl').getValue();
62
 
63
  // Prototype call (no XSS required)
@@ -69,7 +75,7 @@ function getStatus()
69
  var apikey = transport.responseText.evalJSON();
70
  if(apikey.apiUser != undefined && apikey.apiSecret != undefined)
71
  {
72
- generateAuthUrls(apikey.apiUser, apikey.apiSecret);
73
  getStatus();
74
  }
75
 
@@ -125,7 +131,7 @@ function updateUI(status)
125
 
126
  // Transfer/analysis status items
127
  var msg = '';
128
- if(status.client_status == 'transferring')
129
  {
130
  msg = status.client_message;
131
  $$('#row_recommender_relatedproducts_cancel td.value p.note span')[0].update(msg);
@@ -143,17 +149,26 @@ function updateUI(status)
143
  }
144
  else if(status.client_status == 'error')
145
  {
146
- msg = '<b>An error occurred.</b> The error has been logged and sent to CommerceStack engineers. <br/>Please email <b>help@commercestack.com</b> with your <b>API User</b> value (viewable in Account section).';
147
- $$('#row_recommender_relatedproducts_update td.value p.note span')[0].update(msg);
148
- $('row_recommender_relatedproducts_update').show();
149
- $('row_recommender_relatedproducts_cancel').hide();
 
 
 
 
150
  }
151
  else if(status.client_status == 'timeout')
152
  {
153
- msg = '<b>Related Products Manager servers were unable to connect to your Magento installation.</b> <br/>This could happen if this is a test version of your site that is not publicly available. If so, this problem should resolve itself when Related Products Manager is installed on your live, publicly available site.';
154
- $$('#row_recommender_relatedproducts_update td.value p.note span')[0].update(msg);
155
- $('row_recommender_relatedproducts_update').show();
156
- $('row_recommender_relatedproducts_cancel').hide();
 
 
 
 
 
157
  }
158
  else
159
  {
@@ -220,15 +235,38 @@ function updateUI(status)
220
 
221
  commercestackLastStatus = status.client_status;
222
  }
223
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
224
  function updateCommerceStackRecs(updateActionUrl)
225
  {
226
  $$('#row_recommender_relatedproducts_update td.value p.note span')[0]
227
  .update('Starting...');
228
 
 
 
 
229
  // Notify server that we're starting (IndexController will do this as well
230
  // but this is quicker and keeps client status in the UI from flickering
231
- if(commercestackStatusAuthUrl != undefined)
232
  {
233
  // JQuery call for XSS
234
  jQuery.post(commercestackStatusAuthUrl, 'transferring');
11
 
12
  var commercestackBaseUrl = 'https://api.commercestack.com/';
13
  //var commercestackBaseUrl = 'http://127.0.0.1/recommender/public/';
14
+ var commercestackStatusUrl = commercestackBaseUrl + 'status';
15
  var commercestackJobUrl = commercestackBaseUrl + 'job';
16
  var commercestackStatusAuthUrl = null;
17
  var commercestackLastStatus = null;
18
+ var commercestackPe;
19
+ var updateActionUrl = null;
20
+
21
  document.observe("dom:loaded", function() {
22
  $('row_recommender_relatedproducts_update').hide();
23
  $('row_recommender_relatedproducts_cancel').hide();
34
  }
35
  else
36
  {
37
+ commercestackStatusAuthUrl = generateAuthUrl(commercestackStatusUrl, apikey.apiUser, apikey.apiSecret);
38
  getStatus();
39
  }
40
 
46
  }, 5);
47
  });
48
 
49
+ function generateAuthUrl($url, apiUser, apiSecret)
50
  {
51
+ // Assumes URL has no parameters already
52
+ return $url + '?api_user=' + apiUser + '&api_secret=' + apiSecret;
53
  }
54
 
55
  function getStatus()
60
  // update button, the CommerceStack Magento module will request a new API key which the AJAX UI status elements
61
  // needs to function. Because these two processes cannot communicate directly, we periodically ping the CommerceStack Magento module
62
  // here to get that information when it becomes available.
63
+
64
+ // 2015-09-15 update: I'm not convinced any of this ever runs. The commercestackStatusAuthUrl == undefined
65
+ // check above will produce an exception and recommender_account_apikeyurl doesn't exist in the DOM. I think
66
+ // this is an unnecessary hold over from v1.x.
67
  var apiKeyUrl = $('recommender_account_apikeyurl').getValue();
68
 
69
  // Prototype call (no XSS required)
75
  var apikey = transport.responseText.evalJSON();
76
  if(apikey.apiUser != undefined && apikey.apiSecret != undefined)
77
  {
78
+ commercestackStatusAuthUrl = generateAuthUrl(commercestackStatusUrl, apikey.apiUser, apikey.apiSecret);
79
  getStatus();
80
  }
81
 
131
 
132
  // Transfer/analysis status items
133
  var msg = '';
134
+ if(status.client_status.substring(0, 12) == 'transferring')
135
  {
136
  msg = status.client_message;
137
  $$('#row_recommender_relatedproducts_cancel td.value p.note span')[0].update(msg);
149
  }
150
  else if(status.client_status == 'error')
151
  {
152
+ if(this.updateActionUrl == null) {
153
+ msg = '<b>An error occurred.</b> The error has been logged and sent to CommerceStack engineers. <br/>Please email <b>help@commercestack.com</b> with your <b>API User</b> value (viewable in Account section).';
154
+ $$('#row_recommender_relatedproducts_update td.value p.note span')[0].update(msg);
155
+ $('row_recommender_relatedproducts_update').show();
156
+ $('row_recommender_relatedproducts_cancel').hide();
157
+ } else {
158
+ doClientDrivenUpdate();
159
+ }
160
  }
161
  else if(status.client_status == 'timeout')
162
  {
163
+ // Try a client driven update if possible
164
+ if(this.updateActionUrl == null) {
165
+ msg = '<b>Related Products Manager servers were unable to connect to your Magento installation.</b> <br/>This could happen if this is a test version of your site that is not publicly available. If so, this problem should resolve itself when Related Products Manager is installed on your live, publicly available site.';
166
+ $$('#row_recommender_relatedproducts_update td.value p.note span')[0].update(msg);
167
+ $('row_recommender_relatedproducts_update').show();
168
+ $('row_recommender_relatedproducts_cancel').hide();
169
+ } else {
170
+ doClientDrivenUpdate();
171
+ }
172
  }
173
  else
174
  {
235
 
236
  commercestackLastStatus = status.client_status;
237
  }
238
+
239
+ function doClientDrivenUpdate() {
240
+ // Server driven update has failed. Assumes caller has checked to see if updateActionUrl is null
241
+
242
+ // Hacky but effective
243
+ this.updateActionUrl = this.updateActionUrl.replace('/requestUpdate/', '/update/');
244
+ var apiUser = $('recommender_account_apiuser').getValue();
245
+ var apiSecret = $('recommender_account_apisecret').getValue();
246
+ var authUrl = generateAuthUrl(this.updateActionUrl, apiUser, apiSecret)
247
+
248
+ // Notify server that we're starting (IndexController will do this as well
249
+ // but this is quicker and keeps client status in the UI from flickering
250
+ if(commercestackStatusAuthUrl != null)
251
+ {
252
+ // JQuery call for XSS
253
+ jQuery.post(commercestackStatusAuthUrl, 'transferring_client_push');
254
+ }
255
+ updateCommerceStackRecs(authUrl);
256
+ this.updateActionUrl = null; // This will keep us from automatically launching a second time
257
+ }
258
+
259
  function updateCommerceStackRecs(updateActionUrl)
260
  {
261
  $$('#row_recommender_relatedproducts_update td.value p.note span')[0]
262
  .update('Starting...');
263
 
264
+ // Save this in case we need to go to client-driven update
265
+ this.updateActionUrl = updateActionUrl;
266
+
267
  // Notify server that we're starting (IndexController will do this as well
268
  // but this is quicker and keeps client status in the UI from flickering
269
+ if(commercestackStatusAuthUrl != null)
270
  {
271
  // JQuery call for XSS
272
  jQuery.post(commercestackStatusAuthUrl, 'transferring');
package.xml CHANGED
@@ -1,7 +1,7 @@
1
  <?xml version="1.0"?>
2
  <package>
3
  <name>CommerceStack_Recommender</name>
4
- <version>2.1.3</version>
5
  <stability>stable</stability>
6
  <license uri="http://opensource.org/licenses/osl-3.0.php">Open Software License v 3.0</license>
7
  <channel>community</channel>
@@ -16,9 +16,9 @@ Please contact help@commercestack.com with any bugs, issues, or feature requests
16
  After installing, navigate to System-&gt;Configuration-&gt;Related Products Manager to begin.</description>
17
  <notes>Please contact help@commercestack.com with any feature requests, bugs, issues, or questions.</notes>
18
  <authors><author><name>CommerceStack</name><user>dbright</user><email>dbright@commercestack.com</email></author></authors>
19
- <date>2014-12-17</date>
20
- <time>19:50:42</time>
21
- <contents><target name="magecommunity"><dir name="CommerceStack"><dir name="Recommender"><dir name="Block"><dir name="Cart"><file name="Crosssell.php" hash="3502a27a963955eb91cf8165a39af9d5"/></dir><dir name="Catalog"><dir name="Product"><dir name="Edit"><dir name="Tab"><file name="Crosssell.php" hash="4a629f55e16abcd45d8b97ea7b83ee9d"/><file name="Related.php" hash="5d3e6e6fbeadee6a7b5b31675338171e"/></dir><file name="Tabs.php" hash="aa26d6e9e585117100e983616e6a0afa"/></dir></dir></dir><dir name="Product"><dir name="List"><file name="Related.php" hash="597516550d0a8d532f668925d59bbb35"/><file name="Upsell.php" hash="cd8e2be1800665cc7743f0765ce9b02a"/></dir></dir><dir name="System"><dir name="Config"><dir name="Form"><file name="Apikey.php" hash="def235a2710cea0b01ace89bce9ffda4"/><file name="Apikeyurl.php" hash="5ce3031fd84f912b3552d7c37f2a04ac"/><file name="Apisecret.php" hash="9b9a92981a5ff80aa4ec3e4255844801"/><file name="Apiuser.php" hash="bba5a87192ab0821d999aa4ab8cddada"/><file name="Cancelbutton.php" hash="7a87871461a6d589a28ccbc18f9d1603"/><file name="Email.php" hash="0231900a317a75e1f72cfdd440df2e45"/><file name="Helpiframe.php" hash="cd049db20bfa53e6a57eaba796a0f5b6"/><file name="Paymentiframe.php" hash="aac32f6d47bf80aef61c1b4288363f7e"/><file name="Updatebutton.php" hash="997d957fab4ac828237b9b0ca92d51e6"/></dir><file name="Form.php" hash="de1c3b70b03ac09fe1d5c7cd7613feb1"/></dir></dir></dir><dir name="Helper"><file name="Data.php" hash="8ad308615eb1dffe036199970c978a61"/></dir><dir name="Model"><dir name="Product"><file name="Link.php" hash="c2fb978783910af6472fbcf50070bb0f"/></dir><dir name="Resource"><dir name="Eav"><dir name="Mysql4"><dir name="Product"><dir name="Link"><dir name="Product"><file name="Collection.php" hash="3fb32d0da982297829938dc2427b35cb"/></dir></dir><file name="Link.php" hash="0910e13e6a3b3cac3573a4ea1df59e9f"/></dir></dir></dir><dir name="Mysql4"><file name="Setup.php" hash="1e93566c0f56cc52cdf4e4a73153d2f9"/></dir></dir><dir name="System"><dir name="Config"><dir name="Form"><file name="Upsellsource.php" hash="34ed5e3c61bd288340b7de3809953ad3"/></dir></dir></dir></dir><dir name="controllers"><file name="AccountController.php" hash="2ba2f4ae67df9a140ed2b8b5bc4ec986"/><file name="IndexController.php" hash="2a60213f638ebd16d051d83d3d8f726c"/><file name="ProductController.php" hash="83ad4a8d6d22d53f4fb03744950500d9"/></dir><dir name="etc"><file name="config.xml" hash="3020ce4b74c9c4deba33dcb8fa93fc96"/><file name="system.xml" hash="2ee1ffb7b78936caf27acc87bc34a4c9"/></dir><dir name="sql"><dir name="recommender_setup"><file name="mysql4-install-0.9.3.php" hash="cfe38102d96814f6b254cc38c07f2037"/><file name="mysql4-upgrade-0.9.3-0.9.4.php" hash="3beefb00777a6bd04265b7d33c23efa9"/><file name="mysql4-upgrade-0.9.4-0.9.5.php" hash="3beefb00777a6bd04265b7d33c23efa9"/><file name="mysql4-upgrade-0.9.5-1.0.0.php" hash="3beefb00777a6bd04265b7d33c23efa9"/><file name="mysql4-upgrade-1.0.0-1.0.1.php" hash="3beefb00777a6bd04265b7d33c23efa9"/><file name="mysql4-upgrade-1.0.1-1.0.2.php" hash="3beefb00777a6bd04265b7d33c23efa9"/><file name="mysql4-upgrade-1.0.2-1.1.0.php" hash="3beefb00777a6bd04265b7d33c23efa9"/><file name="mysql4-upgrade-1.1.0-1.2.0.php" hash="3beefb00777a6bd04265b7d33c23efa9"/><file name="mysql4-upgrade-1.2.0-1.2.1.php" hash="3beefb00777a6bd04265b7d33c23efa9"/><file name="mysql4-upgrade-1.2.1-1.2.2.php" hash="3beefb00777a6bd04265b7d33c23efa9"/><file name="mysql4-upgrade-1.2.2-1.2.3.php" hash="3beefb00777a6bd04265b7d33c23efa9"/><file name="mysql4-upgrade-1.2.3-1.2.4.php" hash="3beefb00777a6bd04265b7d33c23efa9"/><file name="mysql4-upgrade-1.2.4-1.2.5.php" hash="3beefb00777a6bd04265b7d33c23efa9"/><file name="mysql4-upgrade-1.2.5-1.2.6.php" hash="3beefb00777a6bd04265b7d33c23efa9"/><file name="mysql4-upgrade-1.2.6-2.0.0.php" hash="b2d2b463578d50b2a1a313c36d02ecdf"/></dir></dir></dir><dir name="CsApiClient"><dir name="Model"><file name="Account.php" hash="f48445eafcb1d1d80203987528387dc2"/><file name="Server.php" hash="026be03911b4af5079f2644f06634aa5"/></dir><dir name="etc"><file name="config.xml" hash="852026aa85d127c12b666444779fbc35"/></dir></dir><dir name="CsNotification"><dir name="Model"><dir name="AdminNotification"><file name="Feed.php" hash="1f20e704fe895c4a2748612a028ee186"/></dir></dir><dir name="etc"><file name="config.xml" hash="9ede9a59b2e857828a341bbf53953abe"/></dir></dir></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="layout"><file name="recommender.xml" hash="26d8deea180738fdd5e3f0a5bcbc8f07"/></dir></dir></dir></dir></target><target name="mageweb"><dir name="js"><dir name="commercestack"><dir name="adminhtml"><file name="recommender.js" hash="1f5896967773fe99bebaa852f3173eda"/></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="CommerceStack_Recommender.xml" hash="c0faaf57bb93c70fe6ba45e68c96fc11"/><file name="CommerceStack_CsApiClient.xml" hash="dd56312764a58b18b7e21738ebb8a6b0"/><file name="CommerceStack_CsNotification.xml" hash="94bb10db3af629c7e68bbf32fd18159c"/></dir></target></contents>
22
  <compatible/>
23
  <dependencies><required><php><min>5.2.13</min><max>6.0.0</max></php></required></dependencies>
24
  </package>
1
  <?xml version="1.0"?>
2
  <package>
3
  <name>CommerceStack_Recommender</name>
4
+ <version>2.2.0</version>
5
  <stability>stable</stability>
6
  <license uri="http://opensource.org/licenses/osl-3.0.php">Open Software License v 3.0</license>
7
  <channel>community</channel>
16
  After installing, navigate to System-&gt;Configuration-&gt;Related Products Manager to begin.</description>
17
  <notes>Please contact help@commercestack.com with any feature requests, bugs, issues, or questions.</notes>
18
  <authors><author><name>CommerceStack</name><user>dbright</user><email>dbright@commercestack.com</email></author></authors>
19
+ <date>2015-09-23</date>
20
+ <time>14:26:55</time>
21
+ <contents><target name="magecommunity"><dir name="CommerceStack"><dir name="Recommender"><dir name="Block"><dir name="Cart"><file name="Crosssell.php" hash="9c5a8e7372ecded86474b74f4a08cd13"/></dir><dir name="Catalog"><dir name="Product"><dir name="Edit"><dir name="Tab"><file name="Crosssell.php" hash="4a629f55e16abcd45d8b97ea7b83ee9d"/><file name="Related.php" hash="5d3e6e6fbeadee6a7b5b31675338171e"/></dir><file name="Tabs.php" hash="aa26d6e9e585117100e983616e6a0afa"/></dir></dir></dir><dir name="Product"><dir name="List"><file name="Related.php" hash="39724c5902d46adc59f96a9ac3ceb28b"/><file name="Upsell.php" hash="369b0e7df665c7da2c4a44686341eace"/></dir></dir><dir name="System"><dir name="Config"><dir name="Form"><file name="Apikey.php" hash="def235a2710cea0b01ace89bce9ffda4"/><file name="Apikeyurl.php" hash="5ce3031fd84f912b3552d7c37f2a04ac"/><file name="Apisecret.php" hash="9b9a92981a5ff80aa4ec3e4255844801"/><file name="Apiuser.php" hash="bba5a87192ab0821d999aa4ab8cddada"/><file name="Cancelbutton.php" hash="7a87871461a6d589a28ccbc18f9d1603"/><file name="Email.php" hash="0231900a317a75e1f72cfdd440df2e45"/><file name="Helpiframe.php" hash="cd049db20bfa53e6a57eaba796a0f5b6"/><file name="Paymentiframe.php" hash="aac32f6d47bf80aef61c1b4288363f7e"/><file name="Updatebutton.php" hash="997d957fab4ac828237b9b0ca92d51e6"/></dir><file name="Form.php" hash="de1c3b70b03ac09fe1d5c7cd7613feb1"/></dir></dir></dir><dir name="Helper"><file name="Data.php" hash="1b7818359b91eacca40dc006ecace854"/></dir><dir name="Model"><dir name="Product"><file name="Link.php" hash="e1e86dddc96b58a8cfcdf32f9de851ac"/></dir><dir name="Resource"><dir name="Eav"><dir name="Mysql4"><dir name="Product"><dir name="Link"><dir name="Product"><file name="Collection.php" hash="3fb32d0da982297829938dc2427b35cb"/></dir><file name=".DS_Store" hash="94881651f077db217b9ec427591b721c"/></dir><file name="Link.php" hash="0910e13e6a3b3cac3573a4ea1df59e9f"/><file name=".DS_Store" hash="e999693fd37d82450997699da2d986e2"/></dir><file name=".DS_Store" hash="d032518ab85e6c3cbbb43688f6bc5bfe"/></dir><file name=".DS_Store" hash="4c2b2d46eba62dd07c7ffc1cf30460b4"/></dir><dir name="Mysql4"><file name="Setup.php" hash="1e93566c0f56cc52cdf4e4a73153d2f9"/></dir><file name=".DS_Store" hash="195fa1ec4bb1126dc072b289fb841603"/></dir><dir name="System"><dir name="Config"><dir name="Form"><file name="Upsellsource.php" hash="34ed5e3c61bd288340b7de3809953ad3"/></dir></dir></dir><file name=".DS_Store" hash="b98ec272e7b775e485af3b5db7b24e6c"/></dir><dir name="controllers"><file name="AccountController.php" hash="2ba2f4ae67df9a140ed2b8b5bc4ec986"/><file name="IndexController.php" hash="939da603e85033cc77542ba10d2a4336"/><file name="ProductController.php" hash="83ad4a8d6d22d53f4fb03744950500d9"/></dir><dir name="etc"><file name="config.xml" hash="7193f87cb740973c4066dd8155d0fdd6"/><file name="system.xml" hash="2ee1ffb7b78936caf27acc87bc34a4c9"/></dir><dir name="sql"><dir name="recommender_setup"><file name="mysql4-install-0.9.3.php" hash="cfe38102d96814f6b254cc38c07f2037"/><file name="mysql4-upgrade-0.9.3-0.9.4.php" hash="3beefb00777a6bd04265b7d33c23efa9"/><file name="mysql4-upgrade-0.9.4-0.9.5.php" hash="3beefb00777a6bd04265b7d33c23efa9"/><file name="mysql4-upgrade-0.9.5-1.0.0.php" hash="3beefb00777a6bd04265b7d33c23efa9"/><file name="mysql4-upgrade-1.0.0-1.0.1.php" hash="3beefb00777a6bd04265b7d33c23efa9"/><file name="mysql4-upgrade-1.0.1-1.0.2.php" hash="3beefb00777a6bd04265b7d33c23efa9"/><file name="mysql4-upgrade-1.0.2-1.1.0.php" hash="3beefb00777a6bd04265b7d33c23efa9"/><file name="mysql4-upgrade-1.1.0-1.2.0.php" hash="3beefb00777a6bd04265b7d33c23efa9"/><file name="mysql4-upgrade-1.2.0-1.2.1.php" hash="3beefb00777a6bd04265b7d33c23efa9"/><file name="mysql4-upgrade-1.2.1-1.2.2.php" hash="3beefb00777a6bd04265b7d33c23efa9"/><file name="mysql4-upgrade-1.2.2-1.2.3.php" hash="3beefb00777a6bd04265b7d33c23efa9"/><file name="mysql4-upgrade-1.2.3-1.2.4.php" hash="3beefb00777a6bd04265b7d33c23efa9"/><file name="mysql4-upgrade-1.2.4-1.2.5.php" hash="3beefb00777a6bd04265b7d33c23efa9"/><file name="mysql4-upgrade-1.2.5-1.2.6.php" hash="3beefb00777a6bd04265b7d33c23efa9"/><file name="mysql4-upgrade-1.2.6-2.0.0.php" hash="b2d2b463578d50b2a1a313c36d02ecdf"/></dir></dir><file name=".DS_Store" hash="9567c71fb3193ad25155b1c371035ddf"/></dir><dir name="CsApiClient"><dir name="Model"><file name="Account.php" hash="f48445eafcb1d1d80203987528387dc2"/><file name="Server.php" hash="026be03911b4af5079f2644f06634aa5"/></dir><dir name="etc"><file name="config.xml" hash="8ba10f11a659435f9cfcb564245d2edc"/></dir></dir><dir name="CsNotification"><dir name="Model"><dir name="AdminNotification"><file name="Feed.php" hash="1f20e704fe895c4a2748612a028ee186"/></dir></dir><dir name="etc"><file name="config.xml" hash="9ede9a59b2e857828a341bbf53953abe"/></dir></dir></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="layout"><file name="recommender.xml" hash="26d8deea180738fdd5e3f0a5bcbc8f07"/></dir></dir></dir></dir></target><target name="mageweb"><dir name="js"><dir name="commercestack"><dir name="adminhtml"><file name="recommender.js" hash="94f488138a9760abcdb85a54fe59c885"/></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="CommerceStack_Recommender.xml" hash="c0faaf57bb93c70fe6ba45e68c96fc11"/><file name="CommerceStack_CsApiClient.xml" hash="dd56312764a58b18b7e21738ebb8a6b0"/><file name="CommerceStack_CsNotification.xml" hash="94bb10db3af629c7e68bbf32fd18159c"/></dir></target></contents>
22
  <compatible/>
23
  <dependencies><required><php><min>5.2.13</min><max>6.0.0</max></php></required></dependencies>
24
  </package>