W3CDigitalDataLayerByTriggeredMessaging - Version 0.3.9

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.9
Comparing to
See all releases


Code changes from version 0.3.8 to 0.3.9

app/code/community/TriggeredMessaging/DigitalDataLayer/Model/Page/Observer.php CHANGED
@@ -1,844 +1,764 @@
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
- public function getCurrentPrice($_price,$p1,$p2){
166
- if($p1){
167
- return number_format(Mage::helper('core')->currency((float)$_price,$p1,$p2),2,'.','');
168
- } else {
169
- return floatval(number_format(Mage::helper('core')->currency((float)$_price,$p1,$p2),2,'.',''));
170
- }
171
- }
172
- public function getVersion() {
173
- return $this->_version;
174
- }
175
-
176
- public function getPurchaseCompleteQs() {
177
-
178
- $orderId = $this->_getCheckoutSession()->getLastOrderId();
179
- if ($orderId) {
180
- $order = $this->_getSalesOrder()->load($orderId);
181
- $email = $order->getCustomerEmail();
182
- }else{
183
- $email = $user->getEmail();
184
  }
185
- $qs = "e=" . urlencode($email);
186
 
187
- if($orderId){
188
- $qs = $qs . "&r=" . urlencode($orderId);
 
189
  }
190
 
191
- return $qs;
192
- }
193
-
194
- public function getUser() {
195
- return $this->_user;
196
- }
197
-
198
- public function getPage() {
199
- return $this->_page;
200
- }
201
-
202
- public function getProduct() {
203
- return $this->_product;
204
- }
205
-
206
- public function getCart() {
207
- return $this->_cart;
208
- }
209
-
210
- public function getTransaction() {
211
- return $this->_transaction;
212
- }
213
-
214
- public function getListing() {
215
- return $this->_listing;
216
- }
217
-
218
- public function getEvents() {
219
- return array();
220
- }
221
-
222
-
223
- /*
224
- * Set the model attributes to be passed front end
225
- */
226
-
227
- public function _getPageType() {
228
- try {
229
- if ($this->_isHome()) {
230
- return 'home';
231
- } elseif ($this->_isContent()) {
232
- return 'content';
233
- } elseif ($this->_isCategory()) {
234
- return 'category';
235
- } elseif ($this->_isSearch()) {
236
- return 'search';
237
- } elseif ($this->_isProduct()) {
238
- return 'product';
239
- } elseif ($this->_isCart()) {
240
- return 'basket';
241
- } elseif ($this->_isCheckout()) {
242
- return 'checkout';
243
- } elseif ($this->_isConfirmation()) {
244
- return 'confirmation';
245
- } else {
246
- return $this->_getModuleName();
247
- }
248
- } catch (Exception $e) {
249
  }
250
- }
251
 
252
- public function _getPageBreadcrumb() {
253
- $arr = $this->_getBreadcrumb();
254
- $breadcrumb = array();
 
255
 
256
- try {
257
- foreach ($arr as $category) {
258
- $breadcrumb[] = $category['label'];
259
- }
260
- } catch (Exception $e) {
261
  }
262
 
263
- return $breadcrumb;
264
- }
 
 
265
 
266
- public function _setPage() {
267
  /*
268
- Section 6.3 of http://www.w3.org/2013/12/ceddl-201312.pdf
269
- page: {
270
- pageInfo: {
271
- pageID: "Great Winter Run 2015",
272
- destinationURL: "http://www.greatrun.org/Events/Event.aspx?id=2"},
273
- category: {
274
- primaryCategory: "Cameras",
275
- subCategory1: "Nikon",
276
- pageType: "product"
277
- }
278
  */
279
 
280
- try {
281
- $this->_page = array();
282
-
283
- $this->_page['pageInfo'] = array();
284
- // $this->_page['pageInfo']['pageID']
285
- $this->_page['pageInfo']['pageName'] = '';
286
- $this->_page['pageInfo']['destinationURL'] = Mage::helper('core/url')->getCurrentUrl();
287
- $referringURL = Mage::app()->getRequest()->getServer('HTTP_REFERER');
288
- if ($referringURL) {
289
- $this->_page['pageInfo']['referringURL'] = $referringURL;
290
- }
291
- // $this->_page['pageInfo']['sysEnv']
292
- // $this->_page['pageInfo']['variant']
293
- if ($this->_getPageBreadcrumb()) {
294
- $this->_page['pageInfo']['breadcrumbs'] = $this->_getPageBreadcrumb();
295
- }
296
- // $this->_page['pageInfo']['author']
297
- // $this->_page['pageInfo']['issueDate']
298
- // $this->_page['pageInfo']['effectiveDate']
299
- // $this->_page['pageInfo']['expiryDate']
300
- $this->_page['pageInfo']['language'] = Mage::app()->getLocale()->getLocaleCode();
301
- // $this->_page['pageInfo']['geoRegion']
302
- // $this->_page['pageInfo']['industryCodes']
303
- // $this->_page['pageInfo']['publisher']
304
-
305
- $this->_page['category'] = array();
306
- if (Mage::registry('current_category')) {
307
- // There must be a better way than this
308
- $this->_page['category']['primaryCategory'] = Mage::registry('current_category')->getName();
309
- }
310
- // $this->_page['category']['subCategory1'];
311
- $this->_page['category']['pageType'] = $this->_getPageType();
312
-
313
- // $this->_page['attributes'] = array();
314
-
315
- if ($this->_debug) {
316
- $modules = (array)Mage::getConfig()->getNode('modules')->children();
317
- $mods = array();
318
- foreach ($modules as $key => $value) {
319
- if (strpos($key, 'Mage_')===false) {
320
- $mods[] = $key;
321
 
322
- }
 
 
 
 
 
323
  }
324
- $this->_page['extra_modules'] = $mods;
325
- }
326
 
327
- } catch (Exception $e) {
 
 
 
 
 
 
328
  }
329
- }
330
 
331
- // Set the user info
332
- public function _setUser() {
333
- // Section 6.9 of http://www.w3.org/2013/12/ceddl-201312.pdf
 
 
 
 
 
334
 
335
- try {
336
- $this->_user = array();
337
- $user = $this->_getCustomer();
338
- $user_id = $user->getEntityId();
339
- $firstName = $user->getFirstname();
340
- $lastName = $user->getLastname();
341
- $userGroup = Mage::getModel('customer/group')->load(Mage::getSingleton('customer/session')->getCustomerGroupId());
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
342
 
343
- if ($this->_isConfirmation()) {
344
  $orderId = $this->_getCheckoutSession()->getLastOrderId();
345
  if ($orderId) {
346
- $order = $this->_getSalesOrder()->load($orderId);
347
- $email = $order->getCustomerEmail();
 
 
 
 
 
 
 
348
  }
349
- } else {
350
- $email = $user->getEmail();
351
- }
352
-
353
- $this->_user['profile'] = array();
354
-
355
- $profile = array();
356
-
357
- $profile['profileInfo'] = array();
358
- if ($user_id) {
359
- $profile['profileInfo']['profileID'] = (string) $user_id;
360
- }
361
- if ($firstName){
362
- $profile['profileInfo']['userFirstName'] = $firstName;
363
- }
364
- if ($lastName){
365
- $profile['profileInfo']['userLastName'] = $lastName;
366
- }
367
- if ($email) {
368
- $profile['profileInfo']['email'] = $email;
369
- }
370
- $profile['profileInfo']['language'] = Mage::getStoreConfig('general/locale/code', Mage::app()->getStore()->getId());
371
- $profile['profileInfo']['returningStatus'] = $user_id ? 'true' : 'false';
372
- if($userGroup && $this->_userGroupExp){
373
- $profile['profileInfo']['segment']['userGroupId'] = $userGroup->getData('customer_group_id');
374
- $profile['profileInfo']['segment']['userGroup'] = $userGroup->getData('customer_group_code');
375
- }
376
-
377
- // $profile['address'] = array();
378
- // $profile['address']['line1'];
379
- // $profile['address']['line2'];
380
- // $profile['address']['city'];
381
- // $profile['address']['stateProvince'];
382
- // $profile['address']['postalCode'];
383
- // $profile['address']['country'];
384
-
385
- // $profile['social'] = array();
386
- // $profile['attributes'] = array();
387
-
388
- array_push($this->_user['profile'], $profile);
389
- } catch (Exception $e) {
390
  }
391
- }
392
 
393
- public function _getAddress($address) {
 
 
 
 
 
 
 
 
 
 
394
  /*
395
- address: {
396
- line1: "",
397
- line2: "",
398
- city: "",
399
- stateProvince: "",
400
- postalCode: "",
401
- country: ""
402
- },
403
  */
404
 
405
- $billing = array();
406
-
407
- try {
408
- if ($address) {
409
- $billing['line1'] = $address->getName();
410
- $billing['line2'] = $address->getStreetFull();
411
- $billing['city'] = $address->getCity();
412
- $billing['postalCode'] = $address->getPostcode();
413
- $billing['country'] = $address->getCountry();
414
- $state = $address->getRegion();
415
- $billing['stateProvince'] = $state ? $state : '';
416
- }
417
- } catch (Exception $e) {
 
 
 
 
 
 
 
 
 
 
 
418
  }
419
 
420
- return $billing;
421
- }
 
 
422
 
423
- public function _getProductStock($product) {
424
- return (int) Mage::getModel('cataloginventory/stock_item')->loadByProduct($product)->getQty();
425
- }
 
 
 
426
 
427
- public function _getCurrency() {
428
- return Mage::app()->getStore()->getCurrentCurrencyCode();
429
- }
430
 
