Version Notes
Adding support for configuring Klaviyo settings at the store and website level.
Download this release
Release Info
Developer | Klaviyo Team |
Extension | Klaviyo_Reclaim |
Version | 1.6.2 |
Comparing to | |
See all releases |
Code changes from version 1.3.5 to 1.6.2
- app/code/community/Klaviyo/Reclaim/Block/Adminhtml/System/Config/Fieldset/Info.php +11 -2
- app/code/community/Klaviyo/Reclaim/Block/Adminhtml/System/Config/Form/Button.php +23 -0
- app/code/community/Klaviyo/Reclaim/Helper/Data.php +167 -6
- app/code/community/Klaviyo/Reclaim/Model/Api.php +1 -1
- app/code/community/Klaviyo/Reclaim/Model/Api2/Coupon.php +13 -0
- app/code/community/Klaviyo/Reclaim/Model/Api2/Coupon/Rest/Admin/V1.php +167 -0
- app/code/community/Klaviyo/Reclaim/Model/Api2/Rule.php +17 -0
- app/code/community/Klaviyo/Reclaim/Model/Api2/Rule/Rest/Admin/V1.php +26 -0
- app/code/community/Klaviyo/Reclaim/Model/KlaviyoApi.php +2 -1
- app/code/community/Klaviyo/Reclaim/Model/Observer.php +291 -165
- app/code/community/Klaviyo/Reclaim/controllers/IndexController.php +128 -16
- app/code/community/Klaviyo/Reclaim/controllers/KlaviyoController.php +49 -0
- app/code/community/Klaviyo/Reclaim/etc/adminhtml.xml +21 -21
- app/code/community/Klaviyo/Reclaim/etc/api2.xml +72 -0
- app/code/community/Klaviyo/Reclaim/etc/config.xml +15 -12
- app/code/community/Klaviyo/Reclaim/etc/system.xml +56 -4
- app/design/frontend/base/default/layout/klaviyoreclaim.xml +6 -1
- app/design/frontend/base/default/template/klaviyoreclaim/tracking/product.phtml +21 -7
- package.xml +15 -11
app/code/community/Klaviyo/Reclaim/Block/Adminhtml/System/Config/Fieldset/Info.php
CHANGED
@@ -37,8 +37,17 @@ class Klaviyo_Reclaim_Block_Adminhtml_System_Config_Fieldset_Info extends Mage_A
|
|
37 |
|
38 |
$has_reclaim_entries = Mage::getModel('klaviyo_reclaim/checkout')->getCollection()->count() > 0;
|
39 |
|
40 |
-
$is_extension_failing = $is_enabled
|
41 |
|
42 |
return array($is_extension_failing, $is_api_key_set, $is_cron_running, $has_reclaim_entries);
|
43 |
}
|
44 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
37 |
|
38 |
$has_reclaim_entries = Mage::getModel('klaviyo_reclaim/checkout')->getCollection()->count() > 0;
|
39 |
|
40 |
+
$is_extension_failing = $is_enabled && !($is_api_key_set || $is_cron_running || $has_reclaim_entries);
|
41 |
|
42 |
return array($is_extension_failing, $is_api_key_set, $is_cron_running, $has_reclaim_entries);
|
43 |
}
|
44 |
+
}
|
45 |
+
|
46 |
+
class Klaviyo_Reclaim_Block_Oauth_Credential_Renderer extends Mage_Adminhtml_Block_System_Config_Form_Field
|
47 |
+
{
|
48 |
+
protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element) {
|
49 |
+
$element->setDisabled('disabled');
|
50 |
+
|
51 |
+
return parent::_getElementHtml($element);
|
52 |
+
}
|
53 |
+
}
|
app/code/community/Klaviyo/Reclaim/Block/Adminhtml/System/Config/Form/Button.php
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
class Klaviyo_Reclaim_Block_Adminhtml_System_Config_Form_Button extends Mage_Adminhtml_Block_System_Config_Form_Field
|
3 |
+
{
|
4 |
+
/**
|
5 |
+
* Return element html
|
6 |
+
*
|
7 |
+
* @param Varien_Data_Form_Element_Abstract $element
|
8 |
+
* @return string
|
9 |
+
*/
|
10 |
+
protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element)
|
11 |
+
{
|
12 |
+
$oauthKeysUrl = Mage::helper('adminhtml')->getUrl('adminhtml/klaviyo/oauthkeys');
|
13 |
+
|
14 |
+
$button = $this->getLayout()->createBlock('adminhtml/widget_button')
|
15 |
+
->setData(array(
|
16 |
+
'id' => 'klaviyo_reclaim_generate_oauth_tokens_button',
|
17 |
+
'label' => 'Generate OAuth Tokens',
|
18 |
+
'onclick' => 'setLocation(\'' . $oauthKeysUrl . '\');'
|
19 |
+
));
|
20 |
+
|
21 |
+
return $button->toHtml();
|
22 |
+
}
|
23 |
+
}
|
app/code/community/Klaviyo/Reclaim/Helper/Data.php
CHANGED
@@ -24,7 +24,31 @@ class Klaviyo_Reclaim_Helper_Data extends Mage_Core_Helper_Data
|
|
24 |
* @var string
|
25 |
*/
|
26 |
const XML_PATH_PRIVATE_API_KEY = 'reclaim/general/private_api_key';
|
27 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
/**
|
29 |
* Path to store config for Klaviyo list to sync Magento general subscription list with.
|
30 |
* @var string
|
@@ -37,6 +61,12 @@ class Klaviyo_Reclaim_Helper_Data extends Mage_Core_Helper_Data
|
|
37 |
*/
|
38 |
const XML_PATH_USE_KLAVIYO_LIST_NAME = 'reclaim/general/use_klaviyo_list_name';
|
39 |
|
|
|
|
|
|
|
|
|
|
|
|
|
40 |
/* For the "etc/adminthtml.xml" file when we implement:
|
41 |
<use_klaviyo_list_name translate="label comment">
|
42 |
<label>Use Klaviyo List Name</label>
|
@@ -51,6 +81,52 @@ class Klaviyo_Reclaim_Helper_Data extends Mage_Core_Helper_Data
|
|
51 |
</use_klaviyo_list_name>
|
52 |
*/
|
53 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
54 |
/**
|
55 |
* Utility for fetching settings for our extension.
|
56 |
* @param integer|string|Mage_Core_Model_Store $store
|
@@ -78,9 +154,9 @@ class Klaviyo_Reclaim_Helper_Data extends Mage_Core_Helper_Data
|
|
78 |
*/
|
79 |
public function isEnabled($store=null)
|
80 |
{
|
81 |
-
return
|
82 |
}
|
83 |
-
|
84 |
/**
|
85 |
* Return the Klaviyo Public API key
|
86 |
* @param integer|string|Mage_Core_Model_Store $store
|
@@ -88,7 +164,7 @@ class Klaviyo_Reclaim_Helper_Data extends Mage_Core_Helper_Data
|
|
88 |
*/
|
89 |
public function getPublicApiKey($store=null)
|
90 |
{
|
91 |
-
return
|
92 |
}
|
93 |
|
94 |
/**
|
@@ -98,7 +174,47 @@ class Klaviyo_Reclaim_Helper_Data extends Mage_Core_Helper_Data
|
|
98 |
*/
|
99 |
public function getPrivateApiKey($store=null)
|
100 |
{
|
101 |
-
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
102 |
}
|
103 |
|
104 |
public function getSubscriptionList($store)
|
@@ -141,10 +257,55 @@ class Klaviyo_Reclaim_Helper_Data extends Mage_Core_Helper_Data
|
|
141 |
return $checkout;
|
142 |
}
|
143 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
144 |
public function log($data, $filename)
|
145 |
{
|
146 |
if ($this->config('enable_log') != 0) {
|
147 |
return Mage::getModel('core/log_adapter', $filename)->log($data);
|
148 |
}
|
149 |
}
|
150 |
-
|
|
|
|
|
|
|
|
|
|
24 |
* @var string
|
25 |
*/
|
26 |
const XML_PATH_PRIVATE_API_KEY = 'reclaim/general/private_api_key';
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Path to store config where OAuth consumer key is stored
|
30 |
+
* @var string
|
31 |
+
*/
|
32 |
+
const XML_PATH_CONSUMER_KEY = 'reclaim/general/consumer_key';
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Path to store config where OAuth consumer key is stored
|
36 |
+
* @var string
|
37 |
+
*/
|
38 |
+
const XML_PATH_CONSUMER_SECRET = 'reclaim/general/consumer_secret';
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Path to store config where OAuth consumer key is stored
|
42 |
+
* @var string
|
43 |
+
*/
|
44 |
+
const XML_PATH_AUTHORIZATION_TOKEN = 'reclaim/general/authorization_token';
|
45 |
+
|
46 |
+
/**
|
47 |
+
* Path to store config where OAuth consumer key is stored
|
48 |
+
* @var string
|
49 |
+
*/
|
50 |
+
const XML_PATH_AUTHORIZATION_SECRET = 'reclaim/general/authorization_secret';
|
51 |
+
|
52 |
/**
|
53 |
* Path to store config for Klaviyo list to sync Magento general subscription list with.
|
54 |
* @var string
|
61 |
*/
|
62 |
const XML_PATH_USE_KLAVIYO_LIST_NAME = 'reclaim/general/use_klaviyo_list_name';
|
63 |
|
64 |
+
/**
|
65 |
+
* Path for klaviyo log file.
|
66 |
+
* @var string
|
67 |
+
*/
|
68 |
+
const LOG_FILE_PATH = 'klaviyo.log';
|
69 |
+
|
70 |
/* For the "etc/adminthtml.xml" file when we implement:
|
71 |
<use_klaviyo_list_name translate="label comment">
|
72 |
<label>Use Klaviyo List Name</label>
|
81 |
</use_klaviyo_list_name>
|
82 |
*/
|
83 |
|
84 |
+
/**
|
85 |
+
* Get configuration value by searching for the most specific setting moving from
|
86 |
+
* store scope to website scope to global scope.
|
87 |
+
*
|
88 |
+
* @param $path
|
89 |
+
* @param integer|string|Mage_Core_Model_Store $store
|
90 |
+
* @param bool $returnParentValueIfNull
|
91 |
+
* @return mixed|null
|
92 |
+
* @throws Mage_Core_Exception
|
93 |
+
*/
|
94 |
+
public function getConfigSettingIncludingParents($path, $store=null)
|
95 |
+
{
|
96 |
+
$value = null;
|
97 |
+
|
98 |
+
if (!is_null($store)) {
|
99 |
+
$possible_value = Mage::getStoreConfig($path, $store);
|
100 |
+
if (!is_null($possible_value)) {
|
101 |
+
$value = $possible_value;
|
102 |
+
}
|
103 |
+
|
104 |
+
// If we didn't find a value at the store level, check the website config.
|
105 |
+
if (is_null($value)) {
|
106 |
+
$website = $store->getWebsite();
|
107 |
+
|
108 |
+
// `getWebsite` could return `false` if there's no website associated with the store.
|
109 |
+
// In practice, I'm not sure why this would happen, but Magento allows it.
|
110 |
+
if ($website) {
|
111 |
+
$possible_value = $website->getConfig($path);
|
112 |
+
if (!is_null($possible_value)) {
|
113 |
+
$value = $possible_value;
|
114 |
+
}
|
115 |
+
}
|
116 |
+
}
|
117 |
+
}
|
118 |
+
|
119 |
+
// If we didn't find a value at the store or website level, check the global config.
|
120 |
+
if (is_null($value)) {
|
121 |
+
$possible_value = Mage::getStoreConfig($path);
|
122 |
+
if (!is_null($possible_value)) {
|
123 |
+
$value = $possible_value;
|
124 |
+
}
|
125 |
+
}
|
126 |
+
|
127 |
+
return $value;
|
128 |
+
}
|
129 |
+
|
130 |
/**
|
131 |
* Utility for fetching settings for our extension.
|
132 |
* @param integer|string|Mage_Core_Model_Store $store
|
154 |
*/
|
155 |
public function isEnabled($store=null)
|
156 |
{
|
157 |
+
return $this->getConfigSettingIncludingParents(self::XML_PATH_ENABLED, $store);
|
158 |
}
|
159 |
+
|
160 |
/**
|
161 |
* Return the Klaviyo Public API key
|
162 |
* @param integer|string|Mage_Core_Model_Store $store
|
164 |
*/
|
165 |
public function getPublicApiKey($store=null)
|
166 |
{
|
167 |
+
return $this->getConfigSettingIncludingParents(self::XML_PATH_PUBLIC_API_KEY, $store);
|
168 |
}
|
169 |
|
170 |
/**
|
174 |
*/
|
175 |
public function getPrivateApiKey($store=null)
|
176 |
{
|
177 |
+
return $this->getConfigSettingIncludingParents(self::XML_PATH_PRIVATE_API_KEY, $store);
|
178 |
+
}
|
179 |
+
|
180 |
+
/**
|
181 |
+
* Return the store's OAuth Consumer Key
|
182 |
+
* @param integer|string|Mage_Core_Model_Store $store
|
183 |
+
* @return string
|
184 |
+
*/
|
185 |
+
public function getConsumerKey($store=null)
|
186 |
+
{
|
187 |
+
return $this->getConfigSettingIncludingParents(self::XML_PATH_CONSUMER_KEY, $store);
|
188 |
+
}
|
189 |
+
|
190 |
+
/**
|
191 |
+
* Return the store's OAuth Consumer Secret
|
192 |
+
* @param integer|string|Mage_Core_Model_Store $store
|
193 |
+
* @return string
|
194 |
+
*/
|
195 |
+
public function getConsumerSecret($store=null)
|
196 |
+
{
|
197 |
+
return $this->getConfigSettingIncludingParents(self::XML_PATH_CONSUMER_SECRET, $store);
|
198 |
+
}
|
199 |
+
|
200 |
+
/**
|
201 |
+
* Return the store's OAuth Authorization Token
|
202 |
+
* @param integer|string|Mage_Core_Model_Store $store
|
203 |
+
* @return string
|
204 |
+
*/
|
205 |
+
public function getAuthorizationToken($store=null)
|
206 |
+
{
|
207 |
+
return $this->getConfigSettingIncludingParents(self::XML_PATH_AUTHORIZATION_TOKEN, $store);
|
208 |
+
}
|
209 |
+
|
210 |
+
/**
|
211 |
+
* Return the store's OAuth Authorization Secret
|
212 |
+
* @param integer|string|Mage_Core_Model_Store $store
|
213 |
+
* @return string
|
214 |
+
*/
|
215 |
+
public function getAuthorizationSecret($store=null)
|
216 |
+
{
|
217 |
+
return $this->getConfigSettingIncludingParents(self::XML_PATH_AUTHORIZATION_SECRET, $store);
|
218 |
}
|
219 |
|
220 |
public function getSubscriptionList($store)
|
257 |
return $checkout;
|
258 |
}
|
259 |
|
260 |
+
/**
|
261 |
+
* Set the store's OAuth Consumer Key
|
262 |
+
* @param string
|
263 |
+
* @return void
|
264 |
+
*/
|
265 |
+
public function setConsumerKey($consumerKey)
|
266 |
+
{
|
267 |
+
Mage::getModel('core/config')->saveConfig(self::XML_PATH_CONSUMER_KEY, $consumerKey);
|
268 |
+
}
|
269 |
+
|
270 |
+
/**
|
271 |
+
* Set the store's OAuth Consumer Secret
|
272 |
+
* @param string
|
273 |
+
* @return void
|
274 |
+
*/
|
275 |
+
public function setConsumerSecret($consumerSecret)
|
276 |
+
{
|
277 |
+
Mage::getModel('core/config')->saveConfig(self::XML_PATH_CONSUMER_SECRET, $consumerSecret);
|
278 |
+
}
|
279 |
+
|
280 |
+
/**
|
281 |
+
* Set the store's OAuth Authorization Token
|
282 |
+
* @param string
|
283 |
+
* @return void
|
284 |
+
*/
|
285 |
+
public function setAuthorizationToken($authorizationToken)
|
286 |
+
{
|
287 |
+
Mage::getModel('core/config')->saveConfig(self::XML_PATH_AUTHORIZATION_TOKEN, $authorizationToken);
|
288 |
+
}
|
289 |
+
|
290 |
+
/**
|
291 |
+
* Set the store's OAuth Authroization Secret
|
292 |
+
* @param string
|
293 |
+
* @return void
|
294 |
+
*/
|
295 |
+
public function setAuthorizationSecret($authorizationSecret)
|
296 |
+
{
|
297 |
+
Mage::getModel('core/config')->saveConfig(self::XML_PATH_AUTHORIZATION_SECRET, $authorizationSecret);
|
298 |
+
}
|
299 |
+
|
300 |
public function log($data, $filename)
|
301 |
{
|
302 |
if ($this->config('enable_log') != 0) {
|
303 |
return Mage::getModel('core/log_adapter', $filename)->log($data);
|
304 |
}
|
305 |
}
|
306 |
+
|
307 |
+
public function getLogFile()
|
308 |
+
{
|
309 |
+
return self::LOG_FILE_PATH;
|
310 |
+
}
|
311 |
+
}
|
app/code/community/Klaviyo/Reclaim/Model/Api.php
CHANGED
@@ -123,7 +123,7 @@ class Klaviyo_Reclaim_Model_Api
|
|
123 |
* @return void
|
124 |
*/
|
125 |
protected function _logApiRequest($data) {
|
126 |
-
Mage::log($data);
|
127 |
}
|
128 |
|
129 |
}
|
123 |
* @return void
|
124 |
*/
|
125 |
protected function _logApiRequest($data) {
|
126 |
+
Mage::log($data, Zend_Log::INFO, Mage::helper('klaviyo_reclaim')->getLogFile());
|
127 |
}
|
128 |
|
129 |
}
|
app/code/community/Klaviyo/Reclaim/Model/Api2/Coupon.php
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Coupon Api
|
4 |
+
*
|
5 |
+
* @category Klaviyo
|
6 |
+
* @package Klaviyo_Reclaim
|
7 |
+
* @author Klaviyo Team <support@klaviyo.com>
|
8 |
+
*/
|
9 |
+
class Klaviyo_Reclaim_Model_Api2_Coupon extends Mage_Api2_Model_Resource
|
10 |
+
{
|
11 |
+
|
12 |
+
|
13 |
+
}
|
app/code/community/Klaviyo/Reclaim/Model/Api2/Coupon/Rest/Admin/V1.php
ADDED
@@ -0,0 +1,167 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* Coupon REST API
|
3 |
+
*
|
4 |
+
* @category Klaviyo
|
5 |
+
* @package Klaviyo_Reclaim
|
6 |
+
* @author Klaviyo Team <support@klaviyo.com>
|
7 |
+
*/
|
8 |
+
class Klaviyo_Reclaim_Model_Api2_Coupon_Rest_Admin_V1 extends Klaviyo_Reclaim_Model_Api2_Coupon
|
9 |
+
{
|
10 |
+
const COUPON_CREATE_SUCCESS = 'Coupon created';
|
11 |
+
const COUPON_CODE_GENERATE_FAILURE = 'Unable to generate Coupon code. Please check your parameters and try again.';
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Generate one or more coupon codes using the Generate Coupons rule provided.
|
15 |
+
* This must be a _multiCreate so we can return messages, _creates cannot return messages.
|
16 |
+
* In order for this endpoint to be hit, params must not be an associative array.
|
17 |
+
*
|
18 |
+
* Expected parameters are...
|
19 |
+
* qty: (int) number of coupon codes to instruct Magento to generate
|
20 |
+
* length: (int) length of each generated coupon code
|
21 |
+
* format: (string) coupon code format, one of 'alphanum', 'alpha', and 'num'
|
22 |
+
*
|
23 |
+
* @return string
|
24 |
+
*/
|
25 |
+
protected function _multiCreate($data)
|
26 |
+
{
|
27 |
+
$ruleId = $this->getRequest()->getParam('rule_id');
|
28 |
+
$rule = $this->_loadSalesRule($ruleId);
|
29 |
+
|
30 |
+
$couponData = $data[0];
|
31 |
+
$couponData['rule_id'] = $ruleId;
|
32 |
+
|
33 |
+
$couponGenerator = $rule->getCouponMassGenerator();
|
34 |
+
|
35 |
+
// validate coupon attributes
|
36 |
+
if (!$couponGenerator->validateData($couponData)) {
|
37 |
+
$this->_critical(Mage::helper('salesrule')->__('Coupon API: Invalid parameters provided.'),
|
38 |
+
Mage_Api2_Model_Server::HTTP_BAD_REQUEST);
|
39 |
+
}
|
40 |
+
|
41 |
+
$couponGenerator->setData($couponData);
|
42 |
+
|
43 |
+
if ($couponGenerator->getMaxAttempts()) {
|
44 |
+
$maxGenerateAttempts = $couponGenerator->getMaxAttempts();
|
45 |
+
} else {
|
46 |
+
$maxGenerateAttempts = $couponGenerator::MAX_GENERATE_ATTEMPTS;
|
47 |
+
}
|
48 |
+
|
49 |
+
$now = $couponGenerator->getResource()->formatDate(
|
50 |
+
Mage::getSingleton('core/date')->gmtTimestamp()
|
51 |
+
);
|
52 |
+
|
53 |
+
$coupon = Mage::getModel('salesrule/coupon');
|
54 |
+
|
55 |
+
for ($i = 0; $i < $couponData['qty']; $i++) {
|
56 |
+
$attempt = 0;
|
57 |
+
$breakOut = false;
|
58 |
+
do {
|
59 |
+
if ($attempt >= $maxGenerateAttempts) {
|
60 |
+
// Report this failure for all coupons that still need to be created,
|
61 |
+
// and flag to end trying to generation coupon codes.
|
62 |
+
for ($failedCouponIndex = 0; $failedCouponIndex < $couponData['qty'] - $i; $failedCouponIndex++) {
|
63 |
+
$this->getResponse()->addMessage(self::COUPON_CODE_GENERATE_FAILURE, 0);
|
64 |
+
}
|
65 |
+
$breakOut = true;
|
66 |
+
} else {
|
67 |
+
$code = $couponGenerator->generateCode();
|
68 |
+
$attempt++;
|
69 |
+
}
|
70 |
+
} while ($couponGenerator->getResource()->exists($code));
|
71 |
+
|
72 |
+
if ($breakOut) {
|
73 |
+
break;
|
74 |
+
}
|
75 |
+
|
76 |
+
$expirationDate = $couponGenerator->getToDate();
|
77 |
+
if ($expirationDate instanceof Zend_Date) {
|
78 |
+
$expirationDate = $expirationDate->toString(Varien_Date::DATETIME_INTERNAL_FORMAT);
|
79 |
+
}
|
80 |
+
|
81 |
+
$coupon->setId(null)
|
82 |
+
->setRuleId($ruleId)
|
83 |
+
->setUsageLimit($couponGenerator->getUsesPerCoupon())
|
84 |
+
->setUsagePerCustomer($couponGenerator->getUsesPerCustomer())
|
85 |
+
->setExpirationDate($expirationDate)
|
86 |
+
->setCreatedAt($now)
|
87 |
+
->setType(Mage_SalesRule_Helper_Coupon::COUPON_TYPE_SPECIFIC_AUTOGENERATED)
|
88 |
+
->setCode($code)
|
89 |
+
->save();
|
90 |
+
|
91 |
+
$this->getResponse()->addMessage(
|
92 |
+
self::COUPON_CREATE_SUCCESS, $coupon['code'],
|
93 |
+
array('id' => $coupon->getId()),
|
94 |
+
Mage_Api2_Model_Response::MESSAGE_TYPE_SUCCESS
|
95 |
+
);
|
96 |
+
}
|
97 |
+
}
|
98 |
+
|
99 |
+
/**
|
100 |
+
* Delete a Coupon associated with a rule.
|
101 |
+
*
|
102 |
+
* @return void
|
103 |
+
*/
|
104 |
+
protected function _delete()
|
105 |
+
{
|
106 |
+
$ruleId = $this->getRequest()->getParam('rule_id');
|
107 |
+
$rule = $this->_loadSalesRule($ruleId);
|
108 |
+
|
109 |
+
$couponId = $this->getRequest()->getParam('coupon_id');
|
110 |
+
$coupon = Mage::getModel('salesrule/coupon')->load($couponId);
|
111 |
+
|
112 |
+
// perform validation to ensure the coupon we want to delete is associated with the rule in mind
|
113 |
+
if ($coupon->getRuleId() != $rule->getId()) {
|
114 |
+
$this->_critical(Mage::helper('salesrule/coupon')->__('Coupon API: Invalid parameters provided, rule not associated with coupon.'),
|
115 |
+
Mage_Api2_Model_Server::HTTP_BAD_REQUEST);
|
116 |
+
}
|
117 |
+
|
118 |
+
try {
|
119 |
+
$coupon->delete();
|
120 |
+
} catch (Mage_Core_Exception $e) {
|
121 |
+
$this->_critical($e->getMessage(), Mage_Api2_Model_Server::HTTP_INTERNAL_ERROR);
|
122 |
+
} catch (Exception $e) {
|
123 |
+
$this->_critical(self::RESOURCE_INTERNAL_ERROR);
|
124 |
+
}
|
125 |
+
|
126 |
+
echo 1;
|
127 |
+
}
|
128 |
+
|
129 |
+
/**
|
130 |
+
* Retrieve list of coupon codes.
|
131 |
+
*
|
132 |
+
* @return array
|
133 |
+
*/
|
134 |
+
protected function _retrieveCollection()
|
135 |
+
{
|
136 |
+
$ruleId = $this->getRequest()->getParam('rule_id');
|
137 |
+
$rule = $this->_loadSalesRule($ruleId);
|
138 |
+
|
139 |
+
$couponCollection = Mage::getResourceModel('salesrule/coupon_collection');
|
140 |
+
$couponCollection->addRuleToFilter($rule);
|
141 |
+
|
142 |
+
$this->_applyCollectionModifiers($couponCollection);
|
143 |
+
$data = $couponCollection->load()->toArray();
|
144 |
+
|
145 |
+
return $data['items'];
|
146 |
+
}
|
147 |
+
|
148 |
+
/**
|
149 |
+
* Load sales rule by ID.
|
150 |
+
*
|
151 |
+
* @param int $ruleId
|
152 |
+
* @return Mage_SalesRule_Model_Rule
|
153 |
+
*/
|
154 |
+
protected function _loadSalesRule($ruleId)
|
155 |
+
{
|
156 |
+
if (!$ruleId) {
|
157 |
+
$this->_critical(Mage::helper('salesrule')
|
158 |
+
->__('Rule ID is required.'), Mage_Api2_Model_Server::HTTP_BAD_REQUEST);
|
159 |
+
}
|
160 |
+
$rule = Mage::getModel('salesrule/rule')->load($ruleId);
|
161 |
+
if (!$rule->getId()) {
|
162 |
+
$this->_critical(Mage::helper('salesrule')
|
163 |
+
->__('Rule was not found.'), Mage_Api2_Model_Server::HTTP_NOT_FOUND);
|
164 |
+
}
|
165 |
+
return $rule;
|
166 |
+
}
|
167 |
+
}
|
app/code/community/Klaviyo/Reclaim/Model/Api2/Rule.php
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @category Klaviyo
|
4 |
+
* @package Klaviyo_Reclaim
|
5 |
+
* @copyright Copyright (c) 2013 Klaviyo Inc. (http://www.klaviyo.com)
|
6 |
+
*/
|
7 |
+
/**
|
8 |
+
* Rule Api
|
9 |
+
*
|
10 |
+
* @category Klaviyo
|
11 |
+
* @package Klaviyo_Reclaim
|
12 |
+
* @author Klaviyo Team <support@klaviyo.com>
|
13 |
+
*/
|
14 |
+
class Klaviyo_Reclaim_Model_Api2_Rule extends Mage_Api2_Model_Resource
|
15 |
+
{
|
16 |
+
}
|
17 |
+
|
app/code/community/Klaviyo/Reclaim/Model/Api2/Rule/Rest/Admin/V1.php
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* Rule REST API
|
3 |
+
*
|
4 |
+
* @category Klaviyo
|
5 |
+
* @package Klaviyo_Reclaim
|
6 |
+
* @author Klaviyo Team <support@klaviyo.com>
|
7 |
+
*/
|
8 |
+
class Klaviyo_Reclaim_Model_Api2_Rule_Rest_Admin_V1 extends Klaviyo_Reclaim_Model_Api2_Rule
|
9 |
+
{
|
10 |
+
/**
|
11 |
+
* Retrieve list of coupon codes.
|
12 |
+
*
|
13 |
+
* @return array
|
14 |
+
*/
|
15 |
+
protected function _retrieveCollection()
|
16 |
+
{
|
17 |
+
$data = array();
|
18 |
+
|
19 |
+
$rules = Mage::getResourceModel('salesrule/rule_collection')->load();
|
20 |
+
foreach ($rules as $rule) {
|
21 |
+
array_push($data, $rule->getData());
|
22 |
+
}
|
23 |
+
|
24 |
+
return $data;
|
25 |
+
}
|
26 |
+
}
|
app/code/community/Klaviyo/Reclaim/Model/KlaviyoApi.php
CHANGED
@@ -66,6 +66,7 @@ class Klaviyo_Reclaim_Model_KlaviyoApi
|
|
66 |
|
67 |
function lists($page=0, $count=50) {
|
68 |
$params = array();
|
|
|
69 |
$params['page'] = $page;
|
70 |
$params['count'] = $count;
|
71 |
return $this->callServer('GET', 'lists', $params);
|
@@ -107,7 +108,7 @@ class Klaviyo_Reclaim_Model_KlaviyoApi
|
|
107 |
$client->setParameterPost($params);
|
108 |
} else {
|
109 |
$client->setRawData(http_build_query($params));
|
110 |
-
}
|
111 |
|
112 |
try {
|
113 |
$response = $client->request();
|
66 |
|
67 |
function lists($page=0, $count=50) {
|
68 |
$params = array();
|
69 |
+
$params['type'] = 'list';
|
70 |
$params['page'] = $page;
|
71 |
$params['count'] = $count;
|
72 |
return $this->callServer('GET', 'lists', $params);
|
108 |
$client->setParameterPost($params);
|
109 |
} else {
|
110 |
$client->setRawData(http_build_query($params));
|
111 |
+
}
|
112 |
|
113 |
try {
|
114 |
$response = $client->request();
|
app/code/community/Klaviyo/Reclaim/Model/Observer.php
CHANGED
@@ -15,7 +15,18 @@
|
|
15 |
*/
|
16 |
class Klaviyo_Reclaim_Model_Observer
|
17 |
{
|
18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
var $tracker_cache = array();
|
20 |
|
21 |
/**
|
@@ -31,196 +42,304 @@ class Klaviyo_Reclaim_Model_Observer
|
|
31 |
* @return Klaviyo_Reclaim_Model_Observer
|
32 |
*/
|
33 |
public function trackQuotes() {
|
|
|
34 |
|
35 |
-
|
36 |
-
|
37 |
-
}
|
38 |
|
39 |
-
$this->_errors = array();
|
40 |
try {
|
41 |
-
$adapter = Mage::getSingleton('core/resource')->getConnection('sales_read');
|
42 |
-
|
43 |
// Find quotes that are at least 15 minutes old and have been updated in the last 60 minutes.
|
44 |
$created_window_minutes = 15;
|
45 |
-
|
46 |
-
$created_before = Zend_Date::now();
|
47 |
-
$created_before->sub($created_window_minutes, Zend_Date::MINUTE);
|
48 |
-
$created_before = $adapter->convertDateTime($created_before);
|
49 |
-
|
50 |
$updated_window_minutes = 60;
|
51 |
|
52 |
-
$
|
53 |
-
$updated_after->sub($updated_window_minutes, Zend_Date::MINUTE);
|
54 |
-
$updated_after = $adapter->convertDateTime($updated_after);
|
55 |
-
|
56 |
-
$quotes = Mage::getResourceModel('sales/quote_collection')
|
57 |
-
->addFieldToFilter('converted_at', array('null' => true))
|
58 |
-
->addFieldToFilter('created_at', array('lteq' => $created_before))
|
59 |
-
->addFieldToFilter('updated_at', array('gteq' => $updated_after));
|
60 |
|
|
|
61 |
$quotes_tracked = 0;
|
62 |
|
63 |
foreach ($quotes as $quote) {
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
if (!$billing_address || !$tracker) {
|
68 |
-
continue;
|
69 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
70 |
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
|
|
77 |
|
78 |
-
|
79 |
-
|
80 |
-
$checkout = Mage::helper('klaviyo_reclaim')->getCheckout($quote_id);
|
81 |
-
|
82 |
-
$customer_properties = array(
|
83 |
-
'$email' => $quote->getCustomerEmail(),
|
84 |
-
'$first_name' => $quote->getCustomerFirstname(),
|
85 |
-
'$last_name' => $quote->getCustomerLastname(),
|
86 |
-
'location' => array(
|
87 |
-
'source' => 'magento',
|
88 |
-
'address1' => $billing_address->getStreet(1),
|
89 |
-
'city' => $billing_address->getCity(),
|
90 |
-
'region' => $billing_address->getRegion(),
|
91 |
-
'zip' => $billing_address->getPostcode(),
|
92 |
-
'country' => $billing_address->getCountry()
|
93 |
-
)
|
94 |
-
);
|
95 |
-
|
96 |
-
if ($billing_address->getStreet(2)) {
|
97 |
-
$customer_properties['location']['address2'] = $billing_address->getStreet(2);
|
98 |
-
}
|
99 |
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
$quote_item_product = Mage::getModel('catalog/product')->load($quote_item->getProduct()->getId());
|
110 |
-
$quote_item_product_name = $quote_item_product->getName();
|
111 |
-
|
112 |
-
for ($i = 0; $i < $quote_item_quantity; $i++) {
|
113 |
-
$item_count++;
|
114 |
-
}
|
115 |
-
$item_descriptions[] = $quote_item_product_name;
|
116 |
-
$line_item_count++;
|
117 |
-
|
118 |
-
$product_images = array();
|
119 |
-
foreach ($quote_item_product->getMediaGalleryImages() as $product_image) {
|
120 |
-
if (!$product_image->getDisabled()) {
|
121 |
-
$product_images[] = array(
|
122 |
-
'url' => $product_image->getUrl()
|
123 |
-
);
|
124 |
-
}
|
125 |
-
}
|
126 |
-
|
127 |
-
if ($quote_item_product->isConfigurable()) {
|
128 |
-
$configurable_product_ids[] = $quote_item_product->getId();
|
129 |
-
}
|
130 |
-
|
131 |
-
if ($line_item_count <= self::MAX_LINE_ITEMS_WITH_DETAILS) {
|
132 |
-
$item_details[] = array(
|
133 |
-
'quantity' => (float) $quote_item_quantity,
|
134 |
-
'row_total' => (float) $quote_item->getBaseRowTotal(),
|
135 |
-
'row_discount' => (float) $quote_item->getBaseDiscountAmount(),
|
136 |
-
'product' => array(
|
137 |
-
'id' => $quote_item_product->getId(),
|
138 |
-
'sku' => $quote_item_product->getSKU(),
|
139 |
-
'name' => $quote_item_product->getName(),
|
140 |
-
'price' => (float) $quote_item_product->getPrice(),
|
141 |
-
'images' => $product_images
|
142 |
-
)
|
143 |
-
);
|
144 |
-
}
|
145 |
-
}
|
146 |
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
// Look for simple products that are really represented by a configurable product.
|
152 |
-
foreach ($item_details as $line_index => $line) {
|
153 |
-
$product_id = $line['product']['id'];
|
154 |
-
if (in_array($product_id, $configurable_product_ids) || $line['row_total']) {
|
155 |
-
$tmp[] = $line;
|
156 |
-
continue;
|
157 |
-
}
|
158 |
-
|
159 |
-
$configurable_parent_ids = $model_configurable->getParentIdsByChild($product_id);
|
160 |
-
$common_ids = array_intersect($configurable_product_ids, $configurable_parent_ids);
|
161 |
-
|
162 |
-
// If it's a simple product placeholder for a configurable product, discard it. Otherwise, keep it.
|
163 |
-
if (empty($common_ids)) {
|
164 |
-
$tmp[] = $line;
|
165 |
-
}
|
166 |
-
}
|
167 |
-
|
168 |
-
// We use the temporary array so when we json_encode we get an array, not an object.
|
169 |
-
$item_details = $tmp;
|
170 |
-
}
|
171 |
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
}
|
176 |
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
200 |
|
201 |
-
|
202 |
|
203 |
-
|
204 |
|
205 |
-
|
206 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
207 |
}
|
|
|
|
|
208 |
|
209 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
210 |
}
|
211 |
-
} catch (Exception $e) {
|
212 |
-
$this->_errors[] = $e->getMessage();
|
213 |
-
$this->_errors[] = $e->getTrace();
|
214 |
-
Mage::log($e->getMessage(), Zend_Log::ERR);
|
215 |
-
Mage::logException($e);
|
216 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
217 |
|
218 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
219 |
}
|
220 |
|
221 |
public function syncSubscriber (Varien_Event_Observer $observer) {
|
222 |
-
// Mage::getSingleton('klaviyo_reclaim/api')->lists();
|
223 |
-
|
224 |
if (!Mage::helper('klaviyo_reclaim')->isEnabled()) {
|
225 |
return $observer;
|
226 |
}
|
@@ -255,7 +374,8 @@ class Klaviyo_Reclaim_Model_Observer
|
|
255 |
$subscriber_status = $subscriber->getStatus();
|
256 |
|
257 |
if ($subscriber_status == Mage_Newsletter_Model_Subscriber::STATUS_UNCONFIRMED ||
|
258 |
-
$subscriber_status == Mage_Newsletter_Model_Subscriber::STATUS_SUBSCRIBED
|
|
|
259 |
|
260 |
$response = Mage::getSingleton('klaviyo_reclaim/api')->listSubscriberAdd($list_id, $email, $is_requiring_confirmation);
|
261 |
|
@@ -303,8 +423,14 @@ class Klaviyo_Reclaim_Model_Observer
|
|
303 |
Mage::getSingleton('klaviyo_reclaim/api')->listSubscriberDelete($list_id, $email);
|
304 |
}
|
305 |
|
306 |
-
|
307 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
308 |
}
|
309 |
|
310 |
public function getTracker($quote) {
|
15 |
*/
|
16 |
class Klaviyo_Reclaim_Model_Observer
|
17 |
{
|
18 |
+
/**
|
19 |
+
* Cache for booleans for whether the Klaviyo is enabled for each store.
|
20 |
+
*
|
21 |
+
* @var array
|
22 |
+
*/
|
23 |
+
var $is_enabled_cache = array();
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Cache for tracker objects for each store.
|
27 |
+
*
|
28 |
+
* @var array
|
29 |
+
*/
|
30 |
var $tracker_cache = array();
|
31 |
|
32 |
/**
|
42 |
* @return Klaviyo_Reclaim_Model_Observer
|
43 |
*/
|
44 |
public function trackQuotes() {
|
45 |
+
$this->_errors = array();
|
46 |
|
47 |
+
$quotes_found = 0;
|
48 |
+
$quotes_tracked = 0;
|
|
|
49 |
|
|
|
50 |
try {
|
|
|
|
|
51 |
// Find quotes that are at least 15 minutes old and have been updated in the last 60 minutes.
|
52 |
$created_window_minutes = 15;
|
|
|
|
|
|
|
|
|
|
|
53 |
$updated_window_minutes = 60;
|
54 |
|
55 |
+
$quotes = $this->_fetchRecentQuotes($created_window_minutes, $updated_window_minutes);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
56 |
|
57 |
+
$quotes_found = count($quotes);
|
58 |
$quotes_tracked = 0;
|
59 |
|
60 |
foreach ($quotes as $quote) {
|
61 |
+
if ($this->_maybeTrackQuote($quote)) {
|
62 |
+
$quotes_tracked++;
|
|
|
|
|
|
|
63 |
}
|
64 |
+
}
|
65 |
+
} catch (Exception $e) {
|
66 |
+
$this->_errors[] = $e->getMessage();
|
67 |
+
$this->_errors[] = $e->getTrace();
|
68 |
+
Mage::log($e->getMessage(), Zend_Log::ERR, Mage::helper('klaviyo_reclaim')->getLogFile());
|
69 |
+
Mage::logException($e);
|
70 |
+
}
|
71 |
|
72 |
+
// Log results in case we need to debug.
|
73 |
+
$json = Mage::helper('core')->jsonEncode(array(
|
74 |
+
'cron_job' => 'klaviyo_track_quotes',
|
75 |
+
'found' => $quotes_found,
|
76 |
+
'tracked' => $quotes_tracked
|
77 |
+
));
|
78 |
+
Mage::log($json, Zend_Log::INFO, Mage::helper('klaviyo_reclaim')->getLogFile());
|
79 |
|
80 |
+
return $this;
|
81 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
82 |
|
83 |
+
/**
|
84 |
+
* Read quotes that have been updated recently, but were created at least X minutes ago.
|
85 |
+
*
|
86 |
+
* @param $created_window_minutes (int)
|
87 |
+
* @param $updated_window_minutes (int)
|
88 |
+
* @return Mage_Sales_Model_Resource_Quote_Collection
|
89 |
+
*/
|
90 |
+
protected function _fetchRecentQuotes ($created_window_minutes, $updated_window_minutes) {
|
91 |
+
$adapter = Mage::getSingleton('core/resource')->getConnection('sales_read');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
92 |
|
93 |
+
$created_before = Zend_Date::now();
|
94 |
+
$created_before->sub($created_window_minutes, Zend_Date::MINUTE);
|
95 |
+
$created_before = $adapter->convertDateTime($created_before);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
96 |
|
97 |
+
$updated_after = Zend_Date::now();
|
98 |
+
$updated_after->sub($updated_window_minutes, Zend_Date::MINUTE);
|
99 |
+
$updated_after = $adapter->convertDateTime($updated_after);
|
|
|
100 |
|
101 |
+
return Mage::getResourceModel('sales/quote_collection')
|
102 |
+
->addFieldToFilter('converted_at', array('null' => true))
|
103 |
+
->addFieldToFilter('created_at', array('lteq' => $created_before))
|
104 |
+
->addFieldToFilter('updated_at', array('gteq' => $updated_after));
|
105 |
+
}
|
106 |
+
|
107 |
+
/**
|
108 |
+
* Send tracking information to Klaviyo for a quote if it has the necessary information.
|
109 |
+
*
|
110 |
+
* @param $quote (Mage_Sales_Model_Quote)
|
111 |
+
* @return bool
|
112 |
+
*/
|
113 |
+
protected function _maybeTrackQuote ($quote) {
|
114 |
+
if (!$this->_isEnabledForStore($quote->getStore())) {
|
115 |
+
return false;
|
116 |
+
}
|
117 |
+
|
118 |
+
$tracker = $this->getTracker($quote);
|
119 |
+
$billing_address = $quote->getBillingAddress();
|
120 |
+
|
121 |
+
if (!$billing_address || !$tracker) {
|
122 |
+
return false;
|
123 |
+
}
|
124 |
+
|
125 |
+
// Skip quotes that don't have an email or remote_ip set. Checking the IP is our best guess
|
126 |
+
// at not sending emails for quotes created on the Magento backend. There
|
127 |
+
// doesn't seem a reliable way to tell if a quote is from the frontend or backend.
|
128 |
+
if (!$quote->getCustomerEmail() || is_null($quote->getRemoteIp())) {
|
129 |
+
return false;
|
130 |
+
}
|
131 |
+
|
132 |
+
$quote_id = $quote->getId();
|
133 |
+
|
134 |
+
$checkout = Mage::helper('klaviyo_reclaim')->getCheckout($quote_id);
|
135 |
+
|
136 |
+
$customer_properties = $this->_customerPropertiesForQuote($quote, $billing_address);
|
137 |
|
138 |
+
$item_details = array();
|
139 |
|
140 |
+
$configurable_product_ids = array();
|
141 |
|
142 |
+
foreach ($quote->getItemsCollection() as $quote_item) {
|
143 |
+
$response = $this->_quoteItemData($quote_item);
|
144 |
+
$quote_item_data = $response['quote_item'];
|
145 |
+
$is_product_configurable = $response['is_product_configurable'];
|
146 |
+
|
147 |
+
// Configurable product - The "product" when the product has multiple SKUs, e.g. "t-shirt"
|
148 |
+
// Simple product - The "SKU," e.g. "red, medium t-shirt." For products that don't have options or variations,
|
149 |
+
// this is the "product."
|
150 |
+
// http://docs.magento.com/m1/ce/user_guide/catalog/product-create.html
|
151 |
+
if ($is_product_configurable) {
|
152 |
+
$configurable_product_ids[] = $quote_item_data['product']['id'];
|
153 |
+
}
|
154 |
+
|
155 |
+
$item_details[] = $quote_item_data;
|
156 |
+
}
|
157 |
+
|
158 |
+
$response = $this->_mergeConfigurableAndSimpleQuoteItems($item_details, $configurable_product_ids);
|
159 |
+
$item_details = $response['items'];
|
160 |
+
$item_count = $response['item_count'];
|
161 |
+
$item_descriptions = $response['item_descriptions'];
|
162 |
+
|
163 |
+
// Skip quotes that don't have items.
|
164 |
+
if (empty($item_details)) {
|
165 |
+
return false;
|
166 |
+
}
|
167 |
+
|
168 |
+
$checkout_id = $checkout->getId();
|
169 |
+
$checkout_url = $quote->getStore()->getUrl('reclaim/index/view', array('_query' => array('id' => $checkout_id)));
|
170 |
+
|
171 |
+
$properties = array(
|
172 |
+
'$event_id' => $quote_id,
|
173 |
+
'$value' => (float) $quote->getGrandTotal(),
|
174 |
+
'Items' => $item_descriptions,
|
175 |
+
'Items Count' => $item_count,
|
176 |
+
'$extra' => array(
|
177 |
+
'checkout_url' => $checkout_url,
|
178 |
+
'checkout_id' => $checkout_id,
|
179 |
+
'line_items' => $item_details,
|
180 |
+
)
|
181 |
+
);
|
182 |
+
|
183 |
+
$coupon_code = $quote->getCouponCode();
|
184 |
+
if ($coupon_code) {
|
185 |
+
$totals = $quote->getTotals();
|
186 |
+
$properties['Discount Codes'] = array($coupon_code);
|
187 |
+
if (array_key_exists('discount', $totals) && is_object($totals['discount'])) {
|
188 |
+
$properties['Total Discounts'] = (float) $totals['discount']->getValue() * -1;
|
189 |
+
}
|
190 |
+
}
|
191 |
+
|
192 |
+
$timestamp = strtotime($quote->getUpdatedAt());
|
193 |
+
|
194 |
+
$tracker->track('Checkout Started', $customer_properties, $properties, $timestamp);
|
195 |
+
|
196 |
+
if (!$quote->getIsActive()) {
|
197 |
+
$tracker->track('Checkout Completed', $customer_properties, $properties, $timestamp);
|
198 |
+
}
|
199 |
+
|
200 |
+
return true;
|
201 |
+
}
|
202 |
+
|
203 |
+
/**
|
204 |
+
* Normalize customer and billing address data to an array that's JSON serializable.
|
205 |
+
*
|
206 |
+
* @param $quote (Mage_Sales_Model_Quote)
|
207 |
+
* @param $billing_address (Mage_Sales_Model_Quote_Address)
|
208 |
+
* @return array
|
209 |
+
*/
|
210 |
+
protected function _customerPropertiesForQuote($quote, $billing_address) {
|
211 |
+
$properties = array(
|
212 |
+
'$email' => $quote->getCustomerEmail(),
|
213 |
+
'$first_name' => $quote->getCustomerFirstname(),
|
214 |
+
'$last_name' => $quote->getCustomerLastname(),
|
215 |
+
'Magento Store' => $quote->getStore()->getName(),
|
216 |
+
'Magento WebsiteID' => $quote->getStore()->getWebsiteId(),
|
217 |
+
'location' => array(
|
218 |
+
'source' => 'magento',
|
219 |
+
'address1' => $billing_address->getStreet(1),
|
220 |
+
'city' => $billing_address->getCity(),
|
221 |
+
'region' => $billing_address->getRegion(),
|
222 |
+
'zip' => $billing_address->getPostcode(),
|
223 |
+
'country' => $billing_address->getCountry()
|
224 |
+
)
|
225 |
+
);
|
226 |
+
|
227 |
+
if ($billing_address->getStreet(2)) {
|
228 |
+
$properties['location']['address2'] = $billing_address->getStreet(2);
|
229 |
+
}
|
230 |
+
|
231 |
+
return $properties;
|
232 |
+
}
|
233 |
+
|
234 |
+
/**
|
235 |
+
* Normalize quote item (line item) to an array that's JSON serializable.
|
236 |
+
*
|
237 |
+
* @param $quote_item (Mage_Sales_Model_Quote_Item)
|
238 |
+
* @return array
|
239 |
+
*/
|
240 |
+
protected function _quoteItemData ($quote_item) {
|
241 |
+
$data = array();
|
242 |
+
|
243 |
+
$quote_item_quantity = $quote_item->getQty();
|
244 |
+
$quote_item_product = Mage::getModel('catalog/product')->load($quote_item->getProduct()->getId());
|
245 |
+
|
246 |
+
$product_details = array(
|
247 |
+
'id' => $quote_item_product->getId(),
|
248 |
+
'sku' => $quote_item_product->getSKU(),
|
249 |
+
'name' => $quote_item_product->getName(),
|
250 |
+
'price' => (float) $quote_item_product->getPrice(),
|
251 |
+
'special_price' => (float) $quote_item_product->getFinalPrice()
|
252 |
+
);
|
253 |
+
|
254 |
+
$product_images = array();
|
255 |
+
// If we have a simple product for this configurable product, get the simple name
|
256 |
+
// and also attempt to get the images
|
257 |
+
if ($option = $quote_item->getOptionByCode('simple_product')) {
|
258 |
+
$simple_product = Mage::getModel('catalog/product')->load($option->getProduct()->getId());
|
259 |
+
|
260 |
+
$product_details['simple_name'] = $simple_product->getName();
|
261 |
+
foreach ($simple_product->getMediaGalleryImages() as $product_image) {
|
262 |
+
if (!$product_image->getDisabled()) {
|
263 |
+
$product_images[] = array(
|
264 |
+
'url' => $product_image->getUrl()
|
265 |
+
);
|
266 |
}
|
267 |
+
}
|
268 |
+
}
|
269 |
|
270 |
+
// if there is no simple product or we tried but couldn't find any
|
271 |
+
// images for the simple product just grab the configurable images
|
272 |
+
if (empty($product_images)) {
|
273 |
+
foreach ($quote_item_product->getMediaGalleryImages() as $product_image) {
|
274 |
+
if (!$product_image->getDisabled()) {
|
275 |
+
$product_images[] = array(
|
276 |
+
'url' => $product_image->getUrl()
|
277 |
+
);
|
278 |
+
}
|
279 |
}
|
|
|
|
|
|
|
|
|
|
|
280 |
}
|
281 |
+
$product_details['images'] = $product_images;
|
282 |
+
|
283 |
+
return array(
|
284 |
+
'is_product_configurable' => $quote_item_product->isConfigurable(),
|
285 |
+
'quote_item' => array(
|
286 |
+
'quantity' => (float) $quote_item_quantity,
|
287 |
+
'row_total' => (float) $quote_item->getBaseRowTotal(),
|
288 |
+
'row_discount' => (float) $quote_item->getBaseDiscountAmount(),
|
289 |
+
'product' => $product_details
|
290 |
+
)
|
291 |
+
);
|
292 |
+
}
|
293 |
|
294 |
+
/**
|
295 |
+
* Merged quote item (line item) details in cases where there are two line items to represent
|
296 |
+
* a configurable product and its options.
|
297 |
+
*
|
298 |
+
* @param $item_details (array)
|
299 |
+
* @param $configurable_product_ids (array)
|
300 |
+
* @return array
|
301 |
+
*/
|
302 |
+
protected function _mergeConfigurableAndSimpleQuoteItems($item_details, $configurable_product_ids) {
|
303 |
+
$model_configurable = Mage::getModel('catalog/product_type_configurable');
|
304 |
+
|
305 |
+
$merged = array();
|
306 |
+
$item_descriptions = array();
|
307 |
+
$item_count = 0;
|
308 |
+
|
309 |
+
// Look for simple products that are really represented by a configurable product.
|
310 |
+
foreach ($item_details as $line_index => $line) {
|
311 |
+
$product_id = $line['product']['id'];
|
312 |
+
if (in_array($product_id, $configurable_product_ids) || $line['row_total']) {
|
313 |
+
$merged[] = $line;
|
314 |
+
} else {
|
315 |
+
$configurable_parent_ids = $model_configurable->getParentIdsByChild($product_id);
|
316 |
+
$common_ids = array_intersect($configurable_product_ids, $configurable_parent_ids);
|
317 |
+
|
318 |
+
// If it's a simple product placeholder for a configurable product, discard it. Otherwise, keep it.
|
319 |
+
if (empty($common_ids)) {
|
320 |
+
$merged[] = $line;
|
321 |
+
} else {
|
322 |
+
continue;
|
323 |
+
}
|
324 |
+
}
|
325 |
+
|
326 |
+
if (array_key_exists('simple_name', $line['product'])) {
|
327 |
+
$item_descriptions[] = $line['product']['simple_name'];
|
328 |
+
} else {
|
329 |
+
$item_descriptions[] = $line['product']['name'];
|
330 |
+
}
|
331 |
+
|
332 |
+
$item_count += $line['quantity'];
|
333 |
+
}
|
334 |
+
|
335 |
+
return array(
|
336 |
+
'items' => $merged,
|
337 |
+
'item_count' => $item_count,
|
338 |
+
'item_descriptions' => $item_descriptions
|
339 |
+
);
|
340 |
}
|
341 |
|
342 |
public function syncSubscriber (Varien_Event_Observer $observer) {
|
|
|
|
|
343 |
if (!Mage::helper('klaviyo_reclaim')->isEnabled()) {
|
344 |
return $observer;
|
345 |
}
|
374 |
$subscriber_status = $subscriber->getStatus();
|
375 |
|
376 |
if ($subscriber_status == Mage_Newsletter_Model_Subscriber::STATUS_UNCONFIRMED ||
|
377 |
+
$subscriber_status == Mage_Newsletter_Model_Subscriber::STATUS_SUBSCRIBED ||
|
378 |
+
$subscriber_status == Mage_Newsletter_Model_Subscriber::STATUS_NOT_ACTIVE) {
|
379 |
|
380 |
$response = Mage::getSingleton('klaviyo_reclaim/api')->listSubscriberAdd($list_id, $email, $is_requiring_confirmation);
|
381 |
|
423 |
Mage::getSingleton('klaviyo_reclaim/api')->listSubscriberDelete($list_id, $email);
|
424 |
}
|
425 |
|
426 |
+
protected function _isEnabledForStore($store) {
|
427 |
+
$store_id = $store->getId();
|
428 |
+
|
429 |
+
if (!isset($this->is_enabled_cache[$store_id])) {
|
430 |
+
$this->is_enabled_cache[$store_id] = Mage::helper('klaviyo_reclaim')->isEnabled($store);
|
431 |
+
}
|
432 |
+
|
433 |
+
return $this->is_enabled_cache[$store_id];
|
434 |
}
|
435 |
|
436 |
public function getTracker($quote) {
|
app/code/community/Klaviyo/Reclaim/controllers/IndexController.php
CHANGED
@@ -47,7 +47,7 @@ class Klaviyo_Reclaim_IndexController extends Mage_Core_Controller_Front_Action
|
|
47 |
}
|
48 |
}
|
49 |
}
|
50 |
-
|
51 |
$params = array();
|
52 |
foreach (self::$_preservableRequestParams as $key) {
|
53 |
$value = $this->getRequest()->getParam($key);
|
@@ -91,7 +91,7 @@ class Klaviyo_Reclaim_IndexController extends Mage_Core_Controller_Front_Action
|
|
91 |
}
|
92 |
|
93 |
/**
|
94 |
-
*
|
95 |
*/
|
96 |
public function statusAction()
|
97 |
{
|
@@ -102,28 +102,140 @@ class Klaviyo_Reclaim_IndexController extends Mage_Core_Controller_Front_Action
|
|
102 |
} else {
|
103 |
$helper = Mage::helper('klaviyo_reclaim');
|
104 |
|
105 |
-
$
|
106 |
-
$is_api_key_set = $helper->getPublicApiKey() != NULL;
|
107 |
|
108 |
-
$
|
109 |
-
$hour_ago = Zend_Date::now();
|
110 |
-
$hour_ago->sub(60, Zend_Date::MINUTE);
|
111 |
-
$hour_ago = $adapter->convertDateTime($hour_ago);
|
112 |
|
113 |
-
$
|
114 |
-
|
115 |
-
->addFieldToFilter('finished_at', array('gteq' => $hour_ago))
|
116 |
-
->count() > 0;
|
117 |
|
118 |
-
$
|
|
|
119 |
|
120 |
$response = array(
|
121 |
-
'data' => array(
|
|
|
|
|
|
|
|
|
|
|
122 |
);
|
123 |
}
|
124 |
|
125 |
$this->getResponse()->setHeader('Content-type', 'application/json');
|
126 |
$this->getResponse()->setBody(Mage::helper('core')->jsonEncode($response));
|
127 |
-
return;
|
128 |
}
|
129 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
}
|
48 |
}
|
49 |
}
|
50 |
+
|
51 |
$params = array();
|
52 |
foreach (self::$_preservableRequestParams as $key) {
|
53 |
$value = $this->getRequest()->getParam($key);
|
91 |
}
|
92 |
|
93 |
/**
|
94 |
+
* Klaviyo extension status action
|
95 |
*/
|
96 |
public function statusAction()
|
97 |
{
|
102 |
} else {
|
103 |
$helper = Mage::helper('klaviyo_reclaim');
|
104 |
|
105 |
+
$version = (string) Mage::getConfig()->getNode('modules/Klaviyo_Reclaim/version');
|
|
|
106 |
|
107 |
+
$config_details = $this->_getExtensionConfigDetails();
|
|
|
|
|
|
|
108 |
|
109 |
+
$since_minutes = 60;
|
110 |
+
$cron_details = $this->_getCronScheduleDetails($since_minutes);
|
|
|
|
|
111 |
|
112 |
+
$num_quotes = 5;
|
113 |
+
$quote_details = $this->_getQuoteDetails($num_quotes);
|
114 |
|
115 |
$response = array(
|
116 |
+
'data' => array(
|
117 |
+
'version' => $version,
|
118 |
+
'config' => $config_details,
|
119 |
+
'cron' => $cron_details,
|
120 |
+
'quotes' => $quote_details
|
121 |
+
)
|
122 |
);
|
123 |
}
|
124 |
|
125 |
$this->getResponse()->setHeader('Content-type', 'application/json');
|
126 |
$this->getResponse()->setBody(Mage::helper('core')->jsonEncode($response));
|
|
|
127 |
}
|
128 |
+
|
129 |
+
protected function _getExtensionConfigDetails () {
|
130 |
+
$helper = Mage::helper('klaviyo_reclaim');
|
131 |
+
|
132 |
+
return array(
|
133 |
+
'global' => array(
|
134 |
+
'enabled' => $helper->isEnabled(),
|
135 |
+
'api_key' => $helper->getPublicApiKey() != NULL
|
136 |
+
)
|
137 |
+
);
|
138 |
+
}
|
139 |
+
|
140 |
+
protected function _getCronScheduleDetails ($since_minutes) {
|
141 |
+
$adapter = Mage::getSingleton('core/resource')->getConnection('sales_read');
|
142 |
+
$since = Zend_Date::now();
|
143 |
+
$since->sub($since_minutes, Zend_Date::MINUTE);
|
144 |
+
$since = $adapter->convertDateTime($since);
|
145 |
+
|
146 |
+
$query = $this->_getKlaviyoCronScheduleBaseQuery()
|
147 |
+
->addFieldToFilter('status', Mage_Cron_Model_Schedule::STATUS_SUCCESS)
|
148 |
+
->addFieldToFilter('finished_at', array('gteq' => $since))
|
149 |
+
->addOrder('finished_at', $direction='desc');
|
150 |
+
|
151 |
+
$has_suceeded = $query->count() > 0;
|
152 |
+
$last_success = array();
|
153 |
+
|
154 |
+
if ($has_suceeded) {
|
155 |
+
$job = $query->getFirstItem();
|
156 |
+
|
157 |
+
$last_success = array(
|
158 |
+
'id' => $job->getScheduleId(),
|
159 |
+
'finished_at' => Mage::getSingleton('core/date')->gmtDate($job->getFinishedAt()),
|
160 |
+
'messages' => $job->getMessages()
|
161 |
+
);
|
162 |
+
}
|
163 |
+
|
164 |
+
$query = $this->_getKlaviyoCronScheduleBaseQuery()
|
165 |
+
->addFieldToFilter('status', array('in' => array(
|
166 |
+
Mage_Cron_Model_Schedule::STATUS_MISSED,
|
167 |
+
Mage_Cron_Model_Schedule::STATUS_ERROR
|
168 |
+
)))
|
169 |
+
->addFieldToFilter('created_at', array('gteq' => $since))
|
170 |
+
->addOrder('finished_at', $direction='desc');
|
171 |
+
|
172 |
+
$has_failed = $query->count() > 0;
|
173 |
+
$failures = array();
|
174 |
+
|
175 |
+
if ($has_failed) {
|
176 |
+
foreach ($query as $job) {
|
177 |
+
$failures[] = array(
|
178 |
+
'id' => $job->getScheduleId(),
|
179 |
+
'status' => $job->getStatus(),
|
180 |
+
'messages' => $job->getMessage()
|
181 |
+
);
|
182 |
+
}
|
183 |
+
}
|
184 |
+
|
185 |
+
return array(
|
186 |
+
'last_success' => $last_success,
|
187 |
+
'failures' => $failures
|
188 |
+
);
|
189 |
+
}
|
190 |
+
|
191 |
+
protected function _getKlaviyoCronScheduleBaseQuery () {
|
192 |
+
return Mage::getModel('cron/schedule')->getCollection()
|
193 |
+
->addFieldToFilter('job_code', 'klaviyo_track_quotes');
|
194 |
+
}
|
195 |
+
|
196 |
+
protected function _getQuoteDetails ($num_quotes) {
|
197 |
+
$helper = Mage::helper('klaviyo_reclaim');
|
198 |
+
|
199 |
+
$has_checkout_ids = Mage::getModel('klaviyo_reclaim/checkout')->getCollection()->count() > 0;
|
200 |
+
|
201 |
+
$query = Mage::getResourceModel('sales/quote_collection')
|
202 |
+
->addFieldToFilter('converted_at', array('null' => true))
|
203 |
+
->addOrder('updated_at', $direction='desc')
|
204 |
+
->setPageSize($num_quotes)
|
205 |
+
->setCurPage(1);
|
206 |
+
|
207 |
+
$quotes = array();
|
208 |
+
foreach ($query as $quote) {
|
209 |
+
$store = $quote->getStore();
|
210 |
+
$email = $quote->getCustomerEmail();
|
211 |
+
|
212 |
+
if ($email) {
|
213 |
+
$pieces = explode('@', $email, 2);
|
214 |
+
|
215 |
+
// Obfuscates the email address from `someone@example.com` to `so******@example.com`.
|
216 |
+
$email = substr($pieces[0], 0, 2) . str_repeat('*', 6) . '@' . $pieces[1];
|
217 |
+
}
|
218 |
+
|
219 |
+
$quotes[] = array(
|
220 |
+
'id' => $quote->getEntityId(),
|
221 |
+
'store_id' => $quote->getStoreId(),
|
222 |
+
'gmt_created_at' => Mage::getSingleton('core/date')->gmtDate($quote->getCreatedAt()),
|
223 |
+
'gmt_updated_at' => Mage::getSingleton('core/date')->gmtDate($quote->getUpdatedAt()),
|
224 |
+
'customer_email' => $email,
|
225 |
+
'remote_ip' => $quote->getRemoteIp(),
|
226 |
+
'num_items' => count($quote->getItemsCollection()),
|
227 |
+
'is_active' => $quote->getIsActive(),
|
228 |
+
|
229 |
+
'config' => array(
|
230 |
+
'is_enabled' => $helper->isEnabled($store),
|
231 |
+
'api_key' => $helper->getPublicApiKey($store) != NULL
|
232 |
+
)
|
233 |
+
);
|
234 |
+
}
|
235 |
+
|
236 |
+
return array(
|
237 |
+
'has_checkout_ids' => $has_checkout_ids,
|
238 |
+
'quotes' => $quotes
|
239 |
+
);
|
240 |
+
}
|
241 |
+
}
|
app/code/community/Klaviyo/Reclaim/controllers/KlaviyoController.php
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
class Klaviyo_Reclaim_KlaviyoController extends Mage_Adminhtml_Controller_Action
|
3 |
+
{
|
4 |
+
const OAUTH_CALLBACK_URL = 'https://www.klaviyo.com/integrations/auth/magento';
|
5 |
+
|
6 |
+
const OAUTH_CONSUMER_NAME = 'Klaviyo REST';
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Generate OAuth keys, store them into the Klaviyo configuration, and display them.
|
10 |
+
*/
|
11 |
+
public function oauthkeysAction()
|
12 |
+
{
|
13 |
+
$oauthHelper = Mage::helper('oauth');
|
14 |
+
|
15 |
+
$consumerKey = $oauthHelper->generateConsumerKey();
|
16 |
+
$consumerSecret = $oauthHelper->generateConsumerSecret();
|
17 |
+
|
18 |
+
$consumerModel = Mage::getModel('oauth/consumer');
|
19 |
+
|
20 |
+
$consumerData = array(
|
21 |
+
'name' => self::OAUTH_CONSUMER_NAME,
|
22 |
+
'key' => $consumerKey,
|
23 |
+
'secret' => $consumerSecret);
|
24 |
+
|
25 |
+
$consumerModel->addData($consumerData);
|
26 |
+
$consumerModel->save();
|
27 |
+
|
28 |
+
$token = Mage::getModel('oauth/token');
|
29 |
+
|
30 |
+
$token->createRequestToken($consumerModel->getId(), self::OAUTH_CALLBACK_URL);
|
31 |
+
$token->convertToAccess();
|
32 |
+
|
33 |
+
$requestToken = $token->getToken();
|
34 |
+
$requestTokenSecret = $token->getSecret();
|
35 |
+
|
36 |
+
$user = Mage::getSingleton('admin/session')->getUser();
|
37 |
+
$token->authorize($user->getId(), Mage_Oauth_Model_Token::USER_TYPE_ADMIN);
|
38 |
+
|
39 |
+
$reclaimHelper = Mage::helper('klaviyo_reclaim');
|
40 |
+
$reclaimHelper->setConsumerKey($consumerKey);
|
41 |
+
$reclaimHelper->setConsumerSecret($consumerSecret);
|
42 |
+
$reclaimHelper->setAuthorizationToken($requestToken);
|
43 |
+
$reclaimHelper->setAuthorizationSecret($requestTokenSecret);
|
44 |
+
|
45 |
+
// redirect to the extension configuration page
|
46 |
+
$this->_redirectUrl(Mage::helper('adminhtml')->getUrl('adminhtml/system_config/edit/section/reclaim'));
|
47 |
+
return $this;
|
48 |
+
}
|
49 |
+
}
|
app/code/community/Klaviyo/Reclaim/etc/adminhtml.xml
CHANGED
@@ -1,23 +1,23 @@
|
|
1 |
<?xml version="1.0"?>
|
2 |
<config>
|
3 |
-
<acl>
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
</acl>
|
23 |
-
</config>
|
1 |
<?xml version="1.0"?>
|
2 |
<config>
|
3 |
+
<acl>
|
4 |
+
<resources>
|
5 |
+
<admin>
|
6 |
+
<children>
|
7 |
+
<system>
|
8 |
+
<children>
|
9 |
+
<config>
|
10 |
+
<children>
|
11 |
+
<reclaim translate="title">
|
12 |
+
<title>Klaviyo</title>
|
13 |
+
<sort_order>200</sort_order>
|
14 |
+
</reclaim>
|
15 |
+
</children>
|
16 |
+
</config>
|
17 |
+
</children>
|
18 |
+
</system>
|
19 |
+
</children>
|
20 |
+
</admin>
|
21 |
+
</resources>
|
22 |
+
</acl>
|
23 |
+
</config>
|
app/code/community/Klaviyo/Reclaim/etc/api2.xml
ADDED
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<config>
|
2 |
+
<api2>
|
3 |
+
<resource_groups>
|
4 |
+
<reclaim translate="title" module="Klaviyo_Reclaim">
|
5 |
+
<title>Coupon API</title>
|
6 |
+
<sort_order>10</sort_order>
|
7 |
+
</reclaim>
|
8 |
+
</resource_groups>
|
9 |
+
<resources>
|
10 |
+
<reclaim translate="title" module="Klaviyo_Reclaim">
|
11 |
+
<group>reclaim</group>
|
12 |
+
<model>reclaim/api2_coupon</model>
|
13 |
+
<title>Coupon Code Auto Generation</title>
|
14 |
+
<sort_order>10</sort_order>
|
15 |
+
<privileges>
|
16 |
+
<admin>
|
17 |
+
<create>1</create>
|
18 |
+
<retrieve>1</retrieve>
|
19 |
+
<delete>1</delete>
|
20 |
+
</admin>
|
21 |
+
</privileges>
|
22 |
+
<attributes>
|
23 |
+
<coupon_id>Coupon ID</coupon_id>
|
24 |
+
<code>Code</code>
|
25 |
+
<qty>Quantity</qty>
|
26 |
+
<length>Length</length>
|
27 |
+
<format>Format</format>
|
28 |
+
</attributes>
|
29 |
+
<routes>
|
30 |
+
<route_collection>
|
31 |
+
<route>/klaviyo/rules/:rule_id/codes</route>
|
32 |
+
<action_type>collection</action_type>
|
33 |
+
</route_collection>
|
34 |
+
<route_entity>
|
35 |
+
<route>/klaviyo/rules/:rule_id/codes/:coupon_id</route>
|
36 |
+
<action_type>entity</action_type>
|
37 |
+
</route_entity>
|
38 |
+
</routes>
|
39 |
+
<versions>1</versions>
|
40 |
+
</reclaim>
|
41 |
+
<rules translate="title" module="Klaviyo_Reclaim">
|
42 |
+
<group>reclaim</group>
|
43 |
+
<model>reclaim/api2_rule</model>
|
44 |
+
<title>Fetch Rule Information</title>
|
45 |
+
<sort_order>20</sort_order>
|
46 |
+
<privileges>
|
47 |
+
<admin>
|
48 |
+
<retrieve>1</retrieve>
|
49 |
+
</admin>
|
50 |
+
</privileges>
|
51 |
+
<attributes>
|
52 |
+
<rule_id>Rule Id</rule_id>
|
53 |
+
<name>Rule Name</name>
|
54 |
+
<description>Description</description>
|
55 |
+
<from_date>From Date</from_date>
|
56 |
+
<to_date>To Date</to_date>
|
57 |
+
<uses_per_coupon>Uses Per Coupon</uses_per_coupon>
|
58 |
+
<uses_per_customer>Uses Per Customer</uses_per_customer>
|
59 |
+
<simple_action>Apply</simple_action>
|
60 |
+
<discount_amount>Discount Amount</discount_amount>
|
61 |
+
</attributes>
|
62 |
+
<routes>
|
63 |
+
<route_collection>
|
64 |
+
<route>/klaviyo/rules</route>
|
65 |
+
<action_type>collection</action_type>
|
66 |
+
</route_collection>
|
67 |
+
</routes>
|
68 |
+
<versions>1</versions>
|
69 |
+
</rules>
|
70 |
+
</resources>
|
71 |
+
</api2>
|
72 |
+
</config>
|
app/code/community/Klaviyo/Reclaim/etc/config.xml
CHANGED
@@ -2,7 +2,7 @@
|
|
2 |
<config>
|
3 |
<modules>
|
4 |
<Klaviyo_Reclaim>
|
5 |
-
<version>1.
|
6 |
</Klaviyo_Reclaim>
|
7 |
</modules>
|
8 |
<global>
|
@@ -25,15 +25,6 @@
|
|
25 |
</klaviyo_subscribe_delete_observer>
|
26 |
</observers>
|
27 |
</newsletter_subscriber_delete_after>
|
28 |
-
<customer_save_after>
|
29 |
-
<observers>
|
30 |
-
<klaviyo_update_customer>
|
31 |
-
<type>singleton</type>
|
32 |
-
<class>Klaviyo_Reclaim_Model_Observer</class>
|
33 |
-
<method>syncCustomer</method>
|
34 |
-
</klaviyo_update_customer>
|
35 |
-
</observers>
|
36 |
-
</customer_save_after>
|
37 |
</events>
|
38 |
<models>
|
39 |
<klaviyo_reclaim>
|
@@ -48,6 +39,9 @@
|
|
48 |
</checkout>
|
49 |
</entities>
|
50 |
</reclaim_mysql4>
|
|
|
|
|
|
|
51 |
</models>
|
52 |
<helpers>
|
53 |
<klaviyo_reclaim>
|
@@ -68,7 +62,17 @@
|
|
68 |
</klaviyo_reclaim_setup>
|
69 |
</resources>
|
70 |
</global>
|
71 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
72 |
<adminhtml>
|
73 |
<layout>
|
74 |
<updates>
|
@@ -78,7 +82,6 @@
|
|
78 |
</updates>
|
79 |
</layout>
|
80 |
</adminhtml>
|
81 |
-
|
82 |
<frontend>
|
83 |
<routers>
|
84 |
<klaviyo_reclaim>
|
2 |
<config>
|
3 |
<modules>
|
4 |
<Klaviyo_Reclaim>
|
5 |
+
<version>1.6.2</version>
|
6 |
</Klaviyo_Reclaim>
|
7 |
</modules>
|
8 |
<global>
|
25 |
</klaviyo_subscribe_delete_observer>
|
26 |
</observers>
|
27 |
</newsletter_subscriber_delete_after>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
</events>
|
29 |
<models>
|
30 |
<klaviyo_reclaim>
|
39 |
</checkout>
|
40 |
</entities>
|
41 |
</reclaim_mysql4>
|
42 |
+
<reclaim>
|
43 |
+
<class>Klaviyo_Reclaim_Model</class>
|
44 |
+
</reclaim>
|
45 |
</models>
|
46 |
<helpers>
|
47 |
<klaviyo_reclaim>
|
62 |
</klaviyo_reclaim_setup>
|
63 |
</resources>
|
64 |
</global>
|
65 |
+
<admin>
|
66 |
+
<routers>
|
67 |
+
<adminhtml>
|
68 |
+
<args>
|
69 |
+
<modules>
|
70 |
+
<Klaviyo_Reclaim after="Mage_Adminhtml">Klaviyo_Reclaim</Klaviyo_Reclaim>
|
71 |
+
</modules>
|
72 |
+
</args>
|
73 |
+
</adminhtml>
|
74 |
+
</routers>
|
75 |
+
</admin>
|
76 |
<adminhtml>
|
77 |
<layout>
|
78 |
<updates>
|
82 |
</updates>
|
83 |
</layout>
|
84 |
</adminhtml>
|
|
|
85 |
<frontend>
|
86 |
<routers>
|
87 |
<klaviyo_reclaim>
|
app/code/community/Klaviyo/Reclaim/etc/system.xml
CHANGED
@@ -26,7 +26,6 @@
|
|
26 |
<show_in_default>1</show_in_default>
|
27 |
<show_in_website>1</show_in_website>
|
28 |
<show_in_store>1</show_in_store>
|
29 |
-
|
30 |
<fields>
|
31 |
<enabled translate="label">
|
32 |
<label>Enabled</label>
|
@@ -45,7 +44,7 @@
|
|
45 |
<show_in_default>1</show_in_default>
|
46 |
<show_in_website>1</show_in_website>
|
47 |
<show_in_store>1</show_in_store>
|
48 |
-
<comment><![CDATA[You can get your <strong>public</strong> API key from <a href="https://www.klaviyo.com/
|
49 |
</public_api_key>
|
50 |
<private_api_key translate="label">
|
51 |
<label>Klaviyo Private API Key</label>
|
@@ -54,7 +53,7 @@
|
|
54 |
<show_in_default>1</show_in_default>
|
55 |
<show_in_website>1</show_in_website>
|
56 |
<show_in_store>1</show_in_store>
|
57 |
-
<comment><![CDATA[You can get your <strong>private</strong> API key from <a href="https://www.klaviyo.com/account#api-keys" target="_blank">https://www.klaviyo.com/account#api-keys</a>.]]></comment>
|
58 |
</private_api_key>
|
59 |
<subscription_list translate="label comment">
|
60 |
<label>General Subscription List</label>
|
@@ -76,9 +75,62 @@
|
|
76 |
<show_in_store>1</show_in_store>
|
77 |
<comment><![CDATA[Normally, this should be <strong>Yes</strong>. Abuse of the double opt-in option may cause your account to be suspended.]]></comment>
|
78 |
</require_double_optin>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
79 |
</fields>
|
80 |
</general>
|
81 |
</groups>
|
82 |
</reclaim>
|
83 |
</sections>
|
84 |
-
</config>
|
26 |
<show_in_default>1</show_in_default>
|
27 |
<show_in_website>1</show_in_website>
|
28 |
<show_in_store>1</show_in_store>
|
|
|
29 |
<fields>
|
30 |
<enabled translate="label">
|
31 |
<label>Enabled</label>
|
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 |
+
<comment><![CDATA[You can get your <strong>public</strong> API key from <a href="https://www.klaviyo.com/account#api-keys-tab" target="_blank">https://www.klaviyo.com/account#api-keys-tab</a>.]]></comment>
|
48 |
</public_api_key>
|
49 |
<private_api_key translate="label">
|
50 |
<label>Klaviyo Private API Key</label>
|
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 |
+
<comment><![CDATA[You can get your <strong>private</strong> API key from <a href="https://www.klaviyo.com/account#api-keys-tab" target="_blank">https://www.klaviyo.com/account#api-keys-tab</a>.]]></comment>
|
57 |
</private_api_key>
|
58 |
<subscription_list translate="label comment">
|
59 |
<label>General Subscription List</label>
|
75 |
<show_in_store>1</show_in_store>
|
76 |
<comment><![CDATA[Normally, this should be <strong>Yes</strong>. Abuse of the double opt-in option may cause your account to be suspended.]]></comment>
|
77 |
</require_double_optin>
|
78 |
+
<consumer_key translate="label">
|
79 |
+
<label>Consumer Key</label>
|
80 |
+
<frontend_model>klaviyo_reclaim/oauth_credential_renderer</frontend_model>
|
81 |
+
<frontend_type>text</frontend_type>
|
82 |
+
<disabled>true</disabled>
|
83 |
+
<sort_order>60</sort_order>
|
84 |
+
<show_in_default>1</show_in_default>
|
85 |
+
<show_in_website>1</show_in_website>
|
86 |
+
<show_in_store>1</show_in_store>
|
87 |
+
<can_be_empty>1</can_be_empty>
|
88 |
+
</consumer_key>
|
89 |
+
<consumer_secret translate="label">
|
90 |
+
<label>Consumer Secret</label>
|
91 |
+
<frontend_model>klaviyo_reclaim/oauth_credential_renderer</frontend_model>
|
92 |
+
<frontend_type>text</frontend_type>
|
93 |
+
<disabled>true</disabled>
|
94 |
+
<sort_order>70</sort_order>
|
95 |
+
<show_in_default>1</show_in_default>
|
96 |
+
<show_in_website>1</show_in_website>
|
97 |
+
<show_in_store>1</show_in_store>
|
98 |
+
<can_be_empty>1</can_be_empty>
|
99 |
+
</consumer_secret>
|
100 |
+
<authorization_token translate="label">
|
101 |
+
<label>Authorization Token</label>
|
102 |
+
<frontend_model>klaviyo_reclaim/oauth_credential_renderer</frontend_model>
|
103 |
+
<frontend_type>text</frontend_type>
|
104 |
+
<disabled>true</disabled>
|
105 |
+
<sort_order>80</sort_order>
|
106 |
+
<show_in_default>1</show_in_default>
|
107 |
+
<show_in_website>1</show_in_website>
|
108 |
+
<show_in_store>1</show_in_store>
|
109 |
+
<can_be_empty>1</can_be_empty>
|
110 |
+
</authorization_token>
|
111 |
+
<authorization_secret translate="label">
|
112 |
+
<label>Authorization Secret</label>
|
113 |
+
<frontend_model>klaviyo_reclaim/oauth_credential_renderer</frontend_model>
|
114 |
+
<frontend_type>text</frontend_type>
|
115 |
+
<disabled>true</disabled>
|
116 |
+
<sort_order>90</sort_order>
|
117 |
+
<show_in_default>1</show_in_default>
|
118 |
+
<show_in_website>1</show_in_website>
|
119 |
+
<show_in_store>1</show_in_store>
|
120 |
+
<can_be_empty>1</can_be_empty>
|
121 |
+
</authorization_secret>
|
122 |
+
<generate_tokens_button translate="label">
|
123 |
+
<label></label>
|
124 |
+
<frontend_type>button</frontend_type>
|
125 |
+
<frontend_model>klaviyo_reclaim/adminhtml_system_config_form_button</frontend_model>
|
126 |
+
<sort_order>100</sort_order>
|
127 |
+
<show_in_default>1</show_in_default>
|
128 |
+
<show_in_website>1</show_in_website>
|
129 |
+
<show_in_store>1</show_in_store>
|
130 |
+
</generate_tokens_button>
|
131 |
</fields>
|
132 |
</general>
|
133 |
</groups>
|
134 |
</reclaim>
|
135 |
</sections>
|
136 |
+
</config>
|
app/design/frontend/base/default/layout/klaviyoreclaim.xml
CHANGED
@@ -40,4 +40,9 @@
|
|
40 |
<block type="klaviyo_reclaim/checkout_monitor" name="klaviyo_reclaim.checkout_monitor" template="klaviyoreclaim/checkout/monitor.phtml" />
|
41 |
</reference>
|
42 |
</fancycheckout_index_index>
|
43 |
-
|
|
|
|
|
|
|
|
|
|
40 |
<block type="klaviyo_reclaim/checkout_monitor" name="klaviyo_reclaim.checkout_monitor" template="klaviyoreclaim/checkout/monitor.phtml" />
|
41 |
</reference>
|
42 |
</fancycheckout_index_index>
|
43 |
+
<anattadesign_awesomecheckout_onepage_index>
|
44 |
+
<reference name="before_body_end">
|
45 |
+
<block type="klaviyo_reclaim/checkout_monitor" name="klaviyo_reclaim.checkout_monitor" template="klaviyoreclaim/checkout/monitor.phtml" />
|
46 |
+
</reference>
|
47 |
+
</anattadesign_awesomecheckout_onepage_index>
|
48 |
+
</layout>
|
app/design/frontend/base/default/template/klaviyoreclaim/tracking/product.phtml
CHANGED
@@ -5,19 +5,33 @@ $_product_image_url = NULL;
|
|
5 |
// Check to see if we have an image for this product.
|
6 |
foreach ($_product->getMediaGalleryImages() as $_product_image) {
|
7 |
if (!$_product_image->getDisabled()) {
|
8 |
-
|
9 |
-
|
10 |
}
|
11 |
}
|
12 |
?>
|
13 |
<script text="text/javascript">
|
14 |
var _learnq = _learnq || [];
|
15 |
-
|
16 |
-
|
17 |
-
|
|
|
18 |
SKU: "<?php echo $_product->getSku(); ?>",
|
19 |
-
|
20 |
-
|
|
|
|
|
21 |
Categories: <?php echo $this->getProductCategoriesAsJson(); ?>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
22 |
}]);
|
23 |
</script>
|
5 |
// Check to see if we have an image for this product.
|
6 |
foreach ($_product->getMediaGalleryImages() as $_product_image) {
|
7 |
if (!$_product_image->getDisabled()) {
|
8 |
+
$_product_image_url = $_product_image->getUrl();
|
9 |
+
break;
|
10 |
}
|
11 |
}
|
12 |
?>
|
13 |
<script text="text/javascript">
|
14 |
var _learnq = _learnq || [];
|
15 |
+
|
16 |
+
var item = {
|
17 |
+
ProductID: "<?php echo $_product->getId(); ?>",
|
18 |
+
Name: <?php echo json_encode($_product->getName()); ?>,
|
19 |
SKU: "<?php echo $_product->getSku(); ?>",
|
20 |
+
Price: "<?php echo number_format($_product->getPrice(), 2); ?>",
|
21 |
+
SpecialPrice: "<?php echo number_format($_product->getFinalPrice(), 2); ?>",
|
22 |
+
URL: "<?php echo $_product->getProductUrl(); ?>",
|
23 |
+
<?php if ($_product_image_url) { ?>ImageURL: "<?php echo $_product_image_url; ?>", <?php } ?>
|
24 |
Categories: <?php echo $this->getProductCategoriesAsJson(); ?>
|
25 |
+
};
|
26 |
+
|
27 |
+
_learnq.push(['track', 'Viewed Product', item]);
|
28 |
+
_learnq.push(['trackViewedItem', {
|
29 |
+
Title: item.Name,
|
30 |
+
ItemId: item.ProductID,
|
31 |
+
Price: item.Price,
|
32 |
+
SpecialPrice: item.SpecialPrice,
|
33 |
+
Categories: item.Categories,
|
34 |
+
<?php if ($_product_image_url) { ?>ImageUrl: item.ImageURL,<?php } ?>
|
35 |
+
Url: item.URL
|
36 |
}]);
|
37 |
</script>
|
package.xml
CHANGED
@@ -1,23 +1,27 @@
|
|
1 |
<?xml version="1.0"?>
|
2 |
<package>
|
3 |
<name>Klaviyo_Reclaim</name>
|
4 |
-
<version>1.
|
5 |
<stability>stable</stability>
|
6 |
<license>Open Software License (OSL)</license>
|
7 |
<channel>community</channel>
|
8 |
<extends/>
|
9 |
-
<summary>
|
10 |
<description>With this extension, you can:
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
|
|
|
|
|
|
|
|
17 |
<authors><author><name>Klaviyo Team</name><user>klaviyo</user><email>support@klaviyo.com</email></author></authors>
|
18 |
-
<date>
|
19 |
-
<time>
|
20 |
-
<contents><target name="magecommunity"><dir name="Klaviyo"><dir name="Reclaim"><dir name="Block"><dir name="Adminhtml"><dir name="System"><dir name="Config"><dir name="Fieldset"><file name="Info.php" hash="
|
21 |
<compatible/>
|
22 |
<dependencies><required><php><min>5.1.0</min><max>6.0.0</max></php></required></dependencies>
|
23 |
</package>
|
1 |
<?xml version="1.0"?>
|
2 |
<package>
|
3 |
<name>Klaviyo_Reclaim</name>
|
4 |
+
<version>1.6.2</version>
|
5 |
<stability>stable</stability>
|
6 |
<license>Open Software License (OSL)</license>
|
7 |
<channel>community</channel>
|
8 |
<extends/>
|
9 |
+
<summary>Officially supported Magento extension for the Klaviyo marketing platform.</summary>
|
10 |
<description>With this extension, you can:
|
11 |
+

|
12 |
+
- Import newsletter subscribers into a Klaviyo list.
|
13 |
+
- Track checkouts and send abandoned cart emails.
|
14 |
+
- Track website visitors and the products they view. This data is automatically fed into Klaviyo's analytics engine to build preference profiles for each visitor.
|
15 |
+
- Configure different settings per website or store if you manage multiple brands in one installation.
|
16 |
+

|
17 |
+
Klaviyo also syncs order, customer and product catalog data using Magento's built-in API. This extension supports Magento versions 1.5 and later.
|
18 |
+

|
19 |
+
For Magento 2 installations, download our extension at http://docs.klaviyo.com/.</description>
|
20 |
+
<notes>Adding support for configuring Klaviyo settings at the store and website level.</notes>
|
21 |
<authors><author><name>Klaviyo Team</name><user>klaviyo</user><email>support@klaviyo.com</email></author></authors>
|
22 |
+
<date>2017-04-07</date>
|
23 |
+
<time>13:57:38</time>
|
24 |
+
<contents><target name="magecommunity"><dir name="Klaviyo"><dir name="Reclaim"><dir name="Block"><dir name="Adminhtml"><dir name="System"><dir name="Config"><dir name="Fieldset"><file name="Info.php" hash="3e9e5c9393b4049bee342922faea77a3"/></dir><dir name="Form"><file name="Button.php" hash="9f864eccb15821b49aa8f401545ef7b9"/></dir></dir></dir></dir><dir name="Checkout"><file name="Monitor.php" hash="79992befa61467274ebbd280745d55ab"/></dir><dir name="Tracking"><file name="Default.php" hash="1ebce8bc5e151135e6b505b161a8a756"/><file name="Product.php" hash="905acae51c2e6a425b781a64dc3a3e8e"/></dir></dir><dir name="Helper"><file name="Data.php" hash="92b191f8ebc983f6a7996f2cf98e2fe3"/></dir><dir name="Model"><file name="Api.php" hash="f16169d8397df52ccf25b2f0aeaf2038"/><dir name="Api2"><dir name="Coupon"><dir name="Rest"><dir name="Admin"><file name="V1.php" hash="a449572e24a3458fec93d1682d7d9120"/></dir></dir></dir><file name="Coupon.php" hash="8d975ee135a6991dcb405011d3816f94"/><dir name="Rule"><dir name="Rest"><dir name="Admin"><file name="V1.php" hash="d736d601eaddf3971740374150c386f3"/></dir></dir></dir><file name="Rule.php" hash="fd04f68b9490499336ef8e0de28a74e3"/></dir><file name="Checkout.php" hash="482b26c7e4e882d15634e660415a4d65"/><file name="KlaviyoApi.php" hash="ead08d780556c0378120020e941709eb"/><dir name="Mysql4"><dir name="Checkout"><file name="Collection.php" hash="6c85e812ce3f3e4a341df4c7549a7e7f"/></dir><file name="Checkout.php" hash="8e3399cf5bf95f8aaf1e369ed95a0456"/></dir><file name="Observer.php" hash="d932a070b99f3bea5610a9e86d9b8aa8"/><file name="PublicApiKey.php" hash="dd78ea7d5ea0a25b0c8db2e7c7887a40"/><dir name="System"><dir name="Config"><dir name="Source"><file name="List.php" hash="44bb20b48631d4b193f46037fd9a5d91"/></dir></dir></dir><file name="Tracker.php" hash="0516ed9923d608a8d3b84636e1ba6bb2"/></dir><dir name="controllers"><file name="IndexController.php" hash="de82da454eb6906aad37a07b1b608a82"/><file name="KlaviyoController.php" hash="2c65e5f4101c1bb8a737de2390d51525"/></dir><dir name="etc"><file name="adminhtml.xml" hash="befc296020e0249d72b2a1076a48b45d"/><file name="api2.xml" hash="8a5e921eb5eddd993c9eddfe034a6abe"/><file name="config.xml" hash="2b2b95ea51df232065fb0444ee74c24b"/><file name="system.xml" hash="dd5614ba35fafbbec0de8d6e6bb3fd6d"/></dir><dir name="sql"><dir name="klaviyo_reclaim_setup"><file name="mysql4-install-1.0.5.php" hash="d6479ff37d54bd011270370c7c74e975"/></dir></dir></dir></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="base"><dir name="default"><dir name="layout"><file name="klaviyoreclaim.xml" hash="51b3a3d783921a4e455c141b4a18e7ec"/></dir><dir name="template"><dir name="klaviyoreclaim"><dir name="system"><dir name="config"><dir name="fieldset"><file name="info.phtml" hash="9b972d85ac2e4e5c6d0fada6708b3a4b"/></dir></dir></dir></dir></dir></dir></dir></dir><dir name="frontend"><dir name="base"><dir name="default"><dir name="layout"><file name="klaviyoreclaim.xml" hash="1523ed1271dae7967a7f125fd5a6897c"/></dir><dir name="template"><dir name="klaviyoreclaim"><dir name="checkout"><file name="monitor.phtml" hash="4ed26697dcc4a979345e7fc8835e033c"/></dir><dir name="tracking"><file name="default.phtml" hash="0985dab82f9290d3693298e51fd36745"/><file name="product.phtml" hash="987124910256fa6383e4ccc064c4a088"/></dir></dir></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Klaviyo_Reclaim.xml" hash="97fa1624141df9065e240fd12dd49f2c"/></dir></target><target name="mageskin"><dir name="adminhtml"><dir name="base"><dir name="default"><dir name="klaviyoreclaim"><file name="klaviyoreclaim.css" hash="c12f3281cac2d53b181dfcb7da53f278"/></dir></dir></dir></dir></target></contents>
|
25 |
<compatible/>
|
26 |
<dependencies><required><php><min>5.1.0</min><max>6.0.0</max></php></required></dependencies>
|
27 |
</package>
|