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 +23 -5
- app/code/local/Bazaarvoice/Connector/Block/Reviews.php +23 -5
- app/code/local/Bazaarvoice/Connector/Model/ExportPurchaseFeed.php +11 -11
- app/code/local/Bazaarvoice/Connector/etc/config.xml +1 -1
- lib/Bazaarvoice/BVFooter.php +129 -0
- lib/Bazaarvoice/BVUtility.php +254 -0
- lib/Bazaarvoice/bvseosdk.php +949 -519
- package.xml +4 -4
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 |
-
$
|
| 42 |
-
|
| 43 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
$
|
| 42 |
-
|
| 43 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 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 |
-
|
| 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 |
-
|
| 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 |
-
|
| 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 |
-
|
| 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 |
-
|
| 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 |
-
|
| 598 |
{
|
| 599 |
return date('Y-m-d', strtotime(date('Y-m-d', time()) . ' -' . self::NUM_DAYS_LOOKBACK . ' days'));
|
| 600 |
}
|
| 601 |
|
| 602 |
-
|
| 603 |
{
|
| 604 |
return time() - (24 * 60 * 60 * $delayDaysSinceEvent);
|
| 605 |
}
|
| 606 |
|
| 607 |
-
|
| 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 |
-
|
| 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 |
-
|
| 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.
|
| 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('\?|&|&|#!|#|_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&a=b
|
| 228 |
+
$url = mb_ereg_replace($paramName . '=[^&#%]*&', '', $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
|
| 8 |
-
* for future
|
| 9 |
-
* this SDK as needed to suit your needs.
|
| 10 |
*
|
| 11 |
* This SDK was built with the following assumptions:
|
| 12 |
-
*
|
| 13 |
-
*
|
| 14 |
-
*
|
| 15 |
-
*
|
| 16 |
-
*
|
| 17 |
*
|
| 18 |
*/
|
| 19 |
-
|
| 20 |
/**
|
| 21 |
* Example usage:
|
| 22 |
*
|
| 23 |
* require(bvsdk.php);
|
| 24 |
*
|
| 25 |
* $bv = new BV(array(
|
| 26 |
-
*
|
| 27 |
-
*
|
| 28 |
-
*
|
| 29 |
-
*
|
| 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 |
-
*
|
| 44 |
-
*
|
| 45 |
-
*
|
| 46 |
*
|
| 47 |
* Optional fields
|
| 48 |
-
*
|
| 49 |
-
*
|
| 50 |
-
*
|
| 51 |
-
*
|
| 52 |
-
*
|
| 53 |
-
*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
*/
|
| 55 |
-
|
| 56 |
class BV {
|
| 57 |
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 76 |
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 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 |
-
|
| 93 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
|
| 95 |
-
|
| 96 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
|
| 98 |
-
|
| 99 |
-
$this->questions = new Questions($this->config);
|
| 100 |
-
|
| 101 |
-
// setup the timer object
|
| 102 |
|
| 103 |
-
|
| 104 |
|
| 105 |
-
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 119 |
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 126 |
|
| 127 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 128 |
}
|
| 129 |
-
} // end of BV class
|
| 130 |
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
|
|
|
|
|
|
| 135 |
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 142 |
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
{
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 202 |
}
|
| 203 |
|
|
|
|
| 204 |
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
//--------------------------------------------------------------------
|
| 208 |
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
* @access private
|
| 216 |
-
* @return bool
|
| 217 |
-
*/
|
| 218 |
|
| 219 |
-
|
| 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 |
-
|
| 229 |
-
|
| 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 |
-
|
| 336 |
-
|
| 337 |
-
|
| 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 |
-
|
| 347 |
-
|
| 348 |
-
|
| 349 |
-
|
| 350 |
-
|
| 351 |
-
|
| 352 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 353 |
|
| 354 |
-
|
| 355 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 356 |
|
| 357 |
-
|
| 358 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 359 |
}
|
| 360 |
|
| 361 |
-
|
| 362 |
-
|
| 363 |
-
|
| 364 |
-
|
| 365 |
-
return $this->_fetchFileContent($resource);
|
| 366 |
-
}
|
| 367 |
-
else
|
| 368 |
-
{
|
| 369 |
-
return $this->_fetchCloudContent($resource);
|
| 370 |
-
}
|
| 371 |
}
|
| 372 |
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
|
| 376 |
-
|
| 377 |
-
|
| 378 |
-
|
| 379 |
-
|
| 380 |
-
|
| 381 |
-
|
| 382 |
-
|
| 383 |
-
|
| 384 |
-
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
|
| 388 |
-
|
| 389 |
-
|
| 390 |
-
|
| 391 |
-
|
| 392 |
-
|
| 393 |
-
|
| 394 |
-
|
| 395 |
-
|
| 396 |
-
|
| 397 |
-
|
| 398 |
-
|
| 399 |
-
|
| 400 |
-
|
| 401 |
-
|
| 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 |
-
|
| 424 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 425 |
|
| 426 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 427 |
|
| 428 |
-
|
| 429 |
-
|
| 430 |
-
|
| 431 |
-
|
| 432 |
-
|
|
|
|
| 433 |
|
| 434 |
-
|
| 435 |
-
|
| 436 |
-
$msg = 'HTTP status code of '.$request['info']['http_code'].' was returned';
|
| 437 |
-
return $this->_buildComment($msg,$url);
|
| 438 |
-
}
|
| 439 |
|
| 440 |
-
|
| 441 |
-
|
| 442 |
-
|
| 443 |
-
|
| 444 |
-
|
| 445 |
-
|
| 446 |
-
|
| 447 |
-
|
| 448 |
-
|
| 449 |
-
|
| 450 |
-
|
| 451 |
-
|
| 452 |
-
|
| 453 |
-
|
| 454 |
-
|
| 455 |
-
|
| 456 |
-
|
| 457 |
-
|
| 458 |
-
|
| 459 |
-
|
| 460 |
-
|
| 461 |
-
|
| 462 |
-
|
| 463 |
-
|
| 464 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 465 |
|
| 466 |
-
|
| 467 |
-
|
| 468 |
-
|
| 469 |
-
|
| 470 |
-
|
| 471 |
-
|
| 472 |
-
|
| 473 |
-
|
| 474 |
-
|
| 475 |
-
|
| 476 |
-
|
| 477 |
-
|
| 478 |
-
|
| 479 |
-
|
| 480 |
-
|
| 481 |
-
|
| 482 |
-
|
| 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 |
-
|
| 516 |
-
|
|
|
|
|
|
|
|
|
|
| 517 |
}
|
| 518 |
|
| 519 |
-
|
| 520 |
-
|
| 521 |
-
|
| 522 |
-
|
| 523 |
-
|
| 524 |
-
|
| 525 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 526 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 527 |
|
| 528 |
-
|
|
|
|
|
|
|
| 529 |
|
|
|
|
|
|
|
|
|
|
| 530 |
|
| 531 |
-
|
|
|
|
| 532 |
|
| 533 |
-
|
| 534 |
-
|
| 535 |
-
|
| 536 |
-
|
| 537 |
-
|
| 538 |
-
|
| 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 |
-
|
| 545 |
-
|
| 546 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 547 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 548 |
|
| 549 |
-
|
| 550 |
-
|
| 551 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 552 |
|
| 553 |
-
|
| 554 |
-
|
| 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 |
-
|
|
|
|
| 568 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 569 |
}
|
| 570 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 571 |
|
|
|
|
|
|
|
| 572 |
|
| 573 |
-
class
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 574 |
|
| 575 |
-
function __construct($params = array())
|
| 576 |
-
{
|
| 577 |
// call Base Class constructor
|
| 578 |
parent::__construct($params);
|
| 579 |
-
|
| 580 |
-
// since we are in the
|
| 581 |
-
// we need to set the
|
| 582 |
-
// to
|
| 583 |
// SEO request
|
| 584 |
-
$this->config['
|
| 585 |
-
}
|
| 586 |
|
| 587 |
-
|
| 588 |
-
|
| 589 |
-
|
| 590 |
|
| 591 |
-
|
| 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 |
-
|
| 598 |
-
|
| 599 |
-
|
| 600 |
-
productId: "'.$this->config['product_id'].'"
|
| 601 |
-
});
|
| 602 |
-
</script>
|
| 603 |
-
';
|
| 604 |
-
}
|
| 605 |
|
| 606 |
-
|
|
|
|
| 607 |
|
| 608 |
-
}
|
| 609 |
-
} // end of Questions class
|
| 610 |
|
| 611 |
-
// end of
|
| 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.
|
| 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>
|
| 14 |
-
<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="
|
| 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>
|