431
- public function _getProductModel($product, $_page_) {
432
- /*
433
- Section 6.4 of http://www.w3.org/2013/12/ceddl-201312.pdf
434
- product: [
435
- {
436
- productInfo: {
437
- productID: "greatwinterrun2015", // Unique product ID
438
- productName: "Great Winter Run",
439
- description: "Running, in the winter, in Edinburgh",
440
- productURL: "http://www.greatrun.org/Events/Event.aspx?id=2",
441
- productImage: "http://www.greatrun.org/Events/App_Images/slideshow/cropped/saved/gwir_01f0a780d94.jpg",
442
- productThumbnail: "http://www.greatrun.org/App_Images/2011/Events/logo_GWIR.jpg"
443
- },
444
  category: {
445
- primaryCategory: "Somecategory",
446
- subCategory1: "Subcat"
447
- },
448
- attributes: {
449
- distance: 5000,
450
- country: "Scotland",
451
- date: "2015-01-11", // Dates in ISO 8601
452
- city: "Edinburgh" // You can put any extended data you want passing through in attributes
453
- },
454
- price: {
455
- basePrice: 40.00,
456
- currency: "GBP",
457
- taxRate: 0.2,
458
- priceWithTax: 48.00
459
  }
460
- }
461
- ]
462
- */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
463
 
464
- $product_model = array();
465
- $options = array();
466
- //If there is optional data then add it
467
- if($_page_==='cart'){
468
- $opt = $product->getProduct()->getTypeInstance(true)->getOrderOptions($product->getProduct());
469
- if(isset($opt['attributes_info'])){
470
- foreach($opt['attributes_info'] as $attribute){
471
- $options[$attribute['label']] = $attribute['value'];
 
 
472
  }
 
473
  }
474
- $productId = $product->getProductId();
475
- $product = $this->_getProduct($productId);
476
- }
477
- if($_page_==='list'){
478
- $productId = $product->getId();
479
- $product = $this->_getProduct($productId);
480
  }
481
- try {
482
- // Product Info
483
- $product_model['productInfo'] = array();
484
- $product_model['productInfo']['productID'] = $product->getId();
485
- $product_model['productInfo']['productName'] = $product->getName();
486
- $product_model['productInfo']['description'] = strip_tags($product->getShortDescription());
487
- $product_model['productInfo']['productURL'] = $product->getProductUrl();
488
-
489
- //Check if images contain placeholders
490
- if($product->getImage() && $product->getImage()!=="no_selection"){
491
- $product_model['productInfo']['productImage'] = $product->getImageUrl();
492
- }
493
- if($product->getThumbnail() && $product->getThumbnail()!=="no_selection"){
494
- $product_model['productInfo']['productThumbnail'] = $product->getThumbnailUrl();
495
- }
496
- //Attributes
497
- if ($product->getWeight() && in_array('weight',$this->_expAttr)) {
498
- $product_model['attributes']['weight'] = floatval($product->getWeight());
499
- }
500
- try{
501
- $attributes = Mage::getSingleton('eav/config')->getEntityType(Mage_Catalog_Model_Product::ENTITY)->getAttributeCollection();
502
- foreach($attributes as $attr){
503
- $infoLocation = 'none';
504
- $attrCode = $attr->getAttributecode();
505
- if($attrCode==='color'||$attrCode==='manufacturer'||$attrCode==='size'){
506
- $infoLocation = 'productInfo';
507
- } elseif($attr->getData('is_user_defined')) {
508
- $infoLocation = 'attributes';
509
- }
510
- if($infoLocation!=='none' && in_array($attrCode,$this->_expAttr)){
511
- if($attr->getData('frontend_class')==='validate-number'){
512
- if($attr->getFrontend()->getValue($product)!=='No'){
513
- $product_model[$infoLocation][$attrCode] = floatval($attr->getFrontend()->getValue($product));
514
- }
515
- } elseif($attr->getData('frontend_class')==='validate-digits'){
516
- if($attr->getFrontend()->getValue($product)!=='No'){
517
- $product_model[$infoLocation][$attrCode] = intval($attr->getFrontend()->getValue($product));
518
- }
519
- } else {
520
- if($product->getAttributeText($attrCode)){
521
- $product_model[$infoLocation][$attrCode] = $product->getAttributeText($attrCode);
522
- }
523
- }
524
- }
525
- }
526
- //Add the options captured earlier
527
- if(count($options)){
528
- $product_model['attributes']['options'] = $options;
529
- }
530
- } catch(Exception $e){
531
- }
532
- // Category
533
- // Iterates through all categories, checking for duplicates
534
- $allcategories = $this->_getProductCategories($product);
535
- if($allcategories){
536
- $catiterator = 0;
537
- $setCategories = array();
538
- foreach($allcategories as $cat){
539
- if($catiterator===0){
540
- $product_model['category']['primaryCategory'] = $cat;
541
- $catiterator++;
542
-
543
- } else {
544
- if(!in_array($cat, $setCategories)){
545
- $product_model['category']["subCategory$catiterator"] = $cat;
546
- $catiterator++;
547
- }
548
- }
549
- array_push($setCategories, $cat);
550
- }
551
- if($product->getTypeID()){
552
- $product_model['category']['productType'] = $product->getTypeID();
553
- }
554
- }
555
-
556
- // Price
557
- $product_model['price'] = array();
558
- if(!$product->getSpecialPrice() || $product->getSpecialPrice()!==$product->getFinalPrice()){
559
- $product_model['price']['basePrice'] = $this->getCurrentPrice(floatval($product->getPrice()),false,false);
560
- } else {
561
- $product_model['price']['basePrice'] = $this->getCurrentPrice(floatval($product->getSpecialPrice()),false,false);
562
- $product_model['price']['regularPrice'] = $this->getCurrentPrice(floatval($product->getPrice()),false,false);
563
- }
564
- $product_model['price']['currency'] = $this->_getCurrency();
565
- $product_model['price']['priceWithTax'] = $this->getCurrentPrice(floatval($product->getFinalPrice()),false,false);
566
-
567
- if (!$product_model['price']['priceWithTax']) {
568
- unset( $product_model['price']['priceWithTax'] );
569
- }
570
-
571
- // In case 'basePrice' did not exist
572
- if (!$product_model['price']['basePrice']) {
573
- $product_model['price']['basePrice'] = $this->getCurrentPrice(floatval($product->getGroupPrice()),false,false);
574
- }
575
- if (!$product_model['price']['basePrice']) {
576
- $product_model['price']['basePrice'] = $this->getCurrentPrice(floatval($product->getMinimalPrice()),false,false);
577
- }
578
- if (!$product_model['price']['basePrice']) {
579
- $product_model['price']['basePrice'] = $this->getCurrentPrice(floatval($product->getSpecialPrice()),false,false);
580
- }
581
- if (!$product_model['price']['basePrice']) {
582
- // Extract price for bundle products
583
- $price_model = $product->getPriceModel();
584
- if (method_exists($price_model, 'getOptions')) {
585
- $normal_price = 0.0;
586
- $_options = $price_model->getOptions($product);
587
- foreach($_options as $_option) {
588
- if (!method_exists($_option, 'getDefaultSelection')) {
589
- break;
590
  }
591
- $_selection = $_option->getDefaultSelection();
592
- if ($_selection === null) continue;
593
- $normal_price += $this->getCurrentPrice(floatval($_selection->getPrice()),false,false);
594
- }
595
- $product_model['price']['basePrice'] = $normal_price;
596
- }
597
- }
598
-
599
- if ($this->_debug) {
600
- $product_model['price']['all'] = array();
601
- $product_model['price']['all']['getPrice'] = $this->getCurrentPrice($product->getPrice(),false,false);
602
- $product_model['price']['all']['getMinimalPrice'] = $this->getCurrentPrice($product->getMinimalPrice(),false,false);
603
- $product_model['price']['all']['getPriceModel'] = $product->getPriceModel();
604
- $product_model['price']['all']['getGroupPrice'] = $this->getCurrentPrice($product->getGroupPrice(),false,false);
605
- $product_model['price']['all']['getTierPrice'] = $product->getTierPrice();
606
- $product_model['price']['all']['getTierPriceCount'] = $product->getTierPriceCount();
607
- $product_model['price']['all']['getFormatedTierPrice'] = $product->getFormatedTierPrice();
608
- $product_model['price']['all']['getFormatedPrice'] = $product->getFormatedPrice();
609
- $product_model['price']['all']['getFinalPrice'] = $this->getCurrentPrice($product->getFinalPrice(),false,false);
610
- $product_model['price']['all']['getCalculatedFinalPrice'] = $product->getCalculatedFinalPrice();
611
- $product_model['price']['all']['getSpecialPrice'] = $this->getCurrentPrice($product->getSpecialPrice(),false,false);
612
- }
613
-
614
- // Calculate Tax Rate
615
- $store = Mage::app()->getStore('default');
616
- $taxCalculation = Mage::getModel('tax/calculation');
617
- $request = $taxCalculation->getRateRequest(null, null, null, $store);
618
- $taxClassId = $product->getTaxClassId();
619
- $percent = $taxCalculation->getRate($request->setProductClassId($taxClassId));
620
- $product_model['price']['taxRate'] = ((float) $percent) / 100;
621
-
622
- // For configurable/grouped/composite products, add all associated products to 'linkedProduct'
623
- if($_page_==='product'){
624
- if ($product->isConfigurable() || $product->isGrouped() || $product->isComposite()) {
625
-
626
- $product_model['linkedProduct'] = array();
627
- $simple_collection = array();
628
-
629
- // Add simple products related to configurable products
630
- if ($product->isConfigurable()) {
631
- $conf = Mage::getModel('catalog/product_type_configurable')->setProduct($product);
632
- $simple_collection = $conf->getUsedProductCollection()->addAttributeToSelect('*')->addFilterByRequiredOptions();
633
- }
634
- else {
635
- $type_instance = $product->getTypeInstance(true);
636
- if (method_exists($type_instance, 'getSelectionsCollection')) {
637
- // Add simple products related to bundle products
638
- $simple_collection = $type_instance->getSelectionsCollection(
639
- $type_instance->getOptionsIds($product), $product
640
- );
641
- } else if (method_exists($type_instance, 'getAssociatedProducts')) {
642
- // Add simple products related to grouped products
643
- $simple_collection = $type_instance->getAssociatedProducts($product);
644
- }
645
- }
646
 
647
- // Add related products to the data layer
648
- $min_price = 0.0;
649
- foreach($simple_collection as $simple_product){
650
- array_push($product_model['linkedProduct'], $this->_getProductModel($simple_product, false));
651
- $simple_product_price = $this->getCurrentPrice(floatval($simple_product->getPrice()),false,false);
652
- if ($simple_product_price && (!$min_price || $simple_product_price<$min_price)) {
653
- $min_price = $simple_product_price;
654
- }
655
- }
656
 
657
- // If price could not be extracted before, can set it now
658
- if (!$product_model['price']['basePrice']) {
659
- $product_model['price']['basePrice'] = $this->getCurrentPrice(floatval($min_price),false,false);
660
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
661
 
662
- if (!$product_model['linkedProduct']) {
663
- unset($product_model['linkedProduct']);
 
 
 
 
 
 
 
 
 
 
 
664
  }
665
- }
666
- }
667
-
668
- if ($this->_debug) {
669
- $product_model['more']['isConfigurable'] = $product->isConfigurable();
670
- $product_model['more']['isSuperGroup'] = $product->isSuperGroup();
671
- $product_model['more']['isSuperConfig'] = $product->isSuperConfig();
672
- $product_model['more']['isGrouped'] = $product->isGrouped();
673
- $product_model['more']['isSuper'] = $product->isSuper();
674
- $product_model['more']['isVirtual'] = $product->isVirtual();
675
- $product_model['more']['isRecurring'] = $product->isRecurring();
676
- $product_model['more']['isComposite'] = $product->isComposite();
677
- $product_model['more']['getTypeId'] = $product->getTypeId();
678
- $product_model['more']['getImage'] = $product->getImage();
679
- $product_model['more']['getImageURL'] = $product->getImageUrl();
680
- $product_model['more']['getSmallImage'] = $product->getSmallImage();
681
- $product_model['more']['getSmallImageURL'] = $product->getSmallImageUrl();
682
- $product_model['more']['getThumbnail'] = $product->getThumbnail();
683
- $product_model['more']['getThumbnailURL'] = $product->getThumbnailUrl();
684
- }
685
-
686
- // Other
687
- // $product_model['attributes'] = array();
688
-
689
- // The following are not used in W3C DDL but exist in Universal Variable:
690
- // $product_model['sku_code'] = $product->getSku();
691
- // $product_model['stock'] = (int) $this->_getProductStock($product);
692
- } catch (Exception $e) {
693
  }
694
 
695
- return $product_model;
696
- }
697
-
698
- public function _getProductCategories($product) {
699
- try {
700
- $cats = $product->getCategoryIds();
701
- if ($cats) {
702
- $category_names = array();
703
- foreach ($cats as $category_id) {
704
- $_cat = $this->_getCategory($category_id);
705
- $category_names[] = $_cat->getName();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
706
  }
707
- return $category_names;
708
- }
709
- } catch (Exception $e) {
710
  }
711
 
712
- return false;
713
- }
 
 
714
 
715
- public function _getLineItems($items, $page_type) {
716
- /*
717
- item: [
718
- {
719
- productInfo: {
720
- productID: "greatwinterrun2015", // Unique product ID - links the prod in the cart to the one browsed
721
- // If data isn't available on the cart page, as long as the productID is present, it will be filled
722
- // out from our product database (e.g. name, images, category, extra attributes, price)
723
- productName: "Great Winter Run",
724
- description: "Running, in the winter, in Edinburgh",
725
- productURL: "http://www.greatrun.org/Events/Event.aspx?id=2",
726
- productImage: "http://www.greatrun.org/Events/App_Images/slideshow/cropped/saved/gwir_01f0a780d94.jpg",
727
- productThumbnail: "http://www.greatrun.org/App_Images/2011/Events/logo_GWIR.jpg"
728
- },
729
- category: {
730
- primaryCategory: "Somecategory",
731
- subCategory1: "Subcat"
732
- },
733
- attributes: {
734
- distance: 5000,
735
- country: "Scotland",
736
- date: "2015-01-11", // Dates in ISO 8601
737
- city: "Edinburgh" // You can put any extended data you want passing through in attributes
738
- },
739
- price: {
740
- basePrice: 40.00,
741
- currency: "GBP",
742
- taxRate: 0.2,
743
- priceWithTax: 48.00
 
 
 
 
 
 
744
  }
745
- }
746
- ]
747
- */
748
 
749
- $line_items = array();
750
- try {
751
- foreach($items as $item) {
752
- $productId = $item->getProductId();
753
- $product = $this->_getProduct($productId);
754
-
755
- $parentIds = Mage::getModel('catalog/product_type_grouped')->getParentIdsByChild($productId);
756
- if($parentIds[0]){
757
- $litem_model = $this->_getProductModel($product, 'linked');
758
- $litem_model['linkedProduct'] = array();
759
- array_push($litem_model['linkedProduct'], $this->_getProductModel($this->_getProduct($parentIds[0]), 'linked'));
760
- } else {
761
- $litem_model = $this->_getProductModel($item, 'cart');
762
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
763
 
764
- if ($page_type === 'cart') {
765
- $litem_model['quantity'] = floatval($item->getQty());
766
- } else {
767
- $litem_model['quantity'] = floatval($item->getQtyOrdered());
768
- }
 
 
 
 
 
769
 
770
- if (!is_array($litem_model['price'])) {
771
- $litem_model['price'] = array();
772
- }
773
- if ($item->getCouponCode()) {
774
- $litem_model['price']['voucherCode'] = $item->getCouponCode();
775
- }
776
- if ($item->getDiscountAmount()) {
777
- $litem_model['price']['voucherDiscount'] = abs(floatval($item->getDiscountAmount()));
778
- }
779
- // $litem_model['price']['shipping'];
780
- // $litem_model['price']['shippingMethod'] = $this->_extractShippingMethod($item->getQuote());
781
- $litem_model['price']['priceWithTax'] = $this->getCurrentPrice(floatval($item->getBasePriceInclTax()),false,false); // TODO: This may be different from that in _getProductModel()
782
- $litem_model['price']['cartTotal'] = floatval($item->getRowTotalInclTax());
783
-
784
- if ($this->_debug) {
785
- $litem_model['price']['all']['_getCalculationPrice'] = $this->getCurrentPrice($product->getCalculationPrice(),false,false);
786
- $litem_model['price']['all']['_getCalculationPriceOriginal'] = $this->getCurrentPrice($product->getCalculationPriceOriginal(),false,false);
787
- $litem_model['price']['all']['_getBaseCalculationPrice'] = $this->getCurrentPrice($product->getBaseCalculationPrice(),false,false);
788
- $litem_model['price']['all']['_getBaseCalculationPriceOriginal'] = $this->getCurrentPrice($product->getBaseCalculationPriceOriginal(),false,false);
789
- $litem_model['price']['all']['_getOriginalPrice'] = $this->getCurrentPrice($product->getOriginalPrice(),false,false);
790
- $litem_model['price']['all']['_getBaseOriginalPrice'] = $this->getCurrentPrice($product->getBaseOriginalPrice(),false,false);
791
- $litem_model['price']['all']['_getConvertedPrice'] = $this->getCurrentPrice($product->getConvertedPrice(),false,false);
792
- }
793
 
794
- // $litem_model['attributes'] = array();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
795
 
796
- array_push($line_items, $litem_model);
797
- }
798
- } catch (Exception $e) {
799
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
800
 
801
- return $line_items;
802
- }
803
-
804
- public function _setListing() {
805
- try {
806
- $this->_listing = array();
807
- if ($this->_isCategory()) {
808
- $category = $this->_getCurrentCategory();
809
- } elseif ($this->_isSearch()) {
810
- $category = $this->_getCatalogSearch();
811
- if (isset($_GET['q'])) {
812
- $this->_listing['query'] = $_GET['q'];
 
 
 
 
 
 
 
 
 
 
 
 
 
813
  }
814
- }
815
 
816
- // Note: data on products are retrieved later, after the content layout block,
817
- // since the product list is compiled then.
818
- } catch (Exception $e) {
819
  }
820
- }
821
-
822
- public function _setProduct() {
823
- try {
824
- $product = $this->_getCurrentProduct();
825
- if (!$product) return false;
826
- $this->_product = array();
827
- array_push($this->_product, $this->_getProductModel($product,'product'));
828
- } catch (Exception $e) {
 
 
 
 
 
 
 
 
829
  }
830
- }
831
 
832
- public function _setCart() {
833
- /*
834
- Section 6.5 of http://www.w3.org/2013/12/ceddl-201312.pdf
835
- cart: {
836
- price: {
837
- basePrice: 40.00,
838
- currency: "GBP",
839
- taxRate: 0.2,
840
- cartTotal: 48.00
841
- },
842
  item: [
843
  {
844
  productInfo: {
@@ -869,260 +789,394 @@ class TriggeredMessaging_DigitalDataLayer_Model_Page_Observer {
869
  }
870
  }
871
  ]
872
- }
873
- */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
874
 
875
- try {
876
- $basket = $this->_getCheckoutSession();
877
-
878
- if (!isset($basket)) {
879
- return;
880
- }
881
-
882
- $cart = array();
883
- $quote = $basket->getQuote();
884
- // Set normal params
885
- $cart_id = $basket->getQuoteId();
886
- if ($cart_id) {
887
- $cart['cartID'] = (string) $cart_id;
888
- }
889
- $cart['price'] = array();
890
- if($quote->getBaseSubtotal()){
891
- $cart['price']['basePrice'] = $this->getCurrentPrice(floatval($quote->getBaseSubtotal()),false,false);
892
- } else {
893
- $cart['price']['basePrice'] = 0.0;
894
- }
895
- if ($quote->getShippingAddress()->getCouponCode()) {
896
- $cart['price']['voucherCode'] = $quote->getShippingAddress()->getCouponCode();
897
- }
898
- if ($quote->getShippingAddress()->getDiscountAmount()) {
899
- $cart['price']['voucherDiscount'] = abs((float) $quote->getShippingAddress()->getDiscountAmount());
900
- }
901
- $cart['price']['currency'] = $this->_getCurrency();
902
- if($cart['price']['basePrice'] > 0.0){
903
- $taxRate = (float) $quote->getShippingAddress()->getTaxAmount() / $cart['price']['basePrice'];
904
- $cart['price']['taxRate'] = round($taxRate, 3); // TODO: Find a better way
905
- }
906
- if ($quote->getShippingAmount()) {
907
- $cart['price']['shipping'] = (float) $quote->getShippingAmount();
908
- }
909
- if ($this->_extractShippingMethod($quote)) {
910
- $cart['price']['shippingMethod'] = $this->_extractShippingMethod($quote);
911
- }
912
- if ($quote->getShippingAddress()->getTaxAmount() && $quote->getBaseSubtotal()){
913
- $cart['price']['priceWithTax'] = (float) $quote->getShippingAddress()->getTaxAmount() + $this->getCurrentPrice($quote->getBaseSubtotal(),false,false);
914
- } else {
915
- $cart['price']['priceWithTax'] = 0.0;
916
- }
917
- if($quote->getGrandTotal() && $quote->getGrandTotal()<0){
918
- $cart['price']['cartTotal'] = (float) $quote->getGrandTotal();
919
- } else {
920
- $cart['price']['cartTotal'] = 0.0;
921
- }
922
- // $cart['attributes'] = array();
923
- if ($cart['price']['basePrice']===0.0&&$cart['price']['cartTotal']===0.0&&$cart['price']['priceWithTax']===0.0) {
924
- unset($cart['price']);
925
- }
926
-
927
- // Line items
928
- $items = $quote->getAllVisibleItems();
929
- if (!$items && isset($cart['price'])) {
930
- if ($this->_debug) {
931
- $cart['price']['testLog'] = "Second method used to retrieve cart items.";
932
  }
933
 
934
- // In case items were not retrieved for some reason
935
- $cartHelper = Mage::helper('checkout/cart');
936
- $items = $cartHelper->getCart()->getItems();
937
- }
938
- $cart['items'] = $this->_getLineItems($items, 'cart');
939
- if (empty($cart['items'])) {
940
- unset($cart['items']);
941
- }
942
-
943
- // The following are not used in W3C DDL but exist in Universal Variable:
944
- // $cart['subtotal'] = (float) $quote->getSubtotal();
945
- // $cart['tax'] = (float) $quote->getShippingAddress()->getTaxAmount();
946
- // $cart['subtotal_include_tax'] = (boolean) $this->_doesSubtotalIncludeTax($quote, $cart['tax']);
947
-
948
- if ($cart_id||isset($cart['items'])||isset($cart['price'])) {
949
- $this->_cart = $cart;
950
- }
951
- } catch (Exception $e) {
952
  }
953
- }
954
 
955
- public function _doesSubtotalIncludeTax($order, $tax) {
956
- /*
957
- Conditions:
958
- - if tax is zero, then set to false
959
- - Assume that if grand total is bigger than total after subtracting shipping, then subtotal does NOT include tax
960
- */
961
- try {
962
- $grandTotalWithoutShipping = $order->getGrandTotal() - $order->getShippingAmount();
963
- if ($tax === 0 || $grandTotalWithoutShipping > $order->getSubtotal()) {
964
- return false;
965
- } else {
966
- return true;
967
- }
968
- } catch (Exception $e) {
 
 
 
969
  }
970
- }
971
 
972
- public function _extractShippingMethod($order) {
973
- try {
974
- $shipping_method = $order->getShippingMethod();
975
- } catch (Exception $e) {
 
 
 
 
 
976
  }
977
- return $shipping_method ? $shipping_method : '';
978
- }
979
 
980
- public function _setTransaction() {
981
- /*
982
- Spec:
983
- transaction {
984
- transactionID: "",
985
- profile: {
986
- profileInfo: {
987
- profileID: "",
988
- userName: "",
989
- email: ""
990
- },
991
- address: {
992
- line1: "",
993
- line2: "",
994
- city: "",
995
- stateProvince: "",
996
- postalCode: "",
997
- country: ""
998
- },
999
- shippingAddress: {
1000
- line1: "",
1001
- line2: "",
1002
- city: "",
1003
- stateProvince: "",
1004
- postalCode: "",
1005
- country: ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1006
  }
1007
- },
1008
- total: {
1009
- basePrice: 0,
1010
- voucherCode: "",
1011
- voucherDiscount: 0,
1012
- currency: "USD",
1013
- taxRate: 0,
1014
- shipping: 0,
1015
- shippingMethod: "",
1016
- priceWithTax: 0,
1017
- transactionTotal: 0
1018
- },
1019
- attributes: {},
1020
- item: [
1021
- {
1022
- productInfo
1023
- category
1024
- quantity
1025
- price
1026
- linkedProduct
1027
- attributes
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1028
  }
1029
- ]
1030
- }
1031
- */
1032
 
1033
- try {
1034
- $orderId = $this->_getCheckoutSession()->getLastOrderId();
1035
- if ($orderId) {
1036
- $transaction = array();
1037
- $order = $this->_getSalesOrder()->load($orderId);
1038
-
1039
- // Get general details
1040
- $transaction['transactionID'] = $order->getIncrementId();
1041
- $transaction['total'] = array();
1042
- $transaction['total']['currency'] = $this->_getCurrency();
1043
- $transaction['total']['basePrice'] = (float) $order->getSubtotal();
1044
- // $transaction['tax'] = (float) $order->getTaxAmount();
1045
- // $transaction['subtotal_include_tax'] = $this->_doesSubtotalIncludeTax($order, $transaction['tax']);
1046
- // $transaction['payment_type'] = $order->getPayment()->getMethodInstance()->getTitle();
1047
- $transaction['total']['transactionTotal'] = (float) $order->getGrandTotal();
1048
-
1049
- $voucher = $order->getCouponCode();
1050
- $transaction['total']['voucherCode'] = $voucher ? $voucher : "";
1051
- $voucher_discount = -1 * $order->getDiscountAmount();
1052
- $transaction['total']['voucherDiscount'] = $voucher_discount ? $voucher_discount : 0;
1053
-
1054
- $transaction['total']['shipping'] = (float) $order->getShippingAmount();
1055
- $transaction['total']['shippingMethod'] = $this->_extractShippingMethod($order);
1056
-
1057
- // Get addresses
1058
- $transaction['profile'] = array();
1059
- if($order->getBillingAddress()){
1060
- $billingAddress = $order->getBillingAddress();
1061
- $transaction['profile']['address'] = $this->_getAddress($billingAddress);
1062
- }
1063
- if($order->getShippingAddress()){
1064
- $shippingAddress = $order->getShippingAddress();
1065
- $transaction['profile']['shippingAddress'] = $this->_getAddress($shippingAddress);
1066
- }
1067
- // Get items
1068
- $items = $order->getAllItems();
1069
- $line_items = $this->_getLineItems($items, 'transaction');
1070
- $transaction['item'] = $line_items;
1071
-
1072
- $this->_transaction = $transaction;
1073
- }
1074
- } catch (Exception $e) {
1075
- }
1076
- }
1077
-
1078
- public function setDigitalDataLayer(Varien_Event_Observer $observer) {
1079
- // W3C DDL
1080
- // - pageInstanceID
1081
- // - page
1082
- // - product[n]
1083
- // - cart
1084
- // - transaction
1085
- // - event[n]
1086
- // - component[n]
1087
- // - user[n]
1088
- // - privacyAccessCategories
1089
- // - version = "1.0"
1090
-
1091
- try {
1092
- $triggered_messaging_digital_data_layer_enabled = (boolean)Mage::getStoreConfig('triggered_messaging/triggered_messaging_digital_data_layer_enabled');
1093
-
1094
- if ($triggered_messaging_digital_data_layer_enabled==1) {
1095
- $this->_debug = (boolean)Mage::getStoreConfig('triggered_messaging/triggered_messaging_digital_data_layer_debug_enabled');
1096
- $this->_userGroupExp = (boolean)Mage::getStoreConfig('triggered_messaging/triggered_messaging_digital_data_layer_user_group_enabled');
1097
- $this->_expAttr = explode(',',Mage::getStoreConfig('triggered_messaging/triggered_messaging_digital_data_layer_attributes_enabled'));
1098
-
1099
- $this->_setUser();
1100
- $this->_setPage();
1101
-
1102
- if ($this->_isProduct()) {
1103
- $this->_setProduct();
1104
  }
 
1105
 
1106
- if ($this->_isCategory() || $this->_isSearch()) {
1107
- $this->_setListing();
 
 
 
 
 
 
 
 
 
 
 
 
 
1108
  }
 
1109
 
1110
- if (!$this->_isConfirmation()) {
1111
- $this->_setCart();
 
 
 
1112
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1113
 
1114
- if ($this->_isConfirmation()) {
1115
- $this->_setTransaction();
 
1116
  }
1117
-
1118
- // Add script after content block, to grab products shown on category and search pages
1119
- $layout = $observer->getEvent()->getLayout()->getUpdate();
1120
- $layout->addHandle('tms_block_after_content');
1121
- }
1122
- } catch(Exception $e) {
1123
  }
1124
 
1125
- return $this;
1126
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1127
  }
 
1128
  ?>
1
  <?php
2
 
3
+ class TriggeredMessaging_DigitalDataLayer_Model_Page_Observer
4
+ {
5
+
6
+ // Specification is at
7
+ // http://www.w3.org/2013/12/ceddl-201312.pdf
8
+ protected $_version = "1.0";
9
+ protected $_user = null;
10
+ protected $_page = null;
11
+ protected $_cart = null;
12
+ protected $_product = null;
13
+ protected $_search = null;
14
+ protected $_transaction = null;
15
+ protected $_listing = null;
16
+ protected $_events = array();
17
+
18
+ protected $_debug = false;
19
+
20
+ protected function _getRequest()
21
+ {
22
+ return Mage::app()->getFrontController()->getRequest();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  }
 
24
 
25
+ /*
26
+ * Returns Controller Name
27
+ */
28
+ protected function _getControllerName()
29
+ {
30
+ return $this->_getRequest()->getControllerName();
31
  }
 
32
 
33
+ protected function _getActionName()
34
+ {
35
+ return $this->_getRequest()->getActionName();
 
 
36
  }
 
37
 
38
+ protected function _getModuleName()
39
+ {
40
+ return $this->_getRequest()->getModuleName();
 
 
41
  }
 
42
 
43
+ protected function _getRouteName()
44
+ {
45
+ return $this->_getRequest()->getRouteName();
 
46
  }
47
+
48
+ protected function _getCustomer()
49
+ {
50
+ return Mage::helper('customer')->getCustomer();
 
 
 
 
 
 
 
 
 
51
  }
52
 
53
+ protected function _getBreadcrumb()
54
+ {
55
+ return Mage::helper('catalog')->getBreadcrumbPath();
56
+ }
57
 
58
+ protected function _getCategory($category_id)
59
+ {
60
+ return Mage::getModel('catalog/category')->load($category_id);
 
 
61
  }
62
+
63
+ protected function _getCurrentProduct()
64
+ {
65
+ return Mage::registry('current_product');
 
 
 
 
 
 
 
66
  }
67
+
68
+ protected function _getProduct($productId)
69
+ {
70
+ return Mage::getModel('catalog/product')->load($productId);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  }
 
72
 
73
+ protected function _getCurrentCategory()
74
+ {
75
+ return Mage::registry('current_category');
76
  }
77
 
78
+ protected function _getCatalogSearch()
79
+ {
80
+ return Mage::getSingleton('catalogsearch/advanced');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  }
 
82
 
83
+ protected function _getCheckoutSession()
84
+ {
85
+ return Mage::getSingleton('checkout/session');
86
+ }
87
 
88
+ protected function _getSalesOrder()
89
+ {
90
+ return Mage::getModel('sales/order');
 
 
91
  }
92
 
93
+ protected function _getOrderAddress()
94
+ {
95
+ return Mage::getModel('sales/order_address');
96
+ }
97
 
 
98
  /*
99
+ * Determine which page type we're on
 
 
 
 
 
 
 
 
 
100
  */
101
 
102
+ public function _isHome()
103
+ {
104
+ if (Mage::app()->getRequest()->getRequestString() == "/") {
105
+ return true;
106
+ } else {
107
+ return false;
108
+ }
109
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
 
111
+ public function _isContent()
112
+ {
113
+ if ($this->_getModuleName() == 'cms') {
114
+ return true;
115
+ } else {
116
+ return false;
117
  }
118
+ }
 
119
 
120
+ public function _isCategory()
121
+ {
122
+ if ($this->_getControllerName() == 'category') {
123
+ return true;
124
+ } else {
125
+ return false;
126
+ }
127
  }
 
128
 
129
+ public function _isSearch()
130
+ {
131
+ if ($this->_getModuleName() == 'catalogsearch') {
132
+ return true;
133
+ } else {
134
+ return false;
135
+ }
136
+ }
137
 
138
+ public function _isProduct()
139
+ {
140
+ $onCatalog = false;
141
+ if (Mage::registry('current_product')) {
142
+ $onCatalog = true;
143
+ }
144
+ return $onCatalog;
145
+ }
146
+
147
+ public function _isCart()
148
+ {
149
+ try {
150
+ $request = $this->_getRequest();
151
+ $module = $request->getModuleName();
152
+ $controller = $request->getControllerName();
153
+ $action = $request->getActionName();
154
+ if ($module == 'checkout' && $controller == 'cart' && $action == 'index') {
155
+ return true;
156
+ }
157
+ } catch (Exception $e) {
158
+ }
159
+
160
+ return false;
161
+ }
162
+
163
+ public function _isCheckout()
164
+ {
165
+ if (strpos($this->_getModuleName(), 'checkout') !== false && $this->_getActionName() != 'success') {
166
+ return true;
167
+ } else {
168
+ return false;
169
+ }
170
+ }
171
+
172
+ public function _isConfirmation()
173
+ {
174
+ // default controllerName is "onepage"
175
+ // relax the check, only check if it contains checkout
176
+ // some checkout systems have different prefix/postfix,
177
+ // but all contain checkout
178
+ if (strpos($this->_getModuleName(), 'checkout') !== false && $this->_getActionName() == "success") {
179
+ return true;
180
+ } else {
181
+ return false;
182
+ }
183
+ }
184
+
185
+
186
+ /*
187
+ * Get information on pages to pass to front end
188
+ */
189
+ public function getCurrentPrice($_price, $p1, $p2)
190
+ {
191
+ if ($p1) {
192
+ return number_format(Mage::helper('core')->currency((float)$_price, $p1, $p2), 2, '.', '');
193
+ } else {
194
+ return floatval(number_format(Mage::helper('core')->currency((float)$_price, $p1, $p2), 2, '.', ''));
195
+ }
196
+ }
197
+
198
+ public function getVersion()
199
+ {
200
+ return $this->_version;
201
+ }
202
+
203
+ public function getPurchaseCompleteQs()
204
+ {
205
 
 
206
  $orderId = $this->_getCheckoutSession()->getLastOrderId();
207
  if ($orderId) {
208
+ $order = $this->_getSalesOrder()->load($orderId);
209
+ $email = $order->getCustomerEmail();
210
+ } else {
211
+ $email = $user->getEmail();
212
+ }
213
+ $qs = "e=" . urlencode($email);
214
+
215
+ if ($orderId) {
216
+ $qs = $qs . "&r=" . urlencode($orderId);
217
  }
218
+
219
+ return $qs;
220
+ }
221
+
222
+ public function getUser()
223
+ {
224
+ return $this->_user;
225
+ }
226
+
227
+ public function getPage()
228
+ {
229
+ return $this->_page;
230
+ }
231
+
232
+ public function getProduct()
233
+ {
234
+ return $this->_product;
235
+ }
236
+
237
+ public function getCart()
238
+ {
239
+ return $this->_cart;
240
+ }
241
+
242
+ public function getTransaction()
243
+ {
244
+ return $this->_transaction;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
245
  }
 
246
 
247
+ public function getListing()
248
+ {
249
+ return $this->_listing;
250
+ }
251
+
252
+ public function getEvents()
253
+ {
254
+ return array();
255
+ }
256
+
257
+
258
  /*
259
+ * Set the model attributes to be passed front end
 
 
 
 
 
 
 
260
  */
261
 
262
+ public function _getPageType()
263
+ {
264
+ try {
265
+ if ($this->_isHome()) {
266
+ return 'home';
267
+ } elseif ($this->_isContent()) {
268
+ return 'content';
269
+ } elseif ($this->_isCategory()) {
270
+ return 'category';
271
+ } elseif ($this->_isSearch()) {
272
+ return 'search';
273
+ } elseif ($this->_isProduct()) {
274
+ return 'product';
275
+ } elseif ($this->_isCart()) {
276
+ return 'basket';
277
+ } elseif ($this->_isCheckout()) {
278
+ return 'checkout';
279
+ } elseif ($this->_isConfirmation()) {
280
+ return 'confirmation';
281
+ } else {
282
+ return $this->_getModuleName();
283
+ }
284
+ } catch (Exception $e) {
285
+ }
286
  }
287
 
288
+ public function _getPageBreadcrumb()
289
+ {
290
+ $arr = $this->_getBreadcrumb();
291
+ $breadcrumb = array();
292
 
293
+ try {
294
+ foreach ($arr as $category) {
295
+ $breadcrumb[] = $category['label'];
296
+ }
297
+ } catch (Exception $e) {
298
+ }
299
 
300
+ return $breadcrumb;
301
+ }
 
302
 
303
+ public function _setPage()
304
+ {
305
+ /*
306
+ Section 6.3 of http://www.w3.org/2013/12/ceddl-201312.pdf
307
+ page: {
308
+ pageInfo: {
309
+ pageID: "Great Winter Run 2015",
310
+ destinationURL: "http://www.greatrun.org/Events/Event.aspx?id=2"},
 
 
 
 
 
311
  category: {
312
+ primaryCategory: "Cameras",
313
+ subCategory1: "Nikon",
314
+ pageType: "product"
 
 
 
 
 
 
 
 
 
 
 
315
  }
316
+ */
317
+
318
+ try {
319
+ $this->_page = array();
320
+
321
+ $this->_page['pageInfo'] = array();
322
+ // $this->_page['pageInfo']['pageID']
323
+ $this->_page['pageInfo']['pageName'] = '';
324
+ $this->_page['pageInfo']['destinationURL'] = Mage::helper('core/url')->getCurrentUrl();
325
+ $referringURL = Mage::app()->getRequest()->getServer('HTTP_REFERER');
326
+ if ($referringURL) {
327
+ $this->_page['pageInfo']['referringURL'] = $referringURL;
328
+ }
329
+ // $this->_page['pageInfo']['sysEnv']
330
+ // $this->_page['pageInfo']['variant']
331
+ if ($this->_getPageBreadcrumb()) {
332
+ $this->_page['pageInfo']['breadcrumbs'] = $this->_getPageBreadcrumb();
333
+ }
334
+ // $this->_page['pageInfo']['author']
335
+ // $this->_page['pageInfo']['issueDate']
336
+ // $this->_page['pageInfo']['effectiveDate']
337
+ // $this->_page['pageInfo']['expiryDate']
338
+ $this->_page['pageInfo']['language'] = Mage::app()->getLocale()->getLocaleCode();
339
+ // $this->_page['pageInfo']['geoRegion']
340
+ // $this->_page['pageInfo']['industryCodes']
341
+ // $this->_page['pageInfo']['publisher']
342
+
343
+ $this->_page['category'] = array();
344
+ if (Mage::registry('current_category')) {
345
+ // There must be a better way than this
346
+ $this->_page['category']['primaryCategory'] = Mage::registry('current_category')->getName();
347
+ }
348
+ // $this->_page['category']['subCategory1'];
349
+ $this->_page['category']['pageType'] = $this->_getPageType();
350
 
351
+ // $this->_page['attributes'] = array();
352
+
353
+ if ($this->_debug) {
354
+ $modules = (array)Mage::getConfig()->getNode('modules')->children();
355
+ $mods = array();
356
+ foreach ($modules as $key => $value) {
357
+ if (strpos($key, 'Mage_') === false) {
358
+ $mods[] = $key;
359
+
360
+ }
361
  }
362
+ $this->_page['extra_modules'] = $mods;
363
  }
364
+
365
+ } catch (Exception $e) {
366
+ }
 
 
 
367
  }
368
+
369
+ // Set the user info
370
+ public function _setUser()
371
+ {
372
+ // Section 6.9 of http://www.w3.org/2013/12/ceddl-201312.pdf
373
+
374
+ try {
375
+ $this->_user = array();
376
+ $user = $this->_getCustomer();
377
+ $user_id = $user->getEntityId();
378
+ $firstName = $user->getFirstname();
379
+ $lastName = $user->getLastname();
380
+ $userGroup = Mage::getModel('customer/group')->load(Mage::getSingleton('customer/session')->getCustomerGroupId());
381
+
382
+ if ($this->_isConfirmation()) {
383
+ $orderId = $this->_getCheckoutSession()->getLastOrderId();
384
+ if ($orderId) {
385
+ $order = $this->_getSalesOrder()->load($orderId);
386
+ $email = $order->getCustomerEmail();
387
+ }
388
+ } else {
389
+ $email = $user->getEmail();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
390
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
391
 
392
+ $this->_user['profile'] = array();
 
 
 
 
 
 
 
 
393
 
394
+ $profile = array();
395
+
396
+ $profile['profileInfo'] = array();
397
+ if ($user_id) {
398
+ $profile['profileInfo']['profileID'] = (string)$user_id;
399
+ }
400
+ if ($firstName) {
401
+ $profile['profileInfo']['userFirstName'] = $firstName;
402
+ }
403
+ if ($lastName) {
404
+ $profile['profileInfo']['userLastName'] = $lastName;
405
+ }
406
+ if ($email) {
407
+ $profile['profileInfo']['email'] = $email;
408
+ }
409
+ $profile['profileInfo']['language'] = Mage::getStoreConfig('general/locale/code', Mage::app()->getStore()->getId());
410
+ $profile['profileInfo']['returningStatus'] = $user_id ? 'true' : 'false';
411
+ if ($userGroup && $this->_userGroupExp) {
412
+ $profile['profileInfo']['segment']['userGroupId'] = $userGroup->getData('customer_group_id');
413
+ $profile['profileInfo']['segment']['userGroup'] = $userGroup->getData('customer_group_code');
414
+ }
415
 
416
+ // $profile['address'] = array();
417
+ // $profile['address']['line1'];
418
+ // $profile['address']['line2'];
419
+ // $profile['address']['city'];
420
+ // $profile['address']['stateProvince'];
421
+ // $profile['address']['postalCode'];
422
+ // $profile['address']['country'];
423
+
424
+ // $profile['social'] = array();
425
+ // $profile['attributes'] = array();
426
+
427
+ array_push($this->_user['profile'], $profile);
428
+ } catch (Exception $e) {
429
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
430
  }
431
 
432
+ public function _getAddress($address)
433
+ {
434
+ /*
435
+ address: {
436
+ line1: "",
437
+ line2: "",
438
+ city: "",
439
+ stateProvince: "",
440
+ postalCode: "",
441
+ country: ""
442
+ },
443
+ */
444
+
445
+ $billing = array();
446
+
447
+ try {
448
+ if ($address) {
449
+ $billing['line1'] = $address->getName();
450
+ $billing['line2'] = $address->getStreetFull();
451
+ $billing['city'] = $address->getCity();
452
+ $billing['postalCode'] = $address->getPostcode();
453
+ $billing['country'] = $address->getCountry();
454
+ $state = $address->getRegion();
455
+ $billing['stateProvince'] = $state ? $state : '';
456
+ }
457
+ } catch (Exception $e) {
458
  }
459
+
460
+ return $billing;
 
461
  }
462
 
463
+ public function _getProductStock($product)
464
+ {
465
+ return (int)Mage::getModel('cataloginventory/stock_item')->loadByProduct($product)->getQty();
466
+ }
467
 
468
+ public function _getCurrency()
469
+ {
470
+ return Mage::app()->getStore()->getCurrentCurrencyCode();
471
+ }
472
+
473
+ public function _getProductModel($product, $_page_)
474
+ {
475
+ /*
476
+ Section 6.4 of http://www.w3.org/2013/12/ceddl-201312.pdf
477
+ product: [
478
+ {
479
+ productInfo: {
480
+ productID: "greatwinterrun2015", // Unique product ID
481
+ productName: "Great Winter Run",
482
+ description: "Running, in the winter, in Edinburgh",
483
+ productURL: "http://www.greatrun.org/Events/Event.aspx?id=2",
484
+ productImage: "http://www.greatrun.org/Events/App_Images/slideshow/cropped/saved/gwir_01f0a780d94.jpg",
485
+ productThumbnail: "http://www.greatrun.org/App_Images/2011/Events/logo_GWIR.jpg"
486
+ },
487
+ category: {
488
+ primaryCategory: "Somecategory",
489
+ subCategory1: "Subcat"
490
+ },
491
+ attributes: {
492
+ distance: 5000,
493
+ country: "Scotland",
494
+ date: "2015-01-11", // Dates in ISO 8601
495
+ city: "Edinburgh" // You can put any extended data you want passing through in attributes
496
+ },
497
+ price: {
498
+ basePrice: 40.00,
499
+ currency: "GBP",
500
+ taxRate: 0.2,
501
+ priceWithTax: 48.00
502
+ }
503
  }
504
+ ]
505
+ */
 
506
 
507
+ $product_model = array();
508
+ $options = array();
509
+ //If there is optional data then add it
510
+ if ($_page_ === 'cart') {
511
+ $opt = $product->getProduct()->getTypeInstance(true)->getOrderOptions($product->getProduct());
512
+ if (isset($opt['attributes_info'])) {
513
+ foreach ($opt['attributes_info'] as $attribute) {
514
+ $options[$attribute['label']] = $attribute['value'];
515
+ }
516
+ }
517
+ $productId = $product->getProductId();
518
+ $product = $this->_getProduct($productId);
 
519
  }
520
+ if ($_page_ === 'list') {
521
+ $productId = $product->getId();
522
+ $product = $this->_getProduct($productId);
523
+ }
524
+ try {
525
+ // Product Info
526
+ $product_model['productInfo'] = array();
527
+ $product_model['productInfo']['productID'] = $product->getId();
528
+ $product_model['productInfo']['productName'] = $product->getName();
529
+ $product_model['productInfo']['description'] = strip_tags($product->getShortDescription());
530
+ $product_model['productInfo']['productURL'] = $product->getProductUrl();
531
+
532
+ //Check if images contain placeholders
533
+ if ($product->getImage() && $product->getImage() !== "no_selection") {
534
+ $product_model['productInfo']['productImage'] = $product->getImageUrl();
535
+ }
536
+ if ($product->getThumbnail() && $product->getThumbnail() !== "no_selection") {
537
+ $product_model['productInfo']['productThumbnail'] = $product->getThumbnailUrl();
538
+ }
539
+ //Attributes
540
+ if ($product->getWeight() && in_array('weight', $this->_expAttr)) {
541
+ $product_model['attributes']['weight'] = floatval($product->getWeight());
542
+ }
543
+ try {
544
+ $attributes = Mage::getSingleton('eav/config')->getEntityType(Mage_Catalog_Model_Product::ENTITY)->getAttributeCollection();
545
+ foreach ($attributes as $attr) {
546
+ $infoLocation = 'none';
547
+ $attrCode = $attr->getAttributecode();
548
+ if ($attrCode === 'color' || $attrCode === 'manufacturer' || $attrCode === 'size') {
549
+ if ($product->getAttributeText($attrCode) && in_array($attrCode, $this->_expAttr)) {
550
+ $product_model['productInfo'][$attrCode] = $product->getAttributeText($attrCode);
551
+ }
552
+ } elseif ($attr->getData('is_user_defined') && in_array($attrCode, $this->_expAttr) && $product->getData($attrCode)) {
553
+ if ($attr->getData('frontend_class') === 'validate-number') {
554
+ if ($attr->getFrontend()->getValue($product) !== 'No') {
555
+ $product_model['attributes'][$attrCode] = floatval($attr->getFrontend()->getValue($product));
556
+ }
557
+ } elseif ($attr->getData('frontend_class') === 'validate-digits') {
558
+ if ($attr->getFrontend()->getValue($product) !== 'No') {
559
+ $product_model['attributes'][$attrCode] = intval($attr->getFrontend()->getValue($product));
560
+ }
561
+ } else {
562
+ if ($product->getAttributeText($attrCode)){
563
+ $product_model['attributes'][$attrCode] = $product->getAttributeText($attrCode);
564
+ } elseif ($product->getData($attrCode)) {
565
+ $product_model['attributes'][$attrCode] = $product->getData($attrCode);
566
+ }
567
+ }
568
+ }
569
+ }
570
+ //Add the options captured earlier
571
+ if (count($options)) {
572
+ $product_model['attributes']['options'] = $options;
573
+ }
574
+ } catch (Exception $e) {
575
+ }
576
+ // Category
577
+ // Iterates through all categories, checking for duplicates
578
+ $allcategories = $this->_getProductCategories($product);
579
+ if ($allcategories) {
580
+ $catiterator = 0;
581
+ $setCategories = array();
582
+ foreach ($allcategories as $cat) {
583
+ if ($catiterator === 0) {
584
+ $product_model['category']['primaryCategory'] = $cat;
585
+ $catiterator++;
586
+
587
+ } else {
588
+ if (!in_array($cat, $setCategories)) {
589
+ $product_model['category']["subCategory$catiterator"] = $cat;
590
+ $catiterator++;
591
+ }
592
+ }
593
+ array_push($setCategories, $cat);
594
+ }
595
+ if ($product->getTypeID()) {
596
+ $product_model['category']['productType'] = $product->getTypeID();
597
+ }
598
+ }
599
 
600
+ // Price
601
+ $product_model['price'] = array();
602
+ if (!$product->getSpecialPrice() || $product->getSpecialPrice() !== $product->getFinalPrice()) {
603
+ $product_model['price']['basePrice'] = $this->getCurrentPrice(floatval($product->getPrice()), false, false);
604
+ } else {
605
+ $product_model['price']['basePrice'] = $this->getCurrentPrice(floatval($product->getSpecialPrice()), false, false);
606
+ $product_model['price']['regularPrice'] = $this->getCurrentPrice(floatval($product->getPrice()), false, false);
607
+ }
608
+ $product_model['price']['currency'] = $this->_getCurrency();
609
+ $product_model['price']['priceWithTax'] = $this->getCurrentPrice(floatval($product->getFinalPrice()), false, false);
610
 
611
+ if (!$product_model['price']['priceWithTax']) {
612
+ unset($product_model['price']['priceWithTax']);
613
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
614
 
615
+ // In case 'basePrice' did not exist
616
+ if (!$product_model['price']['basePrice']) {
617
+ $product_model['price']['basePrice'] = $this->getCurrentPrice(floatval($product->getGroupPrice()), false, false);
618
+ }
619
+ if (!$product_model['price']['basePrice']) {
620
+ $product_model['price']['basePrice'] = $this->getCurrentPrice(floatval($product->getMinimalPrice()), false, false);
621
+ }
622
+ if (!$product_model['price']['basePrice']) {
623
+ $product_model['price']['basePrice'] = $this->getCurrentPrice(floatval($product->getSpecialPrice()), false, false);
624
+ }
625
+ if (!$product_model['price']['basePrice']) {
626
+ // Extract price for bundle products
627
+ $price_model = $product->getPriceModel();
628
+ if (method_exists($price_model, 'getOptions')) {
629
+ $normal_price = 0.0;
630
+ $_options = $price_model->getOptions($product);
631
+ foreach ($_options as $_option) {
632
+ if (!method_exists($_option, 'getDefaultSelection')) {
633
+ break;
634
+ }
635
+ $_selection = $_option->getDefaultSelection();
636
+ if ($_selection === null) continue;
637
+ $normal_price += $this->getCurrentPrice(floatval($_selection->getPrice()), false, false);
638
+ }
639
+ $product_model['price']['basePrice'] = $normal_price;
640
+ }
641
+ }
642
 
643
+ if ($this->_debug) {
644
+ $product_model['price']['all'] = array();
645
+ $product_model['price']['all']['getPrice'] = $this->getCurrentPrice($product->getPrice(), false, false);
646
+ $product_model['price']['all']['getMinimalPrice'] = $this->getCurrentPrice($product->getMinimalPrice(), false, false);
647
+ $product_model['price']['all']['getPriceModel'] = $product->getPriceModel();
648
+ $product_model['price']['all']['getGroupPrice'] = $this->getCurrentPrice($product->getGroupPrice(), false, false);
649
+ $product_model['price']['all']['getTierPrice'] = $product->getTierPrice();
650
+ $product_model['price']['all']['getTierPriceCount'] = $product->getTierPriceCount();
651
+ $product_model['price']['all']['getFormatedTierPrice'] = $product->getFormatedTierPrice();
652
+ $product_model['price']['all']['getFormatedPrice'] = $product->getFormatedPrice();
653
+ $product_model['price']['all']['getFinalPrice'] = $this->getCurrentPrice($product->getFinalPrice(), false, false);
654
+ $product_model['price']['all']['getCalculatedFinalPrice'] = $product->getCalculatedFinalPrice();
655
+ $product_model['price']['all']['getSpecialPrice'] = $this->getCurrentPrice($product->getSpecialPrice(), false, false);
656
+ }
657
+
658
+ // Calculate Tax Rate
659
+ $store = Mage::app()->getStore('default');
660
+ $taxCalculation = Mage::getModel('tax/calculation');
661
+ $request = $taxCalculation->getRateRequest(null, null, null, $store);
662
+ $taxClassId = $product->getTaxClassId();
663
+ $percent = $taxCalculation->getRate($request->setProductClassId($taxClassId));
664
+ $product_model['price']['taxRate'] = ((float)$percent) / 100;
665
+
666
+ // For configurable/grouped/composite products, add all associated products to 'linkedProduct'
667
+ if ($_page_ === 'product') {
668
+ if ($product->isConfigurable() || $product->isGrouped() || $product->isComposite()) {
669
+
670
+ $product_model['linkedProduct'] = array();
671
+ $simple_collection = array();
672
+
673
+ // Add simple products related to configurable products
674
+ if ($product->isConfigurable()) {
675
+ $conf = Mage::getModel('catalog/product_type_configurable')->setProduct($product);
676
+ $simple_collection = $conf->getUsedProductCollection()->addAttributeToSelect('*')->addFilterByRequiredOptions();
677
+ } else {
678
+ $type_instance = $product->getTypeInstance(true);
679
+ if (method_exists($type_instance, 'getSelectionsCollection')) {
680
+ // Add simple products related to bundle products
681
+ $simple_collection = $type_instance->getSelectionsCollection(
682
+ $type_instance->getOptionsIds($product), $product
683
+ );
684
+ } else if (method_exists($type_instance, 'getAssociatedProducts')) {
685
+ // Add simple products related to grouped products
686
+ $simple_collection = $type_instance->getAssociatedProducts($product);
687
+ }
688
+ }
689
+
690
+ // Add related products to the data layer
691
+ $min_price = 0.0;
692
+ foreach ($simple_collection as $simple_product) {
693
+ array_push($product_model['linkedProduct'], $this->_getProductModel($simple_product, false));
694
+ $simple_product_price = $this->getCurrentPrice(floatval($simple_product->getPrice()), false, false);
695
+ if ($simple_product_price && (!$min_price || $simple_product_price < $min_price)) {
696
+ $min_price = $simple_product_price;
697
+ }
698
+ }
699
+
700
+ // If price could not be extracted before, can set it now
701
+ if (!$product_model['price']['basePrice']) {
702
+ $product_model['price']['basePrice'] = $this->getCurrentPrice(floatval($min_price), false, false);
703
+ }
704
+
705
+ if (!$product_model['linkedProduct']) {
706
+ unset($product_model['linkedProduct']);
707
+ }
708
+ }
709
+ }
710
 
711
+ if ($this->_debug) {
712
+ $product_model['more']['isConfigurable'] = $product->isConfigurable();
713
+ $product_model['more']['isSuperGroup'] = $product->isSuperGroup();
714
+ $product_model['more']['isSuperConfig'] = $product->isSuperConfig();
715
+ $product_model['more']['isGrouped'] = $product->isGrouped();
716
+ $product_model['more']['isSuper'] = $product->isSuper();
717
+ $product_model['more']['isVirtual'] = $product->isVirtual();
718
+ $product_model['more']['isRecurring'] = $product->isRecurring();
719
+ $product_model['more']['isComposite'] = $product->isComposite();
720
+ $product_model['more']['getTypeId'] = $product->getTypeId();
721
+ $product_model['more']['getImage'] = $product->getImage();
722
+ $product_model['more']['getImageURL'] = $product->getImageUrl();
723
+ $product_model['more']['getSmallImage'] = $product->getSmallImage();
724
+ $product_model['more']['getSmallImageURL'] = $product->getSmallImageUrl();
725
+ $product_model['more']['getThumbnail'] = $product->getThumbnail();
726
+ $product_model['more']['getThumbnailURL'] = $product->getThumbnailUrl();
727
+ }
728
+
729
+ // Other
730
+ // $product_model['attributes'] = array();
731
+
732
+ // The following are not used in W3C DDL but exist in Universal Variable:
733
+ // $product_model['sku_code'] = $product->getSku();
734
+ // $product_model['stock'] = (int) $this->_getProductStock($product);
735
+ } catch (Exception $e) {
736
  }
 
737
 
738
+ return $product_model;
 
 
739
  }
740
+
741
+ public function _getProductCategories($product)
742
+ {
743
+ try {
744
+ $cats = $product->getCategoryIds();
745
+ if ($cats) {
746
+ $category_names = array();
747
+ foreach ($cats as $category_id) {
748
+ $_cat = $this->_getCategory($category_id);
749
+ $category_names[] = $_cat->getName();
750
+ }
751
+ return $category_names;
752
+ }
753
+ } catch (Exception $e) {
754
+ }
755
+
756
+ return false;
757
  }
 
758
 
759
+ public function _getLineItems($items, $page_type)
760
+ {
761
+ /*
 
 
 
 
 
 
 
762
  item: [
763
  {
764
  productInfo: {
789
  }
790
  }
791
  ]
792
+ */
793
+
794
+ $line_items = array();
795
+ try {
796
+ foreach ($items as $item) {
797
+ $productId = $item->getProductId();
798
+ $product = $this->_getProduct($productId);
799
+
800
+ $parentIds = Mage::getModel('catalog/product_type_grouped')->getParentIdsByChild($productId);
801
+ if (count($parentIds) > 0) {
802
+ $litem_model = $this->_getProductModel($product, 'linked');
803
+ $litem_model['linkedProduct'] = array();
804
+ array_push($litem_model['linkedProduct'], $this->_getProductModel($this->_getProduct($parentIds[0]), 'linked'));
805
+ } else {
806
+ $litem_model = $this->_getProductModel($item, 'cart');
807
+ }
808
+
809
+ if ($page_type === 'cart') {
810
+ $litem_model['quantity'] = floatval($item->getQty());
811
+ } else {
812
+ $litem_model['quantity'] = floatval($item->getQtyOrdered());
813
+ }
814
+
815
+ if (!is_array($litem_model['price'])) {
816
+ $litem_model['price'] = array();
817
+ }
818
+ if ($item->getCouponCode()) {
819
+ $litem_model['price']['voucherCode'] = $item->getCouponCode();
820
+ }
821
+ if ($item->getDiscountAmount()) {
822
+ $litem_model['price']['voucherDiscount'] = abs(floatval($item->getDiscountAmount()));
823
+ }
824
+ // $litem_model['price']['shipping'];
825
+ // $litem_model['price']['shippingMethod'] = $this->_extractShippingMethod($item->getQuote());
826
+ $litem_model['price']['priceWithTax'] = $this->getCurrentPrice(floatval($item->getBasePriceInclTax()), false, false); // TODO: This may be different from that in _getProductModel()
827
+ $litem_model['price']['cartTotal'] = floatval($item->getRowTotalInclTax());
828
+
829
+ if ($this->_debug) {
830
+ $litem_model['price']['all']['_getCalculationPrice'] = $this->getCurrentPrice($product->getCalculationPrice(), false, false);
831
+ $litem_model['price']['all']['_getCalculationPriceOriginal'] = $this->getCurrentPrice($product->getCalculationPriceOriginal(), false, false);
832
+ $litem_model['price']['all']['_getBaseCalculationPrice'] = $this->getCurrentPrice($product->getBaseCalculationPrice(), false, false);
833
+ $litem_model['price']['all']['_getBaseCalculationPriceOriginal'] = $this->getCurrentPrice($product->getBaseCalculationPriceOriginal(), false, false);
834
+ $litem_model['price']['all']['_getOriginalPrice'] = $this->getCurrentPrice($product->getOriginalPrice(), false, false);
835
+ $litem_model['price']['all']['_getBaseOriginalPrice'] = $this->getCurrentPrice($product->getBaseOriginalPrice(), false, false);
836
+ $litem_model['price']['all']['_getConvertedPrice'] = $this->getCurrentPrice($product->getConvertedPrice(), false, false);
837
+ }
838
+
839
+ // $litem_model['attributes'] = array();
840
 
841
+ array_push($line_items, $litem_model);
842
+ }
843
+ } catch (Exception $e) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
844
  }
845
 
846
+ return $line_items;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
847
  }
 
848
 
849
+ public function _setListing()
850
+ {
851
+ try {
852
+ $this->_listing = array();
853
+ if ($this->_isCategory()) {
854
+ $category = $this->_getCurrentCategory();
855
+ } elseif ($this->_isSearch()) {
856
+ $category = $this->_getCatalogSearch();
857
+ if (isset($_GET['q'])) {
858
+ $this->_listing['query'] = $_GET['q'];
859
+ }
860
+ }
861
+
862
+ // Note: data on products are retrieved later, after the content layout block,
863
+ // since the product list is compiled then.
864
+ } catch (Exception $e) {
865
+ }
866
  }
 
867
 
868
+ public function _setProduct()
869
+ {
870
+ try {
871
+ $product = $this->_getCurrentProduct();
872
+ if (!$product) return false;
873
+ $this->_product = array();
874
+ array_push($this->_product, $this->_getProductModel($product, 'product'));
875
+ } catch (Exception $e) {
876
+ }
877
  }
 
 
878
 
879
+ public function _setCart()
880
+ {
881
+ /*
882
+ Section 6.5 of http://www.w3.org/2013/12/ceddl-201312.pdf
883
+ cart: {
884
+ price: {
885
+ basePrice: 40.00,
886
+ currency: "GBP",
887
+ taxRate: 0.2,
888
+ cartTotal: 48.00
889
+ },
890
+ item: [
891
+ {
892
+ productInfo: {
893
+ productID: "greatwinterrun2015", // Unique product ID - links the prod in the cart to the one browsed
894
+ // If data isn't available on the cart page, as long as the productID is present, it will be filled
895
+ // out from our product database (e.g. name, images, category, extra attributes, price)
896
+ productName: "Great Winter Run",
897
+ description: "Running, in the winter, in Edinburgh",
898
+ productURL: "http://www.greatrun.org/Events/Event.aspx?id=2",
899
+ productImage: "http://www.greatrun.org/Events/App_Images/slideshow/cropped/saved/gwir_01f0a780d94.jpg",
900
+ productThumbnail: "http://www.greatrun.org/App_Images/2011/Events/logo_GWIR.jpg"
901
+ },
902
+ category: {
903
+ primaryCategory: "Somecategory",
904
+ subCategory1: "Subcat"
905
+ },
906
+ attributes: {
907
+ distance: 5000,
908
+ country: "Scotland",
909
+ date: "2015-01-11", // Dates in ISO 8601
910
+ city: "Edinburgh" // You can put any extended data you want passing through in attributes
911
+ },
912
+ price: {
913
+ basePrice: 40.00,
914
+ currency: "GBP",
915
+ taxRate: 0.2,
916
+ priceWithTax: 48.00
917
+ }
918
+ }
919
+ ]
920
+ }
921
+ */
922
+
923
+ try {
924
+ $basket = $this->_getCheckoutSession();
925
+
926
+ if (!isset($basket)) {
927
+ return;
928
  }
929
+
930
+ $cart = array();
931
+ $quote = $basket->getQuote();
932
+ // Set normal params
933
+ $cart_id = $basket->getQuoteId();
934
+ if ($cart_id) {
935
+ $cart['cartID'] = (string)$cart_id;
936
+ }
937
+ $cart['price'] = array();
938
+ if ($quote->getBaseSubtotal()) {
939
+ $cart['price']['basePrice'] = $this->getCurrentPrice(floatval($quote->getBaseSubtotal()), false, false);
940
+ } else {
941
+ $cart['price']['basePrice'] = 0.0;
942
+ }
943
+ if ($quote->getShippingAddress()->getCouponCode()) {
944
+ $cart['price']['voucherCode'] = $quote->getShippingAddress()->getCouponCode();
945
+ }
946
+ if ($quote->getShippingAddress()->getDiscountAmount()) {
947
+ $cart['price']['voucherDiscount'] = abs((float)$quote->getShippingAddress()->getDiscountAmount());
948
+ }
949
+ $cart['price']['currency'] = $this->_getCurrency();
950
+ if ($cart['price']['basePrice'] > 0.0) {
951
+ $taxRate = (float)$quote->getShippingAddress()->getTaxAmount() / $cart['price']['basePrice'];
952
+ $cart['price']['taxRate'] = round($taxRate, 3); // TODO: Find a better way
953
+ }
954
+ if ($quote->getShippingAmount()) {
955
+ $cart['price']['shipping'] = (float)$quote->getShippingAmount();
956
+ }
957
+ if ($this->_extractShippingMethod($quote)) {
958
+ $cart['price']['shippingMethod'] = $this->_extractShippingMethod($quote);
959
+ }
960
+ if ($quote->getShippingAddress()->getTaxAmount() && $quote->getBaseSubtotal()) {
961
+ $cart['price']['priceWithTax'] = (float)$quote->getShippingAddress()->getTaxAmount() + $this->getCurrentPrice($quote->getBaseSubtotal(), false, false);
962
+ } else {
963
+ $cart['price']['priceWithTax'] = 0.0;
964
+ }
965
+ if ($quote->getGrandTotal() && $quote->getGrandTotal() < 0) {
966
+ $cart['price']['cartTotal'] = (float)$quote->getGrandTotal();
967
+ } else {
968
+ $cart['price']['cartTotal'] = 0.0;
969
+ }
970
+ // $cart['attributes'] = array();
971
+ if ($cart['price']['basePrice'] === 0.0 && $cart['price']['cartTotal'] === 0.0 && $cart['price']['priceWithTax'] === 0.0) {
972
+ unset($cart['price']);
973
  }
 
 
 
974
 
975
+ // Line items
976
+ $items = $quote->getAllVisibleItems();
977
+ if (!$items && isset($cart['price'])) {
978
+ if ($this->_debug) {
979
+ $cart['price']['testLog'] = "Second method used to retrieve cart items.";
980
+ }
981
+
982
+ // In case items were not retrieved for some reason
983
+ $cartHelper = Mage::helper('checkout/cart');
984
+ $items = $cartHelper->getCart()->getItems();
985
+ }
986
+ $cart['items'] = $this->_getLineItems($items, 'cart');
987
+ if (empty($cart['items'])) {
988
+ unset($cart['items']);
989
+ }
990
+
991
+ // The following are not used in W3C DDL but exist in Universal Variable:
992
+ // $cart['subtotal'] = (float) $quote->getSubtotal();
993
+ // $cart['tax'] = (float) $quote->getShippingAddress()->getTaxAmount();
994
+ // $cart['subtotal_include_tax'] = (boolean) $this->_doesSubtotalIncludeTax($quote, $cart['tax']);
995
+
996
+ if ($cart_id || isset($cart['items']) || isset($cart['price'])) {
997
+ $this->_cart = $cart;
998
+ }
999
+ } catch (Exception $e) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1000
  }
1001
+ }
1002
 
1003
+ public function _doesSubtotalIncludeTax($order, $tax)
1004
+ {
1005
+ /*
1006
+ Conditions:
1007
+ - if tax is zero, then set to false
1008
+ - Assume that if grand total is bigger than total after subtracting shipping, then subtotal does NOT include tax
1009
+ */
1010
+ try {
1011
+ $grandTotalWithoutShipping = $order->getGrandTotal() - $order->getShippingAmount();
1012
+ if ($tax === 0 || $grandTotalWithoutShipping > $order->getSubtotal()) {
1013
+ return false;
1014
+ } else {
1015
+ return true;
1016
+ }
1017
+ } catch (Exception $e) {
1018
  }
1019
+ }
1020
 
1021
+ public function _extractShippingMethod($order)
1022
+ {
1023
+ try {
1024
+ $shipping_method = $order->getShippingMethod();
1025
+ } catch (Exception $e) {
1026
  }
1027
+ return $shipping_method ? $shipping_method : '';
1028
+ }
1029
+
1030
+ public function _setTransaction()
1031
+ {
1032
+ /*
1033
+ Spec:
1034
+ transaction {
1035
+ transactionID: "",
1036
+ profile: {
1037
+ profileInfo: {
1038
+ profileID: "",
1039
+ userName: "",
1040
+ email: ""
1041
+ },
1042
+ address: {
1043
+ line1: "",
1044
+ line2: "",
1045
+ city: "",
1046
+ stateProvince: "",
1047
+ postalCode: "",
1048
+ country: ""
1049
+ },
1050
+ shippingAddress: {
1051
+ line1: "",
1052
+ line2: "",
1053
+ city: "",
1054
+ stateProvince: "",
1055
+ postalCode: "",
1056
+ country: ""
1057
+ }
1058
+ },
1059
+ total: {
1060
+ basePrice: 0,
1061
+ voucherCode: "",
1062
+ voucherDiscount: 0,
1063
+ currency: "USD",
1064
+ taxRate: 0,
1065
+ shipping: 0,
1066
+ shippingMethod: "",
1067
+ priceWithTax: 0,
1068
+ transactionTotal: 0
1069
+ },
1070
+ attributes: {},
1071
+ item: [
1072
+ {
1073
+ productInfo
1074
+ category
1075
+ quantity
1076
+ price
1077
+ linkedProduct
1078
+ attributes
1079
+ }
1080
+ ]
1081
+ }
1082
+ */
1083
+
1084
+ try {
1085
+ $orderId = $this->_getCheckoutSession()->getLastOrderId();
1086
+
1087
+ if ($orderId) {
1088
+ $transaction = array();
1089
+ $order = $this->_getSalesOrder()->load($orderId);
1090
+
1091
+ // Get general details
1092
+ $transaction['transactionID'] = $order->getIncrementId();
1093
+ $transaction['total'] = array();
1094
+ $transaction['total']['currency'] = $this->_getCurrency();
1095
+ $transaction['total']['basePrice'] = (float)$order->getSubtotal();
1096
+ // $transaction['tax'] = (float) $order->getTaxAmount();
1097
+ // $transaction['subtotal_include_tax'] = $this->_doesSubtotalIncludeTax($order, $transaction['tax']);
1098
+ // $transaction['payment_type'] = $order->getPayment()->getMethodInstance()->getTitle();
1099
+ $transaction['total']['transactionTotal'] = (float)$order->getGrandTotal();
1100
+
1101
+ $voucher = $order->getCouponCode();
1102
+ $transaction['total']['voucherCode'] = $voucher ? $voucher : "";
1103
+ $voucher_discount = -1 * $order->getDiscountAmount();
1104
+ $transaction['total']['voucherDiscount'] = $voucher_discount ? $voucher_discount : 0;
1105
+
1106
+ $transaction['total']['shipping'] = (float)$order->getShippingAmount();
1107
+ $transaction['total']['shippingMethod'] = $this->_extractShippingMethod($order);
1108
+
1109
+ // Get addresses
1110
+ $transaction['profile'] = array();
1111
+ if ($order->getBillingAddress()) {
1112
+ $billingAddress = $order->getBillingAddress();
1113
+ $transaction['profile']['address'] = $this->_getAddress($billingAddress);
1114
+ }
1115
+ if ($order->getShippingAddress()) {
1116
+ $shippingAddress = $order->getShippingAddress();
1117
+ $transaction['profile']['shippingAddress'] = $this->_getAddress($shippingAddress);
1118
+ }
1119
+ // Get items
1120
+ $items = $order->getAllItems();
1121
+ $line_items = $this->_getLineItems($items, 'transaction');
1122
+ $transaction['item'] = $line_items;
1123
 
1124
+ $this->_transaction = $transaction;
1125
+ }
1126
+ } catch (Exception $e) {
1127
  }
 
 
 
 
 
 
1128
  }
1129
 
1130
+ public function setDigitalDataLayer(Varien_Event_Observer $observer)
1131
+ {
1132
+ // W3C DDL
1133
+ // - pageInstanceID
1134
+ // - page
1135
+ // - product[n]
1136
+ // - cart
1137
+ // - transaction
1138
+ // - event[n]
1139
+ // - component[n]
1140
+ // - user[n]
1141
+ // - privacyAccessCategories
1142
+ // - version = "1.0"
1143
+
1144
+ try {
1145
+ $triggered_messaging_digital_data_layer_enabled = (boolean)Mage::getStoreConfig('triggered_messaging/triggered_messaging_digital_data_layer_enabled');
1146
+
1147
+ if ($triggered_messaging_digital_data_layer_enabled == 1) {
1148
+ $this->_debug = (boolean)Mage::getStoreConfig('triggered_messaging/triggered_messaging_digital_data_layer_debug_enabled');
1149
+ $this->_userGroupExp = (boolean)Mage::getStoreConfig('triggered_messaging/triggered_messaging_digital_data_layer_user_group_enabled');
1150
+ $this->_expAttr = explode(',', Mage::getStoreConfig('triggered_messaging/triggered_messaging_digital_data_layer_attributes_enabled'));
1151
+
1152
+ $this->_setUser();
1153
+ $this->_setPage();
1154
+
1155
+ if ($this->_isProduct()) {
1156
+ $this->_setProduct();
1157
+ }
1158
+
1159
+ if ($this->_isCategory() || $this->_isSearch()) {
1160
+ $this->_setListing();
1161
+ }
1162
+
1163
+ if (!$this->_isConfirmation()) {
1164
+ $this->_setCart();
1165
+ }
1166
+
1167
+ if ($this->_isConfirmation()) {
1168
+ $this->_setTransaction();
1169
+ }
1170
+
1171
+ // Add script after content block, to grab products shown on category and search pages
1172
+ $layout = $observer->getEvent()->getLayout()->getUpdate();
1173
+ $layout->addHandle('tms_block_after_content');
1174
+ }
1175
+ } catch (Exception $e) {
1176
+ }
1177
+
1178
+ return $this;
1179
+ }
1180
  }
1181
+
1182
  ?>
app/code/community/TriggeredMessaging/DigitalDataLayer/Model/System/Config/Source/Productattributes.php CHANGED
@@ -1,6 +1,8 @@
1
  <?php
 
2
  class TriggeredMessaging_DigitalDataLayer_Model_System_Config_Source_Productattributes
3
- { /**
 
4
  * Options getter
5
  *
6
  * @return array
@@ -8,20 +10,21 @@ class TriggeredMessaging_DigitalDataLayer_Model_System_Config_Source_Productattr
8
  public function toOptionArray()
9
  {
10
  $attributes = Mage::getModel('catalog/product')->getAttributes();
11
- $attributeArray = array(array('label'=>'none','value'=>'0'));
12
 
13
- foreach($attributes as $a){
14
 
15
- foreach ($a->getEntityType()->getAttributeCodes() as $attrCode) {
16
- $attribute_details = Mage::getSingleton("eav/config")->getAttribute('catalog_product', $attrCode);
17
- if($attribute_details->getData('is_user_defined')){
18
- array_push($attributeArray,array('label' => $attrCode,'value' => $attrCode));
19
- }
20
  }
21
- break;
22
  }
23
- array_push($attributeArray,array('label' => 'weight','value' => weight));
24
- return $attributeArray;
 
 
25
  }
26
  }
 
27
  ?>
1
  <?php
2
+
3
  class TriggeredMessaging_DigitalDataLayer_Model_System_Config_Source_Productattributes
4
+ {
5
+ /**
6
  * Options getter
7
  *
8
  * @return array
10
  public function toOptionArray()
11
  {
12
  $attributes = Mage::getModel('catalog/product')->getAttributes();
13
+ $attributeArray = array(array('label' => 'none', 'value' => '0'));
14
 
15
+ foreach ($attributes as $a) {
16
 
17
+ foreach ($a->getEntityType()->getAttributeCodes() as $attrCode) {
18
+ $attribute_details = Mage::getSingleton("eav/config")->getAttribute('catalog_product', $attrCode);
19
+ if ($attribute_details->getData('is_user_defined')) {
20
+ array_push($attributeArray, array('label' => $attrCode, 'value' => $attrCode));
 
21
  }
 
22
  }
23
+ break;
24
+ }
25
+ array_push($attributeArray, array('label' => 'weight', 'value' => 'weight'));
26
+ return $attributeArray;
27
  }
28
  }
29
+
30
  ?>
app/code/community/TriggeredMessaging/DigitalDataLayer/etc/config.xml CHANGED
@@ -2,7 +2,7 @@
2
  <config>
3
  <modules>
4
  <TriggeredMessaging_DigitalDataLayer>
5
- <version>0.3.8</version>
6
  </TriggeredMessaging_DigitalDataLayer>
7
  </modules>
8
 
2
  <config>
3
  <modules>
4
  <TriggeredMessaging_DigitalDataLayer>
5
+ <version>0.3.9</version>
6
  </TriggeredMessaging_DigitalDataLayer>
7
  </modules>
8
 
package.xml CHANGED
@@ -1,7 +1,7 @@
1
  <?xml version="1.0"?>
2
  <package>
3
  <name>W3CDigitalDataLayerByTriggeredMessaging</name>
4
- <version>0.3.8</version>
5
  <stability>stable</stability>
6
  <license uri="http://opensource.org/licenses/apachepl.php">Apache Software License v2</license>
7
  <channel>community</channel>
@@ -29,9 +29,9 @@ https://github.com/TriggeredMessaging/digitalDataMagentoExtension/issues</descri
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>2015-01-26</date>
33
- <time>11:25:01</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="fa334484ed3023bc029917dc4b63fdc7"/></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="dc7423c362365db44ddab59e507fc69f"/></dir><dir name="System"><dir name="Config"><dir name="Source"><file name="Productattributes.php" hash="9b10a36c5db2142737368a33bf949f5b"/></dir></dir></dir></dir><dir name="etc"><file name="adminhtml.xml" hash="8b98dee82241e0b69661bf4699cba334"/><file name="cache.xml" hash="1b6658073a8126cd655cb18109559f49"/><file name="config.xml" hash="1a25d798b4941001e11536212abdbb98"/><file name="system.xml" hash="c11c84c7ca24fec850ac87c87791fb03"/></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="c50ea48f04f516eb9a5e171cb4cd7ec8"/><file name="digital_data_layer_after_content.phtml" hash="c28cb1ee3d5cfd761ca7aeb6dae9df20"/></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>
1
  <?xml version="1.0"?>
2
  <package>
3
  <name>W3CDigitalDataLayerByTriggeredMessaging</name>
4
+ <version>0.3.9</version>
5
  <stability>stable</stability>
6
  <license uri="http://opensource.org/licenses/apachepl.php">Apache Software License v2</license>
7
  <channel>community</channel>
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>2015-03-25</date>
33
+ <time>09:35:30</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="fa334484ed3023bc029917dc4b63fdc7"/></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="2d4bfb8def190e68a7781235b0adb4c6"/></dir><dir name="System"><dir name="Config"><dir name="Source"><file name="Productattributes.php" hash="93312ce69f938cab2abffa7e993b531a"/></dir></dir></dir></dir><dir name="etc"><file name="adminhtml.xml" hash="8b98dee82241e0b69661bf4699cba334"/><file name="cache.xml" hash="1b6658073a8126cd655cb18109559f49"/><file name="config.xml" hash="2b6ef2216241acc68ddcde5d87b8256c"/><file name="system.xml" hash="c11c84c7ca24fec850ac87c87791fb03"/></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="c50ea48f04f516eb9a5e171cb4cd7ec8"/><file name="digital_data_layer_after_content.phtml" hash="c28cb1ee3d5cfd761ca7aeb6dae9df20"/></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>