W3CDigitalDataLayerByTriggeredMessaging - Version 0.3.3

Version Notes

All basic features included.
- It can handle full page caching.
- Exception handling has been added.
- Removed empty objects and arrays; Removed duplicated subcategories.
- Added product data for products on category and search pages.
- Fixed issue where configurable products have multiple product entries, and incorrect prices, in the cart object.
- Added more data into digitalData object so websites can be debugged.
- Configurable products have each of their configurations listed under 'linkedProduct'.
- Fixed bug that was breaking some styling.
- Added 'pageName' attribute and fixed type of 'returningStatus' object.
- Tweaks to configuration panel and default values.
- List of all installed non-Magento extensions are exposed (for debug purposes)
- Products associated with a Grouped Product have been included.
- Minimum prices of grouped and bundled products can be extracted.
- Output of debug data can now be controlled through admin configuration panel.

Download this release

Release Info

Developer Muhammed Miah
Extension W3CDigitalDataLayerByTriggeredMessaging
Version 0.3.3
Comparing to
See all releases


Version 0.3.3

app/code/community/TriggeredMessaging/DigitalDataLayer/Block/Ddl.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class TriggeredMessaging_DigitalDataLayer_Block_Ddl extends Mage_Core_Block_Template {
4
+
5
+ }
6
+
7
+ ?>
app/code/community/TriggeredMessaging/DigitalDataLayer/Helper/Data.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class TriggeredMessaging_DigitalDataLayer_Helper_Data extends Mage_Core_Helper_Abstract {
4
+
5
+ }
6
+
7
+ ?>
app/code/community/TriggeredMessaging/DigitalDataLayer/Model/Container/Ddl.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class TriggeredMessaging_DigitalDataLayer_Model_Container_Ddl extends Enterprise_PageCache_Model_Container_Advanced_Quote
3
+ {
4
+ /**
5
+ * Render block content
6
+ *
7
+ * @return string
8
+ */
9
+ protected function _renderBlock()
10
+ {
11
+ $block = $this->_getPlaceHolderBlock();
12
+ return $block->toHtml();
13
+ }
14
+ }
app/code/community/TriggeredMessaging/DigitalDataLayer/Model/Observer.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class TriggeredMessaging_DigitalDataLayer_Model_Observer
3
+ {
4
+ /**
5
+ * Is Enabled Full Page Cache
6
+ *
7
+ * @var bool
8
+ */
9
+ protected $_isEnabled;
10
+
11
+ /**
12
+ * Class constructor
13
+ */
14
+ public function __construct()
15
+ {
16
+ try {
17
+ $this->_isEnabled = Mage::app()->useCache('full_page');
18
+ } catch (Exception $e) {
19
+ }
20
+ }
21
+
22
+ /**
23
+ * Check if full page cache is enabled
24
+ *
25
+ * @return bool
26
+ */
27
+ public function isCacheEnabled()
28
+ {
29
+ return $this->_isEnabled;
30
+ }
31
+ /**
32
+ * Set cart hash in cookie on quote change
33
+ *
34
+ * @param Varien_Event_Observer $observer
35
+ * @return Enterprise_PageCache_Model_Observer
36
+ */
37
+ public function registerQuoteChange(Varien_Event_Observer $observer)
38
+ {
39
+ if (!$this->isCacheEnabled()) {
40
+ return $this;
41
+ }
42
+ try {
43
+ $cacheId = TriggeredMessaging_DigitalDataLayer_Model_Container_Ddl::getCacheId();
44
+ Enterprise_PageCache_Model_Cache::getCacheInstance()->remove($cacheId);
45
+ } catch (Exception $e) {
46
+ }
47
+ return $this;
48
+ }
49
+ }
app/code/community/TriggeredMessaging/DigitalDataLayer/Model/Page/Observer.php ADDED
@@ -0,0 +1,1047 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class TriggeredMessaging_DigitalDataLayer_Model_Page_Observer {
4
+
5
+ // Specification is at
6
+ // http://www.w3.org/2013/12/ceddl-201312.pdf
7
+ protected $_version = "1.0";
8
+ protected $_user = null;
9
+ protected $_page = null;
10
+ protected $_cart = null;
11
+ protected $_product = null;
12
+ protected $_search = null;
13
+ protected $_transaction = null;
14
+ protected $_listing = null;
15
+ protected $_events = array();
16
+
17
+ protected $_debug = false;
18
+
19
+ protected function _getRequest() {
20
+ return Mage::app()->getFrontController()->getRequest();
21
+ }
22
+
23
+ /*
24
+ * Returns Controller Name
25
+ */
26
+ protected function _getControllerName() {
27
+ return $this->_getRequest()->getControllerName();
28
+ }
29
+
30
+ protected function _getActionName() {
31
+ return $this->_getRequest()->getActionName();
32
+ }
33
+
34
+ protected function _getModuleName() {
35
+ return $this->_getRequest()->getModuleName();
36
+ }
37
+
38
+ protected function _getRouteName() {
39
+ return $this->_getRequest()->getRouteName();
40
+ }
41
+
42
+ protected function _getCustomer() {
43
+ return Mage::helper('customer')->getCustomer();
44
+ }
45
+
46
+ protected function _getBreadcrumb() {
47
+ return Mage::helper('catalog')->getBreadcrumbPath();
48
+ }
49
+
50
+ protected function _getCategory($category_id) {
51
+ return Mage::getModel('catalog/category')->load($category_id);
52
+ }
53
+
54
+ protected function _getCurrentProduct() {
55
+ return Mage::registry('current_product');
56
+ }
57
+
58
+ protected function _getProduct($productId) {
59
+ return Mage::getModel('catalog/product')->load($productId);
60
+ }
61
+
62
+ protected function _getCurrentCategory() {
63
+ return Mage::registry('current_category');
64
+ }
65
+
66
+ protected function _getCatalogSearch() {
67
+ return Mage::getSingleton('catalogsearch/advanced');
68
+ }
69
+
70
+ protected function _getCheckoutSession() {
71
+ return Mage::getSingleton('checkout/session');
72
+ }
73
+
74
+ protected function _getSalesOrder() {
75
+ return Mage::getModel('sales/order');
76
+ }
77
+
78
+ protected function _getOrderAddress() {
79
+ return Mage::getModel('sales/order_address');
80
+ }
81
+
82
+ /*
83
+ * Determine which page type we're on
84
+ */
85
+
86
+ public function _isHome() {
87
+ if (Mage::app()->getRequest()->getRequestString() == "/") {
88
+ return true;
89
+ } else {
90
+ return false;
91
+ }
92
+ }
93
+
94
+ public function _isContent() {
95
+ if ($this->_getModuleName() == 'cms') {
96
+ return true;
97
+ } else {
98
+ return false;
99
+ }
100
+ }
101
+
102
+ public function _isCategory() {
103
+ if ($this->_getControllerName() == 'category') {
104
+ return true;
105
+ } else {
106
+ return false;
107
+ }
108
+ }
109
+
110
+ public function _isSearch() {
111
+ if ($this->_getModuleName() == 'catalogsearch') {
112
+ return true;
113
+ } else {
114
+ return false;
115
+ }
116
+ }
117
+
118
+ public function _isProduct() {
119
+ $onCatalog = false;
120
+ if(Mage::registry('current_product')) {
121
+ $onCatalog = true;
122
+ }
123
+ return $onCatalog;
124
+ }
125
+
126
+ public function _isCart() {
127
+ try {
128
+ $request = $this->_getRequest();
129
+ $module = $request->getModuleName();
130
+ $controller = $request->getControllerName();
131
+ $action = $request->getActionName();
132
+ if ($module == 'checkout' && $controller == 'cart' && $action == 'index') {
133
+ return true;
134
+ }
135
+ } catch (Exception $e) {
136
+ }
137
+
138
+ return false;
139
+ }
140
+
141
+ public function _isCheckout() {
142
+ if (strpos($this->_getModuleName(), 'checkout') !== false && $this->_getActionName() != 'success') {
143
+ return true;
144
+ } else {
145
+ return false;
146
+ }
147
+ }
148
+
149
+ public function _isConfirmation() {
150
+ // default controllerName is "onepage"
151
+ // relax the check, only check if it contains checkout
152
+ // some checkout systems have different prefix/postfix,
153
+ // but all contain checkout
154
+ if (strpos($this->_getModuleName(), 'checkout') !== false && $this->_getActionName() == "success") {
155
+ return true;
156
+ } else {
157
+ return false;
158
+ }
159
+ }
160
+
161
+
162
+ /*
163
+ * Get information on pages to pass to front end
164
+ */
165
+
166
+ public function getVersion() {
167
+ return $this->_version;
168
+ }
169
+
170
+ public function getPurchaseCompleteQs() {
171
+
172
+ $orderId = $this->_getCheckoutSession()->getLastOrderId();
173
+ if ($orderId) {
174
+ $order = $this->_getSalesOrder()->load($orderId);
175
+ $email = $order->getCustomerEmail();
176
+ }else{
177
+ $email = $user->getEmail();
178
+ }
179
+ $qs = "e=" . urlencode($email);
180
+
181
+ if($orderId){
182
+ $qs = $qs . "&r=" . urlencode($orderId);
183
+ }
184
+
185
+ return $qs;
186
+ }
187
+
188
+ public function getUser() {
189
+ return $this->_user;
190
+ }
191
+
192
+ public function getPage() {
193
+ return $this->_page;
194
+ }
195
+
196
+ public function getProduct() {
197
+ return $this->_product;
198
+ }
199
+
200
+ public function getCart() {
201
+ return $this->_cart;
202
+ }
203
+
204
+ public function getTransaction() {
205
+ return $this->_transaction;
206
+ }
207
+
208
+ public function getListing() {
209
+ return $this->_listing;
210
+ }
211
+
212
+ public function getEvents() {
213
+ return array();
214
+ }
215
+
216
+
217
+ /*
218
+ * Set the model attributes to be passed front end
219
+ */
220
+
221
+ public function _getPageType() {
222
+ try {
223
+ if ($this->_isHome()) {
224
+ return 'home';
225
+ } elseif ($this->_isContent()) {
226
+ return 'content';
227
+ } elseif ($this->_isCategory()) {
228
+ return 'category';
229
+ } elseif ($this->_isSearch()) {
230
+ return 'search';
231
+ } elseif ($this->_isProduct()) {
232
+ return 'product';
233
+ } elseif ($this->_isCart()) {
234
+ return 'basket';
235
+ } elseif ($this->_isCheckout()) {
236
+ return 'checkout';
237
+ } elseif ($this->_isConfirmation()) {
238
+ return 'confirmation';
239
+ } else {
240
+ return $this->_getModuleName();
241
+ }
242
+ } catch (Exception $e) {
243
+ }
244
+ }
245
+
246
+ public function _getPageBreadcrumb() {
247
+ $arr = $this->_getBreadcrumb();
248
+ $breadcrumb = array();
249
+
250
+ try {
251
+ foreach ($arr as $category) {
252
+ $breadcrumb[] = $category['label'];
253
+ }
254
+ } catch (Exception $e) {
255
+ }
256
+
257
+ return $breadcrumb;
258
+ }
259
+
260
+ public function _setPage() {
261
+ /*
262
+ Section 6.3 of http://www.w3.org/2013/12/ceddl-201312.pdf
263
+ page: {
264
+ pageInfo: {
265
+ pageID: "Great Winter Run 2015",
266
+ destinationURL: "http://www.greatrun.org/Events/Event.aspx?id=2"},
267
+ category: {
268
+ primaryCategory: "Cameras",
269
+ subCategory1: "Nikon",
270
+ pageType: "product"
271
+ }
272
+ */
273
+
274
+ try {
275
+ $this->_page = array();
276
+
277
+ $this->_page['pageInfo'] = array();
278
+ // $this->_page['pageInfo']['pageID']
279
+ // $this->_page['pageInfo']['pageName']
280
+ $this->_page['pageInfo']['destinationURL'] = Mage::helper('core/url')->getCurrentUrl();
281
+ $referringURL = Mage::app()->getRequest()->getServer('HTTP_REFERER');
282
+ if ($referringURL) {
283
+ $this->_page['pageInfo']['referringURL'] = $referringURL;
284
+ }
285
+ // $this->_page['pageInfo']['sysEnv']
286
+ // $this->_page['pageInfo']['variant']
287
+ if ($this->_getPageBreadcrumb()) {
288
+ $this->_page['pageInfo']['breadcrumbs'] = $this->_getPageBreadcrumb();
289
+ }
290
+ // $this->_page['pageInfo']['author']
291
+ // $this->_page['pageInfo']['issueDate']
292
+ // $this->_page['pageInfo']['effectiveDate']
293
+ // $this->_page['pageInfo']['expiryDate']
294
+ $this->_page['pageInfo']['language'] = Mage::app()->getLocale()->getLocaleCode();
295
+ // $this->_page['pageInfo']['geoRegion']
296
+ // $this->_page['pageInfo']['industryCodes']
297
+ // $this->_page['pageInfo']['publisher']
298
+
299
+ $this->_page['category'] = array();
300
+ if (Mage::registry('current_category')) {
301
+ // There must be a better way than this
302
+ $this->_page['category']['primaryCategory'] = Mage::registry('current_category')->getName();
303
+ }
304
+ // $this->_page['category']['subCategory1'];
305
+ $this->_page['category']['pageType'] = $this->_getPageType();
306
+
307
+ // $this->_page['attributes'] = array();
308
+
309
+ if ($this->_debug) {
310
+ $modules = (array)Mage::getConfig()->getNode('modules')->children();
311
+ $mods = array();
312
+ foreach ($modules as $key => $value) {
313
+ if (strpos($key, 'Mage_')===false) {
314
+ $mods[] = $key;
315
+
316
+ }
317
+ }
318
+ $this->_page['extra_modules'] = $mods;
319
+ }
320
+
321
+ } catch (Exception $e) {
322
+ }
323
+ }
324
+
325
+ // Set the user info
326
+ public function _setUser() {
327
+ // Section 6.9 of http://www.w3.org/2013/12/ceddl-201312.pdf
328
+
329
+ try {
330
+ $this->_user = array();
331
+ $user = $this->_getCustomer();
332
+ $user_id = $user->getEntityId();
333
+ $firstName = $user->getFirstname();
334
+ $lastName = $user->getLastname();
335
+
336
+ if ($this->_isConfirmation()) {
337
+ $orderId = $this->_getCheckoutSession()->getLastOrderId();
338
+ if ($orderId) {
339
+ $order = $this->_getSalesOrder()->load($orderId);
340
+ $email = $order->getCustomerEmail();
341
+ }
342
+ } else {
343
+ $email = $user->getEmail();
344
+ }
345
+
346
+ // $this->_user['segment'] = array();
347
+ $this->_user['profile'] = array();
348
+
349
+ $profile = array();
350
+
351
+ $profile['profileInfo'] = array();
352
+ if ($user_id) {
353
+ $profile['profileInfo']['profileID'] = (string) $user_id;
354
+ }
355
+ if ($firstName){
356
+ $profile['profileInfo']['userFirstName'] = $firstName;
357
+ }
358
+ if ($lastName){
359
+ $profile['profileInfo']['userLastName'] = $lastName;
360
+ }
361
+ if ($email) {
362
+ $profile['profileInfo']['email'] = $email;
363
+ }
364
+ $profile['profileInfo']['language'] = Mage::getStoreConfig('general/locale/code', Mage::app()->getStore()->getId());
365
+ $profile['profileInfo']['returningStatus'] = $user_id ? 'true' : 'false';
366
+ // $profile['profileInfo']['type'];
367
+
368
+ // $profile['address'] = array();
369
+ // $profile['address']['line1'];
370
+ // $profile['address']['line2'];
371
+ // $profile['address']['city'];
372
+ // $profile['address']['stateProvince'];
373
+ // $profile['address']['postalCode'];
374
+ // $profile['address']['country'];
375
+
376
+ // $profile['social'] = array();
377
+ // $profile['attributes'] = array();
378
+
379
+ array_push($this->_user['profile'], $profile);
380
+ } catch (Exception $e) {
381
+ }
382
+ }
383
+
384
+ public function _getAddress($address) {
385
+ /*
386
+ address: {
387
+ line1: "",
388
+ line2: "",
389
+ city: "",
390
+ stateProvince: "",
391
+ postalCode: "",
392
+ country: ""
393
+ },
394
+ */
395
+
396
+ $billing = array();
397
+
398
+ try {
399
+ if ($address) {
400
+ $billing['line1'] = $address->getName();
401
+ $billing['line2'] = $address->getStreetFull();
402
+ $billing['city'] = $address->getCity();
403
+ $billing['postalCode'] = $address->getPostcode();
404
+ $billing['country'] = $address->getCountry();
405
+ $state = $address->getRegion();
406
+ $billing['stateProvince'] = $state ? $state : '';
407
+ }
408
+ } catch (Exception $e) {
409
+ }
410
+
411
+ return $billing;
412
+ }
413
+
414
+ public function _getProductStock($product) {
415
+ return (int) Mage::getModel('cataloginventory/stock_item')->loadByProduct($product)->getQty();
416
+ }
417
+
418
+ public function _getCurrency() {
419
+ return Mage::app()->getStore()->getCurrentCurrencyCode();
420
+ }
421
+
422
+ public function _getProductModel($product) {
423
+ /*
424
+ Section 6.4 of http://www.w3.org/2013/12/ceddl-201312.pdf
425
+ product: [
426
+ {
427
+ productInfo: {
428
+ productID: "greatwinterrun2015", // Unique product ID
429
+ productName: "Great Winter Run",
430
+ description: "Running, in the winter, in Edinburgh",
431
+ productURL: "http://www.greatrun.org/Events/Event.aspx?id=2",
432
+ productImage: "http://www.greatrun.org/Events/App_Images/slideshow/cropped/saved/gwir_01f0a780d94.jpg",
433
+ productThumbnail: "http://www.greatrun.org/App_Images/2011/Events/logo_GWIR.jpg"
434
+ },
435
+ category: {
436
+ primaryCategory: "Somecategory",
437
+ subCategory1: "Subcat"
438
+ },
439
+ attributes: {
440
+ distance: 5000,
441
+ country: "Scotland",
442
+ date: "2015-01-11", // Dates in ISO 8601
443
+ city: "Edinburgh" // You can put any extended data you want passing through in attributes
444
+ },
445
+ price: {
446
+ basePrice: 40.00,
447
+ currency: "GBP",
448
+ taxRate: 0.2,
449
+ priceWithTax: 48.00
450
+ }
451
+ }
452
+ ]
453
+ */
454
+
455
+ $product_model = array();
456
+
457
+ try {
458
+ // Product Info
459
+ $product_model['productInfo'] = array();
460
+ $product_model['productInfo']['productID'] = $product->getId();
461
+ $product_model['productInfo']['productName'] = $product->getName();
462
+ $product_model['productInfo']['description'] = strip_tags($product->getShortDescription());
463
+ $product_model['productInfo']['productURL'] = $product->getProductUrl();
464
+
465
+ //Check if images contain placeholders
466
+ if(!($product->getImage()=="no_selection")){
467
+ $product_model['productInfo']['productImage'] = $product->getImageUrl();
468
+ }
469
+ if(!($product->getThumbnail()=="no_selection")){
470
+ $product_model['productInfo']['productThumbnail'] = $product->getThumbnailUrl();
471
+ }
472
+ // $product_model['productInfo']['manufacturer'];
473
+ if ($product->getWeight()) {
474
+ $product_model['productInfo']['size'] = $product->getWeight();
475
+ }
476
+
477
+ // Category
478
+ $categories = $this->_getProductCategories($product);
479
+ if (isset($categories[0])||isset($categories[1])||isset($categories[2])) {
480
+ $product_model['category'] = array();
481
+
482
+ if (isset($categories[0])) {
483
+ $product_model['category']['primaryCategory'] = $categories[0];
484
+ }
485
+ if (isset($categories[1])) {
486
+ $product_model['category']['subCategory1'] = $categories[1];
487
+
488
+ // Delete if subcategory is a duplicate of the primary category
489
+ if (isset($categories[0]) && $categories[0]===$categories[1]) {
490
+ unset( $product_model['category']['subCategory1'] );
491
+ }
492
+ }
493
+ if (isset($categories[2])) {
494
+ $product_model['category']['subCategory2'] = $categories[2];
495
+
496
+ // Delete if subcategory is a duplicate of previous subcategory
497
+ if (isset($categories[1]) && $categories[1]===$categories[2]) {
498
+ unset( $product_model['category']['subCategory2'] );
499
+ }
500
+ }
501
+
502
+ // $product_model['category']['productType'];
503
+ }
504
+
505
+ // Price
506
+ $product_model['price'] = array();
507
+ $product_model['price']['basePrice'] = floatval($product->getPrice());
508
+ $product_model['price']['currency'] = $this->_getCurrency();
509
+ $product_model['price']['priceWithTax'] = floatval($product->getFinalPrice()); // May be lower due to discounts! TODO
510
+
511
+ if (!$product_model['price']['priceWithTax']) {
512
+ unset( $product_model['price']['priceWithTax'] );
513
+ }
514
+
515
+ // In case 'basePrice' did not exist
516
+ if (!$product_model['price']['basePrice']) {
517
+ $product_model['price']['basePrice'] = floatval($product->getGroupPrice());
518
+ }
519
+ if (!$product_model['price']['basePrice']) {
520
+ $product_model['price']['basePrice'] = floatval($product->getMinimalPrice());
521
+ }
522
+ if (!$product_model['price']['basePrice']) {
523
+ $product_model['price']['basePrice'] = floatval($product->getSpecialPrice());
524
+ }
525
+ if (!$product_model['price']['basePrice']) {
526
+ // Extract price for bundle products
527
+ $price_model = $product->getPriceModel();
528
+ if (method_exists($price_model, 'getOptions')) {
529
+ $normal_price = 0.0;
530
+ $_options = $price_model->getOptions($product);
531
+ foreach($_options as $_option) {
532
+ if (!method_exists($_option, 'getDefaultSelection')) {
533
+ break;
534
+ }
535
+ $_selection = $_option->getDefaultSelection();
536
+ if ($_selection === null) continue;
537
+ $normal_price += floatval($_selection->getPrice());
538
+ }
539
+ $product_model['price']['basePrice'] = $normal_price;
540
+ }
541
+ }
542
+
543
+ if ($this->_debug) {
544
+ $product_model['price']['all'] = array();
545
+ $product_model['price']['all']['getPrice'] = $product->getPrice();
546
+ $product_model['price']['all']['getMinimalPrice'] = $product->getMinimalPrice();
547
+ $product_model['price']['all']['getPriceModel'] = $product->getPriceModel();
548
+ $product_model['price']['all']['getGroupPrice'] = $product->getGroupPrice();
549
+ $product_model['price']['all']['getTierPrice'] = $product->getTierPrice();
550
+ $product_model['price']['all']['getTierPriceCount'] = $product->getTierPriceCount();
551
+ $product_model['price']['all']['getFormatedTierPrice'] = $product->getFormatedTierPrice();
552
+ $product_model['price']['all']['getFormatedPrice'] = $product->getFormatedPrice();
553
+ $product_model['price']['all']['getFinalPrice'] = $product->getFinalPrice();
554
+ $product_model['price']['all']['getCalculatedFinalPrice'] = $product->getCalculatedFinalPrice();
555
+ $product_model['price']['all']['getSpecialPrice'] = $product->getSpecialPrice();
556
+ }
557
+
558
+ // Calculate Tax Rate
559
+ $store = Mage::app()->getStore('default');
560
+ $taxCalculation = Mage::getModel('tax/calculation');
561
+ $request = $taxCalculation->getRateRequest(null, null, null, $store);
562
+ $taxClassId = $product->getTaxClassId();
563
+ $percent = $taxCalculation->getRate($request->setProductClassId($taxClassId));
564
+ $product_model['price']['taxRate'] = ((float) $percent) / 100;
565
+
566
+ // For configurable/grouped/composite products, add all associated products to 'linkedProduct'
567
+ if ($product->isConfigurable() || $product->isGrouped() || $product->isComposite()) {
568
+
569
+ $product_model['linkedProduct'] = array();
570
+ $simple_collection = array();
571
+
572
+ // Add simple products related to configurable products
573
+ if ($product->isConfigurable()) {
574
+ $conf = Mage::getModel('catalog/product_type_configurable')->setProduct($product);
575
+ $simple_collection = $conf->getUsedProductCollection()->addAttributeToSelect('*')->addFilterByRequiredOptions();
576
+ }
577
+ else {
578
+ $type_instance = $product->getTypeInstance(true);
579
+ if (method_exists($type_instance, 'getSelectionsCollection')) {
580
+ // Add simple products related to bundle products
581
+ $simple_collection = $type_instance->getSelectionsCollection(
582
+ $type_instance->getOptionsIds($product), $product
583
+ );
584
+ } else if (method_exists($type_instance, 'getAssociatedProducts')) {
585
+ // Add simple products related to grouped products
586
+ $simple_collection = $type_instance->getAssociatedProducts($product);
587
+ }
588
+ }
589
+
590
+ // Add related products to the data layer
591
+ $min_price = 0.0;
592
+ foreach($simple_collection as $simple_product){
593
+ array_push($product_model['linkedProduct'], $this->_getProductModel($simple_product));
594
+ $simple_product_price = floatval($simple_product->getPrice());
595
+ if ($simple_product_price && (!$min_price || $simple_product_price<$min_price)) {
596
+ $min_price = $simple_product_price;
597
+ }
598
+ }
599
+
600
+ // If price could not be extracted before, can set it now
601
+ if (!$product_model['price']['basePrice']) {
602
+ $product_model['price']['basePrice'] = floatval($min_price);
603
+ }
604
+
605
+ if (!$product_model['linkedProduct']) {
606
+ unset($product_model['linkedProduct']);
607
+ }
608
+ }
609
+
610
+ if ($this->_debug) {
611
+ $product_model['more']['isConfigurable'] = $product->isConfigurable();
612
+ $product_model['more']['isSuperGroup'] = $product->isSuperGroup();
613
+ $product_model['more']['isSuperConfig'] = $product->isSuperConfig();
614
+ $product_model['more']['isGrouped'] = $product->isGrouped();
615
+ $product_model['more']['isSuper'] = $product->isSuper();
616
+ $product_model['more']['isVirtual'] = $product->isVirtual();
617
+ $product_model['more']['isRecurring'] = $product->isRecurring();
618
+ $product_model['more']['isComposite'] = $product->isComposite();
619
+ $product_model['more']['getTypeId'] = $product->getTypeId();
620
+ }
621
+
622
+ // Other
623
+ // $product_model['attributes'] = array();
624
+
625
+ // The following are not used in W3C DDL but exist in Universal Variable:
626
+ // $product_model['sku_code'] = $product->getSku();
627
+ // $product_model['stock'] = (int) $this->_getProductStock($product);
628
+ } catch (Exception $e) {
629
+ }
630
+
631
+ return $product_model;
632
+ }
633
+
634
+ public function _getProductCategories($product) {
635
+ try {
636
+ $cats = $product->getCategoryIds();
637
+ if ($cats) {
638
+ $category_names = array();
639
+ foreach ($cats as $category_id) {
640
+ $_cat = $this->_getCategory($category_id);
641
+ $category_names[] = $_cat->getName();
642
+ }
643
+ return $category_names;
644
+ }
645
+ } catch (Exception $e) {
646
+ }
647
+
648
+ return false;
649
+ }
650
+
651
+ public function _getLineItems($items, $page_type) {
652
+ /*
653
+ item: [
654
+ {
655
+ productInfo: {
656
+ productID: "greatwinterrun2015", // Unique product ID - links the prod in the cart to the one browsed
657
+ // If data isn't available on the cart page, as long as the productID is present, it will be filled
658
+ // out from our product database (e.g. name, images, category, extra attributes, price)
659
+ productName: "Great Winter Run",
660
+ description: "Running, in the winter, in Edinburgh",
661
+ productURL: "http://www.greatrun.org/Events/Event.aspx?id=2",
662
+ productImage: "http://www.greatrun.org/Events/App_Images/slideshow/cropped/saved/gwir_01f0a780d94.jpg",
663
+ productThumbnail: "http://www.greatrun.org/App_Images/2011/Events/logo_GWIR.jpg"
664
+ },
665
+ category: {
666
+ primaryCategory: "Somecategory",
667
+ subCategory1: "Subcat"
668
+ },
669
+ attributes: {
670
+ distance: 5000,
671
+ country: "Scotland",
672
+ date: "2015-01-11", // Dates in ISO 8601
673
+ city: "Edinburgh" // You can put any extended data you want passing through in attributes
674
+ },
675
+ price: {
676
+ basePrice: 40.00,
677
+ currency: "GBP",
678
+ taxRate: 0.2,
679
+ priceWithTax: 48.00
680
+ }
681
+ }
682
+ ]
683
+ */
684
+
685
+ $line_items = array();
686
+
687
+ try {
688
+ foreach($items as $item) {
689
+ $productId = $item->getProductId();
690
+ $product = $this->_getProduct($productId);
691
+ // product needs to be visible
692
+ if ($product->isVisibleInSiteVisibility()) {
693
+
694
+ $litem_model = $this->_getProductModel($product);
695
+
696
+ if ($page_type == 'cart') {
697
+ $litem_model['quantity'] = floatval($item->getQty());
698
+ } else {
699
+ $litem_model['quantity'] = floatval($item->getQtyOrdered());
700
+ }
701
+
702
+ if (!is_array($litem_model['price'])) {
703
+ $litem_model['price'] = array();
704
+ }
705
+ if ($item->getCouponCode()) {
706
+ $litem_model['price']['voucherCode'] = $item->getCouponCode();
707
+ }
708
+ if ($item->getDiscountAmount()) {
709
+ $litem_model['price']['voucherDiscount'] = abs(floatval($item->getDiscountAmount()));
710
+ }
711
+ // $litem_model['price']['shipping'];
712
+ // $litem_model['price']['shippingMethod'] = $this->_extractShippingMethod($item->getQuote());
713
+ $litem_model['price']['priceWithTax'] = floatval($item->getBasePriceInclTax()); // TODO: This may be different from that in _getProductModel()
714
+ $litem_model['price']['cartTotal'] = floatval($item->getRowTotalInclTax());
715
+
716
+ if ($this->_debug) {
717
+ $litem_model['price']['all']['_getCalculationPrice'] = $product->getCalculationPrice();
718
+ $litem_model['price']['all']['_getCalculationPriceOriginal'] = $product->getCalculationPriceOriginal();
719
+ $litem_model['price']['all']['_getBaseCalculationPrice'] = $product->getBaseCalculationPrice();
720
+ $litem_model['price']['all']['_getBaseCalculationPriceOriginal'] = $product->getBaseCalculationPriceOriginal();
721
+ $litem_model['price']['all']['_getOriginalPrice'] = $product->getOriginalPrice();
722
+ $litem_model['price']['all']['_getBaseOriginalPrice'] = $product->getBaseOriginalPrice();
723
+ $litem_model['price']['all']['_getConvertedPrice'] = $product->getConvertedPrice();
724
+ }
725
+
726
+ // $litem_model['linkedProduct'] = array();
727
+ // $litem_model['attributes'] = array();
728
+
729
+ array_push($line_items, $litem_model);
730
+ }
731
+ }
732
+ } catch (Exception $e) {
733
+ }
734
+
735
+ return $line_items;
736
+ }
737
+
738
+ public function _setListing() {
739
+ try {
740
+ $this->_listing = array();
741
+ if ($this->_isCategory()) {
742
+ $category = $this->_getCurrentCategory();
743
+ } elseif ($this->_isSearch()) {
744
+ $category = $this->_getCatalogSearch();
745
+ if (isset($_GET['q'])) {
746
+ $this->_listing['query'] = $_GET['q'];
747
+ }
748
+ }
749
+
750
+ // Note: data on products are retrieved later, after the content layout block,
751
+ // since the product list is compiled then.
752
+ } catch (Exception $e) {
753
+ }
754
+ }
755
+
756
+ public function _setProduct() {
757
+ try {
758
+ $product = $this->_getCurrentProduct();
759
+ if (!$product) return false;
760
+ $this->_product = array();
761
+ array_push($this->_product, $this->_getProductModel($product));
762
+ } catch (Exception $e) {
763
+ }
764
+ }
765
+
766
+ public function _setCart() {
767
+ /*
768
+ Section 6.5 of http://www.w3.org/2013/12/ceddl-201312.pdf
769
+ cart: {
770
+ price: {
771
+ basePrice: 40.00,
772
+ currency: "GBP",
773
+ taxRate: 0.2,
774
+ cartTotal: 48.00
775
+ },
776
+ item: [
777
+ {
778
+ productInfo: {
779
+ productID: "greatwinterrun2015", // Unique product ID - links the prod in the cart to the one browsed
780
+ // If data isn't available on the cart page, as long as the productID is present, it will be filled
781
+ // out from our product database (e.g. name, images, category, extra attributes, price)
782
+ productName: "Great Winter Run",
783
+ description: "Running, in the winter, in Edinburgh",
784
+ productURL: "http://www.greatrun.org/Events/Event.aspx?id=2",
785
+ productImage: "http://www.greatrun.org/Events/App_Images/slideshow/cropped/saved/gwir_01f0a780d94.jpg",
786
+ productThumbnail: "http://www.greatrun.org/App_Images/2011/Events/logo_GWIR.jpg"
787
+ },
788
+ category: {
789
+ primaryCategory: "Somecategory",
790
+ subCategory1: "Subcat"
791
+ },
792
+ attributes: {
793
+ distance: 5000,
794
+ country: "Scotland",
795
+ date: "2015-01-11", // Dates in ISO 8601
796
+ city: "Edinburgh" // You can put any extended data you want passing through in attributes
797
+ },
798
+ price: {
799
+ basePrice: 40.00,
800
+ currency: "GBP",
801
+ taxRate: 0.2,
802
+ priceWithTax: 48.00
803
+ }
804
+ }
805
+ ]
806
+ }
807
+ */
808
+
809
+ try {
810
+ $basket = $this->_getCheckoutSession();
811
+
812
+ if (!isset($basket)) {
813
+ return;
814
+ }
815
+
816
+ $cart = array();
817
+ $quote = $basket->getQuote();
818
+
819
+ // Set normal params
820
+ $cart_id = $basket->getQuoteId();
821
+ if ($cart_id) {
822
+ $cart['cartID'] = (string) $cart_id;
823
+ }
824
+ $cart['price'] = array();
825
+ $cart['price']['basePrice'] = (float) $quote->getBaseSubtotal();
826
+ if ($quote->getShippingAddress()->getCouponCode()) {
827
+ $cart['price']['voucherCode'] = $quote->getShippingAddress()->getCouponCode();
828
+ }
829
+ if ($quote->getShippingAddress()->getDiscountAmount()) {
830
+ $cart['price']['voucherDiscount'] = abs((float) $quote->getShippingAddress()->getDiscountAmount());
831
+ }
832
+ $cart['price']['currency'] = $this->_getCurrency();
833
+
834
+ $taxRate = (float) $quote->getShippingAddress()->getTaxAmount() / $quote->getBaseSubtotal();
835
+ $cart['price']['taxRate'] = round($taxRate, 3); // TODO: Find a better way
836
+ if ($quote->getShippingAmount()) {
837
+ $cart['price']['shipping'] = (float) $quote->getShippingAmount();
838
+ }
839
+ if ($this->_extractShippingMethod($quote)) {
840
+ $cart['price']['shippingMethod'] = $this->_extractShippingMethod($quote);
841
+ }
842
+ $cart['price']['priceWithTax'] = (float) $quote->getShippingAddress()->getTaxAmount() + $quote->getBaseSubtotal(); // TODO: Find a better way
843
+ $cart['price']['cartTotal'] = (float) $quote->getGrandTotal();
844
+ // $cart['attributes'] = array();
845
+ if ($cart['price']['basePrice']===0.0&&$cart['price']['cartTotal']===0.0&&$cart['price']['priceWithTax']===0.0) {
846
+ unset($cart['price']);
847
+ }
848
+
849
+ // Line items
850
+ $items = $quote->getAllVisibleItems();
851
+ if (!$items && $cart['price']) {
852
+ if ($this->_debug) {
853
+ $cart['price']['testLog'] = "Second method used to retrieve cart items.";
854
+ }
855
+
856
+ // In case items were not retrieved for some reason
857
+ $cartHelper = Mage::helper('checkout/cart');
858
+ $items = $cartHelper->getCart()->getItems();
859
+ }
860
+ $cart['items'] = $this->_getLineItems($items, 'cart');
861
+ if (empty($cart['items'])) {
862
+ unset($cart['items']);
863
+ }
864
+
865
+ // The following are not used in W3C DDL but exist in Universal Variable:
866
+ // $cart['subtotal'] = (float) $quote->getSubtotal();
867
+ // $cart['tax'] = (float) $quote->getShippingAddress()->getTaxAmount();
868
+ // $cart['subtotal_include_tax'] = (boolean) $this->_doesSubtotalIncludeTax($quote, $cart['tax']);
869
+
870
+ if ($cart_id||isset($cart['items'])||isset($cart['price'])) {
871
+ $this->_cart = $cart;
872
+ }
873
+ } catch (Exception $e) {
874
+ }
875
+ }
876
+
877
+ public function _doesSubtotalIncludeTax($order, $tax) {
878
+ /*
879
+ Conditions:
880
+ - if tax is zero, then set to false
881
+ - Assume that if grand total is bigger than total after subtracting shipping, then subtotal does NOT include tax
882
+ */
883
+ try {
884
+ $grandTotalWithoutShipping = $order->getGrandTotal() - $order->getShippingAmount();
885
+ if ($tax == 0 || $grandTotalWithoutShipping > $order->getSubtotal()) {
886
+ return false;
887
+ } else {
888
+ return true;
889
+ }
890
+ } catch (Exception $e) {
891
+ }
892
+ }
893
+
894
+ public function _extractShippingMethod($order) {
895
+ try {
896
+ $shipping_method = $order->getShippingMethod();
897
+ } catch (Exception $e) {
898
+ }
899
+ return $shipping_method ? $shipping_method : '';
900
+ }
901
+
902
+ public function _setTransaction() {
903
+ /*
904
+ Spec:
905
+ transaction {
906
+ transactionID: "",
907
+ profile: {
908
+ profileInfo: {
909
+ profileID: "",
910
+ userName: "",
911
+ email: ""
912
+ },
913
+ address: {
914
+ line1: "",
915
+ line2: "",
916
+ city: "",
917
+ stateProvince: "",
918
+ postalCode: "",
919
+ country: ""
920
+ },
921
+ shippingAddress: {
922
+ line1: "",
923
+ line2: "",
924
+ city: "",
925
+ stateProvince: "",
926
+ postalCode: "",
927
+ country: ""
928
+ }
929
+ },
930
+ total: {
931
+ basePrice: 0,
932
+ voucherCode: "",
933
+ voucherDiscount: 0,
934
+ currency: "USD",
935
+ taxRate: 0,
936
+ shipping: 0,
937
+ shippingMethod: "",
938
+ priceWithTax: 0,
939
+ transactionTotal: 0
940
+ },
941
+ attributes: {},
942
+ item: [
943
+ {
944
+ productInfo
945
+ category
946
+ quantity
947
+ price
948
+ linkedProduct
949
+ attributes
950
+ }
951
+ ]
952
+ }
953
+ */
954
+
955
+ try {
956
+ $orderId = $this->_getCheckoutSession()->getLastOrderId();
957
+ if ($orderId) {
958
+ $transaction = array();
959
+ $order = $this->_getSalesOrder()->load($orderId);
960
+
961
+ // Get general details
962
+ $transaction['transactionID'] = $order->getIncrementId();
963
+ $transaction['total'] = array();
964
+ $transaction['total']['currency'] = $this->_getCurrency();
965
+ $transaction['total']['basePrice'] = (float) $order->getSubtotal();
966
+ // $transaction['tax'] = (float) $order->getTaxAmount();
967
+ // $transaction['subtotal_include_tax'] = $this->_doesSubtotalIncludeTax($order, $transaction['tax']);
968
+ // $transaction['payment_type'] = $order->getPayment()->getMethodInstance()->getTitle();
969
+ $transaction['total']['transactionTotal'] = (float) $order->getGrandTotal();
970
+
971
+ $voucher = $order->getCouponCode();
972
+ $transaction['total']['voucherCode'] = $voucher ? $voucher : "";
973
+ $voucher_discount = -1 * $order->getDiscountAmount();
974
+ $transaction['total']['voucherDiscount'] = $voucher_discount ? $voucher_discount : 0;
975
+
976
+ $transaction['total']['shipping'] = (float) $order->getShippingAmount();
977
+ $transaction['total']['shippingMethod'] = $this->_extractShippingMethod($order);
978
+
979
+ // Get addresses
980
+ $shippingId = $order->getShippingAddress()->getId();
981
+ $address = $this->_getOrderAddress()->load($shippingId);
982
+ $billingAddress = $order->getBillingAddress();
983
+ $shippingAddress = $order->getShippingAddress();
984
+ $transaction['profile'] = array();
985
+ $transaction['profile']['address'] = $this->_getAddress($billingAddress);
986
+ $transaction['profile']['shippingAddress'] = $this->_getAddress($shippingAddress);
987
+
988
+ // Get items
989
+ $items = $order->getAllItems();
990
+ $line_items = $this->_getLineItems($items, 'transaction');
991
+ $transaction['item'] = $line_items;
992
+
993
+ $this->_transaction = $transaction;
994
+ }
995
+ } catch (Exception $e) {
996
+ }
997
+ }
998
+
999
+ public function setDigitalDataLayer(Varien_Event_Observer $observer) {
1000
+ // W3C DDL
1001
+ // - pageInstanceID
1002
+ // - page
1003
+ // - product[n]
1004
+ // - cart
1005
+ // - transaction
1006
+ // - event[n]
1007
+ // - component[n]
1008
+ // - user[n]
1009
+ // - privacyAccessCategories
1010
+ // - version = "1.0"
1011
+
1012
+ try {
1013
+ $triggered_messaging_digital_data_layer_enabled = (boolean)Mage::getStoreConfig('triggered_messaging/triggered_messaging_digital_data_layer_enabled');
1014
+
1015
+ if ($triggered_messaging_digital_data_layer_enabled==1) {
1016
+ $this->_debug = (boolean)Mage::getStoreConfig('triggered_messaging/triggered_messaging_digital_data_layer_debug_enabled');
1017
+
1018
+ $this->_setUser();
1019
+ $this->_setPage();
1020
+
1021
+ if ($this->_isProduct()) {
1022
+ $this->_setProduct();
1023
+ }
1024
+
1025
+ if ($this->_isCategory() || $this->_isSearch()) {
1026
+ $this->_setListing();
1027
+ }
1028
+
1029
+ if (!$this->_isConfirmation()) {
1030
+ $this->_setCart();
1031
+ }
1032
+
1033
+ if ($this->_isConfirmation()) {
1034
+ $this->_setTransaction();
1035
+ }
1036
+
1037
+ // Add script after content block, to grab products shown on category and search pages
1038
+ $layout = $observer->getEvent()->getLayout()->getUpdate();
1039
+ $layout->addHandle('tms_block_after_content');
1040
+ }
1041
+ } catch(Exception $e) {
1042
+ }
1043
+
1044
+ return $this;
1045
+ }
1046
+ }
1047
+ ?>
app/code/community/TriggeredMessaging/DigitalDataLayer/etc/adminhtml.xml ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <config>
3
+ <acl>
4
+ <resources>
5
+ <admin>
6
+ <children>
7
+ <system>
8
+ <children>
9
+ <config>
10
+ <children>
11
+ <triggeredmessaging_digitaldatalayer_configuration translate="title" module="digital_data_layer_main">
12
+ <sort_order>0</sort_order>
13
+ <title>Manage Triggered Messaging Digital Data Layer Extension</title>
14
+ </triggeredmessaging_digitaldatalayer_configuration>
15
+ <triggeredmessaging_digitaldatalayer_support translate="title" module="digital_data_layer_main">
16
+ <sort_order>1</sort_order>
17
+ <title>Triggered Messaging Support Information</title>
18
+ </triggeredmessaging_digitaldatalayer_support>
19
+ </children>
20
+ </config>
21
+ </children>
22
+ </system>
23
+ </children>
24
+ </admin>
25
+ </resources>
26
+ </acl>
27
+ </config>
app/code/community/TriggeredMessaging/DigitalDataLayer/etc/cache.xml ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <config>
3
+ <placeholders>
4
+ <triggeredmessaging_ddl_session_data>
5
+ <block>digital_data_layer_main/ddl</block>
6
+ <name>digital_data_layer_session_data</name>
7
+ <placeholder>DDL_SESSION</placeholder>
8
+ <container>TriggeredMessaging_DigitalDataLayer_Model_Container_Ddl</container>
9
+ </triggeredmessaging_ddl_session_data>
10
+ </placeholders>
11
+ </config>
app/code/community/TriggeredMessaging/DigitalDataLayer/etc/config.xml ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <modules>
4
+ <TriggeredMessaging_DigitalDataLayer>
5
+ <version>0.3.0</version>
6
+ </TriggeredMessaging_DigitalDataLayer>
7
+ </modules>
8
+
9
+ <global>
10
+ <models>
11
+ <digital_data_layer_main>
12
+ <class>TriggeredMessaging_DigitalDataLayer_Model</class>
13
+ </digital_data_layer_main>
14
+ </models>
15
+ <helpers>
16
+ <digital_data_layer_main>
17
+ <class>TriggeredMessaging_DigitalDataLayer_Helper</class>
18
+ </digital_data_layer_main>
19
+ </helpers>
20
+ <blocks>
21
+ <digital_data_layer_main>
22
+ <class>TriggeredMessaging_DigitalDataLayer_Block</class>
23
+ </digital_data_layer_main>
24
+ </blocks>
25
+ </global>
26
+
27
+ <frontend>
28
+ <!-- Layout -->
29
+ <layout>
30
+ <updates>
31
+ <triggered_messaging_digital_data_layer>
32
+ <file>triggeredmessaging/digital_data_layer.xml</file>
33
+ </triggered_messaging_digital_data_layer>
34
+ </updates>
35
+ </layout>
36
+ <events>
37
+ <controller_action_layout_load_before>
38
+ <observers>
39
+ <TriggeredMessaging_DigitalDataLayer_Model_Page_Observer>
40
+ <type>singleton</type>
41
+ <class>digital_data_layer_main/page_observer</class>
42
+ <method>setDigitalDataLayer</method>
43
+ </TriggeredMessaging_DigitalDataLayer_Model_Page_Observer>
44
+ </observers>
45
+ </controller_action_layout_load_before>
46
+ <sales_quote_save_after>
47
+ <observers>
48
+ <triggeredmessaging_digital_data_layer>
49
+ <class>digital_data_layer_main/observer</class>
50
+ <method>registerQuoteChange</method>
51
+ </triggeredmessaging_digital_data_layer>
52
+ </observers>
53
+ </sales_quote_save_after>
54
+ </events>
55
+ </frontend>
56
+
57
+ <!-- Admin Configuration Default Values -->
58
+ <default>
59
+ <triggered_messaging>
60
+ <triggered_messaging_digital_data_layer_enabled>1</triggered_messaging_digital_data_layer_enabled>
61
+ <triggered_messaging_digital_data_layer_debug_enabled>0</triggered_messaging_digital_data_layer_debug_enabled>
62
+ <triggered_messaging_script_enabled>1</triggered_messaging_script_enabled>
63
+ <triggered_messaging_website_id></triggered_messaging_website_id>
64
+ </triggered_messaging>
65
+ </default>
66
+ </config>
app/code/community/TriggeredMessaging/DigitalDataLayer/etc/system.xml ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <tabs>
4
+ <TriggeredMessaging_DigitalDataLayer_tab translate="label">
5
+ <label>W3C Digital Data Layer</label>
6
+ <sort_order>100</sort_order>
7
+ </TriggeredMessaging_DigitalDataLayer_tab>
8
+ </tabs>
9
+
10
+ <sections>
11
+ <triggeredmessaging_digitaldatalayer_configuration translate="label">
12
+ <label>Configuration</label>
13
+ <tab>TriggeredMessaging_DigitalDataLayer_tab</tab>
14
+ <sort_order>0</sort_order>
15
+ <show_in_default>1</show_in_default>
16
+ <show_in_website>1</show_in_website>
17
+ <show_in_store>1</show_in_store>
18
+ <groups>
19
+ <TriggeredMessaging_DigitalDataLayer_Settings translate="label">
20
+ <label>Digital Data Layer Configuration</label>
21
+ <sort_order>1</sort_order>
22
+ <show_in_default>1</show_in_default>
23
+ <show_in_website>1</show_in_website>
24
+ <show_in_store>1</show_in_store>
25
+ <fields>
26
+ <TriggeredMessaging_DigitalDataLayer_Enabled translate="label">
27
+ <label>Enable Digital Data Layer</label>
28
+ <frontend_type>select</frontend_type>
29
+ <source_model>adminhtml/system_config_source_yesno</source_model>
30
+ <config_path>triggered_messaging/triggered_messaging_digital_data_layer_enabled</config_path>
31
+ <comment><![CDATA[ See <a href="http://www.w3.org/2013/12/ceddl-201312.pdf" target="_blank">http://www.w3.org/2013/12/ceddl-201312.pdf</a> for the specification. ]]></comment>
32
+ <sort_order>1</sort_order>
33
+ <show_in_default>1</show_in_default>
34
+ <show_in_website>1</show_in_website>
35
+ <show_in_store>1</show_in_store>
36
+ </TriggeredMessaging_DigitalDataLayer_Enabled>
37
+
38
+ <TriggeredMessaging_DigitalDataLayer_DebugEnabled translate="label">
39
+ <label>Enable Debug Mode</label>
40
+ <frontend_type>select</frontend_type>
41
+ <source_model>adminhtml/system_config_source_yesno</source_model>
42
+ <config_path>triggered_messaging/triggered_messaging_digital_data_layer_debug_enabled</config_path>
43
+ <comment><![CDATA[ Outputs more data about the page to the digitalData layer, for debugging purposes. Should be disabled for production use. ]]></comment>
44
+ <sort_order>2</sort_order>
45
+ <show_in_default>1</show_in_default>
46
+ <show_in_website>1</show_in_website>
47
+ <show_in_store>1</show_in_store>
48
+ <depends>
49
+ <TriggeredMessaging_DigitalDataLayer_Enabled>1</TriggeredMessaging_DigitalDataLayer_Enabled>
50
+ </depends>
51
+ </TriggeredMessaging_DigitalDataLayer_DebugEnabled>
52
+ </fields>
53
+ <expanded>1</expanded>
54
+ </TriggeredMessaging_DigitalDataLayer_Settings>
55
+
56
+ <TriggeredMessaging_Script_Settings translate="label">
57
+ <label>Triggered Messaging Script Configuration (Optional)</label>
58
+ <sort_order>2</sort_order>
59
+ <show_in_default>1</show_in_default>
60
+ <show_in_website>1</show_in_website>
61
+ <show_in_store>1</show_in_store>
62
+ <fields>
63
+ <TriggeredMessaging_Script_Enabled translate="label">
64
+ <label>Enable Script</label>
65
+ <frontend_type>select</frontend_type>
66
+ <source_model>adminhtml/system_config_source_yesno</source_model>
67
+ <config_path>triggered_messaging/triggered_messaging_script_enabled</config_path>
68
+ <comment><![CDATA[ <a href="http://www.triggeredmessaging.com/supported-ecommerce-platforms/magento?utm_campaign=magento_plugin&utm_medium=web&utm_source=magento" target="_blank">More information on Triggered Messaging and Magento</a>. ]]></comment>
69
+ <sort_order>1</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
+ </TriggeredMessaging_Script_Enabled>
74
+
75
+ <TriggeredMessaging_Website_Id translate="label">
76
+ <label>Website ID</label>
77
+ <frontend_type>text</frontend_type>
78
+ <config_path>triggered_messaging/triggered_messaging_website_id</config_path>
79
+ <comment><![CDATA[ You can find your Website ID within your <a href="https://admin.triggeredmessaging.com/" target="_blank">Triggered Messaging admin area</a>. ]]>
80
+ </comment>
81
+ <validate>validate-alphanum</validate>
82
+ <sort_order>2</sort_order>
83
+ <show_in_default>1</show_in_default>
84
+ <show_in_website>1</show_in_website>
85
+ <show_in_store>1</show_in_store>
86
+ <depends>
87
+ <TriggeredMessaging_Script_Enabled>1</TriggeredMessaging_Script_Enabled>
88
+ </depends>
89
+ </TriggeredMessaging_Website_Id>
90
+ </fields>
91
+ <expanded>1</expanded>
92
+ </TriggeredMessaging_Script_Settings>
93
+ </groups>
94
+ </triggeredmessaging_digitaldatalayer_configuration>
95
+
96
+ <triggeredmessaging_digitaldatalayer_support translate="label">
97
+ <label>Support</label>
98
+ <tab>TriggeredMessaging_DigitalDataLayer_tab</tab>
99
+ <sort_order>1</sort_order>
100
+ <show_in_default>1</show_in_default>
101
+ <show_in_website>1</show_in_website>
102
+ <show_in_store>1</show_in_store>
103
+ <groups>
104
+ <TriggeredMessaging_Info_Support translate="label">
105
+ <label>Support Details</label>
106
+ <sort_order>1</sort_order>
107
+ <show_in_default>1</show_in_default>
108
+ <show_in_website>1</show_in_website>
109
+ <show_in_store>1</show_in_store>
110
+ <fields>
111
+ <TriggeredMessaging_Info_Support_Extension translate="label">
112
+ <label>Plugin Support</label>
113
+ <frontend_type>label</frontend_type>
114
+ <comment><![CDATA[ For issues with the extension, create an issue in the github repository: <a href="https://github.com/TriggeredMessaging/digitalDataMagentoExtension/issues" target="_blank">TriggeredMessaging/digitalDataMagentoExtension</a> ]]></comment>
115
+ <sort_order>1</sort_order>
116
+ <show_in_default>1</show_in_default>
117
+ <show_in_website>1</show_in_website>
118
+ <show_in_store>1</show_in_store>
119
+ </TriggeredMessaging_Info_Support_Extension>
120
+ <TriggeredMessaging_Info_Support_Script translate="label">
121
+ <label>Triggered Messaging Support</label>
122
+ <frontend_type>label</frontend_type>
123
+ <comment><![CDATA[ For issues with the Triggered Messaging functionality, contact support: <a href="mailto:hello@triggeredmessaging.com?Subject=Magento%20Extension%20Support%20Request" target="_blank">hello@triggeredmessaging.com</a> ]]></comment>
124
+ <sort_order>2</sort_order>
125
+ <show_in_default>1</show_in_default>
126
+ <show_in_website>1</show_in_website>
127
+ <show_in_store>1</show_in_store>
128
+ </TriggeredMessaging_Info_Support_Script>
129
+ </fields>
130
+ <expanded>1</expanded>
131
+ </TriggeredMessaging_Info_Support>
132
+
133
+ <TriggeredMessaging_Info_Testing translate="label">
134
+ <label>Testing</label>
135
+ <sort_order>2</sort_order>
136
+ <show_in_default>1</show_in_default>
137
+ <show_in_website>1</show_in_website>
138
+ <show_in_store>1</show_in_store>
139
+ <fields>
140
+ <TriggeredMessaging_Info_Testing_Note translate="label">
141
+ <label>Testing</label>
142
+ <frontend_type>label</frontend_type>
143
+ <comment><![CDATA[ You can test that your store is exposing the correct data by entering your URL at <a href="http://digitaldatavalidator.com/" target="_blank">digitalDataValidator.com</a> ]]></comment>
144
+ <sort_order>1</sort_order>
145
+ <show_in_default>1</show_in_default>
146
+ <show_in_website>1</show_in_website>
147
+ <show_in_store>1</show_in_store>
148
+ </TriggeredMessaging_Info_Testing_Note>
149
+ </fields>
150
+ <expanded>1</expanded>
151
+ </TriggeredMessaging_Info_Testing>
152
+ </groups>
153
+ </triggeredmessaging_digitaldatalayer_support>
154
+ </sections>
155
+ </config>
app/design/frontend/base/default/layout/triggeredmessaging/digital_data_layer.xml ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <layout version="1.0.0">
3
+ <default>
4
+ <reference name="head">
5
+ <block type="digital_data_layer_main/ddl" name="digital_data_layer" as="digital_data_layer" template="triggeredmessaging/digital_data_layer.phtml"/>
6
+ </reference>
7
+ </default>
8
+ <tms_block_after_content>
9
+ <reference name="content">
10
+ <block type="digital_data_layer_main/ddl" name="digital_data_layer" as="digital_data_layer" template="triggeredmessaging/digital_data_layer_after_content.phtml"/>
11
+ </reference>
12
+ </tms_block_after_content>
13
+ </layout>
app/design/frontend/base/default/template/triggeredmessaging/digital_data_layer.phtml ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php try { ?>
2
+ <?php
3
+ /* @var $this TriggeredMessaging_DigitalDataLayer_Block_Ddl */
4
+ $triggered_messaging_website_id = Mage::getStoreConfig('triggered_messaging/triggered_messaging_website_id');
5
+ $triggered_messaging_script_enabled = Mage::getStoreConfig('triggered_messaging/triggered_messaging_script_enabled');
6
+ $triggered_messaging_digital_data_layer_enabled = Mage::getStoreConfig('triggered_messaging/triggered_messaging_digital_data_layer_enabled');
7
+
8
+ $observer = 'digital_data_layer_main/page_observer';
9
+ $mage = Mage::getSingleton($observer);
10
+ ?>
11
+ <?php if ($triggered_messaging_digital_data_layer_enabled == 1) :?>
12
+ <?php
13
+ // extract variable only when digital data layer is enabled
14
+ $version = $mage->getVersion();
15
+ $page = $mage->getPage();
16
+ $user = $mage->getUser();
17
+ $product = $mage->getProduct();
18
+ $cart = $mage->getCart();
19
+ $listing = $mage->getListing();
20
+ $transaction = $mage->getTransaction();
21
+ $events = $mage->getEvents();
22
+ ?>
23
+ <?php
24
+ try {
25
+ // Extract things only available within template (and not model)
26
+ if (!$page['pageInfo']) {
27
+ $page['pageInfo'] = array();
28
+ }
29
+ if (!$page['pageInfo']['pageName']) {
30
+ $page['pageInfo']['pageName'] = $this->getLayout()->getBlock('head')->getTitle();
31
+ }
32
+ } catch(Exception $e) {}
33
+ ?>
34
+ <!-- W3C Digital Data Layer Start -->
35
+ <script type="text/javascript">
36
+ try {
37
+ window.digitalData = window.digitalData || {};
38
+ window.digitalData.version = <?php echo json_encode($version); ?>;
39
+ window.digitalData.page = <?php echo json_encode($page); ?>;
40
+ window.digitalData.user = <?php echo json_encode($user); ?>;
41
+ <?php if ($product) : ?>
42
+ window.digitalData.product = <?php echo json_encode($product) ?>;
43
+ <?php endif; ?>
44
+ <?php if ($cart) : ?>
45
+ window.digitalData.cart = <?php echo json_encode($cart) ?>;
46
+ <?php endif; ?>
47
+ <?php if ($listing): ?>
48
+ window.digitalData.listing = <?php echo json_encode($listing) ?>;
49
+ <?php endif; ?>
50
+ <?php if ($transaction): ?>
51
+ window.digitalData.transaction = <?php echo json_encode($transaction) ?>;
52
+ <?php endif; ?>
53
+ <?php if ($events): ?>
54
+ window.digitalData.events = <?php echo json_encode($events); ?>;
55
+ <?php endif; ?>
56
+ } catch(e) {
57
+ }
58
+ </script>
59
+ <!-- W3C Digital Data Layer End -->
60
+ <?php endif; ?>
61
+
62
+ <?php if ($triggered_messaging_script_enabled) :?>
63
+ <!-- Triggered Messaging Script Start -->
64
+ <?php if ($triggered_messaging_website_id) :?>
65
+ <script defer="defer" async="async" src='//d81mfvml8p5ml.cloudfront.net/<?php echo $triggered_messaging_website_id; ?>.js'></script>
66
+ <?php if ($mage->_isConfirmation()) :?>
67
+ <?php $pcqs = $mage->getPurchaseCompleteQs(); ?>
68
+ <img src="//d1f0tbk1v3e25u.cloudfront.net/pc/<?php echo $triggered_messaging_website_id; ?>/?<?php echo $pcqs; ?>" />
69
+ <?php endif;?>
70
+ <?php else: ?>
71
+ <script type="text/javascript">
72
+ console.log("You have enabled the Triggered Messaging script from the Magento Admin Configuration Panel, but have not supplied a website ID. Please contact hello@triggeredmessaging.com if you would like some assistance.");
73
+ </script>
74
+ <?php endif;?>
75
+ <!-- Triggered Messaging Script End -->
76
+ <?php endif;?>
77
+
78
+ <?php } catch (Exception $e) {} ?>
app/design/frontend/base/default/template/triggeredmessaging/digital_data_layer_after_content.phtml ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php try {
2
+
3
+ /* @var $this TriggeredMessaging_DigitalDataLayer_Block_Ddl */
4
+ $triggered_messaging_digital_data_layer_enabled = Mage::getStoreConfig('triggered_messaging/triggered_messaging_digital_data_layer_enabled');
5
+
6
+ // extract variable only when digital data layer is enabled
7
+ if ($triggered_messaging_digital_data_layer_enabled == 1) :
8
+
9
+ $observer = 'digital_data_layer_main/page_observer';
10
+ $mage = Mage::getSingleton($observer);
11
+
12
+ $page = $mage->getPage();
13
+ if (isset($page['category']) &&
14
+ isset($page['category']['pageType'])) {
15
+ switch ($page['category']['pageType']) {
16
+ case 'category':
17
+ case 'search':
18
+ case 'tag':
19
+ break;
20
+ default:
21
+ return;
22
+ }
23
+ }
24
+
25
+ // Grab product list data (from category and search pages)
26
+ $that = Mage::getBlockSingleton('catalog/product_list');
27
+ if (!$that) return;
28
+ $_productCollection = $that->getLoadedProductCollection();
29
+ if (!$_productCollection) return;
30
+ if ($_productCollection->count()===0) return;
31
+
32
+ // Extract products displayed on category page
33
+ // Need to do it after content block as the specified products are loaded then
34
+ $products = array();
35
+ foreach ($_productCollection as $_product) {
36
+ array_push($products, $mage->_getProductModel($_product));
37
+ }
38
+
39
+ $preloaded_product = $mage->getProduct();
40
+ ?>
41
+ <!-- W3C Digital Data Layer Extra Info Start -->
42
+ <script type="text/javascript">
43
+ try {
44
+ <?php if ($products) : ?>
45
+ <?php if (!$preloaded_product) : ?>
46
+ window.digitalData.product = <?php echo json_encode($products) ?>;
47
+ <?php else : ?>
48
+ window.digitalData.product.push.apply(window.digitalData.product, <?php echo json_encode($products) ?>);
49
+ <?php endif; ?>
50
+ <?php endif; ?>
51
+ } catch(e) {
52
+ }
53
+ </script>
54
+ <!-- W3C Digital Data Layer Extra Info End -->
55
+ <?php
56
+ endif;
57
+
58
+ } catch (Exception $e) {} ?>
app/etc/modules/TriggeredMessaging_DigitalDataLayer.xml ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?xml version = "1.0"?>
2
+ <config>
3
+ <modules>
4
+ <TriggeredMessaging_DigitalDataLayer>
5
+ <active>true</active>
6
+ <codePool>community</codePool>
7
+ </TriggeredMessaging_DigitalDataLayer>
8
+ </modules>
9
+ </config>
package.xml ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>W3CDigitalDataLayerByTriggeredMessaging</name>
4
+ <version>0.3.3</version>
5
+ <stability>stable</stability>
6
+ <license uri="http://opensource.org/licenses/apachepl.php">Apache Software License v2</license>
7
+ <channel>community</channel>
8
+ <extends/>
9
+ <summary>Adds the W3C Digital Data Layer spec to your store.</summary>
10
+ <description>Adds the W3C Digital Data Layer to Magento. The Digital Data Layer (DDL) is a standardised way of exposing data on your eCommerce site. If you implement to the specification, it will mean that you will not have to duplicate your work to expose data to multiple providers (e.g. an analytics provider and a tag management provider).&#xD;
11
+ The plugin also allows easy installation of Triggered Messaging scripts in your store.&#xD;
12
+ &#xD;
13
+ Any bugs or support requests should be raised here:&#xD;
14
+ &#xD;
15
+ https://github.com/TriggeredMessaging/digitalDataMagentoExtension/issues</description>
16
+ <notes>All basic features included.&#xD;
17
+ - It can handle full page caching.&#xD;
18
+ - Exception handling has been added.&#xD;
19
+ - Removed empty objects and arrays; Removed duplicated subcategories.&#xD;
20
+ - Added product data for products on category and search pages.&#xD;
21
+ - Fixed issue where configurable products have multiple product entries, and incorrect prices, in the cart object.&#xD;
22
+ - Added more data into digitalData object so websites can be debugged.&#xD;
23
+ - Configurable products have each of their configurations listed under 'linkedProduct'.&#xD;
24
+ - Fixed bug that was breaking some styling.&#xD;
25
+ - Added 'pageName' attribute and fixed type of 'returningStatus' object.&#xD;
26
+ - Tweaks to configuration panel and default values.&#xD;
27
+ - List of all installed non-Magento extensions are exposed (for debug purposes)&#xD;
28
+ - Products associated with a Grouped Product have been included.&#xD;
29
+ - Minimum prices of grouped and bundled products can be extracted.&#xD;
30
+ - Output of debug data can now be controlled through admin configuration panel.</notes>
31
+ <authors><author><name>Muhammed Miah</name><user>momiah1234</user><email>muhammed.miah@triggeredmessaging.com</email></author><author><name>Triggered Messaging</name><user>tmsdemo</user><email>demostore@triggeredmessaging.com</email></author><author><name>Mike Austin</name><user>tmsdemo</user><email>dev@triggeredmessaging.com</email></author></authors>
32
+ <date>2014-09-29</date>
33
+ <time>12:59:57</time>
34
+ <contents><target name="magecommunity"><dir name="TriggeredMessaging"><dir name="DigitalDataLayer"><dir name="Block"><file name="Ddl.php" hash="134766e2a49c3ecfbf03d1025dd41e8b"/></dir><dir name="Helper"><file name="Data.php" hash="5a6e19225e7db056946c95b1477e5c18"/></dir><dir name="Model"><dir name="Container"><file name="Ddl.php" hash="cc9dcd8cf4e460a01c242ac675ce5634"/></dir><file name="Observer.php" hash="26e0dcfc7a19d5310e44e004c144c23e"/><dir name="Page"><file name="Observer.php" hash="2f438462d1372e7e993b301b552ac861"/></dir></dir><dir name="etc"><file name="adminhtml.xml" hash="8b98dee82241e0b69661bf4699cba334"/><file name="cache.xml" hash="1b6658073a8126cd655cb18109559f49"/><file name="config.xml" hash="a29f9fb1250c40a8d973e470ffab46ac"/><file name="system.xml" hash="a72e032172f1a6b05ba969415bf33f63"/></dir></dir></dir></target><target name="magedesign"><dir name="frontend"><dir name="base"><dir name="default"><dir name="template"><dir name="triggeredmessaging"><file name="digital_data_layer.phtml" hash="61001c0d1d3478f9db8366f4386da754"/><file name="digital_data_layer_after_content.phtml" hash="6ee383b4d2011fd7e718b2930b7cd95d"/></dir></dir><dir name="layout"><dir name="triggeredmessaging"><file name="digital_data_layer.xml" hash="3c58b9faec1d9c63e1fb0a3fcc35a2d8"/></dir></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="TriggeredMessaging_DigitalDataLayer.xml" hash="cfc3b1bff54f3e1f224f120e536bc411"/></dir></target></contents>
35
+ <compatible/>
36
+ <dependencies><required><php><min>5.2.0</min><max>6.0.0</max></php></required></dependencies>
37
+ </package>