Advocado - Version 1.1.0

Version Notes

We've introduced a new mechanic to this extension. It does not allow double-dipping -- if a shopper gets an instant discount, they should not be able to redeem a coupon code for the same cart.

You can sign up for an account immediately after you register. Do let us know if you have any feedback or comments either through our website http://getadvocado.com, or via our email: dax@getadvocado.com

Download this release

Release Info

Developer SY Quek
Extension Advocado
Version 1.1.0
Comparing to
See all releases


Code changes from version 1.0.3 to 1.1.0

app/code/community/GozoLabs/Advocado/Block/Adminhtml/Login.php CHANGED
@@ -126,7 +126,6 @@ class GozoLabs_Advocado_Block_Adminhtml_Login extends
126
  * - storeGroupName
127
  */
128
  public function websiteStoreGroups() {
129
- Mage::log('trying to find the errors');
130
  $websites = Mage::getModel('core/website')
131
  ->getCollection();
132
 
126
  * - storeGroupName
127
  */
128
  public function websiteStoreGroups() {
 
129
  $websites = Mage::getModel('core/website')
130
  ->getCollection();
131
 
app/code/community/GozoLabs/Advocado/Helper/Analytics.php ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // test commit
4
+ if ( isset( $_SERVER[ 'ADVOC_LOCAL_DEBUG' ] ) || isset( $_SERVER['ADVOC_STAGING'] ) ) {
5
+ define( 'SEGMENT_IO_PIN', '8sm4kvvp9hyzwn99ykdr' );
6
+ } else {
7
+ define( 'SEGMENT_IO_PIN', 'mdsygwjezbsovqi0j31y' );
8
+ }
9
+
10
+ //require_once(Mage::getBaseDir('lib') . '/Raven/Client.php');
11
+ require_once(Mage::getBaseDir('lib') . '/Analytics/Analytics.php');
12
+
13
+ /**
14
+ * Logging tool for Advocado's use.
15
+ * There are specific cases for its use, however we need to take note that
16
+ * we are just an extension. So we should not be capturing all exceptions.
17
+ * Rather we should just catch exceptions that are relevant to this
18
+ * extension's smooth operation (for iteration).
19
+ *
20
+ */
21
+ class GozoLabs_Advocado_Helper_Analytics extends Mage_Core_Helper_Abstract {
22
+
23
+ public $isInit;
24
+ public $siteUrl;
25
+
26
+ public function isInitialized() {
27
+ return isset($this->isInit) && $this->isInit;
28
+ }
29
+
30
+ private function getBaseIdTraits() {
31
+ if (!isset($this->siteUrl) || !$this->siteUrl) {
32
+ $dh = Mage::helper('gozolabs_advocado');
33
+ $this->siteUrl = $dh->getSiteUrl();
34
+ }
35
+ return array(
36
+ 'Site URL'=> $this->siteUrl,
37
+ 'Platform' => 'Magento'
38
+ );
39
+ }
40
+
41
+ public function initialize() {
42
+ Mage::log('[Analytics] Initialising with ' . SEGMENT_IO_PIN);
43
+ Analytics::init(SEGMENT_IO_PIN);
44
+ Mage::log('[Analytics] Initialised');
45
+ $this->isInit = true;
46
+ $baseTraits = $this->getBaseIdTraits();
47
+ Mage::log('[Analytics] got base traits');
48
+ $this->identify($baseTraits);
49
+ Mage::log('[Analytics] identified');
50
+ }
51
+
52
+ public function identify($traits) {
53
+ if (!$this->isInitialized()) {
54
+ $this->initialize();
55
+ }
56
+ Analytics::identify('019mr8mf4r', $traits);
57
+ }
58
+
59
+ public function track($eventName, $traits) {
60
+ if (!$this->isInitialized()) {
61
+ Mage::log('[Analytics] Starting initialisation');
62
+ $this->initialize();
63
+ Mage::log('[Analytics] Done initialisation');
64
+ }
65
+ Mage::log('[Analytics] Getting the base traits');
66
+ $baseIdTraits = $this->getBaseIdTraits();
67
+ Mage::log('[Analytics] base traits: ' . var_export($baseIdTraits, true));
68
+ $t = array_merge($traits, $this->getBaseIdTraits());
69
+ Mage::log('[Analytics] Tracking: '.$eventName.' , ' . var_export($traits, true));
70
+ Analytics::track('019mr8mf4r', $eventName, $t);
71
+ }
72
+ }
73
+
74
+
75
+ ?>
app/code/community/GozoLabs/Advocado/Helper/Backend.php CHANGED
@@ -91,10 +91,22 @@ class GozoLabs_Advocado_Helper_Backend extends Mage_Core_Helper_Abstract {
91
  const TEMP_WEBSITE_ID = 1;
92
  const TEMP_STORE_GROUP_ID = 1;
93
 
 
 
 
 
 
 
 
 
94
 
95
  public function isStoreConnected() {
96
  $sc = $this->storeCredentials();
97
  if ($sc) {
 
 
 
 
98
  return true;
99
  }
100
  return false;
@@ -731,6 +743,10 @@ class GozoLabs_Advocado_Helper_Backend extends Mage_Core_Helper_Abstract {
731
  strval($rp->getStatus()) .
732
  ')' .
733
  $rp->getMessage());
 
 
 
 
734
  }
735
 
736
  }
91
  const TEMP_WEBSITE_ID = 1;
92
  const TEMP_STORE_GROUP_ID = 1;
93
 
94
+ private $analyticsHelper;
95
+
96
+ private function getAnalytics() {
97
+ if (!isset($this->analyticsHelper) || !$this->analyticsHelper) {
98
+ $this->analyticsHelper = Mage::helper('gozolabs_advocado/analytics');
99
+ }
100
+ return $this->analyticsHelper;
101
+ }
102
 
103
  public function isStoreConnected() {
104
  $sc = $this->storeCredentials();
105
  if ($sc) {
106
+ $this->getAnalytics()->track(
107
+ 'Connected Store Viewing Dashboard',
108
+ array()
109
+ );
110
  return true;
111
  }
112
  return false;
743
  strval($rp->getStatus()) .
744
  ')' .
745
  $rp->getMessage());
746
+ $log = Mage::helper('gozolabs_advocado/analytics');
747
+ $log->track('Backend Error', array(
748
+ 'Status' => 'Cannot invalidate coupon (Backend Coupon ID=' . $couponId . ')',
749
+ ));
750
  }
751
 
752
  }
app/code/community/GozoLabs/Advocado/Helper/Data.php CHANGED
@@ -74,6 +74,10 @@ class AdvocPCoupon extends AdvocModelInstance {
74
  ) ) ? true : false;
75
  }
76
 
 
 
 
 
