PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.6.6
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.6.6
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
metasync / admin / class-metasync-post-meta-setting.php

class-metasync-post-meta-setting.php in Search Atlas SEO – OTTO AI SEO Automation for WordPress 2.6.6, at admin/class-metasync-post-meta-setting.php

461 lines 19.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * The header and footer code snippets functionality of the plugin.
5 *
6 *
7 * @link https://searchatlas.com
8 * @since 1.0.0
9 * @package Metasync
10 * @subpackage Metasync/admin
11 * @author Engineering Team <[email protected]>
12 */
13
14 // Abort if this file is accessed directly.
15 if (!defined('ABSPATH')) {
16 exit;
17 }
18
19 class Metasync_Post_Meta_Settings
20 {
21 private $common;
22
23 public function __construct()
24 {
25 $this->common = new Metasync_Common();
26
27 add_action('admin_init', [$this, 'add_post_meta_data'], 2);
28 add_action('save_post', [$this, 'common_robots_meta_box_save']);
29 add_action('save_post', [$this, 'advance_robots_meta_box_save']);
30 add_action('save_post', [$this, 'redirection_meta_box_save']);
31 add_action('save_post', [$this, 'canonical_meta_box_save']);
32 add_action('save_post', [$this, 'video_sitemap_meta_box_save']);
33 }
34
35 public function add_post_meta_data()
36 {
37 // Don't show meta boxes if user's role doesn't have plugin access
38 if (!Metasync::current_user_has_plugin_access()) {
39 return;
40 }
41
42 $plugin_name = Metasync::get_effective_plugin_name();
43 $general_settings = Metasync::get_option('general', []);
44
45 // Only add meta boxes if not disabled in settings
46 if (empty($general_settings['disable_common_robots_metabox'])) {
47 add_meta_box('common-robots-meta', "Common Robots Meta by $plugin_name", [$this, 'common_robots_meta_box_display'], ['page', 'post'], 'normal', 'default');
48 }
49
50 if (empty($general_settings['disable_advance_robots_metabox'])) {
51 add_meta_box('advance-robots-meta', "Advance Robots Meta by $plugin_name", [$this, 'advance_robots_meta_box_display'], ['page', 'post'], 'normal', 'default');
52 }
53
54 if (empty($general_settings['disable_redirection_metabox'])) {
55 add_meta_box('post-redirection-meta', "Redirection by $plugin_name", [$this, 'post_redirection_display'], ['page', 'post'], 'normal', 'default');
56 }
57
58 if (empty($general_settings['disable_canonical_metabox'])) {
59 add_meta_box('post-canonical-meta', "Canonical by $plugin_name", [$this, 'post_canonical_display'], ['page', 'post'], 'normal', 'default');
60 }
61
62 // Video Sitemap meta box — only if video sitemap is enabled
63 $video_settings = get_option('metasync_video_sitemap_settings', []);
64 if (!empty($video_settings['enabled'])) {
65 $video_post_types = !empty($video_settings['post_types']) ? (array) $video_settings['post_types'] : ['post', 'page'];
66 add_meta_box(
67 'metasync-video-sitemap-meta',
68 "Video Sitemap by $plugin_name",
69 [$this, 'video_sitemap_meta_box_display'],
70 $video_post_types,
71 'normal',
72 'default'
73 );
74 }
75 }
76
77 public function common_robots_meta_box_display()
78 {
79 global $post;
80 $post_meta_robots = get_post_meta($post->ID, 'metasync_common_robots', true);
81 // Check new spelling first, fall back to old for backward compatibility
82 $common_meta_robots = Metasync::get_option('common_robots_meta') ?? Metasync::get_option('common_robots_mata') ?? '';
83 $common_robots = $post_meta_robots ? $post_meta_robots : $common_meta_robots;
84 wp_nonce_field('metasync_common_robots_nonce', 'metasync_common_robots_nonce');
85 ?>
86 <ul class="checkbox-list">
87 <li>
88 <input type="checkbox" name="common_robots_meta[index]" id="robots_common1" value="index" <?php isset($common_robots['index']) ? checked('index', $common_robots['index']) : '' ?>>
89 <label for="robots_common1">Index </br>
90 <span class="description">
91 <span>Search engines to index and show these pages in the search results.</span>
92 </span>
93 </label>
94 </li>
95 <li>
96 <input type="checkbox" name="common_robots_meta[noindex]" id="robots_common2" value="noindex" <?php isset($common_robots['noindex']) ? checked('noindex', $common_robots['noindex']) : '' ?>>
97 <label for="robots_common2">No Index </br>
98 <span class="description">
99 <span>Prevents search engines from indexing and displaying these pages in search results.</span>
100 </span>
101 </label>
102 </li>
103 <li>
104 <input type="checkbox" name="common_robots_meta[nofollow]" id="robots_common3" value="nofollow" <?php isset($common_robots['nofollow']) ? checked('nofollow', $common_robots['nofollow']) : '' ?>>
105 <label for="robots_common3">No Follow </br>
106 <span class="description">
107 <span>Prevents search engines from following the links on the pages.</span>
108 </span>
109 </label>
110 </li>
111 <li>
112 <input type="checkbox" name="common_robots_meta[noarchive]" id="robots_common4" value="noarchive" <?php isset($common_robots['noarchive']) ? checked('noarchive', $common_robots['noarchive']) : '' ?>>
113 <label for="robots_common4">No Archive </br>
114 <span class="description">
115 <span>Prevents search engines from showing cached links for pages.</span>
116 </span>
117 </label>
118 </li>
119 <li>
120 <input type="checkbox" name="common_robots_meta[noimageindex]" id="robots_common5" value="noimageindex" <?php isset($common_robots['noimageindex']) ? checked('noimageindex', $common_robots['noimageindex']) : '' ?>>
121 <label for="robots_common5">No Image Index </br>
122 <span class="description">
123 <span>Prevents your pages from appearing as the referring page for images in image search results.</span>
124 </span>
125 </label>
126 </li>
127 <li>
128 <input type="checkbox" name="common_robots_meta[nosnippet]" id="robots_common6" value="nosnippet" <?php isset($common_robots['nosnippet']) ? checked('nosnippet', $common_robots['nosnippet']) : '' ?>>
129 <label for="robots_common6">No Snippet </br>
130 <span class="description">
131 <span>Prevents search engines from showing a snippet in the search results.</span>
132 </span>
133 </label>
134 </li>
135 </ul>
136
137 <?php
138 }
139
140 public function advance_robots_meta_box_display()
141 {
142 global $post;
143 $post_meta_robots = get_post_meta($post->ID, 'metasync_advance_robots', true);
144 // Check new spelling first, fall back to old for backward compatibility
145 $common_meta_robots = Metasync::get_option('advance_robots_meta') ?? Metasync::get_option('advance_robots_mata') ?? '';
146 $advance_robots = $post_meta_robots ? $post_meta_robots : $common_meta_robots;
147 $snippet_advance_robots_enable = $advance_robots['max-snippet']['enable'] ?? '';
148 $snippet_advance_robots_length = $advance_robots['max-snippet']['length'] ?? '';
149 $video_advance_robots_enable = $advance_robots['max-video-preview']['enable'] ?? '';
150 $video_advance_robots_length = $advance_robots['max-video-preview']['length'] ?? '';
151 $image_advance_robots_enable = $advance_robots['max-image-preview']['enable'] ?? '';
152 $image_advance_robots_length = $advance_robots['max-image-preview']['length'] ?? '';
153 wp_nonce_field('metasync_advance_robots_nonce', 'metasync_advance_robots_nonce');
154 ?>
155 <ul class="checkbox-list">
156 <li>
157 <label for="advanced_robots_snippet">
158 <input type="checkbox" name="advanced_robots_meta[max-snippet][enable]" id="advanced_robots_snippet" value="1" <?php checked('1', esc_attr($snippet_advance_robots_enable)) ?>>
159 Snippet </br>
160 <input type="number" class="input-length" name="advanced_robots_meta[max-snippet][length]" id="advanced_robots_snippet_value" value="<?php echo esc_attr($snippet_advance_robots_length); ?>" min="-1"> </br>
161 <span class="description">
162 <span>Add maximum text-length, in characters, of a snippet for your page.</span>
163 </span>
164 </label>
165 </li>
166 <li>
167 <label for="advanced_robots_video">
168 <input type="checkbox" name="advanced_robots_meta[max-video-preview][enable]" id="advanced_robots_video" value="1" <?php checked('1', esc_attr($video_advance_robots_enable)) ?>>
169 Video Preview </br>
170 <input type="number" class="input-length" name="advanced_robots_meta[max-video-preview][length]" id="advanced_robots_video_value" value="<?php echo esc_attr($video_advance_robots_length); ?>" min="-1"> </br>
171 <span class="description">
172 <span>Add maximum duration in seconds of an animated video preview.</span>
173 </span>
174 </label>
175 </li>
176 <li>
177 <label for="advanced_robots_image">
178 <input type="checkbox" name="advanced_robots_meta[max-image-preview][enable]" id="advanced_robots_image" value="1" <?php checked('1', esc_attr($image_advance_robots_enable)) ?>>
179 Image Preview </br>
180 <select class="input-length" name="advanced_robots_meta[max-image-preview][length]' ?>" id="advanced_robots_image_value">
181 <option value="large" <?php selected(esc_attr($image_advance_robots_length), 'large'); ?>>Large</option>
182 <option value="standard" <?php selected(esc_attr($image_advance_robots_length), 'standard'); ?>>Standard</option>
183 <option value="none" <?php selected(esc_attr($image_advance_robots_length), 'none'); ?>>None</option>
184 </select>
185 </br>
186 <span class="description">
187 <span>Add maximum size of image preview to show the images on this page.</span>
188 </span>
189 </label>
190 </li>
191 </ul>
192 <?php
193 }
194
195 public function post_redirection_display()
196 {
197 global $post;
198 $post_redirection = get_post_meta($post->ID, 'metasync_post_redirection_meta', true) ?? '';
199 $enable = $post_redirection['enable'] ?? '';
200 $type = $post_redirection['type'] ?? '';
201 $url = $post_redirection['url'] ?? '';
202 wp_nonce_field('metasync_post_redirection_nonce', 'metasync_post_redirection_nonce');
203 ?>
204 <ul class="checkbox-list">
205 <li>
206 <input type="checkbox" name="post_redirect_meta[enable]" id="post_redirection" value="true" <?php checked('true', esc_attr($enable)); ?>>
207 <label for="post_redirection">Redirection</label>
208 </li>
209 <li class="hide"> Redirection Type:
210 <select class="regular-text" name="post_redirect_meta[type]" id="post_redirection_type">
211 <option value="301" <?php selected(esc_attr($type), '301'); ?>>301 Permanent Move</option>
212 <option value="302" <?php selected(esc_attr($type), '302'); ?>>302 Temporary Move</option>
213 <option value="307" <?php selected(esc_attr($type), '307'); ?>>307 Temporary Redirect</option>
214 <option value="410" <?php selected(esc_attr($type), '410'); ?>>410 Content Deleted</option>
215 <option value="451" <?php selected(esc_attr($type), '451'); ?>>451 Content Unavailable</option>
216 </select>
217 </li>
218 <li class="hide" id="post_redirect_url"> Destination URL:
219 <input type="text" class="regular-text" name="post_redirect_meta[url]" id="post_redirect_url_val" value="<?php echo esc_attr($url); ?>">
220 </li>
221 </ul>
222 <?php
223 }
224
225 public function post_canonical_display()
226 {
227 global $post;
228
229 $post_canonical = get_post_meta($post->ID, 'meta_canonical', true) ?? '';
230 // Fix legacy array values stored by sanitize_array()
231 if (is_array($post_canonical)) {
232 $post_canonical = reset($post_canonical) ?: '';
233 // Repair the stored value so it won't recur
234 if (!empty($post_canonical)) {
235 update_post_meta($post->ID, 'meta_canonical', (string) $post_canonical);
236 }
237 }
238 wp_nonce_field('metasync_post_canonical_nonce', 'metasync_post_canonical_nonce');
239 ?>
240 <ul>
241 <li> Canonical URL:
242 <input type="text" class="regular-text" name="post_canonical_url_meta" placeholder="<?php echo get_permalink($post->ID) ?>" value="<?php echo esc_attr($post_canonical); ?>">
243 </li>
244 </ul>
245 <?php
246 }
247
248 public function common_robots_meta_box_save($post_id)
249 {
250 if (!current_user_can('edit_post', $post_id))
251 return;
252
253 // WP-197: When saving from Gutenberg (REST API context) and the sidebar
254 // JSON exists, skip — the sidebar auto-save is the source of truth.
255 // Classic editor form submits (non-REST) always proceed so classic-only
256 // users can still save via the meta boxes.
257 if (defined('REST_REQUEST') && REST_REQUEST && !empty(get_post_meta($post_id, '_metasync_robots_advanced', true))) {
258 return;
259 }
260
261 $post_data = metasync_sanitize_input_array($_POST);
262 // Check for new field name first, then old for backward compatibility
263 $field_name = isset($post_data['common_robots_meta']) ? 'common_robots_meta' : 'common_robots_mata';
264
265 if (!isset($post_data['metasync_common_robots_nonce'], $post_data[$field_name]) || !wp_verify_nonce($post_data['metasync_common_robots_nonce'], 'metasync_common_robots_nonce'))
266 return;
267
268 $old_common_robots = get_post_meta($post_id, 'metasync_common_robots', true);
269
270 $common_robots = [];
271 if (!empty($post_data[$field_name])) {
272 $common_robots = $this->common->sanitize_array($post_data[$field_name]);
273 }
274
275 if (!empty($common_robots))
276 update_post_meta($post_id, 'metasync_common_robots', $common_robots);
277 elseif (empty($common_robots) && $old_common_robots)
278 delete_post_meta($post_id, 'metasync_common_robots', $old_common_robots);
279 }
280
281 public function advance_robots_meta_box_save($post_id)
282 {
283 if (!current_user_can('edit_post', $post_id))
284 return;
285
286 // WP-197: When saving from Gutenberg (REST API context) and the sidebar
287 // JSON exists, skip — the sidebar auto-save is the source of truth.
288 if (defined('REST_REQUEST') && REST_REQUEST && !empty(get_post_meta($post_id, '_metasync_robots_advanced', true))) {
289 return;
290 }
291
292 $post_data = metasync_sanitize_input_array($_POST);
293 // Check for new field name first, then old for backward compatibility
294 $field_name = isset($post_data['advanced_robots_meta']) ? 'advanced_robots_meta' : 'advanced_robots_mata';
295
296 if (!isset($post_data['metasync_advance_robots_nonce'], $post_data[$field_name]) || !wp_verify_nonce($post_data['metasync_advance_robots_nonce'], 'metasync_advance_robots_nonce'))
297 return;
298
299 $old_advance_robots = get_post_meta($post_id, 'metasync_advance_robots', true);
300
301 $advance_robots = $this->common->sanitize_array($post_data[$field_name]);
302
303 if (!empty($advance_robots))
304 update_post_meta($post_id, 'metasync_advance_robots', $advance_robots);
305 elseif (empty($advance_robots) && $old_advance_robots)
306 delete_post_meta($post_id, 'metasync_advance_robots', $old_advance_robots);
307 }
308
309 public function redirection_meta_box_save($post_id)
310 {
311 if (!current_user_can('edit_post', $post_id))
312 return;
313
314 $post_data = metasync_sanitize_input_array($_POST);
315 // Check for new field name first, then old for backward compatibility
316 $field_name = isset($post_data['post_redirect_meta']) ? 'post_redirect_meta' : 'post_redirect_mata';
317
318 if (!isset($post_data['metasync_post_redirection_nonce'], $post_data[$field_name]) || !wp_verify_nonce($post_data['metasync_post_redirection_nonce'], 'metasync_post_redirection_nonce'))
319 return;
320
321 $old_post_redirection_meta = get_post_meta($post_id, 'metasync_post_redirection_meta', true);
322
323 $post_redirection_meta = $this->common->sanitize_array($post_data[$field_name]);
324
325 if (isset($post_redirection_meta['enable']))
326 update_post_meta($post_id, 'metasync_post_redirection_meta', $post_redirection_meta);
327 else
328 delete_post_meta($post_id, 'metasync_post_redirection_meta', $old_post_redirection_meta);
329 }
330
331 public function canonical_meta_box_save($post_id)
332 {
333 if (!current_user_can('edit_post', $post_id))
334 return;
335
336 $post_data = metasync_sanitize_input_array($_POST);
337 // Check for new field name first, then old for backward compatibility
338 $field_name = isset($post_data['post_canonical_url_meta']) ? 'post_canonical_url_meta' : 'post_canonical_url_mata';
339
340 if (!isset($post_data['metasync_post_canonical_nonce'], $post_data[$field_name]) || !wp_verify_nonce($post_data['metasync_post_canonical_nonce'], 'metasync_post_canonical_nonce'))
341 return;
342
343 $old_post_canonical_meta = get_post_meta($post_id, 'meta_canonical', true);
344
345 // Canonical is a URL string — sanitize as URL, not array
346 $raw_value = $post_data[$field_name];
347 if (is_array($raw_value)) {
348 $raw_value = reset($raw_value); // extract first element if array
349 }
350 $post_canonical_meta = esc_url_raw(trim((string) $raw_value));
351
352 if (!empty($post_canonical_meta))
353 update_post_meta($post_id, 'meta_canonical', $post_canonical_meta);
354 else
355 delete_post_meta($post_id, 'meta_canonical', $old_post_canonical_meta);
356 }
357
358 public function show_top_admin_bar() {
359 if ( Metasync::current_user_has_plugin_access() ) {
360 show_admin_bar( true );
361 }
362 }
363
364 /**
365 * Display the Video Sitemap meta box in post editor.
366 */
367 public function video_sitemap_meta_box_display()
368 {
369 global $post;
370 $video_url = get_post_meta($post->ID, '_metasync_video_url', true);
371 $video_thumbnail = get_post_meta($post->ID, '_metasync_video_thumbnail', true);
372 $video_title = get_post_meta($post->ID, '_metasync_video_title', true);
373 $video_desc = get_post_meta($post->ID, '_metasync_video_description', true);
374 $video_duration = get_post_meta($post->ID, '_metasync_video_duration', true);
375
376 wp_nonce_field('metasync_video_sitemap_meta_nonce', 'metasync_video_sitemap_meta_nonce');
377 ?>
378 <p style="color: #666; margin-bottom: 12px;">
379 <?php esc_html_e('Override auto-detected video data for this post. Leave fields empty to use auto-detection.', 'metasync'); ?>
380 </p>
381 <table class="form-table" style="margin: 0;">
382 <tr>
383 <th scope="row"><label for="metasync_video_url"><?php esc_html_e('Video URL', 'metasync'); ?></label></th>
384 <td><input type="url" id="metasync_video_url" name="metasync_video_url" value="<?php echo esc_attr($video_url); ?>" class="large-text" placeholder="https://www.youtube.com/watch?v=..." /></td>
385 </tr>
386 <tr>
387 <th scope="row"><label for="metasync_video_thumbnail"><?php esc_html_e('Thumbnail URL', 'metasync'); ?></label></th>
388 <td><input type="url" id="metasync_video_thumbnail" name="metasync_video_thumbnail" value="<?php echo esc_attr($video_thumbnail); ?>" class="large-text" placeholder="https://img.youtube.com/vi/.../hqdefault.jpg" /></td>
389 </tr>
390 <tr>
391 <th scope="row"><label for="metasync_video_title"><?php esc_html_e('Video Title', 'metasync'); ?></label></th>
392 <td><input type="text" id="metasync_video_title" name="metasync_video_title" value="<?php echo esc_attr($video_title); ?>" class="large-text" placeholder="<?php esc_attr_e('Defaults to post title', 'metasync'); ?>" /></td>
393 </tr>
394 <tr>
395 <th scope="row"><label for="metasync_video_description"><?php esc_html_e('Video Description', 'metasync'); ?></label></th>
396 <td><textarea id="metasync_video_description" name="metasync_video_description" class="large-text" rows="3" placeholder="<?php esc_attr_e('Defaults to post excerpt', 'metasync'); ?>"><?php echo esc_textarea($video_desc); ?></textarea></td>
397 </tr>
398 <tr>
399 <th scope="row"><label for="metasync_video_duration"><?php esc_html_e('Duration (seconds)', 'metasync'); ?></label></th>
400 <td><input type="number" id="metasync_video_duration" name="metasync_video_duration" value="<?php echo esc_attr($video_duration); ?>" class="small-text" min="0" step="1" placeholder="300" /></td>
401 </tr>
402 </table>
403 <?php
404 }
405
406 /**
407 * Save the Video Sitemap meta box data.
408 *
409 * @param int $post_id The post ID.
410 */
411 public function video_sitemap_meta_box_save($post_id)
412 {
413 if (!isset($_POST['metasync_video_sitemap_meta_nonce']) ||
414 !wp_verify_nonce($_POST['metasync_video_sitemap_meta_nonce'], 'metasync_video_sitemap_meta_nonce')) {
415 return;
416 }
417
418 if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
419 return;
420 }
421
422 if (!current_user_can('edit_post', $post_id)) {
423 return;
424 }
425
426 $fields = [
427 'metasync_video_url' => '_metasync_video_url',
428 'metasync_video_thumbnail' => '_metasync_video_thumbnail',
429 'metasync_video_title' => '_metasync_video_title',
430 'metasync_video_description' => '_metasync_video_description',
431 'metasync_video_duration' => '_metasync_video_duration',
432 ];
433
434 foreach ($fields as $form_key => $meta_key) {
435 if (!isset($_POST[$form_key])) {
436 continue;
437 }
438
439 $value = $_POST[$form_key];
440
441 // Sanitize per field type
442 if (in_array($meta_key, ['_metasync_video_url', '_metasync_video_thumbnail'], true)) {
443 $value = esc_url_raw($value);
444 } elseif ($meta_key === '_metasync_video_duration') {
445 $value = absint($value);
446 $value = $value > 0 ? $value : '';
447 } elseif ($meta_key === '_metasync_video_description') {
448 $value = sanitize_textarea_field($value);
449 } else {
450 $value = sanitize_text_field($value);
451 }
452
453 if (!empty($value)) {
454 update_post_meta($post_id, $meta_key, $value);
455 } else {
456 delete_post_meta($post_id, $meta_key);
457 }
458 }
459 }
460 }
461