PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.7.2
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.7.2
2.7.1 2.7.2 2.7.0 2.6.26 2.6.25 2.6.24 2.6.23 2.6.22 2.6.21 2.6.20 2.6.19 2.6.18 2.6.17 2.6.16 2.6.15 2.6.14 2.6.13 2.6.12 2.6.11 2.6.10 2.6.9 2.6.8 2.6.7 2.6.6 2.6.5 All 141 releases
← All changes | public/class-metasync-public.php +96 -16 2.6.22 → 2.7.2 View file →
@@ -148,23 +148,26 @@
148 148 * between the defined hooks and the functions defined in this
149 149 * class.
150 150 */
151 151
152 - # Enqueue the JavaScript file 'otto-tracker.js' with defer for better performance
153 - # Load in footer (true) and add defer attribute to prevent render blocking
154 - wp_enqueue_script($this->plugin_name . '-tracker', plugin_dir_url(__FILE__) . 'js/otto-tracker.min.js', array('jquery'), $this->version, true);
155 - wp_enqueue_script($this->plugin_name, plugin_dir_url(__FILE__) . 'js/metasync-public.js', array('jquery'), $this->version, true);
152 + # Get the Otto Pixel UUID from plugin settings.
153 + $otto_uuid = Metasync::get_option('general')['otto_pixel_uuid'] ?? '';
156 154
155 + # The tracker self-exits when window.saOttoData is absent, and saOttoData is
156 + # only localized when a UUID is configured — so without a UUID the request
157 + # ships a script that can never do anything. Skip it entirely instead.
158 + # OTTO SSR is always enabled by default when UUID is set.
159 + if (!empty($otto_uuid)) {
157 160
158 - # Get the Otto Pixel UUID from plugin settings.
159 - $otto_uuid = Metasync::get_option('general')['otto_pixel_uuid'] ?? '';
161 + # Enqueue the JavaScript file 'otto-tracker.js' with defer for better performance
162 + # Load in footer (true) and add defer attribute to prevent render blocking
163 + # No jQuery dependency: the tracker is plain ES5/ES6 and uses no $.
164 + wp_enqueue_script($this->plugin_name . '-tracker', plugin_dir_url(__FILE__) . 'js/otto-tracker.min.js', array(), $this->version, true);
160 165
161 - # Get the current full page URL safely.
162 - $page_url = esc_url(home_url(add_query_arg([], $_SERVER['REQUEST_URI'])));
166 + # Get the current full page URL safely.
167 + $page_url = esc_url(home_url(add_query_arg([], $_SERVER['REQUEST_URI'])));
163 168
164 - # Pass PHP data to the JavaScript file using wp_localize_script.
165 - # OTTO SSR is always enabled by default when UUID is set
166 - if (!empty($otto_uuid)) {
169 + # Pass PHP data to the JavaScript file using wp_localize_script.
167 170 wp_localize_script($this->plugin_name . '-tracker', 'saOttoData', [
168 171 'otto_uuid' => $otto_uuid,
169 172 'page_url' => $page_url,
170 173 'context' => null,
@@ -171,13 +174,79 @@
171 174 'enable_metadesc' => true, // Always enabled by default
172 175 ]);
173 176 }
174 177
175 - # Add defer attribute to OTTO scripts for performance optimization
176 - add_filter('script_loader_tag', array($this, 'add_defer_attribute'), 10, 2);
178 + # metasync-public.js drives nothing but the [accordion_metasync] shortcode,
179 + # so it only needs to load on a page that actually renders that shortcode.
180 + if ($this->has_accordion_shortcode()) {
181 + wp_enqueue_script($this->plugin_name, plugin_dir_url(__FILE__) . 'js/metasync-public.js', array(), $this->version, true);
182 +
183 + # CSS and JS gate on the same predicate so a placement the scan misses degrades to visible-but-unstyled content, never hidden content.
184 + wp_enqueue_style($this->plugin_name . '-accordion', plugin_dir_url(__FILE__) . 'css/metasync-accordion.css', array(), $this->version, 'all');
185 + }
177 186 }
178 187
179 188 /**
189 + * Whether the current request is likely to render the [accordion_metasync] shortcode.
190 + *
191 + * The shortcode is registered globally, so it can render from more than the
192 + * singular post_content: the excerpt, Elementor's `_elementor_data` post meta
193 + * (JSON, but has_shortcode() still matches the shortcode text inside it), and
194 + * classic Text/Block widgets placed in a sidebar shown on any template.
195 + *
196 + * This is a static scan, not a guarantee — it can't see every builder/theme
197 + * storage format. The `metasync_load_accordion_assets` filter is the escape
198 + * hatch: `add_filter('metasync_load_accordion_assets', '__return_true')` to
199 + * force-load the accordion assets when a placement here is missed.
200 + *
201 + * @since 1.0.0
202 + * @return bool
203 + */
204 + private function has_accordion_shortcode()
205 + {
206 + $load = false;
207 +
208 + if (is_singular()) {
209 + $post = get_post();
210 + if ($post) {
211 + if (!empty($post->post_content) && has_shortcode($post->post_content, 'accordion_metasync')) {
212 + $load = true;
213 + } elseif (!empty($post->post_excerpt) && has_shortcode($post->post_excerpt, 'accordion_metasync')) {
214 + $load = true;
215 + } else {
216 + $elementor_data = get_post_meta($post->ID, '_elementor_data', true);
217 + if (is_string($elementor_data) && $elementor_data !== '' && has_shortcode($elementor_data, 'accordion_metasync')) {
218 + $load = true;
219 + }
220 + }
221 + }
222 + }
223 +
224 + if (!$load) {
225 + foreach (array('widget_text', 'widget_block') as $widget_option) {
226 + $instances = get_option($widget_option);
227 + if (empty($instances) || !is_array($instances)) {
228 + continue;
229 + }
230 +
231 + foreach ($instances as $instance) {
232 + if (!is_array($instance)) {
233 + continue;
234 + }
235 + foreach ($instance as $field_value) {
236 + if (is_string($field_value) && has_shortcode($field_value, 'accordion_metasync')) {
237 + $load = true;
238 + break 3;
239 + }
240 + }
241 + }
242 + }
243 + }
244 +
245 + return apply_filters('metasync_load_accordion_assets', $load);
246 + }
247 +
248 + /**
180 249 * Add defer attribute to OTTO scripts to prevent render blocking
181 250 * This improves PageSpeed scores by allowing scripts to load asynchronously
182 251 *
183 252 * @since 1.0.0
@@ -185,12 +254,23 @@
185 254 * @param string $handle The script handle
186 255 * @return string Modified script tag with defer attribute
187 256 */
188 257 public function add_defer_attribute($tag, $handle) {
258 + # This filter is registered globally and also runs on admin screens, where
259 + # the admin bundle handle ('metasync', js/metasync-admin.js) collides with
260 + # our own plugin_name handle. Deferring the admin head script would run it
261 + # after metasync-dashboard.js (its footer-loaded dependent), inverting the
262 + # dependency order. Bail out on admin requests before the handle check.
263 + if (is_admin()) {
264 + return $tag;
265 + }
266 +
189 267 # Only add defer to our OTTO scripts
190 268 if ($handle === $this->plugin_name . '-tracker' || $handle === $this->plugin_name) {
191 - # Check if defer is not already present
192 - if (strpos($tag, 'defer') === false) {
269 + # Check if a defer attribute is not already present. A plain strpos()
270 + # for 'defer' false-positives on any script URL that merely contains
271 + # the substring (e.g. "...?deferred=1"), so match the attribute itself.
272 + if (!preg_match('/\sdefer(\s|=|>)/i', $tag)) {
193 273 # Add defer attribute before the closing >
194 274 $tag = str_replace(' src', ' defer src', $tag);
195 275 }
196 276 }
@@ -298,9 +378,9 @@
298 378 /**
299 379 * Enqueue custom CSS when Divi Visual Builder is active.
300 380 */
301 381 public function enqueue_divi_builder_css() {
302 - if ( empty( $_GET['et_fb'] ) ) {
382 + if ( empty( $_GET['et_fb'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only builder flag for CSS enqueue
303 383 return;
304 384 }
305 385
306 386 $post_id = get_the_ID();