Skip to content

Commit 3bf55f9

Browse files
authored
Merge pull request #80 from n49/slider_v1.1.8
added fallback if the schema is not found in header
2 parents 93eb5d4 + 14d507a commit 3bf55f9

3 files changed

Lines changed: 121 additions & 34 deletions

File tree

includes/class-feed-shortcode.php

Lines changed: 118 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ class Feed_Shortcode {
88

99
private static $schema_scripts = array();
1010
private static $schemas_processed = false;
11+
private static $schemas_output_in_head = false; // Track if schemas were successfully output in head
1112

1213
public function __construct(Feed_Deserializer $feed_deserializer) {
1314
$this->feed_deserializer = $feed_deserializer;
@@ -40,46 +41,109 @@ public function pre_process_schemas() {
4041

4142
global $wp_query, $post;
4243

43-
$content = '';
4444
$feed_ids = array();
45+
$content_sources = array();
4546

4647
// Check current post/page content
4748
if ($post && isset($post->post_content)) {
48-
$content .= $post->post_content;
49+
$content_sources[] = $post->post_content;
4950
}
5051

5152
// Check all posts in the main query (for archive pages, etc.)
5253
if ($wp_query && isset($wp_query->posts) && is_array($wp_query->posts)) {
5354
foreach ($wp_query->posts as $query_post) {
5455
if (isset($query_post->post_content)) {
55-
$content .= $query_post->post_content;
56+
$content_sources[] = $query_post->post_content;
5657
}
5758
}
5859
}
5960

60-
// Extract all opio_feed shortcode IDs from content using regex
61-
if (preg_match_all('/\[opio_feed[^\]]*id=["\']?(\d+)["\']?[^\]]*\]/i', $content, $matches)) {
62-
if (!empty($matches[1])) {
63-
$feed_ids = array_unique(array_map('intval', $matches[1]));
61+
// Check widget areas for shortcodes
62+
// Widgets store content differently, so we need to check widget options
63+
$all_widgets = wp_get_sidebars_widgets();
64+
if (is_array($all_widgets)) {
65+
foreach ($all_widgets as $sidebar_id => $widget_ids) {
66+
if (!is_array($widget_ids)) continue;
67+
68+
foreach ($widget_ids as $widget_id) {
69+
// Check text widgets (classic widget)
70+
if (preg_match('/^text-(\d+)$/', $widget_id, $text_match)) {
71+
$text_widgets = get_option('widget_text', array());
72+
if (is_array($text_widgets)) {
73+
foreach ($text_widgets as $widget_instance) {
74+
if (isset($widget_instance['text'])) {
75+
$content_sources[] = $widget_instance['text'];
76+
}
77+
}
78+
}
79+
}
80+
81+
// Check custom HTML widgets (WordPress 4.8+)
82+
if (preg_match('/^custom_html-(\d+)$/', $widget_id, $html_match)) {
83+
$html_widgets = get_option('widget_custom_html', array());
84+
if (is_array($html_widgets)) {
85+
foreach ($html_widgets as $widget_instance) {
86+
if (isset($widget_instance['content'])) {
87+
$content_sources[] = $widget_instance['content'];
88+
}
89+
}
90+
}
91+
}
92+
93+
// Check block widgets (WordPress 5.8+ / Full Site Editing)
94+
if (preg_match('/^block-(\d+)$/', $widget_id, $block_match)) {
95+
$block_widgets = get_option('widget_block', array());
96+
if (is_array($block_widgets)) {
97+
foreach ($block_widgets as $widget_instance) {
98+
if (isset($widget_instance['content'])) {
99+
$content_sources[] = $widget_instance['content'];
100+
}
101+
}
102+
}
103+
}
104+
}
64105
}
65106
}
66107

67-
// Also try parsing shortcode attributes for cases where format might differ
68-
if (has_shortcode($content, 'opio_feed')) {
69-
// Use WordPress shortcode parser
70-
preg_match_all('/\[opio_feed([^\]]*)\]/i', $content, $shortcode_matches);
71-
if (!empty($shortcode_matches[1])) {
72-
foreach ($shortcode_matches[1] as $atts_string) {
73-
$atts = shortcode_parse_atts($atts_string);
74-
if (isset($atts['id'])) {
75-
$feed_ids[] = intval($atts['id']);
108+
// Extract shortcodes from all content sources
109+
foreach ($content_sources as $content) {
110+
if (empty($content)) continue;
111+
112+
// Match: [opio_feed id='52'], [opio_feed id="52"], [opio_feed id=52]
113+
// This regex handles spaces and single/double quotes
114+
if (preg_match_all('/\[opio_feed[^\]]*id\s*=\s*["\']?(\d+)["\']?[^\]]*\]/i', $content, $matches)) {
115+
if (!empty($matches[1])) {
116+
foreach ($matches[1] as $feed_id_str) {
117+
$feed_id = absint($feed_id_str);
118+
if ($feed_id > 0) {
119+
$feed_ids[] = $feed_id;
120+
}
121+
}
122+
}
123+
}
124+
125+
// Also use WordPress's shortcode parser for robustness
126+
if (has_shortcode($content, 'opio_feed')) {
127+
$pattern = get_shortcode_regex(array('opio_feed'));
128+
if (preg_match_all('/' . $pattern . '/s', $content, $shortcode_matches)) {
129+
foreach ($shortcode_matches[3] as $atts_string) {
130+
if (empty($atts_string)) continue;
131+
$atts = shortcode_parse_atts($atts_string);
132+
if (isset($atts['id'])) {
133+
$feed_id = absint($atts['id']);
134+
if ($feed_id > 0) {
135+
$feed_ids[] = $feed_id;
136+
}
137+
}
76138
}
77139
}
78-
$feed_ids = array_unique($feed_ids);
79140
}
80141
}
81142

82-
// Process all found feed IDs
143+
// Remove duplicates
144+
$feed_ids = array_unique(array_filter($feed_ids));
145+
146+
// Process all found feed IDs - fetch schema from feed.op.io BEFORE wp_head
83147
foreach ($feed_ids as $feed_id) {
84148
if ($feed_id > 0) {
85149
$this->fetch_and_extract_schema($feed_id);
@@ -167,12 +231,18 @@ private function extract_schema_from_html($html) {
167231
* Remove schema scripts from HTML content
168232
*/
169233
private function remove_schema_from_html($html) {
234+
// First, remove entire head sections that contain our schema (in case feed has nested head tags)
235+
// Match head tags that contain our schema script
236+
$head_with_schema_pattern = '/<head[^>]*>.*?<script[^>]*(?:id=["\']jsonldSchema["\'][^>]*type=["\']application\/ld\+json["\']|type=["\']application\/ld\+json["\'][^>]*id=["\']jsonldSchema["\'])[^>]*>.*?<\/script>.*?<\/head>/is';
237+
$html = preg_replace($head_with_schema_pattern, '', $html);
238+
170239
// Remove script tags with id="jsonldSchema" and type="application/ld+json"
171240
// Handles attributes in any order and with single or double quotes
172-
$pattern = '/<script[^>]*(?:id=["\']jsonldSchema["\'][^>]*type=["\']application\/ld\+json["\']|type=["\']application\/ld\+json["\'][^>]*id=["\']jsonldSchema["\'])[^>]*>.*?<\/script>/is';
173-
$html = preg_replace($pattern, '', $html);
241+
// This catches any remaining schema scripts (even outside head tags)
242+
$schema_script_pattern = '/<script[^>]*(?:id=["\']jsonldSchema["\'][^>]*type=["\']application\/ld\+json["\']|type=["\']application\/ld\+json["\'][^>]*id=["\']jsonldSchema["\'])[^>]*>.*?<\/script>/is';
243+
$html = preg_replace($schema_script_pattern, '', $html);
174244

175-
// Also remove any stray head tags that might be in the content
245+
// Also remove any stray/empty head tags that might be in the content
176246
// Remove opening <head> tags
177247
$html = preg_replace('/<head[^>]*>/i', '', $html);
178248
// Remove closing </head> tags
@@ -192,12 +262,13 @@ public function start_output_buffering() {
192262
}
193263

194264
// Check if output buffering is already started (by another plugin)
195-
// If so, we'll work with the existing buffer
196-
if (ob_get_level() > 0) {
197-
return;
198-
}
265+
// If another plugin started buffering, we'll still add our own buffer on top
266+
// This ensures our callback runs and can inject schemas
267+
$existing_level = ob_get_level();
199268

200-
// Start output buffering with callback
269+
// Start our own output buffering with callback
270+
// Even if other buffers exist, our callback will be called when WordPress flushes
271+
// The callback modifies the final HTML before it's sent, so Google crawler sees it
201272
ob_start(array($this, 'buffer_callback'));
202273
}
203274

@@ -240,15 +311,16 @@ public function buffer_callback($buffer) {
240311
// Check if head already contains our schemas
241312
$has_our_schema = (stripos($head_content, 'id="jsonldSchema"') !== false);
242313
if ($has_our_schema) {
243-
// Schemas already in head, return as-is
314+
// Schemas already in head, mark as successful
315+
self::$schemas_output_in_head = true;
244316
return $buffer;
245317
}
246318
}
247319

248320
// Find the closing </head> tag
249321
$head_position = stripos($buffer, '</head>');
250322
if ($head_position === false) {
251-
// No head tag found, return as-is
323+
// No head tag found, return as-is (schema will remain in body)
252324
return $buffer;
253325
}
254326

@@ -264,6 +336,9 @@ public function buffer_callback($buffer) {
264336
// Inject schemas before closing </head> tag
265337
$buffer = substr_replace($buffer, $schema_html . '</head>', $head_position, 7);
266338

339+
// Mark that schemas were successfully injected via buffer
340+
self::$schemas_output_in_head = true;
341+
267342
return $buffer;
268343
}
269344

@@ -286,6 +361,9 @@ public function output_schemas_to_head() {
286361
foreach ($unique_schemas as $schema) {
287362
echo $schema . "\n";
288363
}
364+
365+
// Mark that schemas were successfully output in head
366+
self::$schemas_output_in_head = true;
289367
}
290368

291369
public function init($atts) {
@@ -329,13 +407,22 @@ public function init($atts) {
329407

330408
// Extract schema and store it (even if wp_head already fired - we'll inject via buffer)
331409
$schemas = $this->extract_schema_from_html($reviews);
332-
if (!empty($schemas)) {
410+
$has_schema = !empty($schemas);
411+
412+
if ($has_schema) {
333413
// Always store schemas - we'll inject them via output buffer if wp_head already fired
334414
self::$schema_scripts = array_merge(self::$schema_scripts, $schemas);
335415
}
336416

337-
// Always remove schema scripts from body content
338-
$reviews = $this->remove_schema_from_html($reviews);
417+
// Remove schema scripts from body content if we've collected schemas
418+
// If schemas are in our collection, they will be (or already were) output in head
419+
// Only keep schema in body if NO schemas were collected at all (safety fallback)
420+
if ($has_schema || self::$schemas_output_in_head || !empty(self::$schema_scripts)) {
421+
// We found schema in this feed OR schemas were already processed/output
422+
// Remove schema from body since it will be (or was) in head
423+
$reviews = $this->remove_schema_from_html($reviews);
424+
}
425+
// If no schemas were found and none are in collection, leave in body as fallback
339426

340427
// Wrap entire feed content with Nitropack exclusion wrapper
341428
echo '<div data-nitro-exclude="all" data-nitro-ignore="true" data-nitro-no-optimize="true" data-nitro-preserve-ws="true">';

opio.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Plugin Name: Widget for OPIO Reviews
44
Plugin URI:
55
Description: Instantly add OPIO Reviews on your website to increase user confidence and SEO.
6-
Version: 1.1.7
6+
Version: 1.1.8
77
Author: Dhiraj Timalsina <dhiraj@n49.com>
88
Text Domain: widget-for-opio-reviews
99
Domain Path: /languages
@@ -22,7 +22,7 @@
2222

2323
require(ABSPATH . 'wp-includes/version.php');
2424

25-
define('OPIO_PLUGIN_VERSION' , '1.1.7');
25+
define('OPIO_PLUGIN_VERSION' , '1.1.8');
2626
define('OPIO_PLUGIN_FILE' , __FILE__);
2727
define('OPIO_PLUGIN_URL' , plugins_url(basename(plugin_dir_path(__FILE__ )), basename(__FILE__)));
2828
define('OPIO_ASSETS_URL' , OPIO_PLUGIN_URL . '/assets/');

readme.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Author: Dhiraj Timalsina
33
Tags: Widget for OPIO Reviews, opio, reviews, rating, widget, google business, testimonials
44
Tested up to: 6.4
5-
Stable tag: 1.1.7
5+
Stable tag: 1.1.8
66
License: GPLv2 or later
77
License URI: http://www.gnu.org/licenses/gpl-2.0.html
88

0 commit comments

Comments
 (0)