cartdefender_actions - Version 1.1.3

Version Notes

No-op refactors and minor bugfixes.

Download this release

Release Info

Developer Radoslaw Gliniecki
Extension cartdefender_actions
Version 1.1.3
Comparing to
See all releases


Code changes from version 1.1.1 to 1.1.3

app/code/community/CartDefender/Actions/Block/Script.php CHANGED
@@ -1,7 +1,9 @@
1
  <?php
2
 
 
 
3
  /**
4
- * Cart Defender Actions plugin for Magento
5
  *
6
  * @category design_default
7
  * @package CartDefender_Actions
@@ -9,93 +11,110 @@
9
  * @copyright Copyright (c) 2016 Heptium Ltd. (http://www.cartdefender.com/)
10
  * @license Open Software License
11
  */
12
- class CartDefender_Actions_Block_Script extends Mage_Core_Block_Template {
 
13
 
14
- public function _construct() {
15
- parent::_construct();
16
- Mage::helper('actions')->log("Script Block constructing");
17
- $api = Mage::helper('actions')->getApi();
18
- if (Mage::helper('actions')->enabled() == true && !empty($api)) {
19
- $this->setTemplate('actions/script.phtml');
20
- }
21
- }
22
 
23
- /**
24
- * Helper method to get configuration data
25
- */
26
- public function getSetting($key) {
27
- $data = Mage::helper('actions')->getSettings();
28
- return isset($data[$key]) ? $data[$key] : null;
29
- }
 
 
 
 
30
 
31
- /**
32
- * Helper method to get CartDefender JavaScript URL
33
- */
34
- public function getJavaScriptUrl() {
35
- return $this->getHost() . CartDefender_Actions_Helper_Data::CD_SCRIPT_PATH;
36
- }
37
-
38
- private function getHost() {
39
- return $this->getSetting('test')
40
- ? $this->getSetting('test_server_url_start')
41
- : CartDefender_Actions_Helper_Data::CD_HOST;
42
- }
43
-
44
- private function getCurrentQuoteData() {
45
- $quote = Mage::getSingleton('checkout/session')->getQuote();
46
- // get array of all items which can be displayed directly
47
- $itemsVisible = $quote->getAllVisibleItems();
48
- $cart_items = array();
49
- foreach ($itemsVisible as $item) {
50
- $item_data = $item->getData();
51
- $cart_items[] = $item_data;
52
  }
53
- //load data into array
54
- $quoteData = $quote->getData();
55
- $quoteData['itemsVisible'] = $cart_items;
56
- return $quoteData;
57
- }
58
-
59
- public function getVariables() {
60
- // "static" to compute only once and return cached value later.
61
- static $cdsvar_data;
62
 
63
- if (empty($cdsvar_data)) {
64
- $store = Mage::app()->getStore();
65
- $storeGroup = $store->getGroup();
66
- $storeId = $store->getId();
67
- $website = Mage::app()->getWebsite(); // Gets the current website details
68
- if (empty(CartDefender_Actions_Model_Observer::$correlation_id)) {
69
- Mage::helper('actions')->log("Script Block Setting correlation because it's empty");
70
- CartDefender_Actions_Model_Observer::setCorrelationId();
71
- }
72
 
73
- $cdsvar_data = array(
74
- 'website_url' => Mage::getStoreConfig('web/unsecure/base_url', 0),
75
- 'app_software_name' => 'Magento ' . Mage::getEdition(),
76
- 'app_software_version' => Mage::getVersion(),
77
- 'website_code' => $website->getCode(),
78
- 'website_name' => $website->getName(),
79
- 'website_default_shop_id' => $website->getDefaultGroupId(),
80
- 'website_is_default' => $website->getIsDefault(),
81
- 'shop_id' => $storeGroup->getGroupId(),
82
- 'shop_name' => $storeGroup->getName(),
83
- 'shop_root_category_id' => $storeGroup->getRootCategoryId(),
84
- 'shop_default_shop_view_id' => $storeGroup->getDefaultStoreId(),
85
- 'shop_view_id' => $store->getStoreId(),
86
- 'shop_view_code' => $store->getCode(),
87
- 'shop_view_name' => $store->getName(),
88
- 'shop_view_locale_code' => Mage::getStoreConfig('general/locale/code', $storeId),
89
- 'shop_view_url' => $store->getBaseUrl(),
90
- 'shop_view_home_url' => $store->getHomeUrl(),
91
- 'checkout_link' => Mage::helper('checkout/url')->getCheckoutUrl(),
92
- 'multishipping_checkout_link' => Mage::helper('checkout/url')->getMSCheckoutUrl(),
93
- 'request_route_name' => Mage::app()->getRequest()->getRouteName(),
94
- 'page_identifier' => Mage::getSingleton('cms/page')->getIdentifier()
95
- );
96
  }
97
- $serialized_data = Zend_Json::encode($cdsvar_data, true);
98
- return $serialized_data;
99
- }
100
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  }
1
  <?php
2
 
3
+ use CartDefender_Actions_Helper_Data as CDData;
4
+
5
  /**
6
+ * Cart Defender main Block.
7
  *
8
  * @category design_default
9
  * @package CartDefender_Actions
11
  * @copyright Copyright (c) 2016 Heptium Ltd. (http://www.cartdefender.com/)
12
  * @license Open Software License
13
  */
14
+ class CartDefender_Actions_Block_Script extends Mage_Core_Block_Template
15
+ {
16
 
17
+ /**
18
+ * @var CartDefender_Actions_Helper_Logger $logger Logger of Cart Defender
19
+ * specific messages.
20
+ */
21
+ private $logger;
 
 
 
22
 
23
+ /** Initializes this class, particularly the logger and its Template. */
24
+ public function _construct()
25
+ {
26
+ parent::_construct();
27
+ $this->logger = Mage::helper('actions/logger');
28
+ $this->logger->log('Script->_construct');
29
+ $api = Mage::helper('actions')->getApi();
30
+ if (Mage::helper('actions')->enabled() == true && !empty($api)) {
31
+ $this->setTemplate('actions/script.phtml');
32
+ }
33
+ }
34
 
35
+ /**
36
+ * Returns a named Cart Defender configuration setting value.
37
+ *
38
+ * @param string $key the name of the setting.
39
+ * @return string|null the value of the setting or null if key is invalid.
40
+ */
41
+ public function getSetting($key)
42
+ {
43
+ $data = Mage::helper('actions')->getSettings();
44
+ return isset($data[$key]) ? $data[$key] : null;
 
 
 
 
 
 
 
 
 
 
 
45
  }
 
 
 
 
 
 
 
 
 
46
 
47
+ /**
48
+ * Returns the CartDefender JavaScript URL.
49
+ *
50
+ * @return string the CartDefender JavaScript URL.
51
+ */
52
+ public function getJavaScriptUrl()
53
+ {
54
+ return $this->getHost() . CDData::CD_SCRIPT_PATH;
55
+ }
56
 
57
+ /**
58
+ * Returns the hostname of Cart Defender server.
59
+ *
60
+ * @return string the hostname of Cart Defender server.
61
+ */
62
+ private function getHost()
63
+ {
64
+ return $this->getSetting('test')
65
+ ? $this->getSetting('test_server_url_start') : CDData::CD_HOST;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  }
 
 
 
67
 
68
+ /**
69
+ * Returns a JSON-encoded list of values to be (invisibly) rendered
70
+ * on the page, for use of Cart Defender JavaScript.
71
+ *
72
+ * @return string JSON-encoded list of values of interest to our JS.
73
+ */
74
+ public function getVariables()
75
+ {
76
+ // "static" to compute only once and return cached value later.
77
+ static $cdsvarData = null;
78
+ if ($cdsvarData === null) {
79
+ $store = Mage::app()->getStore();
80
+ $storeGroup = $store->getGroup();
81
+ $storeId = $store->getId();
82
+ $website = Mage::app()->getWebsite();
83
+ Mage::getSingleton('actions/correlationIdManager')
84
+ ->ensureCorrelationIdSet();
85
+
86
+ $cdsvarData = array(
87
+ 'website_url' =>
88
+ Mage::getStoreConfig('web/unsecure/base_url', 0),
89
+ 'app_software_name' => 'Magento ' . Mage::getEdition(),
90
+ 'app_software_version' => Mage::getVersion(),
91
+ 'website_code' => $website->getCode(),
92
+ 'website_name' => $website->getName(),
93
+ 'website_default_shop_id' => $website->getDefaultGroupId(),
94
+ 'website_is_default' => $website->getIsDefault(),
95
+ 'shop_id' => $storeGroup->getGroupId(),
96
+ 'shop_name' => $storeGroup->getName(),
97
+ 'shop_root_category_id' => $storeGroup->getRootCategoryId(),
98
+ 'shop_default_shop_view_id' =>
99
+ $storeGroup->getDefaultStoreId(),
100
+ 'shop_view_id' => $store->getStoreId(),
101
+ 'shop_view_code' => $store->getCode(),
102
+ 'shop_view_name' => $store->getName(),
103
+ 'shop_view_locale_code' =>
104
+ Mage::getStoreConfig('general/locale/code', $storeId),
105
+ 'shop_view_url' => $store->getBaseUrl(),
106
+ 'shop_view_home_url' => $store->getHomeUrl(),
107
+ 'checkout_link' =>
108
+ Mage::helper('checkout/url')->getCheckoutUrl(),
109
+ 'multishipping_checkout_link' =>
110
+ Mage::helper('checkout/url')->getMSCheckoutUrl(),
111
+ 'request_route_name' =>
112
+ Mage::app()->getRequest()->getRouteName(),
113
+ 'page_identifier' =>
114
+ Mage::getSingleton('cms/page')->getIdentifier()
115
+ );
116
+ }
117
+ $serializedData = Zend_Json::encode($cdsvarData, true);
118
+ return $serializedData;
119
+ }
120
  }
app/code/community/CartDefender/Actions/Helper/Data.php CHANGED
@@ -1,114 +1,232 @@
1
  <?php
2
 
3
  /**
4
- * Cart Defender Actions plugin for Magento
5
  *
6
- * @category design_default
7
  * @package CartDefender_Actions
8
  * @author Heptium Ltd.
9
  * @copyright Copyright (c) 2016 Heptium Ltd. (http://www.cartdefender.com/)
10
  * @license Open Software License
11
  */
12
- class CartDefender_Actions_Helper_Data extends Mage_Core_Helper_Abstract {
13
-
14
- /**
15
- * Value for missing or unknown data.
16
- */
17
- const MISSING_VALUE = '(?MV?)';
18
-
19
- /**
20
- * The protocol & hostname of the Cart Defender server.
21
- */
22
- const CD_HOST = 'https://app.cartdefender.com';
23
-
24
- /*
25
- * The CD_PLUGIN_BIZ_API_ constants refer to the CartDefender biz event API for shop platform
26
- * plugins (e.g. WooCommerce, Magento, etc), as opposed to JSON REST API for custom shops.
27
- */
28
-
29
- /**
30
- * The start of the path part of the biz API URL.
31
- */
32
- const CD_PLUGIN_BIZ_API_PATH_START = '/plugin';
33
-
34
- /**
35
- * The version of the biz API.
36
- */
37
- const CD_PLUGIN_BIZ_API_VERSION = 'v1-beta';
38
-
39
- /**
40
- * The end of the path part of the biz API URL.
41
- */
42
- const CD_PLUGIN_BIZ_API_PATH_END = '/magentoBizEvent';
43
-
44
- /**
45
- * The path part of the URL to Cart Defender JavaScript file.
46
- */
47
- const CD_SCRIPT_PATH = '/script/cartdefender.js';
48
-
49
- public function getApi() {
50
- return Mage::getStoreConfig('actions/settings/api');
51
- }
52
-
53
- public function enabled() {
54
- return (bool) Mage::getStoreConfig('actions/settings/enabled');
55
- }
56
-
57
- private function test() {
58
- return (bool) Mage::getStoreConfig('actions/settings/test');
59
- }
60
-
61
- private function getTestServerUrlStart() {
62
- return Mage::getStoreConfig('actions/settings/test_server_url_start');
63
- }
64
-
65
- private function getUseRawTestUrlForBizApi() {
66
- return (bool) Mage::getStoreConfig('actions/settings/use_raw_test_url_for_biz_api');
67
- }
68
-
69
- private function getCurrentSendKeyValue() {
70
- return Mage::getStoreConfig('actions/settings/send_key');
71
- }
72
-
73
- /**
74
- * Obtains an internal shared key to access the controller which is sending the business events.
75
- */
76
- public function getSendKey() {
77
- $send_key = $this->getCurrentSendKeyValue();
78
- if (empty($send_key)) {
79
- $send_key = hexdec(bin2hex(openssl_random_pseudo_bytes(3)))
80
- . hexdec(bin2hex(openssl_random_pseudo_bytes(3)))
81
- . hexdec(bin2hex(openssl_random_pseudo_bytes(3)))
82
- . hexdec(bin2hex(openssl_random_pseudo_bytes(3)));
83
- Mage::getConfig()->saveConfig('actions/settings/send_key', $send_key, 'default', 0);
84
  }
85
- return $send_key;
86
- }
87
-
88
- public function getSettings() {
89
- // "static" to compute only once and return cached value later.
90
- static $data;
91
- if (empty($data)) {
92
- // Settings
93
- $data = array(
94
- 'api' => $this->getApi(),
95
- 'enabled' => $this->enabled(),
96
- 'test' => $this->test(),
97
- 'test_server_url_start' => $this->getTestServerUrlStart(),
98
- 'use_raw_test_url_for_biz_api' => $this->getUseRawTestUrlForBizApi(),
99
- 'send_key' => $this->getSendKey()
100
- );
101
  }
102
- return isset($data) ? $data : null;
103
- }
104
-
105
- /**
106
- * Logs a message if running in test mode.
107
- */
108
- public function log($message) {
109
- $settings = $this->getSettings();
110
- if (!empty($settings) && $settings['test']) {
111
- Mage::log($message);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  }
113
- }
114
  }
1
  <?php
2
 
3
  /**
4
+ * Cart Defender extension helper, with miscellaneous utility functions.
5
  *
 
6
  * @package CartDefender_Actions
7
  * @author Heptium Ltd.
8
  * @copyright Copyright (c) 2016 Heptium Ltd. (http://www.cartdefender.com/)
9
  * @license Open Software License
10
  */
