cartdefender_actions - Version 1.1.0

Version Notes

Initial stable release for Magento Connect. Covers integration with Cart Defender Service.

Download this release

Release Info

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


Version 1.1.0

app/code/community/CartDefender/Actions/Block/Script.php ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Cart Defender Actions plugin for Magento
5
+ *
6
+ * @category design_default
7
+ * @package CartDefender_Actions
8
+ * @author Heptium Ltd. (http://www.cartdefender.com/)
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
+ }
app/code/community/CartDefender/Actions/Helper/Data.php ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ }
app/code/community/CartDefender/Actions/Model/Observer.php ADDED
@@ -0,0 +1,472 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Cart Defender business event capture 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
+ 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 prepareBizEventData($event_name, $observer_data, $async_sender_api, $event_no) {
213
+ $website = Mage::app()->getWebsite(); // Gets the current website details
214
+ $store = Mage::app()->getStore(); // Gets the current store's details
215
+ $storeId = $store->getStoreId();
216
+ $storeGroup = $store->getGroup();
217
+
218
+ $app_software_name = "Magento " . Mage::getEdition();
219
+ $app_software_version = Mage::getVersion();
220
+ $this->captureSessionData($sessionId, $visitorData, $customer_id, $customerData, $is_logged_in);
221
+ $cart = $this->captureCartData();
222
+ $full_orders = $this->captureOrderData($observer_data);
223
+ $event = array(
224
+ 'api' => Mage::helper('actions')->getApi(),
225
+ 'appSoftwareName' => $app_software_name,
226
+ 'appSoftwareVersion' => $app_software_version,
227
+ 'eventType' => $event_name,
228
+ 'timestamp' => time(),
229
+ 'shop_current_currency' => $store->getCurrentCurrencyCode(),
230
+ 'cart' => $cart,
231
+ 'orders' => $full_orders,
232
+ 'eventNumber' => $event_no,
233
+ 'website_id' => $website->getId(),
234
+ 'website_code' => $website->getCode(),
235
+ 'website_name' => $website->getName(),
236
+ 'websiteData' => $website->getData(),
237
+ 'shopData' => $storeGroup->getData(),
238
+ 'shopViewData' => $store->getData(),
239
+ 'shopViewLocaleCode' => Mage::getStoreConfig('general/locale/code', $storeId),
240
+ 'shopViewBaseUrl' => $store->getBaseUrl(),
241
+ 'shopViewHomeUrl' => $store->getHomeUrl(),
242
+ 'checkout_link' => Mage::helper('checkout/url')->getCheckoutUrl(),
243
+ 'multishipping_checkout_link' => Mage::helper('checkout/url')->getMSCheckoutUrl(),
244
+ 'correlationId' => self::$correlation_id,
245
+ 'visitorId' => isset($visitorData['visitor_id'])
246
+ ? $visitorData['visitor_id'] : $this->MISSING_VALUE,
247
+ 'visitorData' => $visitorData,
248
+ 'isLoggedIn' => $is_logged_in,
249
+ 'customerId' => $customer_id,
250
+ 'customerData' => $customerData,
251
+ 'previousBizEventLatency' => $this->MISSING_VALUE
252
+ );
253
+ return Zend_Json::encode($event, true);
254
+ }
255
+
256
+ private function shouldSendSessionState() {
257
+ if (session_id()) {
258
+ $now = time();
259
+ $last_event_at = Mage::getSingleton('core/session')->getLastEventTime();
260
+ $result = empty($last_event_at) || (($now - $last_event_at) > self::EVENT_TIME_THRESHOLD);
261
+
262
+ Mage::helper('actions')->log(" -->Last event time: "
263
+ . (empty($last_event_at) ? "never" : $last_event_at));
264
+ Mage::helper('actions')->log("Should send session state result: " . $result
265
+ . " Time difference: " . (empty($last_event_at) ? "N/A" : ($now - $last_event_at)));
266
+ return $result;
267
+ }
268
+ return false;
269
+ }
270
+
271
+ /**
272
+ * Main event observer function. See ../etc/config.xml for observer configuration.
273
+ *
274
+ * List of events: http://www.nicksays.co.uk/magento-events-cheat-sheet-1-7/
275
+ */
276
+ public function captureEvent($observer) {
277
+ if (self::isCartDefenderEnabledAndIsRequestNonLocalAndIsRequestNonAdmin()) {
278
+ if (!isset(self::$response_started) && !isset(self::$correlation_id)) {
279
+ Mage::helper('actions')->log("Capture Event - Response not started yet, and no correlation "
280
+ . "ID so we set correlation ID - Event: " . $observer->getEvent()->getName());
281
+ self::setCorrelationId();
282
+ }
283
+ if (isset(self::$correlation_id)) { //do not send an event without correlation id
284
+ if ($this->shouldSendSessionState()) {
285
+ $this->sendStartOfSessionState();
286
+ Mage::helper('actions')->log("Capture Event - Sent Session State Update. Event: "
287
+ . $observer->getEvent()->getName());
288
+ }
289
+ $event_name = $observer->getEvent()->getName();
290
+ $observer_data = $observer->getData();
291
+ $this->sendEvent($event_name, $observer_data);
292
+ Mage::helper('actions')->log("Capture Event - Success - got the event name: ". $event_name);
293
+ }
294
+ }
295
+ }
296
+
297
+ /**
298
+ * Event observer function which should be called whenever a customer logs in.
299
+ * See ../etc/config.xml for observer configuration.
300
+ */
301
+ public function handleCustomerLogin($observer) {
302
+ // Note that the customer_login event carries the pre-login cart.
303
+ $this->captureEvent($observer);
304
+ // This isn't cleared on login automatically even though Magento "frontend" cookie changes.
305
+ Mage::getSingleton('core/session')->unsLastEventTime();
306
+ }
307
+
308
+ /**
309
+ * Event observer function which should be called whenever a customer logs out.
310
+ * See ../etc/config.xml for observer configuration.
311
+ */
312
+ public function handleCustomerLogout($observer) {
313
+ $this->captureEvent($observer);
314
+ // This isn't cleared on logout automatically even though Magento "frontend" cookie changes.
315
+ Mage::getSingleton('core/session')->unsLastEventTime();
316
+ }
317
+
318
+ /**
319
+ * Creates the text of a POST request (headers & contents) carrying a biz event, targeting
320
+ * the biz event sender script on localhost.
321
+ *
322
+ * @param string $biz_event_sender_url_path the URL path of the event sender
323
+ * @param string $biz_event_sender_host the hostname of localhost. We can't use "localhost" or
324
+ * "127.0.0.1" in case request is sent via SSL and the certificate doesn't include these two.
325
+ * @param int $event_no sequence number of the event, used for logging purposes.
326
+ * @param string $data_string the biz event data to send.
327
+ * @param bool $is_local_request is the request local for the same server ("PHP to PHP")
328
+ * @param string $correlation_id the correlation identifier for events
329
+ */
330
+ private function createBizEventRequest($biz_event_sender_url_path, $biz_event_sender_host,
331
+ $event_no, $data_string, $is_local_request, $correlation_id) {
332
+ $settings = Mage::helper('actions')->getSettings();
333
+ $query_params = array(
334
+ 'event_no' => $event_no,
335
+ 'data' => $data_string,
336
+ 'is_local_request' => $is_local_request,
337
+ 'correlation_id' => $correlation_id,
338
+ 'send_key' => $settings['send_key']
339
+ );
340
+ $postdata = http_build_query($query_params);
341
+
342
+ $req = "";
343
+ $req.= "POST " . $biz_event_sender_url_path . " HTTP/1.1\r\n";
344
+ $req.= "Host: " . $biz_event_sender_host . "\r\n";
345
+ $req.= "Content-type: application/x-www-form-urlencoded\r\n";
346
+ $req.= "Content-length: " . strlen($postdata) . "\r\n";
347
+ $req.= "\r\n";
348
+ $req.= $postdata;
349
+ $req.= "\r\n\r\n";
350
+
351
+ return $req;
352
+ }
353
+
354
+ /**
355
+ * If not set yet, sets a 64-bit Cart Defender correlation id for the current session.
356
+ * The value is either taken from a cookie, or generated and put on a new cookie,
357
+ * which we then try to store.
358
+ *
359
+ * NB: Cookie must be set before any content is sent to user's browser during a
360
+ * given PHP request processing. Call this using a very early hook.
361
+ * Mage::getModel(‘core/cookie’)->set($name, $value, $period);
362
+ */
363
+ public static function setCorrelationId() {
364
+ if (self::isCartDefenderEnabledAndIsRequestNonLocalAndIsRequestNonAdmin()) {
365
+ if (!isset(self::$correlation_id)) {
366
+ Mage::helper('actions')->log("--->>>> Correlation variable not set, checking cookie.");
367
+ $cookie = Mage::getSingleton('core/cookie');
368
+ if (method_exists($cookie, 'get')) {
369
+ $correlation_cookie = $cookie->get(self::CD_CORRELATION_COOKIE_NAME);
370
+ if (!empty($correlation_cookie)) {
371
+ self::$correlation_id = $correlation_cookie;
372
+ Mage::helper('actions')->log("--->>>> Taking id from cookie: " . self::$correlation_id);
373
+ } else {
374
+ // Don't use 4 as param b/c hexdec() has a limit of 7fffffff.
375
+ self::$correlation_id = hexdec(bin2hex(openssl_random_pseudo_bytes(3)))
376
+ . hexdec(bin2hex(openssl_random_pseudo_bytes(3)))
377
+ . hexdec(bin2hex(openssl_random_pseudo_bytes(2)));
378
+ /* This sets an HTTP response header. But if client browser has 1-st party cookies
379
+ disabled, the value won't be saved and returned to PHP engine on next call. We'll
380
+ try to regenerate it in the same way, again failing to make it permanent. However,
381
+ the web sensor will detect cookies disabled in JS and not load itself. The
382
+ eventual outcome will be no web events, and biz events with CD correlation ids varying
383
+ with each PHP request. Also, the nulls set corresponding settings to shop defaults. */
384
+ Mage::getSingleton('core/cookie')->set(self::CD_CORRELATION_COOKIE_NAME,
385
+ self::$correlation_id, 0, '/', null /* domain */,
386
+ null /* secure */, false /* HttpOnly */);
387
+ Mage::helper('actions')->log("--->>>> Cookie was empty, now: " . self::$correlation_id);
388
+ }
389
+ } else {
390
+ Mage::helper('actions')->log("--->>>> Could not get the correlation cookie from the "
391
+ . "browser. Server variable is: " . self::$correlation_id);
392
+ }
393
+ }
394
+ }
395
+ }
396
+
397
+ /**
398
+ * Event observer function which should be called whenever PHP is about to start sending a
399
+ * response to the browser. See ../etc/config.xml for observer configuration.
400
+ */
401
+ public function ensureCookieSetBeforeResponse($observer) {
402
+ if (self::isCartDefenderEnabledAndIsRequestNonLocalAndIsRequestNonAdmin()) {
403
+ if (empty(self::$response_started)) {
404
+ self::$response_started = true;
405
+ self::setCorrelationId();
406
+ if ($this->shouldSendSessionState()) { //check if should send start of session
407
+ $this->sendStartOfSessionState();
408
+ Mage::helper('actions')->log("Ensure cookie set - Sent Session State Update. Event: "
409
+ . $observer->getEvent()->getName());
410
+ }
411
+ Mage::helper('actions')->log("Ensure cookie set - Response has not started yet - Event: "
412
+ . $observer->getEvent()->getName() . " Correlation ID: " . self::$correlation_id);
413
+ } else {
414
+ Mage::helper('actions')->log("Ensure cookie set - Response started, don't set id - Event: "
415
+ . $observer->getEvent()->getName() . " Correlation ID: " . $self::$correlation_id);
416
+ }
417
+ } else {
418
+ Mage::helper('actions')->log("Ensure cookie set - local/admin/disabled, don't set id - Event:"
419
+ . $observer->getEvent()->getName() . " Correlation ID: " . self::$correlation_id
420
+ . " is Admin: " . Mage::app()->getStore()->isAdmin());
421
+ }
422
+ }
423
+
424
+ private function sendStartOfSessionState() {
425
+ Mage::helper('actions')->log("--->>>> Before Start of Session State. " . time());
426
+ if (isset(self::$correlation_id)) { //do not send an event without correlation id
427
+ $event_name = self::START_OF_SESSION;
428
+ $observer_data = array();
429
+ $this->sendEvent($event_name, $observer_data);
430
+ Mage::helper('actions')->log("Send Start of session state - Success");
431
+ }
432
+ }
433
+
434
+ private function sendEvent($event_name, $observer_data) {
435
+ //setting the local request flag, which denotes PHP to PHP call within current server
436
+ $is_local_request = true;
437
+ $connect = $this->connectionConfigSetup();
438
+ $async_sender_api = $connect['api'];
439
+ $connection_scheme = $connect['scheme'];
440
+ $event_no = Mage::getSingleton('core/session')->getEventNo();
441
+ $event_no = empty($event_no) ? 0 : $event_no;
442
+ Mage::getSingleton('core/session')->setEventNo($event_no + 1);
443
+ $data_string =
444
+ $this->prepareBizEventData($event_name, $observer_data, $async_sender_api, $event_no);
445
+ $req = $this->createBizEventRequest($async_sender_api['path'], $async_sender_api['host'],
446
+ $event_no, $data_string, $is_local_request, self::$correlation_id);
447
+ $socket = @fsockopen($connection_scheme . $async_sender_api['host'], $async_sender_api['port'],
448
+ $errno, $errstr, 0.025); //25ms timeout
449
+ $success = @fwrite($socket, $req);
450
+ Mage::getSingleton('core/session')->setLastEventTime(time());
451
+ if ($success) {
452
+ Mage::helper('actions')->log("[Send Event] - Success - sent the full event:" . $data_string);
453
+ } else {
454
+ Mage::helper('actions')->log("[Send Event] - ERROR - number: " . $errno
455
+ . 'Error string: ' . $errstr
456
+ . " the full event:" . $data_string);
457
+ }
458
+ $success = @fclose($socket);
459
+ }
460
+
461
+ /**
462
+ * Checks if this is an external request as opposed to a local server call to avoid an
463
+ * endless loop. Also verify that it's not Admin area of the store and that module is enabled.
464
+ */
465
+ private static function isCartDefenderEnabledAndIsRequestNonLocalAndIsRequestNonAdmin() {
466
+ $settings = Mage::helper('actions')->getSettings();
467
+ return empty($_POST['is_local_request'])
468
+ && !Mage::app()->getStore()->isAdmin()
469
+ && $settings['enabled'];
470
+ }
471
+
472
+ }
app/code/community/CartDefender/Actions/controllers/CartDefenderSenderController.php ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ ?>
app/code/community/CartDefender/Actions/etc/config.xml ADDED
@@ -0,0 +1,319 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>
app/code/community/CartDefender/Actions/etc/system.xml ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ <sections>
14
+ <actions translate="label" module="actions">
15
+ <label>Cart Defender Popups</label>
16
+ <tab>sales</tab>
17
+ <frontend_type>text</frontend_type>
18
+ <sort_order>1</sort_order>
19
+ <show_in_default>1</show_in_default>
20
+ <show_in_website>1</show_in_website>
21
+ <show_in_store>1</show_in_store>
22
+ <groups>
23
+ <settings translate="label">
24
+ <label>Settings</label>
25
+ <frontend_type>text</frontend_type>
26
+ <sort_order>1</sort_order>
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
+ <fields>
31
+ <enabled translate="label">
32
+ <label>Enabled</label>
33
+ <frontend_type>select</frontend_type>
34
+ <source_model>adminhtml/system_config_source_yesno</source_model>
35
+ <sort_order>1</sort_order>
36
+ <show_in_default>1</show_in_default>
37
+ <show_in_website>1</show_in_website>
38
+ <show_in_store>1</show_in_store>
39
+ </enabled>
40
+ <api translate="label">
41
+ <label>API key</label>
42
+ <frontend_type>text</frontend_type>
43
+ <sort_order>2</sort_order>
44
+ <show_in_default>1</show_in_default>
45
+ <show_in_website>1</show_in_website>
46
+ <show_in_store>1</show_in_store>
47
+ </api>
48
+ <send_key translate="label">
49
+ <label>Generated internal request key</label>
50
+ <frontend_type>text</frontend_type>
51
+ <sort_order>3</sort_order>
52
+ <show_in_default>1</show_in_default>
53
+ <show_in_website>0</show_in_website>
54
+ <show_in_store>0</show_in_store>
55
+ </send_key>
56
+ <test translate="label">
57
+ <label>Test mode</label>
58
+ <frontend_type>select</frontend_type>
59
+ <source_model>adminhtml/system_config_source_yesno</source_model>
60
+ <sort_order>4</sort_order>
61
+ <show_in_default>1</show_in_default>
62
+ <show_in_website>1</show_in_website>
63
+ <show_in_store>1</show_in_store>
64
+ </test>
65
+ <!-- Don't use a trailing slash in test server URL.-->
66
+ <test_server_url_start>
67
+ <label>Test server URL protocol, domain, and path prefix (if any)</label>
68
+ <frontend_type>text</frontend_type>
69
+ <sort_order>5</sort_order>
70
+ <show_in_default>1</show_in_default>
71
+ <show_in_website>1</show_in_website>
72
+ <show_in_store>1</show_in_store>
73
+ <depends><test>1</test></depends>
74
+ </test_server_url_start>
75
+ <!-- This setting controls if the "Test server URL" value is used exactly
76
+ as provided for sending biz events - i.e. without appending things
77
+ like "/plugin/<correlation id>/v1-beta/bizEvent". This is useful for
78
+ inspecting biz event JSONs with something like RequestBin. -->
79
+ <use_raw_test_url_for_biz_api>
80
+ <label>Use unadorned test server URL for JSON API</label>
81
+ <frontend_type>select</frontend_type>
82
+ <source_model>adminhtml/system_config_source_yesno</source_model>
83
+ <sort_order>6</sort_order>
84
+ <show_in_default>1</show_in_default>
85
+ <show_in_website>1</show_in_website>
86
+ <show_in_store>1</show_in_store>
87
+ <depends><test>1</test></depends>
88
+ </use_raw_test_url_for_biz_api>
89
+ </fields>
90
+ </settings>
91
+ </groups>
92
+ </actions>
93
+ </sections>
94
+ </config>
app/design/frontend/base/default/layout/actions.xml ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ */
12
+ -->
13
+ <layout>
14
+ <default>
15
+ <reference name="before_body_end">
16
+ <block type="actions/script" name="actions" as="actions" template="actions/script.phtml"/>
17
+ </reference>
18
+ </default>
19
+ </layout>
app/design/frontend/base/default/template/actions/script.phtml ADDED
@@ -0,0 +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 } ?>
app/etc/modules/CartDefender_Actions.xml ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ <active>true</active>
16
+ <codePool>community</codePool>
17
+ </CartDefender_Actions>
18
+ </modules>
19
+ </config>
package.xml ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>cartdefender_actions</name>
4
+ <version>1.1.0</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>Initial stable release for Magento Connect. Covers integration with Cart Defender Service.</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-06-30</date>
14
+ <time>19:13:42</time>
15
+ <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="545653986fc2fe4a4007011ec1429cf9"/></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="7c2c60a71221a23990e37bb94041a50f"/></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>
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>