PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.6.10
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.6.10
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 2.6.4 2.6.3 2.5.23 All 138 releases
metasync / public / class-metasync-public.php

class-metasync-public.php in Search Atlas SEO – OTTO AI SEO Automation for WordPress 2.6.10, at public/class-metasync-public.php

352 lines 9.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 // If this file is called directly, abort.
3 if (!defined('ABSPATH')) {
4 exit;
5 }
6
7
8 /**
9 * The public-facing functionality of the plugin.
10 *
11 * Thin coordinator that instantiates Metasync_Rest_Api and Metasync_Seo_Output
12 * and retains only asset enqueue methods, plugin links, and shortcode init.
13 *
14 * @link https://searchatlas.com
15 * @since 1.0.0
16 *
17 * @package Metasync
18 * @subpackage Metasync/public
19 */
20
21 /**
22 * The public-facing functionality of the plugin.
23 *
24 * Defines the plugin name, version, and two examples hooks for how to
25 * enqueue the public-facing stylesheet and JavaScript.
26 *
27 * @package Metasync
28 * @subpackage Metasync/public
29 * @author Engineering Team <support@searchatlas.com>
30 */
31
32 class Metasync_Public
33 {
34
35 /**
36 * The ID of this plugin.
37 *
38 * @since 1.0.0
39 * @access private
40 * @var string $plugin_name The ID of this plugin.
41 */
42 private $plugin_name;
43
44 /**
45 * The version of this plugin.
46 *
47 * @since 1.0.0
48 * @access private
49 * @var string $version The current version of this plugin.
50 */
51 private $version;
52
53 /**
54 * REST API handler instance.
55 *
56 * @since 1.0.0
57 * @access private
58 * @var Metasync_Rest_Api $rest_api
59 */
60 private $rest_api;
61
62 /**
63 * SEO Output handler instance.
64 *
65 * @since 1.0.0
66 * @access private
67 * @var Metasync_Seo_Output $seo_output
68 */
69 private $seo_output;
70
71 /**
72 * Initialize the class and set its properties.
73 *
74 * @since 1.0.0
75 * @param string $plugin_name The name of the plugin.
76 * @param string $version The version of this plugin.
77 */
78 public function __construct($plugin_name, $version)
79 {
80 $this->plugin_name = $plugin_name;
81 $this->version = $version;
82
83 $this->rest_api = new Metasync_Rest_Api($plugin_name, $version);
84 $this->seo_output = new Metasync_Seo_Output($plugin_name, $version);
85 }
86
87 /**
88 * Get the REST API handler instance.
89 *
90 * @return Metasync_Rest_Api
91 */
92 public function get_rest_api()
93 {
94 return $this->rest_api;
95 }
96
97 /**
98 * Get the SEO Output handler instance.
99 *
100 * @return Metasync_Seo_Output
101 */
102 public function get_seo_output()
103 {
104 return $this->seo_output;
105 }
106
107 /**
108 * Register the stylesheets for the public-facing side of the site.
109 *
110 * @since 1.0.0
111 */
112 public function enqueue_styles()
113 {
114
115 /**
116 * This function is provided for demonstration purposes only.
117 *
118 * An instance of this class should be passed to the run() function
119 * defined in Metasync_Loader as all of the hooks are defined
120 * in that particular class.
121 *
122 * The Metasync_Loader will then create the relationship
123 * between the defined hooks and the functions defined in this
124 * class.
125 */
126 $enabled_plugin_css = Metasync::get_option('general')['enabled_plugin_css'] ?? '';
127 if($enabled_plugin_css!=="default" && $enabled_plugin_css!=='' ){
128 wp_enqueue_style($this->plugin_name, plugin_dir_url(__FILE__) . 'css/metasync-public.css', array(), $this->version, 'all');
129 }
130 }
131
132 /**
133 * Register the JavaScript for the public-facing side of the site.
134 *
135 * @since 1.0.0
136 */
137 public function enqueue_scripts()
138 {
139
140 /**
141 * This function is provided for demonstration purposes only.
142 *
143 * An instance of this class should be passed to the run() function
144 * defined in Metasync_Loader as all of the hooks are defined
145 * in that particular class.
146 *
147 * The Metasync_Loader will then create the relationship
148 * between the defined hooks and the functions defined in this
149 * class.
150 */
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);
156
157
158 # Get the Otto Pixel UUID from plugin settings.
159 $otto_uuid = Metasync::get_option('general')['otto_pixel_uuid'] ?? '';
160
161 # Get the current full page URL safely.
162 $page_url = esc_url(home_url(add_query_arg([], $_SERVER['REQUEST_URI'])));
163
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)) {
167 wp_localize_script($this->plugin_name . '-tracker', 'saOttoData', [
168 'otto_uuid' => $otto_uuid,
169 'page_url' => $page_url,
170 'context' => null,
171 'enable_metadesc' => true, // Always enabled by default
172 ]);
173 }
174
175 # Add defer attribute to OTTO scripts for performance optimization
176 add_filter('script_loader_tag', array($this, 'add_defer_attribute'), 10, 2);
177 }
178
179 /**
180 * Add defer attribute to OTTO scripts to prevent render blocking
181 * This improves PageSpeed scores by allowing scripts to load asynchronously
182 *
183 * @since 1.0.0
184 * @param string $tag The script tag
185 * @param string $handle The script handle
186 * @return string Modified script tag with defer attribute
187 */
188 public function add_defer_attribute($tag, $handle) {
189 # Only add defer to our OTTO scripts
190 if ($handle === $this->plugin_name . '-tracker' || $handle === $this->plugin_name) {
191 # Check if defer is not already present
192 if (strpos($tag, 'defer') === false) {
193 # Add defer attribute before the closing >
194 $tag = str_replace(' src', ' defer src', $tag);
195 }
196 }
197 return $tag;
198 }
199
200 public function metasync_plugin_init()
201 {
202 $this->rest_api->rest_authorization_middleware();
203 $this->shortcodes_init();
204 }
205
206 public function shortcodes_init()
207 {
208 # add_shortcode('accordion', 'metasync_accordion');
209
210 # name changed from accordion to accordion_metasync to avoid conflict with other plugins
211 add_shortcode('accordion_metasync', 'metasync_accordion');
212 // add_shortcode('markdown', 'metasync_markdown');
213
214
215 function metasync_accordion($atts, $content = "")
216 {
217 // SECURITY FIX: Properly escape shortcode attributes
218 $title = isset($atts['title']) ? esc_html($atts['title']) : '';
219 $safe_content = wp_kses_post($content);
220
221 $block = "<div class=\"metasync-accordion-block\">
222 <button class=\"metasync-accordion\">{$title}</button>
223 <div class=\"metasync-panel\">{$safe_content}</div>
224 </div>";
225 return $block;
226 }
227 }
228
229 public function metasync_plugin_links($links)
230 {
231 # Removed Sync link as requested
232
233 # Solving Issue 103
234 # get the neneral options into a var
235 $general_options = Metasync::get_option('general');
236
237 # set the menu slug to the default
238 $menu_slug = 'searchatlas';
239
240 #now check if the menu slug is in the general opts
241 if (is_array($general_options) && !empty($general_options['white_label_plugin_menu_slug'])) {
242
243 #set the slug to the modified
244 $menu_slug = $general_options['white_label_plugin_menu_slug'];
245 }
246
247 $links[] = '<a href="' . get_admin_url(null, 'admin.php?page=' .$menu_slug) . '">' . esc_html__('Settings', 'metasync') . '</a>';
248 return $links;
249 }
250
251 /**
252 * Enqueue custom CSS stored in post meta for converted pages.
253 * Runs at priority 999 (after theme CSS) so custom styles win.
254 */
255 public function enqueue_page_custom_css() {
256 if ( ! is_singular() ) {
257 return;
258 }
259
260 $post_id = get_the_ID();
261 if ( ! $post_id ) {
262 return;
263 }
264
265 $custom_css = get_post_meta( $post_id, '_metasync_custom_css', true );
266 if ( empty( $custom_css ) ) {
267 return;
268 }
269
270 $scoped_css = $this->scope_css_to_body_class( $custom_css, $post_id );
271
272 wp_register_style( 'metasync-page-css-' . $post_id, false );
273 wp_enqueue_style( 'metasync-page-css-' . $post_id );
274 wp_add_inline_style( 'metasync-page-css-' . $post_id, $scoped_css );
275 }
276
277 /**
278 * Enqueue custom CSS into Elementor editor preview iframe.
279 */
280 public function enqueue_elementor_editor_css() {
281 $post_id = get_the_ID();
282 if ( ! $post_id ) {
283 return;
284 }
285
286 $custom_css = get_post_meta( $post_id, '_metasync_custom_css', true );
287 if ( empty( $custom_css ) ) {
288 return;
289 }
290
291 $scoped_css = $this->scope_css_to_body_class( $custom_css, $post_id );
292
293 wp_register_style( 'metasync-elementor-css-' . $post_id, false );
294 wp_enqueue_style( 'metasync-elementor-css-' . $post_id );
295 wp_add_inline_style( 'metasync-elementor-css-' . $post_id, $scoped_css );
296 }
297
298 /**
299 * Enqueue custom CSS when Divi Visual Builder is active.
300 */
301 public function enqueue_divi_builder_css() {
302 if ( empty( $_GET['et_fb'] ) ) {
303 return;
304 }
305
306 $post_id = get_the_ID();
307 if ( ! $post_id ) {
308 return;
309 }
310
311 $custom_css = get_post_meta( $post_id, '_metasync_custom_css', true );
312 if ( empty( $custom_css ) ) {
313 return;
314 }
315
316 $scoped_css = $this->scope_css_to_body_class( $custom_css, $post_id );
317
318 wp_register_style( 'metasync-divi-css-' . $post_id, false );
319 wp_enqueue_style( 'metasync-divi-css-' . $post_id );
320 wp_add_inline_style( 'metasync-divi-css-' . $post_id, $scoped_css );
321 }
322
323 /**
324 * Scope all CSS selectors to body.postid-{ID} for higher specificity.
325 *
326 * @param string $css Raw CSS.
327 * @param int $post_id Post ID.
328 * @return string Scoped CSS.
329 */
330 private function scope_css_to_body_class( $css, $post_id ) {
331 $prefix = 'body.postid-' . intval( $post_id );
332 $scoped = '';
333 $pattern = '/([^{]+)\{([^}]*)\}/s';
334
335 preg_match_all( $pattern, $css, $matches, PREG_SET_ORDER );
336
337 foreach ( $matches as $match ) {
338 $selectors = array_map( 'trim', explode( ',', $match[1] ) );
339 $scoped_selectors = array_map( function( $sel ) use ( $prefix ) {
340 if ( empty( $sel ) || strpos( $sel, '@' ) === 0 ) {
341 return $sel;
342 }
343 return $prefix . ' ' . $sel;
344 }, $selectors );
345 $scoped .= implode( ', ', $scoped_selectors ) . ' {' . $match[2] . "}\n";
346 }
347
348 return $scoped ?: $css;
349 }
350
351 }
352