Bazaarvoice_Connector - Version 6.3.0

Version Notes

Plugin corresponds to version 6.0.2 of the Bazaarvoice platform

Download this release

Release Info

Developer BV DTS
Extension Bazaarvoice_Connector
Version 6.3.0
Comparing to
See all releases


Code changes from version 6.2.0 to 6.3.0

app/code/local/Bazaarvoice/Connector/Block/Questions.php CHANGED
@@ -38,13 +38,31 @@ class Bazaarvoice_Connector_Block_Questions extends Mage_Core_Block_Template
38
  str_replace(' ', '_', Mage::getStoreConfig('bazaarvoice/general/deployment_zone')) .
39
  '-' . Mage::getStoreConfig('bazaarvoice/general/locale');
40
  }
41
- $bv = new BV(array(
42
- 'deployment_zone_id' => $deploymentZoneId, // replace with your display code (BV provided)
43
- 'product_id' => Mage::helper('bazaarvoice')->getProductId(Mage::registry('current_product')), // replace with product id
 
 
 
 
 
 
 
 
 
 
 
44
  'cloud_key' => Mage::getStoreConfig('bazaarvoice/general/cloud_seo_key'), // BV provided value
 
 
45
  'staging' => (Mage::getStoreConfig('bazaarvoice/general/environment') == "staging" ? TRUE : FALSE)
46
- ));
47
- $seoContent = $bv->questions->renderSeo();
 
 
 
 
 
48
  }
49
 
50
  return $seoContent;
38
  str_replace(' ', '_', Mage::getStoreConfig('bazaarvoice/general/deployment_zone')) .
39
  '-' . Mage::getStoreConfig('bazaarvoice/general/locale');
40
  }
41
+ $product = Mage::registry('current_product');
42
+ $productUrl = Mage::helper('core/url')->getCurrentUrl();
43
+ $parts = parse_url($productUrl);
44
+ if(isset($parts['query'])) {
45
+ parse_str($parts['query'], $query);
46
+ unset($query['bvrrp']);
47
+ $baseUrl = $parts['scheme'] . '://' . $parts['host'] . $parts['path'] . '?' . http_build_query($query);
48
+ } else {
49
+ $baseUrl = $productUrl;
50
+ }
51
+ $params = array(
52
+ 'seo_sdk_enabled' => TRUE,
53
+ 'bv_root_folder' => $deploymentZoneId, // replace with your display code (BV provided)
54
+ 'subject_id' => Mage::helper('bazaarvoice')->getProductId($product), // replace with product id
55
  'cloud_key' => Mage::getStoreConfig('bazaarvoice/general/cloud_seo_key'), // BV provided value
56
+ 'base_url' => $baseUrl,
57
+ 'page_url' => $productUrl,
58
  'staging' => (Mage::getStoreConfig('bazaarvoice/general/environment') == "staging" ? TRUE : FALSE)
59
+ );
60
+ if($this->getRequest()->getParam('bvreveal') == 'debug')
61
+ $params['bvreveal'] = 'debug';
62
+
63
+ $bv = new BV($params);
64
+ $seoContent = $bv->questions->getContent();
65
+ $seoContent .= '<!-- BV Questions Parameters: ' . print_r($params, 1) . '-->';
66
  }
67
 
68
  return $seoContent;
app/code/local/Bazaarvoice/Connector/Block/Reviews.php CHANGED
@@ -38,13 +38,31 @@ class Bazaarvoice_Connector_Block_Reviews extends Mage_Core_Block_Template
38
  str_replace(' ', '_', Mage::getStoreConfig('bazaarvoice/general/deployment_zone')) .
39
  '-' . Mage::getStoreConfig('bazaarvoice/general/locale');
40
  }
41
- $bv = new BV(array(
42
- 'deployment_zone_id' => $deploymentZoneId, // replace with your display code (BV provided)
43
- 'product_id' => Mage::helper('bazaarvoice')->getProductId(Mage::registry('current_product')), // replace with product id
 
 
 
 
 
 
 
 
 
 
 
44
  'cloud_key' => Mage::getStoreConfig('bazaarvoice/general/cloud_seo_key'), // BV provided value
 
 
45
  'staging' => (Mage::getStoreConfig('bazaarvoice/general/environment') == "staging" ? TRUE : FALSE)
46
- ));
47
- $seoContent = $bv->reviews->renderSeo();
 
 
 
 
 
48
  }
49
 
50
  return $seoContent;
38
  str_replace(' ', '_', Mage::getStoreConfig('bazaarvoice/general/deployment_zone')) .
39
  '-' . Mage::getStoreConfig('bazaarvoice/general/locale');
40
  }
41
+ $product = Mage::registry('current_product');
42
+ $productUrl = Mage::helper('core/url')->getCurrentUrl();
43
+ $parts = parse_url($productUrl);
44
+ if(isset($parts['query'])) {
45
+ parse_str($parts['query'], $query);
46
+ unset($query['bvrrp']);
47
+ $baseUrl = $parts['scheme'] . '://' . $parts['host'] . $parts['path'] . '?' . http_build_query($query);
48
+ } else {
49
+ $baseUrl = $productUrl;
50
+ }
51
+ $params = array(
52
+ 'seo_sdk_enabled' => TRUE,
53
+ 'bv_root_folder' => $deploymentZoneId, // replace with your display code (BV provided)
54
+ 'subject_id' => Mage::helper('bazaarvoice')->getProductId($product), // replace with product id
55
  'cloud_key' => Mage::getStoreConfig('bazaarvoice/general/cloud_seo_key'), // BV provided value
56
+ 'base_url' => $baseUrl,
57
+ 'page_url' => $productUrl,
58
  'staging' => (Mage::getStoreConfig('bazaarvoice/general/environment') == "staging" ? TRUE : FALSE)
59
+ );
60
+ if($this->getRequest()->getParam('bvreveal') == 'debug')
61
+ $params['bvreveal'] = 'debug';
62
+
63
+ $bv = new BV($params);
64
+ $seoContent = $bv->reviews->getContent();
65
+ $seoContent .= '<!-- BV Reviews Parameters: ' . print_r($params, 1) . '-->';
66
  }
67
 
68
  return $seoContent;
app/code/local/Bazaarvoice/Connector/Model/ExportPurchaseFeed.php CHANGED
@@ -330,7 +330,7 @@ class Bazaarvoice_Connector_Model_ExportPurchaseFeed extends Mage_Core_Model_Abs
330
  * @param Mage_Core_Model_Website $website
331
  * @return int
332
  */
