Version Notes
First release of the Strands Recommender plugin for the Magento community.
Visit http://recommender.strands.com/magento
Download this release
Release Info
Developer | Magento Core Team |
Extension | Mage_StrandsRecommender |
Version | 1.3.2 |
Comparing to | |
See all releases |
Version 1.3.2
- app/code/community/Strands/Recommender/Block/Js.php +31 -0
- app/code/community/Strands/Recommender/Block/System/Config/Form/Field.php +13 -0
- app/code/community/Strands/Recommender/Block/Widget.php +251 -0
- app/code/community/Strands/Recommender/Helper/Abstract.php +35 -0
- app/code/community/Strands/Recommender/Helper/Data.php +27 -0
- app/code/community/Strands/Recommender/Helper/Logger.php +48 -0
- app/code/community/Strands/Recommender/Model/Abstract.php +339 -0
- app/code/community/Strands/Recommender/Model/Api.php +194 -0
- app/code/community/Strands/Recommender/Model/Numberoption.php +48 -0
- app/code/community/Strands/Recommender/Model/Observer.php +297 -0
- app/code/community/Strands/Recommender/etc/config.xml +146 -0
- app/code/community/Strands/Recommender/etc/system.xml +129 -0
- app/design/frontend/default/default/layout/strands_recommender.xml +47 -0
- app/design/frontend/default/default/template/strands/recommender/widget.phtml +5 -0
- app/etc/modules/Strands_Recommender.xml +9 -0
- package.xml +35 -0
app/code/community/Strands/Recommender/Block/Js.php
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
Class Strands_Recommender_Block_Js extends Mage_Core_Block_Text
|
4 |
+
{
|
5 |
+
|
6 |
+
protected function _toHtml()
|
7 |
+
{
|
8 |
+
$model = Mage::getModel('recommender/abstract');
|
9 |
+
|
10 |
+
if (!$model->isActive()) {
|
11 |
+
return '';
|
12 |
+
}
|
13 |
+
|
14 |
+
$protocol = Mage::app()->getStore()->isCurrentlySecure() ? 'https' : 'http';
|
15 |
+
$this->addText('
|
16 |
+
<script type="text/javascript" src="'.$protocol.'://bizsolutions.strands.com/sbsstatic/js/sbsLib-1.0.min.js"></script>');
|
17 |
+
|
18 |
+
$this->addText('
|
19 |
+
<script language="Javascript" type="text/javascript">
|
20 |
+
try {
|
21 |
+
SBS.Worker.go("'.$model->getApiId().'");
|
22 |
+
} catch(ex) { }
|
23 |
+
</script>');
|
24 |
+
|
25 |
+
return parent::_toHtml();
|
26 |
+
}
|
27 |
+
|
28 |
+
}
|
29 |
+
|
30 |
+
|
31 |
+
?>
|
app/code/community/Strands/Recommender/Block/System/Config/Form/Field.php
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
Class Strands_Recommender_Block_System_Config_Form_Field extends Mage_Adminhtml_Block_System_Config_Form_Field
|
4 |
+
{
|
5 |
+
public function render(Varien_Data_Form_Element_Abstract $element)
|
6 |
+
{
|
7 |
+
if (Mage::getStoreConfig('recommender/settings/freedirectory_hidden')
|
8 |
+
|| Mage::getModel('recommender/abstract')->isActive()) return '';
|
9 |
+
return parent::render($element);
|
10 |
+
}
|
11 |
+
|
12 |
+
}
|
13 |
+
?>
|
app/code/community/Strands/Recommender/Block/Widget.php
ADDED
@@ -0,0 +1,251 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
Class Strands_Recommender_Block_Widget extends Mage_Core_Block_Template
|
4 |
+
{
|
5 |
+
protected $_checkout = null;
|
6 |
+
protected $_quote = null;
|
7 |
+
protected $_template = "strands/recommender/widget.phtml";
|
8 |
+
protected $helper = null;
|
9 |
+
|
10 |
+
|
11 |
+
public function _construct()
|
12 |
+
{
|
13 |
+
parent::_construct();
|
14 |
+
$this->helper = Mage::helper('recommender');
|
15 |
+
}
|
16 |
+
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Function is set to blank to prevent the Block from Auto Rendering
|
20 |
+
*/
|
21 |
+
protected function _toHtml()
|
22 |
+
{
|
23 |
+
$model = Mage::getModel('recommender/abstract');
|
24 |
+
if (!$model->isActive()) {
|
25 |
+
return '';
|
26 |
+
}
|
27 |
+
|
28 |
+
if (!$this->hasData('widget_name')) $this->setWidgetName($this->_getTemplate());
|
29 |
+
//if (!$this->hasData('widget_name')) $this->setWidgetName($this->helper->getCurrentPage());
|
30 |
+
if (!$this->hasData('item')) $this->setItem($this->getBasketItems());
|
31 |
+
if (!$this->hasData('strands_id')) $this->setStrandsId($model->getStrandsId());
|
32 |
+
|
33 |
+
return parent::_toHtml();
|
34 |
+
}
|
35 |
+
|
36 |
+
|
37 |
+
/**
|
38 |
+
* @return the Items in the Basket or Items Ordered
|
39 |
+
*/
|
40 |
+
protected function getBasketItems()
|
41 |
+
{
|
42 |
+
|
43 |
+
if ($_product = Mage::registry('product')) {
|
44 |
+
return urlencode($_product->getId());
|
45 |
+
}
|
46 |
+
elseif ($this->helper->getCurrentPage() == "checkout_cart_index" || $this->helper->getCurrentPage() == "checkout_cart") {
|
47 |
+
$ret = array();
|
48 |
+
|
49 |
+
|
50 |
+
foreach ($this->getQuote()->getAllVisibleItems() as $item) {
|
51 |
+
$ret[] = urlencode($item->getProductId());
|
52 |
+
}
|
53 |
+
|
54 |
+
return implode("_._", $ret);
|
55 |
+
}
|
56 |
+
elseif ($this->helper->getCurrentPage() == "checkout_onepage_success"
|
57 |
+
|| $this->helper->getCurrentPage() == "checkout_success"
|
58 |
+
|| $this->helper->getCurrentPage() == "checkout_success_index") {
|
59 |
+
|
60 |
+
$orders = $this->getOrders();
|
61 |
+
$ret = array();
|
62 |
+
|
63 |
+
foreach ($orders as $order) {
|
64 |
+
$ret[] = urlencode($this->setCurrentOrder($order)->addOrderItems()); // CHECK
|
65 |
+
}
|
66 |
+
|
67 |
+
return implode("_._", $ret);
|
68 |
+
}
|
69 |
+
|
70 |
+
return '';
|
71 |
+
}
|
72 |
+
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Method to add Items to the order
|
76 |
+
*
|
77 |
+
* @return string of array joined with _._
|
78 |
+
*/
|
79 |
+
protected function addOrderItems()
|
80 |
+
{
|
81 |
+
$order = $this->getCurrentOrder();
|
82 |
+
if (!$order) {
|
83 |
+
return '';
|
84 |
+
}
|
85 |
+
|
86 |
+
if (!$order instanceof Mage_Sales_Model_Order) {
|
87 |
+
$order = Mage::getModel('sales/order')->load($order);
|
88 |
+
}
|
89 |
+
|
90 |
+
if (!$order) {
|
91 |
+
return '';
|
92 |
+
}
|
93 |
+
|
94 |
+
|
95 |
+
$ret = array();
|
96 |
+
foreach ($order->getAllItems() as $item) {
|
97 |
+
if ($item->getParentItemId()) {
|
98 |
+
continue;
|
99 |
+
}
|
100 |
+
|
101 |
+
$ret[] = $item->getProductId();
|
102 |
+
}
|
103 |
+
|
104 |
+
return implode("_._",$ret);
|
105 |
+
}
|
106 |
+
|
107 |
+
protected function getOrders()
|
108 |
+
{
|
109 |
+
$quote = $this->getCheckout()->getLastQuoteId();
|
110 |
+
if (!$quote) {
|
111 |
+
return '';
|
112 |
+
}
|
113 |
+
|
114 |
+
if ($quote instanceof Mage_Sales_Model_Quote) {
|
115 |
+
$quoteId = $quote->getId();
|
116 |
+
} else {
|
117 |
+
$quoteId = $quote;
|
118 |
+
}
|
119 |
+
|
120 |
+
if (!$quoteId) {
|
121 |
+
return '';
|
122 |
+
}
|
123 |
+
|
124 |
+
$orders = Mage::getResourceModel('sales/order_collection')
|
125 |
+
->addAttributeToFilter('quote_id', $quoteId)
|
126 |
+
->load();
|
127 |
+
|
128 |
+
return $orders;
|
129 |
+
}
|
130 |
+
|
131 |
+
|
132 |
+
/**
|
133 |
+
* Method borrowed from Mage_Catalog_Block_Product
|
134 |
+
*
|
135 |
+
* @return product or false
|
136 |
+
*/
|
137 |
+
public function getProduct()
|
138 |
+
{
|
139 |
+
if (!$this->hasData('product')) {
|
140 |
+
$this->setData('product', Mage::registry('product'));
|
141 |
+
}
|
142 |
+
return $this->getData('product');
|
143 |
+
}
|
144 |
+
|
145 |
+
|
146 |
+
/**
|
147 |
+
* Get active quote
|
148 |
+
*
|
149 |
+
* @return Mage_Sales_Model_Quote
|
150 |
+
*/
|
151 |
+
public function getQuote()
|
152 |
+
{
|
153 |
+
if (null === $this->_quote) {
|
154 |
+
$this->_quote = $this->getCheckout()->getQuote();
|
155 |
+
}
|
156 |
+
return $this->_quote;
|
157 |
+
}
|
158 |
+
|
159 |
+
/**
|
160 |
+
* From Mage_Checkout_Block_Cart_Abstract
|
161 |
+
* Get checkout session
|
162 |
+
*
|
163 |
+
* @return Mage_Checkout_Model_session
|
164 |
+
*/
|
165 |
+
public function getCheckout()
|
166 |
+
{
|
167 |
+
if (null === $this->_checkout) {
|
168 |
+
$this->_checkout = Mage::getSingleton('checkout/session');
|
169 |
+
}
|
170 |
+
return $this->_checkout;
|
171 |
+
}
|
172 |
+
|
173 |
+
|
174 |
+
/**
|
175 |
+
* Function to get the default template code
|
176 |
+
*
|
177 |
+
* @return string of Template name
|
178 |
+
*/
|
179 |
+
protected function _getTemplate()
|
180 |
+
{
|
181 |
+
switch($this->helper->getCurrentPage()) {
|
182 |
+
case "cms_index_index":
|
183 |
+
return "cms_index_index";
|
184 |
+
break;
|
185 |
+
|
186 |
+
case "catalog_product_view":
|
187 |
+
return "catalog_product_view";
|
188 |
+
break;
|
189 |
+
|
190 |
+
case "catalog_category_view":
|
191 |
+
return "catalog_category_view";
|
192 |
+
break;
|
193 |
+
|
194 |
+
case "checkout_cart_index":
|
195 |
+
return "checkout_cart_index";
|
196 |
+
break;
|
197 |
+
|
198 |
+
case "checkout_onepage_success":
|
199 |
+
return "checkout_onepage_success";
|
200 |
+
break;
|
201 |
+
|
202 |
+
case "checkout_multishipping_success":
|
203 |
+
return "checkout_multishipping_success";
|
204 |
+
break;
|
205 |
+
|
206 |
+
default:
|
207 |
+
return "cms_index_index";
|
208 |
+
break;
|
209 |
+
}
|
210 |
+
}
|
211 |
+
|
212 |
+
public function getDFilter()
|
213 |
+
{
|
214 |
+
if (!$this->hasData('d_filter')) {
|
215 |
+
switch ($this->helper->getCurrentPage()) {
|
216 |
+
case 'catalog_product_view':
|
217 |
+
if ($this->getCatProduct()) {
|
218 |
+
$categories = $this->getProduct()->getCategoryIds();
|
219 |
+
|
220 |
+
$html = '';
|
221 |
+
$i = 1;
|
222 |
+
if (count($categories)) {
|
223 |
+
foreach ($categories as $_cat) {
|
224 |
+
$html .= ($i < count($categories)) ? $_cat . "_._" : $_cat;
|
225 |
+
}
|
226 |
+
$i++;
|
227 |
+
} else $html = false;
|
228 |
+
} else $html = false;
|
229 |
+
break;
|
230 |
+
|
231 |
+
case 'catalog_category_view':
|
232 |
+
$_category = Mage::registry('current_category');
|
233 |
+
$html = 'ctgy_name:'.$_category->getId();
|
234 |
+
break;
|
235 |
+
|
236 |
+
default:
|
237 |
+
$html = false;
|
238 |
+
break;
|
239 |
+
}
|
240 |
+
$this->setData('d_filter', $html);
|
241 |
+
|
242 |
+
|
243 |
+
|
244 |
+
}
|
245 |
+
return $this->getData('d_filter');
|
246 |
+
}
|
247 |
+
|
248 |
+
}
|
249 |
+
|
250 |
+
|
251 |
+
?>
|
app/code/community/Strands/Recommender/Helper/Abstract.php
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
Class Strands_Recommender_Helper_Abstract extends Varien_Object
|
4 |
+
{
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Translate, borrowed from Mage_Core_Helper_Abstract
|
8 |
+
*
|
9 |
+
* @return string
|
10 |
+
*/
|
11 |
+
public function __()
|
12 |
+
{
|
13 |
+
$args = func_get_args();
|
14 |
+
$expr = new Mage_Core_Model_Translate_Expr(array_shift($args), $this->_getModuleName());
|
15 |
+
array_unshift($args, $expr);
|
16 |
+
return Mage::app()->getTranslator()->translate($args);
|
17 |
+
}
|
18 |
+
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Retrieve helper module name
|
22 |
+
*
|
23 |
+
* @return string
|
24 |
+
*/
|
25 |
+
protected function _getModuleName()
|
26 |
+
{
|
27 |
+
if (!$this->_moduleName) {
|
28 |
+
$class = get_class($this);
|
29 |
+
$this->_moduleName = substr($class, 0, strpos($class, '_Helper'));
|
30 |
+
}
|
31 |
+
return $this->_moduleName;
|
32 |
+
}
|
33 |
+
}
|
34 |
+
|
35 |
+
?>
|
app/code/community/Strands/Recommender/Helper/Data.php
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
Class Strands_Recommender_Helper_Data extends Strands_Recommender_Helper_Abstract
|
4 |
+
{
|
5 |
+
|
6 |
+
|
7 |
+
/**
|
8 |
+
* @return the current page
|
9 |
+
*/
|
10 |
+
public function getCurrentPage()
|
11 |
+
{
|
12 |
+
if (!$this->hasData("current_page")) {
|
13 |
+
$module = strtolower(Mage::app()->getRequest()->getModuleName());
|
14 |
+
$controller = strtolower(Mage::app()->getRequest()->getControllerName());
|
15 |
+
$action = strtolower(Mage::app()->getRequest()->getActionName());
|
16 |
+
|
17 |
+
if (isset($action) && $action != '') $this->setCurrentPage($module.'_'.$controller.'_'.$action);
|
18 |
+
elseif (isset($controller) && $controller != '') $this->setCurrentPage($module.'_'.$controller.'_index');
|
19 |
+
else $this->setCurrentPage($module."_index_index");
|
20 |
+
|
21 |
+
if ($this->getCurrentPage() == 'checkout_multishipping_success') $this->setCurrentPage('checkout_onepage_success');
|
22 |
+
} return $this->getData('current_page');
|
23 |
+
}
|
24 |
+
|
25 |
+
}
|
26 |
+
|
27 |
+
?>
|
app/code/community/Strands/Recommender/Helper/Logger.php
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
Class Strands_Recommender_Helper_Logger extends Strands_Recommender_Helper_Data
|
4 |
+
{
|
5 |
+
const PREFIX = "Strands ";
|
6 |
+
const ERROR1 = "Error: Insufficient permissions. File/directory is not writeable";
|
7 |
+
|
8 |
+
protected $_log = "recommender.log";
|
9 |
+
protected $_logHandler = null;
|
10 |
+
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Method to log error messages
|
14 |
+
*
|
15 |
+
* @param $message is error message we are seeking to log
|
16 |
+
* @return boolean, true on success
|
17 |
+
*/
|
18 |
+
public function log($message)
|
19 |
+
{
|
20 |
+
if ($this->_logHandler === null) {
|
21 |
+
$folder = Mage::getConfig()->getVarDir() . "/log";
|
22 |
+
|
23 |
+
if (!file_exists($folder)) {
|
24 |
+
if (!@mkdir($folder)) {
|
25 |
+
echo self::PREFIX . self::ERROR1 . "\r\n";
|
26 |
+
return false;
|
27 |
+
}
|
28 |
+
}
|
29 |
+
|
30 |
+
if (!$this->_logHandler = @fopen($folder . DS . $this->_log, 'a')) {
|
31 |
+
echo self::PREFIX . self::ERROR1 . "\r\n";
|
32 |
+
return false;
|
33 |
+
}
|
34 |
+
|
35 |
+
|
36 |
+
}
|
37 |
+
|
38 |
+
fwrite($this->_logHandler, self::PREFIX . " [" . date("mdY h:i:s") ."]: " . $message . "\r\n");
|
39 |
+
return true;
|
40 |
+
}
|
41 |
+
|
42 |
+
public function __destruct()
|
43 |
+
{
|
44 |
+
fclose($this->_logHandler);
|
45 |
+
}
|
46 |
+
}
|
47 |
+
|
48 |
+
?>
|
app/code/community/Strands/Recommender/Model/Abstract.php
ADDED
@@ -0,0 +1,339 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
Class Strands_Recommender_Model_Abstract extends Mage_Core_Model_Abstract
|
4 |
+
{
|
5 |
+
|
6 |
+
public function getVersion()
|
7 |
+
{
|
8 |
+
$version = '1.3.2';
|
9 |
+
return $version;
|
10 |
+
|
11 |
+
}
|
12 |
+
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Method to retrieve Strands Id Stored in Cookie
|
16 |
+
*
|
17 |
+
* @return self with data
|
18 |
+
*/
|
19 |
+
public function getStrandsId($logged = false)
|
20 |
+
{
|
21 |
+
if (!$this->hasData('strands_id')) {
|
22 |
+
$session = Mage::getSingleton("customer/session");
|
23 |
+
|
24 |
+
if ($session->isLoggedIn()) {
|
25 |
+
$this->setStrandsId($session->getCustomerId());
|
26 |
+
Mage::getModel('core/cookie')->set('strandsSBS_P_UserId',$session->getCustomerId(),true,'/',null,null,false);
|
27 |
+
} elseif ($logged) {
|
28 |
+
if (strlen(Mage::app()->getFrontController()->getRequest()->getCookie('strandsSBS_P_UserId')))
|
29 |
+
return Mage::app()->getFrontController()->getRequest()->getCookie('strandsSBS_P_UserId');
|
30 |
+
elseif (strlen(Mage::app()->getFrontController()->getRequest()->getCookie('strandsSBS_S_UserId')))
|
31 |
+
return Mage::app()->getFrontController()->getRequest()->getCookie('strandsSBS_S_UserId');
|
32 |
+
else {
|
33 |
+
$newCookie = $this->create_anon_cookie();
|
34 |
+
Mage::getModel('core/cookie')->set('strandsSBS_S_UserId',$newCookie,true,'/',null,null,false);
|
35 |
+
return $newCookie;
|
36 |
+
}
|
37 |
+
} else {
|
38 |
+
$this->setStrandsId('');
|
39 |
+
}
|
40 |
+
}
|
41 |
+
return $this->getData('strands_id');
|
42 |
+
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Method to create an anonymous cookie
|
47 |
+
*/
|
48 |
+
private function create_anon_cookie()
|
49 |
+
{
|
50 |
+
|
51 |
+
//$cookie = 'MGN_'.rand(0,1000).(((int)(time()/1000))*rand(0,1000)).rand(0,1000).'_'.$this->ms_time();
|
52 |
+
$cookie = 'MGN_'.$this->ms_time().'_'.rand(0,100000);
|
53 |
+
return $cookie;
|
54 |
+
}
|
55 |
+
|
56 |
+
private function ms_time()
|
57 |
+
{
|
58 |
+
$timeday = gettimeofday();
|
59 |
+
$sec = intval($timeday['sec']);
|
60 |
+
$msec = intval(floor($timeday['usec']/1000));
|
61 |
+
if (strlen($msec) == 2) $msec = '0'.$msec;
|
62 |
+
elseif (strlen($msec) == 1) $msec = '00'.$msec;
|
63 |
+
|
64 |
+
return $sec.$msec;
|
65 |
+
}
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Method to check if the module is active
|
69 |
+
*
|
70 |
+
* @return bool
|
71 |
+
*/
|
72 |
+
public function isActive()
|
73 |
+
{
|
74 |
+
if (!Mage::getStoreConfigFlag('recommender/settings/active')) return false;
|
75 |
+
//if (strlen($this->getCustomerId()) < 1) return false;
|
76 |
+
if (strlen($this->getApiId()) < 1) return false;
|
77 |
+
if (strlen($this->getCustomerToken()) < 1) return false;
|
78 |
+
return true;
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Method to check if the catalog must be uploaded in that very moment
|
83 |
+
*
|
84 |
+
* @return bool
|
85 |
+
*/
|
86 |
+
public function getUploadCatalogNow()
|
87 |
+
{
|
88 |
+
if (!$this->hasData('recommender_uploadcatalognow')) {
|
89 |
+
$this->setData('recommender_uploadcatalognow', Mage::getStoreConfig('recommender/settings/strands_uploadcatalognow'));
|
90 |
+
}
|
91 |
+
return $this->getData('recommender_uploadcatalognow');
|
92 |
+
}
|
93 |
+
|
94 |
+
public function isDirectory()
|
95 |
+
{
|
96 |
+
if (!$this->isActive()) return false;
|
97 |
+
return (Mage::getStoreConfigFlag('recommender/settings/freedirectory')) ? true : false;
|
98 |
+
}
|
99 |
+
|
100 |
+
/**
|
101 |
+
* Method to retrieve customer id
|
102 |
+
*
|
103 |
+
* @return self
|
104 |
+
*/
|
105 |
+
|
106 |
+
public function getCustomerId()
|
107 |
+
{
|
108 |
+
if (!$this->hasData('recommender_id')) {
|
109 |
+
$this->setData('recommender_id', Mage::getStoreConfig('recommender/settings/strands_customer_id'));
|
110 |
+
}
|
111 |
+
return $this->getData('recommender_id');
|
112 |
+
}
|
113 |
+
|
114 |
+
|
115 |
+
|
116 |
+
/**
|
117 |
+
* Method to retrieve api id
|
118 |
+
*
|
119 |
+
* @return self
|
120 |
+
*/
|
121 |
+
public function getApiId()
|
122 |
+
{
|
123 |
+
if (!$this->hasData('recommender_api_id')) {
|
124 |
+
$this->setData('recommender_api_id', Mage::getStoreConfig('recommender/settings/strands_api_id'));
|
125 |
+
}
|
126 |
+
return $this->getData('recommender_api_id');
|
127 |
+
}
|
128 |
+
|
129 |
+
|
130 |
+
/**
|
131 |
+
* Method to retrieve customer token
|
132 |
+
*
|
133 |
+
* @return self
|
134 |
+
*/
|
135 |
+
public function getCustomerToken()
|
136 |
+
{
|
137 |
+
if (!$this->hasData('recommender_customer_token')) {
|
138 |
+
$this->setData('recommender_customer_token', Mage::getStoreConfig('recommender/settings/strands_customer_token'));
|
139 |
+
}
|
140 |
+
return $this->getData('recommender_customer_token');
|
141 |
+
}
|
142 |
+
|
143 |
+
|
144 |
+
/**
|
145 |
+
* Method to retrieve strands login
|
146 |
+
*
|
147 |
+
* @return self
|
148 |
+
*/
|
149 |
+
public function getLogin()
|
150 |
+
{
|
151 |
+
if (!$this->hasData('recommender_login')) {
|
152 |
+
$this->setData('recommender_login', Mage::getStoreConfig('recommender/settings/strands_login'));
|
153 |
+
}
|
154 |
+
return $this->getData('recommender_login');
|
155 |
+
}
|
156 |
+
|
157 |
+
|
158 |
+
/**
|
159 |
+
* Method to retrieve strands password
|
160 |
+
*
|
161 |
+
* @return self
|
162 |
+
*/
|
163 |
+
public function getPassword()
|
164 |
+
{
|
165 |
+
if (!$this->hasData('recommender_pass')) {
|
166 |
+
$this->setData('recommender_pass', Mage::getStoreConfig('recommender/settings/strands_password'));
|
167 |
+
}
|
168 |
+
return $this->getData('recommender_pass');
|
169 |
+
}
|
170 |
+
|
171 |
+
|
172 |
+
/**
|
173 |
+
* Method to retrieve desired catalog upload time
|
174 |
+
*/
|
175 |
+
public function getDesiredCatalogTime()
|
176 |
+
{
|
177 |
+
if (!$this->hasData('recommender_desired_catalog_time')) {
|
178 |
+
$this->setData('recommender_desired_catalog_time', Mage::getStoreConfig('recommender/settings/strands_desired_catalog_time'));
|
179 |
+
}
|
180 |
+
return $this->getData('recommender_desired_catalog_time');
|
181 |
+
}
|
182 |
+
|
183 |
+
/**
|
184 |
+
* Method to retrieve Log status
|
185 |
+
*
|
186 |
+
* @return self with data
|
187 |
+
*/
|
188 |
+
public function getLogStatus()
|
189 |
+
{
|
190 |
+
if (!$this->hasData('log_status')) {
|
191 |
+
$this->setData('log_status', Mage::getStoreConfig('recommender/settings/log'));
|
192 |
+
}
|
193 |
+
return $this->getData('log_status');
|
194 |
+
}
|
195 |
+
|
196 |
+
/**
|
197 |
+
* Method borrowed from Mage_Catalog_Block_Product
|
198 |
+
*
|
199 |
+
* @return product or false
|
200 |
+
*/
|
201 |
+
public function getCurrentProduct()
|
202 |
+
{
|
203 |
+
if (!$this->hasData('product')) {
|
204 |
+
$this->setData('product', Mage::registry('product'));
|
205 |
+
}
|
206 |
+
return $this->getData('product');
|
207 |
+
}
|
208 |
+
|
209 |
+
|
210 |
+
public function extractCatalog($localpath,$localfile)
|
211 |
+
{
|
212 |
+
|
213 |
+
$xml = new DOMDocument('1.0','utf-8');
|
214 |
+
$store = Mage::app()->getStore();
|
215 |
+
|
216 |
+
$model = Mage::getModel('recommender/abstract');
|
217 |
+
|
218 |
+
$root = $xml->createElement('sbs-catalog');
|
219 |
+
$root->appendChild($xml->createAttribute('version'))->appendChild($xml->createTextNode('1.0'));
|
220 |
+
$xml->appendChild($root);
|
221 |
+
|
222 |
+
$root->appendChild($xml->createElement('company'))->appendChild($xml->createTextNode(htmlentities(Mage::getStoreConfig('general/store_information/name'))));
|
223 |
+
$root->appendChild($xml->createElement('link'))->appendChild($xml->createTextNode(htmlentities($store->getBaseUrl())));
|
224 |
+
$root->appendChild($xml->createElement('description'))->appendChild($xml->createTextNode(htmlentities(Mage::getStoreConfig('general/store_information/name'))));
|
225 |
+
|
226 |
+
$collection = Mage::getModel('catalog/product')
|
227 |
+
->getCollection()
|
228 |
+
->addAttributeToSelect('sku')
|
229 |
+
->addAttributeToSelect('name')
|
230 |
+
->addAttributeToSelect('attribute_set_id')
|
231 |
+
->addAttributeToSelect('type_id');
|
232 |
+
|
233 |
+
foreach ($collection as $item) {
|
234 |
+
$_product = Mage::getModel('catalog/product')->load($item->getId());
|
235 |
+
|
236 |
+
$parentIdArray = $_product->loadParentProductIds()->getData('parent_product_ids');
|
237 |
+
if (count($parentIdArray) && $_product->getVisibility() == 1) {
|
238 |
+
continue;
|
239 |
+
}
|
240 |
+
|
241 |
+
$item = $xml->createElement('item');
|
242 |
+
$item->appendChild($xml->createElement('id'))->appendChild($xml->createCDATASection($_product->getId()));
|
243 |
+
$item->appendChild($xml->createElement('title'))->appendChild($xml->createCDATASection("'".htmlentities($_product->getName()."'")));
|
244 |
+
$item->appendChild($xml->createElement('link'))->appendChild($xml->createTextNode(htmlentities($_product->getProductUrl())));
|
245 |
+
$item->appendChild($xml->createElement('description'))->appendChild($xml->createCDATASection($_product->getDescription()));
|
246 |
+
$item->appendChild($xml->createElement('image_link'))->appendChild($xml->createTextNode(htmlentities(Mage::helper('catalog/image')->init($_product,'image'))));
|
247 |
+
$item->appendChild($xml->createElement('small_image_link'))->appendChild($xml->createTextNode(htmlentities(Mage::helper('catalog/image')->init($_product,'small_image')->resize(135))));
|
248 |
+
$item->appendChild($xml->createElement('saleprice'))->appendChild($xml->createCDATASection($_product->getPrice()));
|
249 |
+
|
250 |
+
$item->appendChild($xml->createElement('sku'))->appendChild($xml->createCDATASection($_product->getSku()));
|
251 |
+
|
252 |
+
$_attributes = $this->getAdditionalData($_product);
|
253 |
+
|
254 |
+
foreach ($_attributes as $_attr) {
|
255 |
+
$item->appendChild($xml->createElement('a_'.$_attr['code']))->appendChild($xml->createCDATASection($_attr['value']));
|
256 |
+
}
|
257 |
+
|
258 |
+
|
259 |
+
|
260 |
+
$_options = $_product->getOptions();
|
261 |
+
if (count($_options)) {
|
262 |
+
foreach($_options as $_option) {
|
263 |
+
foreach ($_option->getData() as $_key => $_val) {
|
264 |
+
$item->appendChild($xml->createElement('o_'.$_key))->appendChild($xml->createCDATASection($_val));
|
265 |
+
}
|
266 |
+
|
267 |
+
foreach($_option->getValues() as $_value) {
|
268 |
+
foreach ($_value->getData() as $_key => $_val) {
|
269 |
+
$item->appendChild($xml->createElement('o_'.$_key))->appendChild($xml->createCDATASection($_val));
|
270 |
+
}
|
271 |
+
}
|
272 |
+
}
|
273 |
+
}
|
274 |
+
|
275 |
+
if (count($_product->getCategoryIds())) {
|
276 |
+
foreach ($_product->getCategoryIds() as $_cat) {
|
277 |
+
$item->appendChild($xml->createElement('category'))->appendChild($xml->createTextNode($_cat));
|
278 |
+
}
|
279 |
+
}
|
280 |
+
|
281 |
+
$root->appendChild($item);
|
282 |
+
|
283 |
+
}
|
284 |
+
|
285 |
+
$output = $xml->saveXML();
|
286 |
+
|
287 |
+
$sc = fopen("$localpath$localfile",'w');
|
288 |
+
fwrite($sc,$output);
|
289 |
+
fclose($sc);
|
290 |
+
|
291 |
+
}
|
292 |
+
|
293 |
+
|
294 |
+
|
295 |
+
/**
|
296 |
+
* Code borrowed from Mage_Catalog_Block_Product_View_Attributes
|
297 |
+
*
|
298 |
+
* @return additional data for product
|
299 |
+
*/
|
300 |
+
public function getAdditionalData($product, array $excludeAttr = array())
|
301 |
+
{
|
302 |
+
if (!$product) return;
|
303 |
+
|
304 |
+
$data = array();
|
305 |
+
//$product = $product;
|
306 |
+
$attributes = $product->getAttributes();
|
307 |
+
foreach ($attributes as $attribute) {
|
308 |
+
// if ($attribute->getIsVisibleOnFront() && $attribute->getIsUserDefined() && !in_array($attribute->getAttributeCode(), $excludeAttr)) {
|
309 |
+
if (!in_array($attribute->getAttributeCode(), $excludeAttr)) {
|
310 |
+
|
311 |
+
$value = $attribute->getFrontend()->getValue($product);
|
312 |
+
|
313 |
+
// TODO this is temporary skipping eco taxes
|
314 |
+
if (is_string($value)) {
|
315 |
+
if (strlen($value) && $product->hasData($attribute->getAttributeCode())) {
|
316 |
+
if ($attribute->getFrontendInput() == 'price') {
|
317 |
+
$value = Mage::app()->getStore()->convertPrice($value,true);
|
318 |
+
}
|
319 |
+
|
320 |
+
//$value = htmlentities($value); // REMOVED
|
321 |
+
$data[$attribute->getAttributeCode()] = array(
|
322 |
+
'label' => $attribute->getStoreLabel(),
|
323 |
+
'value' => $value,
|
324 |
+
'code' => $attribute->getAttributeCode()
|
325 |
+
);
|
326 |
+
}
|
327 |
+
}
|
328 |
+
}
|
329 |
+
}
|
330 |
+
return $data;
|
331 |
+
}
|
332 |
+
|
333 |
+
|
334 |
+
|
335 |
+
|
336 |
+
|
337 |
+
}
|
338 |
+
|
339 |
+
?>
|
app/code/community/Strands/Recommender/Model/Api.php
ADDED
@@ -0,0 +1,194 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
Class Strands_Recommender_Model_Api extends Strands_Recommender_Model_Abstract
|
4 |
+
{
|
5 |
+
const DEBUG = false;
|
6 |
+
|
7 |
+
protected $_items = null;
|
8 |
+
protected $_param = array();
|
9 |
+
|
10 |
+
|
11 |
+
public function _construct()
|
12 |
+
{
|
13 |
+
$this->setBaseApiUrl("http://bizsolutions.strands.com/api2/event/");
|
14 |
+
}
|
15 |
+
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Method for Api Call
|
19 |
+
*
|
20 |
+
* @param $args array of different args for the call
|
21 |
+
*/
|
22 |
+
public function callApi()
|
23 |
+
{
|
24 |
+
$session = Mage::getSingleton('core/session');
|
25 |
+
|
26 |
+
//if (!$this->isActive()) return;
|
27 |
+
|
28 |
+
///$apiCall = $this->getBaseApiUrl() . $this->getApiAction() . '.sbs?apid=' . $this->getApiId();
|
29 |
+
|
30 |
+
$apiCall = $this->getBaseApiUrl();
|
31 |
+
|
32 |
+
if ($this->getApiAction()) {
|
33 |
+
$apiCall = $apiCall . $this->getApiAction() . '.sbs?apid=' . $this->getApiId();
|
34 |
+
} else {
|
35 |
+
$apiCall = $apiCall . '?apid=' . $this->getApiId() . '&token=' . $this->getCustomerToken();
|
36 |
+
$apiCall = $apiCall . '&version=' . $this->getVersion();
|
37 |
+
if ($this->getTarget() == 'setup') {
|
38 |
+
$apiCall = $apiCall . '&id=id&title=title&link=link&description=description&image_link=image_link&price=saleprice&tag=tag&category=category';
|
39 |
+
}
|
40 |
+
}
|
41 |
+
|
42 |
+
|
43 |
+
if ($this->getApiAction() == 'userlogged'){
|
44 |
+
/*
|
45 |
+
* Block for user logged in action
|
46 |
+
*/
|
47 |
+
$apiCall .= "&olduser=" . $this->getOldUser();
|
48 |
+
}
|
49 |
+
elseif ($this->getApiAction() == "addshoppingcart") {
|
50 |
+
/*
|
51 |
+
* Block for modifying the shopping cart
|
52 |
+
*/
|
53 |
+
|
54 |
+
if (count($this->getCartItems())) {
|
55 |
+
$_items = '';
|
56 |
+
|
57 |
+
foreach ($this->getCartItems() as $item) {
|
58 |
+
if ($item->getHasChildren()) {
|
59 |
+
if ($item->getParentItem() != null) {
|
60 |
+
continue;
|
61 |
+
} else {
|
62 |
+
continue;
|
63 |
+
}
|
64 |
+
} else {
|
65 |
+
if ($item->getParentItem() != null) {
|
66 |
+
$_items .= "&item=" . urlencode($item->getParentItem()->getProductId());
|
67 |
+
} else {
|
68 |
+
$_items .= "&item=" . urlencode($item->getProductId());
|
69 |
+
}
|
70 |
+
}
|
71 |
+
}
|
72 |
+
$apiCall .= $_items;
|
73 |
+
}
|
74 |
+
}
|
75 |
+
|
76 |
+
if ($this->hasData('product')) $apiCall .= "&item=" . urlencode($this->getProduct());
|
77 |
+
if ($this->hasData('items')) $apiCall .= $this->getItems();
|
78 |
+
if ($this->hasData('search_keywords')) $apiCall .= "&searchstring=" . urlencode($this->getSearchKeywords());
|
79 |
+
if ($this->hasData('user') && strlen($this->getUser())) $apiCall .= "&user=" . $this->getUser();
|
80 |
+
if ($this->hasData('type')) $apiCall .= "&type=" . $this->getType();
|
81 |
+
if ($this->hasData('passv_api_url')) $apiCall .= "&passvapi=" . $this->getPassvApiUrl();
|
82 |
+
if ($this->hasData('currency')) $apiCall .= "¤cy=" . $this->getCurrency();
|
83 |
+
|
84 |
+
$response = $this->makeCall($apiCall);
|
85 |
+
|
86 |
+
|
87 |
+
if ($this->getLogStatus()) {
|
88 |
+
$message = ($response['success']) ? 'Success' : 'Failed';
|
89 |
+
$message .= ": Request Call: $apiCall\r\n";
|
90 |
+
$message .= $response['message'];
|
91 |
+
|
92 |
+
|
93 |
+
if (!$success = Mage::helper('recommender/logger')->log($message)) {
|
94 |
+
//$session->addError('Recommender log failed to instantate or write');
|
95 |
+
}
|
96 |
+
}
|
97 |
+
|
98 |
+
//if ($response['success']) $session->addError($response['message']);
|
99 |
+
//else $session->addError($response['message']);
|
100 |
+
|
101 |
+
return $this;
|
102 |
+
}
|
103 |
+
|
104 |
+
|
105 |
+
/**
|
106 |
+
* Method for cURL call
|
107 |
+
*
|
108 |
+
* @param $url is the URL we are calling
|
109 |
+
* @return array response
|
110 |
+
*/
|
111 |
+
protected function makeCall($url, $params = array())
|
112 |
+
{
|
113 |
+
$ret = array('success' => true);
|
114 |
+
|
115 |
+
try {
|
116 |
+
$curl = curl_init();
|
117 |
+
curl_setopt($curl, CURLOPT_URL, $url);
|
118 |
+
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, false);
|
119 |
+
curl_setopt($curl, CURLOPT_HEADER, false);
|
120 |
+
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); //
|
121 |
+
//curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 2);
|
122 |
+
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
123 |
+
curl_setopt($curl, CURLOPT_USERAGENT, "Magento Strands Recommender v1.0");
|
124 |
+
|
125 |
+
if (isset($params['CURLOPT']) && count($params['CURLOPT'])) {
|
126 |
+
foreach ($params['CURLOPT'] as $k => $v) {
|
127 |
+
curl_setopt($curl, constant("CURLOPT_$k"), $v);
|
128 |
+
}
|
129 |
+
}
|
130 |
+
|
131 |
+
$ret['message'] = curl_exec($curl);
|
132 |
+
$ret['message'] = ($this->getMessage()) ? $ret['message'] : 'Success';
|
133 |
+
|
134 |
+
curl_close($curl);
|
135 |
+
} catch(Exception $e) {
|
136 |
+
$ret['success'] = false;
|
137 |
+
$ret['message'] = $e->getMessage();
|
138 |
+
}
|
139 |
+
|
140 |
+
return $ret;
|
141 |
+
}
|
142 |
+
|
143 |
+
public function callApiTwo()
|
144 |
+
{
|
145 |
+
$apiCall = $this->getBaseApiUrl() . "?";
|
146 |
+
$session = Mage::getModel('adminhtml/session');
|
147 |
+
|
148 |
+
$i = 0;
|
149 |
+
$params_data = '';
|
150 |
+
foreach ($this->_param as $key => $val) {
|
151 |
+
$params_data .= ($i > 0) ? "&$key=$val" : "$key=$val";
|
152 |
+
$i++;
|
153 |
+
}
|
154 |
+
|
155 |
+
$options = array(
|
156 |
+
'CURLOPT' => array(
|
157 |
+
'POST' => true,
|
158 |
+
'POSTFIELDS' => $params_data
|
159 |
+
));
|
160 |
+
|
161 |
+
$response = $this->makeCall($apiCall, $options);
|
162 |
+
|
163 |
+
$serialOpts = serialize($options);
|
164 |
+
if ($this->getLogStatus()) {
|
165 |
+
$message = ($response['success']) ? 'Success' : 'Failed';
|
166 |
+
$message .= ": Request Call: $apiCall\r\n";
|
167 |
+
$message .= "Params: $params_data\r\n";
|
168 |
+
$message .= "Options: $options\r\n";
|
169 |
+
$message .= $response['message'];
|
170 |
+
|
171 |
+
|
172 |
+
if (!$success = Mage::helper('recommender/logger')->log($message)) {
|
173 |
+
//$session->addError('Recommender log failed to instantate or write');
|
174 |
+
}
|
175 |
+
}
|
176 |
+
|
177 |
+
return $this;
|
178 |
+
}
|
179 |
+
|
180 |
+
public function setParam($key, $val)
|
181 |
+
{
|
182 |
+
$this->_param[$key] = $val;
|
183 |
+
return $this;
|
184 |
+
}
|
185 |
+
|
186 |
+
protected function getMessage()
|
187 |
+
{
|
188 |
+
if (!$this->hasData('message')) $this->setMessage(true);
|
189 |
+
return $this->getData('message');
|
190 |
+
}
|
191 |
+
|
192 |
+
}
|
193 |
+
|
194 |
+
?>
|
app/code/community/Strands/Recommender/Model/Numberoption.php
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
|
4 |
+
/**
|
5 |
+
* Used in creating options for Numbers selection
|
6 |
+
*
|
7 |
+
*/
|
8 |
+
class Strands_Recommender_Model_Numberoption
|
9 |
+
{
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Options getter
|
13 |
+
*
|
14 |
+
* @return array
|
15 |
+
*/
|
16 |
+
public function toOptionArray()
|
17 |
+
{
|
18 |
+
return array(
|
19 |
+
array('value' => 0, 'label'=>Mage::helper('adminhtml')->__('0')),
|
20 |
+
array('value' => 1, 'label'=>Mage::helper('adminhtml')->__('1')),
|
21 |
+
array('value' => 2, 'label'=>Mage::helper('adminhtml')->__('2')),
|
22 |
+
array('value' => 3, 'label'=>Mage::helper('adminhtml')->__('3')),
|
23 |
+
array('value' => 4, 'label'=>Mage::helper('adminhtml')->__('4')),
|
24 |
+
array('value' => 5, 'label'=>Mage::helper('adminhtml')->__('5')),
|
25 |
+
array('value' => 6, 'label'=>Mage::helper('adminhtml')->__('6')),
|
26 |
+
array('value' => 7, 'label'=>Mage::helper('adminhtml')->__('7')),
|
27 |
+
array('value' => 8, 'label'=>Mage::helper('adminhtml')->__('8')),
|
28 |
+
array('value' => 9, 'label'=>Mage::helper('adminhtml')->__('9')),
|
29 |
+
array('value' => 10, 'label'=>Mage::helper('adminhtml')->__('10')),
|
30 |
+
array('value' => 11, 'label'=>Mage::helper('adminhtml')->__('11')),
|
31 |
+
array('value' => 12, 'label'=>Mage::helper('adminhtml')->__('12')),
|
32 |
+
array('value' => 13, 'label'=>Mage::helper('adminhtml')->__('13')),
|
33 |
+
array('value' => 14, 'label'=>Mage::helper('adminhtml')->__('14')),
|
34 |
+
array('value' => 15, 'label'=>Mage::helper('adminhtml')->__('15')),
|
35 |
+
array('value' => 16, 'label'=>Mage::helper('adminhtml')->__('16')),
|
36 |
+
array('value' => 17, 'label'=>Mage::helper('adminhtml')->__('17')),
|
37 |
+
array('value' => 18, 'label'=>Mage::helper('adminhtml')->__('18')),
|
38 |
+
array('value' => 19, 'label'=>Mage::helper('adminhtml')->__('19')),
|
39 |
+
array('value' => 20, 'label'=>Mage::helper('adminhtml')->__('20')),
|
40 |
+
array('value' => 21, 'label'=>Mage::helper('adminhtml')->__('21')),
|
41 |
+
array('value' => 22, 'label'=>Mage::helper('adminhtml')->__('22')),
|
42 |
+
array('value' => 23, 'label'=>Mage::helper('adminhtml')->__('23')),
|
43 |
+
);
|
44 |
+
}
|
45 |
+
|
46 |
+
}
|
47 |
+
|
48 |
+
?>
|
app/code/community/Strands/Recommender/Model/Observer.php
ADDED
@@ -0,0 +1,297 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
Class Strands_Recommender_Model_Observer extends Strands_Recommender_Model_Abstract
|
4 |
+
{
|
5 |
+
|
6 |
+
|
7 |
+
/**
|
8 |
+
* replacement constructor method
|
9 |
+
*/
|
10 |
+
protected function _construct()
|
11 |
+
{
|
12 |
+
parent::_construct();
|
13 |
+
}
|
14 |
+
|
15 |
+
|
16 |
+
/**
|
17 |
+
* call method for events
|
18 |
+
* At call we make a url call to strands
|
19 |
+
*
|
20 |
+
* @param $observer observer from the dispatch Event
|
21 |
+
*/
|
22 |
+
public function eventCartChange($observer)
|
23 |
+
{
|
24 |
+
if (!$this->isActive()) return;
|
25 |
+
$api = Mage::getSingleton('recommender/api');
|
26 |
+
|
27 |
+
$api->setApiAction('addshoppingcart')
|
28 |
+
->setUser($this->getStrandsId(true))
|
29 |
+
->setCartItems($this->getCartItems())
|
30 |
+
->callApi();
|
31 |
+
|
32 |
+
return $this;
|
33 |
+
}
|
34 |
+
|
35 |
+
|
36 |
+
public function eventWishlistAdd($observer)
|
37 |
+
{
|
38 |
+
if (!$this->isActive()) return;
|
39 |
+
$api = Mage::getSingleton('recommender/api');
|
40 |
+
$product = $observer->getEvent()->getProduct();
|
41 |
+
|
42 |
+
$api->setApiAction('addwishlist')
|
43 |
+
->setUser($this->getStrandsId(true))
|
44 |
+
->setProduct($product->getId())
|
45 |
+
->callApi();
|
46 |
+
|
47 |
+
return $this;
|
48 |
+
}
|
49 |
+
|
50 |
+
|
51 |
+
public function eventUserLogin($observer)
|
52 |
+
{
|
53 |
+
if (!$this->isActive()) return;
|
54 |
+
$api = Mage::getSingleton('recommender/api');
|
55 |
+
$session = Mage::getSingleton("customer/session");
|
56 |
+
|
57 |
+
if (strlen(Mage::app()->getFrontController()->getRequest()->getCookie('strandsSBS_P_UserId'))) {
|
58 |
+
Mage::getModel('core/cookie')->set('strandsSBS_P_UserId',$session->getCustomerId(),true,'/',null,null,false);
|
59 |
+
} else {
|
60 |
+
if (strlen(Mage::app()->getFrontController()->getRequest()->getCookie('strandsSBS_S_UserId'))) {
|
61 |
+
$api->setApiAction('userlogged')
|
62 |
+
->setOldUser(Mage::app()->getFrontController()->getRequest()->getCookie('strandsSBS_S_UserId'))
|
63 |
+
->setUser($this->getStrandsId(true))
|
64 |
+
->callApi();
|
65 |
+
} else {
|
66 |
+
Mage::getModel('core/cookie')->set('strandsSBS_P_UserId',$session->getCustomerId(),true,'/',null,null,false);
|
67 |
+
}
|
68 |
+
}
|
69 |
+
|
70 |
+
return $this;
|
71 |
+
}
|
72 |
+
|
73 |
+
|
74 |
+
public function eventPostDispatchSearch($observer)
|
75 |
+
{
|
76 |
+
if (!$this->isActive()) return;
|
77 |
+
$api = Mage::getSingleton('recommender/api');
|
78 |
+
$data = Mage::helper('recommender');
|
79 |
+
|
80 |
+
$api->setApiAction('searched')
|
81 |
+
->setSearchKeywords(Mage::app()->getFrontController()->getRequest()->getParam('q'))
|
82 |
+
->setUser($this->getStrandsId(true))
|
83 |
+
->callApi();
|
84 |
+
|
85 |
+
return $this;
|
86 |
+
}
|
87 |
+
|
88 |
+
|
89 |
+
public function eventPostDispatchProductView($observer)
|
90 |
+
{
|
91 |
+
if (!$this->isActive()) return;
|
92 |
+
|
93 |
+
$api = Mage::getSingleton('recommender/api');
|
94 |
+
$data = Mage::helper('recommender');
|
95 |
+
|
96 |
+
$api->setApiAction('visited')
|
97 |
+
->setProduct($this->getCurrentProduct()->getId())
|
98 |
+
->setUser($this->getStrandsId(true))
|
99 |
+
->callApi();
|
100 |
+
|
101 |
+
return $this;
|
102 |
+
}
|
103 |
+
|
104 |
+
|
105 |
+
public function eventPreDispatch($observer)
|
106 |
+
{
|
107 |
+
$api = Mage::getSingleton('recommender/api');
|
108 |
+
$data = Mage::helper('recommender');
|
109 |
+
|
110 |
+
$cont = $observer->getEvent()->getControllerAction();
|
111 |
+
$groups = $cont->getRequest()->getPost('groups');
|
112 |
+
|
113 |
+
if ($cont->getRequest()->getParam('section') == 'recommender') {
|
114 |
+
$callApi = false;
|
115 |
+
$freedirectory = false;
|
116 |
+
|
117 |
+
$fields = $cont->getRequest()->getParam('section');
|
118 |
+
if (intval(Mage::getStoreConfigFlag('recommender/settings/active')) != intval($groups['settings']['fields']['active']['value'])) {
|
119 |
+
if ($groups['settings']['fields']['active']['value']) {
|
120 |
+
//$api->setBaseApiUrl('https://recommender.strands.com/signup_activate');
|
121 |
+
$api->setBaseApiUrl('https://recommender.strands.com/account/plugin/setup/');
|
122 |
+
$api->setTarget('setup');
|
123 |
+
$callApi = true;
|
124 |
+
///if ($groups['settings']['fields']['freedirectory']['value']) $freedirectory = true;
|
125 |
+
} else {
|
126 |
+
//$api->setBaseApiUrl('https://recommender.strands.com/signup_deactivate');
|
127 |
+
$api->setBaseApiUrl('https://recommender.strands.com/account/plugin/deactivate/');
|
128 |
+
$api->setTarget('deactivate');
|
129 |
+
$callApi = true;
|
130 |
+
}
|
131 |
+
}
|
132 |
+
|
133 |
+
elseif (Mage::getStoreConfig('recommender/settings/strands_api_id') != $groups['settings']['fields']['strands_api_id']['value']) {
|
134 |
+
//$api->setBaseApiUrl('https://recommender.strands.com/signup_activate');
|
135 |
+
$api->setBaseApiUrl('https://recommender.strands.com/account/plugin/setup/');
|
136 |
+
$api->setTarget('setup');
|
137 |
+
$callApi = true;
|
138 |
+
///if ($groups['settings']['fields']['freedirectory']['value']) $freedirectory = true;
|
139 |
+
}
|
140 |
+
|
141 |
+
elseif (Mage::getStoreConfig('recommender/settings/strands_customer_token') != $groups['settings']['fields']['strands_customer_token']['value']) {
|
142 |
+
$api->setBaseApiUrl('https://recommender.strands.com/account/plugin/setup/');
|
143 |
+
$api->setTarget('setup');
|
144 |
+
$callApi = true;
|
145 |
+
}
|
146 |
+
|
147 |
+
|
148 |
+
|
149 |
+
if ($callApi)
|
150 |
+
$api->setType('magento')
|
151 |
+
->setMessage(false)
|
152 |
+
->setRecommenderApiId($groups['settings']['fields']['strands_api_id']['value'])
|
153 |
+
->setRecommenderCustomerToken($groups['settings']['fields']['strands_customer_token']['value'])
|
154 |
+
////->setPassvApiUrl(htmlentities(Mage::getUrl("recommender/index", array('_secure' => true))))
|
155 |
+
//->setPassvApiUrl('1')
|
156 |
+
->callApi();
|
157 |
+
|
158 |
+
|
159 |
+
if ($freedirectory)
|
160 |
+
$api->setBaseApiUrl('http://www.emediastores.com/mcms/index.php')
|
161 |
+
->setMessage(true)
|
162 |
+
->setParam('rt', 'api/registerrv')
|
163 |
+
->setParam('cart', 'magento')
|
164 |
+
->setParam('passvapi', Mage::getUrl("recommender/index", array('_secure' => true)))
|
165 |
+
->setParam('api', 'strands')
|
166 |
+
->setParam('customer_id', $this->getCustomerId())
|
167 |
+
->setParam('api_key', $this->getApiId())
|
168 |
+
->setParam('store_code', Mage::app()->getStore()->getId())
|
169 |
+
->setParam('store_name', Mage::app()->getStore()->getName())
|
170 |
+
->setParam('store_owner', Mage::getStoreConfig('trans_email/ident_general/name'))
|
171 |
+
->setParam('store_email', Mage::getStoreConfig('trans_email/ident_general/email'))
|
172 |
+
->setParam('store_company', Mage::getStoreConfig('general/store_information/name'))
|
173 |
+
->setParam('store_address', Mage::getStoreConfig('general/store_information/address'))
|
174 |
+
->setParam('store_phone', Mage::getStoreConfig('general/store_information/phone'))
|
175 |
+
->callApiTwo();
|
176 |
+
|
177 |
+
|
178 |
+
if (intval($groups['settings']['fields']['strands_catalog_upload_now']['value']) == 1) {
|
179 |
+
Mage::getConfig()->saveConfig('recommender/settings/strands_uploadcatalognow',time());
|
180 |
+
}
|
181 |
+
|
182 |
+
}
|
183 |
+
|
184 |
+
|
185 |
+
return $this;
|
186 |
+
}
|
187 |
+
|
188 |
+
|
189 |
+
public function eventPayInvoice($observer)
|
190 |
+
{
|
191 |
+
if (!$this->isActive()) return;
|
192 |
+
$api = Mage::getSingleton('recommender/api');
|
193 |
+
$order = $observer->getEvent()->getPayment()->getOrder();
|
194 |
+
$items = $order->getItemsCollection();
|
195 |
+
|
196 |
+
$itemsUrl = '';
|
197 |
+
|
198 |
+
foreach ($items as $item) {
|
199 |
+
if ($item->getHasChildren()) {
|
200 |
+
if ($item->getParentItem() != null) {
|
201 |
+
continue;
|
202 |
+
} else {
|
203 |
+
continue;
|
204 |
+
}
|
205 |
+
} else {
|
206 |
+
if ($item->getParentItem() != null) {
|
207 |
+
$itemsUrl .= "&item=" . urlencode($item->getParentItem()->getProductId() . "::" . $item->getParentItem()->getPrice() . "::" . $item->getParentItem()->getQtyOrdered());
|
208 |
+
} else {
|
209 |
+
$itemsUrl .= "&item=" . urlencode($item->getProductId() . "::" . $item->getPrice() . "::" . $item->getQtyOrdered());
|
210 |
+
}
|
211 |
+
}
|
212 |
+
|
213 |
+
}
|
214 |
+
|
215 |
+
if ($order->getOrderCurrencyCode() != null)
|
216 |
+
$currency_code = $order->getOrderCurrencyCode();
|
217 |
+
else
|
218 |
+
$currency_code = Mage::app()->getStore()->getCurrentCurrencyCode();
|
219 |
+
|
220 |
+
$api->setApiAction('purchased')
|
221 |
+
->setUser($this->getStrandsId(true))
|
222 |
+
->setItems($itemsUrl)
|
223 |
+
->setCurrency($currency_code)
|
224 |
+
->callApi();
|
225 |
+
}
|
226 |
+
|
227 |
+
|
228 |
+
public function uploadCatalog()
|
229 |
+
{
|
230 |
+
|
231 |
+
if (!$this->isActive()) return;
|
232 |
+
|
233 |
+
$timeBefore = date(DATE_RSS);
|
234 |
+
|
235 |
+
$timeCatalog = $this->getDesiredCatalogTime();
|
236 |
+
if ($timeCatalog == null)
|
237 |
+
$timeCatalog = '0';
|
238 |
+
|
239 |
+
$currentTime = getdate();
|
240 |
+
$currentHour = $currentTime['hours'];
|
241 |
+
$currentMinute = $currentTime['minutes'];
|
242 |
+
$catalogHour = intval($timeCatalog);
|
243 |
+
|
244 |
+
if (($this->getUploadCatalogNow()+65 < time()) && (($currentHour != $catalogHour) || ($currentMinute != intval('27')))) {
|
245 |
+
return;
|
246 |
+
}
|
247 |
+
|
248 |
+
$localfile = "strandsCatalog.xml";
|
249 |
+
$absPath = Mage::getBaseDir();
|
250 |
+
$localpath = $absPath."/media/";
|
251 |
+
|
252 |
+
$this->extractCatalog($localpath,$localfile);
|
253 |
+
|
254 |
+
$timeAfter = date(DATE_RSS);
|
255 |
+
|
256 |
+
$ftpserver = "recommender.strands.com";
|
257 |
+
$ftppath = "/catalog/complete";
|
258 |
+
$ftpuser = $this->getLogin();
|
259 |
+
$ftppass = $this->getPassword();
|
260 |
+
|
261 |
+
$remoteurl = "ftp://${ftpuser}:${ftppass}@${ftpserver}${ftppath}/${localfile}";
|
262 |
+
|
263 |
+
$fp = curl_init();
|
264 |
+
|
265 |
+
$lc = fopen("$localpath$localfile","r");
|
266 |
+
|
267 |
+
curl_setopt($fp, CURLOPT_URL, $remoteurl);
|
268 |
+
// curl_setopt($fp, CURLOPT_BINARYTRANSFER, 0);
|
269 |
+
curl_setopt($fp, CURLOPT_UPLOAD, 1);
|
270 |
+
curl_setopt($fp, CURLOPT_INFILE, $lc);
|
271 |
+
|
272 |
+
$success = curl_exec($fp);
|
273 |
+
curl_close($fp);
|
274 |
+
fclose($lc);
|
275 |
+
|
276 |
+
if ($success) {
|
277 |
+
Mage::getConfig()->saveConfig('recommender/settings/strands_catalog_time',date(DATE_RSS));
|
278 |
+
Mage::getConfig()->saveConfig('recommender/settings/strands_catalog_upload_now',0);
|
279 |
+
}
|
280 |
+
|
281 |
+
}
|
282 |
+
|
283 |
+
|
284 |
+
/**
|
285 |
+
* Method to retrive cart collection
|
286 |
+
*
|
287 |
+
* @return Mage_Sales_Model_Qoute?
|
288 |
+
*/
|
289 |
+
protected function getCartItems()
|
290 |
+
{
|
291 |
+
return Mage::getSingleton('checkout/session')->getQuote()->getItemsCollection();
|
292 |
+
}
|
293 |
+
|
294 |
+
|
295 |
+
}
|
296 |
+
|
297 |
+
?>
|
app/code/community/Strands/Recommender/etc/config.xml
ADDED
@@ -0,0 +1,146 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0"?>
|
2 |
+
<config>
|
3 |
+
<modules>
|
4 |
+
<Strands_Recommender>
|
5 |
+
<version>1.1.1</version>
|
6 |
+
</Strands_Recommender>
|
7 |
+
</modules>
|
8 |
+
|
9 |
+
<global>
|
10 |
+
<models>
|
11 |
+
<recommender>
|
12 |
+
<class>Strands_Recommender_Model</class>
|
13 |
+
</recommender>
|
14 |
+
</models>
|
15 |
+
<blocks>
|
16 |
+
<recommender>
|
17 |
+
<class>Strands_Recommender_Block</class>
|
18 |
+
</recommender>
|
19 |
+
</blocks>
|
20 |
+
<helpers>
|
21 |
+
<recommender>
|
22 |
+
<class>Strands_Recommender_Helper</class>
|
23 |
+
</recommender>
|
24 |
+
</helpers>
|
25 |
+
</global>
|
26 |
+
|
27 |
+
<frontend>
|
28 |
+
<layout>
|
29 |
+
<updates>
|
30 |
+
<recommender>
|
31 |
+
<file>strands_recommender.xml</file>
|
32 |
+
</recommender>
|
33 |
+
</updates>
|
34 |
+
</layout>
|
35 |
+
<events>
|
36 |
+
<checkout_cart_product_add_after>
|
37 |
+
<observers>
|
38 |
+
<recommender_product_add_observer>
|
39 |
+
<type>singleton</type>
|
40 |
+
<class>recommender/observer</class>
|
41 |
+
<method>eventCartChange</method>
|
42 |
+
</recommender_product_add_observer>
|
43 |
+
</observers>
|
44 |
+
</checkout_cart_product_add_after>
|
45 |
+
<customer_login>
|
46 |
+
<observers>
|
47 |
+
<recommender_customer_login_observer>
|
48 |
+
<type>singleton</type>
|
49 |
+
<class>recommender/observer</class>
|
50 |
+
<method>eventUserLogin</method>
|
51 |
+
</recommender_customer_login_observer>
|
52 |
+
</observers>
|
53 |
+
</customer_login>
|
54 |
+
<controller_action_postdispatch_catalogsearch_result_index>
|
55 |
+
<observers>
|
56 |
+
<recommender_postdispatch_observer>
|
57 |
+
<type>singleton</type>
|
58 |
+
<class>recommender/observer</class>
|
59 |
+
<method>eventPostDispatchSearch</method>
|
60 |
+
</recommender_postdispatch_observer>
|
61 |
+
</observers>
|
62 |
+
</controller_action_postdispatch_catalogsearch_result_index>
|
63 |
+
<controller_action_postdispatch_catalog_product_view>
|
64 |
+
<observers>
|
65 |
+
<recommender_postdispatch_observer>
|
66 |
+
<type>singleton</type>
|
67 |
+
<class>recommender/observer</class>
|
68 |
+
<method>eventPostDispatchProductView</method>
|
69 |
+
</recommender_postdispatch_observer>
|
70 |
+
</observers>
|
71 |
+
</controller_action_postdispatch_catalog_product_view>
|
72 |
+
<sales_order_payment_place_end>
|
73 |
+
<observers>
|
74 |
+
<recommender_pay_observer>
|
75 |
+
<type>singleton</type>
|
76 |
+
<class>recommender/observer</class>
|
77 |
+
<method>eventPayInvoice</method>
|
78 |
+
</recommender_pay_observer>
|
79 |
+
</observers>
|
80 |
+
</sales_order_payment_place_end>
|
81 |
+
<wishlist_add_product>
|
82 |
+
<observers>
|
83 |
+
<recommender_wishlist_observer>
|
84 |
+
<type>singleton</type>
|
85 |
+
<class>recommender/observer</class>
|
86 |
+
<method>eventWishlistAdd</method>
|
87 |
+
</recommender_wishlist_observer>
|
88 |
+
</observers>
|
89 |
+
</wishlist_add_product>
|
90 |
+
</events>
|
91 |
+
<routers>
|
92 |
+
<recommender>
|
93 |
+
<use>admin</use>
|
94 |
+
<args>
|
95 |
+
<module>Strands_Recommender</module>
|
96 |
+
<frontName>recommender</frontName>
|
97 |
+
</args>
|
98 |
+
</recommender>
|
99 |
+
</routers>
|
100 |
+
</frontend>
|
101 |
+
|
102 |
+
<adminhtml>
|
103 |
+
<events>
|
104 |
+
<controller_action_predispatch_adminhtml_system_config_save>
|
105 |
+
<observers>
|
106 |
+
<recommender_predispatch_observer>
|
107 |
+
<type>singleton</type>
|
108 |
+
<class>recommender/observer</class>
|
109 |
+
<method>eventPreDispatch</method>
|
110 |
+
</recommender_predispatch_observer>
|
111 |
+
</observers>
|
112 |
+
</controller_action_predispatch_adminhtml_system_config_save>
|
113 |
+
</events>
|
114 |
+
<acl>
|
115 |
+
<resources>
|
116 |
+
<all>
|
117 |
+
<title>Allow Everything</title>
|
118 |
+
</all>
|
119 |
+
<admin>
|
120 |
+
<children>
|
121 |
+
<system>
|
122 |
+
<children>
|
123 |
+
<config>
|
124 |
+
<children>
|
125 |
+
<recommender module="recommender">
|
126 |
+
<title>Strands Recommender</title>
|
127 |
+
</recommender>
|
128 |
+
</children>
|
129 |
+
</config>
|
130 |
+
</children>
|
131 |
+
</system>
|
132 |
+
</children>
|
133 |
+
</admin>
|
134 |
+
</resources>
|
135 |
+
</acl>
|
136 |
+
</adminhtml>
|
137 |
+
<crontab>
|
138 |
+
<jobs>
|
139 |
+
<recommender_apply_all>
|
140 |
+
<schedule><cron_expr>* * * * *</cron_expr></schedule>
|
141 |
+
<run><model>recommender/observer::uploadCatalog</model></run>
|
142 |
+
</recommender_apply_all>
|
143 |
+
|
144 |
+
</jobs>
|
145 |
+
</crontab>
|
146 |
+
</config>
|
app/code/community/Strands/Recommender/etc/system.xml
ADDED
@@ -0,0 +1,129 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0"?>
|
2 |
+
<config>
|
3 |
+
<sections>
|
4 |
+
<recommender translate="label" module="recommender">
|
5 |
+
<label>Strands Recommender</label>
|
6 |
+
<tab>advanced</tab>
|
7 |
+
<frontend_type>text</frontend_type>
|
8 |
+
<sort_order>999</sort_order>
|
9 |
+
<show_in_default>1</show_in_default>
|
10 |
+
<show_in_website>1</show_in_website>
|
11 |
+
<show_in_store>1</show_in_store>
|
12 |
+
<groups>
|
13 |
+
<settings translate="label">
|
14 |
+
<label>Recommender Settings</label>
|
15 |
+
<frontend_type>text</frontend_type>
|
16 |
+
<show_in_default>1</show_in_default>
|
17 |
+
<show_in_website>1</show_in_website>
|
18 |
+
<show_in_store>1</show_in_store>
|
19 |
+
<fields>
|
20 |
+
<active>
|
21 |
+
<label>Enable</label>
|
22 |
+
<frontend_type>select</frontend_type>
|
23 |
+
<source_model>adminhtml/system_config_source_yesno</source_model>
|
24 |
+
<sort_order>1</sort_order>
|
25 |
+
<show_in_default>1</show_in_default>
|
26 |
+
<show_in_website>1</show_in_website>
|
27 |
+
<show_in_store>1</show_in_store>
|
28 |
+
</active>
|
29 |
+
<log>
|
30 |
+
<label>Log Api Calls</label>
|
31 |
+
<comment><![CDATA[Do no select yes unless instructed to by Strands Recommender Client Services]]></comment>
|
32 |
+
<frontend_type>select</frontend_type>
|
33 |
+
<source_model>adminhtml/system_config_source_yesno</source_model>
|
34 |
+
<sort_order>2</sort_order>
|
35 |
+
<show_in_default>0</show_in_default>
|
36 |
+
<show_in_website>0</show_in_website>
|
37 |
+
<show_in_store>0</show_in_store>
|
38 |
+
</log>
|
39 |
+
<strands_api_id translate="label,comment">
|
40 |
+
<label>Strands API ID</label>
|
41 |
+
<comment><![CDATA[Strands API ID provided by Strands]]></comment>
|
42 |
+
<frontend_type>text</frontend_type>
|
43 |
+
<sort_order>3</sort_order>
|
44 |
+
<show_in_default>1</show_in_default>
|
45 |
+
<show_in_website>1</show_in_website>
|
46 |
+
<show_in_store>1</show_in_store>
|
47 |
+
</strands_api_id>
|
48 |
+
<strands_customer_token translate="label,comment">
|
49 |
+
<label>Strands Customer Validation Token</label>
|
50 |
+
<comment><![CDATA[Strands Customer Validation Token provided by Strands]]></comment>
|
51 |
+
<frontend_type>text</frontend_type>
|
52 |
+
<sort_order>4</sort_order>
|
53 |
+
<show_in_default>1</show_in_default>
|
54 |
+
<show_in_website>1</show_in_website>
|
55 |
+
<show_in_store>1</show_in_store>
|
56 |
+
</strands_customer_token>
|
57 |
+
<strands_login translate="label,comment">
|
58 |
+
<label>Strands Login</label>
|
59 |
+
<comment><![CDATA[Strands login provided by Strands]]></comment>
|
60 |
+
<frontend_type>text</frontend_type>
|
61 |
+
<sort_order>5</sort_order>
|
62 |
+
<show_in_default>1</show_in_default>
|
63 |
+
<show_in_website>1</show_in_website>
|
64 |
+
<show_in_store>1</show_in_store>
|
65 |
+
</strands_login>
|
66 |
+
<strands_password translate="label,comment">
|
67 |
+
<label>Strands Password</label>
|
68 |
+
<comment><![CDATA[Strands password chosen, the same used in Strands signup]]></comment>
|
69 |
+
<frontend_type>password</frontend_type>
|
70 |
+
<sort_order>6</sort_order>
|
71 |
+
<show_in_default>1</show_in_default>
|
72 |
+
<show_in_website>1</show_in_website>
|
73 |
+
<show_in_store>1</show_in_store>
|
74 |
+
</strands_password>
|
75 |
+
<freedirectory>
|
76 |
+
<label>Free Directory</label>
|
77 |
+
<comment><![CDATA[Through a special partnership with eMediaSales, Strands Recommender users on Miva Merchant can receive a free account and listing at http://www.emediastores.com, the premier Miva Merchant store and product directory on the internet.]]></comment>
|
78 |
+
<frontend_type>select</frontend_type>
|
79 |
+
<frontend_model>recommender/system_config_form_field</frontend_model>
|
80 |
+
<source_model>adminhtml/system_config_source_yesno</source_model>
|
81 |
+
<sort_order>7</sort_order>
|
82 |
+
<show_in_default>0</show_in_default>
|
83 |
+
<show_in_website>0</show_in_website>
|
84 |
+
<show_in_store>0</show_in_store>
|
85 |
+
</freedirectory>
|
86 |
+
<freedirectory_hidden>
|
87 |
+
<label>Free Directory Hidden</label>
|
88 |
+
<frontend_type>select</frontend_type>
|
89 |
+
<source_model>adminhtml/system_config_source_yesno</source_model>
|
90 |
+
<sort_order>8</sort_order>
|
91 |
+
<show_in_default>0</show_in_default>
|
92 |
+
<show_in_website>0</show_in_website>
|
93 |
+
<show_in_store>0</show_in_store>
|
94 |
+
</freedirectory_hidden>
|
95 |
+
<strands_desired_catalog_time translate="label,comment">
|
96 |
+
<label>Time to upload catalog</label>
|
97 |
+
<comment><![CDATA[Select the time (only hour in GMT) when you want your catalog to be uploaded to Strands Recommender (daily)]]></comment>
|
98 |
+
<frontend_type>select</frontend_type>
|
99 |
+
<source_model>recommender/numberoption</source_model>
|
100 |
+
<sort_order>9</sort_order>
|
101 |
+
<show_in_default>1</show_in_default>
|
102 |
+
<show_in_website>1</show_in_website>
|
103 |
+
<show_in_store>1</show_in_store>
|
104 |
+
</strands_desired_catalog_time>
|
105 |
+
<strands_catalog_time translate="label,comment">
|
106 |
+
<label>Last catalog upload</label>
|
107 |
+
<comment><![CDATA[Shows the time when the last catalog was successfully uploaded to Strands Recommender (DO NOT FILL!)]]></comment>
|
108 |
+
<frontend_type>text</frontend_type>
|
109 |
+
<sort_order>11</sort_order>
|
110 |
+
<show_in_default>1</show_in_default>
|
111 |
+
<show_in_website>1</show_in_website>
|
112 |
+
<show_in_store>1</show_in_store>
|
113 |
+
</strands_catalog_time>
|
114 |
+
<strands_catalog_upload_now translate="label,comment">
|
115 |
+
<label>Upload catalog now</label>
|
116 |
+
<comment><![CDATA[Select Yes if you want to upload your catalog right now]]></comment>
|
117 |
+
<frontend_type>select</frontend_type>
|
118 |
+
<source_model>adminhtml/system_config_source_yesno</source_model>
|
119 |
+
<sort_order>10</sort_order>
|
120 |
+
<show_in_default>1</show_in_default>
|
121 |
+
<show_in_website>1</show_in_website>
|
122 |
+
<show_in_store>1</show_in_store>
|
123 |
+
</strands_catalog_upload_now>
|
124 |
+
</fields>
|
125 |
+
</settings>
|
126 |
+
</groups>
|
127 |
+
</recommender>
|
128 |
+
</sections>
|
129 |
+
</config>
|
app/design/frontend/default/default/layout/strands_recommender.xml
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0"?>
|
2 |
+
<layout version="1.0.0">
|
3 |
+
|
4 |
+
|
5 |
+
<!--
|
6 |
+
Load Recommender on the homepage
|
7 |
+
-->
|
8 |
+
<cms_index_index>
|
9 |
+
|
10 |
+
</cms_index_index>
|
11 |
+
|
12 |
+
<!--
|
13 |
+
Load Recommender on the product page
|
14 |
+
-->
|
15 |
+
<catalog_product_view>
|
16 |
+
|
17 |
+
</catalog_product_view>
|
18 |
+
|
19 |
+
<!--
|
20 |
+
Load Recommender on the category page
|
21 |
+
-->
|
22 |
+
<catalog_category_view>
|
23 |
+
|
24 |
+
</catalog_category_view>
|
25 |
+
|
26 |
+
<!--
|
27 |
+
Load Recommender on the shopping cart page
|
28 |
+
-->
|
29 |
+
<checkout_cart_index>
|
30 |
+
|
31 |
+
</checkout_cart_index>
|
32 |
+
|
33 |
+
<!--
|
34 |
+
Load Recommender on the success purchase page for onepage checkout page
|
35 |
+
-->
|
36 |
+
<checkout_onepage_success>
|
37 |
+
|
38 |
+
</checkout_onepage_success>
|
39 |
+
|
40 |
+
<!--
|
41 |
+
Load Recommender on the success purchase page for multishipping checkout page
|
42 |
+
-->
|
43 |
+
<checkout_multishipping_success>
|
44 |
+
|
45 |
+
</checkout_multishipping_success>
|
46 |
+
|
47 |
+
</layout>
|
app/design/frontend/default/default/template/strands/recommender/widget.phtml
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<div class="strandsRecs"
|
2 |
+
tpl="<?php echo $this->getWidgetName() ?>"
|
3 |
+
<?php echo ($this->getStrandsId()) ? "user=\"{$this->getStrandsId()}\"" : '' ?>
|
4 |
+
<?php echo ($this->getItem()) ? "item=\"{$this->getItem()}\"" : '' ?>
|
5 |
+
<?php echo ($this->getDFilter()) ? "dfilter=\"{$this->getDFilter()}\"" : '' ?>></div>
|
app/etc/modules/Strands_Recommender.xml
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0"?>
|
2 |
+
<config>
|
3 |
+
<modules>
|
4 |
+
<Strands_Recommender>
|
5 |
+
<active>true</active>
|
6 |
+
<codePool>community</codePool>
|
7 |
+
</Strands_Recommender>
|
8 |
+
</modules>
|
9 |
+
</config>
|
package.xml
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0"?>
|
2 |
+
<package>
|
3 |
+
<name>Mage_StrandsRecommender</name>
|
4 |
+
<version>1.3.2</version>
|
5 |
+
<stability>stable</stability>
|
6 |
+
<license uri="http://www.opensource.org/licenses/osl-3.0.php">OSL v3.0</license>
|
7 |
+
<channel>community</channel>
|
8 |
+
<extends/>
|
9 |
+
<summary>Strand Recommender is the leading personalization and PRODUCT RECOMMENDATIONS service on the web today for small and mid-sized stores, bringing you the easiest and most cost effective way to personalize your customers shopping experience, automate up-sells and cross-sells, boost conversions, and develop actionable user profiles.</summary>
|
10 |
+
<description>Strands Recommender helps retailers increase customer retention and conversion rates through personalized product recommendations. The recommender platform captures product attributes, user behavior, and third party data like ratings and reviews, and delivers the right suggestions to users throughout the shopping experience. Strands Recommender is an independent business unit of Strands Labs, Inc., an industry leader in the personalization space, Strands today has over six years of relevant industry experience, over 100 employees, and a growing list of retail customers such as Skymall, TravelSmith, Kitson, FootwearEtc, and many other IR500 customers. http://recommender.strands.com
|
11 |
+
|
12 |
+
|
13 |
+
Features:
|
14 |
+
30-day free trial, month-to-month or annual plans
|
15 |
+
Automated up-sell & cross-sell recommendations based on crowd behavior and product attributes
|
16 |
+
Personalized recommendations based on individual behavior and similar individual profiles
|
17 |
+
Visible and actionable user profiles
|
18 |
+
Email recommendations and targeted campaigns
|
19 |
+
Product and user-level analytics showing impact & ROI
|
20 |
+
Merchant-defined promotions
|
21 |
+
Rules & filters to "curate" recommendations
|
22 |
+
Algorithm controls and configuration options.
|
23 |
+
Highly scalable, tolerant of millions to tens of millions of products & users
|
24 |
+
Integrates with BazaarVoice, PowerReviews, RateVoice.
|
25 |
+
API for developers and affiliates</description>
|
26 |
+
<notes>First release of the Strands Recommender plugin for the Magento community.
|
27 |
+
|
28 |
+
Visit http://recommender.strands.com/magento</notes>
|
29 |
+
<authors><author><name>Strands Recommender</name><user>auto-converted</user><email>support-sbs@strands.com</email></author></authors>
|
30 |
+
<date>2010-09-23</date>
|
31 |
+
<time>13:19:08</time>
|
32 |
+
<contents><target name="magedesign"><dir name="frontend"><dir name="default"><dir name="default"><dir name="layout"><file name="strands_recommender.xml" hash="e4452ce114e4ac6f8c3b42c0f6a83542"/></dir><dir name="template"><dir name="strands"><dir name="recommender"><file name="widget.phtml" hash="f70046679504792dcea57b1a04de2991"/></dir></dir></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Strands_Recommender.xml" hash="bd28f5b01dfa3aa711c2484e68b3b4c8"/></dir></target><target name="magecommunity"><dir name="Strands"><dir name="Recommender"><dir name="Block"><dir name="System"><dir name="Config"><dir name="Form"><file name="Field.php" hash="08ab8029c8b1fda4b82c2b75babc69b2"/></dir></dir></dir><file name="Js.php" hash="4e861c83345e819fcf87f4082a9135e8"/><file name="Widget.php" hash="62a4d6c7f0187bf5a6f3d4670c091874"/></dir><dir name="etc"><file name="config.xml" hash="6116fd2544b18eaa09481403a37814bd"/><file name="system.xml" hash="8215afaee350a58e633b660f1191c570"/></dir><dir name="Helper"><file name="Abstract.php" hash="fd88693cae72710e81b151ad84278584"/><file name="Data.php" hash="9f97536deb3727cfc4f9822adca9a826"/><file name="Logger.php" hash="ba5999655822a2b1663c98cf3f44de6a"/></dir><dir name="Model"><file name="Abstract.php" hash="0b8be14b6097f4dd8b2caab49f271857"/><file name="Api.php" hash="4a8dc80ab1aa5046adb47cd1064b3884"/><file name="Numberoption.php" hash="34ffcbfe6b4004b213013b12ddaa6abf"/><file name="Observer.php" hash="c3ade1e22317c800a0937ac1cc10e2cf"/></dir></dir></dir></target></contents>
|
33 |
+
<compatible/>
|
34 |
+
<dependencies/>
|
35 |
+
</package>
|