77
  public function getData( $field=null ) {
78
 
79
  if ($field) {
@@ -1102,6 +1106,7 @@ class GozoLabs_Advocado_Helper_Data extends Mage_Core_Helper_Data {
1102
  const SHARE_CODES_PARENT_SUB = 'parent_sub_id';
1103
  const SHARE_CODES_ST_CODE = 'st_code';
1104
  const SHARE_CODES_SHARES = 'shares';
 
1105
 
1106
  function getWebsite($websiteId) {
1107
  $sites = Mage::app()->getWebsites();
@@ -1147,6 +1152,56 @@ class GozoLabs_Advocado_Helper_Data extends Mage_Core_Helper_Data {
1147
  return $product->isConfigurable();
1148
  }
1149
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1150
  /**
1151
  * @param $product1 Mage_Catalog_Model_Product
1152
  * @param $product2 Mage_Catalog_Model_Product
@@ -1308,6 +1363,38 @@ class GozoLabs_Advocado_Helper_Data extends Mage_Core_Helper_Data {
1308
  return $websiteIds;
1309
  }
1310
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1311
  function createCartCoupon( $code, $amt, $toDate, $type='amt',
1312
  $websiteIds=null ) {
1313
 
@@ -1345,7 +1432,6 @@ class GozoLabs_Advocado_Helper_Data extends Mage_Core_Helper_Data {
1345
  ->setIsActive(1)
1346
  ->setConditionsSerialized('')
1347
  ->setActionsSerialized('')
1348
- ->setConditionsSerialized('')
1349
  ->setCustomerGroupIds($this->_getAllCustomerGroups())
1350
  ->setStopRulesProcessing(0)
1351
  ->setIsAdvanced(1)
74
  ) ) ? true : false;
75
  }
76
 
77
+ public function getRule() {
78
+ return $this->origObject;
79
+ }
80
+
81
  public function getData( $field=null ) {
82
 
83
  if ($field) {
1106
  const SHARE_CODES_PARENT_SUB = 'parent_sub_id';
1107
  const SHARE_CODES_ST_CODE = 'st_code';
1108
  const SHARE_CODES_SHARES = 'shares';
1109
+ const ADVOCADO_CATEGORY_NAME = 'Advocado Products';
1110
 
1111
  function getWebsite($websiteId) {
1112
  $sites = Mage::app()->getWebsites();
1152
  return $product->isConfigurable();
1153
  }
1154
 
1155
+ /* ------------------------------------------------------------------------
1156
+ * For Advocado Magento v1.1.0
1157
+ * -----------------------------------------------------------------------*/
1158
+
1159
+ /* Advocado works with a special category so that application of coupons
1160
+ * only takes place on these items.
1161
+ */
1162
+ function createAdvocadoCategory() {
1163
+ $cat = new Mage_Catalog_Model_Category();
1164
+ $cat->setName(self::ADVOCADO_CATEGORY_NAME);
1165
+ $cat->setUrlKey('advocado-discount');
1166
+ $cat->setIsActive(1);
1167
+ $cat->setIncludeInMenu(0);
1168
+ $cat->setDisplayMode('PRODUCTS');
1169
+ $cat->setIsAnchor(0);
1170
+ // we use the root catalog, so the parent ID is 0
1171
+ $parentCategory = Mage::getModel('catalog/category')
1172
+ ->load(
1173
+ Mage::app()->getWebsite(1)
1174
+ ->getDefaultStore()
1175
+ ->getRootCategoryId()
1176
+ );
1177
+ Mage::log('parent category id = ' . $parentCategory->getId());
1178
+ $cat->setPath($parentCategory->getPath());
1179
+ $cat->save();
1180
+ return $cat;
1181
+ }
1182
+
1183
+ function getAdvocadoCategory() {
1184
+
1185
+ $all = Mage::getModel('catalog/category')->getCollection()
1186
+ ->addAttributeToSelect('name')
1187
+ ->addAttributeToSelect('is_active');
1188
+
1189
+ $advocCat = null;
1190
+
1191
+ foreach($all as $_cat) {
1192
+ if ($_cat->getName() == self::ADVOCADO_CATEGORY_NAME) {
1193
+ // For other purposes
1194
+ $advocCat = $_cat;
1195
+ break;
1196
+ }
1197
+ }
1198
+ if ($advocCat == null) {
1199
+ Mage::log('Creating advocado category');
1200
+ $advocCat = $this->createAdvocadoCategory();
1201
+ }
1202
+ return $advocCat;
1203
+ }
1204
+
1205
  /**
1206
  * @param $product1 Mage_Catalog_Model_Product
1207
  * @param $product2 Mage_Catalog_Model_Product
1363
  return $websiteIds;
1364
  }
1365
 
1366
+ /**
1367
+ * Will attach a condition to coupon. In this case we want to
1368
+ * focus only on applying this coupon on a specific category.
1369
+ * @return $coupon (which was passed in as a parameter)
1370
+ */
1371
+ function exclusiveToAdvocadoProducts($coupon) {
1372
+ $cat = $this->getAdvocadoCategory();
1373
+ // to understand what this means, i recommend this blogpost
1374
+ // http://mikebywaters.wordpress.com/2011/12/18/programmatically-create-shopping-cart-price-rules-with-conditions-and-actions/
1375
+ $actions = array(
1376
+ '1' => array(
1377
+ 'type' => 'salesrule/rule_condition_product',
1378
+ 'aggregator' => 'all',
1379
+ 'value' => '1',
1380
+ 'new_child' => false
1381
+ ),
1382
+
1383
+ '1--1' => array(
1384
+ 'type' => 'salesrule/rule_condition_product',
1385
+ 'attribute' => 'category_ids',
1386
+ 'operator' => '==',
1387
+ 'value' => $cat->getId(),
1388
+ 'is_value_processed' => false
1389
+ )
1390
+ );
1391
+ $rule = $coupon->getRule();
1392
+ $rule->setData('actions', $actions);
1393
+ $rule->loadPost($rule->getData());
1394
+ $rule->save();
1395
+ return $coupon;
1396
+ }
1397
+
1398
  function createCartCoupon( $code, $amt, $toDate, $type='amt',
1399
  $websiteIds=null ) {
1400
 
1432
  ->setIsActive(1)
1433
  ->setConditionsSerialized('')
1434
  ->setActionsSerialized('')
 
1435
  ->setCustomerGroupIds($this->_getAllCustomerGroups())
1436
  ->setStopRulesProcessing(0)
1437
  ->setIsAdvanced(1)
app/code/community/GozoLabs/Advocado/Model/Observer.php CHANGED
@@ -168,8 +168,11 @@ class GozoLabs_Advocado_Model_Observer {
168
  // delete
169
  $coupon->delete();
170
  } else {
171
- Mage::log('backend failed to update coupon with ' .
172
- 'code = ' . $couponCode );
 
 
 
173
  }
174
  } else {
175
  // we would normally delete
168
  // delete
169
  $coupon->delete();
170
  } else {
171
+ Mage::log('backend failed to update coupon with ' . 'code = ' . $couponCode );
172
+ $log = Mage::helper('gozolabs_advocado/analytics');
173
+ $log->track('Backend Error', array(
174
+ 'Status' => 'Did not delete coupon with code=' . $couponCode
175
+ ));
176
  }
177
  } else {
178
  // we would normally delete
app/code/community/GozoLabs/Advocado/controllers/CampaignController.php ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ function _log( $msg ) {
4
+ Mage::log( $msg );
5
+ }
6
+
7
+ function _helper() {
8
+ return Mage::helper('gozolabs_advocado');
9
+ }
10
+
11
+ class GozoLabs_Advocado_CampaignController extends Mage_Core_Controller_Front_Action {
12
+
13
+ const PARAM_WEBSITE_STORE_GROUP = 'website_store_group';
14
+ const PARAM_PRODUCTS_PAGE = 'page';
15
+ const PARAM_PRODUCTS_PAGE_LIMIT = 'limit';
16
+
17
+ private function getJsonResponse() {
18
+ return $this->getResponse()->setHeader('Content-Type',
19
+ 'application/json');
20
+ }
21
+
22
+ private function jsonify($data) {
23
+ return Mage::helper('core')->jsonEncode($data);
24
+ }
25
+
26
+ /**
27
+ * Authenticates with stored credentials to ensure that
28
+ * they are well and truly the same.
29
+ *
30
+ * IF at first they are not the same, we get the backend to
31
+ * refresh the credentials. If at the 2nd round of
32
+ * comparison they are still not the same, this test
33
+ * fails.
34
+ *
35
+ */
36
+ private function authenticate($siteKey, $siteToken, $creds = null ) {
37
+
38
+ $be = Mage::helper('gozolabs_advocado/backend');
39
+ if ( ! $creds ) {
40
+
41
+ $helper = Mage::helper('gozolabs_advocado');
42
+ $websiteId = $helper->getCurrentWebsiteId();
43
+ $storeGroupId = $helper->getCurrentStoreGroupId();
44
+
45
+ $creds = $be->storeCredentials(
46
+ $websiteId,
47
+ $storeGroupId
48
+ );
49
+ // if still not there, means they never were
50
+ if ( ! $creds ) {
51
+ $data = $be->requestSiteToken();
52
+ if ( ! $data ) {
53
+ return False;
54
+ } else {
55
+ $creds = $be->storeCredentials(
56
+ $websiteId,
57
+ $storeGroupId
58
+ );
59
+ }
60
+ }
61
+ return $this->authenticate( $siteKey, $siteToken, $creds );
62
+
63
+ } else {
64
+ Mage::log('for store with id '
65
+ . $creds->getStoreId()
66
+ . ', site credentials exist: - siteKey: '
67
+ . $creds->getSiteKey()
68
+ . ', siteToken: '
69
+ . $creds->getSiteToken()
70
+ . '; cand_siteKey: '
71
+ . $siteKey
72
+ . ', cand_siteToken: '
73
+ . $siteToken
74
+ );
75
+ // test equality
76
+ if ( $siteToken == $creds->getSiteToken()
77
+ && $siteKey == $creds->getSiteKey() ) {
78
+ return True;
79
+ }
80
+ }
81
+ return False;
82
+ }
83
+
84
+ private function doCategoryUpdate($type, $req) {
85
+
86
+ // should be a comma delimited
87
+ $csvProductIds = $req->getParam('product_ids');
88
+ $siteKey = $req->getParam('site_key');
89
+ $siteToken = $req->getParam('site_token');
90
+
91
+ if ($this->authenticate($siteKey, $siteToken)) {
92
+
93
+ $productIds = explode(',', $csvProductIds);
94
+ Mage::log('product Ids = ' . var_export($productIds, true));
95
+ $_ = Mage::helper('gozolabs_advocado');
96
+ $specialCat = $_->getAdvocadoCategory();
97
+ $catId = $specialCat->getId();
98
+ Mage::log('Advocado Category with ID = ' . $specialCat->getId());
99
+ $catApi = Mage::getSingleton('catalog/category_api');
100
+
101
+ // TODO: might need catch an exception here and return an
102
+ // appropriate response
103
+ foreach( $productIds as $pId ) {
104
+ // add the product to the category
105
+ if ($type == 'add') {
106
+ $catApi->assignProduct(
107
+ $specialCat->getId(),
108
+ $pId
109
+ );
110
+ } else if ($type == 'remove') {
111
+ // check product exists in category before removing
112
+ $product = Mage::getModel('catalog/product')->load($pId);
113
+ if (in_array($catId, $product->getCategoryIds())) {
114
+ $catApi->removeProduct($catId, $pId);
115
+ }
116
+ //$catApi->removeProduct(
117
+ //$specialCat->getId(),
118
+ //$pId
119
+ //);
120
+ } else {
121
+ $log = Mage::helper('gozolabs_advocado/analytics');
122
+ $log->track('Invalid category update type', array(
123
+ 'Site Key' => $siteKey
124
+ ));
125
+ }
126
+ }
127
+
128
+ $this->getJsonResponse()
129
+ ->setHttpResponseCode(200);
130
+ } else {
131
+ $this->getJsonResponse()
132
+ ->setHttpResponseCode(403)
133
+ ->appendBody(
134
+ $this->jsonify(
135
+ array(
136
+ 'error_code' => 403,
137
+ 'error_msg' => 'Not allowed'
138
+ )
139
+ ));
140
+ }
141
+
142
+ }
143
+
144
+ /** Places products under a special Advocado category so that
145
+ * they can receive a discount.
146
+ */
147
+ public function addcategoryAction() {
148
+
149
+ $req = $this->getRequest();
150
+ Mage::log('Adding to advocado category');
151
+ $this->doCategoryUpdate('add', $req);
152
+
153
+ }
154
+
155
+ public function removecategoryAction() {
156
+ $req = $this->getRequest();
157
+ Mage::log('Removing from advocado category');
158
+ $this->doCategoryUpdate('remove', $req);
159
+ }
160
+ }
161
+
162
+ ?>
app/code/community/GozoLabs/Advocado/controllers/V1Controller.php CHANGED
@@ -200,6 +200,7 @@ class GozoLabs_Advocado_V1Controller extends Mage_Core_Controller_Front_Action {
200
  * authentication with store credentials.
201
  */
202
  public function ordersAction() {
 
203
  $req = $this->getRequest();
204
  $siteKey = $req->getParam('site_key');
205
  $siteToken = $req->getParam('site_token');
@@ -292,6 +293,7 @@ class GozoLabs_Advocado_V1Controller extends Mage_Core_Controller_Front_Action {
292
 
293
  }
294
 
 
295
  private function _parseRawUrlEncodedBody( $request ) {
296
  $raw = $request->getRawBody();
297
  $pairs = explode( '&', $raw );
@@ -336,6 +338,39 @@ class GozoLabs_Advocado_V1Controller extends Mage_Core_Controller_Front_Action {
336
  return $request->getParam('action');
337
  }
338
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
339
  /**
340
  *
341
  * Handles request related to coupons.
@@ -368,6 +403,13 @@ class GozoLabs_Advocado_V1Controller extends Mage_Core_Controller_Front_Action {
368
  $req->getParam('type'),
369
  $this->_getAdvocadoWebsiteIds());
370
 
 
 
 
 
 
 
 
371
  $this->getJsonResponse()
372
  ->setHttpResponseCode(201)
373
  ->setBody( $this->jsonify(
@@ -440,6 +482,23 @@ class GozoLabs_Advocado_V1Controller extends Mage_Core_Controller_Front_Action {
440
  )));
441
 
442
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
443
  } else {
444
  $this->getJsonResponse()
445
  ->setHttpResponseCode(405);
200
  * authentication with store credentials.
201
  */
202
  public function ordersAction() {
203
+
204
  $req = $this->getRequest();
205
  $siteKey = $req->getParam('site_key');
206
  $siteToken = $req->getParam('site_token');
293
 
294
  }
295
 
296
+
297
  private function _parseRawUrlEncodedBody( $request ) {
298
  $raw = $request->getRawBody();
299
  $pairs = explode( '&', $raw );
338
  return $request->getParam('action');
339
  }
340
 
341
+ private function applyCouponCode($couponCode, $notice='') {
342
+
343
+ if ($notice == '') {
344
+ $notice = $this->__('Congratulations! You have been given a discount!');
345
+ }
346
+
347
+ if ($couponCode != '') {
348
+ Mage::getSingleton('checkout/session')->setData('coupon_code', $couponCode);
349
+ $cart = Mage::getSingleton('checkout/cart')
350
+ ->getQuote()
351
+ ->setCouponCode($couponCode)
352
+ ->collectTotals()
353
+ ->save();
354
+
355
+ // a success message to congratulate them
356
+ Mage::getSingleton('core/session')->addSuccess($notice);
357
+ return $cart;
358
+
359
+ } else {
360
+ Mage::getSingleton('checkout/session')->setData('coupon_code', '');
361
+ $cart = Mage::getSingleton('checkout/cart');
362
+ $items = Mage::getSingleton('checkout/session')
363
+ ->getQuote()
364
+ ->getItemsCollection();
365
+
366
+ foreach($items as $item) {
367
+ $cart->removeItem( $item->getId() );
368
+ }
369
+ $cart->save();
370
+ return null;
371
+ }
372
+ }
373
+
374
  /**
375
  *
376
  * Handles request related to coupons.
403
  $req->getParam('type'),
404
  $this->_getAdvocadoWebsiteIds());
405
 
406
+ // find out if it's a special advocado
407
+ // coupon, only applicable to advocado
408
+ // products. If it is, lets make it so
409
+ if ($req->getParam('advocado_type') == 'instant_discount') {
410
+ $helper->exclusiveToAdvocadoProducts($result);
411
+ }
412
+
413
  $this->getJsonResponse()
414
  ->setHttpResponseCode(201)
415
  ->setBody( $this->jsonify(
482
  )));
483
 
484
  }
485
+ } else if ($action == 'apply') {
486
+
487
+ // no need for authentication
488
+ $cart = $this->applyCouponCode($req->getParam('coupon_code'));
489
+ if ($cart == null) {
490
+ $response = $this->getJsonResponse()
491
+ ->setHttpResponseCode(403)
492
+ ->appendBody(
493
+ $this->jsonify(
494
+ array('error_code'=>403,
495
+ 'error_msg' => 'Forbidden. Invalid.'
496
+ ))
497
+ );
498
+ } else {
499
+ $response = $this->getJsonResponse()
500
+ ->setHttpResponseCode(200);
501
+ }
502
  } else {
503
  $this->getJsonResponse()
504
  ->setHttpResponseCode(405);
app/code/community/GozoLabs/Advocado/etc/config.xml CHANGED
@@ -47,14 +47,20 @@
47
  <!--</gozolabs_advocado>-->
48
  <!--</observers>-->
49
  <!--</sales_quote_add_item>-->
50
- <sales_quote_item_set_product>
51
- <observers>
52
- <gozolabs_advocado>
53
- <class>gozolabs_advocado/observer</class>
54
- <method>addItemToCart</method>
55
- </gozolabs_advocado>
56
- </observers>
57
- </sales_quote_item_set_product>
 
 
 
 
 
 
58
  <checkout_submit_all_after>
59
  <observers>
60
  <gozolabs_advocado>
47
  <!--</gozolabs_advocado>-->
48
  <!--</observers>-->
49
  <!--</sales_quote_add_item>-->
50
+ <!-- the below observer was in version 1.1
51
+ however because there is no longer a
52
+ need to verify the share (just apply
53
+ the coupon code),
54
+ no point setting the discount
55
+ -->
56
+ <!--<sales_quote_item_set_product>-->
57
+ <!--<observers>-->
58
+ <!--<gozolabs_advocado>-->
59
+ <!--<class>gozolabs_advocado/observer</class>-->
60
+ <!--<method>addItemToCart</method>-->
61
+ <!--</gozolabs_advocado>-->
62
+ <!--</observers>-->
63
+ <!--</sales_quote_item_set_product>-->
64
  <checkout_submit_all_after>
65
  <observers>
66
  <gozolabs_advocado>
app/design/frontend/base/default/layout/gozolabs_advocado.xml CHANGED
@@ -4,7 +4,7 @@
4
  <reference name="head">
5
  <block name="advocado.setup" type="core/text">
6
  <action method="setText">
7
- <text><![CDATA[<script type="text/javascript" src="https://advocado-frontend.s3.amazonaws.com/js/magento/loader.min.js"></script>]]></text>
8
  </action>
9
  </block>
10
  </reference>
@@ -16,7 +16,7 @@
16
  <reference name="head">
17
  <block name="advocado.setup" type="core/text">
18
  <action method="setText">
19
- <text><![CDATA[<script type="text/javascript" src="https://advocado-frontend.s3.amazonaws.com/js/magento/loader.min.js"></script>]]></text>
20
  </action>
21
  </block>
22
  </reference>
4
  <reference name="head">
5
  <block name="advocado.setup" type="core/text">
6
  <action method="setText">
7
+ <text><![CDATA[<script type="text/javascript" src="https://cdnw.getadvocado.com/js/magento/loader.min.js"></script>]]></text>
8
  </action>
9
  </block>
10
  </reference>
16
  <reference name="head">
17
  <block name="advocado.setup" type="core/text">
18
  <action method="setText">
19
+ <text><![CDATA[<script type="text/javascript" src="https://cdnw.getadvocado.com/js/magento/loader.min.js"></script>]]></text>
20
  </action>
21
  </block>
22
  </reference>
lib/Analytics/Analytics.php ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if (!function_exists('json_encode')) {
4
+ throw new Exception('Analytics needs the JSON PHP extension.');
5
+ }
6
+
7
+ require(dirname(__FILE__) . '/Analytics/Client.php');
8
+
9
+
10
+ class Analytics {
11
+
12
+ private static $client;
13
+
14
+ /**
15
+ * Initializes the default client to use. Uses the socket consumer by default.
16
+ * @param string $secret your project's secret key
17
+ * @param array $options passed straight to the client
18
+ */
19
+ public static function init($secret, $options = array()) {
20
+
21
+ if (!$secret){
22
+ throw new Exception("Analytics::init Secret parameter is required");
23
+ }
24
+
25
+ self::$client = new Analytics_Client($secret, $options);
26
+ }
27
+
28
+ /**
29
+ * Tracks a user action
30
+ * @param string $user_id user id string
31
+ * @param string $event name of the event
32
+ * @param array $properties properties associated with the event [optional]
33
+ * @param number $timestamp unix seconds since epoch (time()) [optional]
34
+ * @param array $context [optional]
35
+ * @return boolean whether the track call succeeded
36
+ */
37
+ public static function track($user_id, $event, $properties = null,
38
+ $timestamp = null, $context = array()) {
39
+ self::check_client();
40
+ return self::$client->track($user_id, $event, $properties, $timestamp,
41
+ $context);
42
+ }
43
+
44
+ /**
45
+ * Tags traits about the user.
46
+ * @param string $user_id
47
+ * @param array $traits
48
+ * @param number $timestamp unix seconds since epoch (time()) [optional]
49
+ * @param array $context [optional]
50
+ * @return boolean whether the track call succeeded
51
+ */
52
+ public static function identify($user_id, $traits = array(),
53
+ $timestamp = null, $context = array()) {
54
+ self::check_client();
55
+ return self::$client->identify($user_id, $traits, $timestamp, $context);
56
+ }
57
+
58
+ /**
59
+ * Aliases the user id from a temporary id to a permanent one
60
+ * @param string $from user id to alias from
61
+ * @param string $to user id to alias to
62
+ * @param number $timestamp unix seconds since epoch (time()) [optional]
63
+ * @param array $context [optional]
64
+ * @return boolean whether the alias call succeeded
65
+ */
66
+ public static function alias($from, $to, $timestamp = null,
67
+ $context = array()) {
68
+ self::check_client();
69
+ return self::$client->alias($from, $to, $timestamp, $context);
70
+ }
71
+
72
+ /**
73
+ * Ensures that the client is indeed set. Throws an exception when not set.
74
+ */
75
+ private static function check_client() {
76
+
77
+ if (self::$client == null) {
78
+ throw new Exception("Analytics::init must be called " .
79
+ "before track or identify");
80
+ }
81
+ }
82
+ }
lib/Analytics/Analytics/Client.php ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require(__DIR__ . '/Consumer.php');
4
+ require(__DIR__ . '/QueueConsumer.php');
5
+ require(__DIR__ . '/Consumer/File.php');
6
+ require(__DIR__ . '/Consumer/ForkCurl.php');
7
+ require(__DIR__ . '/Consumer/Socket.php');
8
+
9
+ class Analytics_Client {
10
+
11
+ private $consumer;
12
+
13
+ /**
14
+ * Create a new analytics object with your app's secret
15
+ * key
16
+ *
17
+ * @param string $secret
18
+ * @param array $options array of consumer options [optional]
19
+ * @param string Consumer constructor to use, socket by default.
20
+ */
21
+ public function __construct($secret, $options = array()) {
22
+
23
+ $consumers = array(
24
+ "socket" => "Analytics_Consumer_Socket",
25
+ "file" => "Analytics_Consumer_File",
26
+ "fork_curl" => "Analytics_Consumer_ForkCurl"
27
+ );
28
+
29
+ # Use our socket consumer by default
30
+ $consumer_type = isset($options["consumer"]) ? $options["consumer"] :
31
+ "socket";
32
+ $Consumer = $consumers[$consumer_type];
33
+
34
+ $this->consumer = new $Consumer($secret, $options);
35
+ }
36
+
37
+ public function __destruct() {
38
+ $this->consumer->__destruct();
39
+ }
40
+
41
+ /**
42
+ * Tracks a user action
43
+ * @param [string] $user_id user id string
44
+ * @param [string] $event name of the event
45
+ * @param [array] $properties properties associated with the event [optional]
46
+ * @param [number] $timestamp unix seconds since epoch (time()) [optional]
47
+ * @return [boolean] whether the track call succeeded
48
+ */
49
+ public function track($user_id, $event, $properties = null,
50
+ $timestamp = null, $context = array()) {
51
+
52
+ $context = array_merge($context, $this->getContext());
53
+
54
+ $timestamp = $this->formatTime($timestamp);
55
+
56
+ // json_encode will serialize as []
57
+ if (count($properties) == 0) {
58
+ $properties = null;
59
+ }
60
+
61
+ return $this->consumer->track($user_id, $event, $properties, $context,
62
+ $timestamp);
63
+ }
64
+
65
+ /**
66
+ * Tags traits about the user.
67
+ * @param [string] $user_id
68
+ * @param [array] $traits
69
+ * @param [number] $timestamp unix seconds since epoch (time()) [optional]
70
+ * @return [boolean] whether the track call succeeded
71
+ */
72
+ public function identify($user_id, $traits = array(), $timestamp = null,
73
+ $context = array()) {
74
+
75
+ $context = array_merge($context, $this->getContext());
76
+
77
+ $timestamp = $this->formatTime($timestamp);
78
+
79
+ // json_encode will serialize as []
80
+ if (count($traits) == 0) {
81
+ $traits = null;
82
+ }
83
+
84
+ return $this->consumer->identify($user_id, $traits, $context,
85
+ $timestamp);
86
+ }
87
+
88
+ /**
89
+ * Aliases from one user id to another
90
+ * @param string $from
91
+ * @param string $to
92
+ * @param number $timestamp unix seconds since epoch (time()) [optional]
93
+ * @param array $context [optional]
94
+ * @return boolean whether the alias call succeeded
95
+ */
96
+ public function alias($from, $to, $timestamp = null, $context = array()) {
97
+
98
+ $context = array_merge($context, $this->getContext());
99
+
100
+ $timestamp = $this->formatTime($timestamp);
101
+
102
+ return $this->consumer->alias($from, $to, $context, $timestamp);
103
+ }
104
+
105
+ /**
106
+ * Formats a timestamp by making sure it is set, and then converting it to
107
+ * iso8601 format.
108
+ * @param time $timestamp - time in seconds (time())
109
+ */
110
+ private function formatTime($timestamp) {
111
+
112
+ if ($timestamp == null) $timestamp = time();
113
+
114
+ # Format for iso8601
115
+ return date("c", $timestamp);
116
+ }
117
+
118
+
119
+ /**
120
+ * Add the segment.io context to the request
121
+ * @return array additional context
122
+ */
123
+ private function getContext () {
124
+ return array( "library" => "analytics-php" );
125
+ }
126
+ }
lib/Analytics/Analytics/Consumer.php ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ abstract class Analytics_Consumer {
3
+
4
+ protected $type = "Consumer";
5
+
6
+ protected $options;
7
+ protected $secret;
8
+
9
+ /**
10
+ * Store our secret and options as part of this consumer
11
+ * @param string $secret
12
+ * @param array $options
13
+ */
14
+ public function __construct($secret, $options = array()) {
15
+ $this->secret = $secret;
16
+ $this->options = $options;
17
+ }
18
+
19
+
20
+ /**
21
+ * Tracks a user action
22
+ * @param string $user_id user id string
23
+ * @param string $event name of the event
24
+ * @param array $properties properties associated with the event
25
+ * @param string $timestamp iso8601 of the timestamp
26
+ * @return boolean whether the track call succeeded
27
+ */
28
+ abstract public function track($user_id, $event, $properties, $context,
29
+ $timestamp);
30
+
31
+ /**
32
+ * Tags traits about the user.
33
+ * @param string $user_id
34
+ * @param array $traits
35
+ * @param string $timestamp iso8601 of the timestamp
36
+ * @return boolean whether the track call succeeded
37
+ */
38
+ abstract public function identify($user_id, $traits, $context, $timestamp);
39
+
40
+ /**
41
+ * Aliases from one user id to another
42
+ * @param string $from
43
+ * @param string $to
44
+ * @param array $context
45
+ * @param string $timestamp iso8601 of the timestamp
46
+ * @return boolean whether the alias call succeeded
47
+ */
48
+ abstract public function alias($from, $to, $context, $timestamp);
49
+
50
+ /**
51
+ * Check whether debug mode is enabled
52
+ * @return boolean
53
+ */
54
+ protected function debug() {
55
+ return isset($this->options["debug"]) ? $this->options["debug"] : false;
56
+ }
57
+
58
+ /**
59
+ * Check whether we should connect to the API using SSL. This is enabled by
60
+ * default with connections which make batching requests. For connections
61
+ * which can save on round-trip times, we disable it.
62
+ * @return boolean
63
+ */
64
+ protected function ssl() {
65
+ return isset($this->options["ssl"]) ? $this->options["ssl"] : false;
66
+ }
67
+
68
+
69
+ /**
70
+ * On an error, try and call the error handler, if debugging output to
71
+ * error_log as well.
72
+ * @param string $code
73
+ * @param string $msg
74
+ */
75
+ protected function handleError($code, $msg) {
76
+
77
+ if (isset($this->options['error_handler'])) {
78
+ $handler = $this->options['error_handler'];
79
+ $handler($code, $msg);
80
+ }
81
+
82
+ if ($this->debug()) {
83
+ error_log("[Analytics][" . $this->type . "] " . $msg);
84
+ }
85
+ }
86
+ }
lib/Analytics/Analytics/Consumer/File.php ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Analytics_Consumer_File extends Analytics_Consumer {
4
+
5
+ private $file_handle;
6
+ protected $type = "File";
7
+
8
+ /**
9
+ * The file consumer writes track and identify calls to a file.
10
+ * @param string $secret
11
+ * @param array $options
12
+ * string "filename" - where to log the analytics calls
13
+ */
14
+ public function __construct($secret, $options = array()) {
15
+
16
+ if (!isset($options["filename"]))
17
+ $options["filename"] = sys_get_temp_dir() . DIRECTORY_SEPARATOR . "analytics.log";
18
+
19
+ parent::__construct($secret, $options);
20
+
21
+ try {
22
+ $this->file_handle = fopen($options["filename"], "a");
23
+ chmod($options["filename"], 0777);
24
+ } catch (Exception $e) {
25
+ $this->handleError($e->getCode(), $e->getMessage());
26
+ }
27
+ }
28
+
29
+ public function __destruct() {
30
+ if ($this->file_handle &&
31
+ get_resource_type($this->file_handle) != "Unknown") {
32
+ fclose($this->file_handle);
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Tracks a user action
38
+ * @param [string] $user_id user id string
39
+ * @param [string] $event name of the event
40
+ * @param [array] $properties properties associated with the event
41
+ * @param [string] $timestamp iso8601 of the timestamp
42
+ * @return [boolean] whether the track call succeeded
43
+ */
44
+ public function track($user_id, $event, $properties, $context, $timestamp) {
45
+
46
+ $body = array(
47
+ "secret" => $this->secret,
48
+ "user_id" => $user_id,
49
+ "event" => $event,
50
+ "properties" => $properties,
51
+ "timestamp" => $timestamp,
52
+ "context" => $context,
53
+ "action" => "track"
54
+ );
55
+
56
+ return $this->write($body);
57
+ }
58
+
59
+ /**
60
+ * Tags traits about the user.
61
+ * @param [string] $user_id
62
+ * @param [array] $traits
63
+ * @param [string] $timestamp iso8601 of the timestamp
64
+ * @return [boolean] whether the track call succeeded
65
+ */
66
+ public function identify($user_id, $traits, $context, $timestamp) {
67
+
68
+ $body = array(
69
+ "secret" => $this->secret,
70
+ "user_id" => $user_id,
71
+ "traits" => $traits,
72
+ "context" => $context,
73
+ "timestamp" => $timestamp,
74
+ "action" => "identify"
75
+ );
76
+
77
+ return $this->write($body);
78
+ }
79
+
80
+ /**
81
+ * Aliases from one user id to another
82
+ * @param string $from
83
+ * @param string $to
84
+ * @param array $context
85
+ * @param string $timestamp iso8601 of the timestamp
86
+ * @return boolean whether the alias call succeeded
87
+ */
88
+ public function alias($from, $to, $context, $timestamp) {
89
+
90
+ $body = array(
91
+ "secret" => $this->secret,
92
+ "from" => $from,
93
+ "to" => $to,
94
+ "context" => $context,
95
+ "timestamp" => $timestamp,
96
+ "action" => "alias"
97
+ );
98
+
99
+ return $this->write($body);
100
+ }
101
+
102
+ /**
103
+ * Writes the API call to a file as line-delimited json
104
+ * @param [array] $body post body content.
105
+ * @return [boolean] whether the request succeeded
106
+ */
107
+ private function write($body) {
108
+
109
+ if (!$this->file_handle)
110
+ return false;
111
+
112
+ $content = json_encode($body);
113
+ $content.= "\n";
114
+
115
+ return fwrite($this->file_handle, $content) == strlen($content);
116
+ }
117
+ }
lib/Analytics/Analytics/Consumer/ForkCurl.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Analytics_Consumer_ForkCurl extends Analytics_QueueConsumer {
4
+
5
+ protected $type = "ForkCurl";
6
+
7
+
8
+ /**
9
+ * Creates a new queued fork consumer which queues fork and identify
10
+ * calls before adding them to
11
+ * @param string $secret
12
+ * @param array $options
13
+ * boolean "debug" - whether to use debug output, wait for response.
14
+ * number "max_queue_size" - the max size of messages to enqueue
15
+ * number "batch_size" - how many messages to send in a single request
16
+ */
17
+ public function __construct($secret, $options = array()) {
18
+ parent::__construct($secret, $options);
19
+ }
20
+
21
+ /**
22
+ * Make an async request to our API. Fork a curl process, immediately send
23
+ * to the API. If debug is enabled, we wait for the response.
24
+ * @param array $messages array of all the messages to send
25
+ * @return boolean whether the request succeeded
26
+ */
27
+ public function flushBatch($messages) {
28
+
29
+ $body = array(
30
+ "batch" => $messages,
31
+ "secret" => $this->secret
32
+ );
33
+
34
+ $payload = json_encode($body);
35
+
36
+ # Escape for shell usage.
37
+ $payload = escapeshellarg($payload);
38
+
39
+ $protocol = $this->ssl() ? "https://" : "http://";
40
+ $host = "api.segment.io";
41
+ $path = "/v1/import";
42
+ $url = $protocol . $host . $path;
43
+
44
+ $cmd = "curl -X POST -H 'Content-Type: application/json'";
45
+ $cmd.= " -d " . $payload . " '" . $url . "'";
46
+
47
+ if (!$this->debug()) {
48
+ $cmd .= " > /dev/null 2>&1 &";
49
+ }
50
+
51
+ exec($cmd, $output, $exit);
52
+
53
+ if ($exit != 0) {
54
+ $this->handleError($exit, $output);
55
+ }
56
+
57
+ return $exit == 0;
58
+ }
59
+ }
lib/Analytics/Analytics/Consumer/Socket.php ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Analytics_Consumer_Socket extends Analytics_QueueConsumer {
4
+
5
+ protected $type = "Socket";
6
+ private $socket_failed;
7
+
8
+ /**
9
+ * Creates a new socket consumer for dispatching async requests immediately
10
+ * @param string $secret
11
+ * @param array $options
12
+ * number "timeout" - the timeout for connecting
13
+ * function "error_handler" - function called back on errors.
14
+ * boolean "debug" - whether to use debug output, wait for response.
15
+ */
16
+ public function __construct($secret, $options = array()) {
17
+
18
+ if (!isset($options["timeout"]))
19
+ $options["timeout"] = 0.5;
20
+
21
+ if (!isset($options["host"]))
22
+ $options["host"] = "api.segment.io";
23
+
24
+ parent::__construct($secret, $options);
25
+ }
26
+
27
+
28
+ public function flushBatch($batch) {
29
+ $socket = $this->createSocket();
30
+
31
+ if (!$socket)
32
+ return;
33
+
34
+ $payload = array("secret" => $this->secret,
35
+ "batch" => $batch );
36
+
37
+ $payload = json_encode($payload);
38
+
39
+ $body = $this->createBody($this->options["host"], $payload);
40
+ return $this->makeRequest($socket, $body);
41
+ }
42
+
43
+
44
+ private function createSocket() {
45
+
46
+ if ($this->socket_failed)
47
+ return false;
48
+
49
+ $protocol = $this->ssl() ? "ssl" : "tcp";
50
+ $host = $this->options["host"];
51
+ $port = $this->ssl() ? 443 : 80;
52
+ $timeout = $this->options["timeout"];
53
+
54
+ try {
55
+ # Open our socket to the API Server.
56
+ $socket = pfsockopen($protocol . "://" . $host, $port, $errno,
57
+ $errstr, $timeout);
58
+
59
+ # If we couldn't open the socket, handle the error.
60
+ if ($errno != 0) {
61
+ $this->handleError($errno, $errstr);
62
+ $this->socket_failed = true;
63
+ return false;
64
+ }
65
+
66
+ return $socket;
67
+
68
+ } catch (Exception $e) {
69
+ $this->handleError($e->getCode(), $e->getMessage());
70
+ $this->socket_failed = true;
71
+ return false;
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Attempt to write the request to the socket, wait for response if debug
77
+ * mode is enabled.
78
+ * @param stream $socket the handle for the socket
79
+ * @param string $req request body
80
+ * @return boolean $success
81
+ */
82
+ private function makeRequest($socket, $req, $retry = true) {
83
+
84
+ $bytes_written = 0;
85
+ $bytes_total = strlen($req);
86
+ $closed = false;
87
+
88
+ # Write the request
89
+ while (!$closed && $bytes_written < $bytes_total) {
90
+ try {
91
+ $written = fwrite($socket, $req);
92
+ } catch (Exception $e) {
93
+ $this->handleError($e->getCode(), $e->getMessage());
94
+ $closed = true;
95
+ }
96
+ if (!isset($written) || !$written) {
97
+ $closed = true;
98
+ } else {
99
+ $bytes_written += $written;
100
+ }
101
+ }
102
+
103
+ # If the socket has been closed, attempt to retry a single time.
104
+ if ($closed) {
105
+ fclose($socket);
106
+
107
+ if ($retry) {
108
+ $socket = $this->createSocket();
109
+ if ($socket) return $this->makeRequest($socket, $req, false);
110
+ }
111
+ return false;
112
+ }
113
+
114
+
115
+ $success = true;
116
+
117
+ if ($this->debug()) {
118
+ $res = $this->parseResponse(fread($socket, 2048));
119
+
120
+ if ($res["status"] != "200") {
121
+ $this->handleError($res["status"], $res["message"]);
122
+ $success = false;
123
+ }
124
+ }
125
+
126
+ return $success;
127
+ }
128
+
129
+
130
+ /**
131
+ * Create the body to send as the post request.
132
+ * @param string $host
133
+ * @param string $content
134
+ * @return string body
135
+ */
136
+ private function createBody($host, $content) {
137
+
138
+ $req = "";
139
+ $req.= "POST /v1/import HTTP/1.1\r\n";
140
+ $req.= "Host: " . $host . "\r\n";
141
+ $req.= "Content-Type: application/json\r\n";
142
+ $req.= "Accept: application/json\r\n";
143
+ $req.= "Content-length: " . strlen($content) . "\r\n";
144
+ $req.= "\r\n";
145
+ $req.= $content;
146
+
147
+ return $req;
148
+ }
149
+
150
+
151
+ /**
152
+ * Parse our response from the server, check header and body.
153
+ * @param string $res
154
+ * @return array
155
+ * string $status HTTP code, e.g. "200"
156
+ * string $message JSON response from the api
157
+ */
158
+ private function parseResponse($res) {
159
+
160
+ $contents = explode("\n", $res);
161
+
162
+ # Response comes back as HTTP/1.1 200 OK
163
+ # Final line contains HTTP response.
164
+ $status = explode(" ", $contents[0], 3);
165
+ $result = $contents[count($contents) - 1];
166
+
167
+ return array(
168
+ "status" => isset($status[1]) ? $status[1] : null,
169
+ "message" => $result
170
+ );
171
+ }
172
+ }
lib/Analytics/Analytics/QueueConsumer.php ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ abstract class Analytics_QueueConsumer extends Analytics_Consumer {
3
+
4
+ protected $type = "QueueConsumer";
5
+
6
+ protected $queue;
7
+ protected $max_queue_size = 1000;
8
+ protected $batch_size = 100;
9
+
10
+ /**
11
+ * Store our secret and options as part of this consumer
12
+ * @param string $secret
13
+ * @param array $options
14
+ */
15
+ public function __construct($secret, $options = array()) {
16
+ parent::__construct($secret, $options);
17
+
18
+ if (isset($options["max_queue_size"]))
19
+ $this->max_queue_size = $options["max_queue_size"];
20
+
21
+ if (isset($options["batch_size"]))
22
+ $this->batch_size = $options["batch_size"];
23
+
24
+ $this->queue = array();
25
+ }
26
+
27
+ public function __destruct() {
28
+ # Flush our queue on destruction
29
+ $this->flush();
30
+ }
31
+
32
+ /**
33
+ * Tracks a user action
34
+ * @param string $user_id user id string
35
+ * @param string $event name of the event
36
+ * @param array $properties properties associated with the event
37
+ * @param string $timestamp iso8601 of the timestamp
38
+ * @return boolean whether the track call succeeded
39
+ */
40
+ public function track($user_id, $event, $properties, $context, $timestamp) {
41
+
42
+ $body = array(
43
+ "secret" => $this->secret,
44
+ "userId" => $user_id,
45
+ "event" => $event,
46
+ "properties" => $properties,
47
+ "timestamp" => $timestamp,
48
+ "context" => $context,
49
+ "action" => "track"
50
+ );
51
+
52
+ return $this->enqueue($body);
53
+ }
54
+
55
+ /**
56
+ * Tags traits about the user.
57
+ * @param string $user_id
58
+ * @param array $traits
59
+ * @param string $timestamp iso8601 of the timestamp
60
+ * @return boolean whether the track call succeeded
61
+ */
62
+ public function identify($user_id, $traits, $context, $timestamp) {
63
+
64
+ $body = array(
65
+ "secret" => $this->secret,
66
+ "userId" => $user_id,
67
+ "traits" => $traits,
68
+ "context" => $context,
69
+ "timestamp" => $timestamp,
70
+ "action" => "identify"
71
+ );
72
+
73
+ return $this->enqueue($body);
74
+ }
75
+
76
+ /**
77
+ * Aliases from one user id to another
78
+ * @param string $from
79
+ * @param string $to
80
+ * @param array $context
81
+ * @param string $timestamp iso8601 of the timestamp
82
+ * @return boolean whether the alias call succeeded
83
+ */
84
+ public function alias($from, $to, $context, $timestamp) {
85
+
86
+ $body = array(
87
+ "secret" => $this->secret,
88
+ "from" => $from,
89
+ "to" => $to,
90
+ "context" => $context,
91
+ "timestamp" => $timestamp,
92
+ "action" => "alias"
93
+ );
94
+
95
+ return $this->enqueue($body);
96
+ }
97
+
98
+ /**
99
+ * Adds an item to our queue.
100
+ * @param mixed $item
101
+ * @return boolean whether the queue has room
102
+ */
103
+ protected function enqueue($item) {
104
+
105
+ $count = count($this->queue);
106
+
107
+ if ($count > $this->max_queue_size)
108
+ return false;
109
+
110
+ $count = array_push($this->queue, $item);
111
+
112
+ if ($count > $this->batch_size)
113
+ $this->flush();
114
+
115
+ return true;
116
+ }
117
+
118
+
119
+ /**
120
+ * Flushes our queue of messages by batching them to the server
121
+ */
122
+ protected function flush() {
123
+
124
+ $count = count($this->queue);
125
+ $success = true;
126
+
127
+ while($count > 0 && $success) {
128
+
129
+ $batch = array_splice($this->queue, 0, min($this->batch_size, $count));
130
+ $success = $this->flushBatch($batch);
131
+
132
+ $count = count($this->queue);
133
+ }
134
+
135
+ return $success;
136
+ }
137
+
138
+ /**
139
+ * Flushes a batch of messages.
140
+ * @param [type] $batch [description]
141
+ * @return [type] [description]
142
+ */
143
+ abstract function flushBatch($batch);
144
+ }
package.xml CHANGED
@@ -1,7 +1,7 @@
1
  <?xml version="1.0"?>
2
  <package>
3
  <name>Advocado</name>
4
- <version>1.0.3</version>
5
  <stability>stable</stability>
6
  <license uri="http://www.opensource.org/licenses/mit-license.php">MITL</license>
7
  <channel>community</channel>
@@ -24,11 +24,13 @@
24
  &lt;li&gt;Generate more referrals - Reward your customers for every successful referral they make and drive more sales for your business.&lt;/li&gt;&#xD;
25
  &lt;li&gt;Easy to set up - You can install our plugin or copy &amp; paste a snippet of codes to get Advocado up and running. Our set up wizard will guide you through this.&lt;/li&gt;&#xD;
26
  &lt;/ol&gt;</description>
27
- <notes>The extension is stable. You can sign up for an account immediately after you register. Do let us know if you have any feedback or comments either through our website http://getadvocado.com, or via our email: dax@getadvocado.com</notes>
 
 
28
  <authors><author><name>SY Quek</name><user>syquek</user><email>quek@getadvocado.com</email></author></authors>
29
- <date>2013-07-04</date>
30
- <time>08:57:23</time>
31
- <contents><target name="magecommunity"><dir name="GozoLabs"><dir name="Advocado"><dir name="Block"><dir name="Adminhtml"><file name="Login.php" hash="248b44bad5faaa29949350b1b66f270c"/></dir><file name="Site.php" hash="3b62c41138ac5a90e1f9432a10e6d19e"/></dir><dir name="Helper"><file name="Admin.php" hash="bcda9a0a4e383c14b8c6f7372167a14d"/><file name="Backend.php" hash="c962424ed0fead92c4e6c46a6d342af0"/><file name="Data.php" hash="d22c841dbbf286abe72b6bab27a05ede"/></dir><dir name="Model"><file name="Credentials.php" hash="f7d6c7c82369b4e71fa1ef605c7d5736"/><file name="Observer.php" hash="61a39f02e706ab3f89ab9ae6a09e3d8e"/><dir name="Resource"><dir name="Credentials"><file name="Collection.php" hash="d7061008f932a933bff5a934133f4f58"/></dir><file name="Credentials.php" hash="db06bec94d7eaf9e9511ef87296f791a"/></dir></dir><dir name="controllers"><dir name="Adminhtml"><file name="AdvocadoController.php" hash="b47e8a4eb15cb6fe497a04afa9e146e3"/></dir><file name="CartController.php" hash="e42cb1757de7760862d905b64c1a21f0"/><file name="IndexController.php" hash="105efc19158e33b8dae67e23682d08d5"/><file name="V1Controller.php" hash="2772f96a47b27412bc9cb8d96c2bbb04"/></dir><dir name="etc"><file name="adminhtml.xml" hash="7c94738fe2d40efca729d81311afa529"/><file name="config.xml" hash="7192b1520527e09a1308043291770202"/></dir><dir name="sql"><dir name="gozolabs_advocado_setup"><file name="install-0.1.0.php" hash="db8f967eb2a9af200305bde91abe423a"/></dir></dir></dir></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="layout"><file name="gozolabs_advocado.xml" hash="8a3eee7e9e393ba02fea4d67a0ddcda1"/></dir><dir name="template"><dir name="gozolabs"><dir name="advocado"><file name="login.phtml" hash="8ade68e2f79c41c5b95d585a325cf883"/></dir></dir></dir></dir></dir></dir><dir name="frontend"><dir name="base"><dir name="default"><dir name="layout"><file name="gozolabs_advocado.xml" hash="6fafe8ed3e99b66655af63d29894831a"/></dir><dir name="template"><dir name="gozolabs"><dir name="advocado"><file name="site.phtml" hash="a239ca877dd55c3c45943c428352df35"/></dir></dir></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="GozoLabs_Advocado.xml" hash="5309e7603426b687ad46a42ab565c692"/></dir></target><target name="mageskin"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="css"><file name="gozolabs_advocado.css" hash="4177c19b32d7aed880d1ce695dfc0ad7"/><file name="animate.min.css" hash="bbe717113fde11700cb83ec3d79d9de0"/></dir><dir name="js"><dir name="gozolabs_advocado"><file name="gozolabs_advocado.js" hash="65327c4aa702cd15bf6e78dcd60e8fdf"/><file name="jquery.min.js" hash="e1288116312e4728f98923c79b034b67"/></dir></dir></dir></dir></dir></target></contents>
32
  <compatible/>
33
  <dependencies><required><php><min>5.1.0</min><max>6.0.0</max></php><package><name>Mage_Core_Modules</name><channel>community</channel><min>1.6.0.0</min><max>1.7.0.2</max></package></required></dependencies>
34
  </package>
1
  <?xml version="1.0"?>
2
  <package>
3
  <name>Advocado</name>
4
+ <version>1.1.0</version>
5
  <stability>stable</stability>
6
  <license uri="http://www.opensource.org/licenses/mit-license.php">MITL</license>
7
  <channel>community</channel>
24
  &lt;li&gt;Generate more referrals - Reward your customers for every successful referral they make and drive more sales for your business.&lt;/li&gt;&#xD;
25
  &lt;li&gt;Easy to set up - You can install our plugin or copy &amp; paste a snippet of codes to get Advocado up and running. Our set up wizard will guide you through this.&lt;/li&gt;&#xD;
26
  &lt;/ol&gt;</description>
27
+ <notes>We've introduced a new mechanic to this extension. It does not allow double-dipping -- if a shopper gets an instant discount, they should not be able to redeem a coupon code for the same cart.&#xD;
28
+ &#xD;
29
+ You can sign up for an account immediately after you register. Do let us know if you have any feedback or comments either through our website http://getadvocado.com, or via our email: dax@getadvocado.com</notes>
30
  <authors><author><name>SY Quek</name><user>syquek</user><email>quek@getadvocado.com</email></author></authors>
31
+ <date>2013-08-14</date>
32
+ <time>03:56:22</time>
33
+ <contents><target name="magecommunity"><dir name="GozoLabs"><dir name="Advocado"><dir name="Block"><dir name="Adminhtml"><file name="Login.php" hash="39706986405f7348ef71192d1c86615d"/></dir><file name="Site.php" hash="3b62c41138ac5a90e1f9432a10e6d19e"/></dir><dir name="Helper"><file name="Admin.php" hash="bcda9a0a4e383c14b8c6f7372167a14d"/><file name="Analytics.php" hash="2bf2b9439ace050851ff54faf6734f31"/><file name="Backend.php" hash="125ee66f4aa5d43368679d960c165494"/><file name="Data.php" hash="c9d2abf77196411f3c618a6d5917f878"/></dir><dir name="Model"><file name="Credentials.php" hash="f7d6c7c82369b4e71fa1ef605c7d5736"/><file name="Observer.php" hash="f39c3df5aa69c724f48e4e28afdb7b06"/><dir name="Resource"><dir name="Credentials"><file name="Collection.php" hash="d7061008f932a933bff5a934133f4f58"/></dir><file name="Credentials.php" hash="db06bec94d7eaf9e9511ef87296f791a"/></dir></dir><dir name="controllers"><dir name="Adminhtml"><file name="AdvocadoController.php" hash="b47e8a4eb15cb6fe497a04afa9e146e3"/></dir><file name="CampaignController.php" hash="95d945d30e11561c06c63a65744f0998"/><file name="CartController.php" hash="e42cb1757de7760862d905b64c1a21f0"/><file name="IndexController.php" hash="105efc19158e33b8dae67e23682d08d5"/><file name="V1Controller.php" hash="1707a3e88ad6c53d39c387b4e52eda2d"/></dir><dir name="etc"><file name="adminhtml.xml" hash="7c94738fe2d40efca729d81311afa529"/><file name="config.xml" hash="9dba171f358f27c5f9e51de2d533b040"/></dir><dir name="sql"><dir name="gozolabs_advocado_setup"><file name="install-0.1.0.php" hash="db8f967eb2a9af200305bde91abe423a"/></dir></dir></dir></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="layout"><file name="gozolabs_advocado.xml" hash="8a3eee7e9e393ba02fea4d67a0ddcda1"/></dir><dir name="template"><dir name="gozolabs"><dir name="advocado"><file name="login.phtml" hash="8ade68e2f79c41c5b95d585a325cf883"/></dir></dir></dir></dir></dir></dir><dir name="frontend"><dir name="base"><dir name="default"><dir name="layout"><file name="gozolabs_advocado.xml" hash="5e560a16061cb5f792c0351b6c34bb69"/></dir><dir name="template"><dir name="gozolabs"><dir name="advocado"><file name="site.phtml" hash="a239ca877dd55c3c45943c428352df35"/></dir></dir></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="GozoLabs_Advocado.xml" hash="5309e7603426b687ad46a42ab565c692"/></dir></target><target name="mageskin"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="css"><file name="gozolabs_advocado.css" hash="4177c19b32d7aed880d1ce695dfc0ad7"/><file name="animate.min.css" hash="bbe717113fde11700cb83ec3d79d9de0"/></dir><dir name="js"><dir name="gozolabs_advocado"><file name="gozolabs_advocado.js" hash="65327c4aa702cd15bf6e78dcd60e8fdf"/><file name="jquery.min.js" hash="e1288116312e4728f98923c79b034b67"/></dir></dir></dir></dir></dir></target><target name="magelib"><dir name="Analytics"><dir name="Analytics"><file name="Client.php" hash="0648505a1ad5e4700b0a20855bb34fac"/><dir name="Consumer"><file name="File.php" hash="602b0430e01b131ef4a07f89bb548956"/><file name="ForkCurl.php" hash="83fa57b1b0f09f592e726545d58f12bc"/><file name="Socket.php" hash="b3c6aeacfb0f73ee842288cf2b6e2d56"/></dir><file name="Consumer.php" hash="8299490437cd905bd32f9294ac60b64e"/><file name="QueueConsumer.php" hash="cc20ab42b8013847f06355f32b46e94f"/></dir><file name="Analytics.php" hash="f5703a127a85f014335624016716260d"/></dir></target></contents>
34
  <compatible/>
35
  <dependencies><required><php><min>5.1.0</min><max>6.0.0</max></php><package><name>Mage_Core_Modules</name><channel>community</channel><min>1.6.0.0</min><max>1.7.0.2</max></package></required></dependencies>
36
  </package>