Bazaarvoice_Connector - Version 6.3.7

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


Code changes from version 6.3.6 to 6.3.7

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