11
+ class CartDefender_Actions_Helper_Data extends Mage_Core_Helper_Abstract
12
+ {
13
+
14
+ /**
15
+ * Value for missing or unknown data.
16
+ */
17
+ const MISSING_VALUE = '(?MV?)';
18
+
19
+ /**
20
+ * The protocol & hostname of the Cart Defender server.
21
+ */
22
+ const CD_HOST = 'https://app.cartdefender.com';
23
+
24
+ /*
25
+ * The CD_PLUGIN_BIZ_API_ constants refer to the CartDefender biz event
26
+ * API for shop platform plugins (e.g. WooCommerce, Magento, etc),
27
+ * as opposed to JSON REST API for custom shops.
28
+ */
29
+
30
+ /**
31
+ * The start of the path part of the biz API URL.
32
+ */
33
+ const CD_PLUGIN_BIZ_API_PATH_START = '/plugin';
34
+
35
+ /**
36
+ * The version of the biz API.
37
+ */
38
+ const CD_PLUGIN_BIZ_API_VERSION = 'v1-beta';
39
+
40
+ /**
41
+ * The end of the path part of the biz API URL.
42
+ */
43
+ const CD_PLUGIN_BIZ_API_PATH_END = '/magentoBizEvent';
44
+
45
+ /**
46
+ * The path part of the URL to Cart Defender JavaScript file.
47
+ */
48
+ const CD_SCRIPT_PATH = '/script/cartdefender.js';
49
+
50
+ /**
51
+ * The name of the start of session biz event.
52
+ */
53
+ const START_OF_SESSION = 'start_of_session';
54
+
55
+ /**
56
+ * Returns the API setting.
57
+ *
58
+ * @return string the API setting.
59
+ */
60
+ public function getApi()
61
+ {
62
+ return Mage::getStoreConfig('actions/settings/api');
63
+ }
64
+
65
+ /**
66
+ * Returns whether the Cart Defender extension is enabled.
67
+ *
68
+ * @return bool whether the Cart Defender extension is enabled.
69
+ */
70
+ public function enabled()
71
+ {
72
+ return (bool) Mage::getStoreConfig('actions/settings/enabled');
 
 
 
 
 
 
 
 
 
 
73
  }
74
+
75
+ /**
76
+ * Returns whether the Cart Defender extension is running in test mode.
77
+ *
78
+ * @return bool whether the Cart Defender extension is running in test mode.
79
+ */
80
+ private function test()
81
+ {
82
+ return (bool) Mage::getStoreConfig('actions/settings/test');
 
 
 
 
 
 
 
83
  }
84
+
85
+ /**
86
+ * For cases when Cart Defender extension is running in test mode, returns
87
+ * the test server URL protocol, domain, and path prefix (if any).
88
+ *
89
+ * @return string the test server URL protocol, domain,
90
+ * and path prefix (if any).
91
+ */
92
+ private function getTestServerUrlStart()
93
+ {
94
+ return Mage::getStoreConfig('actions/settings/test_server_url_start');
95
+ }
96
+
97
+ /**
98
+ * Returns whether the value returned by getTestServerUrlStart() is used
99
+ * exactly as provided for sending biz events - i.e. without appending
100
+ * things like "/plugin/[correlation id]/v1-beta/bizEvent". This is useful
101
+ * for inspecting biz event JSONs with something like RequestBin.
102
+ *
103
+ * @return bool whether the value returned by getTestServerUrlStart()
104
+ * is used exactly as provided for sending biz events.
105
+ */
106
+ private function getUseRawTestUrlForBizApi()
107
+ {
108
+ return (bool) Mage::getStoreConfig(
109
+ 'actions/settings/use_raw_test_url_for_biz_api'
110
+ );
111
+ }
112
+
113
+ /**
114
+ * Obtains an internal shared key to access the synchronous remote
115
+ * sender PHP script, used e.g. to send business events to Cart Defender
116
+ * servers.
117
+ *
118
+ * @return string shared key needed to access the remote sender script.
119
+ */
120
+ public function getSendKey()
121
+ {
122
+ $sendKey = Mage::getStoreConfig('actions/settings/send_key');
123
+ if (empty($sendKey)) {
124
+ $sendKey = hexdec(bin2hex(openssl_random_pseudo_bytes(3)))
125
+ . hexdec(bin2hex(openssl_random_pseudo_bytes(3)))
126
+ . hexdec(bin2hex(openssl_random_pseudo_bytes(3)))
127
+ . hexdec(bin2hex(openssl_random_pseudo_bytes(3)));
128
+ Mage::getConfig()->saveConfig(
129
+ 'actions/settings/send_key',
130
+ $sendKey,
131
+ 'default',
132
+ 0
133
+ );
134
+ }
135
+ return $sendKey;
136
+ }
137
+
138
+ /**
139
+ * Returns an array containing Cart Defender configuration settings.
140
+ *
141
+ * @return array Cart Defender configuration settings.
142
+ */
143
+ public function getSettings()
144
+ {
145
+ // "static" to compute only once and return cached value later.
146
+ static $data = null;
147
+ if ($data === null) {
148
+ // Settings
149
+ $data = array(
150
+ 'api' => $this->getApi(),
151
+ 'enabled' => $this->enabled(),
152
+ 'test' => $this->test(),
153
+ 'test_server_url_start' => $this->getTestServerUrlStart(),
154
+ 'use_raw_test_url_for_biz_api' =>
155
+ $this->getUseRawTestUrlForBizApi(),
156
+ 'send_key' => $this->getSendKey()
157
+ );
158
+ }
159
+ return $data;
160
+ }
161
+
162
+ /**
163
+ * Recursively converts to UTF-8 all the strings contained in an array,
164
+ * an object, or just a single string.
165
+ *
166
+ * @param mixed $input the variable whose string entries are
167
+ * to be converted to UTF-8. If it's not an array, object,
168
+ * or string, the function exits immediately and does nothing.
169
+ * @param array $objectsDone helper array storing what has already
170
+ * been seen in the recursion. Pass in an empty array when
171
+ * using this function.
172
+ * @return mixed input, but with string entries converted to UTF-8.
173
+ */
174
+ public function utf8ize($input, &$objectsDone)
175
+ {
176
+ if (!in_array($input, $objectsDone, true)) {
177
+ if (is_array($input)) {
178
+ $objectsDone[] = $input;
179
+ foreach ($input as $key => $value) {
180
+ $input[$key] = $this->utf8ize($value, $objectsDone);
181
+ }
182
+ } elseif (is_string($input)) {
183
+ if (!mb_detect_encoding($input, 'utf-8', true)) {
184
+ $input = utf8_encode($input);
185
+ return $input;
186
+ } else {
187
+ return $input;
188
+ }
189
+ } elseif (is_object($input)) {
190
+ $objectsDone[] = $input;
191
+ foreach ($input as $key => $value) {
192
+ $input->$key = $this->utf8ize($value, $objectsDone);
193
+ }
194
+ }
195
+ return $input;
196
+ } else {
197
+ return $input;
198
+ }
199
+ }
200
+
201
+ /**
202
+ * First call to this function in a given Magento session returns 0.
203
+ * Each following call returns the previous number incremened by 1.
204
+ *
205
+ * @return int the next sequence number in this Magento session.
206
+ */
207
+ public function getSequenceNo()
208
+ {
209
+ $session = Mage::getSingleton('core/session');
210
+ $number = $session->getSequenceNo() ? : 0;
211
+ $session->setSequenceNo($number + 1);
212
+ return $number;
213
+ }
214
+
215
+ /**
216
+ * Checks that:
217
+ * (1) Cart Defender extension is enabled,
218
+ * (2) this is an external request as opposed to a local server call,
219
+ * to avoid an endless loop,
220
+ * (3) we're not in the Admin area of the store.
221
+ *
222
+ * @return bool whether extension is enabled, and current request is
223
+ * non local & non admin.
224
+ */
225
+ public function isCDEnabledAndRequestNonLocalNonAdmin()
226
+ {
227
+ $settings = $this->getSettings();
228
+ return !Mage::app()->getRequest()->getPost('is_local_request')
229
+ && !Mage::app()->getStore()->isAdmin()
230
+ && $settings['enabled'];
231
  }
 
232
  }