333
- private function processOrdersForWebsite(Varien_Io_File $ioObject, Mage_Core_Model_Website $website)
334
  {
335
  // Get a collection of all the orders
336
  $orders = Mage::getModel('sales/order')->getCollection();
@@ -370,7 +370,7 @@ class Bazaarvoice_Connector_Model_ExportPurchaseFeed extends Mage_Core_Model_Abs
370
  * @param Mage_Core_Model_Store_Group $group
371
  * @return int
372
  */
373
- private function processOrdersForGroup(Varien_Io_File $ioObject, Mage_Core_Model_Store_Group $group)
374
  {
375
  // Get a collection of all the orders
376
  $orders = Mage::getModel('sales/order')->getCollection();
@@ -410,7 +410,7 @@ class Bazaarvoice_Connector_Model_ExportPurchaseFeed extends Mage_Core_Model_Abs
410
  * @param Mage_Core_Model_Store $store
411
  * @return int
412
  */
413
- private function processOrdersForStore(Varien_Io_File $ioObject, Mage_Core_Model_Store $store)
414
  {
415
  // Get a collection of all the orders
416
  $orders = Mage::getModel('sales/order')->getCollection();
@@ -446,7 +446,7 @@ class Bazaarvoice_Connector_Model_ExportPurchaseFeed extends Mage_Core_Model_Abs
446
  * @param $orders
447
  * @return int
448
  */
449
- private function writeOrdersToFile(Varien_Io_File $ioObject, $orders)
450
  {
451
  // Get ref to BV helper
452
  /* @var $bvHelper Bazaarvoice_Connector_Helper_Data */
@@ -568,7 +568,7 @@ class Bazaarvoice_Connector_Model_ExportPurchaseFeed extends Mage_Core_Model_Abs
568
  }
569
  }
570
 
571
- private function orderToString(Mage_Sales_Model_Order $order)
572
  {
573
  return "\nOrder {Id: " . $order->getIncrementId()
574
  . "\n\tCustomerId: " . $order->getCustomerId()
@@ -583,7 +583,7 @@ class Bazaarvoice_Connector_Model_ExportPurchaseFeed extends Mage_Core_Model_Abs
583
  . "\n}";
584
  }
585
 
586
- private function getTriggeringEventDate(Mage_Sales_Model_Order $order, $triggeringEvent)
587
  {
588
  $timestamp = strtotime($order->getCreatedAtDate());
589
 
@@ -594,17 +594,17 @@ class Bazaarvoice_Connector_Model_ExportPurchaseFeed extends Mage_Core_Model_Abs
594
  return date('c', $timestamp);
595
  }
596
 
597
- private function getNumDaysLookbackStartDate()
598
  {
599
  return date('Y-m-d', strtotime(date('Y-m-d', time()) . ' -' . self::NUM_DAYS_LOOKBACK . ' days'));
600
  }
601
 
602
- private function getDelayDaysThresholdTimestamp($delayDaysSinceEvent)
603
  {
604
  return time() - (24 * 60 * 60 * $delayDaysSinceEvent);
605
  }
606
 
607
- private function shouldIncludeOrder(Mage_Sales_Model_Order $order, $triggeringEvent, $delayDaysSinceEvent)
608
  {
609
  // Have we already included this order in a previous feed?
610
  if ($order->getData(self::ALREADY_SENT_IN_FEED_FLAG) === '1') {
@@ -681,7 +681,7 @@ class Bazaarvoice_Connector_Model_ExportPurchaseFeed extends Mage_Core_Model_Abs
681
  return true;
682
  }
683
 
684
- private function hasOrderCompletelyShipped(Mage_Sales_Model_Order $order)
685
  {
686
  $hasOrderCompletelyShipped = true;
687
  $items = $order->getAllItems();
@@ -696,7 +696,7 @@ class Bazaarvoice_Connector_Model_ExportPurchaseFeed extends Mage_Core_Model_Abs
696
  return $hasOrderCompletelyShipped;
697
  }
698
 
699
- private function getLatestShipmentDate(Mage_Sales_Model_Order $order)
700
  {
701
  $latestShipmentTimestamp = 0;
702
 
330
  * @param Mage_Core_Model_Website $website
331
  * @return int
332
  */
333
+ protected function processOrdersForWebsite(Varien_Io_File $ioObject, Mage_Core_Model_Website $website)
334
  {
335
  // Get a collection of all the orders
336
  $orders = Mage::getModel('sales/order')->getCollection();
370
  * @param Mage_Core_Model_Store_Group $group
371
  * @return int
372
  */
373
+ protected function processOrdersForGroup(Varien_Io_File $ioObject, Mage_Core_Model_Store_Group $group)
374
  {
375
  // Get a collection of all the orders
376
  $orders = Mage::getModel('sales/order')->getCollection();
410
  * @param Mage_Core_Model_Store $store
411
  * @return int
412
  */
413
+ protected function processOrdersForStore(Varien_Io_File $ioObject, Mage_Core_Model_Store $store)
414
  {
415
  // Get a collection of all the orders
416
  $orders = Mage::getModel('sales/order')->getCollection();
446
  * @param $orders
447
  * @return int
448
  */
449
+ protected function writeOrdersToFile(Varien_Io_File $ioObject, $orders)
450
  {
451
  // Get ref to BV helper
452
  /* @var $bvHelper Bazaarvoice_Connector_Helper_Data */
568
  }
569
  }
570
 
571
+ protected function orderToString(Mage_Sales_Model_Order $order)
572
  {
573
  return "\nOrder {Id: " . $order->getIncrementId()
574
  . "\n\tCustomerId: " . $order->getCustomerId()
583
  . "\n}";
584
  }
585
 
586
+ protected function getTriggeringEventDate(Mage_Sales_Model_Order $order, $triggeringEvent)
587
  {
588
  $timestamp = strtotime($order->getCreatedAtDate());
589
 
594
  return date('c', $timestamp);
595
  }
596
 
597
+ protected function getNumDaysLookbackStartDate()
598
  {
599
  return date('Y-m-d', strtotime(date('Y-m-d', time()) . ' -' . self::NUM_DAYS_LOOKBACK . ' days'));
600
  }
601
 
602
+ protected function getDelayDaysThresholdTimestamp($delayDaysSinceEvent)
603
  {
604
  return time() - (24 * 60 * 60 * $delayDaysSinceEvent);
605
  }
606
 
607
+ protected function shouldIncludeOrder(Mage_Sales_Model_Order $order, $triggeringEvent, $delayDaysSinceEvent)
608
  {
609
  // Have we already included this order in a previous feed?
610
  if ($order->getData(self::ALREADY_SENT_IN_FEED_FLAG) === '1') {
681
  return true;
682
  }
683
 
684
+ protected function hasOrderCompletelyShipped(Mage_Sales_Model_Order $order)
685
  {
686
  $hasOrderCompletelyShipped = true;
687
  $items = $order->getAllItems();
696
  return $hasOrderCompletelyShipped;
697
  }
698
 
699
+ protected function getLatestShipmentDate(Mage_Sales_Model_Order $order)
700
  {
701
  $latestShipmentTimestamp = 0;
702
 
app/code/local/Bazaarvoice/Connector/etc/config.xml CHANGED
@@ -8,7 +8,7 @@
8
  <config>
9
  <modules>
10
  <Bazaarvoice_Connector>
11
- <version>6.2.0</version>
12
  <depends>
13
  <!-- no dependencies -->
14
  </depends>
8
  <config>
9
  <modules>
10
  <Bazaarvoice_Connector>
11
+ <version>6.3.0</version>
12
  <depends>
13
  <!-- no dependencies -->
14
  </depends>
lib/Bazaarvoice/BVFooter.php ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * BV PHP SEO SDK Footer
5
+ */
6
+ class BVFooter {
7
+ const VERSION = '3.1.0';
8
+
9
+ private $base;
10
+ private $url;
11
+ private $access_method;
12
+ private $msg;
13
+
14
+ /**
15
+ * BVFooter Class Constructor
16
+ *
17
+ * @access public
18
+ * @param array ($base) - base class parameters
19
+ * @param string ($url) - SEO url
20
+ * @param string ($access_method) - access method
21
+ * @param string ($msg) - build message
22
+ * @return object
23
+ */
24
+ public function __construct($base, $access_method, $msg) {
25
+ $this->base = $base;
26
+ $this->access_method = $access_method;
27
+ $this->msg = $msg;
28
+ }
29
+
30
+ /**
31
+ * buildSDKFooter
32
+ *
33
+ * Returns hidden SDK footer.
34
+ *
35
+ * @access public
36
+ * @return string Html formatted footer.
37
+ */
38
+ public function buildSDKFooter() {
39
+ $method_type = !empty($this->base->config['internal_file_path']) ? 'LOCAL' : 'CLOUD';
40
+ $access_method = $this->access_method;
41
+ $time_end = microtime(true);
42
+
43
+ if (!empty($this->base->start_time)) {
44
+ $exec_time = round(($time_end - $this->base->start_time) * 1000, 2);
45
+ } else {
46
+ $exec_time = 0;
47
+ }
48
+ $content_type = mb_strtoupper($this->base->config['content_type']);
49
+ $subject_type = mb_strtoupper($this->base->config['subject_type']);
50
+
51
+ $footer = "\n" . '<ul id="BVSEOSDK_meta" style="display:none !important;">';
52
+ $footer .= "\n" . ' <li data-bvseo="sdk">bvseo_sdk, p_sdk, ' . self::VERSION . '</li>';
53
+ $footer .= "\n" . ' <li data-bvseo="sp_mt">' . $method_type . ', ' . $access_method . ', ' . $exec_time . 'ms</li>';
54
+ $footer .= "\n" . ' <li data-bvseo="ct_st">' . $content_type . ', ' . $subject_type . '</li>';
55
+ if (!empty($this->msg)) {
56
+ $footer .= "\n" . ' <li data-bvseo="ms">bvseo-msg: ' . $this->msg . '</li>';
57
+ }
58
+ $footer .= "\n" . '</ul>';
59
+
60
+ return $footer;
61
+ }
62
+
63
+ /**
64
+ * buildSDKDebugFooter
65
+ *
66
+ * Returns hidden SDK debug footer.
67
+ *
68
+ * @access public
69
+ * @return string Html formatted debug footer.
70
+ */
71
+ public function buildSDKDebugFooter() {
72
+ $staging = !empty($this->base->config['staging']) ? 'TRUE' : 'FALSE';
73
+ $testing = !empty($this->base->config['testing']) ? 'TRUE' : 'FALSE';
74
+ $sdk_enabled = !empty($this->base->config['seo_sdk_enabled']) ? 'TRUE' : 'FALSE';
75
+ $ssl_enabled = !empty($this->base->config['ssl_enabled']) ? 'TRUE' : 'FALSE';
76
+ $proxy_host = !empty($this->base->config['proxy_host']) ? $this->base->config['proxy_host'] : 'none';
77
+ $proxy_port = !empty($this->base->config['proxy_port']) ? $this->base->config['proxy_port'] : '0';
78
+ $local_seo_file_root = (!empty($this->base->config['load_seo_files_locally'])) ? $this->base->config['local_seo_file_root'] : 'FALSE';
79
+ $content_type = mb_strtoupper($this->base->config['content_type']);
80
+ $subject_type = mb_strtoupper($this->base->config['subject_type']);
81
+ if (!empty($this->base->config['page_params']['subject_id'])
82
+ && !empty($this->base->config['page_params']['content_type'])
83
+ && $this->base->config['page_params']['content_type'] == $this->base->config['content_type']) {
84
+ $subject_id = $this->base->config['page_params']['subject_id'];
85
+ } else {
86
+ $subject_id = $this->base->config['subject_id'];
87
+ }
88
+
89
+ $footer = "\n" . '<ul id="BVSEOSDK_DEBUG" style="display:none;">';
90
+
91
+ $footer .= "\n" . ' <li data-bvseo="staging">' . $staging . '</li>';
92
+ $footer .= "\n" . ' <li data-bvseo="testing">' . $testing . '</li>';
93
+ $footer .= "\n" . ' <li data-bvseo="seo.sdk.enabled">' . $sdk_enabled . '</li>';
94
+ if (!isset($this->base->config['subject_type']) || $this->base->config['subject_type'] != 'seller') {
95
+ $footer .= "\n" . ' <li data-bvseo="stagingS3Hostname">' . $this->base->bv_config['seo-domain']['staging'] . '</li>';
96
+ $footer .= "\n" . ' <li data-bvseo="productionS3Hostname">' . $this->base->bv_config['seo-domain']['production'] . '</li>';
97
+ $footer .= "\n" . ' <li data-bvseo="testingStagingS3Hostname">' . $this->base->bv_config['seo-domain']['testing_staging'] . '</li>';
98
+ $footer .= "\n" . ' <li data-bvseo="testingProductionS3Hostname">' . $this->base->bv_config['seo-domain']['testing_production'] . '</li>';
99
+ }
100
+ $footer .= "\n" . ' <li data-bvseo="proxyHost">' . $proxy_host . '</li>';
101
+ $footer .= "\n" . ' <li data-bvseo="proxyPort">' . $proxy_port . '</li>';
102
+ $footer .= "\n" . ' <li data-bvseo="seo.sdk.execution.timeout.bot">' . $this->base->config['execution_timeout_bot'] . '</li>';
103
+ $footer .= "\n" . ' <li data-bvseo="seo.sdk.execution.timeout">' . $this->base->config['execution_timeout'] . '</li>';
104
+ $footer .= "\n" . ' <li data-bvseo="localSEOFileRoot">' . $local_seo_file_root . '</li>';
105
+ $footer .= "\n" . ' <li data-bvseo="cloudKey">' . $this->base->config['cloud_key'] . '</li>';
106
+ $footer .= "\n" . ' <li data-bvseo="bv.root.folder">' . $this->base->config['bv_root_folder'] . '</li>';
107
+ $footer .= "\n" . ' <li data-bvseo="seo.sdk.charset">' . $this->base->config['charset'] . '</li>';
108
+ $footer .= "\n" . ' <li data-bvseo="seo.sdk.ssl.enabled">' . $ssl_enabled . '</li>';
109
+ $footer .= "\n" . ' <li data-bvseo="crawlerAgentPattern">' . $this->base->config['crawler_agent_pattern'] . '</li>';
110
+ $footer .= "\n" . ' <li data-bvseo="subjectID">' . urlencode($subject_id) . '</li>';
111
+
112
+
113
+ $footer .= "\n" . ' <li data-bvseo="en">' . $sdk_enabled . '</li>';
114
+ $footer .= "\n" . ' <li data-bvseo="pn">bvseo-' . $this->base->config['page'] . '</li>';
115
+ $footer .= "\n" . ' <li data-bvseo="userAgent">' . $_SERVER['HTTP_USER_AGENT'] . '</li>';
116
+ $footer .= "\n" . ' <li data-bvseo="pageURI">' . $this->base->config['page_url'] . '</li>';
117
+ $footer .= "\n" . ' <li data-bvseo="baseURI">' . $this->base->config['base_url'] . '</li>';
118
+ $footer .= "\n" . ' <li data-bvseo="contentType">' . $content_type . '</li>';
119
+ $footer .= "\n" . ' <li data-bvseo="subjectType">' . $subject_type . '</li>';
120
+ if (!empty($this->base->seo_url)) {
121
+ $footer .= "\n" . ' <li data-bvseo="contentURL">' . $this->base->seo_url . '</li>';
122
+ }
123
+
124
+ $footer .= "\n" . '</ul>';
125
+
126
+ return $footer;
127
+ }
128
+
129
+ }
lib/Bazaarvoice/BVUtility.php ADDED
@@ -0,0 +1,254 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Tick function for execTimer.
5
+ *
6
+ * @param int ($start) - start time in ms
7
+ * @param int ($exec_time_ms) - execution time in ms
8
+ * @param bool ($is_bot) - shows the mode in which script was run
9
+ */
10
+ function tick_timer($start, $exec_time, $is_bot) {
11
+ static $once = true;
12
+ if ((microtime(1) - $start) > $exec_time) {
13
+ if ($once) {
14
+ $once = false;
15
+ throw new Exception('Execution timed out' . ($is_bot ? ' for search bot' : '') . ', exceeded ' . $exec_time * 1000 . 'ms');
16
+ }
17
+ }
18
+ }
19
+
20
+ /**
21
+ * BV PHP SEO SDK Utilities.
22
+ */
23
+ class BVUtility {
24
+ public static $supportedContentTypes = array(
25
+ 'r' => 'REVIEWS',
26
+ 'q' => 'QUESTIONS',
27
+ 's' => 'STORIES',
28
+ 'u' => 'UNIVERSAL',
29
+ 'sp'=> 'SPOTLIGHTS'
30
+ );
31
+ private static $supportedSubjectTypes = array(
32
+ 'p' => 'PRODUCT',
33
+ 'c' => 'CATEGORY',
34
+ 'e' => 'ENTRY',
35
+ 'd' => 'DETAIL',
36
+ 's' => 'SELLER'
37
+ );
38
+
39
+ /**
40
+ * Method used to limit execution time of the script.
41
+ *
42
+ * @access public
43
+ * @param int ($exec_time_ms) - execution time in ms
44
+ * @param bool ($is_bot) - shows the mode in which script was run
45
+ */
46
+ public static function execTimer($exec_time_ms, $is_bot = false, $start = 0) {
47
+ $exec_time = $exec_time_ms / 1000;
48
+ declare(ticks = 1); // or more if 1 takes too much time
49
+ if (empty($start)) {
50
+ $start = microtime(1);
51
+ }
52
+ register_tick_function('tick_timer', $start, $exec_time, $is_bot);
53
+ }
54
+
55
+ /**
56
+ * Method used to stop execution time checker.
57
+ *
58
+ * @access public
59
+ */
60
+ public static function stopTimer() {
61
+ unregister_tick_function('tick_timer');
62
+ }
63
+
64
+ /**
65
+ * Parse the provided "bvstate" parameter value.
66
+ *
67
+ * @access public
68
+ * @param string $bvstate - Value of the bvstate parameter.
69
+ * @return array - parsed "bvstate" parameters.
70
+ */
71
+ public static function getBVStateHash($bvstate) {
72
+ $bvStateHash = array();
73
+ $bvp = mb_split("/", $bvstate);
74
+ foreach ($bvp as $param) {
75
+ $key = static::mb_trim(mb_substr($param, 0, mb_strpos($param, ':')));
76
+ $bvStateHash[$key] = static::mb_trim(mb_substr($param, mb_strpos($param, ':') + 1));
77
+ }
78
+ return $bvStateHash;
79
+ }
80
+
81
+ /**
82
+ * Checks content type or subject type is supported.
83
+ * If type is not supported throw exception.
84
+ *
85
+ * @access public
86
+ * @param string ($type) - content type or subject type which have to be checked.
87
+ * @param string ($typeType) - default 'ct', mark of type 'ct' - content type, 'st' - subject type
88
+ * @return boolean True if type is correct and no exception was thrown.
89
+ */
90
+ public static function checkType($type, $typeType = 'ct') {
91
+ if ($typeType == 'st') {
92
+ $typeName = 'subject type';
93
+ $typeArray = static::$supportedSubjectTypes;
94
+ } else {
95
+ $typeName = 'content type';
96
+ $typeArray = static::$supportedContentTypes;
97
+ }
98
+ if (!array_key_exists(mb_strtolower($type), $typeArray)) {
99
+ foreach ($typeArray as $key => $value) {
100
+ $supportList[] = $key . '=' . $value;
101
+ }
102
+ throw new Exception('Obtained not supported ' . $typeName
103
+ . '. BV Class supports following ' . $typeName . ': '
104
+ . implode(', ', $supportList));
105
+ }
106
+
107
+ return true;
108
+ }
109
+
110
+ /**
111
+ * Generates an array of parameters from the bvstate parameter value.
112
+ *
113
+ * @access public
114
+ * @param string $bvstate - "bvstate" parameter value.
115
+ * @return array - array of parameters that are ready to use in script.
116
+ */
117
+ public static function getBVStateParams($bvstate) {
118
+ $bvStateHash = self::getBVStateHash($bvstate);
119
+ $params = array();
120
+
121
+ // If the content type 'ct' parameter is not present, then ignore bvstate.
122
+ if (empty($bvStateHash['ct'])) {
123
+ return $params;
124
+ }
125
+
126
+ if (!empty($bvStateHash)) {
127
+ if (!empty($bvStateHash['id'])) {
128
+ $params['subject_id'] = $bvStateHash['id'];
129
+ }
130
+ if (!empty($bvStateHash['pg'])) {
131
+ $params['page'] = $bvStateHash['pg'];
132
+ }
133
+ if (!empty($bvStateHash['ct'])) {
134
+ $cType = $bvStateHash['ct'];
135
+ self::checkType($cType, 'ct');
136
+ $params['content_type'] = mb_strtolower(self::$supportedContentTypes[$cType]);
137
+ }
138
+ if (!empty($bvStateHash['st'])) {
139
+ $sType = $bvStateHash['st'];
140
+ self::checkType($sType, 'st');
141
+ $params['subject_type'] = mb_strtolower(self::$supportedSubjectTypes[$sType]);
142
+ }
143
+ if (!empty($bvStateHash['reveal'])) {
144
+ $params['bvreveal'] = $bvStateHash['reveal'];
145
+ }
146
+ }
147
+
148
+ if (!empty($params)) {
149
+ // This acts as a flag to tell us that a useful bvstate value was in fact
150
+ // extracted from the URL.
151
+ $params['base_url_bvstate'] = TRUE;
152
+ }
153
+ if (empty($params['page'])) {
154
+ $params['page'] = '1';
155
+ }
156
+
157
+ return $params;
158
+ }
159
+
160
+ /**
161
+ * Parse name=value parameters from the URL query string, fragment, and
162
+ * _escaped_fragment_.
163
+ *
164
+ * @access public
165
+ * @param string ($url) - The URL.
166
+ * @return array - An array of parameters values indexed by parameter names.
167
+ */
168
+ public static function parseUrlParameters($url) {
169
+ // Why are we doing things in this devious way? The answer is to be as
170
+ // multibyte-supportive as possible. Most of the URL-parsing tools in the
171
+ // toolbox appear to be only varying degrees of multibyte-supportive; good
172
+ // for UTF-8 but not so great if you venture beyond that.
173
+
174
+ // Break down the URL into a mix of things, some of which are name=value
175
+ // pairs.
176
+ $params = array();
177
+ $chunks = mb_split('\?|&amp;|&|#!|#|_escaped_fragment_=|%26', $url);
178
+ foreach ($chunks as $chunk) {
179
+ // If this is name=value, then there will be two items.
180
+ $values = mb_split('=', $chunk);
181
+ if (sizeof($values) == 2) {
182
+ // Since we're moving left to right in the URL, and we want query string
183
+ // to win over fragment if there are the same parameters in both, then
184
+ // only add if not already there.
185
+ if (!isset($params[$values[0]])) {
186
+ $params[$values[0]] = $values[1];
187
+ }
188
+ }
189
+ }
190
+ return $params;
191
+ }
192
+
193
+ /**
194
+ * Remove a parameter from the provided URL.
195
+ *
196
+ * This will remove the named parameter wherever it occurs as name=value in
197
+ * the URL via a simple regex replacement. This is crude but the most
198
+ * straightforward way of going about this in PHP.
199
+ *
200
+ * If there is a query string delimeter following the name=value parameter
201
+ * then that will also be removed.
202
+ *
203
+ * E.g. we're expecting to remove the bvstate from URLs such as:
204
+ *
205
+ * http://example.com/product/123?bvstate=pg:4/ct:r
206
+ * http://example.com/product/123#!bvstate=pg:4/ct:r
207
+ *
208
+ * This will only be used for Bazaarvoice SEO parameters, so apologies in
209
+ * advance to the one person in the universe for whom bvstate=xyz is a vital
210
+ * part of the URL path.
211
+ *
212
+ * Note that the fragment isn't passed to the server, so we're not really
213
+ * going to see that in practice. Attention is given to that here for the
214
+ * sake of completeness.
215
+ *
216
+ * @access public
217
+ * @param string ($url) - The URL.
218
+ * @param string ($paramName) - Name of the parameter to be removed.
219
+ * @return string - The updated URL.
220
+ */
221
+ public static function removeUrlParam($url, $paramName) {
222
+ // The ereg POSIX regex functions are all greedy all the time, which makes
223
+ // this harder than it has to be.
224
+ //
225
+ // Big assumption: our seo link values will never contain the % character.
226
+ //
227
+ // http://example.com/product/123?bvstate=pg:4/ct:r&amp;a=b
228
+ $url = mb_ereg_replace($paramName . '=[^&#%]*&amp;', '', $url);
229
+ // http://example.com/product/123?bvstate=pg:4/ct:r&a=b
230
+ // http://example.com/product/123?#!bvstate=pg:4/ct:r&a=b
231
+ // http://example.com/product/123?_escaped_fragment_=bvstate=pg:4/ct:r%26a=b
232
+ $url = mb_ereg_replace($paramName . '=[^&#%]*(&|%26)', '', $url);
233
+ // http://example.com/product/123?bvstate=pg:4/ct:r#!x/y/z
234
+ $url = mb_ereg_replace($paramName . '=[^&#]*#', '#', $url);
235
+ // This one last as it will break everything if we haven't already dealt
236
+ // with all of the cases, since .* is always greedy in POSIX regex.
237
+ // http://example.com/product/123?bvstate=pg:4/ct:r
238
+ $url = mb_ereg_replace($paramName . '=[^&#%]*$', '', $url);
239
+ return $url;
240
+ }
241
+
242
+ /**
243
+ * A multibyte-safe trim.
244
+ * (http://stackoverflow.com/questions/10066647/multibyte-trim-in-php/10067670#10067670)
245
+ *
246
+ * @access public
247
+ * @param string ($str) - The string that will be trimmed.
248
+ * @return string - The trimmed string.
249
+ */
250
+ public static function mb_trim($str) {
251
+ return mb_ereg_replace('(^\s+)|(\s+$)', '', $str);
252
+ }
253
+
254
+ }
lib/Bazaarvoice/bvseosdk.php CHANGED
@@ -3,609 +3,1039 @@
3
  /**
4
  * BV PHP SEO SDK
5
  *
6
- * Base code to power either SEO or SEO and display. This SDK
7
- * is provided as is and Bazaarvoice, Inc. is not responsbile
8
- * for future mainentence or support. You are free to modify
9
- * this SDK as needed to suit your needs.
10
  *
11
  * This SDK was built with the following assumptions:
12
- * - you are running PHP 5 or greater
13
- * - you have the curl library installed
14
- * - every request has the user agent header
15
- * in it (if using a CDN like Akamai additional configuration
16
- * maybe required).
17
  *
18
  */
19
-
20
  /**
21
  * Example usage:
22
  *
23
  * require(bvsdk.php);
24
  *
25
  * $bv = new BV(array(
26
- * 'deployment_zone_id' => '1234-en_US',
27
- * 'product_id' => 'XXYYY',
28
- * 'cloud_key' => 'company-cdfa682b84bef44672efed074093ccd3',
29
- * 'staging' => FALSE
30
  * ));
31
- *
32
  */
 
 
 
 
 
 
 
 
 
33
 
34
  // ------------------------------------------------------------------------
35
 
36
  /**
37
  * BV Class
38
- *
39
- * When you instantiate the BV class, pass it's constructor an array
40
- * containing the following key value pairs.
41
- *
42
  * Required fields:
43
- * deployment_zone_id (string)
44
- * product_id (string)
45
- * cloud_key (string)
46
  *
47
  * Optional fields
48
- * current_page_url (string) (defaults to detecting the current_page automtically)
49
- * staging (boolean) (defaults to false, need to put true for testing with staging data)
50
- * subject_type (string) (defaults to product, for questions you can pass in categories here if needed)
51
- * latency_timeout (int) (in millseconds) (defaults to 1000ms)
52
- * bv_product (string) (defaults to reviews)
53
- * bot_list (string) (defualts to msnbot|googlebot|teoma|bingbot|yandexbot|yahoo)
 
 
 
 
 
54
  */
55
-
56
  class BV {
57
 
58
- /**
59
- * BV Class Constructor
60
- *
61
- * The constructor takes in all the arguments via a single array.
62
- *
63
- * @access public
64
- * @param array
65
- * @return object
66
- */
67
- public function __construct($params = array())
68
- {
69
- // check to make sure we have the required paramaters
70
- if( empty($params) OR ! $params['deployment_zone_id'] OR ! $params['product_id'])
71
- {
72
- throw new Exception('BV Class missing required paramters.
73
- BV expects an array with the following indexes: deployment_zone_id (string) and product_id
74
- (string). ');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  }
 
 
 
 
 
 
 
 
 
 
 
76
 
77
- // config array, defaults are defined here
78
- $this->config = array(
79
- 'staging' => FALSE,
80
- 'subject_type' => 'product',
81
- 'latency_timeout' => 1000,
82
- 'current_page_url' => isset($params['current_page_url']) ? $params['current_page_url'] : '', //get the current page url passed in as a "parameter"
83
- 'base_page_url' => $this->_getCurrentUrl(),
84
- 'bot_detection' => FALSE, // bot detection should only be enabled if average execution time regularly exceeds 350ms.
85
- 'include_display_integration_code' => FALSE,
86
- 'client_name' => $params['deployment_zone_id'],
87
- 'internal_file_path' => FALSE,
88
- 'bot_list' => 'msnbot|google|teoma|bingbot|yandexbot|yahoo', // used in regex to determine if request is a bot or not
89
 
 
 
 
 
 
90
  );
 
91
 
92
- // merge passed in params with defualts for config.
93
- $this->config = array_merge($this->config, $params);
 
 
 
 
 
 
 
94
 
95
- // setup the reviews object
96
- $this->reviews = new Reviews($this->config);
 
 
 
 
 
 
 
 
97
 
98
- // setup the questions object
99
- $this->questions = new Questions($this->config);
100
-
101
- // setup the timer object
102
 
103
- }
104
 
105
- // since this is used to set the default for an optional config option it is
106
- // included in the BV class.
107
- public function _getCurrentUrl(){
108
- // depending on protocal set the
109
- // beginging of url and defualt port
110
- if(isset($_SERVER["HTTPS"])){
111
- $url = 'https://';
112
- $defaultPort = '443';
113
- }else{
114
- $url = 'http://';
115
- $defaultPort = '80';
116
- }
117
 
118
- $url .= $_SERVER["SERVER_NAME"];
 
 
 
 
119
 
120
- // if there is a port other than the defaultPort being used it needs to be included
121
- if ($_SERVER["SERVER_PORT"] != $defaultPort){
122
- $url .= ":".$_SERVER["SERVER_PORT"];
123
- }
124
-
125
- $url .= $_SERVER["REQUEST_URI"];
 
 
 
 
 
126
 
127
- return $url;
 
 
 
 
 
 
 
 
 
 
 
128
  }
129
- } // end of BV class
130
 
131
- // Most shared functionatly is here so when we add support for questions
132
- // and answers it should be minimal changes. Just need to create an answers
133
- // class which inherits from Base.
134
- class Base{
 
 
135
 
136
- public function __construct($params = array())
137
- {
138
- if ( ! $params)
139
- {
140
- throw new Exception('BV Base Class missing config array.');
141
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
 
143
- $this->config = $params;
144
-
145
- // setup bv (internal) defaults
146
- $this->bv_config['seo-domain']['staging'] = 'seo-stg.bazaarvoice.com';
147
- $this->bv_config['seo-domain']['production'] = 'seo.bazaarvoice.com';
148
- }
149
-
150
- /**
151
- * Render SEO
152
- *
153
- * Method used to do all the work to fetch, parse, and then return
154
- * the SEO payload. This is set as protected so classes inheriting
155
- * from the base class can invoke it or replace it if needed.
156
- *
157
- * @access protected
158
- * @return string
159
- */
160
- protected function _renderSEO()
161
- {
162
- // we will return a payload of a string
163
- $pay_load = '';
164
-
165
- // we only want to render SEO when it's a search engine bot
166
- if ($this->_isBot())
167
- {
168
-
169
-
170
- // get the page number of SEO content to load
171
- $page_number = $this->_getPageNumber();
172
-
173
- // build the URL to access the SEO content for
174
- // this product / page combination
175
- $seo_url = $this->_buildSeoUrl($page_number);
176
-
177
- // make call to get SEO payload from cloud
178
- $seo_content = $this->_fetchSeoContent($seo_url);
179
-
180
- // replace tokens for pagination URLs with page_url
181
- $seo_content = $this->_replaceTokens($seo_content);
182
-
183
- // if debug mode is on we want to include more debug data
184
- //if (isset($_GET['bvreveal']))
185
- //{
186
- // if($_GET['bvreveal'] == 'debug')
187
- // {
188
- // $printable_config = $this->config;
189
- // $seo_content .= $this->_buildComment('Config options: '.print_r($printable_config, TRUE));
190
- // }
191
- //}
192
-
193
- $pay_load = $page_number;
194
- $pay_load = $seo_content;
195
- }
196
- else
197
- {
198
- $pay_load = $this->_buildComment('JavaScript-only Display','');
199
- }
200
 
201
- return $pay_load;
 
 
 
 
 
 
 
202
  }
203
 
 
204
 
205
- // --------------------------------------------------------------------
206
- /* Private methods. Internal workings of SDK. */
207
- //--------------------------------------------------------------------
208
 
209
- /**
210
- * isBot
211
- *
212
- * Helper method to determine if current request is a bot or not. Will
213
- * use the configured regex string which can be overriden with params.
214
- *
215
- * @access private
216
- * @return bool
217
- */
218
 
219
- private function _isBot()
220
- {
221
- // we need to check the user agent string to see if this is a bot,
222
- // unless the bvreveal parameter is there or we have disabled bot
223
- // detection through the bot_detection flag
224
- if(isset($_GET['bvreveal']) OR ! $this->config['bot_detection']){
225
- return TRUE;
226
- }
227
 
228
- // search the user agent string for an indictation if this is a search bot or not
229
- return preg_match('/('.$this->config['bot_list'].')/i', $_SERVER['HTTP_USER_AGENT']);
230
- }
231
-
232
- /**
233
- * getPageNumber
234
- *
235
- * Helper method to pull from the URL the page of SEO we need to view.
236
- *
237
- * @access private
238
- * @return int
239
- */
240
- private function _getPageNumber()
241
- {
242
- // default to page 1 if a page is not specified in the URL
243
- $page_number = 1;
244
-
245
- //parse the current url that's passed in via the parameters
246
- $currentUrlArray = parse_url($this->config['current_page_url']);
247
-
248
- $query = $currentUrlArray['path']; //get the path out of the parsed url array
249
-
250
- parse_str($query, $bvcurrentpagedata); //parse the sub url such that you get the important part...page number
251
-
252
- // bvpage is not currently implemented
253
- if (isset($_GET['bvpage']) ){
254
- $page_number = (int) $_GET['bvpage'];
255
-
256
- // remove the bvpage parameter from the base URL so we don't keep appending it
257
- $seo_param = str_replace('/', '\/', $_GET['bvrrp']); // need to escape slashses for regex
258
- $this->config['base_page_url'] = preg_replace('/[?&]bvrrp='.$seo_param.'/', '', $this->config['base_page_url']);
259
- }
260
- // other implementations use the bvrrp, bvqap, or bvsyp parameter ?bvrrp=1234-en_us/reviews/product/2/ASF234.htm
261
- else if(isset($_GET['bvrrp']) OR isset($_GET['bvqap']) OR isset($_GET['bvsyp']) ){
262
- if(isset($_GET['bvrrp']))
263
- {
264
- $bvparam = $_GET['bvrrp'];
265
- }
266
- else if(isset($_GET['bvqap']))
267
- {
268
- $bvparam = $_GET['bvqap'];
269
- }
270
- else
271
- {
272
- $bvparam = $_GET['bvsyp'];
273
- }
274
- }
275
- else if(isset($bvcurrentpagedata)) //if the base url doesn't include the page number information and the current url
276
- //is defined then use the data from the current URL.
277
- {
278
-
279
- if (isset($bvcurrentpagedata['bvpage']) )
280
- {
281
- $page_number = (int) $bvcurrentpagedata['bvpage'];
282
- $bvparam=$bvcurrentpagedata['bvpage'];
283
- // remove the bvpage parameter from the base URL so we don't keep appending it
284
- $seo_param = str_replace('/', '\/', $_GET['bvrrp']); // need to escape slashses for regex
285
- $this->config['base_page_url'] = preg_replace('/[?&]bvrrp='.$seo_param.'/', '', $this->config['base_page_url']);
286
- }
287
- // other implementations use the bvrrp, bvqap, or bvsyp parameter ?bvrrp=1234-en_us/reviews/product/2/ASF234.htm
288
- else if(isset($bvcurrentpagedata['bvrrp'])
289
- || isset($bvcurrentpagedata['bvqap'])
290
- || isset($bvcurrentpagedata['bvsyp']) )
291
- {
292
- if(isset($bvcurrentpagedata['bvrrp']))
293
- {
294
- $bvparam = $bvcurrentpagedata['bvrrp'];
295
- }
296
- else if(isset($bvcurrentpagedata['bvqap']))
297
- {
298
- $bvparam = $bvcurrentpagedata['bvqap'];
299
- }
300
- else
301
- {
302
- $bvparam = $bvcurrentpagedata['bvsyp'];
303
- }
304
- }
305
- }
306
-
307
- if(isset($bvparam)){
308
- preg_match('/\/(\d+?)\/[^\/]+$/', $bvparam, $page_number);
309
- $page_number = max(1, (int) $page_number[1]);
310
-
311
- // remove the bvrrp parameter from the base URL so we don't keep appending it
312
- $seo_param = str_replace('/', '\/', $bvparam); // need to escape slashses for regex
313
- $this->config['base_page_url'] = preg_replace('/[?&]bvrrp='.$seo_param.'/', '', $this->config['base_page_url']);
314
- }
315
- return $page_number;
316
- }// end of _getPageNumber()
317
-
318
- /**
319
- * buildSeoUrl
320
- *
321
- * Helper method to that builds the URL to the SEO payload
322
- *
323
- * @access private
324
- * @param int (page number)
325
- * @return string
326
- */
327
- private function _buildSeoUrl($page_number){
328
- // are we pointing at staging or production?
329
- if($this->config['staging']){
330
- $hostname = $this->bv_config['seo-domain']['staging'];
331
- }else{
332
- $hostname = $this->bv_config['seo-domain']['production'];
333
- }
334
 
335
- // dictates order of URL
336
- $url_parts = array(
337
- 'http://'.$hostname,
338
- $this->config['cloud_key'],
339
- $this->config['deployment_zone_id'],
340
- $this->config['bv_product'],
341
- $this->config['subject_type'],
342
- $page_number,
343
- urlencode($this->config['product_id']).'.htm'
344
- );
345
 
346
- // if our SEO content source is a file path
347
- // we need to remove the first two sections
348
- // and prepend the passed in file path
349
- if($this->config['internal_file_path'])
350
- {
351
- unset($url_parts[0]);
352
- unset($url_parts[1]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
353
 
354
- return $this->config['internal_file_path'].implode("/", $url_parts);
355
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
356
 
357
- // implode will convert array to a string with / in between each value in array
358
- return implode("/", $url_parts);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
359
  }
360
 
361
- private function _fetchSeoContent($resource)
362
- {
363
- if($this->config['internal_file_path'])
364
- {
365
- return $this->_fetchFileContent($resource);
366
- }
367
- else
368
- {
369
- return $this->_fetchCloudContent($resource);
370
- }
371
  }
372
 
373
- /**
374
- * fetchFileContent
375
- *
376
- * Helper method that will take in a file path and return it's payload while
377
- * handling the possible errors or exceptions that can happen.
378
- *
379
- * @access private
380
- * @param string (valid file path)
381
- * @return string (contents of file)
382
- */
383
- private function _fetchFileContent($path){
384
- return file_get_contents($path);
385
- }
386
-
387
-
388
- /**
389
- * fetchCloudContent
390
- *
391
- * Helper method that will take in a URL and return it's payload while
392
- * handling the possible errors or exceptions that can happen.
393
- *
394
- * @access private
395
- * @param string (valid url)
396
- * @return string
397
- */
398
- private function _fetchCloudContent($url){
399
-
400
- // is cURL installed yet?
401
- // if ( ! function_exists('curl_init')){
402
- // return '<!-- curl library is not installed -->';
403
- // }
404
-
405
- // create a new cURL resource handle
406
- $ch = curl_init();
407
-
408
- curl_setopt($ch, CURLOPT_URL, $url); // Set URL to download
409
- curl_setopt($ch, CURLOPT_REFERER, $this->config['current_page_url']); // Set a referer as coming from the current page url
410
- curl_setopt($ch, CURLOPT_HEADER, 0); // Include header in result? (0 = yes, 1 = no)
411
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // Should cURL return or print out the data? (true = return, false = print)
412
- curl_setopt($ch, CURLOPT_TIMEOUT, ($this->config['latency_timeout'] / 1000)); // Timeout in seconds
413
-
414
- // make the request to the given URL and then store the response, request info, and error number
415
- // so we can use them later
416
- $request = array(
417
- 'response' => curl_exec($ch),
418
- 'info' => curl_getinfo($ch),
419
- 'error_number' => curl_errno($ch),
420
- 'error_message' => curl_error($ch)
421
- );
422
 
423
- // Close the cURL resource, and free system resources
424
- curl_close($ch);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
425
 
426
- $msg="";
 
 
 
 
427
 
428
- // see if we got any errors with the connection
429
- if($request['error_number'] != 0){
430
- $msg = 'Error - '.$request['error_message'];
431
- $this->_buildComment($msg,$url);
432
- }
 
433
 
434
- // see if we got a status code of something other than 200
435
- if($request['info']['http_code'] != 200){
436
- $msg = 'HTTP status code of '.$request['info']['http_code'].' was returned';
437
- return $this->_buildComment($msg,$url);
438
- }
439
 
440
- // if we are here we got a response so let's return it
441
- $this->response_time = round($request['info']['total_time'] * 1000);
442
- return $request['response'].$this->_buildComment($msg,$url);
443
- }
444
-
445
- /**
446
- * replaceTokens
447
- *
448
- * After we have an SEO payload we need to replace the {INSERT_PAGE_URI}
449
- * tokens with the current page url so pagination works.
450
- *
451
- * @access private
452
- * @param string (valid url)
453
- * @return string
454
- */
455
-
456
- private function _replaceTokens($content){
457
- // determine if query string exists in current page url
458
- if (parse_url($this->config['base_page_url'], PHP_URL_QUERY) != ''){
459
- // append an amperstand, because the URL already has a ? mark
460
- $page_url_query_prefix = '&';
461
- } else {
462
- // append a question mark, since this URL currently has no query
463
- $page_url_query_prefix = '?';
464
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
465
 
466
- $content = str_replace('{INSERT_PAGE_URI}', $this->config['base_page_url'] . $page_url_query_prefix, $content);
467
-
468
- return $content;
469
- }
470
-
471
- private function _buildComment($msg,$url){
472
- $footer = '<ul id="BVSEOSDK" style="display:none;">';
473
- $footer .= "\n".' <li id="vn">bvseo-1.0.1.3-beta</li>';
474
- $footer .= "\n".' <li id="sl">bvseo-p</li>';
475
- if ($this->config['internal_file_path']) {
476
- $footer .= "\n".' <li id="mt">bvseo-FILE</li>';
477
- } else {
478
- $footer .= "\n".' <li id="mt">bvseo-CLOUD</li>';
479
- }
480
- if(isset($this->response_time))
481
- $footer .= "\n".' <li id="et">bvseo-'.$this->response_time.'ms</li>';
482
- $footer .= "\n".' <li id="ct">bvseo-'.strtoupper($this->config['bv_product']).'</li>';
483
- $footer .= "\n".' <li id="st">bvseo-'.strtoupper($this->config['subject_type']).'</li>';
484
- $footer .= "\n".' <li id="am">bvseo-getContent</li>';
485
- if (strlen($msg) > 0) {
486
- $footer .= "\n".' <li id="ms">bvseo-msg: '.$msg.'</li>';
487
- }
488
- $footer .= "\n".'</ul>';
489
-
490
- //when in debug mode, also display the following information
491
- if (isset($_GET['bvreveal'])){
492
- if($_GET['bvreveal'] == 'debug') {
493
- $footer .= "\n".'<ul id="BVSEOSDK_DEBUG" style="display:none;">';
494
- $footer .= "\n".' <li id="cloudKey">'.$this->config['cloud_key'].'</li>';
495
- $footer .= "\n".' <li id="bv.root.folder">'.$this->config['deployment_zone_id'].'</li>';
496
- $footer .= "\n".' <li id="stagingS3Hostname">'.$this->bv_config['seo-domain']['staging'].'</li>';
497
- $footer .= "\n".' <li id="productionS3Hostname">'.$this->bv_config['seo-domain']['production'].'</li>';
498
- $staging = ($this->config['staging']) ? 'TRUE' : 'FALSE';
499
- $footer .= "\n".' <li id="staging">'.$staging.'</li>';
500
- $footer .= "\n".' <li id="seo.sdk.execution.timeout">'.$this->config['latency_timeout'].'</li>';
501
- $bot_detection = ($this->config['bot_detection']) ? 'TRUE' : 'FALSE';
502
- $footer .= "\n".' <li id="botDetection">'.$bot_detection.'</li>';
503
- $footer .= "\n".' <li id="crawlerAgentPattern">'.$this->config['bot_list'].'</li>';
504
- $footer .= "\n".' <li id="userAgent">'.$_SERVER['HTTP_USER_AGENT'].'</li>';
505
- $footer .= "\n".' <li id="pageURI">'.$this->config['current_page_url'].'</li>';
506
- $footer .= "\n".' <li id="baseURI">'.$this->config['base_page_url'].'</li>';
507
- $footer .= "\n".' <li id="subjectID">'.urlencode($this->config['product_id']).'</li>';
508
- $footer .= "\n".' <li id="contentType">'.strtoupper($this->config['bv_product']).'</li>';
509
- $footer .= "\n".' <li id="subjectType">'.strtoupper($this->config['subject_type']).'</li>';
510
- $footer .= "\n".' <li id="contentURL">'.$url.'</li>';
511
- $footer .= "\n".'</ul>';
512
- }
513
- }
514
 
515
- return $footer;
516
- // return "\n".'<!--BVSEO|dp: '.$this->config['deployment_zone_id'].'|sdk: v1.0-p|msg: '.$msg.' -->';
 
 
 
517
  }
518
 
519
- private function _booleanToString($boolean){
520
- if ($boolean){
521
- return 'TRUE';
522
- }else{
523
- return 'FALSE';
524
- }
525
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
526
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
527
 
528
- } // end of Base class
 
 
529
 
 
 
 
530
 
531
- class Reviews extends Base{
 
532
 
533
- function __construct($params = array())
534
- {
535
- // call Base Class constructor
536
- parent::__construct($params);
537
-
538
- // since we are in the reviews class
539
- // we need to set the bv_product config
540
- // to reviews so we get reviews in our
541
- // SEO request
542
- $this->config['bv_product'] = 'reviews';
543
 
544
- // for reviews subject type will always
545
- // need to be product
546
- $this->config['subject_type'] = 'product';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
547
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
548
 
549
- public function renderSeo()
550
- {
551
- $pay_load = $this->_renderSeo();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
552
 
553
- // if they want to power display integration as well
554
- // then we need to include the JS integration code
555
- // regardless of if it's a bot or not
556
- if($this->config['include_display_integration_code'])
557
- {
558
- $pay_load .= '
559
- <script>
560
- $BV.ui("rr", "show_reviews", {
561
- productId: "'.$this->config['product_id'].'"
562
- });
563
- </script>
564
- ';
565
- }
566
 
567
- return $pay_load;
 
568
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
569
  }
570
- } // end of Reviews class
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
571
 
 
 
572
 
573
- class Questions extends Base{
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
574
 
575
- function __construct($params = array())
576
- {
577
  // call Base Class constructor
578
  parent::__construct($params);
579
-
580
- // since we are in the questions class
581
- // we need to set the bv_product config
582
- // to questions so we get questions in our
583
  // SEO request
584
- $this->config['bv_product'] = 'questions';
585
- }
586
 
587
- public function renderSeo()
588
- {
589
- $pay_load = $this->_renderSeo();
590
 
591
- // if they want to power display integration as well
592
- // then we need to include the JS integration code
593
- // regardless of if it's a bot or not
594
- if($this->config['include_display_integration_code'])
595
- {
596
 
597
- $pay_load .= '
598
- <script>
599
- $BV.ui("qa", "show_questions", {
600
- productId: "'.$this->config['product_id'].'"
601
- });
602
- </script>
603
- ';
604
- }
605
 
606
- return $pay_load;
 
607
 
608
- }
609
- } // end of Questions class
610
 
611
- // end of bvsdk.php
3
  /**
4
  * BV PHP SEO SDK
5
  *
6
+ * Base code to power either SEO or SEO and display. This SDK
7
+ * is provided as is and Bazaarvoice, Inc. is not responsible
8
+ * for future maintenance or support. You are free to modify
9
+ * this SDK as needed to suit your needs.
10
  *
11
  * This SDK was built with the following assumptions:
12
+ * - you are running PHP 5 or greater
13
+ * - you have the curl library installed
14
+ * - every request has the user agent header
15
+ * in it (if using a CDN like Akamai additional configuration
16
+ * maybe required).
17
  *
18
  */
 
19
  /**
20
  * Example usage:
21
  *
22
  * require(bvsdk.php);
23
  *
24
  * $bv = new BV(array(
25
+ * 'bv_root_folder' => '1234-en_US',
26
+ * 'subject_id' => 'XXYYY',
27
+ * 'cloud_key' => 'company-cdfa682b84bef44672efed074093ccd3',
28
+ * 'staging' => FALSE
29
  * ));
30
+ *
31
  */
32
+ require_once 'BVUtility.php';
33
+ require_once 'BVFooter.php';
34
+
35
+ // Should be declared in file where execTimer will be used.
36
+ // If declared in the another file it does not affect the current file.
37
+ declare(ticks = 1);
38
+
39
+ // Default charset will be used in case charset parameter is not properly configured by user.
40
+ define('DEFAULT_CHARSET', 'UTF-8');
41
 
42
  // ------------------------------------------------------------------------
43
 
44
  /**
45
  * BV Class
46
+ *
47
+ * When you instantiate the BV class, pass it's constructor an array
48
+ * containing the following key value pairs.
49
+ *
50
  * Required fields:
51
+ * bv_root_folder (string)
52
+ * subject_id (string)
53
+ * cloud_key (string)
54
  *
55
  * Optional fields
56
+ * base_url (string) (defaults to detecting the base_url automatically)
57
+ * page_url (string) (defaults to empty, to provide query parameters )
58
+ * staging (boolean) (defaults to false, need to put true for testing with staging data)
59
+ * testing (boolean) (defaults to false, need to put true for testing with testing data)
60
+ * content_type (string) (defaults to reviews, you can pass content type here if needed)
61
+ * subject_type (string) (defaults to product, you can pass subject type here if needed)
62
+ * content_sub_type (string) (defaults to stories, for stories you can pass either STORIES_LIST or STORIES_GRID content type)
63
+ * execution_timeout (int) (in milliseconds) (defaults to 500ms, to set period of time before the BVSEO injection times out for user agents that do not match the criteria set in CRAWLER_AGENT_PATTERN)
64
+ * execution_timeout_bot (int) (in milliseconds) (defaults to 2000ms, to set period of time before the BVSEO injection times out for user agents that match the criteria set in CRAWLER_AGENT_PATTERN)
65
+ * charset (string) (defaults to UTF-8, to set alternate character for SDK output)
66
+ * crawler_agent_pattern (string) (defaults to msnbot|googlebot|teoma|bingbot|yandexbot|yahoo)
67
  */
 
68
  class BV {
69
 
70
+ /**
71
+ * BV Class Constructor
72
+ *
73
+ * The constructor takes in all the arguments via a single array.
74
+ *
75
+ * @access public
76
+ * @param array
77
+ * @return object
78
+ */
79
+ public function __construct($params = array()) {
80
+
81
+ $this->validateParameters($params);
82
+
83
+ // config array, defaults are defined here.
84
+ $this->config = array(
85
+ 'staging' => FALSE,
86
+ 'testing' => FALSE,
87
+ 'content_type' => isset($params['content_type']) ? $params['content_type'] : 'reviews',
88
+ 'subject_type' => isset($params['subject_type']) ? $params['subject_type'] : 'product',
89
+ 'page_url' => isset($params['page_url']) ? $params['page_url'] : '',
90
+ 'base_url' => isset($params['base_url']) ? $params['base_url'] : '',
91
+ 'include_display_integration_code' => FALSE,
92
+ 'client_name' => $params['bv_root_folder'],
93
+ 'local_seo_file_root' => '',
94
+ 'load_seo_files_locally' => FALSE,
95
+ // used in regex to determine if request is a bot or not
96
+ 'crawler_agent_pattern' => 'msnbot|google|teoma|bingbot|yandexbot|yahoo',
97
+ 'ssl_enabled' => FALSE,
98
+ 'proxy_host' => '',
99
+ 'proxy_port' => '',
100
+ 'charset' => 'UTF-8',
101
+ 'seo_sdk_enabled' => TRUE,
102
+ 'execution_timeout' => 500,
103
+ 'execution_timeout_bot' => 2000,
104
+ 'bvreveal' => isset($params['bvreveal']) ? $params['bvreveal'] : '',
105
+ 'page' => 1,
106
+ 'page_params' => array()
107
+ );
108
+
109
+ // Merge passed in params with defaults for config.
110
+ $this->config = array_merge($this->config, $params);
111
+
112
+ // Obtain all the name=value parameters from either the page URL passed in,
113
+ // or from the actual page URL as seen by PHP. Parameter values from the
114
+ // actual URL override those from the URL passed in, as that is usually a
115
+ // trucated URL where present at all.
116
+ //
117
+ // Note that we're taking parameters from query string, fragment, or
118
+ // _escaped_fragment_. (Though fragment is not passed to the server, so
119
+ // we won't actually see that in practice).
120
+ //
121
+ // We're after bvrrp, bvqap, bvsyp, and bvstate, but sweep up everything
122
+ // while we're here.
123
+ if (isset($params['page_url'])) {
124
+ $this->config['bv_page_data'] = BVUtility::parseUrlParameters($params['page_url']);
125
+ }
126
+
127
+ // Extract bvstate if present and parse that into a set of useful values.
128
+ if (isset($this->config['bv_page_data']['bvstate'])) {
129
+ $this->config['page_params'] = BVUtility::getBVStateParams($this->config['bv_page_data']['bvstate']);
130
+ }
131
+
132
+ // Remove any trailing URL delimeters from the base URL. E.g.:
133
+ // http://example.com?
134
+ // http://example.com?a=b&
135
+ // http://example.com?a=b&_escaped_fragment_=x/y/z?r=s%26
136
+ //
137
+ $this->config['base_url'] = mb_ereg_replace('(&|\?|%26)$', '', $this->config['base_url']);
138
+
139
+ // Get rid of all the other things we care about from the base URL, so that
140
+ // we don't double up the parameters.
141
+ $this->config['base_url'] = BVUtility::removeUrlParam($this->config['base_url'], 'bvstate');
142
+ $this->config['base_url'] = BVUtility::removeUrlParam($this->config['base_url'], 'bvrrp');
143
+ $this->config['base_url'] = BVUtility::removeUrlParam($this->config['base_url'], 'bvqap');
144
+ $this->config['base_url'] = BVUtility::removeUrlParam($this->config['base_url'], 'bvsyp');
145
+
146
+ // Create the processor objects.
147
+ $this->reviews = new Reviews($this->config);
148
+ $this->questions = new Questions($this->config);
149
+ $this->stories = new Stories($this->config);
150
+ $this->spotlights = new Spotlights($this->config);
151
+ $this->sellerratings = new SellerRatings($this->config);
152
+
153
+ // Assign one to $this->SEO based on the content type.
154
+ $ct = isset($this->config['page_params']['content_type']) ? $this->config['page_params']['content_type'] : $this->config['content_type'];
155
+ if (isset($ct)) {
156
+ switch ($ct) {
157
+ case 'reviews': {
158
+ $st = isset($this->config['page_params']['subject_type']) ? $this->config['page_params']['subject_type'] : $this->config['subject_type'];
159
+ if (isset($st) && $st == 'seller') {
160
+ $this->SEO = $this->sellerratings;
161
+ } else {
162
+ $this->SEO = $this->reviews;
163
+ }
164
+ break;
165
  }
166
+ case 'questions': $this->SEO = $this->questions;
167
+ break;
168
+ case 'stories': $this->SEO = $this->stories;
169
+ break;
170
+ case 'spotlights': $this->SEO = $this->spotlights;
171
+ break;
172
+ default:
173
+ throw new Exception('Invalid content_type value provided: ' . $this->config['content_type']);
174
+ }
175
+ }
176
+ }
177
 
178
+ protected function validateParameters($params) {
179
+ if (!is_array($params)) {
180
+ throw new Exception(
181
+ 'BV class constructor argument $params must be an array.'
182
+ );
183
+ }
 
 
 
 
 
 
184
 
185
+ // check to make sure we have the required parameters.
186
+ if (empty($params['bv_root_folder'])) {
187
+ throw new Exception(
188
+ 'BV class constructor argument $params is missing required bv_root_folder key. An ' .
189
+ 'array containing bv_root_folder (string) is expected.'
190
  );
191
+ }
192
 
193
+ if (empty($params['subject_id'])) {
194
+ throw new Exception(
195
+ 'BV class constructor argument $params is missing required subject_id key. An ' .
196
+ 'array containing subject_id (string) is expected.'
197
+ );
198
+ }
199
+ }
200
+ }
201
+ // end of BV class
202
 
203
+ /**
204
+ * Base Class containing most shared functionality. So when we add support for
205
+ * questions and answers it should be minimal changes. Just need to create an
206
+ * answers class which inherits from Base.
207
+ *
208
+ * Configuration array is required for creation class object.
209
+ *
210
+ */
211
+ class Base {
212
+ private $msg = '';
213
 
214
+ public function __construct($params = array()) {
 
 
 
215
 
216
+ $this->validateParams($params);
217
 
218
+ $this->config = $params;
 
 
 
 
 
 
 
 
 
 
 
219
 
220
+ // setup bv (internal) defaults
221
+ $this->bv_config['seo-domain']['staging'] = 'seo-stg.bazaarvoice.com';
222
+ $this->bv_config['seo-domain']['production'] = 'seo.bazaarvoice.com';
223
+ $this->bv_config['seo-domain']['testing_staging'] = 'seo-qa-stg.bazaarvoice.com';
224
+ $this->bv_config['seo-domain']['testing_production'] = 'seo-qa.bazaarvoice.com';
225
 
226
+ // seller rating display is a special snowflake
227
+ $this->bv_config['srd-domain'] = 'srd.bazaarvoice.com';
228
+ $this->bv_config['srd-prefix-staging'] = 'stg';
229
+ $this->bv_config['srd-prefix-production'] = 'prod';
230
+ $this->bv_config['srd-prefix-testing_staging'] = 'qa-stg';
231
+ $this->bv_config['srd-prefix-testing_production'] = 'qa';
232
+
233
+ $this->config['latency_timeout'] = $this->_isBot()
234
+ ? $this->config['execution_timeout_bot']
235
+ : $this->config['execution_timeout'];
236
+ }
237
 
238
+ protected function validateParams($params) {
239
+ if (!is_array($params)) {
240
+ throw new Exception('BV Base Class missing config array.');
241
+ }
242
+ }
243
+
244
+ /**
245
+ * A check on the bvstate parameter content type value.
246
+ */
247
+ protected function _checkBVStateContentType() {
248
+ if (empty($this->config['page_params']['content_type'])) {
249
+ return TRUE;
250
  }
 
251
 
252
+ if (
253
+ !empty($this->config['page_params']['content_type']) &&
254
+ $this->config['page_params']['content_type'] == $this->config['content_type']
255
+ ) {
256
+ return TRUE;
257
+ }
258
 
259
+ return FALSE;
260
+ }
261
+
262
+ /**
263
+ * Function for collecting messages.
264
+ */
265
+ protected function _setBuildMessage($msg) {
266
+ $msg = rtrim($msg, ";");
267
+ $this->msg .= ' ' . $msg . ';';
268
+ }
269
+
270
+ /**
271
+ * Is this SDK enabled?
272
+ *
273
+ * Return true if either seo_sdk_enabled is set truthy or bvreveal flags are
274
+ * set.
275
+ */
276
+ private function _isSdkEnabled() {
277
+ return $this->config['seo_sdk_enabled'] || $this->_getBVReveal();
278
+ }
279
+
280
+ /**
281
+ * Check if charset is correct, if not set to default
282
+ */
283
+ private function _checkCharset($seo_content) {
284
+ if (isset($this->config['charset'])) {
285
+ $supportedCharsets = mb_list_encodings();
286
+ if (!in_array($this->config['charset'], $supportedCharsets)) {
287
+ $this->config['charset'] = DEFAULT_CHARSET;
288
+ $this->_setBuildMessage("Charset is not configured properly. "
289
+ . "BV-SEO-SDK will load default charset and continue.");
290
+ }
291
+ } else {
292
+ $this->config['charset'] = DEFAULT_CHARSET;
293
+ }
294
+ }
295
+
296
+ /**
297
+ * Return encoded content with set charset
298
+ */
299
+ private function _charsetEncode($seo_content) {
300
+ if (isset($this->config['charset'])) {
301
+ $enc = mb_detect_encoding($seo_content);
302
+ $seo_content = mb_convert_encoding($seo_content, $this->config['charset'], $enc);
303
+ }
304
 
305
+ return $seo_content;
306
+ }
307
+
308
+ /**
309
+ * Return full SEO content.
310
+ */
311
+ private function _getFullSeoContents() {
312
+ $seo_content = '';
313
+
314
+ // get the page number of SEO content to load
315
+ $page_number = $this->_getPageNumber();
316
+
317
+ // build the URL to access the SEO content for
318
+ // this product / page combination
319
+ $this->seo_url = $this->_buildSeoUrl($page_number);
320
+
321
+ // make call to get SEO payload from cloud unless seo_sdk_enabled is false
322
+ // make call if bvreveal param in query string is set to 'debug'
323
+ if ($this->_isSdkEnabled()) {
324
+ $seo_content = $this->_fetchSeoContent($this->seo_url);
325
+
326
+ $this->_checkCharset($seo_content);
327
+ $seo_content = $this->_charsetEncode($seo_content);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
328
 
329
+ // replace tokens for pagination URLs with page_url
330
+ $seo_content = $this->_replaceTokens($seo_content);
331
+ }
332
+ // show footer even if seo_sdk_enabled flag is false
333
+ else {
334
+ $this->_setBuildMessage(
335
+ 'SEO SDK is disabled. Enable by setting seo.sdk.enabled to true.'
336
+ );
337
  }
338
 
339
+ $payload = $seo_content;
340
 
341
+ return $payload;
342
+ }
 
343
 
344
+ /**
345
+ * Remove predefined section from a string.
346
+ */
347
+ private function _replaceSection($str, $search_str_begin, $search_str_end) {
348
+ $result = $str;
349
+ $start_index = mb_strrpos($str, $search_str_begin);
 
 
 
350
 
351
+ if ($start_index !== false) {
352
+ $end_index = mb_strrpos($str, $search_str_end);
 
 
 
 
 
 
353
 
354
+ if ($end_index !== false) {
355
+ $end_index += mb_strlen($search_str_end);
356
+ $str_begin = mb_substr($str, 0, $start_index);
357
+ $str_end = mb_substr($str, $end_index);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
358
 
359
+ $result = $str_begin . $str_end;
360
+ }
361
+ }
 
 
 
 
 
 
 
362
 
363
+ return $result;
364
+ }
365
+
366
+ /**
367
+ * Get only aggregate rating from SEO content.
368
+ */
369
+ protected function _renderAggregateRating() {
370
+ $payload = $this->_renderSEO('getAggregateRating');
371
+
372
+ // remove reviews section from full_contents
373
+ $payload = $this->_replaceSection($payload, '<!--begin-reviews-->', '<!--end-reviews-->');
374
+
375
+ // remove pagination section from full contents
376
+ $payload = $this->_replaceSection($payload, '<!--begin-pagination-->', '<!--end-pagination-->');
377
+
378
+ return $payload;
379
+ }
380
+
381
+ /**
382
+ * Get only reviews from SEO content.
383
+ */
384
+ protected function _renderReviews() {
385
+ $payload = $this->_renderSEO('getReviews');
386
+
387
+ // remove aggregate rating section from full_contents
388
+ $payload = $this->_replaceSection($payload, '<!--begin-aggregate-rating-->', '<!--end-aggregate-rating-->');
389
+
390
+ // Remove schema.org product text from reviews if it exists
391
+ $schema_org_text = "itemscope itemtype=\"http://schema.org/Product\"";
392
+ $payload = mb_ereg_replace($schema_org_text, '', $payload);
393
+
394
+ return $payload;
395
+ }
396
+
397
+ /**
398
+ * Render SEO
399
+ *
400
+ * Method used to do all the work to fetch, parse, and then return
401
+ * the SEO payload. This is set as protected so classes inheriting
402
+ * from the base class can invoke it or replace it if needed.
403
+ *
404
+ * @access protected
405
+ * @param $access_method
406
+ * @return string
407
+ */
408
+ protected function _renderSEO($access_method) {
409
+ $payload = '';
410
+ $this->start_time = microtime(1);
411
+
412
+ $isBot = $this->_isBot();
413
+
414
+ if (!$isBot && $this->config['latency_timeout'] == 0) {
415
+ $this->_setBuildMessage("EXECUTION_TIMEOUT is set to 0 ms; JavaScript-only Display.");
416
+ } else {
417
+
418
+ if ($isBot && $this->config['latency_timeout'] < 100) {
419
+ $this->config['latency_timeout'] = 100;
420
+ $this->_setBuildMessage("EXECUTION_TIMEOUT_BOT is less than the minimum value allowed. Minimum value of 100ms used.");
421
+ }
422
+
423
+ try {
424
+ BVUtility::execTimer($this->config['latency_timeout'], $isBot, $this->start_time);
425
+ $payload = $this->_getFullSeoContents($access_method);
426
+ } catch (Exception $e) {
427
+ $this->_setBuildMessage($e->getMessage());
428
+ }
429
+ BVUtility::stopTimer();
430
+ }
431
 
432
+ $payload .= $this->_buildComment($access_method);
433
+ return $payload;
434
+ }
435
+
436
+ // -------------------------------------------------------------------
437
+ // Private methods. Internal workings of SDK.
438
+ //--------------------------------------------------------------------
439
+
440
+ /**
441
+ * isBot
442
+ *
443
+ * Helper method to determine if current request is a bot or not. Will
444
+ * use the configured regex string which can be overridden with params.
445
+ *
446
+ * @access private
447
+ * @return bool
448
+ */
449
+ private function _isBot() {
450
+ $bvreveal = $this->_getBVReveal();
451
+ if ($bvreveal) {
452
+ return TRUE;
453
+ }
454
 
455
+ // search the user agent string for an indication if this is a search bot or not
456
+ return mb_eregi('(' . $this->config['crawler_agent_pattern'] . ')', $_SERVER['HTTP_USER_AGENT']);
457
+ }
458
+
459
+ /**
460
+ * getBVReveal
461
+ *
462
+ * Return true if bvreveal flags are set, either via reveal:debug in the
463
+ * bvstate query parameter or in the old bvreveal query parameter, or is
464
+ * passed in via the configuration of the main class.
465
+ */
466
+ private function _getBVReveal() {
467
+ // Passed in as configuration override?
468
+ if (
469
+ !empty($this->config['bvreveal']) &&
470
+ $this->config['bvreveal'] == 'debug'
471
+ ) {
472
+ return TRUE;
473
+ }
474
+ // Set via bvstate query parameter?
475
+ else if (
476
+ !empty($this->config['page_params']['bvreveal']) &&
477
+ $this->config['page_params']['bvreveal'] == 'debug'
478
+ ) {
479
+ return TRUE;
480
+ }
481
+ // Set via bvreveal query parameter?
482
+ else if (
483
+ !empty($this->config['bv_page_data']['bvreveal']) &&
484
+ $this->config['bv_page_data']['bvreveal'] == 'debug'
485
+ ) {
486
+ return TRUE;
487
+ } else {
488
+ return FALSE;
489
+ }
490
+ }
491
+
492
+ /**
493
+ * getPageNumber
494
+ *
495
+ * Helper method to pull from the URL the page of SEO we need to view.
496
+ *
497
+ * @access private
498
+ * @return int
499
+ */
500
+ private function _getPageNumber() {
501
+ $page_number = 1;
502
+
503
+ // Override from config.
504
+ if (isset($this->config['page']) && $this->config['page'] != $page_number) {
505
+ $page_number = (int) $this->config['page'];
506
+ }
507
+ // Check the bvstate parameter if one was found and successfully parsed.
508
+ else if (isset($this->config['page_params']['base_url_bvstate'])) {
509
+ // We only apply the bvstate page number parameter if the content type
510
+ // specified matches the content type being generated here. E.g. if
511
+ // someone calls up a page with bvstate=ct:r/pg:2 and loads stories rather
512
+ // than reviews, show page 1 for stories. Only show page 2 if they are in
513
+ // fact displaying review content.
514
+ if ($this->config['content_type'] == $this->config['page_params']['content_type']) {
515
+ $page_number = $this->config['page_params']['page'];
516
+ }
517
+ }
518
+ // other implementations use the bvrrp, bvqap, or bvsyp parameter
519
+ // ?bvrrp=1234-en_us/reviews/product/2/ASF234.htm
520
+ //
521
+ // Note that unlike bvstate, we don't actually check for the content type
522
+ // to match the parameter type for the legacy page parameters bvrrp, bvqap,
523
+ // and bvsyp. This is consistent with the behavior of the other SDKs, even
524
+ // if it doesn't really make much sense.
525
+ //
526
+ // Note that there is a bug in the SEO-CPS content generation where it uses
527
+ // the bvrrp parameter in place of bvqap, so this may all be sort of
528
+ // deliberate, if not sensible.
529
+ else if (isset($this->config['bv_page_data']['bvrrp'])) {
530
+ $bvparam = $this->config['bv_page_data']['bvrrp'];
531
+ } else if (isset($this->config['bv_page_data']['bvqap'])) {
532
+ $bvparam = $this->config['bv_page_data']['bvqap'];
533
+ } else if (isset($this->config['bv_page_data']['bvsyp'])) {
534
+ $bvparam = $this->config['bv_page_data']['bvsyp'];
535
  }
536
 
537
+ if (!empty($bvparam)) {
538
+ $match = array();
539
+ mb_ereg('\/(\d+)\/', $bvparam, $match);
540
+ $page_number = max(1, (int) $match[1]);
 
 
 
 
 
 
541
  }
542
 
543
+ return $page_number;
544
+ }
545
+
546
+ /**
547
+ * buildSeoUrl
548
+ *
549
+ * Helper method to that builds the URL to the SEO payload
550
+ *
551
+ * @access private
552
+ * @param int (page number)
553
+ * @return string
554
+ */
555
+ private function _buildSeoUrl($page_number) {
556
+ $primary_selector = 'seo-domain';
557
+
558
+ // calculate, which environment should we be using
559
+ if ($this->config['testing']) {
560
+ if ($this->config['staging']) {
561
+ $env_selector = 'testing_staging';
562
+ } else {
563
+ $env_selector = 'testing_production';
564
+ }
565
+ } else {
566
+ if ($this->config['staging']) {
567
+ $env_selector = 'staging';
568
+ } else {
569
+ $env_selector = 'production';
570
+ }
571
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
572
 
573
+ $url_scheme = $this->config['ssl_enabled'] ? 'https://' : 'http://';
574
+
575
+ if ($this->config['content_type'] == 'reviews' &&
576
+ $this->config['subject_type'] == 'seller') {
577
+ // when content type is reviews and subject type is seller,
578
+ // we're dealing with seller rating, so use different primary selector
579
+ $primary_selector = 'srd-domain';
580
+ // for seller rating we use different selector for prefix
581
+ $hostname = $this->bv_config[$primary_selector] . '/' . $this->bv_config['srd-prefix-' . $env_selector];
582
+ } else {
583
+ $hostname = $this->bv_config[$primary_selector][$env_selector];
584
+ };
585
+
586
+ // dictates order of URL
587
+ $url_parts = array(
588
+ $url_scheme . $hostname,
589
+ $this->config['cloud_key'],
590
+ $this->config['bv_root_folder'],
591
+ $this->config['content_type'],
592
+ $this->config['subject_type'],
593
+ $page_number
594
+ );
595
+
596
+ if (isset($this->config['content_sub_type']) && !empty($this->config['content_sub_type'])) {
597
+ $url_parts[] = $this->config['content_sub_type'];
598
+ }
599
 
600
+ if (!empty($this->config['page_params']['subject_id']) && $this->_checkBVStateContentType()) {
601
+ $url_parts[] = urlencode($this->config['page_params']['subject_id']) . '.htm';
602
+ } else {
603
+ $url_parts[] = urlencode($this->config['subject_id']) . '.htm';
604
+ }
605
 
606
+ // if our SEO content source is a file path
607
+ // we need to remove the first two sections
608
+ // and prepend the passed in file path
609
+ if (!empty($this->config['load_seo_files_locally']) && !empty($this->config['local_seo_file_root'])) {
610
+ unset($url_parts[0]);
611
+ unset($url_parts[1]);
612
 
613
+ return $this->config['local_seo_file_root'] . implode("/", $url_parts);
614
+ }
 
 
 
615
 
616
+ // implode will convert array to a string with / in between each value in array
617
+ return implode("/", $url_parts);
618
+ }
619
+
620
+ /**
621
+ * Return a SEO content from local or distant sourse.
622
+ */
623
+ private function _fetchSeoContent($resource) {
624
+ if ($this->config['load_seo_files_locally']) {
625
+ return $this->_fetchFileContent($resource);
626
+ } else {
627
+ return $this->_fetchCloudContent($resource);
628
+ }
629
+ }
630
+
631
+ /**
632
+ * fetchFileContent
633
+ *
634
+ * Helper method that will take in a file path and return it's payload while
635
+ * handling the possible errors or exceptions that can happen.
636
+ *
637
+ * @access private
638
+ * @param string (valid file path)
639
+ * @return string (content of file)
640
+ */
641
+ private function _fetchFileContent($path) {
642
+ $file = @file_get_contents($path);
643
+ if ($file === FALSE) {
644
+ $this->_setBuildMessage('Trying to get content from "' . $path
645
+ . '". The resource file is currently unavailable');
646
+ } else {
647
+ $this->_setBuildMessage('Local file content was uploaded');
648
+ }
649
+ return $file;
650
+ }
651
+
652
+ public function curlExecute($ch) {
653
+ return curl_exec($ch);
654
+ }
655
+
656
+ public function curlInfo($ch) {
657
+ return curl_getinfo($ch);
658
+ }
659
+
660
+ public function curlErrorNo($ch) {
661
+ return curl_errno($ch);
662
+ }
663
+
664
+ public function curlError($ch) {
665
+ return curl_error($ch);
666
+ }
667
+
668
+ /**
669
+ * fetchCloudContent
670
+ *
671
+ * Helper method that will take in a URL and return it's payload while
672
+ * handling the possible errors or exceptions that can happen.
673
+ *
674
+ * @access private
675
+ * @param string (valid url)
676
+ * @return string
677
+ */
678
+ private function _fetchCloudContent($url) {
679
+
680
+ // is cURL installed yet?
681
+ // if ( ! function_exists('curl_init')){
682
+ // return '<!-- curl library is not installed -->';
683
+ // }
684
+ // create a new cURL resource handle
685
+ $ch = curl_init();
686
+
687
+ // Set URL to download
688
+ curl_setopt($ch, CURLOPT_URL, $url);
689
+ // Set a referer as coming from the current page url
690
+ curl_setopt($ch, CURLOPT_REFERER, $this->config['page_url']);
691
+ // Include header in result? (0 = yes, 1 = no)
692
+ curl_setopt($ch, CURLOPT_HEADER, 0);
693
+ // Should cURL return or print out the data? (true = return, false = print)
694
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
695
+ // Timeout in seconds
696
+ curl_setopt($ch, CURLOPT_TIMEOUT, ($this->config['latency_timeout'] / 1000));
697
+ // Enable decoding of the response
698
+ curl_setopt($ch, CURLOPT_ENCODING, 'gzip,deflate');
699
+ // Enable following of redirects
700
+ curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
701
+
702
+ if ($this->config['proxy_host'] != '') {
703
+ curl_setopt($ch, CURLOPT_PROXY, $this->config['proxy_host']);
704
+ curl_setopt($ch, CURLOPT_PROXYPORT, $this->config['proxy_port']);
705
+ }
706
 
707
+ // make the request to the given URL and then store the response,
708
+ // request info, and error number
709
+ // so we can use them later
710
+ $request = array(
711
+ 'response' => $this->curlExecute($ch),
712
+ 'info' => $this->curlInfo($ch),
713
+ 'error_number' => $this->curlErrorNo($ch),
714
+ 'error_message' => $this->curlError($ch)
715
+ );
716
+
717
+ // Close the cURL resource, and free system resources
718
+ curl_close($ch);
719
+
720
+ // see if we got any errors with the connection
721
+ if ($request['error_number'] != 0) {
722
+ $this->_setBuildMessage('Error - ' . $request['error_message']);
723
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
724
 
725
+ // see if we got a status code of something other than 200
726
+ if ($request['info']['http_code'] != 200) {
727
+ $this->_setBuildMessage('HTTP status code of '
728
+ . $request['info']['http_code'] . ' was returned');
729
+ return '';
730
  }
731
 
732
+ // if we are here we got a response so let's return it
733
+ $this->response_time = round($request['info']['total_time'] * 1000);
734
+ return $request['response'];
735
+ }
736
+
737
+ /**
738
+ * replaceTokens
739
+ *
740
+ * After we have an SEO payload we need to replace the {INSERT_PAGE_URI}
741
+ * tokens with the current page url so pagination works.
742
+ *
743
+ * @access private
744
+ * @param string (valid url)
745
+ * @return string
746
+ */
747
+ private function _replaceTokens($content) {
748
+ $page_url_query_prefix = '';
749
+
750
+ // Attach a suitable ending to the base URL if it doesn't already end with
751
+ // either ? or &. This is complicated by the _escaped_fragment_ case.
752
+ //
753
+ // We're assuming that the base URL can't have a fragment or be a hashbang
754
+ // URL - that just won't work in conjunction with the assumption that we
755
+ // always postfix the SEO query parameters to the end of the URL.
756
+ //
757
+ // If the base url ends with an empty _escaped_fragment_ property.
758
+ if (mb_ereg('_escaped_fragment_=$', $this->config['base_url'])) {
759
+ // Append nothing for this annoying edge case.
760
+ }
761
+ // Otherwise if there is something in the _escaped_fragment_ then append
762
+ // the escaped ampersand.
763
+ else if (mb_ereg('_escaped_fragment_=.+$', $this->config['base_url'])) {
764
+ $page_url_query_prefix = '%26';
765
+ }
766
+ // Otherwise we're back to thinking about query strings.
767
+ else if (!mb_ereg('[\?&]$', $this->config['base_url'])) {
768
+ if(mb_ereg('\?', $this->config['base_url'])) {
769
+ $page_url_query_prefix = '&';
770
+ } else {
771
+ $page_url_query_prefix = '?';
772
+ }
773
+ }
774
 
775
+ $content = mb_ereg_replace(
776
+ '{INSERT_PAGE_URI}',
777
+ // Make sure someone doesn't sneak in "><script>...<script> in the URL
778
+ // contents.
779
+ htmlspecialchars(
780
+ $this->config['base_url'] . $page_url_query_prefix,
781
+ ENT_QUOTES | ENT_HTML5,
782
+ $this->config['charset'],
783
+ // Don't double-encode.
784
+ false
785
+ ),
786
+ $content
787
+ );
788
+
789
+ return $content;
790
+ }
791
+
792
+ /**
793
+ * Return hidden metadata for adding to SEO content.
794
+ */
795
+ private function _buildComment($access_method) {
796
+ $bvf = new BVFooter($this, $access_method, $this->msg);
797
+ $footer = $bvf->buildSDKFooter();
798
+ $reveal = $this->_getBVReveal();
799
+ if ($reveal) {
800
+ $footer .= $bvf->buildSDKDebugFooter();
801
+ }
802
+ return $footer;
803
+ }
804
 
805
+ public function getBVMessages() {
806
+ return $this->msg;
807
+ }
808
 
809
+ public function getContent() {
810
+ $this->_setBuildMessage('Content Type "' . $this->config['content_type'] . '" is not supported by getContent().');
811
+ $pay_load = $this->_buildComment('', 'getContent');
812
 
813
+ return $pay_load;
814
+ }
815
 
816
+ public function getAggregateRating() {
817
+ $this->_setBuildMessage('Content Type "' . $this->config['content_type'] . '" is not supported by getAggregateRating().');
818
+ $pay_load = $this->_buildComment('', 'getAggregateRating');
819
+
820
+ return $pay_load;
821
+ }
 
 
 
 
822
 
823
+ public function getReviews() {
824
+ $this->_setBuildMessage('Content Type "' . $this->config['content_type'] . '" is not supported by getReviews().');
825
+ $pay_load = $this->_buildComment('', 'getReviews');
826
+
827
+ return $pay_load;
828
+ }
829
+
830
+ }
831
+ // end of Base class
832
+
833
+ /**
834
+ * Reviews Class
835
+ *
836
+ * Base class extention for work with "reviews" content type.
837
+ */
838
+ class Reviews extends Base {
839
+
840
+ function __construct($params = array()) {
841
+ // call Base Class constructor
842
+ parent::__construct($params);
843
+
844
+ // since we are in the reviews class
845
+ // we need to set the content_type config
846
+ // to reviews so we get reviews in our
847
+ // SEO request
848
+ $this->config['content_type'] = 'reviews';
849
+
850
+ // for reviews subject type will always
851
+ // need to be product
852
+ $this->config['subject_type'] = 'product';
853
+ }
854
+
855
+ public function getAggregateRating() {
856
+ return $this->_renderAggregateRating();
857
+ }
858
+
859
+ public function getReviews() {
860
+ return $this->_renderReviews();
861
+ }
862
+
863
+ public function getContent() {
864
+ $payload = $this->_renderSEO('getContent');
865
+
866
+ if (!empty($this->config['page_params']['subject_id']) && $this->_checkBVStateContentType()) {
867
+ $subject_id = $this->config['page_params']['subject_id'];
868
+ } else {
869
+ $subject_id = $this->config['subject_id'];
870
  }
871
+ // if they want to power display integration as well
872
+ // then we need to include the JS integration code
873
+ if ($this->config['include_display_integration_code']) {
874
+ $payload .= '
875
+ <script>
876
+ $BV.ui("rr", "show_reviews", {
877
+ productId: "' . $subject_id . '"
878
+ });
879
+ </script>
880
+ ';
881
+ }
882
+
883
+ return $payload;
884
+ }
885
 
886
+ }
887
+ // end of Reviews class
888
+
889
+ /**
890
+ * Questions Class
891
+ *
892
+ * Base class extention for work with "questions" content type.
893
+ */
894
+ class Questions extends Base {
895
+
896
+ function __construct($params = array()) {
897
+ // call Base Class constructor
898
+ parent::__construct($params);
899
+
900
+ // since we are in the questions class
901
+ // we need to set the content_type config
902
+ // to questions so we get questions in our
903
+ // SEO request
904
+ $this->config['content_type'] = 'questions';
905
+ }
906
+
907
+ public function getContent() {
908
+ $payload = $this->_renderSEO('getContent');
909
+ if (!empty($this->config['page_params']['subject_id']) && $this->_checkBVStateContentType()) {
910
+ $subject_id = $this->config['page_params']['subject_id'];
911
+ } else {
912
+ $subject_id = $this->config['subject_id'];
913
+ }
914
+ // if they want to power display integration as well
915
+ // then we need to include the JS integration code
916
+ if ($this->config['include_display_integration_code']) {
917
+
918
+ $payload .= '
919
+ <script>
920
+ $BV.ui("qa", "show_questions", {
921
+ productId: "' . $subject_id . '"
922
+ });
923
+ </script>
924
+ ';
925
+ }
926
 
927
+ return $payload;
928
+ }
 
 
 
 
 
 
 
 
 
 
 
929
 
930
+ }
931
+ // end of Questions class
932
 
933
+ /**
934
+ * Stories Class
935
+ *
936
+ * Base class extention for work with "stories" content type.
937
+ */
938
+ class Stories extends Base {
939
+
940
+ function __construct($params = array()) {
941
+ // call Base Class constructor
942
+ parent::__construct($params);
943
+
944
+ // since we are in the stories class
945
+ // we need to set the content_type config
946
+ // to stories so we get stories in our
947
+ // SEO request
948
+ $this->config['content_type'] = 'stories';
949
+
950
+ // for stories subject type will always
951
+ // need to be product
952
+ $this->config['subject_type'] = 'product';
953
+
954
+ // for stories we have to set content sub type
955
+ // the sub type is configured as either STORIES_LIST or STORIES_GRID
956
+ // the folder names are "stories" and "storiesgrid" respectively.
957
+ if (isset($this->config['content_sub_type'])
958
+ && $this->config['content_sub_type'] == "stories_grid") {
959
+ $this->config['content_sub_type'] = "storiesgrid";
960
+ } else {
961
+ $this->config['content_sub_type'] = "stories";
962
  }
963
+ }
964
+
965
+ public function getContent() {
966
+ $payload = $this->_renderSeo('getContent');
967
+ if (!empty($this->config['page_params']['subject_id']) && $this->_checkBVStateContentType()) {
968
+ $subject_id = $this->config['page_params']['subject_id'];
969
+ } else {
970
+ $subject_id = $this->config['subject_id'];
971
+ }
972
+ // if they want to power display integration as well
973
+ // then we need to include the JS integration code
974
+ if ($this->config['include_display_integration_code']) {
975
+ $payload .= '
976
+ <script>
977
+ $BV.ui("su", "show_stories", {
978
+ productId: "' . $subject_id . '"
979
+ });
980
+ </script>
981
+ ';
982
+ }
983
+
984
+ return $payload;
985
+ }
986
 
987
+ }
988
+ // end of Stories class
989
 
990
+ class Spotlights extends Base {
991
+
992
+ function __construct($params = array()) {
993
+ // call Base Class constructor
994
+ parent::__construct($params);
995
+
996
+ // since we are in the spotlights class
997
+ // we need to set the content_type config
998
+ // to spotlights so we get reviews in our
999
+ // SEO request
1000
+ $this->config['content_type'] = 'spotlights';
1001
+
1002
+ // for spotlights subject type will always
1003
+ // need to be category
1004
+ $this->config['subject_type'] = 'category';
1005
+ }
1006
+
1007
+ public function getContent() {
1008
+ return $this->_renderSEO('getContent');
1009
+ }
1010
+
1011
+ }
1012
+ // end of Spotlights class
1013
+
1014
+ class SellerRatings extends Base {
1015
+
1016
+ function __construct($params = array()) {
1017
 
 
 
1018
  // call Base Class constructor
1019
  parent::__construct($params);
1020
+
1021
+ // since we are in the Seller Rating class
1022
+ // we need to set the content_type config
1023
+ // to reviews so we get reviews in our
1024
  // SEO request
1025
+ $this->config['content_type'] = 'reviews';
 
1026
 
1027
+ // for seller rating subject type will always
1028
+ // need to be seller
1029
+ $this->config['subject_type'] = 'seller';
1030
 
1031
+ }
 
 
 
 
1032
 
1033
+ public function getContent() {
1034
+ return $this->_renderSEO('getContent');
1035
+ }
 
 
 
 
 
1036
 
1037
+ }
1038
+ // end of Spotlights class
1039
 
 
 
1040
 
1041
+ // end of bvseosdk.php
package.xml CHANGED
@@ -1,7 +1,7 @@
1
  <?xml version="1.0"?>
2
  <package>
3
  <name>Bazaarvoice_Connector</name>
4
- <version>6.2.0</version>
5
  <stability>stable</stability>
6
  <license>Bazaarvoice, Inc.</license>
7
  <channel>community</channel>
@@ -10,9 +10,9 @@
10
  <description>See www.bazaarvoice.com for further details on our offerings</description>
11
  <notes>Plugin corresponds to version 6.0.2 of the Bazaarvoice platform</notes>
12
  <authors><author><name>BV DTS</name><user>bvdts</user><email>dts@bazaarvoice.com</email></author></authors>
13
- <date>2015-12-18</date>
14
- <time>18:54:01</time>
15
- <contents><target name="magelocal"><dir name="Bazaarvoice"><dir name="Connector"><dir name="Block"><file name="Bazaarvoice.php" hash="473e92e58fd86c823fb55765ed473414"/><file name="Questions.php" hash="04a7302c66539d17fac4f75b13b2ff2a"/><file name="Ratings.php" hash="6822db584f2ee777beb8d39c662e8465"/><file name="Reviews.php" hash="3eb82792967e70e5fed975521c5b5ceb"/><dir name="Roi"><file name="Beacon.php" hash="388253b41a5e251ee4c435b42304c6ca"/></dir><file name="Submissioncontainer.php" hash="23d04ee9ece011d107bb8b914653a24d"/></dir><dir name="Helper"><file name="Data.php" hash="2887f1dcf71ca7badf18bebffec3dc6c"/><file name="Sftp.php" hash="1c2867bdaaaaa94e14848a07a2b6b688"/><file name="SftpConnection.php" hash="d1ebf3c0d501fc39f9db15b76ccae3e7"/></dir><dir name="Model"><file name="ExportProductFeed.php" hash="015100464ca6aa1e0df8239127c2b20a"/><file name="ExportPurchaseFeed.php" hash="836b92a34272945e5cd70b018870711d"/><dir name="Mysql4"><file name="Setup.php" hash="884c886c1e9fa395f05e7872ba6478a2"/></dir><dir name="ProductFeed"><file name="Brand.php" hash="b2ab72e79d7e3afe23b38bc3b64ba115"/><file name="Category.php" hash="a3f6f51ac7ff3bc93889fd0befc9802e"/><file name="Product.php" hash="9870f90af87896918c1e1c239d074e5b"/></dir><file name="RetrieveInlineRatingsFeed.php" hash="c64ad33753408111e04322ef4b39ef34"/><file name="RetrieveSmartSEOPackage.php" hash="ececf2f31e0b3ca9c2dda4ed6967c8c1"/><dir name="Source"><file name="AuthenticationMethod.php" hash="582d6c76372bac64728e6e4d68f959e4"/><file name="Environment.php" hash="1e575c9adb480df80e4a8a917960bd55"/><file name="FeedGenerationScope.php" hash="b2450e4c0c69b0da328f1d0d7d67012a"/><file name="TriggeringEvent.php" hash="fa47f3a2fcec92d9603f21541c853035"/></dir></dir><dir name="controllers"><file name="FeedController.php" hash="933a1555d97ac2cec8ea52306cf63102"/><file name="IndexController.php" hash="16b9a353153d40ebc32e759112ca6d6f"/></dir><dir name="etc"><file name="adminhtml.xml" hash="39cf8642bfc219709849618519a1c767"/><file name="config.xml" hash="1d10a8baa6f1cc56757f50d670f003f7"/><file name="system.xml" hash="efbc1b4d25f09f93d1bba3c012f77f27"/></dir><dir name="sql"><dir name="bazaarvoice_setup"><file name="mysql4-install-0.1.0.php" hash="2cf37e5f08ae8bb7f8c43d23d9493e58"/></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Bazaarvoice_Connector.xml" hash="c517b52d29fec93a83a8010451368ee1"/></dir></target><target name="magedesign"><dir name="frontend"><dir name="enterprise"><dir name="default"><dir name="template"><dir name="bazaarvoice"><dir name="display"><file name="headerincludes.phtml" hash="0faf96303bfe38e1c4130dfdbaff9c87"/><file name="headerproduct.phtml" hash="b2f48fcb558b33faeb4c6fa0cff7a064"/><dir name="qa"><file name="questions.phtml" hash="b5c186576e9c20cd764860ad08702349"/><file name="questionsummary.phtml" hash="c6044994c1d355730d7e5cfd3a6e3125"/></dir><dir name="rr"><file name="ratings.phtml" hash="56047515b347d0250d44e4e2eb0a923c"/><file name="reviews.phtml" hash="7d2ad8b18910010d22240c9521131290"/><file name="reviewsummary.phtml" hash="75e823088dd75387fc94e6028d8236a5"/></dir></dir><dir name="submit"><file name="roi_beacon.phtml" hash="494b9a44ceb14b1f376611d30ac18645"/><file name="submissioncontainer.phtml" hash="3f3f22b922c1aa5b87f7bbf4fbeea51c"/></dir></dir></dir><dir name="layout"><file name="bazaarvoice.xml" hash="e8d6c4a2ca62a21ddac1cd22e1ee8ad0"/></dir></dir></dir></dir></target><target name="magelocale"><dir name="en_US"><dir name="template"><dir name="email"><file name="bazaarvoice_notification.html" hash="9a0e90ac62d926dad4db13719f3c8b73"/></dir></dir></dir></target><target name="mageskin"><dir name="frontend"><dir name="base"><dir name="default"><dir name="images"><dir name="bazaarvoice"><file name="rating-0_0.gif" hash="f50bd3f45f69a753614b2e76f53bdafc"/><file name="rating-1_0.gif" hash="c691e11e3250a18939aec523734d9b67"/><file name="rating-1_1.gif" hash="26377f1337bb6fb9e340292243a6f780"/><file name="rating-1_2.gif" hash="5c51583dc52d901c61d9470d5faeb2a4"/><file name="rating-1_3.gif" hash="3948c716d18ea0389ce9e57c347e7b6d"/><file name="rating-1_4.gif" hash="2211d8586bda467cb8fcc617670b94df"/><file name="rating-1_5.gif" hash="3fa9480c8b86f85749147fa0e8144b05"/><file name="rating-1_6.gif" hash="a577c79e7ea0c6c59ac15251c39de515"/><file name="rating-1_7.gif" hash="b5b52fa267632eda6ba5b3be56319397"/><file name="rating-1_8.gif" hash="205170e1ffbfcc81569286a9e1a88eb5"/><file name="rating-1_9.gif" hash="63709f7b2a2e2f14ae442dbef6513f25"/><file name="rating-2_0.gif" hash="4eec2468b5e41dc03d198ed6fe084a53"/><file name="rating-2_1.gif" hash="155cab7b16f4cfef3e94b99ca297cedc"/><file name="rating-2_2.gif" hash="2e2dc606fd83853bdf90a3beb901cf3e"/><file name="rating-2_3.gif" hash="638632f37a750558722c0bf1a79f2546"/><file name="rating-2_4.gif" hash="6b0a85c21066c6402b9f8914284b999f"/><file name="rating-2_5.gif" hash="c4792dac3b9d5a914a72a4200f931c6e"/><file name="rating-2_6.gif" hash="1c7ac3f4e3721d4779721973cfaaa8db"/><file name="rating-2_7.gif" hash="21b680dce6ffef505532afea7fea1452"/><file name="rating-2_8.gif" hash="136ac6b284d1a2b9452a06eea993c1fa"/><file name="rating-2_9.gif" hash="d13af6920569aa85da6dfb0a139d560a"/><file name="rating-3_0.gif" hash="6b30e597cc23aec52dbd2be978d52351"/><file name="rating-3_1.gif" hash="cb544d168a949100fb5ee117adbd765b"/><file name="rating-3_2.gif" hash="75124c4b4dfc5cbcf5ae3ccfa7bdf906"/><file name="rating-3_3.gif" hash="0693b6a471361957da1dc8ee2e9af5ec"/><file name="rating-3_4.gif" hash="1a6e3cff41a61e1bbed9296badb94392"/><file name="rating-3_5.gif" hash="7f63ecf505414386267fad2e92617a9f"/><file name="rating-3_6.gif" hash="8b9b9ccebc3537cffd2bed75c60eaa9e"/><file name="rating-3_7.gif" hash="7f83f3996a738d1fd6763204cd964376"/><file name="rating-3_8.gif" hash="219a1f2dbd45bcb58a58f460c9491bbf"/><file name="rating-3_9.gif" hash="e4114607ca469db2fd5f87ac21c4f00a"/><file name="rating-4_0.gif" hash="a15541525186bf6911202e0f64daa4a6"/><file name="rating-4_1.gif" hash="818971c067beb397247095f5eedbac29"/><file name="rating-4_2.gif" hash="5b9599176771adfbf8c52c7dfa04e565"/><file name="rating-4_3.gif" hash="18dc68db736819e17ab5cf0d5725d99c"/><file name="rating-4_4.gif" hash="56b124f1a2e599918b462ce29cd1cd96"/><file name="rating-4_5.gif" hash="2044f11b1f7005f66f14219c5fce1020"/><file name="rating-4_6.gif" hash="3166f044e7f73f9b3e75bda4507eaa35"/><file name="rating-4_7.gif" hash="20546d3ebee7a364927e9da9274996a7"/><file name="rating-4_8.gif" hash="9dab0f19785d1592a96c5c295842f308"/><file name="rating-4_9.gif" hash="19a47143b04aceae85def246059fba33"/><file name="rating-5_0.gif" hash="e43b403663785255d2f023ca35566ac3"/></dir></dir></dir></dir></dir></target><target name="magelib"><dir name="Bazaarvoice"><file name="bvseosdk.php" hash="238d2a03d28a980abc71f56c2933e037"/></dir></target><target name="mageweb"><dir name="shell"><file name="bv_export_order_feed.php" hash="309995ede2f85d95a0b91d8845c06ae5"/><file name="bv_export_product_feed.php" hash="1696c363c97bf9943560045b297c98fb"/></dir></target></contents>
16
  <compatible/>
17
  <dependencies><required><php><min>5.2.0</min><max>6.0.0</max></php></required></dependencies>
18
  </package>
1
  <?xml version="1.0"?>
2
  <package>
3
  <name>Bazaarvoice_Connector</name>
4
+ <version>6.3.0</version>
5
  <stability>stable</stability>
6
  <license>Bazaarvoice, Inc.</license>
7
  <channel>community</channel>
10
  <description>See www.bazaarvoice.com for further details on our offerings</description>
11
  <notes>Plugin corresponds to version 6.0.2 of the Bazaarvoice platform</notes>
12
  <authors><author><name>BV DTS</name><user>bvdts</user><email>dts@bazaarvoice.com</email></author></authors>
13
+ <date>2016-01-22</date>
14
+ <time>16:34:59</time>
15
+ <contents><target name="magelocal"><dir name="Bazaarvoice"><dir name="Connector"><dir name="Block"><file name="Bazaarvoice.php" hash="473e92e58fd86c823fb55765ed473414"/><file name="Questions.php" hash="10606c0da9a307f5988b2e15361ee02f"/><file name="Ratings.php" hash="6822db584f2ee777beb8d39c662e8465"/><file name="Reviews.php" hash="7c03096679d1de2a25ebb5f1c9f140df"/><dir name="Roi"><file name="Beacon.php" hash="388253b41a5e251ee4c435b42304c6ca"/></dir><file name="Submissioncontainer.php" hash="23d04ee9ece011d107bb8b914653a24d"/></dir><dir name="Helper"><file name="Data.php" hash="2887f1dcf71ca7badf18bebffec3dc6c"/><file name="Sftp.php" hash="1c2867bdaaaaa94e14848a07a2b6b688"/><file name="SftpConnection.php" hash="d1ebf3c0d501fc39f9db15b76ccae3e7"/></dir><dir name="Model"><file name="ExportProductFeed.php" hash="015100464ca6aa1e0df8239127c2b20a"/><file name="ExportPurchaseFeed.php" hash="d00c6ab40ccd7f0b6bc63eb0d3a9a3c4"/><dir name="Mysql4"><file name="Setup.php" hash="884c886c1e9fa395f05e7872ba6478a2"/></dir><dir name="ProductFeed"><file name="Brand.php" hash="b2ab72e79d7e3afe23b38bc3b64ba115"/><file name="Category.php" hash="a3f6f51ac7ff3bc93889fd0befc9802e"/><file name="Product.php" hash="9870f90af87896918c1e1c239d074e5b"/></dir><file name="RetrieveInlineRatingsFeed.php" hash="c64ad33753408111e04322ef4b39ef34"/><file name="RetrieveSmartSEOPackage.php" hash="ececf2f31e0b3ca9c2dda4ed6967c8c1"/><dir name="Source"><file name="AuthenticationMethod.php" hash="582d6c76372bac64728e6e4d68f959e4"/><file name="Environment.php" hash="1e575c9adb480df80e4a8a917960bd55"/><file name="FeedGenerationScope.php" hash="b2450e4c0c69b0da328f1d0d7d67012a"/><file name="TriggeringEvent.php" hash="fa47f3a2fcec92d9603f21541c853035"/></dir></dir><dir name="controllers"><file name="FeedController.php" hash="933a1555d97ac2cec8ea52306cf63102"/><file name="IndexController.php" hash="16b9a353153d40ebc32e759112ca6d6f"/></dir><dir name="etc"><file name="adminhtml.xml" hash="39cf8642bfc219709849618519a1c767"/><file name="config.xml" hash="4613fab9a92532b9abaca238b990e455"/><file name="system.xml" hash="efbc1b4d25f09f93d1bba3c012f77f27"/></dir><dir name="sql"><dir name="bazaarvoice_setup"><file name="mysql4-install-0.1.0.php" hash="2cf37e5f08ae8bb7f8c43d23d9493e58"/></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Bazaarvoice_Connector.xml" hash="c517b52d29fec93a83a8010451368ee1"/></dir></target><target name="magedesign"><dir name="frontend"><dir name="enterprise"><dir name="default"><dir name="template"><dir name="bazaarvoice"><dir name="display"><file name="headerincludes.phtml" hash="0faf96303bfe38e1c4130dfdbaff9c87"/><file name="headerproduct.phtml" hash="b2f48fcb558b33faeb4c6fa0cff7a064"/><dir name="qa"><file name="questions.phtml" hash="b5c186576e9c20cd764860ad08702349"/><file name="questionsummary.phtml" hash="c6044994c1d355730d7e5cfd3a6e3125"/></dir><dir name="rr"><file name="ratings.phtml" hash="56047515b347d0250d44e4e2eb0a923c"/><file name="reviews.phtml" hash="7d2ad8b18910010d22240c9521131290"/><file name="reviewsummary.phtml" hash="75e823088dd75387fc94e6028d8236a5"/></dir></dir><dir name="submit"><file name="roi_beacon.phtml" hash="494b9a44ceb14b1f376611d30ac18645"/><file name="submissioncontainer.phtml" hash="3f3f22b922c1aa5b87f7bbf4fbeea51c"/></dir></dir></dir><dir name="layout"><file name="bazaarvoice.xml" hash="e8d6c4a2ca62a21ddac1cd22e1ee8ad0"/></dir></dir></dir></dir></target><target name="magelocale"><dir name="en_US"><dir name="template"><dir name="email"><file name="bazaarvoice_notification.html" hash="9a0e90ac62d926dad4db13719f3c8b73"/></dir></dir></dir></target><target name="mageskin"><dir name="frontend"><dir name="base"><dir name="default"><dir name="images"><dir name="bazaarvoice"><file name="rating-0_0.gif" hash="f50bd3f45f69a753614b2e76f53bdafc"/><file name="rating-1_0.gif" hash="c691e11e3250a18939aec523734d9b67"/><file name="rating-1_1.gif" hash="26377f1337bb6fb9e340292243a6f780"/><file name="rating-1_2.gif" hash="5c51583dc52d901c61d9470d5faeb2a4"/><file name="rating-1_3.gif" hash="3948c716d18ea0389ce9e57c347e7b6d"/><file name="rating-1_4.gif" hash="2211d8586bda467cb8fcc617670b94df"/><file name="rating-1_5.gif" hash="3fa9480c8b86f85749147fa0e8144b05"/><file name="rating-1_6.gif" hash="a577c79e7ea0c6c59ac15251c39de515"/><file name="rating-1_7.gif" hash="b5b52fa267632eda6ba5b3be56319397"/><file name="rating-1_8.gif" hash="205170e1ffbfcc81569286a9e1a88eb5"/><file name="rating-1_9.gif" hash="63709f7b2a2e2f14ae442dbef6513f25"/><file name="rating-2_0.gif" hash="4eec2468b5e41dc03d198ed6fe084a53"/><file name="rating-2_1.gif" hash="155cab7b16f4cfef3e94b99ca297cedc"/><file name="rating-2_2.gif" hash="2e2dc606fd83853bdf90a3beb901cf3e"/><file name="rating-2_3.gif" hash="638632f37a750558722c0bf1a79f2546"/><file name="rating-2_4.gif" hash="6b0a85c21066c6402b9f8914284b999f"/><file name="rating-2_5.gif" hash="c4792dac3b9d5a914a72a4200f931c6e"/><file name="rating-2_6.gif" hash="1c7ac3f4e3721d4779721973cfaaa8db"/><file name="rating-2_7.gif" hash="21b680dce6ffef505532afea7fea1452"/><file name="rating-2_8.gif" hash="136ac6b284d1a2b9452a06eea993c1fa"/><file name="rating-2_9.gif" hash="d13af6920569aa85da6dfb0a139d560a"/><file name="rating-3_0.gif" hash="6b30e597cc23aec52dbd2be978d52351"/><file name="rating-3_1.gif" hash="cb544d168a949100fb5ee117adbd765b"/><file name="rating-3_2.gif" hash="75124c4b4dfc5cbcf5ae3ccfa7bdf906"/><file name="rating-3_3.gif" hash="0693b6a471361957da1dc8ee2e9af5ec"/><file name="rating-3_4.gif" hash="1a6e3cff41a61e1bbed9296badb94392"/><file name="rating-3_5.gif" hash="7f63ecf505414386267fad2e92617a9f"/><file name="rating-3_6.gif" hash="8b9b9ccebc3537cffd2bed75c60eaa9e"/><file name="rating-3_7.gif" hash="7f83f3996a738d1fd6763204cd964376"/><file name="rating-3_8.gif" hash="219a1f2dbd45bcb58a58f460c9491bbf"/><file name="rating-3_9.gif" hash="e4114607ca469db2fd5f87ac21c4f00a"/><file name="rating-4_0.gif" hash="a15541525186bf6911202e0f64daa4a6"/><file name="rating-4_1.gif" hash="818971c067beb397247095f5eedbac29"/><file name="rating-4_2.gif" hash="5b9599176771adfbf8c52c7dfa04e565"/><file name="rating-4_3.gif" hash="18dc68db736819e17ab5cf0d5725d99c"/><file name="rating-4_4.gif" hash="56b124f1a2e599918b462ce29cd1cd96"/><file name="rating-4_5.gif" hash="2044f11b1f7005f66f14219c5fce1020"/><file name="rating-4_6.gif" hash="3166f044e7f73f9b3e75bda4507eaa35"/><file name="rating-4_7.gif" hash="20546d3ebee7a364927e9da9274996a7"/><file name="rating-4_8.gif" hash="9dab0f19785d1592a96c5c295842f308"/><file name="rating-4_9.gif" hash="19a47143b04aceae85def246059fba33"/><file name="rating-5_0.gif" hash="e43b403663785255d2f023ca35566ac3"/></dir></dir></dir></dir></dir></target><target name="magelib"><dir name="Bazaarvoice"><file name="BVFooter.php" hash="383d2f4ffd8a986f7e745676d052f724"/><file name="BVUtility.php" hash="88bef98454329c75069a4806d2448625"/><file name="bvseosdk.php" hash="33a494f6dea9b8a3c8d0f517e79165b1"/></dir></target><target name="mageweb"><dir name="shell"><file name="bv_export_order_feed.php" hash="309995ede2f85d95a0b91d8845c06ae5"/><file name="bv_export_product_feed.php" hash="1696c363c97bf9943560045b297c98fb"/></dir></target></contents>
16
  <compatible/>
17
  <dependencies><required><php><min>5.2.0</min><max>6.0.0</max></php></required></dependencies>
18
  </package>