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);