app/code/community/CartDefender/Actions/Helper/Logger.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Cart Defender logging helper. Currently, our logging is enabled if
5
+ * the extension is running in test mode.
6
+ *
7
+ * @package CartDefender_Actions
8
+ * @author Heptium Ltd.
9
+ * @copyright Copyright (c) 2016 Heptium Ltd. (http://www.cartdefender.com/)
10
+ * @license Open Software License
11
+ */
12
+ class CartDefender_Actions_Helper_Logger extends Mage_Core_Helper_Abstract
13
+ {
14
+
15
+ /** @var mixed $enabled Whether Cart Defender specific logging is enabled. */
16
+ private $enabled = null;
17
+
18
+ /**
19
+ * Logs a message if Cart Defender logging is enabled.
20
+ *
21
+ * @param string $function the function name calling the logger.
22
+ * @param string $message the message to log.
23
+ * @return void
24
+ */
25
+ public function log($function, $message = '')
26
+ {
27
+ if ($this->enabled === null) {
28
+ $settings = Mage::helper('actions')->getSettings();
29
+ $this->enabled = ($settings ? $settings['test'] : null);
30
+ }
31
+ if ($this->enabled) {
32
+ Mage::log('[CD] [' . $function . '] ' . $message);
33
+ }
34
+ }
35
+ }
app/code/community/CartDefender/Actions/Model/AsyncLocalSender.php ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Asynchronous sender of text data to "synchronous remote sender" (another
5
+ * Cart Defender PHP script available on localhost), which then sends the
6
+ * data synchronously to Cart Defender servers.
7
+ *
8
+ * @package CartDefender_Actions
9
+ * @author Heptium Ltd.
10
+ * @copyright Copyright (c) 2016 Heptium Ltd. (http://www.cartdefender.com/)
11
+ * @license Open Software License
12
+ */
13
+ class CartDefender_Actions_Model_AsyncLocalSender extends Varien_Object
14
+ {
15
+
16
+ /**
17
+ * @var CartDefender_Actions_Helper_Logger $logger Logger of Cart Defender
18
+ * specific messages.
19
+ */
20
+ protected $logger;
21
+
22
+ /** Initializes this class, particularly the logger. */
23
+ public function _construct()
24
+ {
25
+ parent::_construct();
26
+ $this->logger = Mage::helper('actions/logger');
27
+ }
28
+
29
+ /**
30
+ * Asynchronously sends the data passed to "synchronous remote sender"
31
+ * (another Cart Defender PHP script available on localhost),
32
+ * which then sends the data synchronously to Cart Defender servers.
33
+ *
34
+ * See https://segment.com/blog/how-to-make-async-requests-in-php/
35
+ * for background on using fsockopen/pfsockopen. See however
36
+ * http://stackoverflow.com/questions/34769361 for the problem with
37
+ * pfsockopen. For this reason we use fsockopen, but to circumvent
38
+ * the fact that the connection opening is blocking, which can be
39
+ * very costly with remote servers, we connect to the same server
40
+ * (or group of servers) that the PHP process executing this runs on.
41
+ * Thus, we get blocking, but very fast connection opening, and then
42
+ * we send the request asynchronously, without waiting for response -
43
+ * using fclose() immediately.
44
+ *
45
+ * @param string $data the data to send.
46
+ * @param int $sequenceNo sequence number of the data being sent,
47
+ * used for logging purposes.
48
+ * @param string $correlationId the correlation identifier needed
49
+ * by Cart Defender backend.
50
+ * @return void
51
+ */
52
+ public function send($data, $sequenceNo, $correlationId)
53
+ {
54
+ $remoteSenderConf = $this->getRemoteSenderConf();
55
+ $request = $this->createRequest(
56
+ $remoteSenderConf['path'],
57
+ $remoteSenderConf['host'],
58
+ $data,
59
+ $sequenceNo,
60
+ $correlationId
61
+ );
62
+ $socket = fsockopen(
63
+ $remoteSenderConf['protocol'] . $remoteSenderConf['host'],
64
+ $remoteSenderConf['port'],
65
+ $errno,
66
+ $errstr,
67
+ 0.025 /*timeout*/
68
+ );
69
+ $success = fwrite($socket, $request);
70
+ Mage::getSingleton('core/session')->setLastEventTime(time());
71
+ $this->logger->log('AsyncLocalSender->send', ($success ? 'Success.'
72
+ : ('Error (num: [' . $errno . '], msg: [' . $errstr . '])'))
73
+ . ' Data: [' . $data . ']');
74
+ fclose($socket);
75
+ }
76
+
77
+ /**
78
+ * Retrieves the configuration of synchronous remote sender,
79
+ * specifically its URL parts.
80
+ *
81
+ * @return array the configuration of synchronous remote sender, with
82
+ * entries such as 'protocol', 'host', 'port', 'path'.
83
+ */
84
+ private function getRemoteSenderConf()
85
+ {
86
+ // "static" to compute only once and return cached value later.
87
+ static $remoteSenderConf = null;
88
+ if ($remoteSenderConf === null) {
89
+ $remoteSenderConf = parse_url(Mage::getUrl(
90
+ 'cartdefender/CartDefenderSender/send',
91
+ array('_secure' => true)
92
+ ));
93
+ if (empty($remoteSenderConf['port'])) {
94
+ $remoteSenderConf['port'] =
95
+ ($remoteSenderConf['scheme'] == 'http') ? 80 : 443;
96
+ }
97
+ $remoteSenderConf['protocol'] =
98
+ ($remoteSenderConf['scheme'] == 'http') ? 'tcp://' : 'ssl://';
99
+ }
100
+ return $remoteSenderConf;
101
+ }
102
+
103
+ /**
104
+ * Creates the text of a POST request (headers & contents)
105
+ * carrying some data, targeting the synchronous remote sender
106
+ * PHP script on localhost.
107
+ *
108
+ * Note that we can't use Magento's Varien_Http_Client to construct
109
+ * the request because it doesn't allow for extracting the request
110
+ * text without sending it too, synchronously.
111
+ *
112
+ * @param string $remoteSenderUrlPath the URL path
113
+ * of the synchronous remote sender.
114
+ * @param string $remoteSenderHost the hostname of localhost.
115
+ * We can't use "localhost" or "127.0.0.1" in case request
116
+ * is sent via SSL and the certificate doesn't include these two.
117
+ * @param string $data the data to send.
118
+ * @param int $sequenceNo sequence number of the data being sent,
119
+ * used for logging purposes.
120
+ * @param string $correlationId the correlation identifier
121
+ * needed by Cart Defender backend.
122
+ * @return string the text of a POST request, which can be passed to fwrite.
123
+ */
124
+ private function createRequest(
125
+ $remoteSenderUrlPath,
126
+ $remoteSenderHost,
127
+ $data,
128
+ $sequenceNo,
129
+ $correlationId
130
+ ) {
131
+ $settings = Mage::helper('actions')->getSettings();
132
+ $queryParams = array(
133
+ 'sequence_no' => $sequenceNo,
134
+ 'data' => $data,
135
+ 'is_local_request' => true, // PHP to PHP call within current server
136
+ 'correlation_id' => $correlationId,
137
+ 'send_key' => $settings['send_key']
138
+ );
139
+ $postdata = http_build_query($queryParams);
140
+
141
+ // Note that Carriage Return (\r) characters below are in accordance
142
+ // with RFC 2616. See http://stackoverflow.com/questions/5757290 Also,
143
+ // note that double quotes let \r & \n be interpreted correctly.
144
+
145
+ $request = "";
146
+ $request.= "POST " . $remoteSenderUrlPath . " HTTP/1.1\r\n";
147
+ $request.= "Host: " . $remoteSenderHost . "\r\n";
148
+ $request.= "Content-type: application/x-www-form-urlencoded\r\n";
149
+ $request.= "Content-length: " . strlen($postdata) . "\r\n";
150
+ $request.= "\r\n";
151
+ $request.= $postdata;
152
+ $request.= "\r\n\r\n";
153
+
154
+ return $request;
155
+ }
156
+ }
app/code/community/CartDefender/Actions/Model/CorrelationIdManager.php ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ use CartDefender_Actions_Helper_Data as CDData;
4
+
5
+ /**
6
+ * Cart Defender business logic for managing "correlation id", i.e. a number
7
+ * used by our backend servers to match events.
8
+ *
9
+ * @package CartDefender_Actions
10
+ * @author Heptium Ltd.
11
+ * @copyright Copyright (c) 2016 Heptium Ltd. (http://www.cartdefender.com/)
12
+ * @license Open Software License
13
+ */
14
+ class CartDefender_Actions_Model_CorrelationIdManager extends Varien_Object
15
+ {
16
+
17
+ /**
18
+ * The name of the cookie storing the CartDefender correlation id.
19
+ */
20
+ const CD_CORRELATION_COOKIE_NAME = "__cd_732655870348746856";
21
+
22
+ /**
23
+ * @var string|null $correlationId The CartDefender id used for correlating
24
+ * web and business events.
25
+ */
26
+ private $correlationId = null;
27
+
28
+ /**
29
+ * @var bool $responseStarted Whether the event
30
+ * "controller_front_send_response_before" was fired.
31
+ */
32
+ private $responseStarted = false;
33
+
34
+ /**
35
+ * @var CartDefender_Actions_Model_EventAsyncLocalSender $sender
36
+ * Asynchronous sender of data to "synchronous remote sender"
37
+ * (another Cart Defender PHP script available on localhost),
38
+ * which then sends the data synchronously to Cart Defender servers.
39
+ */
40
+ private $sender;
41
+
42
+ /**
43
+ * @var CartDefender_Actions_Helper_Logger $logger Logger of Cart Defender
44
+ * specific messages.
45
+ */
46
+ private $logger;
47
+
48
+ /** Initializes this class, particularly the utility objects it uses. */
49
+ public function _construct()
50
+ {
51
+ parent::_construct();
52
+ $this->sender = Mage::getSingleton('actions/eventAsyncLocalSender');
53
+ $this->logger = Mage::helper('actions/logger');
54
+ }
55
+
56
+ /**
57
+ * Returns the correlation id variable value.
58
+ *
59
+ * @return string|null the correlation id variable value.
60
+ */
61
+ public function getCorrelationId()
62
+ {
63
+ return $this->correlationId;
64
+ }
65
+
66
+ /**
67
+ * Indicates that the response to the browser has begun, therefore not
68
+ * permitting setting cookies anymore.
69
+ *
70
+ * @return void
71
+ */
72
+ public function setResponseStartedTrue()
73
+ {
74
+ $this->responseStarted = true;
75
+ }
76
+
77
+ /**
78
+ * If correlation id variable is not yet set for the current request,
79
+ * tries to set it.
80
+ *
81
+ * Setting it means either taking it from existing correlation id cookie,
82
+ * or if no such cookie exists and we can still create cookies
83
+ * (before response to the browser starts), trying to do that.
84
+ *
85
+ * @return string|null the correlation id (whether setting it was successful
86
+ * or not).
87
+ */
88
+ public function ensureCorrelationIdSet()
89
+ {
90
+ if (!Mage::helper('actions')->isCDEnabledAndRequestNonLocalNonAdmin()) {
91
+ return $this->correlationId;
92
+ }
93
+
94
+ $this->logger->log(
95
+ 'CorrelationIdManager->ensureCorrelationIdSet',
96
+ 'Response started: ' . ($this->responseStarted ? 'Y' : 'N')
97
+ );
98
+
99
+ if (($this->correlationId !== null) || $this->responseStarted) {
100
+ return $this->correlationId;
101
+ }
102
+
103
+ $cookie = Mage::getSingleton('core/cookie');
104
+ $corrCookie = $cookie->get(self::CD_CORRELATION_COOKIE_NAME);
105
+ $this->correlationId = $corrCookie ?: $this->generateCorrelationId();
106
+ if (!$corrCookie) {
107
+ $this->setCorrelationIdCookie();
108
+ // Notify CD servers of new correlation id.
109
+ $this->sender->sendEvent(
110
+ CDData::START_OF_SESSION,
111
+ $this->correlationId
112
+ );
113
+ }
114
+
115
+ $this->logger->log(
116
+ 'CorrelationIdManager->ensureCorrelationIdSet',
117
+ 'Correlation id [' . $this->correlationId . '] '
118
+ . ($corrCookie ? 'taken from' : 'created, set on new') . ' cookie.'
119
+ );
120
+
121
+ return $this->correlationId;
122
+ }
123
+
124
+ /**
125
+ * Generates a 64-bit random decimal string.
126
+ *
127
+ * @return string 64-bit random decimal string.
128
+ */
129
+ private function generateCorrelationId()
130
+ {
131
+ // Don't use 4 as param b/c hexdec() has a limit of 7fffffff.
132
+ return hexdec(bin2hex(openssl_random_pseudo_bytes(3)))
133
+ . hexdec(bin2hex(openssl_random_pseudo_bytes(3)))
134
+ . hexdec(bin2hex(openssl_random_pseudo_bytes(2)));
135
+ }
136
+
137
+ /**
138
+ * Sets a correlation id cookie on the HTTP response with the value
139
+ * of the correlation_id variable.
140
+ *
141
+ * @return void
142
+ */
143
+ private function setCorrelationIdCookie()
144
+ {
145
+ // This sets an HTTP response header. But if client browser has
146
+ // 1-st party cookies disabled, the value won't be saved and
147
+ // returned to PHP engine on next call. We'll try to regenerate it
148
+ // in the same way, again failing to make it permanent. However,
149
+ // the web sensor will detect cookies disabled in JS and not load
150
+ // itself. The eventual outcome will be no web events, and biz
151
+ // events with CD correlation ids varying with each PHP request.
152
+ // Also, the nulls set corresponding settings to shop defaults.
153
+ Mage::getSingleton('core/cookie')->set(
154
+ self::CD_CORRELATION_COOKIE_NAME,
155
+ $this->correlationId,
156
+ 0,
157
+ '/',
158
+ null /*domain*/,
159
+ null /*secure*/,
160
+ false /*HttpOnly*/
161
+ );
162
+ }
163
+ }
app/code/community/CartDefender/Actions/Model/EventAsyncLocalSender.php ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Specialization of AsyncLocalSender, for sending business events.
5
+ *
6
+ * @package CartDefender_Actions
7
+ * @author Heptium Ltd.
8
+ * @copyright Copyright (c) 2016 Heptium Ltd. (http://www.cartdefender.com/)
9
+ * @license Open Software License
10
+ */
11
+ class CartDefender_Actions_Model_EventAsyncLocalSender
12
+ extends CartDefender_Actions_Model_AsyncLocalSender
13
+ {
14
+
15
+ /**
16
+ * @var CartDefender_Actions_Model_EventBuilder $eventBuilder Builder of
17
+ * Cart Defender business events to be sent to Cart Defender servers.
18
+ */
19
+ private $eventBuilder;
20
+
21
+ /** Initializes this class, particularly the logger. */
22
+ public function _construct()
23
+ {
24
+ parent::_construct();
25
+ $this->eventBuilder = Mage::getSingleton('actions/eventBuilder');
26
+ }
27
+
28
+ /**
29
+ * Asynchronously sends a biz event to Cart Defender servers.
30
+ *
31
+ * @param string $eventName name of the event.
32
+ * @param string|null $correlationId id used for correlating
33
+ * web and business events.
34
+ * @param array $data event data.
35
+ * @return void
36
+ */
37
+ public function sendEvent($eventName, $correlationId, $data = array())
38
+ {
39
+ $sequenceNo = Mage::helper('actions')->getSequenceNo();
40
+ $event = $this->eventBuilder->buildEvent(
41
+ $eventName,
42
+ $data,
43
+ $sequenceNo
44
+ );
45
+ $this->send($event, $sequenceNo, $correlationId);
46
+ $this->logger->log(
47
+ 'EventAsyncLocalSender->sendEvent',
48
+ 'Sent event: ' . $eventName
49
+ );
50
+ }
51
+ }
app/code/community/CartDefender/Actions/Model/EventBuilder.php ADDED
@@ -0,0 +1,214 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ use CartDefender_Actions_Helper_Data as CDData;
4
+
5
+ /**
6
+ * Cart Defender business event builder.
7
+ *
8
+ * @package CartDefender_Actions
9
+ * @author Heptium Ltd.
10
+ * @copyright Copyright (c) 2016 Heptium Ltd. (http://www.cartdefender.com/)
11
+ * @license Open Software License
12
+ */
13
+ class CartDefender_Actions_Model_EventBuilder
14
+ {
15
+
16
+ /**
17
+ * Builds a Cart Defender business event in JSON-encoded form.
18
+ *
19
+ * @param string $eventName name of the event to create.
20
+ * @param array $observerData data coming from the event hook.
21
+ * @param int $eventNo sequence number used for logging.
22
+ * @return string the built event in JSON-encoded form.
23
+ */
24
+ public function buildEvent($eventName, $observerData, $eventNo)
25
+ {
26
+ $website = Mage::app()->getWebsite();
27
+ $store = Mage::app()->getStore();
28
+
29
+ $sessionData = $this->captureSessionData();
30
+ $cart = $this->captureCartData();
31
+ $fullOrders = $this->captureOrderData($observerData);
32
+ $visitorData = $sessionData['visitor_data'];
33
+ $event = array(
34
+ 'api' => Mage::helper('actions')->getApi(),
35
+ 'appSoftwareName' => 'Magento ' . Mage::getEdition(),
36
+ 'appSoftwareVersion' => Mage::getVersion(),
37
+ 'eventType' => $eventName,
38
+ 'timestamp' => time(),
39
+ 'shopCurrentCurrency' => $store->getCurrentCurrencyCode(),
40
+ 'cart' => $cart,
41
+ 'orders' => $fullOrders,
42
+ 'eventNumber' => $eventNo,
43
+ 'websiteId' => $website->getId(),
44
+ 'websiteCode' => $website->getCode(),
45
+ 'websiteName' => $website->getName(),
46
+ 'websiteData' => $website->getData(),
47
+ 'shopData' => $store->getGroup()->getData(),
48
+ 'shopViewData' => $store->getData(),
49
+ 'shopViewLocaleCode' => Mage::getStoreConfig(
50
+ 'general/locale/code',
51
+ $store->getStoreId()
52
+ ),
53
+ 'shopViewBaseUrl' => $store->getBaseUrl(),
54
+ 'shopViewHomeUrl' => $store->getHomeUrl(),
55
+ 'checkoutLink' => Mage::helper('checkout/url')->getCheckoutUrl(),
56
+ 'multishippingCheckoutLink' =>
57
+ Mage::helper('checkout/url')->getMSCheckoutUrl(),
58
+ 'visitorId' => isset($visitorData['visitor_id'])
59
+ ? $visitorData['visitor_id'] : CDData::MISSING_VALUE,
60
+ 'visitorData' => $visitorData,
61
+ 'isLoggedIn' => $sessionData['is_logged_in'],
62
+ 'customerId' => $sessionData['customer_id'],
63
+ 'customerData' => $sessionData['customer_data'],
64
+ 'previousBizEventLatency' => CDData::MISSING_VALUE
65
+ );
66
+ $done = array();
67
+ $utf8izedEvent = Mage::helper('actions')->utf8ize($event, $done);
68
+ return Zend_Json::encode($utf8izedEvent, true);
69
+ }
70
+
71
+ /**
72
+ * Returns various bits of data to be sent on each event,
73
+ * obtained from Magento session.
74
+ *
75
+ * @return array various bits of data to be sent on each event,
76
+ * obtained from Magento session.
77
+ */
78
+ private function captureSessionData()
79
+ {
80
+ if (!session_id()) {
81
+ return array(
82
+ 'visitor_data' => CDData::MISSING_VALUE,
83
+ 'customer_id' => CDData::MISSING_VALUE,
84
+ 'customer_data' => CDData::MISSING_VALUE,
85
+ 'is_logged_in' => CDData::MISSING_VALUE);
86
+ }
87
+
88
+ $sessionCore = Mage::getSingleton('core/session');
89
+ $sessionCustomer = Mage::getSingleton('customer/session');
90
+ return array(
91
+ 'visitor_data' => $sessionCore['visitor_data'],
92
+ 'customer_id' => isset($sessionCustomer)
93
+ ? $sessionCustomer->getCustomerId() : CDData::MISSING_VALUE,
94
+ 'customer_data' => isset($sessionCustomer)
95
+ ? $this->removePersonalData(
96
+ $sessionCustomer->getCustomer()->getData()
97
+ )
98
+ : CDData::MISSING_VALUE,
99
+ 'is_logged_in' => isset($sessionCustomer)
100
+ && $sessionCustomer->isLoggedIn());
101
+ }
102
+
103
+ /**
104
+ * Returns various bits of data to be sent on each event,
105
+ * obtained from the Magento cart.
106
+ *
107
+ * @return array various bits of data to be sent on each event,
108
+ * obtained from the Magento cart.
109
+ */
110
+ private function captureCartData()
111
+ {
112
+ return session_id()
113
+ ? $this->getCartFromQuote(
114
+ Mage::getSingleton('checkout/session')->getQuote()
115
+ )
116
+ : array(CDData::MISSING_VALUE);
117
+ }
118
+
119
+ /**
120
+ * Returns various bits of data to be sent on each event,
121
+ * obtained from the Magento cart.
122
+ *
123
+ * @param object $quote the Magento cart.
124
+ * @return array various bits of data to be sent on each event,
125
+ * obtained from the Magento cart.
126
+ */
127
+ private function getCartFromQuote($quote)
128
+ {
129
+ $cartItems = array();
130
+ $cartData = array();
131
+ if (isset($quote)) {
132
+ $cartData = $this->removePersonalData($quote->getData());
133
+ $items = $quote->getAllVisibleItems();
134
+ foreach ($items as $item) {
135
+ $itemData = $item->getData();
136
+ $cartItems[] = $itemData;
137
+ }
138
+ } else {
139
+ $quote = CDData::MISSING_VALUE;
140
+ $cartData[] = CDData::MISSING_VALUE;
141
+ $cartItems[] = CDData::MISSING_VALUE;
142
+ }
143
+ return array('cart_data' => $cartData, 'cart_items' => $cartItems);
144
+ }
145
+
146
+ /**
147
+ * Returns various bits of data to be sent on each event,
148
+ * obtained from the Magento orders.
149
+ *
150
+ * @param array $observerData data coming from the event hook.
151
+ * @return array various bits of data to be sent on each event,
152
+ * obtained from the Magento orders.
153
+ */
154
+ private function captureOrderData($observerData)
155
+ {
156
+ $fullOrders = array(CDData::MISSING_VALUE);
157
+ if (!session_id() || !isset($observerData['order_ids'])) {
158
+ return $fullOrders;
159
+ }
160
+
161
+ $orderIds = $observerData['order_ids'];
162
+ foreach ($orderIds as $orderId) {
163
+ $oneFullOrder = array();
164
+ $oneFullOrder['order_id'] = $orderId;
165
+
166
+ $order = Mage::getModel('sales/order')->load($orderId);
167
+ $orderData = $this->removePersonalData($order->getData());
168
+ $oneFullOrder['order_data'] = $orderData;
169
+
170
+ $quoteId = $orderData['quote_id'];
171
+ $cartFromOrder = Mage::getModel('sales/quote')->load($quoteId);
172
+ $oneFullOrder['cart'] = $this->getCartFromQuote($cartFromOrder);
173
+
174
+ $items = $order->getAllVisibleItems();
175
+ $orderItems = array();
176
+ foreach ($items as $item) {
177
+ $itemData = $item->getData();
178
+ $orderItems[] = $itemData;
179
+ }
180
+ $oneFullOrder['order_items'] = $orderItems;
181
+
182
+ $fullOrders[] = $oneFullOrder;
183
+ }
184
+ return $fullOrders;
185
+ }
186
+
187
+ /**
188
+ * Removes sensitive data from the input array and returns it.
189
+ *
190
+ * @param array $input some data.
191
+ * @return array $input with sensitive data removed.
192
+ */
193
+ private function removePersonalData($input)
194
+ {
195
+ unset($input['email']);
196
+ unset($input['prefix']);
197
+ unset($input['firstname']);
198
+ unset($input['middlename']);
199
+ unset($input['lastname']);
200
+ unset($input['suffix']);
201
+ unset($input['taxvat']);
202
+ unset($input['password_hash']);
203
+ unset($input['customer_tax_class_id']);
204
+ unset($input['customer_email']);
205
+ unset($input['customer_prefix']);
206
+ unset($input['customer_firstname']);
207
+ unset($input['customer_middlename']);
208
+ unset($input['customer_lastname']);
209
+ unset($input['customer_suffix']);
210
+ unset($input['customer_note']);
211
+ unset($input['customer_taxvat']);
212
+ return $input;
213
+ }
214
+ }
app/code/community/CartDefender/Actions/Model/Observer.php CHANGED
@@ -1,5 +1,7 @@
1
  <?php
2
 
 
 
3
  /**
4
  * Cart Defender business event capture for Magento
5
  *
@@ -8,492 +10,147 @@
8
  * @copyright Copyright (c) 2016 Heptium Ltd. (http://www.cartdefender.com/)
9
  * @license Open Software License
10
  */
11
- class CartDefender_Actions_Model_Observer extends Varien_Event_Observer {
12
-
13
- /**
14
- * Value for missing or unknown data taken from Helper class
15
- */
16
- private $MISSING_VALUE = CartDefender_Actions_Helper_Data::MISSING_VALUE;
17
-
18
- /**
19
- * Whether the event "controller_front_send_response_before" was fired.
20
- *
21
- * @since 0.0.1
22
- * @access private
23
- */
24
- private static $response_started;
25
-
26
- /**
27
- * The CartDefender id used for correlating web and business events.
28
- *
29
- * @since 0.0.1
30
- * @access public
31
- * @var string $correlation_id The CartDefender correlation id.
32
- */
33
- public static $correlation_id;
34
-
35
- /**
36
- * The name of the cookie storing the CartDefender correlation id.
37
- *
38
- * @since 0.0.1
39
- */
40
- const CD_CORRELATION_COOKIE_NAME = "__cd_732655870348746856";
41
-
42
- /**
43
- * Number of seconds in 5 minutes.
44
- *
45
- * @since 0.0.1
46
- */
47
- const FIVE_MINUTES = 300;
48
-
49
- /**
50
- * Threshold since last event was sent in seconds.
51
- *
52
- * @since 0.0.1
53
- */
54
- const EVENT_TIME_THRESHOLD = 7200;
55
-
56
- /**
57
- * Key used to store on the session the time required to send the last biz event to our servers.
58
- */
59
- const BIZ_EVENT_LATENCY_KEY = 'biz_event_latency_key';
60
-
61
- /**
62
- * Key used to store on the session the errors occurring during the sending of biz events.
63
- */
64
- const BIZ_EVENT_SENDING_ERRORS_KEY = 'biz_event_sending_errors_key';
65
-
66
- /**
67
- * Key used to store on the session the latest sequence number of a biz event.
68
- */
69
- const BIZ_EVENT_SEQUENCE_NO_KEY = 'biz_event_sequence_no_key';
70
- const START_OF_SESSION = 'start_of_session';
71
-
72
- private function connectionConfigSetup() {
73
- // "static" to compute only once and return cached value later.
74
- static $async_sender_api;
75
- static $connection_scheme;
76
- if (empty($async_sender_api) or empty($connection_scheme)) {
77
- $async_sender_api = parse_url(Mage::getUrl('cartdefender/CartDefenderSender/sendEvents',
78
- array('_secure' => true)));
79
- if (empty($async_sender_api['port'])) {
80
- if ($async_sender_api['scheme'] == 'http') {
81
- $async_sender_api['port'] = 80;
82
- }
83
- if ($async_sender_api['scheme'] == 'https') {
84
- $async_sender_api['port'] = 443;
85
- }
86
- }
87
- $connection_scheme = ($async_sender_api['scheme'] == 'http') ? 'tcp://' : 'ssl://';
88
- }
89
- return array(
90
- 'api' => $async_sender_api,
91
- 'scheme' => $connection_scheme);
92
- }
93
-
94
- private function captureSessionData(&$sessionId, &$visitorData, &$customer_id, &$customerData,
95
- &$is_logged_in) {
96
- if (!session_id()) {
97
- $sessionId = $this->MISSING_VALUE;
98
- $visitorData = $this->MISSING_VALUE;
99
- $customer_id = $this->MISSING_VALUE;
100
- $customerData = $this->MISSING_VALUE;
101
- $is_logged_in = $this->MISSING_VALUE;
102
- $sessionCore = $this->MISSING_VALUE;
103
- $sessionCustomer = $this->MISSING_VALUE;
104
- } else {
105
- $sessionCore = Mage::getSingleton("core/session");
106
- $sessionCustomer = Mage::getSingleton('customer/session');
107
- $visitorData = $sessionCore['visitor_data'];
108
- $sessionId = $sessionCore->getEncryptedSessionId();
109
-
110
- if (!isset($sessionCustomer)) {
111
- $sessionCustomer = $this->MISSING_VALUE;
112
- $customerData = $this->MISSING_VALUE;
113
- $customer_id = $this->MISSING_VALUE;
114
- $is_logged_in = false;
115
- } else {
116
- $customer_id = $sessionCustomer->getCustomerId();
117
- $customerData = $sessionCustomer->getCustomer()->getData();
118
- /* Remove personal details */
119
- unset($customerData['email']);
120
- unset($customerData['prefix']);
121
- unset($customerData['firstname']);
122
- unset($customerData['middlename']);
123
- unset($customerData['lastname']);
124
- unset($customerData['suffix']);
125
- unset($customerData['taxvat']);
126
- unset($customerData['password_hash']);
127
-
128
- $is_logged_in = $sessionCustomer->isLoggedIn();
129
- }
130
  }
131
- }
132
-
133
- private function obtainCartFromQuote($quote) {
134
- $cart_items = array();
135
- $cart_data = array();
136
- if (isset($quote)) {
137
- $cart_data = $quote->getData();
138
- /* Remove personal details */
139
- unset($cart_data['customer_tax_class_id']);
140
- unset($cart_data['customer_email']);
141
- unset($cart_data['customer_prefix']);
142
- unset($cart_data['customer_firstname']);
143
- unset($cart_data['customer_middlename']);
144
- unset($cart_data['customer_lastname']);
145
- unset($cart_data['customer_suffix']);
146
- unset($cart_data['customer_note']);
147
- unset($cart_data['customer_taxvat']);
148
- unset($cart_data['password_hash']);
149
-
150
- $items = $quote->getAllVisibleItems();
151
- foreach ($items as $item) {
152
- $item_data = $item->getData();
153
- $cart_items[] = $item_data;
154
- }
155
- } else {
156
- $quote = $this->MISSING_VALUE;
157
- $cart_data[] = $this->MISSING_VALUE;
158
- $cart_items[] = $this->MISSING_VALUE;
159
- }
160
- return array('cart_data' => $cart_data, 'cart_items' => $cart_items);
161
- }
162
-
163
- private function captureCartData() {
164
- return session_id()
165
- ? $this->obtainCartFromQuote(Mage::getSingleton('checkout/session')->getQuote())
166
- : array($this->MISSING_VALUE);
167
- }
168
-
169
- private function captureOrderData($observer_data) {
170
- $full_orders = array($this->MISSING_VALUE);
171
- if (session_id() && isset($observer_data['order_ids'])) {
172
- $orderIds = $observer_data['order_ids'];
173
- foreach ($orderIds as $_orderId) {
174
- $one_full_order = array(); //init empty var
175
- $one_full_order['order_id'] = $_orderId;
176
 
177
- $order = Mage::getModel('sales/order')->load($_orderId);
178
- $order_data = $order->getData();
179
-
180
- /* Remove personal details */
181
- unset($order_data['customer_tax_class_id']);
182
- unset($order_data['customer_email']);
183
- unset($order_data['customer_prefix']);
184
- unset($order_data['customer_firstname']);
185
- unset($order_data['customer_middlename']);
186
- unset($order_data['customer_lastname']);
187
- unset($order_data['customer_suffix']);
188
- unset($order_data['customer_note']);
189
- unset($order_data['customer_taxvat']);
190
- unset($order_data['password_hash']);
191
-
192
- $one_full_order['order_data'] = $order_data;
193
-
194
- $quote_id = $order_data['quote_id'];
195
- $cart_from_order = Mage::getModel('sales/quote')->load($quote_id);
196
- $one_full_order['cart'] = $this->obtainCartFromQuote($cart_from_order);
197
-
198
- $items = $order->getAllVisibleItems();
199
- $order_items = array(); //init empty var
200
- foreach ($items as $item) {
201
- $item_data = $item->getData();
202
- $order_items[] = $item_data;
203
  }
204
- $one_full_order['order_items'] = $order_items;
205
 
206
- $full_orders[] = $one_full_order;
207
- }
208
- }
209
- return $full_orders;
210
- }
211
 
212
- private function utf8ize($mixed, &$objects_done) {
213
- if (!in_array($mixed, $objects_done, true)) {
214
- if (is_array($mixed)) {
215
- $objects_done[] = $mixed;
216
- foreach ($mixed as $key => $value) {
217
- $mixed[$key] = $this->utf8ize($value, $objects_done);
218
  }
219
- } else if (is_string($mixed)) {
220
- if (!mb_detect_encoding($mixed, 'utf-8', true)) {
221
- $mixed = utf8_encode($mixed);
222
- return $mixed;
223
- } else {
224
- return $mixed;
225
- }
226
- } else if (is_object($mixed)) {
227
- $objects_done[] = $mixed;
228
- foreach ($mixed as $key => $value) {
229
- $mixed->$key = $this->utf8ize($value, $objects_done);
230
- }
231
- }
232
- return $mixed;
233
- } else {
234
- return $mixed;
235
  }
236
- }
237
-
238
- private function prepareBizEventData($event_name, $observer_data, $async_sender_api, $event_no) {
239
- $website = Mage::app()->getWebsite(); // Gets the current website details
240
- $store = Mage::app()->getStore(); // Gets the current store's details
241
- $storeId = $store->getStoreId();
242
- $storeGroup = $store->getGroup();
243
-
244
- $app_software_name = "Magento " . Mage::getEdition();
245
- $app_software_version = Mage::getVersion();
246
- $this->captureSessionData($sessionId, $visitorData, $customer_id, $customerData, $is_logged_in);
247
- $cart = $this->captureCartData();
248
- $full_orders = $this->captureOrderData($observer_data);
249
- $event = array(
250
- 'api' => Mage::helper('actions')->getApi(),
251
- 'appSoftwareName' => $app_software_name,
252
- 'appSoftwareVersion' => $app_software_version,
253
- 'eventType' => $event_name,
254
- 'timestamp' => time(),
255
- 'shop_current_currency' => $store->getCurrentCurrencyCode(),
256
- 'cart' => $cart,
257
- 'orders' => $full_orders,
258
- 'eventNumber' => $event_no,
259
- 'website_id' => $website->getId(),
260
- 'website_code' => $website->getCode(),
261
- 'website_name' => $website->getName(),
262
- 'websiteData' => $website->getData(),
263
- 'shopData' => $storeGroup->getData(),
264
- 'shopViewData' => $store->getData(),
265
- 'shopViewLocaleCode' => Mage::getStoreConfig('general/locale/code', $storeId),
266
- 'shopViewBaseUrl' => $store->getBaseUrl(),
267
- 'shopViewHomeUrl' => $store->getHomeUrl(),
268
- 'checkout_link' => Mage::helper('checkout/url')->getCheckoutUrl(),
269
- 'multishipping_checkout_link' => Mage::helper('checkout/url')->getMSCheckoutUrl(),
270
- 'correlationId' => self::$correlation_id,
271
- 'visitorId' => isset($visitorData['visitor_id'])
272
- ? $visitorData['visitor_id'] : $this->MISSING_VALUE,
273
- 'visitorData' => $visitorData,
274
- 'isLoggedIn' => $is_logged_in,
275
- 'customerId' => $customer_id,
276
- 'customerData' => $customerData,
277
- 'previousBizEventLatency' => $this->MISSING_VALUE
278
- );
279
- $objects_done = array();
280
- return Zend_Json::encode($this->utf8ize($event, $objects_done), true);
281
- }
282
 
283
- private function shouldSendSessionState() {
284
- if (session_id()) {
285
- $now = time();
286
- $last_event_at = Mage::getSingleton('core/session')->getLastEventTime();
287
- $result = empty($last_event_at) || (($now - $last_event_at) > self::EVENT_TIME_THRESHOLD);
288
-
289
- Mage::helper('actions')->log(" -->Last event time: "
290
- . (empty($last_event_at) ? "never" : $last_event_at));
291
- Mage::helper('actions')->log("Should send session state result: " . $result
292
- . " Time difference: " . (empty($last_event_at) ? "N/A" : ($now - $last_event_at)));
293
- return $result;
294
- }
295
- return false;
296
- }
297
-
298
- /**
299
- * Main event observer function. See ../etc/config.xml for observer configuration.
300
- *
301
- * List of events: http://www.nicksays.co.uk/magento-events-cheat-sheet-1-7/
302
- */
303
- public function captureEvent($observer) {
304
- if (self::isCartDefenderEnabledAndIsRequestNonLocalAndIsRequestNonAdmin()) {
305
- if (!isset(self::$response_started) && !isset(self::$correlation_id)) {
306
- Mage::helper('actions')->log("Capture Event - Response not started yet, and no correlation "
307
- . "ID so we set correlation ID - Event: " . $observer->getEvent()->getName());
308
- self::setCorrelationId();
309
- }
310
- if (isset(self::$correlation_id)) { //do not send an event without correlation id
311
- if ($this->shouldSendSessionState()) {
312
- $this->sendStartOfSessionState();
313
- Mage::helper('actions')->log("Capture Event - Sent Session State Update. Event: "
314
- . $observer->getEvent()->getName());
315
  }
316
- $event_name = $observer->getEvent()->getName();
317
- $observer_data = $observer->getData();
318
- $this->sendEvent($event_name, $observer_data);
319
- Mage::helper('actions')->log("Capture Event - Success - got the event name: ". $event_name);
320
- }
321
- }
322
- }
323
 
324
- /**
325
- * Event observer function which should be called whenever a customer logs in.
326
- * See ../etc/config.xml for observer configuration.
327
- */
328
- public function handleCustomerLogin($observer) {
329
- // Note that the customer_login event carries the pre-login cart.
330
- $this->captureEvent($observer);
331
- // This isn't cleared on login automatically even though Magento "frontend" cookie changes.
332
- Mage::getSingleton('core/session')->unsLastEventTime();
333
- }
334
-
335
- /**
336
- * Event observer function which should be called whenever a customer logs out.
337
- * See ../etc/config.xml for observer configuration.
338
- */
339
- public function handleCustomerLogout($observer) {
340
- $this->captureEvent($observer);
341
- // This isn't cleared on logout automatically even though Magento "frontend" cookie changes.
342
- Mage::getSingleton('core/session')->unsLastEventTime();
343
- }
344
-
345
- /**
346
- * Creates the text of a POST request (headers & contents) carrying a biz event, targeting
347
- * the biz event sender script on localhost.
348
- *
349
- * @param string $biz_event_sender_url_path the URL path of the event sender
350
- * @param string $biz_event_sender_host the hostname of localhost. We can't use "localhost" or
351
- * "127.0.0.1" in case request is sent via SSL and the certificate doesn't include these two.
352
- * @param int $event_no sequence number of the event, used for logging purposes.
353
- * @param string $data_string the biz event data to send.
354
- * @param bool $is_local_request is the request local for the same server ("PHP to PHP")
355
- * @param string $correlation_id the correlation identifier for events
356
- */
357
- private function createBizEventRequest($biz_event_sender_url_path, $biz_event_sender_host,
358
- $event_no, $data_string, $is_local_request, $correlation_id) {
359
- $settings = Mage::helper('actions')->getSettings();
360
- $query_params = array(
361
- 'event_no' => $event_no,
362
- 'data' => $data_string,
363
- 'is_local_request' => $is_local_request,
364
- 'correlation_id' => $correlation_id,
365
- 'send_key' => $settings['send_key']
366
- );
367
- $postdata = http_build_query($query_params);
368
 
369
- $req = "";
370
- $req.= "POST " . $biz_event_sender_url_path . " HTTP/1.1\r\n";
371
- $req.= "Host: " . $biz_event_sender_host . "\r\n";
372
- $req.= "Content-type: application/x-www-form-urlencoded\r\n";
373
- $req.= "Content-length: " . strlen($postdata) . "\r\n";
374
- $req.= "\r\n";
375
- $req.= $postdata;
376
- $req.= "\r\n\r\n";
377
 
378
- return $req;
379
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
380
 
381
- /**
382
- * If not set yet, sets a 64-bit Cart Defender correlation id for the current session.
383
- * The value is either taken from a cookie, or generated and put on a new cookie,
384
- * which we then try to store.
385
- *
386
- * NB: Cookie must be set before any content is sent to user's browser during a
387
- * given PHP request processing. Call this using a very early hook.
388
- * Mage::getModel(‘core/cookie’)->set($name, $value, $period);
389
- */
390
- public static function setCorrelationId() {
391
- if (self::isCartDefenderEnabledAndIsRequestNonLocalAndIsRequestNonAdmin()) {
392
- if (!isset(self::$correlation_id)) {
393
- Mage::helper('actions')->log("--->>>> Correlation variable not set, checking cookie.");
394
- $cookie = Mage::getSingleton('core/cookie');
395
- if (method_exists($cookie, 'get')) {
396
- $correlation_cookie = $cookie->get(self::CD_CORRELATION_COOKIE_NAME);
397
- if (!empty($correlation_cookie)) {
398
- self::$correlation_id = $correlation_cookie;
399
- Mage::helper('actions')->log("--->>>> Taking id from cookie: " . self::$correlation_id);
400
- } else {
401
- // Don't use 4 as param b/c hexdec() has a limit of 7fffffff.
402
- self::$correlation_id = hexdec(bin2hex(openssl_random_pseudo_bytes(3)))
403
- . hexdec(bin2hex(openssl_random_pseudo_bytes(3)))
404
- . hexdec(bin2hex(openssl_random_pseudo_bytes(2)));
405
- /* This sets an HTTP response header. But if client browser has 1-st party cookies
406
- disabled, the value won't be saved and returned to PHP engine on next call. We'll
407
- try to regenerate it in the same way, again failing to make it permanent. However,
408
- the web sensor will detect cookies disabled in JS and not load itself. The
409
- eventual outcome will be no web events, and biz events with CD correlation ids varying
410
- with each PHP request. Also, the nulls set corresponding settings to shop defaults. */
411
- Mage::getSingleton('core/cookie')->set(self::CD_CORRELATION_COOKIE_NAME,
412
- self::$correlation_id, 0, '/', null /* domain */,
413
- null /* secure */, false /* HttpOnly */);
414
- Mage::helper('actions')->log("--->>>> Cookie was empty, now: " . self::$correlation_id);
415
- }
416
- } else {
417
- Mage::helper('actions')->log("--->>>> Could not get the correlation cookie from the "
418
- . "browser. Server variable is: " . self::$correlation_id);
419
- }
420
- }
421
  }
422
- }
423
 
424
- /**
425
- * Event observer function which should be called whenever PHP is about to start sending a
426
- * response to the browser. See ../etc/config.xml for observer configuration.
427
- */
428
- public function ensureCookieSetBeforeResponse($observer) {
429
- if (self::isCartDefenderEnabledAndIsRequestNonLocalAndIsRequestNonAdmin()) {
430
- if (empty(self::$response_started)) {
431
- self::$response_started = true;
432
- self::setCorrelationId();
433
- if ($this->shouldSendSessionState()) { //check if should send start of session
434
- $this->sendStartOfSessionState();
435
- Mage::helper('actions')->log("Ensure cookie set - Sent Session State Update. Event: "
436
- . $observer->getEvent()->getName());
437
  }
438
- Mage::helper('actions')->log("Ensure cookie set - Response has not started yet - Event: "
439
- . $observer->getEvent()->getName() . " Correlation ID: " . self::$correlation_id);
440
- } else {
441
- Mage::helper('actions')->log("Ensure cookie set - Response started, don't set id - Event: "
442
- . $observer->getEvent()->getName() . " Correlation ID: " . $self::$correlation_id);
443
- }
444
- } else {
445
- Mage::helper('actions')->log("Ensure cookie set - local/admin/disabled, don't set id - Event:"
446
- . $observer->getEvent()->getName() . " Correlation ID: " . self::$correlation_id
447
- . " is Admin: " . Mage::app()->getStore()->isAdmin());
448
- }
449
- }
450
 
451
- private function sendStartOfSessionState() {
452
- Mage::helper('actions')->log("--->>>> Before Start of Session State. " . time());
453
- if (isset(self::$correlation_id)) { //do not send an event without correlation id
454
- $event_name = self::START_OF_SESSION;
455
- $observer_data = array();
456
- $this->sendEvent($event_name, $observer_data);
457
- Mage::helper('actions')->log("Send Start of session state - Success");
458
- }
459
- }
460
 
461
- private function sendEvent($event_name, $observer_data) {
462
- //setting the local request flag, which denotes PHP to PHP call within current server
463
- $is_local_request = true;
464
- $connect = $this->connectionConfigSetup();
465
- $async_sender_api = $connect['api'];
466
- $connection_scheme = $connect['scheme'];
467
- $event_no = Mage::getSingleton('core/session')->getEventNo();
468
- $event_no = empty($event_no) ? 0 : $event_no;
469
- Mage::getSingleton('core/session')->setEventNo($event_no + 1);
470
- $data_string =
471
- $this->prepareBizEventData($event_name, $observer_data, $async_sender_api, $event_no);
472
- $req = $this->createBizEventRequest($async_sender_api['path'], $async_sender_api['host'],
473
- $event_no, $data_string, $is_local_request, self::$correlation_id);
474
- $socket = @fsockopen($connection_scheme . $async_sender_api['host'], $async_sender_api['port'],
475
- $errno, $errstr, 0.025); //25ms timeout
476
- $success = @fwrite($socket, $req);
477
- Mage::getSingleton('core/session')->setLastEventTime(time());
478
- if ($success) {
479
- Mage::helper('actions')->log("[Send Event] - Success - sent the full event:" . $data_string);
480
- } else {
481
- Mage::helper('actions')->log("[Send Event] - ERROR - number: " . $errno
482
- . 'Error string: ' . $errstr
483
- . " the full event:" . $data_string);
484
  }
485
- $success = @fclose($socket);
486
- }
487
-
488
- /**
489
- * Checks if this is an external request as opposed to a local server call to avoid an
490
- * endless loop. Also verify that it's not Admin area of the store and that module is enabled.
491
- */
492
- private static function isCartDefenderEnabledAndIsRequestNonLocalAndIsRequestNonAdmin() {
493
- $settings = Mage::helper('actions')->getSettings();
494
- return empty($_POST['is_local_request'])
495
- && !Mage::app()->getStore()->isAdmin()
496
- && $settings['enabled'];
497
- }
498
-
499
  }
1
  <?php
2
 
3
+ use CartDefender_Actions_Helper_Data as CDData;
4
+
5
  /**
6
  * Cart Defender business event capture for Magento
7
  *
10
  * @copyright Copyright (c) 2016 Heptium Ltd. (http://www.cartdefender.com/)
11
  * @license Open Software License
12
  */
13
+ class CartDefender_Actions_Model_Observer extends Varien_Event_Observer
14
+ {
15
+
16
+ /**
17
+ * Threshold since last event was sent in seconds.
18
+ */
19
+ const EVENT_TIME_THRESHOLD = 7200;
20
+
21
+ /**
22
+ * @var CartDefender_Actions_Model_CorrelationIdManager
23
+ * $correlationIdMgr Container for business logic related
24
+ * to correlation id, used to match business events and web events.
25
+ */
26
+ private $correlationIdMgr;
27
+
28
+ /**
29
+ * @var CartDefender_Actions_Model_EventAsyncLocalSender $sender
30
+ * Asynchronous sender of data to "synchronous remote sender"
31
+ * (another Cart Defender PHP script available on localhost),
32
+ * which then sends the data synchronously to Cart Defender servers.
33
+ */
34
+ private $sender;
35
+
36
+ /**
37
+ * @var CartDefender_Actions_Helper_Logger $logger Logger of Cart Defender
38
+ * specific messages.
39
+ */
40
+ private $logger;
41
+
42
+ /** Initializes this class, particularly the utility objects it uses. */
43
+ public function _construct()
44
+ {
45
+ parent::_construct();
46
+ $this->correlationIdMgr =
47
+ Mage::getSingleton('actions/correlationIdManager');
48
+ $this->sender = Mage::getSingleton('actions/eventAsyncLocalSender');
49
+ $this->logger = Mage::helper('actions/logger');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
 
52
+ /**
53
+ * Main event observer function.
54
+ *
55
+ * List of events: http://www.nicksays.co.uk/magento-events-cheat-sheet-1-7/
56
+ *
57
+ * @param object $observer event data.
58
+ * @return void
59
+ */
60
+ public function captureEvent($observer)
61
+ {
62
+ if (!Mage::helper('actions')->isCDEnabledAndRequestNonLocalNonAdmin()) {
63
+ return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  }
65
+ $this->logger->log('Observer->captureEvent');
66
 
67
+ $corrId = $this->correlationIdMgr->ensureCorrelationIdSet();
68
+ if ($corrId !== null) {
69
+ if ($this->isLongSinceLastEvent()) {
70
+ $this->sender->sendEvent(CDData::START_OF_SESSION, $corrId);
71
+ }
72
 
73
+ $eventName = $observer->getEvent()->getName();
74
+ $eventData = $observer->getData();
75
+ $this->sender->sendEvent($eventName, $corrId, $eventData);
 
 
 
76
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
 
79
+ /**
80
+ * Event observer function for event "controller_front_send_response_before"
81
+ *
82
+ * @param object $observer event data.
83
+ * @return void
84
+ */
85
+ public function handleControllerFrontSendResponseBefore($observer)
86
+ {
87
+ if (!Mage::helper('actions')->isCDEnabledAndRequestNonLocalNonAdmin()) {
88
+ return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  }
90
+ $this->logger->log('Observer->handleControllerFrontSendResponseBefore');
 
 
 
 
 
 
91
 
92
+ $corrId = $this->correlationIdMgr->ensureCorrelationIdSet();
93
+ if ($corrId !== null) {
94
+ if ($this->isLongSinceLastEvent()) {
95
+ $this->sender->sendEvent(CDData::START_OF_SESSION, $corrId);
96
+ }
97
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
 
99
+ $this->correlationIdMgr->setResponseStartedTrue();
100
+ }
 
 
 
 
 
 
101
 
102
+ /**
103
+ * Event observer function for event "customer_login"
104
+ * (when a customer logs in.)
105
+ *
106
+ * @param object $observer event data.
107
+ * @return void
108
+ */
109
+ public function handleCustomerLogin($observer)
110
+ {
111
+ // Note that the customer_login event carries the pre-login cart.
112
+ $this->captureEvent($observer);
113
+ // This isn't cleared on login automatically
114
+ // even though Magento "frontend" cookie changes.
115
+ Mage::getSingleton('core/session')->unsLastEventTime();
116
+ }
117
 
118
+ /**
119
+ * Event observer function for event "customer_logout"
120
+ * (when a customer logs out.)
121
+ *
122
+ * @param object $observer event data.
123
+ * @return void
124
+ */
125
+ public function handleCustomerLogout($observer)
126
+ {
127
+ $this->captureEvent($observer);
128
+ // This isn't cleared on logout automatically
129
+ // even though Magento "frontend" cookie changes.
130
+ Mage::getSingleton('core/session')->unsLastEventTime();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  }
 
132
 
133
+ /**
134
+ * Returns whether last biz event sent to Cart Defender servers happened a
135
+ * long time ago (EVENT_TIME_THRESHOLD). It's intended use is to determine
136
+ * whether we should now send session state biz event.
137
+ *
138
+ * @return bool whether last biz event was sent long ago.
139
+ */
140
+ private function isLongSinceLastEvent()
141
+ {
142
+ if (!session_id()) {
143
+ return false;
 
 
144
  }
 
 
 
 
 
 
 
 
 
 
 
 
145
 
146
+ $now = time();
147
+ $lastEventAt = Mage::getSingleton('core/session')->getLastEventTime();
148
+ $result = empty($lastEventAt)
149
+ || (($now - $lastEventAt) > self::EVENT_TIME_THRESHOLD);
 
 
 
 
 
150
 
151
+ $this->logger->log('Observer->isLongSinceLastEvent', 'Result: '
152
+ . ($result ? 'Y' : 'N') . ' Time diff: '
153
+ . ($lastEventAt ? ($now - $lastEventAt): 'N/A'));
154
+ return $result;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
  }
app/code/community/CartDefender/Actions/controllers/CartDefenderSenderController.php CHANGED
@@ -1,82 +1,127 @@
1
  <?php
2
 
 
 
3
  /**
4
- * A script sending business events asynchronously to Cart Defender servers.
5
- *
6
- * The main reason for this script is so that we can launch it not blocking the main
7
- * PHP process rendering the page for the user. If we blocked, the page rendering or
8
- * AJAX processing would be slowed down by the time it takes for the round-trips to
9
- * our servers (at least 3 in case of SSL - about 250 milliseconds for any transatlantic
10
- * connections).
11
  *
12
- * @link http://cartdefender.com
13
- * @since 0.0.1
 
 
 
14
  *
15
  * @package CartDefender_Actions
16
  * @author Heptium Ltd.
17
  * @copyright Copyright (c) 2016 Heptium Ltd. (http://www.cartdefender.com/)
18
  * @license Open Software License
19
  */
20
- class CartDefender_Actions_CartDefenderSenderController extends Mage_Core_Controller_Front_Action {
 
 
 
 
 
 
 
 
21
 
22
- public function sendEventsAction() {
23
- $settings = Mage::helper('actions')->getSettings();
24
- // works only if flag is set to denote local API call and data exists
25
- if (!empty($_POST['is_local_request'])
26
- && $settings['send_key'] === $_POST['send_key']
27
- && !empty($_POST['data'])
28
- && $settings['enabled']) {
29
- $millis_before = round(microtime(true) * 1000);
30
-
31
- $data = $_POST['data'];
32
- $options = array(
33
- 'http' => array(
34
- 'header' => "Content-type: application/json\r\n"
35
- . "Authorization: Basic " . base64_encode($settings['api'] . ":") . "\r\n",
36
- 'method' => 'POST',
37
- 'content' => $data));
38
- $context = stream_context_create($options);
39
- $url = $this->getUrl($settings);
40
- $success = file_get_contents($url, false, $context);
41
-
42
- // Logging.
43
- $millis_after = round(microtime(true) * 1000);
44
- if (!$success) {
45
- // These will appear in the general server logs.
46
- Mage::helper('actions')->log('[Cart Defender biz event sender ERROR]'
47
- . ' Event number: ' . $_POST['event_no']
48
- . ' Url: ' . $url
49
- . ' Request time: ' . $millis_before
50
- . ' Request latency: ' . ($millis_after - $millis_before));
51
- } else {
52
- Mage::helper('actions')->log('[Cart Defender biz event sender success]'
53
- . ' Event number: ' . $_POST['event_no']
54
- . ' Url: ' . $url
55
- . ' Request time: ' . $millis_before
56
- . ' Request latency: ' . ($millis_after - $millis_before)
57
- . ' Is local equest: ' . $_POST['is_local_request']);
58
- }
59
- } else {
60
- Mage::helper('actions')->log('[Cart Defender biz event sender ERROR] '
61
- . 'Not a local POST request with an event or disabled');
 
62
  }
63
- Mage::helper('actions')->log('[Cart Defender biz event sender] Work done, time to finish.');
64
- echo 'Done';
65
- }
66
-
67
- /**
68
- * Returns the URL to which business events should be sent.
69
- */
70
- private function getUrl($settings) {
71
- $path = CartDefender_Actions_Helper_Data::CD_PLUGIN_BIZ_API_PATH_START
72
- . '/' . $_POST['correlation_id'] . '/'
73
- . CartDefender_Actions_Helper_Data::CD_PLUGIN_BIZ_API_VERSION
74
- . CartDefender_Actions_Helper_Data::CD_PLUGIN_BIZ_API_PATH_END;
75
- $use_raw_test_url = $settings['use_raw_test_url_for_biz_api'];
76
- return $settings['test']
77
- ? ($settings['test_server_url_start'] . ($use_raw_test_url ? '' : $path))
78
- : (CartDefender_Actions_Helper_Data::CD_HOST . $path);
79
- }
80
- }
81
 
82
- ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  <?php
2
 
3
+ use CartDefender_Actions_Helper_Data as CDData;
4
+
5
  /**
6
+ * A script sending text data to Cart Defender servers. While it sends
7
+ * the data synchronously, it's meant to be used indirectly, via
8
+ * AsyncLocalSender. This other utility does pass the data to this script
9
+ * asynchronously, thus achieving non blocking behavior as far as
10
+ * the PHP process from which we wanted to send the data is concerned.
 
 
11
  *
12
+ * The reason for this setup is not blocking the PHP process rendering
13
+ * the page for the user. If we blocked, the page rendering or AJAX
14
+ * processing would be slowed down by the time it takes for the round-trips
15
+ * to our servers (at least 3 in case of SSL - about 250 milliseconds for
16
+ * transatlantic connections).
17
  *
18
  * @package CartDefender_Actions
19
  * @author Heptium Ltd.
20
  * @copyright Copyright (c) 2016 Heptium Ltd. (http://www.cartdefender.com/)
21
  * @license Open Software License
22
  */
23
+ class CartDefender_Actions_CartDefenderSenderController
24
+ extends Mage_Core_Controller_Front_Action
25
+ {
26
+
27
+ /**
28
+ * @var CartDefender_Actions_Helper_Logger $logger Logger of Cart Defender
29
+ * specific messages.
30
+ */
31
+ private $logger;
32
 
33
+ /** Initializes this class, particularly the logger. */
34
+ public function _construct()
35
+ {
36
+ parent::_construct();
37
+ $this->logger = Mage::helper('actions/logger');
38
+ }
39
+
40
+ /**
41
+ * Synchronously sends the text data contained in the POST request
42
+ * invoking this controller to the Cart Defender servers.
43
+ *
44
+ * @return void
45
+ */
46
+ public function sendAction()
47
+ {
48
+ $settings = Mage::helper('actions')->getSettings();
49
+ $request = $this->getRequest();
50
+ if ($this->isRequestAllowed($request, $settings)) {
51
+ $millisBefore = round(microtime(true) * 1000);
52
+
53
+ // This is where the biz event is actually sent.
54
+ $response = $this->sendRequest($request, $settings);
55
+
56
+ // Logging.
57
+ $millisAfter = round(microtime(true) * 1000);
58
+ $this->logger->log(
59
+ 'CartDefenderSenderController->sendAction',
60
+ (($response->getStatus() == 200) ? 'Success' : 'Error')
61
+ . ' Sequence number: ' . $request->getPost('sequence_no')
62
+ . ' Url: ' . $this->getUrl($settings)
63
+ . ' Request time: ' . $millisBefore
64
+ . ' Request latency: ' . ($millisAfter - $millisBefore)
65
+ );
66
+ } else {
67
+ $this->logger->log(
68
+ 'CartDefenderSenderController->sendAction',
69
+ 'Error - request not allowed.'
70
+ );
71
+ }
72
+ $this->logger->log('CartDefenderSenderController->sendAction', 'Done');
73
+ echo 'Done';
74
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
 
76
+ /**
77
+ * Returns whether the request is allowed for processing.
78
+ *
79
+ * @param object $request the request to check.
80
+ * @param array $settings Cart Defender configuration settings.
81
+ * @return bool whether the request is allowed for processing.
82
+ */
83
+ private function isRequestAllowed($request, $settings)
84
+ {
85
+ $isLocalRequest = $request->getPost('is_local_request');
86
+ $data = $request->getPost('data');
87
+ return $settings['enabled'] // CD plugin enabled?
88
+ && !empty($isLocalRequest) // Is local request?
89
+ // Send key matches?
90
+ && ($settings['send_key'] === $request->getPost('send_key'))
91
+ && !empty($data); // Data non-empty?
92
+ }
93
+
94
+ /**
95
+ * Returns the URL to which the data should be sent by this script.
96
+ *
97
+ * @param array $settings Cart Defender configuration settings.
98
+ * @return string URL to which the data should be sent by this script.
99
+ */
100
+ private function getUrl($settings)
101
+ {
102
+ $path = CDData::CD_PLUGIN_BIZ_API_PATH_START
103
+ . '/' . $this->getRequest()->getPost('correlation_id') . '/'
104
+ . CDData::CD_PLUGIN_BIZ_API_VERSION
105
+ . CDData::CD_PLUGIN_BIZ_API_PATH_END;
106
+ $useRawTestUrl = $settings['use_raw_test_url_for_biz_api'];
107
+ return $settings['test'] ? ($settings['test_server_url_start']
108
+ . ($useRawTestUrl ? '' : $path)) : (CDData::CD_HOST . $path);
109
+ }
110
+
111
+ /**
112
+ * Sends a POST request to Cart Defender servers.
113
+ *
114
+ * @param object $request the request to send.
115
+ * @param array $settings Cart Defender configuration settings.
116
+ * @return void
117
+ */
118
+ private function sendRequest($request, $settings)
119
+ {
120
+ $client = new Varien_Http_Client($this->getUrl($settings));
121
+ $client->setMethod(Varien_Http_Client::POST);
122
+ $client->setAuth($settings['api'], '', Zend_Http_Client::AUTH_BASIC);
123
+ $client->setRawData($request->getPost('data'));
124
+ $client->setEncType('application/json');
125
+ return $client->request();
126
+ }
127
+ }
app/code/community/CartDefender/Actions/etc/config.xml CHANGED
@@ -1,319 +1,322 @@
1
  <?xml version="1.0"?>
2
  <!--
3
  /**
4
- * Cart Defender Actions plugin for Magento
5
  *
6
  * @package CartDefender_Actions
7
- * @author Heptium Ltd.
8
  * @copyright Copyright (c) 2016 Heptium Ltd. (http://www.cartdefender.com/)
9
  * @license Open Software License
10
  */
11
  -->
12
  <config>
13
- <modules>
14
- <CartDefender_Actions>
15
- <version>1.1.0</version>
16
- </CartDefender_Actions>
17
- </modules>
18
 
19
- <global>
20
- <blocks>
21
- <actions>
22
- <class>CartDefender_Actions_Block</class>
23
- </actions>
24
- </blocks>
25
- <helpers>
26
- <actions>
27
- <class>CartDefender_Actions_Helper</class>
28
- </actions>
29
- </helpers>
30
-
31
- <events>
32
- <controller_front_send_response_before>
33
- <observers>
34
- <cartdefender_controller_front_send_response_before>
35
- <type>singleton</type>
36
- <class>CartDefender_Actions_Model_Observer</class>
37
- <method>ensureCookieSetBeforeResponse</method>
38
- </cartdefender_controller_front_send_response_before>
39
- </observers>
40
- </controller_front_send_response_before>
41
- <catalog_controller_product_view>
42
- <observers>
43
- <cartdefender_catalog_controller_product_view>
44
- <type>singleton</type>
45
- <class>CartDefender_Actions_Model_Observer</class>
46
- <method>captureEvent</method>
47
- </cartdefender_catalog_controller_product_view>
48
- </observers>
49
- </catalog_controller_product_view>
50
- <checkout_cart_save_after>
51
- <observers>
52
- <cartdefender_checkout_cart_save_after>
53
- <type>singleton</type>
54
- <class>CartDefender_Actions_Model_Observer</class>
55
- <method>captureEvent</method>
56
- </cartdefender_checkout_cart_save_after>
57
- </observers>
58
- </checkout_cart_save_after>
59
- <checkout_controller_multishipping_shipping_post>
60
- <observers>
61
- <cartdefender_checkout_controller_multishipping_shipping_post>
62
- <type>singleton</type>
63
- <class>CartDefender_Actions_Model_Observer</class>
64
- <method>captureEvent</method>
65
- </cartdefender_checkout_controller_multishipping_shipping_post>
66
- </observers>
67
- </checkout_controller_multishipping_shipping_post>
68
- <checkout_controller_onepage_save_shipping_method>
69
- <observers>
70
- <cartdefender_checkout_controller_onepage_save_shipping_method>
71
- <type>singleton</type>
72
- <class>CartDefender_Actions_Model_Observer</class>
73
- <method>captureEvent</method>
74
- </cartdefender_checkout_controller_onepage_save_shipping_method>
75
- </observers>
76
- </checkout_controller_onepage_save_shipping_method>
77
- <checkout_multishipping_controller_success_action>
78
- <observers>
79
- <cartdefender_checkout_multishipping_controller_success_action>
80
- <type>singleton</type>
81
- <class>CartDefender_Actions_Model_Observer</class>
82
- <method>captureEvent</method>
83
- </cartdefender_checkout_multishipping_controller_success_action>
84
- </observers>
85
- </checkout_multishipping_controller_success_action>
86
- <checkout_onepage_controller_success_action>
87
- <observers>
88
- <cartdefender_checkout_onepage_controller_success_action>
89
- <type>singleton</type>
90
- <class>CartDefender_Actions_Model_Observer</class>
91
- <method>captureEvent</method>
92
- </cartdefender_checkout_onepage_controller_success_action>
93
- </observers>
94
- </checkout_onepage_controller_success_action>
95
- <customer_login>
96
- <observers>
97
- <cartdefender_customer_login>
98
- <type>singleton</type>
99
- <class>CartDefender_Actions_Model_Observer</class>
100
- <method>handleCustomerLogin</method>
101
- </cartdefender_customer_login>
102
- </observers>
103
- </customer_login>
104
- <customer_logout>
105
- <observers>
106
- <cartdefender_customer_logout>
107
- <type>singleton</type>
108
- <class>CartDefender_Actions_Model_Observer</class>
109
- <method>handleCustomerLogout</method>
110
- </cartdefender_customer_logout>
111
- </observers>
112
- </customer_logout>
113
- <googlecheckout_checkout_before>
114
- <observers>
115
- <cartdefender_googlecheckout_checkout_before>
116
- <type>singleton</type>
117
- <class>CartDefender_Actions_Model_Observer</class>
118
- <method>captureEvent</method>
119
- </cartdefender_googlecheckout_checkout_before>
120
- </observers>
121
- </googlecheckout_checkout_before>
122
- <googlecheckout_save_order_after>
123
- <observers>
124
- <cartdefender_googlecheckout_save_order_after>
125
- <type>singleton</type>
126
- <class>CartDefender_Actions_Model_Observer</class>
127
- <method>captureEvent</method>
128
- </cartdefender_googlecheckout_save_order_after>
129
- </observers>
130
- </googlecheckout_save_order_after>
131
- <order_cancel_after>
132
- <observers>
133
- <cartdefender_order_cancel_after>
134
- <type>singleton</type>
135
- <class>CartDefender_Actions_Model_Observer</class>
136
- <method>captureEvent</method>
137
- </cartdefender_order_cancel_after>
138
- </observers>
139
- </order_cancel_after>
140
- <sales_order_payment_cancel>
141
- <observers>
142
- <cartdefender_sales_order_payment_cancel>
143
- <type>singleton</type>
144
- <class>CartDefender_Actions_Model_Observer</class>
145
- <method>captureEvent</method>
146
- </cartdefender_sales_order_payment_cancel>
147
- </observers>
148
- </sales_order_payment_cancel>
149
- <sales_order_payment_capture>
150
- <observers>
151
- <cartdefender_sales_order_payment_capture>
152
- <type>singleton</type>
153
- <class>CartDefender_Actions_Model_Observer</class>
154
- <method>captureEvent</method>
155
- </cartdefender_sales_order_payment_capture>
156
- </observers>
157
- </sales_order_payment_capture>
158
- <sales_order_payment_pay>
159
- <observers>
160
- <cartdefender_sales_order_payment_pay>
161
- <type>singleton</type>
162
- <class>CartDefender_Actions_Model_Observer</class>
163
- <method>captureEvent</method>
164
- </cartdefender_sales_order_payment_pay>
165
- </observers>
166
- </sales_order_payment_pay>
167
- <sales_order_payment_place_end>
168
- <observers>
169
- <cartdefender_sales_order_payment_place_end>
170
- <type>singleton</type>
171
- <class>CartDefender_Actions_Model_Observer</class>
172
- <method>captureEvent</method>
173
- </cartdefender_sales_order_payment_place_end>
174
- </observers>
175
- </sales_order_payment_place_end>
176
- <sales_order_payment_place_start>
177
- <observers>
178
- <cartdefender_sales_order_payment_place_start>
179
- <type>singleton</type>
180
- <class>CartDefender_Actions_Model_Observer</class>
181
- <method>captureEvent</method>
182
- </cartdefender_sales_order_payment_place_start>
183
- </observers>
184
- </sales_order_payment_place_start>
185
- <sales_order_payment_refund>
186
- <observers>
187
- <cartdefender_sales_order_payment_refund>
188
- <type>singleton</type>
189
- <class>CartDefender_Actions_Model_Observer</class>
190
- <method>captureEvent</method>
191
- </cartdefender_sales_order_payment_refund>
192
- </observers>
193
- </sales_order_payment_refund>
194
- <sales_order_payment_void>
195
- <observers>
196
- <cartdefender_sales_order_payment_void>
197
- <type>singleton</type>
198
- <class>CartDefender_Actions_Model_Observer</class>
199
- <method>captureEvent</method>
200
- </cartdefender_sales_order_payment_void>
201
- </observers>
202
- </sales_order_payment_void>
203
- <wishlist_add_item>
204
- <observers>
205
- <cartdefender_wishlist_add_item>
206
- <type>singleton</type>
207
- <class>CartDefender_Actions_Model_Observer</class>
208
- <method>captureEvent</method>
209
- </cartdefender_wishlist_add_item>
210
- </observers>
211
- </wishlist_add_item>
212
- <wishlist_add_product>
213
- <observers>
214
- <cartdefender_wishlist_add_product>
215
- <type>singleton</type>
216
- <class>CartDefender_Actions_Model_Observer</class>
217
- <method>captureEvent</method>
218
- </cartdefender_wishlist_add_product>
219
- </observers>
220
- </wishlist_add_product>
221
- <wishlist_add_product>
222
- <observers>
223
- <cartdefender_wishlist_add_product>
224
- <type>singleton</type>
225
- <class>CartDefender_Actions_Model_Observer</class>
226
- <method>captureEvent</method>
227
- </cartdefender_wishlist_add_product>
228
- </observers>
229
- </wishlist_add_product>
230
- <wishlist_product_add_after>
231
- <observers>
232
- <cartdefender_wishlist_product_add_after>
233
- <type>singleton</type>
234
- <class>CartDefender_Actions_Model_Observer</class>
235
- <method>captureEvent</method>
236
- </cartdefender_wishlist_product_add_after>
237
- </observers>
238
- </wishlist_product_add_after>
239
- <wishlist_share>
240
- <observers>
241
- <cartdefender_wishlist_share>
242
- <type>singleton</type>
243
- <class>CartDefender_Actions_Model_Observer</class>
244
- <method>captureEvent</method>
245
- </cartdefender_wishlist_share>
246
- </observers>
247
- </wishlist_share>
248
- <wishlist_update_item>
249
- <observers>
250
- <cartdefender_wishlist_update_item>
251
- <type>singleton</type>
252
- <class>CartDefender_Actions_Model_Observer</class>
253
- <method>captureEvent</method>
254
- </cartdefender_wishlist_update_item>
255
- </observers>
256
- </wishlist_update_item>
257
- <controller_action_predispatch_checkout_onepage_index>
258
- <observers>
259
- <cartdefender_controller_action_predispatch_checkout_onepage_index>
260
- <type>singleton</type>
261
- <class>CartDefender_Actions_Model_Observer</class>
262
- <method>captureEvent</method>
263
- </cartdefender_controller_action_predispatch_checkout_onepage_index>
264
- </observers>
265
- </controller_action_predispatch_checkout_onepage_index>
266
- <controller_action_predispatch_checkout_multishipping_index>
267
- <observers>
268
- <cartdefender_controller_action_predispatch_checkout_multishipping_index>
269
- <type>singleton</type>
270
- <class>CartDefender_Actions_Model_Observer</class>
271
- <method>captureEvent</method>
272
- </cartdefender_controller_action_predispatch_checkout_multishipping_index>
273
- </observers>
274
- </controller_action_predispatch_checkout_multishipping_index>
275
-
276
- </events>
277
- </global>
278
 
279
- <frontend>
280
- <layout>
281
- <updates>
282
- <actions>
283
- <file>actions.xml</file>
284
- </actions>
285
- </updates>
286
- </layout>
287
- <routers>
288
- <actions>
289
- <use>standard</use>
290
- <args>
291
- <module>CartDefender_Actions</module>
292
- <frontName>cartdefender</frontName>
293
- </args>
294
- </actions>
295
- </routers>
296
- </frontend>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
297
 
298
- <adminhtml>
299
- <acl>
300
- <resources>
301
- <admin>
302
- <children>
303
- <system>
304
- <children>
305
- <config>
306
- <children>
307
- <actions translate="title" module="actions">
308
- <title>Actions</title>
309
- </actions>
310
- </children>
311
- </config>
312
- </children>
313
- </system>
314
- </children>
315
- </admin>
316
- </resources>
317
- </acl>
318
- </adminhtml>
319
  </config>
1
  <?xml version="1.0"?>
2
  <!--
3
  /**
4
+ * Cart Defender Actions plugin for Magento
5
  *
6
  * @package CartDefender_Actions
7
+ * @author Heptium Ltd.
8
  * @copyright Copyright (c) 2016 Heptium Ltd. (http://www.cartdefender.com/)
9
  * @license Open Software License
10
  */
11
  -->
12
  <config>
13
+ <modules>
14
+ <CartDefender_Actions>
15
+ <version>1.1.3</version>
16
+ </CartDefender_Actions>
17
+ </modules>
18
 
19
+ <global>
20
+ <blocks>
21
+ <actions>
22
+ <class>CartDefender_Actions_Block</class>
23
+ </actions>
24
+ </blocks>
25
+ <helpers>
26
+ <actions>
27
+ <class>CartDefender_Actions_Helper</class>
28
+ </actions>
29
+ </helpers>
30
+ <models>
31
+ <actions>
32
+ <class>CartDefender_Actions_Model</class>
33
+ </actions>
34
+ </models>
35
+ </global>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
 
37
+ <frontend>
38
+ <events>
39
+ <controller_front_send_response_before>
40
+ <observers>
41
+ <cartdefender_controller_front_send_response_before>
42
+ <type>singleton</type>
43
+ <class>CartDefender_Actions_Model_Observer</class>
44
+ <method>handleControllerFrontSendResponseBefore</method>
45
+ </cartdefender_controller_front_send_response_before>
46
+ </observers>
47
+ </controller_front_send_response_before>
48
+ <catalog_controller_product_view>
49
+ <observers>
50
+ <cartdefender_catalog_controller_product_view>
51
+ <type>singleton</type>
52
+ <class>CartDefender_Actions_Model_Observer</class>
53
+ <method>captureEvent</method>
54
+ </cartdefender_catalog_controller_product_view>
55
+ </observers>
56
+ </catalog_controller_product_view>
57
+ <checkout_cart_save_after>
58
+ <observers>
59
+ <cartdefender_checkout_cart_save_after>
60
+ <type>singleton</type>
61
+ <class>CartDefender_Actions_Model_Observer</class>
62
+ <method>captureEvent</method>
63
+ </cartdefender_checkout_cart_save_after>
64
+ </observers>
65
+ </checkout_cart_save_after>
66
+ <checkout_controller_multishipping_shipping_post>
67
+ <observers>
68
+ <cartdefender_checkout_controller_multishipping_shipping_post>
69
+ <type>singleton</type>
70
+ <class>CartDefender_Actions_Model_Observer</class>
71
+ <method>captureEvent</method>
72
+ </cartdefender_checkout_controller_multishipping_shipping_post>
73
+ </observers>
74
+ </checkout_controller_multishipping_shipping_post>
75
+ <checkout_controller_onepage_save_shipping_method>
76
+ <observers>
77
+ <cartdefender_checkout_controller_onepage_save_shipping_method>
78
+ <type>singleton</type>
79
+ <class>CartDefender_Actions_Model_Observer</class>
80
+ <method>captureEvent</method>
81
+ </cartdefender_checkout_controller_onepage_save_shipping_method>
82
+ </observers>
83
+ </checkout_controller_onepage_save_shipping_method>
84
+ <checkout_multishipping_controller_success_action>
85
+ <observers>
86
+ <cartdefender_checkout_multishipping_controller_success_action>
87
+ <type>singleton</type>
88
+ <class>CartDefender_Actions_Model_Observer</class>
89
+ <method>captureEvent</method>
90
+ </cartdefender_checkout_multishipping_controller_success_action>
91
+ </observers>
92
+ </checkout_multishipping_controller_success_action>
93
+ <checkout_onepage_controller_success_action>
94
+ <observers>
95
+ <cartdefender_checkout_onepage_controller_success_action>
96
+ <type>singleton</type>
97
+ <class>CartDefender_Actions_Model_Observer</class>
98
+ <method>captureEvent</method>
99
+ </cartdefender_checkout_onepage_controller_success_action>
100
+ </observers>
101
+ </checkout_onepage_controller_success_action>
102
+ <customer_login>
103
+ <observers>
104
+ <cartdefender_customer_login>
105
+ <type>singleton</type>
106
+ <class>CartDefender_Actions_Model_Observer</class>
107
+ <method>handleCustomerLogin</method>
108
+ </cartdefender_customer_login>
109
+ </observers>
110
+ </customer_login>
111
+ <customer_logout>
112
+ <observers>
113
+ <cartdefender_customer_logout>
114
+ <type>singleton</type>
115
+ <class>CartDefender_Actions_Model_Observer</class>
116
+ <method>handleCustomerLogout</method>
117
+ </cartdefender_customer_logout>
118
+ </observers>
119
+ </customer_logout>
120
+ <googlecheckout_checkout_before>
121
+ <observers>
122
+ <cartdefender_googlecheckout_checkout_before>
123
+ <type>singleton</type>
124
+ <class>CartDefender_Actions_Model_Observer</class>
125
+ <method>captureEvent</method>
126
+ </cartdefender_googlecheckout_checkout_before>
127
+ </observers>
128
+ </googlecheckout_checkout_before>
129
+ <googlecheckout_save_order_after>
130
+ <observers>
131
+ <cartdefender_googlecheckout_save_order_after>
132
+ <type>singleton</type>
133
+ <class>CartDefender_Actions_Model_Observer</class>
134
+ <method>captureEvent</method>
135
+ </cartdefender_googlecheckout_save_order_after>
136
+ </observers>
137
+ </googlecheckout_save_order_after>
138
+ <order_cancel_after>
139
+ <observers>
140
+ <cartdefender_order_cancel_after>
141
+ <type>singleton</type>
142
+ <class>CartDefender_Actions_Model_Observer</class>
143
+ <method>captureEvent</method>
144
+ </cartdefender_order_cancel_after>
145
+ </observers>
146
+ </order_cancel_after>
147
+ <sales_order_payment_cancel>
148
+ <observers>
149
+ <cartdefender_sales_order_payment_cancel>
150
+ <type>singleton</type>
151
+ <class>CartDefender_Actions_Model_Observer</class>
152
+ <method>captureEvent</method>
153
+ </cartdefender_sales_order_payment_cancel>
154
+ </observers>
155
+ </sales_order_payment_cancel>
156
+ <sales_order_payment_capture>
157
+ <observers>
158
+ <cartdefender_sales_order_payment_capture>
159
+ <type>singleton</type>
160
+ <class>CartDefender_Actions_Model_Observer</class>
161
+ <method>captureEvent</method>
162
+ </cartdefender_sales_order_payment_capture>
163
+ </observers>
164
+ </sales_order_payment_capture>
165
+ <sales_order_payment_pay>
166
+ <observers>
167
+ <cartdefender_sales_order_payment_pay>
168
+ <type>singleton</type>
169
+ <class>CartDefender_Actions_Model_Observer</class>
170
+ <method>captureEvent</method>
171
+ </cartdefender_sales_order_payment_pay>
172
+ </observers>
173
+ </sales_order_payment_pay>
174
+ <sales_order_payment_place_end>
175
+ <observers>
176
+ <cartdefender_sales_order_payment_place_end>
177
+ <type>singleton</type>
178
+ <class>CartDefender_Actions_Model_Observer</class>
179
+ <method>captureEvent</method>
180
+ </cartdefender_sales_order_payment_place_end>
181
+ </observers>
182
+ </sales_order_payment_place_end>
183
+ <sales_order_payment_place_start>
184
+ <observers>
185
+ <cartdefender_sales_order_payment_place_start>
186
+ <type>singleton</type>
187
+ <class>CartDefender_Actions_Model_Observer</class>
188
+ <method>captureEvent</method>
189
+ </cartdefender_sales_order_payment_place_start>
190
+ </observers>
191
+ </sales_order_payment_place_start>
192
+ <sales_order_payment_refund>
193
+ <observers>
194
+ <cartdefender_sales_order_payment_refund>
195
+ <type>singleton</type>
196
+ <class>CartDefender_Actions_Model_Observer</class>
197
+ <method>captureEvent</method>
198
+ </cartdefender_sales_order_payment_refund>
199
+ </observers>
200
+ </sales_order_payment_refund>
201
+ <sales_order_payment_void>
202
+ <observers>
203
+ <cartdefender_sales_order_payment_void>
204
+ <type>singleton</type>
205
+ <class>CartDefender_Actions_Model_Observer</class>
206
+ <method>captureEvent</method>
207
+ </cartdefender_sales_order_payment_void>
208
+ </observers>
209
+ </sales_order_payment_void>
210
+ <wishlist_add_item>
211
+ <observers>
212
+ <cartdefender_wishlist_add_item>
213
+ <type>singleton</type>
214
+ <class>CartDefender_Actions_Model_Observer</class>
215
+ <method>captureEvent</method>
216
+ </cartdefender_wishlist_add_item>
217
+ </observers>
218
+ </wishlist_add_item>
219
+ <wishlist_add_product>
220
+ <observers>
221
+ <cartdefender_wishlist_add_product>
222
+ <type>singleton</type>
223
+ <class>CartDefender_Actions_Model_Observer</class>
224
+ <method>captureEvent</method>
225
+ </cartdefender_wishlist_add_product>
226
+ </observers>
227
+ </wishlist_add_product>
228
+ <wishlist_add_product>
229
+ <observers>
230
+ <cartdefender_wishlist_add_product>
231
+ <type>singleton</type>
232
+ <class>CartDefender_Actions_Model_Observer</class>
233
+ <method>captureEvent</method>
234
+ </cartdefender_wishlist_add_product>
235
+ </observers>
236
+ </wishlist_add_product>
237
+ <wishlist_product_add_after>
238
+ <observers>
239
+ <cartdefender_wishlist_product_add_after>
240
+ <type>singleton</type>
241
+ <class>CartDefender_Actions_Model_Observer</class>
242
+ <method>captureEvent</method>
243
+ </cartdefender_wishlist_product_add_after>
244
+ </observers>
245
+ </wishlist_product_add_after>
246
+ <wishlist_share>
247
+ <observers>
248
+ <cartdefender_wishlist_share>
249
+ <type>singleton</type>
250
+ <class>CartDefender_Actions_Model_Observer</class>
251
+ <method>captureEvent</method>
252
+ </cartdefender_wishlist_share>
253
+ </observers>
254
+ </wishlist_share>
255
+ <wishlist_update_item>
256
+ <observers>
257
+ <cartdefender_wishlist_update_item>
258
+ <type>singleton</type>
259
+ <class>CartDefender_Actions_Model_Observer</class>
260
+ <method>captureEvent</method>
261
+ </cartdefender_wishlist_update_item>
262
+ </observers>
263
+ </wishlist_update_item>
264
+ <controller_action_predispatch_checkout_onepage_index>
265
+ <observers>
266
+ <cartdefender_controller_action_predispatch_checkout_onepage_index>
267
+ <type>singleton</type>
268
+ <class>CartDefender_Actions_Model_Observer</class>
269
+ <method>captureEvent</method>
270
+ </cartdefender_controller_action_predispatch_checkout_onepage_index>
271
+ </observers>
272
+ </controller_action_predispatch_checkout_onepage_index>
273
+ <controller_action_predispatch_checkout_multishipping_index>
274
+ <observers>
275
+ <cartdefender_controller_action_predispatch_checkout_multishipping_index>
276
+ <type>singleton</type>
277
+ <class>CartDefender_Actions_Model_Observer</class>
278
+ <method>captureEvent</method>
279
+ </cartdefender_controller_action_predispatch_checkout_multishipping_index>
280
+ </observers>
281
+ </controller_action_predispatch_checkout_multishipping_index>
282
+ </events>
283
+ <layout>
284
+ <updates>
285
+ <actions>
286
+ <file>actions.xml</file>
287
+ </actions>
288
+ </updates>
289
+ </layout>
290
+ <routers>
291
+ <actions>
292
+ <use>standard</use>
293
+ <args>
294
+ <module>CartDefender_Actions</module>
295
+ <frontName>cartdefender</frontName>
296
+ </args>
297
+ </actions>
298
+ </routers>
299
+ </frontend>
300
 
301
+ <adminhtml>
302
+ <acl>
303
+ <resources>
304
+ <admin>
305
+ <children>
306
+ <system>
307
+ <children>
308
+ <config>
309
+ <children>
310
+ <actions translate="title" module="actions">
311
+ <title>Actions</title>
312
+ </actions>
313
+ </children>
314
+ </config>
315
+ </children>
316
+ </system>
317
+ </children>
318
+ </admin>
319
+ </resources>
320
+ </acl>
321
+ </adminhtml>
322
  </config>
app/code/community/CartDefender/Actions/etc/system.xml CHANGED
@@ -1,10 +1,10 @@
1
  <?xml version="1.0"?>
2
  <!--
3
  /**
4
- * Cart Defender Actions plugin for Magento
5
  *
6
  * @package CartDefender_Actions
7
- * @author Heptium Ltd.
8
  * @copyright Copyright (c) 2016 Heptium Ltd. (http://www.cartdefender.com/)
9
  * @license Open Software License
10
  */
@@ -27,12 +27,12 @@
27
  <show_in_default>1</show_in_default>
28
  <show_in_website>1</show_in_website>
29
  <show_in_store>1</show_in_store>
30
- <comment><![CDATA[ <p>You can configure the Cart Defender's Magento Extension settings
31
- in this section. </p>
32
- <p>To register for Cart Defender's service and enable the Bot on your website
33
- <a href="https://portal.cartdefender.com/#!register"
34
- target="_blank">go to the registration page</a>.</p>
35
- <p> To configure your Bot's messages <a href="https://portal.cartdefender.com"
36
  target="_blank">go to the Cart Defender Portal</a>.</p>
37
  ]]>
38
  </comment>
1
  <?xml version="1.0"?>
2
  <!--
3
  /**
4
+ * Cart Defender Actions plugin for Magento
5
  *
6
  * @package CartDefender_Actions
7
+ * @author Heptium Ltd.
8
  * @copyright Copyright (c) 2016 Heptium Ltd. (http://www.cartdefender.com/)
9
  * @license Open Software License
10
  */
27
  <show_in_default>1</show_in_default>
28
  <show_in_website>1</show_in_website>
29
  <show_in_store>1</show_in_store>
30
+ <comment><![CDATA[ <p>You can configure the Cart Defender's Magento Extension settings
31
+ in this section. </p>
32
+ <p>To register for Cart Defender's service and enable the Bot on your website
33
+ <a href="https://portal.cartdefender.com/#!register"
34
+ target="_blank">go to the registration page</a>.</p>
35
+ <p> To configure your Bot's messages <a href="https://portal.cartdefender.com"
36
  target="_blank">go to the Cart Defender Portal</a>.</p>
37
  ]]>
38
  </comment>
app/design/frontend/base/default/layout/actions.xml CHANGED
@@ -1,11 +1,11 @@
1
  <?xml version="1.0"?>
2
  <!--
3
  /**
4
- * Cart Defender Actions plugin for Magento
5
  *
6
  * @category design_default
7
  * @package CartDefender_Actions
8
- * @author Heptium Ltd.
9
  * @copyright Copyright (c) 2016 Heptium Ltd. (http://www.cartdefender.com/)
10
  * @license Open Software License
11
  */
1
  <?xml version="1.0"?>
2
  <!--
3
  /**
4
+ * Cart Defender Actions plugin for Magento
5
  *
6
  * @category design_default
7
  * @package CartDefender_Actions
8
+ * @author Heptium Ltd.
9
  * @copyright Copyright (c) 2016 Heptium Ltd. (http://www.cartdefender.com/)
10
  * @license Open Software License
11
  */
app/design/frontend/base/default/template/actions/script.phtml CHANGED
@@ -1,23 +1,23 @@
1
  <?php
2
  /**
3
- * Actions plugin for Magento
4
  *
5
  * @package CartDefender_Actions
6
- * @author Heptium Ltd.
7
  * @copyright Copyright (c) 2016 Heptium Ltd. (http://www.cartdefender.com/)
8
  * @license Open Software License
9
  */
10
  ?>
11
 
12
  <?php if ($this->getSetting('enabled')) { ?>
13
- <?php if ($this->getSetting('test')) { ?>
14
- <p> Cart Defender Actions module is running in TEST MODE. </p>
15
- <?php } ?>
16
- <script type='text/javascript'>
17
- /* <![CDATA[ */
18
- var cdsvar = <?php echo $this->getVariables(); ?>;
19
- /* ]]> */
20
- </script>
21
- <script type="text/javascript" src=<?php echo $this->getJavaScriptUrl(); ?> >
22
- </script>
23
  <?php } ?>
1
  <?php
2
  /**
3
+ * Actions plugin for Magento
4
  *
5
  * @package CartDefender_Actions
6
+ * @author Heptium Ltd.
7
  * @copyright Copyright (c) 2016 Heptium Ltd. (http://www.cartdefender.com/)
8
  * @license Open Software License
9
  */
10
  ?>
11
 
12
  <?php if ($this->getSetting('enabled')) { ?>
13
+ <?php if ($this->getSetting('test')) { ?>
14
+ <p> Cart Defender Actions module is running in TEST MODE. </p>
15
+ <?php } ?>
16
+ <script type='text/javascript'>
17
+ /* <![CDATA[ */
18
+ var cdsvar = <?php echo $this->getVariables(); ?>;
19
+ /* ]]> */
20
+ </script>
21
+ <script type="text/javascript" src=<?php echo $this->getJavaScriptUrl(); ?> >
22
+ </script>
23
  <?php } ?>
app/etc/modules/CartDefender_Actions.xml CHANGED
@@ -1,10 +1,10 @@
1
  <?xml version="1.0"?>
2
  <!--
3
  /**
4
- * Cart Defender Actions plugin for Magento
5
  *
6
  * @package CartDefender_Actions
7
- * @author Heptium Ltd.
8
  * @copyright Copyright (c) 2016 Heptium Ltd. (http://www.cartdefender.com/)
9
  * @license Open Software License
10
  */
1
  <?xml version="1.0"?>
2
  <!--
3
  /**
4
+ * Cart Defender Actions plugin for Magento
5
  *
6
  * @package CartDefender_Actions
7
+ * @author Heptium Ltd.
8
  * @copyright Copyright (c) 2016 Heptium Ltd. (http://www.cartdefender.com/)
9
  * @license Open Software License
10
  */
package.xml CHANGED
@@ -1,20 +1,18 @@
1
  <?xml version="1.0"?>
2
  <package>
3
  <name>cartdefender_actions</name>
4
- <version>1.1.1</version>
5
  <stability>stable</stability>
6
  <license uri="http://opensource.org/licenses/osl-3.0.php">Open Software License (OSL)</license>
7
  <channel>community</channel>
8
  <extends/>
9
  <summary>Increase sales with smart targeted popups and offers delivered to your visitors in real time.</summary>
10
- <description>You can instantly use messages and offers which convert your visitors into customers. You focus on your business needs without A/B tests, as Cart Defender learns which popups covert the best and adjusts itself automatically.</description>
11
- <notes>Improve handling of non-UTF8 charsets in later PHP versions.&#xD;
12
- Enhance configuration page with links to Cart Defender Portal and registration page&#xD;
13
- </notes>
14
  <authors><author><name>Radoslaw Gliniecki</name><user>Cart_Defender</user><email>radek@cartdefender.com</email></author><author><name>Jan Zankowski</name><user>janzankowski</user><email>jan@cartdefender.com</email></author><author><name>Przemyslaw Gliniecki</name><user>psgliniecki</user><email>psg@cartdefender.com</email></author></authors>
15
- <date>2016-07-29</date>
16
- <time>11:56:23</time>
17
- <contents><target name="magecommunity"><dir name="CartDefender"><dir name="Actions"><dir name="Block"><file name="Script.php" hash="6879aeda8fa9d464124915992a761d00"/></dir><dir name="Helper"><file name="Data.php" hash="3b85a232191fa48f1487687c95c371fc"/></dir><dir name="Model"><file name="Observer.php" hash="f05d5a34beef7ec1f743ba4053321caf"/></dir><dir name="controllers"><file name="CartDefenderSenderController.php" hash="aef1db479967b4cc35623dc34e3979b7"/></dir><dir name="etc"><file name="config.xml" hash="220f0ff171665eb589b24b389fd2787a"/><file name="system.xml" hash="19f9087cbe9e92edde8372abe20f1354"/></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="CartDefender_Actions.xml" hash="e0458c6a3a4e1ae4210d523e8fcdd6fb"/></dir></target><target name="magedesign"><dir name="frontend"><dir name="base"><dir name="default"><dir name="template"><dir name="actions"><file name="script.phtml" hash="c25ad0d5fcdec392e8a69dc489667a91"/></dir></dir><dir name="layout"><file name="actions.xml" hash="ec83d87d66ef40a87b00bd0851be610f"/></dir></dir></dir></dir></target></contents>
18
  <compatible/>
19
  <dependencies><required><php><min>5.3.0</min><max>6.0.0</max></php><package><name>Mage_Core_Modules</name><channel>community</channel><min>1.7.0.2</min><max>1.9.2.4</max></package><extension><name>openssl</name><min>0.9.6</min><max>1.0.1</max></extension></required></dependencies>
20
  </package>
1
  <?xml version="1.0"?>
2
  <package>
3
  <name>cartdefender_actions</name>
4
+ <version>1.1.3</version>
5
  <stability>stable</stability>
6
  <license uri="http://opensource.org/licenses/osl-3.0.php">Open Software License (OSL)</license>
7
  <channel>community</channel>
8
  <extends/>
9
  <summary>Increase sales with smart targeted popups and offers delivered to your visitors in real time.</summary>
10
+ <description>You can instantly use messages and offers which convert your visitors into customers. You focus on your business needs without A/B tests, as Cart Defender learns which popups convert the best and adjusts itself automatically.</description>
11
+ <notes>No-op refactors and minor bugfixes.</notes>
 
 
12
  <authors><author><name>Radoslaw Gliniecki</name><user>Cart_Defender</user><email>radek@cartdefender.com</email></author><author><name>Jan Zankowski</name><user>janzankowski</user><email>jan@cartdefender.com</email></author><author><name>Przemyslaw Gliniecki</name><user>psgliniecki</user><email>psg@cartdefender.com</email></author></authors>
13
+ <date>2016-10-27</date>
14
+ <time>20:04:27</time>
15
+ <contents><target name="magecommunity"><dir name="CartDefender"><dir name="Actions"><dir name="Block"><file name="Script.php" hash="b234814d8cd363bcb22c3f9ed33bb80b"/></dir><dir name="Helper"><file name="Data.php" hash="dd4057deaaa8b0bdcfcc3e6f7c31091c"/><file name="Logger.php" hash="f21b3aed521b3761a890b22ba0f247a9"/></dir><dir name="Model"><file name="AsyncLocalSender.php" hash="f4df465d8bba0799a5381b50e7fe244f"/><file name="CorrelationIdManager.php" hash="427b1999971c28ee4a5dc39d6c158be1"/><file name="EventAsyncLocalSender.php" hash="cd9a25e21225cb1966da47fbbc8f9854"/><file name="EventBuilder.php" hash="d814e3b3c947e5a4815b43ba268a9b87"/><file name="Observer.php" hash="b6e3237c696a417557fa7a3a98b75358"/></dir><dir name="controllers"><file name="CartDefenderSenderController.php" hash="2932402af887cac75255758c7fd4da34"/></dir><dir name="etc"><file name="config.xml" hash="b6332ca2c6038b906edb525d1cf4df71"/><file name="system.xml" hash="3a0ef9f8ca4414d7714e717a64816a33"/></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="CartDefender_Actions.xml" hash="f9e77235e41f79ac086160ef2e494837"/></dir></target><target name="magedesign"><dir name="frontend"><dir name="base"><dir name="default"><dir name="template"><dir name="actions"><file name="script.phtml" hash="aa322fad9cd04ae7c910936e043fa8f1"/></dir></dir><dir name="layout"><file name="actions.xml" hash="e6a4b2f6d8132a833a1b5f8b9fce7166"/></dir></dir></dir></dir></target></contents>
16
  <compatible/>
17
  <dependencies><required><php><min>5.3.0</min><max>6.0.0</max></php><package><name>Mage_Core_Modules</name><channel>community</channel><min>1.7.0.2</min><max>1.9.2.4</max></package><extension><name>openssl</name><min>0.9.6</min><max>1.0.1</max></extension></required></dependencies>
18
  </package>