PluginProbe
Search Atlas SEO – OTTO AI SEO Automation for WordPress / 2.5.23
Search Atlas SEO – OTTO AI SEO Automation for WordPress v2.5.23
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 / includes / class-metasync-settings-fields.php

class-metasync-settings-fields.php in Search Atlas SEO – OTTO AI SEO Automation for WordPress 2.5.23, at includes/class-metasync-settings-fields.php

2,388 lines 122.8 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 * Settings field callbacks and accordion/section helpers extracted from Metasync_Admin.
9 *
10 * @package Metasync
11 * @subpackage Metasync/includes
12 */
13 class Metasync_Settings_Fields {
14
15 private static $instance = null;
16
17 /**
18 * Reference to the admin instance for callbacks to non-extracted admin methods.
19 *
20 * @var Metasync_Admin|null
21 */
22 private $admin_instance = null;
23
24 private function __construct() {}
25
26 /**
27 * @return self
28 */
29 public static function instance() {
30 if (null === self::$instance) {
31 self::$instance = new self();
32 }
33 return self::$instance;
34 }
35
36 /**
37 * Inject the admin instance so callbacks to non-extracted methods still work.
38 *
39 * @param Metasync_Admin $admin
40 */
41 public function set_admin_instance($admin) {
42 $this->admin_instance = $admin;
43 }
44
45 // ────────────────────────────────────────────────────────────────
46 // Accordion / Section helpers
47 // ────────────────────────────────────────────────────────────────
48
49 public function get_accordion_sections_config() {
50 return array(
51 'connection' => array(
52 'title' => 'Connection & Authentication',
53 'description' => 'Manage your API connection and authentication settings',
54 'icon' => '🔐',
55 'priority' => 10,
56 'default_open' => true,
57 'fields' => array(
58 'searchatlas_api_key',
59 'apikey'
60 )
61 ),
62 'otto_ssr' => array(
63 'title' => Metasync::get_whitelabel_otto_name() . ' Server-Side Rendering',
64 'description' => 'Configure ' . Metasync::get_whitelabel_otto_name() . ' rendering and display options',
65 'icon' => '🚀',
66 'priority' => 20,
67 'default_open' => false,
68 'fields' => array(
69 'otto_pixel_uuid',
70 'otto_disable_on_loggedin',
71 'otto_disable_preview_button',
72 'otto_wp_rocket_compat',
73 )
74 ),
75 'edge_cache' => array(
76 'title' => 'Edge Cache / CDN',
77 'description' => 'Configure CDN credentials for cache-tag purging',
78 'icon' => '🌐',
79 'priority' => 22,
80 'default_open' => false,
81 'render_callback' => array('Metasync_Edge_Cache_Settings', 'render'),
82 ),
83 'bot_detection' => array(
84 'title' => 'Bot Detection & Filtering',
85 'description' => 'Manage bot traffic and reduce unnecessary API calls',
86 'icon' => '🤖',
87 'priority' => 25,
88 'default_open' => false,
89 'fields' => array(
90 'otto_disable_for_bots',
91 'otto_bot_whitelist',
92 'otto_bot_blacklist',
93 'otto_bot_statistics_link'
94 )
95 ),
96 'editor_settings' => array(
97 'title' => 'Post/Page Editor Settings',
98 'description' => 'Customize meta boxes and editor functionality',
99 'icon' => '✏️',
100 'priority' => 40,
101 'default_open' => false,
102 'fields' => array(
103 'disable_common_robots_metabox',
104 'disable_advance_robots_metabox',
105 'disable_redirection_metabox',
106 'disable_canonical_metabox',
107 'disable_social_opengraph_metabox',
108 'disable_schema_markup_metabox',
109 'open_external_links'
110 )
111 ),
112 'user_management' => array(
113 'title' => 'User Management for Content',
114 'description' => 'Configure which users are allowed to be authors of content synced.',
115 'icon' => '👥',
116 'priority' => 50,
117 'default_open' => false,
118 'fields' => array(
119 'content_genius_sync_roles'
120 )
121 ),
122 'advanced' => array(
123 'title' => 'Plugin Settings',
124 'description' => 'System configuration and maintenance options',
125 'icon' => '⚙️',
126 'priority' => 60,
127 'default_open' => false,
128 'fields' => array(
129 'permalink_structure',
130 'hide_dashboard_framework',
131 'show_admin_bar_status',
132 'enable_auto_updates',
133 'import_external_data'
134 )
135 )
136 );
137 }
138
139 public function get_advanced_accordion_config() {
140 $config = array();
141
142 $user = wp_get_current_user();
143 if (in_array('administrator', (array) $user->roles)) {
144 $config['plugin_access'] = array(
145 'title' => 'User Roles with Plugin Access',
146 'description' => 'Control which user roles can see and access this plugin',
147 'icon' => '🔐',
148 'priority' => 5,
149 'default_open' => false,
150 'render_callback' => array($this, 'render_plugin_access_roles_section')
151 );
152 }
153
154 $config['debug_mode'] = array(
155 'title' => 'Debug Mode',
156 'description' => 'Manage debug mode with automatic disable and safety limits',
157 'icon' => '🐛',
158 'priority' => 8,
159 'default_open' => false,
160 'render_callback' => array($this->admin_instance, 'render_debug_mode_section')
161 );
162
163 $config['error_logs'] = array(
164 'title' => 'Error Logs',
165 'description' => 'View and manage error logs to troubleshoot issues',
166 'icon' => '⚠️',
167 'priority' => 10,
168 'default_open' => true,
169 'render_callback' => array($this->admin_instance, 'render_error_log_content')
170 );
171
172 $config['execution_settings'] = array(
173 'title' => 'Execution Settings',
174 'description' => 'Configure resource limits and execution parameters',
175 'icon' => '',
176 'priority' => 15,
177 'default_open' => false,
178 'render_callback' => array($this, 'render_execution_settings_section')
179 );
180
181 $config['otto_cache'] = array(
182 'title' => 'Cache Management',
183 'description' => 'Manage ' . Metasync::get_whitelabel_otto_name() . ' cache and clear all cache plugins',
184 'icon' => '🗄️',
185 'priority' => 20,
186 'default_open' => false,
187 'render_callback' => array($this->admin_instance, 'render_otto_cache_management')
188 );
189
190 $config['reset_settings'] = array(
191 'title' => 'Reset Plugin Settings',
192 'description' => 'Reset all plugin settings to default values',
193 'icon' => '🔄',
194 'priority' => 30,
195 'default_open' => false,
196 'render_callback' => array($this, 'render_reset_settings_section')
197 );
198
199 return $config;
200 }
201
202 public function render_advanced_accordion() {
203 $sections_config = $this->get_advanced_accordion_config();
204
205 uasort($sections_config, function($a, $b) {
206 return $a['priority'] - $b['priority'];
207 });
208
209 echo '<div class="metasync-settings-accordion">';
210
211 foreach ($sections_config as $section_key => $section_data) {
212 $section_id = 'metasync-advanced-section-' . $section_key;
213 $is_open = $section_data['default_open'];
214 $aria_expanded = $is_open ? 'true' : 'false';
215 $content_state = $is_open ? 'open' : 'closed';
216
217 echo '<div class="metasync-accordion-section" data-section="' . esc_attr($section_key) . '">';
218
219 echo '<div class="metasync-accordion-header" role="button" tabindex="0" aria-expanded="' . $aria_expanded . '" aria-controls="' . $section_id . '">';
220 echo '<div class="metasync-accordion-title">';
221 echo '<span class="metasync-accordion-icon">' . esc_html($section_data['icon']) . '</span>';
222 echo '<div class="metasync-accordion-text">';
223 echo '<h3>' . esc_html($section_data['title']) . '</h3>';
224 echo '<p class="metasync-accordion-description">' . esc_html($section_data['description']) . '</p>';
225 echo '</div>';
226 echo '</div>';
227 echo '<button type="button" class="metasync-accordion-toggle" aria-label="Toggle section">';
228 echo '<span class="toggle-icon">▼</span>';
229 echo '</button>';
230 echo '</div>';
231
232 echo '<div class="metasync-accordion-content" id="' . $section_id . '" data-state="' . $content_state . '">';
233
234 if (isset($section_data['render_callback']) && is_callable($section_data['render_callback'])) {
235 call_user_func($section_data['render_callback']);
236 }
237
238 echo '</div>';
239 echo '</div>';
240 }
241
242 echo '</div>';
243 }
244
245 public function render_reset_settings_section() {
246 ?>
247 <div style="background: var(--dashboard-card-bg); padding: 20px; border-radius: 8px;">
248 <div style="background: rgba(255, 243, 205, 0.1); border: 1px solid rgba(255, 234, 167, 0.3); border-radius: 8px; padding: 20px; margin-bottom: 20px;">
249 <h4 style="color: var(--dashboard-warning, #f59e0b); margin: 0 0 12px 0; display: flex; align-items: center; gap: 8px;">
250 <span>⚠️</span>
251 <span>Important Warning</span>
252 </h4>
253 <p style="color: var(--dashboard-text-secondary); margin: 0 0 12px 0;">This action will permanently delete:</p>
254 <ul style="color: var(--dashboard-text-secondary); margin: 0 0 12px 20px; line-height: 1.8;">
255 <li>All API keys and authentication tokens</li>
256 <li>White label branding settings</li>
257 <li>Plugin configuration and preferences</li>
258 <li>Instant indexing settings</li>
259 <li>All cached data and crawl information</li>
260 </ul>
261 <p style="color: var(--dashboard-warning, #f59e0b); margin: 0; font-weight: 600;">You will need to reconfigure the plugin completely after this reset.</p>
262 </div>
263 <form method="post" action="" onsubmit="return confirmClearSettings(event)">
264 <?php wp_nonce_field('metasync_clear_all_settings_nonce', 'clear_all_settings_nonce'); ?>
265 <input type="hidden" name="clear_all_settings" value="yes" />
266 <button type="submit" class="metasync-btn-danger" style="background: var(--dashboard-error, #ef4444); color: #ffffff; border: none; padding: 10px 20px; border-radius: 8px; font-weight: 500; cursor: pointer; transition: all 0.3s ease; box-shadow: 0 2px 4px rgba(239, 68, 68, 0.2);" onmouseover="this.style.background='#dc2626'; this.style.transform='translateY(-2px)'; this.style.boxShadow='0 4px 8px rgba(239, 68, 68, 0.3)';" onmouseout="this.style.background='var(--dashboard-error, #ef4444)'; this.style.transform='translateY(0)'; this.style.boxShadow='0 2px 4px rgba(239, 68, 68, 0.2)';">
267 🗑 Clear All Settings
268 </button>
269 </form>
270 </div>
271 <script>
272 function confirmClearSettings(event) {
273 event.preventDefault();
274
275 var firstConfirm = confirm("⚠️ WARNING: This will permanently delete ALL plugin settings!\n\nThis action cannot be undone. Are you sure you want to continue?");
276 if (!firstConfirm) {
277 return false;
278 }
279
280 var secondConfirm = confirm("🚨 FINAL WARNING 🚨\n\nThis will delete:\n• All API keys and authentication tokens\n• White label branding settings\n• Plugin configuration and preferences\n• Instant indexing settings\n• All cached data\n\nYou will need to reconfigure the entire plugin from scratch.\n\nType 'DELETE' in the next prompt to confirm.");
281 if (!secondConfirm) {
282 return false;
283 }
284
285 var typeConfirm = prompt("Type 'DELETE' (in capital letters) to confirm you want to permanently clear all settings:");
286 if (typeConfirm !== 'DELETE') {
287 alert("Settings reset cancelled. Type 'DELETE' exactly to confirm.");
288 return false;
289 }
290
291 event.target.submit();
292 return false;
293 }
294 </script>
295 <?php
296 }
297
298 public function render_google_index_section() {
299 if (!function_exists('google_index_direct')) {
300 require_once plugin_dir_path(dirname(__FILE__)) . 'google-index/google-index-init.php';
301 }
302
303 $google_index = google_index_direct();
304 $service_info = $google_index->get_service_account_info();
305 $is_configured = !isset($service_info['error']);
306
307 include plugin_dir_path(dirname(__FILE__)) . 'views/metasync-google-index-api-settings.php';
308 }
309
310 public function render_bing_index_section() {
311 require_once plugin_dir_path(dirname(__FILE__)) . 'bing-index/class-metasync-bing-instant-index.php';
312 $bing_index = new Metasync_Bing_Instant_Index();
313 $api_key = $bing_index->get_setting('api_key');
314 $endpoint = $bing_index->get_setting('endpoint', 'indexnow');
315 $post_types_settings = $bing_index->get_setting('post_types', []);
316 $is_configured = !empty($api_key);
317
318 $post_types = get_post_types(['public' => true], 'objects');
319
320 ?>
321 <div style="padding: 20px;">
322 <!-- About IndexNow (Collapsible) -->
323 <details style="margin-bottom: 20px;">
324 <summary style="cursor: pointer; font-weight: 600; color: var(--dashboard-text-primary);">
325 ℹ️ About IndexNow Protocol
326 </summary>
327 <div style="margin-top: 10px; padding: 15px; background: rgba(255, 255, 255, 0.03); border-radius: 4px;">
328 <p style="color: var(--dashboard-text-secondary); margin: 0 0 10px 0;">
329 IndexNow is a simple protocol that allows websites to instantly notify search engines about URL changes.
330 It's supported by Bing, Yandex, Naver, Seznam, and other search engines.
331 </p>
332 <ul style="color: var(--dashboard-text-secondary); margin: 0 0 10px 20px;">
333 <li>✓ Instant notification to multiple search engines</li>
334 <li>✓ Simple API key authentication (no OAuth required)</li>
335 <li>✓ Supports batch URL submissions (up to 10,000 URLs)</li>
336 <li>✓ Free to use with no quotas</li>
337 <li>✓ Fire-and-forget protocol (no status checking needed)</li>
338 </ul>
339 <p style="color: var(--dashboard-text-secondary); margin: 0;">
340 <strong>Resources:</strong>
341 <a href="https://www.indexnow.org/documentation" target="_blank" style="color: var(--dashboard-accent);">IndexNow Documentation</a> |
342 <a href="https://www.bing.com/webmasters" target="_blank" style="color: var(--dashboard-accent);">Bing Webmaster Tools</a>
343 </p>
344 </div>
345 </details>
346
347 <!-- Configuration Status -->
348 <div style="margin-bottom: 20px;">
349 <?php if ($is_configured): ?>
350 <div style="padding: 12px; background: rgba(76, 175, 80, 0.1); border-left: 4px solid #4caf50; border-radius: 4px;">
351 <p style="margin: 0; color: var(--dashboard-text-primary);">
352 <strong style="color: #4caf50;">�
353 IndexNow API Configured</strong><br>
354 <span style="color: var(--dashboard-text-secondary); font-size: 0.95em;">
355 API Key: <code style="padding: 2px 6px; background: rgba(0,0,0,0.1); border-radius: 3px;"><?php echo esc_html(substr($api_key, 0, 8) . '...' . substr($api_key, -8)); ?></code>
356 </span>
357 </p>
358 </div>
359 <?php else: ?>
360 <div style="padding: 12px; background: rgba(255, 152, 0, 0.1); border-left: 4px solid #ff9800; border-radius: 4px;">
361 <p style="margin: 0; color: var(--dashboard-text-primary);">
362 <strong style="color: #ff9800;">⚠️ Configuration Required</strong><br>
363 <span style="color: var(--dashboard-text-secondary); font-size: 0.95em;">
364 Configure your IndexNow API key below to enable instant indexing with Bing and other search engines.
365 </span>
366 </p>
367 </div>
368 <?php endif; ?>
369 </div>
370
371 <!-- Settings Fields -->
372 <table class="form-table" style="margin-top: 0;">
373 <tr>
374 <th scope="row" style="width: 200px; padding-top: 15px;">
375 <label for="metasync_bing_api_key_inline">IndexNow API Key <span style="color: #d63638;">*</span></label>
376 </th>
377 <td style="padding-top: 15px;">
378 <input type="text"
379 name="metasync_bing_api_key_inline"
380 id="metasync_bing_api_key_inline"
381 class="large-text"
382 value="<?php echo esc_attr($api_key); ?>"
383 placeholder="Enter your IndexNow API key (32+ character hexadecimal string)" />
384 <br>
385 <button type="button"
386 id="generate-bing-api-key-inline"
387 class="button button-secondary"
388 style="margin-top: 8px;">
389 🔑 Generate Random API Key
390 </button>
391 <p class="description" style="margin-top: 8px;">
392 Your IndexNow API key is required to submit URLs for instant indexing. You can generate a random key or use your own (32+ character hexadecimal string).
393 After saving, a verification file will be automatically created at your site root.
394 </p>
395 </td>
396 </tr>
397
398 <tr>
399 <th scope="row" style="padding-top: 15px;">
400 <label>API Endpoint</label>
401 </th>
402 <td style="padding-top: 15px;">
403 <fieldset>
404 <label style="display: block; margin-bottom: 8px;">
405 <input type="radio"
406 name="metasync_bing_endpoint_inline"
407 value="indexnow"
408 <?php checked($endpoint, 'indexnow'); ?>>
409 <strong>IndexNow.org</strong> (Recommended)
410 <span style="color: var(--dashboard-text-secondary); font-size: 0.9em; display: block; margin-left: 24px;">
411 Notifies Bing, Yandex, Naver, Seznam, and other participating search engines
412 </span>
413 </label>
414 <label style="display: block;">
415 <input type="radio"
416 name="metasync_bing_endpoint_inline"
417 value="bing"
418 <?php checked($endpoint, 'bing'); ?>>
419 <strong>Bing.com</strong> (Bing-specific)
420 <span style="color: var(--dashboard-text-secondary); font-size: 0.9em; display: block; margin-left: 24px;">
421 Direct submission to Bing only
422 </span>
423 </label>
424 </fieldset>
425 <p class="description">
426 Select which endpoint to use for submitting URLs. The IndexNow.org endpoint is recommended as it notifies multiple search engines at once.
427 </p>
428 </td>
429 </tr>
430
431 <tr>
432 <th scope="row" style="padding-top: 15px;">
433 <label>Auto-Submit Post Types</label>
434 </th>
435 <td style="padding-top: 15px;">
436 <fieldset>
437 <?php foreach ($post_types as $post_type): ?>
438 <label style="display: inline-block; margin-right: 20px; margin-bottom: 8px;">
439 <input type="checkbox"
440 name="metasync_bing_post_types_inline[<?php echo esc_attr($post_type->name); ?>]"
441 value="<?php echo esc_attr($post_type->name); ?>"
442 <?php checked(in_array($post_type->name, $post_types_settings, true)); ?>>
443 <?php echo esc_html($post_type->label); ?>
444 </label>
445 <?php endforeach; ?>
446 </fieldset>
447 <p class="description">
448 Selected post types will be automatically submitted to IndexNow when published or updated.
449 </p>
450 </td>
451 </tr>
452
453 <tr>
454 <th scope="row" style="padding-top: 15px;">
455 <label>Plugin Source Control</label>
456 </th>
457 <td style="padding-top: 15px;">
458 <fieldset>
459 <label style="display: flex; align-items: flex-start; gap: 8px;">
460 <input type="checkbox"
461 name="metasync_bing_disable_other_plugins_inline"
462 value="1"
463 <?php checked($bing_index->get_setting('disable_other_plugins', true), true); ?>
464 style="margin-top: 2px;">
465 <span>
466 <strong>Disable other IndexNow plugins</strong>
467 <span style="display: block; color: var(--dashboard-text-secondary); font-size: 0.9em; margin-top: 4px; font-weight: normal;">
468 When enabled, Our plugin will be the exclusive source for IndexNow submissions in Bing Webmaster Tools.
469 This disables IndexNow features in Yoast SEO, Rank Math, and other competing plugins to prevent duplicate submissions.
470 </span>
471 </span>
472 </label>
473 </fieldset>
474 </td>
475 </tr>
476 </table>
477
478 </div>
479
480 <script>
481 jQuery(document).ready(function($) {
482 $('#generate-bing-api-key-inline').on('click', function() {
483 const array = new Uint8Array(16);
484 crypto.getRandomValues(array);
485 const hexString = Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
486 $('#metasync_bing_api_key_inline').val(hexString);
487
488 $('#enable_binginstantindex').prop('checked', true);
489
490 $('#metasync_bing_api_key_inline').trigger('change');
491 });
492
493 $('#metasync_bing_api_key_inline').on('input', function() {
494 const apiKey = $(this).val().trim();
495 if (apiKey.length >= 8) {
496 $('#enable_binginstantindex').prop('checked', true);
497 }
498 });
499
500 function toggleBingConfig() {
501 const isEnabled = $('#enable_binginstantindex').is(':checked');
502 const $apiConfig = $('#enable_binginstantindex').closest('tr').nextAll('tr').first();
503 if (isEnabled) {
504 $apiConfig.show();
505 } else {
506 $apiConfig.hide();
507 }
508 }
509
510 toggleBingConfig();
511
512 $('#enable_binginstantindex').on('change', toggleBingConfig);
513 });
514 </script>
515
516 <style>
517 .form-table th {
518 color: var(--dashboard-text-primary);
519 }
520
521 .form-table td {
522 color: var(--dashboard-text-secondary);
523 }
524
525 .form-table code {
526 padding: 2px 6px;
527 background: rgba(0, 0, 0, 0.1);
528 border-radius: 3px;
529 font-size: 0.9em;
530 }
531
532 details summary {
533 transition: color 0.2s;
534 }
535
536 details summary:hover {
537 color: var(--dashboard-accent);
538 }
539
540 details[open] summary {
541 margin-bottom: 10px;
542 }
543 </style>
544 <?php
545 }
546
547 public function render_plugin_access_roles_section() {
548 $general_options = Metasync::get_option('general');
549
550 $setting_exists = isset($general_options['plugin_access_roles']);
551 $selected_roles = $general_options['plugin_access_roles'] ?? null;
552
553 if (is_string($selected_roles)) {
554 $selected_roles = array($selected_roles);
555 } elseif (!is_array($selected_roles)) {
556 $selected_roles = null;
557 }
558
559 if (!function_exists('wp_roles')) {
560 require_once(ABSPATH . 'wp-admin/includes/user.php');
561 }
562 global $wp_roles;
563
564 if (!isset($wp_roles) || !is_object($wp_roles)) {
565 $wp_roles = new WP_Roles();
566 }
567
568 $all_roles = $wp_roles->roles;
569
570 $all_roles_checked = is_array($selected_roles) && in_array('all', $selected_roles);
571 ?>
572 <div style="background: var(--dashboard-card-bg); padding: 20px; border-radius: 8px;">
573 <p style="color: var(--dashboard-text-secondary); margin: 0 0 20px 0;">
574 Select which user roles can see and access this plugin's menu, settings, and options in the WordPress admin area.
575 </p>
576
577 <form method="post" action="<?php echo admin_url('admin.php?page=' . Metasync_Admin::$page_slug . '&tab=advanced'); ?>" id="plugin-access-roles-form">
578 <?php wp_nonce_field('metasync_plugin_access_roles_nonce', 'plugin_access_roles_nonce'); ?>
579 <input type="hidden" name="save_plugin_access_roles" value="yes" />
580
581 <div class="metasync-role-selector-container" style="background: var(--dashboard-card-bg-alt, rgba(255,255,255,0.05)); border: 1px solid var(--dashboard-border); border-radius: 8px; padding: 16px; max-height: 300px; overflow-y: auto;">
582
583 <!-- All Roles Option -->
584 <label class="metasync-role-option-all" style="display: flex; align-items: center; gap: 10px; padding: 12px; cursor: pointer; border-radius: 6px; transition: background 0.2s ease;">
585 <input type="checkbox"
586 id="plugin-access-all-roles"
587 name="plugin_access_roles[]"
588 value="all"
589 style="width: 18px; height: 18px; cursor: pointer;"
590 <?php checked($all_roles_checked, true); ?> />
591 <strong style="color: var(--dashboard-text-primary);">All Roles</strong>
592 </label>
593
594 <!-- Divider -->
595 <hr style="border: none; border-top: 1px solid var(--dashboard-border); margin: 12px 0;">
596
597 <?php
598 if (!empty($all_roles) && is_array($all_roles)) {
599 foreach ($all_roles as $role_key => $role_details) {
600 if ($role_key === 'administrator') {
601 continue;
602 }
603
604 if (!isset($role_details['name'])) {
605 continue;
606 }
607
608 $is_checked = is_array($selected_roles) && in_array($role_key, $selected_roles);
609 $role_name = translate_user_role($role_details['name']);
610 ?>
611 <label class="metasync-role-option" style="display: flex; align-items: center; gap: 10px; padding: 10px 12px; cursor: pointer; border-radius: 6px; transition: background 0.2s ease;">
612 <input type="checkbox"
613 class="plugin-access-individual-role"
614 name="plugin_access_roles[]"
615 value="<?php echo esc_attr($role_key); ?>"
616 style="width: 18px; height: 18px; cursor: pointer;"
617 <?php checked($is_checked, true); ?> />
618 <span style="color: var(--dashboard-text-primary);"><?php echo esc_html($role_name); ?></span>
619 </label>
620 <?php
621 }
622 }
623 ?>
624
625 </div>
626
627 <!-- Description -->
628 <div style="background: rgba(59, 130, 246, 0.1); border: 1px solid rgba(59, 130, 246, 0.3); border-radius: 8px; padding: 16px; margin: 20px 0;">
629 <p style="color: var(--dashboard-text-secondary); margin: 0; font-size: 13px;">
630 <strong style="color: var(--dashboard-info, #3b82f6);">ℹ️ How it works:</strong><br>
631 <strong>Administrators always have access</strong> to this plugin regardless of settings.<br>
632 <strong>By default</strong>, only Administrators can access the plugin (no roles selected).<br>
633 If <strong>"All Roles"</strong> is selected, all users will see the plugin.<br>
634 Users with unchecked roles will not see the plugin in their WordPress admin area.
635 </p>
636 </div>
637
638 <button type="submit" class="metasync-btn-primary" style="background: var(--dashboard-gradient-primary); color: #ffffff; border: none; padding: 12px 24px; border-radius: 8px; font-weight: 500; cursor: pointer; transition: all 0.3s ease; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);" onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 4px 8px rgba(0, 0, 0, 0.15)';" onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='0 2px 4px rgba(0, 0, 0, 0.1)';">
639 💾 Save Access Settings
640 </button>
641 </form>
642 </div>
643 <script>
644 jQuery(document).ready(function($) {
645 var $allRolesCheckbox = $('#plugin-access-all-roles');
646 var $individualRoles = $('.plugin-access-individual-role');
647
648 $allRolesCheckbox.on('change', function() {
649 if ($(this).is(':checked')) {
650 $individualRoles.prop('checked', false);
651 }
652 });
653
654 $individualRoles.on('change', function() {
655 if ($(this).is(':checked')) {
656 $allRolesCheckbox.prop('checked', false);
657 }
658 });
659 });
660 </script>
661 <?php
662 }
663
664 public function get_default_execution_settings() {
665 return array(
666 'max_execution_time' => 30,
667 'max_memory_limit' => 256,
668 'log_batch_size' => 1000,
669 'action_scheduler_batches' => 1,
670 'otto_rate_limit' => 10,
671 'queue_cleanup_days' => 31
672 );
673 }
674
675 public function get_execution_setting($key, $default = null) {
676 $settings = get_option('metasync_execution_settings', array());
677 $defaults = $this->get_default_execution_settings();
678
679 if (empty($settings)) {
680 return isset($defaults[$key]) ? $defaults[$key] : $default;
681 }
682
683 return isset($settings[$key]) ? $settings[$key] : (isset($defaults[$key]) ? $defaults[$key] : $default);
684 }
685
686 public function get_all_execution_settings() {
687 $settings = get_option('metasync_execution_settings', array());
688 $defaults = $this->get_default_execution_settings();
689
690 return wp_parse_args($settings, $defaults);
691 }
692
693 public function can_change_memory_limit() {
694 $current_limit = ini_get('memory_limit');
695
696 $test_result = @ini_set('memory_limit', $current_limit);
697
698 return $test_result !== false;
699 }
700
701 public function get_server_limits() {
702 $max_execution_time = ini_get('max_execution_time');
703 $memory_limit = ini_get('memory_limit');
704
705 $memory_limit_mb = $this->parse_memory_limit_to_mb($memory_limit);
706
707 $wp_memory_limit = null;
708 if (defined('WP_MAX_MEMORY_LIMIT')) {
709 $wp_memory_limit = WP_MAX_MEMORY_LIMIT;
710 } elseif (defined('WP_MEMORY_LIMIT')) {
711 $wp_memory_limit = WP_MEMORY_LIMIT;
712 }
713 $wp_memory_limit_mb = $wp_memory_limit ? $this->parse_memory_limit_to_mb($wp_memory_limit) : null;
714
715 $actual_php_limit_mb = $memory_limit_mb;
716 if ($wp_memory_limit_mb && $wp_memory_limit_mb >= 256) {
717 $actual_php_limit_mb = 128;
718 }
719
720 $can_change_memory = $this->can_change_memory_limit();
721
722 $memory_limit_display = $actual_php_limit_mb == -1 ? 'Unlimited' : $actual_php_limit_mb . ' MB';
723
724 return array(
725 'max_execution_time' => $max_execution_time == -1 ? 'Unlimited' : $max_execution_time . ' seconds',
726 'memory_limit' => $memory_limit_display,
727 'max_execution_time_raw' => $max_execution_time,
728 'memory_limit_raw' => $actual_php_limit_mb,
729 'can_change_memory' => $can_change_memory
730 );
731 }
732
733 public function apply_memory_limit() {
734 if (!$this->can_change_memory_limit()) {
735 return false;
736 }
737
738 $memory_limit_mb = $this->get_execution_setting('max_memory_limit');
739
740 $result = @ini_set('memory_limit', $memory_limit_mb . 'M');
741
742 return $result !== false;
743 }
744
745 public function parse_memory_limit_to_mb($memory_limit) {
746 if ($memory_limit == -1 || $memory_limit == '-1') {
747 return -1;
748 }
749
750 $memory_limit = trim($memory_limit);
751 $last = strtolower(substr($memory_limit, -1));
752 $value = (int) $memory_limit;
753
754 switch ($last) {
755 case 'g':
756 $value *= 1024;
757 break;
758 case 'm':
759 break;
760 case 'k':
761 $value /= 1024;
762 break;
763 }
764
765 return $value;
766 }
767
768 public function render_execution_settings_section() {
769 $settings = $this->get_all_execution_settings();
770 $server_limits = $this->get_server_limits();
771
772 $warnings = array();
773 if ($server_limits['max_execution_time_raw'] != -1 && $settings['max_execution_time'] > $server_limits['max_execution_time_raw']) {
774 $warnings['max_execution_time'] = true;
775 }
776 if ($server_limits['memory_limit_raw'] != -1 && $settings['max_memory_limit'] > $server_limits['memory_limit_raw']) {
777 $warnings['max_memory_limit'] = true;
778 }
779 ?>
780 <div style="background: var(--dashboard-card-bg); padding: 20px; border-radius: 8px;">
781 <p style="color: var(--dashboard-text-secondary); margin: 0 0 20px 0;">
782 Configure resource limits and execution parameters for plugin operations. Adjust these settings based on your server capabilities.
783 </p>
784
785 <form id="metasync-execution-settings-form" method="post">
786 <?php wp_nonce_field('metasync_execution_settings_nonce', 'execution_settings_nonce'); ?>
787
788 <!-- Execution & Memory Section -->
789 <div style="background: var(--dashboard-card-bg-alt, rgba(255,255,255,0.05)); border: 1px solid var(--dashboard-border); border-radius: 8px; padding: 20px; margin-bottom: 20px;">
790 <h3 style="color: var(--dashboard-text-primary); margin: 0 0 16px 0; font-size: 16px; font-weight: 600;">Execution & Memory</h3>
791
792 <div style="margin-bottom: 20px;">
793 <label style="display: block; color: var(--dashboard-text-primary); margin-bottom: 8px; font-weight: 500;">
794 Max Execution Time:
795 </label>
796 <div style="display: flex; align-items: center; gap: 10px;">
797 <input type="number"
798 id="max_execution_time"
799 name="max_execution_time"
800 value="<?php echo esc_attr($settings['max_execution_time']); ?>"
801 min="1"
802 max="300"
803 required
804 style="width: 100px; padding: 8px; border: 1px solid var(--dashboard-border); border-radius: 6px; background: var(--dashboard-card-bg); color: var(--dashboard-text-primary);" />
805 <span style="color: var(--dashboard-text-secondary);">seconds</span>
806 </div>
807 <p style="color: var(--dashboard-text-secondary); font-size: 12px; margin: 4px 0 0 0;">
808 Server Limit: <?php echo esc_html($server_limits['max_execution_time']); ?>
809 </p>
810 <p id="max_execution_time_warning" style="display: none; color: #f59e0b; font-size: 12px; margin: 4px 0 0 0;">
811 <span style="margin-right: 4px;">⚠️</span>
812 <span>Configured value exceeds server limit</span>
813 </p>
814 <?php if (isset($warnings['max_execution_time'])): ?>
815 <script>
816 jQuery(document).ready(function($) {
817 $('#max_execution_time_warning').show();
818 });
819 </script>
820 <?php endif; ?>
821 </div>
822
823 <div>
824 <label style="display: block; color: var(--dashboard-text-primary); margin-bottom: 8px; font-weight: 500;">
825 Max Memory Limit:
826 </label>
827 <div style="display: flex; align-items: center; gap: 10px;">
828 <input type="number"
829 id="max_memory_limit"
830 name="max_memory_limit"
831 value="<?php echo esc_attr($settings['max_memory_limit']); ?>"
832 min="64"
833 max="512"
834 <?php if (!$server_limits['can_change_memory']): ?>
835 readonly
836 disabled
837 <?php endif; ?>
838 required
839 style="width: 100px; padding: 8px; border: 1px solid var(--dashboard-border); border-radius: 6px; background: <?php echo $server_limits['can_change_memory'] ? 'var(--dashboard-card-bg)' : 'rgba(128, 128, 128, 0.1)'; ?>; color: <?php echo $server_limits['can_change_memory'] ? 'var(--dashboard-text-primary)' : 'var(--dashboard-text-secondary)'; ?>; cursor: <?php echo $server_limits['can_change_memory'] ? 'text' : 'not-allowed'; ?>;" />
840 <span style="color: var(--dashboard-text-secondary);">MB</span>
841 </div>
842 <p style="color: var(--dashboard-text-secondary); font-size: 12px; margin: 4px 0 0 0;">
843 Server Limit: <?php echo esc_html($server_limits['memory_limit']); ?>
844 </p>
845 <?php if (!$server_limits['can_change_memory']): ?>
846 <p style="color: #f59e0b; font-size: 12px; margin: 4px 0 0 0; display: flex; align-items: center; gap: 4px;">
847 <span>🔒</span>
848 <span>Server does not allow changing memory limit. This setting is read-only.</span>
849 </p>
850 <?php else: ?>
851 <p id="max_memory_limit_warning" style="display: none; color: #f59e0b; font-size: 12px; margin: 4px 0 0 0;">
852 <span style="margin-right: 4px;">⚠️</span>
853 <span>Configured value exceeds server limit</span>
854 </p>
855 <?php if (isset($warnings['max_memory_limit'])): ?>
856 <script>
857 jQuery(document).ready(function($) {
858 $('#max_memory_limit_warning').show();
859 });
860 </script>
861 <?php endif; ?>
862 <?php endif; ?>
863 </div>
864 </div>
865
866 <!-- Batch Processing Section -->
867 <div style="background: var(--dashboard-card-bg-alt, rgba(255,255,255,0.05)); border: 1px solid var(--dashboard-border); border-radius: 8px; padding: 20px; margin-bottom: 20px;">
868 <h3 style="color: var(--dashboard-text-primary); margin: 0 0 16px 0; font-size: 16px; font-weight: 600;">Batch Processing</h3>
869
870 <div style="margin-bottom: 20px;">
871 <label style="display: block; color: var(--dashboard-text-primary); margin-bottom: 8px; font-weight: 500;">
872 Log Processing Batch Size:
873 </label>
874 <div style="display: flex; align-items: center; gap: 10px;">
875 <input type="number"
876 id="log_batch_size"
877 name="log_batch_size"
878 value="<?php echo esc_attr($settings['log_batch_size']); ?>"
879 min="100"
880 max="5000"
881 required
882 style="width: 100px; padding: 8px; border: 1px solid var(--dashboard-border); border-radius: 6px; background: var(--dashboard-card-bg); color: var(--dashboard-text-primary);" />
883 <span style="color: var(--dashboard-text-secondary);">lines</span>
884 </div>
885 </div>
886
887 <div>
888 <label style="display: block; color: var(--dashboard-text-primary); margin-bottom: 8px; font-weight: 500;">
889 Action Scheduler Concurrent Batches:
890 </label>
891 <div style="display: flex; align-items: center; gap: 10px;">
892 <input type="number"
893 id="action_scheduler_batches"
894 name="action_scheduler_batches"
895 value="<?php echo esc_attr($settings['action_scheduler_batches']); ?>"
896 min="1"
897 max="10"
898 required
899 style="width: 100px; padding: 8px; border: 1px solid var(--dashboard-border); border-radius: 6px; background: var(--dashboard-card-bg); color: var(--dashboard-text-primary);" />
900 </div>
901 <p style="color: #f59e0b; font-size: 12px; margin: 4px 0 0 0; display: flex; align-items: center; gap: 4px;">
902 <span>⚠️</span>
903 <span>Higher values increase server load</span>
904 </p>
905 </div>
906 </div>
907
908 <!-- API Rate Limiting Section -->
909 <div style="background: var(--dashboard-card-bg-alt, rgba(255,255,255,0.05)); border: 1px solid var(--dashboard-border); border-radius: 8px; padding: 20px; margin-bottom: 20px;">
910 <h3 style="color: var(--dashboard-text-primary); margin: 0 0 16px 0; font-size: 16px; font-weight: 600;">API Rate Limiting</h3>
911
912 <div style="margin-bottom: 20px;">
913 <label style="display: block; color: var(--dashboard-text-primary); margin-bottom: 8px; font-weight: 500;">
914 OTTO API Calls Per Minute:
915 </label>
916 <div style="display: flex; align-items: center; gap: 10px;">
917 <input type="number"
918 id="otto_rate_limit"
919 name="otto_rate_limit"
920 value="<?php echo esc_attr($settings['otto_rate_limit']); ?>"
921 min="1"
922 max="60"
923 required
924 style="width: 100px; padding: 8px; border: 1px solid var(--dashboard-border); border-radius: 6px; background: var(--dashboard-card-bg); color: var(--dashboard-text-primary);" />
925 </div>
926 </div>
927
928 <div>
929 <label style="display: block; color: var(--dashboard-text-primary); margin-bottom: 8px; font-weight: 500;">
930 Queue Auto-Cleanup:
931 </label>
932 <div style="display: flex; align-items: center; gap: 10px;">
933 <input type="number"
934 id="queue_cleanup_days"
935 name="queue_cleanup_days"
936 value="<?php echo esc_attr($settings['queue_cleanup_days']); ?>"
937 min="7"
938 max="90"
939 required
940 style="width: 100px; padding: 8px; border: 1px solid var(--dashboard-border); border-radius: 6px; background: var(--dashboard-card-bg); color: var(--dashboard-text-primary);" />
941 <span style="color: var(--dashboard-text-secondary);">days</span>
942 </div>
943 </div>
944 </div>
945
946 <!-- Save Button -->
947 <div style="margin-top: 20px;">
948 <button type="submit"
949 id="metasync-execution-settings-save-btn"
950 class="button button-primary"
951 style="padding: 10px 20px; font-size: 14px; font-weight: 500;">
952 <span class="save-text">Save Settings</span>
953 <span class="save-spinner" style="display: none; margin-left: 8px;"></span>
954 </button>
955 </div>
956
957 <!-- Success/Error Messages -->
958 <div id="metasync-execution-settings-message" style="display: none; margin-top: 16px; padding: 12px; border-radius: 6px;"></div>
959 </form>
960 </div>
961
962 <script>
963 jQuery(document).ready(function($) {
964 var $form = $('#metasync-execution-settings-form');
965 var $saveBtn = $('#metasync-execution-settings-save-btn');
966 var $message = $('#metasync-execution-settings-message');
967
968 var serverMaxExecTime = <?php echo $server_limits['max_execution_time_raw'] == -1 ? 'Infinity' : $server_limits['max_execution_time_raw']; ?>;
969 var serverMaxMemory = <?php echo $server_limits['memory_limit_raw'] == -1 ? 'Infinity' : $server_limits['memory_limit_raw']; ?>;
970 var canChangeMemory = <?php echo $server_limits['can_change_memory'] ? 'true' : 'false'; ?>;
971
972 function checkServerLimits() {
973 var maxExecTime = parseInt($('#max_execution_time').val()) || 0;
974 var maxMemory = parseInt($('#max_memory_limit').val()) || 0;
975
976 if (serverMaxExecTime !== Infinity && maxExecTime > serverMaxExecTime) {
977 $('#max_execution_time_warning').show();
978 } else {
979 $('#max_execution_time_warning').hide();
980 }
981
982 if (canChangeMemory && serverMaxMemory !== Infinity && maxMemory > serverMaxMemory) {
983 $('#max_memory_limit_warning').show();
984 } else {
985 $('#max_memory_limit_warning').hide();
986 }
987 }
988
989 $('#max_execution_time, #max_memory_limit').on('input change', function() {
990 checkServerLimits();
991 $(this).css('border-color', 'var(--dashboard-border)');
992 });
993
994 function highlightInvalidField($field, isValid) {
995 if (isValid) {
996 $field.css({
997 'border-color': 'var(--dashboard-border)',
998 'box-shadow': 'none'
999 });
1000 } else {
1001 $field.css({
1002 'border-color': '#ef4444',
1003 'box-shadow': '0 0 0 3px rgba(239, 68, 68, 0.1)'
1004 });
1005 }
1006 }
1007
1008 $('#max_execution_time').on('blur', function() {
1009 var value = parseInt($(this).val()) || 0;
1010 var isValid = value >= 1 && value <= 300 && (serverMaxExecTime === Infinity || value <= serverMaxExecTime);
1011 highlightInvalidField($(this), isValid);
1012 });
1013
1014 $('#max_memory_limit').on('blur', function() {
1015 if (!canChangeMemory) return;
1016 var value = parseInt($(this).val()) || 0;
1017 var isValid = value >= 64 && value <= 512 && (serverMaxMemory === Infinity || value <= serverMaxMemory);
1018 highlightInvalidField($(this), isValid);
1019 });
1020
1021 checkServerLimits();
1022
1023 $form.on('submit', function(e) {
1024 e.preventDefault();
1025
1026 var formData = {
1027 action: 'metasync_save_execution_settings',
1028 execution_settings_nonce: $('#execution_settings_nonce').val(),
1029 max_execution_time: $('#max_execution_time').val(),
1030 max_memory_limit: $('#max_memory_limit').val(),
1031 log_batch_size: $('#log_batch_size').val(),
1032 action_scheduler_batches: $('#action_scheduler_batches').val(),
1033 otto_rate_limit: $('#otto_rate_limit').val(),
1034 queue_cleanup_days: $('#queue_cleanup_days').val()
1035 };
1036
1037 $('input[type="number"]').css({
1038 'border-color': 'var(--dashboard-border)',
1039 'box-shadow': 'none'
1040 });
1041
1042 var hasError = false;
1043 var errorField = null;
1044
1045 if (formData.max_execution_time < 1 || formData.max_execution_time > 300) {
1046 showMessage('Max Execution Time must be between 1 and 300 seconds.', 'error');
1047 highlightInvalidField($('#max_execution_time'), false);
1048 errorField = $('#max_execution_time');
1049 hasError = true;
1050 } else if (serverMaxExecTime !== Infinity && formData.max_execution_time > serverMaxExecTime) {
1051 showMessage('Max Execution Time exceeds server limit of ' + serverMaxExecTime + ' seconds. Please reduce the value.', 'error');
1052 highlightInvalidField($('#max_execution_time'), false);
1053 errorField = $('#max_execution_time');
1054 hasError = true;
1055 }
1056
1057 if (canChangeMemory) {
1058 if (formData.max_memory_limit < 64 || formData.max_memory_limit > 512) {
1059 showMessage('Max Memory Limit must be between 64 and 512 MB.', 'error');
1060 highlightInvalidField($('#max_memory_limit'), false);
1061 if (!hasError) {
1062 errorField = $('#max_memory_limit');
1063 hasError = true;
1064 }
1065 } else if (serverMaxMemory !== Infinity && formData.max_memory_limit > serverMaxMemory) {
1066 showMessage('Max Memory Limit exceeds server limit of ' + serverMaxMemory + ' MB. Please reduce the value.', 'error');
1067 highlightInvalidField($('#max_memory_limit'), false);
1068 if (!hasError) {
1069 errorField = $('#max_memory_limit');
1070 hasError = true;
1071 }
1072 }
1073 }
1074
1075 if (formData.log_batch_size < 100 || formData.log_batch_size > 5000) {
1076 showMessage('Log Batch Size must be between 100 and 5000 lines.', 'error');
1077 highlightInvalidField($('#log_batch_size'), false);
1078 if (!hasError) {
1079 errorField = $('#log_batch_size');
1080 hasError = true;
1081 }
1082 }
1083 if (formData.action_scheduler_batches < 1 || formData.action_scheduler_batches > 10) {
1084 showMessage('Action Scheduler Batches must be between 1 and 10.', 'error');
1085 highlightInvalidField($('#action_scheduler_batches'), false);
1086 if (!hasError) {
1087 errorField = $('#action_scheduler_batches');
1088 hasError = true;
1089 }
1090 }
1091 if (formData.otto_rate_limit < 1 || formData.otto_rate_limit > 60) {
1092 showMessage('OTTO Rate Limit must be between 1 and 60 calls per minute.', 'error');
1093 highlightInvalidField($('#otto_rate_limit'), false);
1094 if (!hasError) {
1095 errorField = $('#otto_rate_limit');
1096 hasError = true;
1097 }
1098 }
1099 if (formData.queue_cleanup_days < 7 || formData.queue_cleanup_days > 90) {
1100 showMessage('Queue Cleanup Days must be between 7 and 90 days.', 'error');
1101 highlightInvalidField($('#queue_cleanup_days'), false);
1102 if (!hasError) {
1103 errorField = $('#queue_cleanup_days');
1104 hasError = true;
1105 }
1106 }
1107
1108 if (hasError) {
1109 if (errorField) {
1110 errorField.focus();
1111 $('html, body').animate({
1112 scrollTop: errorField.offset().top - 100
1113 }, 300);
1114 }
1115 return;
1116 }
1117
1118 $saveBtn.prop('disabled', true);
1119 $saveBtn.find('.save-text').text('Saving...');
1120 $saveBtn.find('.save-spinner').show();
1121 $message.hide();
1122
1123 $.ajax({
1124 url: ajaxurl,
1125 type: 'POST',
1126 data: formData,
1127 success: function(response) {
1128 if (response.success) {
1129 showMessage(response.data.message || 'Settings saved successfully!', 'success');
1130 $saveBtn.prop('disabled', false);
1131 $saveBtn.find('.save-text').text('Save Settings');
1132 $saveBtn.find('.save-spinner').hide();
1133 setTimeout(function() {
1134 checkServerLimits();
1135 }, 100);
1136 $('html, body').animate({
1137 scrollTop: $form.offset().top - 100
1138 }, 300);
1139 } else {
1140 showMessage(response.data.message || 'Error saving settings.', 'error');
1141 $saveBtn.prop('disabled', false);
1142 $saveBtn.find('.save-text').text('Save Settings');
1143 $saveBtn.find('.save-spinner').hide();
1144 $('html, body').animate({
1145 scrollTop: $message.offset().top - 100
1146 }, 300);
1147 }
1148 },
1149 error: function() {
1150 showMessage('An error occurred while saving settings. Please try again.', 'error');
1151 $saveBtn.prop('disabled', false);
1152 $saveBtn.find('.save-text').text('Save Settings');
1153 $saveBtn.find('.save-spinner').hide();
1154 $('html, body').animate({
1155 scrollTop: $message.offset().top - 100
1156 }, 300);
1157 }
1158 });
1159 });
1160
1161 function showMessage(text, type) {
1162 $message.removeClass('notice-success notice-error')
1163 .addClass('notice-' + type)
1164 .css({
1165 'background': type === 'success' ? 'rgba(34, 197, 94, 0.1)' : 'rgba(239, 68, 68, 0.1)',
1166 'border': '1px solid ' + (type === 'success' ? 'rgba(34, 197, 94, 0.3)' : 'rgba(239, 68, 68, 0.3)'),
1167 'color': type === 'success' ? '#22c55e' : '#ef4444',
1168 'padding': '12px 16px',
1169 'border-radius': '6px',
1170 'font-size': '14px',
1171 'line-height': '1.5',
1172 'display': 'block'
1173 })
1174 .html('<strong style="margin-right: 8px;">' + (type === 'success' ? '' : '') + '</strong>' + text)
1175 .show();
1176
1177 if (type === 'success') {
1178 setTimeout(function() {
1179 $message.fadeOut(300);
1180 }, 5000);
1181 }
1182 }
1183 });
1184 </script>
1185 <?php
1186 }
1187
1188 public function get_field_tooltips() {
1189 $plugin_name = Metasync::get_effective_plugin_name();
1190 $otto_name = Metasync::get_whitelabel_otto_name();
1191
1192 return array(
1193 'searchatlas_api_key' => sprintf('Connect your WordPress site to your %s dashboard to retrieve your Search Atlas API key and Otto UUID. This does not create a WordPress login session.', $plugin_name),
1194 'apikey' => sprintf('Auto-generated authentication token used for secure API communication between your WordPress site and %s services. You can refresh this token if needed for security purposes.', $plugin_name),
1195 'otto_pixel_uuid' => sprintf('Your unique %s tracking pixel identifier. This UUID is used to track %s modifications and analytics on your website pages.', $otto_name, $otto_name),
1196 'otto_disable_on_loggedin' => sprintf('Disable %s modifications when you are logged in to WordPress. This allows you to see and edit the original content without %s\'s enhancements during editing sessions.', $otto_name, $otto_name),
1197 'otto_disable_preview_button' => sprintf('Hide the %s frontend toolbar that displays the status indicator, preview button, and debug button. Enable this for a cleaner frontend experience.', $otto_name),
1198 'otto_wp_rocket_compat' => sprintf('WP Rocket Compatibility Mode: Controls how %s interacts with WP Rocket. "Auto" (recommended) allows both to work together by avoiding DONOTCACHEPAGE constant unless necessary for Brizy pages or SG Optimizer conflicts. This ensures WP Rocket\'s JavaScript delay and optimization features continue working.', $otto_name),
1199 'otto_disable_for_bots' => sprintf('Enable bot detection to automatically skip %s processing for search engine crawlers, SEO tools, and other bots. This reduces unnecessary API calls and improves performance.', $otto_name),
1200 'otto_bot_whitelist' => sprintf('Enter bot names or user-agent patterns (one per line) that should always be processed by %s, even when "Disable for Bots" is enabled. For example: Googlebot, Bingbot. This allows you to ensure specific search engines always see your optimized content.', $otto_name),
1201 'otto_bot_blacklist' => sprintf('Enter bot names or user-agent patterns (one per line) that should always be blocked from %s processing, regardless of other settings. For example: BadBot, MaliciousCrawler. Use this to exclude problematic crawlers or unwanted traffic sources.', $otto_name),
1202 'otto_bot_statistics_link' => 'View detailed bot detection statistics including total hits, API calls saved, breakdown by bot type, and unique bot entries with hit counts.',
1203 'disable_common_robots_metabox' => 'Hide the Common Robots meta box from post and page edit screens. This removes the robots meta tag controls (index/noindex, follow/nofollow) from the editor interface.',
1204 'disable_advance_robots_metabox' => 'Hide the Advanced Robots meta box from post and page edit screens. This removes advanced robots directives like max-snippet, max-image-preview, and max-video-preview settings.',
1205 'disable_redirection_metabox' => 'Hide the Redirection meta box from post and page edit screens. This removes the URL redirect configuration options from the editor interface.',
1206 'disable_canonical_metabox' => 'Hide the Canonical URL meta box from post and page edit screens. This removes the canonical URL override field from the editor interface.',
1207 'disable_social_opengraph_metabox' => 'Hide the Social Media & Open Graph meta box from post and page edit screens. This removes Facebook, Twitter, and other social media meta tag controls from the editor.',
1208 'disable_schema_markup_metabox' => 'Hide the Schema Markup meta box from post and page edit screens. This removes the structured data (Article, FAQ, Product, Recipe, etc.) configuration from the editor interface.',
1209 'open_external_links' => 'Automatically add target="_blank" attribute to external links appearing in your posts, pages, and other post types when rendered by Otto.',
1210 'content_genius_sync_roles' => 'Select which WordPress user roles should be synchronized with Content Genius. This determines which users will have their profiles and permissions synced for content collaboration.',
1211 'permalink_structure' => sprintf('Displays your current WordPress permalink structure. %s works best with pretty permalinks (not "Plain"). If you see a warning, visit Settings > Permalinks to change your structure.', $plugin_name),
1212 'hide_dashboard_framework' => sprintf('Hide the main %s dashboard from the WordPress admin menu. This is useful if you want to reduce menu clutter but still keep the plugin active.', $plugin_name),
1213 'show_admin_bar_status' => sprintf('Display the %s status indicator in the WordPress admin bar at the top of your screen. This provides quick visibility of plugin status and key metrics.', $plugin_name),
1214 'enable_auto_updates' => sprintf('Allow WordPress to automatically update the %s plugin when new versions are released. Recommended for security patches, but you may prefer manual updates for major versions.', $plugin_name),
1215 'import_external_data' => sprintf('Import your existing SEO settings and metadata from other popular SEO plugins like Yoast, Rank Math, or All in One SEO. This makes migration to %s seamless without losing your SEO data.', $plugin_name),
1216 'import_seo_metadata' => 'Migrate your existing SEO titles and meta descriptions from Yoast, Rank Math, or All in One SEO. This one-click import preserves your search rankings by copying your optimized meta data to MetaSync, even if the source plugin is deactivated.'
1217 );
1218 }
1219
1220 public function get_field_section($field_id) {
1221 $sections = $this->get_accordion_sections_config();
1222
1223 foreach ($sections as $section_key => $section_data) {
1224 if (in_array($field_id, $section_data['fields'])) {
1225 return $section_key;
1226 }
1227 }
1228
1229 return null;
1230 }
1231
1232 public function render_accordion_sections($page) {
1233 global $wp_settings_sections, $wp_settings_fields;
1234
1235 if (!isset($wp_settings_fields[$page])) {
1236 return;
1237 }
1238
1239 $sections_config = $this->get_accordion_sections_config();
1240
1241 echo '<div class="metasync-settings-accordion">';
1242
1243 uasort($sections_config, function($a, $b) {
1244 return $a['priority'] - $b['priority'];
1245 });
1246
1247 foreach ($sections_config as $section_key => $section_data) {
1248 $section_id = 'metasync-section-' . $section_key;
1249 $is_open = $section_data['default_open'];
1250 $aria_expanded = $is_open ? 'true' : 'false';
1251 $content_state = $is_open ? 'open' : 'closed';
1252
1253 echo '<div class="metasync-accordion-section" data-section="' . esc_attr($section_key) . '">';
1254
1255 echo '<div class="metasync-accordion-header" role="button" tabindex="0" aria-expanded="' . $aria_expanded . '" aria-controls="' . $section_id . '">';
1256 echo '<div class="metasync-accordion-title">';
1257 echo '<span class="metasync-accordion-icon">' . esc_html($section_data['icon']) . '</span>';
1258 echo '<div class="metasync-accordion-text">';
1259 echo '<h3>' . esc_html($section_data['title']) . '</h3>';
1260 echo '<p class="metasync-accordion-description">' . esc_html($section_data['description']) . '</p>';
1261 echo '</div>';
1262 echo '</div>';
1263 echo '<button type="button" class="metasync-accordion-toggle" aria-label="Toggle section">';
1264 echo '<span class="toggle-icon">▼</span>';
1265 echo '</button>';
1266 echo '</div>';
1267
1268 echo '<div class="metasync-accordion-content" id="' . $section_id . '" data-state="' . $content_state . '">';
1269
1270 if (isset($section_data['render_callback']) && is_callable($section_data['render_callback'])) {
1271 call_user_func($section_data['render_callback']);
1272 } elseif (isset($section_data['fields']) && is_array($section_data['fields'])) {
1273 echo '<table class="form-table" role="presentation">';
1274
1275 $tooltips = $this->get_field_tooltips();
1276
1277 foreach ($section_data['fields'] as $field_id) {
1278 if (isset($wp_settings_fields[$page]['metasync_settings'][$field_id])) {
1279 $field = $wp_settings_fields[$page]['metasync_settings'][$field_id];
1280
1281 echo '<tr>';
1282 echo '<th scope="row">';
1283 if (!empty($field['title'])) {
1284 echo '<div class="metasync-field-label-wrapper">';
1285 echo '<label for="' . esc_attr($field['id']) . '">' . $field['title'] . '</label>';
1286
1287 if (isset($tooltips[$field_id])) {
1288 echo '<button type="button" class="metasync-tooltip-trigger" data-tooltip-id="' . esc_attr($field_id) . '" aria-label="More information">';
1289 echo '<svg class="metasync-info-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">';
1290 echo '<circle cx="12" cy="12" r="10"></circle>';
1291 echo '<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path>';
1292 echo '<line x1="12" y1="17" x2="12.01" y2="17"></line>';
1293 echo '</svg>';
1294 echo '</button>';
1295
1296 echo '<div class="metasync-tooltip" id="tooltip-' . esc_attr($field_id) . '" role="tooltip">';
1297 echo '<div class="metasync-tooltip-arrow"></div>';
1298 echo '<div class="metasync-tooltip-content">' . esc_html($tooltips[$field_id]) . '</div>';
1299 echo '</div>';
1300 }
1301
1302 echo '</div>';
1303 }
1304 echo '</th>';
1305 echo '<td>';
1306 call_user_func($field['callback'], $field['args']);
1307 echo '</td>';
1308 echo '</tr>';
1309 }
1310 }
1311
1312 echo '</table>';
1313 }
1314
1315 echo '</div>';
1316 echo '</div>';
1317 }
1318
1319 echo '</div>';
1320 }
1321
1322 // ────────────────────────────────────────────────────────────────
1323 // Settings field callbacks
1324 // ────────────────────────────────────────────────────────────────
1325
1326 public function metasync_settings_genkey_callback()
1327 {
1328 $current_token = Metasync::get_option('general')['apikey'] ?? '';
1329
1330 if (!empty($current_token)) {
1331 $display_value = $current_token;
1332 $status_message = 'Plugin Auth Token is active and ready for authentication.';
1333 $refresh_help = 'Click refresh to generate a new token and update the heartbeat API.';
1334 } else {
1335 $display_value = 'Auto-generated when connecting to ' . esc_html(Metasync::get_effective_plugin_name());
1336 $status_message = 'Plugin Auth Token will be automatically generated when you click "Connect to ' . esc_html(Metasync::get_effective_plugin_name()) . '".';
1337 $refresh_help = 'You can also manually generate a token by clicking refresh.';
1338 }
1339
1340 printf(
1341 '<input type="text" id="apikey" name="' . Metasync_Admin::option_key . '[general][apikey]" value="%s" size="40" readonly="readonly" /> ',
1342 esc_attr($display_value)
1343 );
1344
1345 printf('<button type="button" id="refresh-plugin-auth-token" class="button button-secondary" style="margin-left: 10px;">🔄 Refresh Token</button>');
1346 printf('<p class="description">%s %s</p>', $status_message, $refresh_help);
1347 }
1348
1349 public function linkgraph_token_callback()
1350 {
1351 printf(
1352 '<input type="text" id="linkgraph_token" name="' . Metasync_Admin::option_key . '[general][linkgraph_token]" value="%s" size="25" readonly="readonly" />',
1353 isset(Metasync::get_option('general')['linkgraph_token']) ? esc_attr(Metasync::get_option('general')['linkgraph_token']) : ''
1354 );
1355
1356 printf(
1357 '<input type="text" id="linkgraph_customer_id" name="' . Metasync_Admin::option_key . '[general][linkgraph_customer_id]" value="%s" size="25" readonly="readonly" />',
1358 isset(Metasync::get_option('general')['linkgraph_customer_id']) ? esc_attr(Metasync::get_option('general')['linkgraph_customer_id']) : ''
1359 );
1360
1361 ?>
1362 <button type="button" class="button button-primary" id="lgloginbtn">Fetch Token</button>
1363 <input type="text" id="lgusername" class="input lguser hidden" placeholder="username" />
1364 <input type="text" id="lgpassword" class="input lguser hidden" placeholder="password" />
1365 <p id="lgerror" class="notice notice-error hidden" style="display: none;"></p>
1366 <?php
1367 }
1368
1369
1370 public function time_elapsed_string($datetime, $full = false)
1371 {
1372 if(empty($datetime)){
1373 return "";
1374 }
1375 $now = new DateTime;
1376 $ago = new DateTime($datetime);
1377
1378 $diff = $now->diff($ago);
1379
1380 $string = [
1381 'y' => 'year',
1382 'm' => 'month',
1383 'w' => 'week',
1384 'd' => 'day',
1385 'h' => 'hour',
1386 'i' => 'minute',
1387 's' => 'second',
1388 ];
1389
1390 foreach ($string as $k => &$v) {
1391 if (isset($diff->$k) && $diff->$k) {
1392 $v = $diff->$k . ' ' . $v . ($diff->$k > 1 ? 's' : '');
1393 } else {
1394 unset($string[$k]);
1395 }
1396 }
1397 if (!$full) $string = array_slice($string, 0, 1);
1398 return $string ? implode(', ', $string) . ' ago' : 'just now';
1399 }
1400
1401 public function searchatlas_api_key_callback()
1402 {
1403 $current_api_key = isset(Metasync::get_option('general')['searchatlas_api_key']) ? esc_attr(Metasync::get_option('general')['searchatlas_api_key']) : '';
1404 $otto_uuid = isset(Metasync::get_option('general')['otto_pixel_uuid']) ? Metasync::get_option('general')['otto_pixel_uuid'] : '';
1405
1406 $has_api_key = !empty($current_api_key);
1407 $has_otto_uuid = !empty($otto_uuid);
1408
1409 $is_fully_connected = $this->admin_instance->is_heartbeat_connected();
1410
1411 printf('<div class="metasync-sa-connect-container">');
1412
1413 printf('<div class="metasync-sa-connect-title">');
1414 printf('🔐 One-Click Authentication');
1415 printf('</div>');
1416
1417 printf('<div class="metasync-sa-connect-description">');
1418 if ($is_fully_connected) {
1419 printf('Your %s account is fully synced with active heartbeat API. You can re-authenticate to refresh your connection or connect a different account.', esc_html(Metasync::get_effective_plugin_name()));
1420 } elseif ($has_api_key && !$has_otto_uuid) {
1421 printf('Your %s API key is configured, but %s UUID is missing. Please re-authenticate to complete the setup.', esc_html(Metasync::get_effective_plugin_name()), esc_html(Metasync::get_whitelabel_otto_name()));
1422 } else {
1423 printf('Connect your %s account with one click. This will automatically configure your API key and %s UUID below, enabling all plugin features.', esc_html(Metasync::get_effective_plugin_name()), esc_html(Metasync::get_whitelabel_otto_name()));
1424 }
1425 printf('</div>');
1426
1427 ?>
1428 <div class="metasync-mcp-consent" style="margin-top: 15px; padding: 12px 15px; background: #f0f9ff; border-left: 3px solid #0ea5e9; border-radius: 4px;">
1429 <p style="margin: 0; color: #334155; font-size: 13px; line-height: 1.6;">
1430 <strong>🤖 AI-Powered SEO Automation:</strong> By authenticating, you authorize <strong><?php echo esc_html(Metasync::get_effective_plugin_name()); ?> Brain</strong> (our AI assistant) to access your WordPress admin capabilities through the Model Context Protocol (MCP). This enables intelligent automation for SEO optimizations, content enhancements, and performance improvements.
1431 </p>
1432 </div>
1433 <?php
1434
1435 printf('<div class="metasync-sa-connect-buttons">');
1436
1437 printf('<button type="button" id="connect-searchatlas-btn" class="metasync-sa-connect-btn">');
1438 if ($is_fully_connected) {
1439 printf('🔄 Re-authenticate with %s', esc_html(Metasync::get_effective_plugin_name()));
1440 } elseif ($has_api_key && !$has_otto_uuid) {
1441 printf('🔧 Complete Authentication Setup');
1442 } else {
1443 printf('🔗 Connect to %s', esc_html(Metasync::get_effective_plugin_name()));
1444 }
1445 printf('</button>');
1446
1447 if ($has_api_key) {
1448 printf('<button type="button" id="reset-searchatlas-auth" class="metasync-sa-reset-btn" style="margin-left: 10px;">');
1449 printf('🔓 Disconnect Account');
1450 printf('</button>');
1451 }
1452
1453 printf('</div>');
1454
1455 printf('<div style="margin-top: 15px;">');
1456 printf('<details style="margin-top: 10px;">');
1457 printf('<summary style="cursor: pointer; color: #666; font-size: 13px;">💡 Authentication Tips</summary>');
1458 printf('<div style="padding: 10px 0; color: #666; font-size: 13px; line-height: 1.5;">');
1459 printf('• Make sure you have a %s account before connecting<br/>', esc_html(Metasync::get_effective_plugin_name()));
1460 printf('• The authentication window will open in a popup - please allow popups<br/>');
1461 printf('• The process typically takes 15-30 seconds to complete<br/>');
1462 printf('• Your API key will be automatically filled in the field below<br/>');
1463 printf('• If you encounter issues, try disabling ad blockers temporarily<br/>');
1464 printf('• Contact <a href="mailto:%s">%s</a> if you need assistance', Metasync::SUPPORT_EMAIL, Metasync::SUPPORT_EMAIL);
1465 printf('</div>');
1466 printf('</details>');
1467 printf('</div>');
1468
1469 printf('</div>');
1470
1471 printf('<div style="margin-top: 20px;">');
1472 printf('<label for="searchatlas-api-key" style="font-weight: 600; display: block; margin-bottom: 8px;">');
1473 printf('🔑 %s API Key', esc_html(Metasync::get_effective_plugin_name()));
1474 if ($is_fully_connected) {
1475 printf('<span style="color: #46b450; margin-left: 10px; font-weight: normal;">✓ Synced</span>');
1476 } elseif ($has_api_key && !$has_otto_uuid) {
1477 printf('<span style="color: #ff8c00; margin-left: 10px; font-weight: normal;">⚠️ Partial Connection (Missing %s UUID)</span>', esc_html(Metasync::get_whitelabel_otto_name()));
1478 }
1479 printf('</label>');
1480
1481 printf(
1482 '<input type="text" id="searchatlas-api-key" name="' . Metasync_Admin::option_key . '[general][searchatlas_api_key]" value="%s" size="40" class="regular-text" placeholder="Your API key will appear here after authentication" />',
1483 $current_api_key
1484 );
1485
1486 printf('<p class="description" style="margin-top: 8px;">');
1487 if ($is_fully_connected) {
1488 printf('Your %s API key for secure communication with the platform. Use the authentication button above to refresh or change accounts.', esc_html(Metasync::get_effective_plugin_name()));
1489 } elseif ($has_api_key && !$has_otto_uuid) {
1490 printf('Your API key is configured but %s UUID is missing. Re-authenticate above to complete the setup and enable dashboard access.', esc_html(Metasync::get_whitelabel_otto_name()));
1491 } else {
1492 printf('This field will be automatically populated when you authenticate using the button above. You can also manually enter your API key if you have one.');
1493 }
1494 printf('</p>');
1495
1496 ?>
1497 <p class="description" style="margin-top: 10px; padding: 8px 12px; background: #fff9e6; border-left: 3px solid #f0b849; border-radius: 4px; font-size: 12px;">
1498 <strong>📝 Manual Authentication:</strong> If you manually enter your <?php echo esc_html(Metasync::get_effective_plugin_name()); ?> API Key and OTTO UUID, you also consent to the same AI-powered automation permissions described above.
1499 </p>
1500 <?php
1501
1502 printf('</div>');
1503
1504 if( isset(Metasync::get_option('general')['searchatlas_api_key'])&&Metasync::get_option('general')['searchatlas_api_key']!=''){
1505 $timestamp = @Metasync::get_option('general')['send_auth_token_timestamp'];
1506 printf(
1507 '<p id="sendAuthTokenTimestamp" class="descriptionValue">%s (%s)</p>',
1508 esc_attr($timestamp),
1509 $this->time_elapsed_string($timestamp)
1510 );
1511
1512
1513 }
1514 }
1515
1516 public function bing_site_verification_callback()
1517 {
1518 printf(
1519 '<input type="text" id="bing_site_verification" name="' . Metasync_Admin::option_key . '[searchengines][bing_site_verification]" value="%s" size="50" />',
1520 isset(Metasync::get_option('searchengines')['bing_site_verification']) ? esc_attr(Metasync::get_option('searchengines')['bing_site_verification']) : ''
1521 );
1522
1523 printf(' <br> <span class="description"> Enter Bing Webmaster Tools verification code: </span> ');
1524 printf(' <a href="https://www.bing.com/webmasters/about" target="_blank">Get from here</a> <br> ');
1525 }
1526
1527 public function yandex_site_verification_callback()
1528 {
1529 printf(
1530 '<input type="text" id="yandex_site_verification" name="' . Metasync_Admin::option_key . '[searchengines][yandex_site_verification]" value="%s" size="50" />',
1531 isset(Metasync::get_option('searchengines')['yandex_site_verification']) ? esc_attr(Metasync::get_option('searchengines')['yandex_site_verification']) : ''
1532 );
1533
1534 printf(' <br> <span class="description"> Enter Yandex verification code: </span>');
1535 printf(' <a href="https://passport.yandex.com/auth" target="_blank">Get from here</a> <br> ');
1536 }
1537
1538 public function google_site_verification_callback()
1539 {
1540 printf(
1541 '<input type="text" id="google_site_verification" name="' . Metasync_Admin::option_key . '[searchengines][google_site_verification]" value="%s" size="50" />',
1542 isset(Metasync::get_option('searchengines')['google_site_verification']) ? esc_attr(Metasync::get_option('searchengines')['google_site_verification']) : ''
1543 );
1544
1545 printf(' <br> <span class="description"> Enter Google Search Console verification code: </span>');
1546 printf(' <a href="https://www.google.com/webmasters/verification" target="_blank">Get from here</a> <br> ');
1547 }
1548
1549 public function pinterest_site_verification_callback()
1550 {
1551 printf(
1552 '<input type="text" id="pinterest_site_verification" name="' . Metasync_Admin::option_key . '[searchengines][pinterest_site_verification]" value="%s" size="50" />',
1553 isset(Metasync::get_option('searchengines')['pinterest_site_verification']) ? esc_attr(Metasync::get_option('searchengines')['pinterest_site_verification']) : ''
1554 );
1555
1556 printf(' <br> <span class="description"> Enter Pinterest verification code: </span>');
1557 printf(' <a href="https://in.pinterest.com/" target="_blank">Get from here</a> <br> ');
1558 }
1559
1560 public function local_seo_person_organization_callback()
1561 {
1562 $person_organization = Metasync::get_option('localseo')['local_seo_person_organization'] ?? '';
1563 ?>
1564 <select id="local_seo_person_organization" name="<?php echo esc_attr(Metasync_Admin::option_key . '[localseo][local_seo_person_organization]') ?>">
1565 <?php
1566 printf('<option value="Person" %s >Person</option>', selected('Person', esc_attr($person_organization)));
1567 printf('<option value="Organization" %s >Organization</option>', selected('Organization', esc_attr($person_organization)));
1568 ?>
1569 </select>
1570 <?php
1571 printf(' <br> <span class="description"> Choose whether the site represents a person or an organization. </span>');
1572 }
1573
1574 public function local_seo_name_callback()
1575 {
1576 printf(
1577 '<input type="text" id="local_seo_name" name="' . Metasync_Admin::option_key . '[localseo][local_seo_name]" value="%s" size="50" />',
1578 isset(Metasync::get_option('localseo')['local_seo_name']) ? esc_attr(Metasync::get_option('localseo')['local_seo_name']) : get_bloginfo()
1579 );
1580
1581 printf(' <br> <span class="description"> Your name or company name </span>');
1582 }
1583
1584 public function local_seo_logo_callback()
1585 {
1586 $local_seo_logo = Metasync::get_option('localseo')['local_seo_logo'] ?? '';
1587
1588 printf(
1589 '<input type="hidden" id="local_seo_logo" name="' . Metasync_Admin::option_key . '[localseo][local_seo_logo]" value="%s" size="50" />',
1590 isset(Metasync::get_option('localseo')['local_seo_logo']) ? esc_attr(Metasync::get_option('localseo')['local_seo_logo']) : ''
1591 );
1592
1593 printf(' <br> <input class="button-secondary" type="button" id="logo_upload_button" value="Add or Upload File">');
1594
1595 printf(' <br><br> <span class="description bold"> Min Size: 160Χ90px, Max Size: 1920X1080px. </span> <br> <span class="description"> A squared image is preferred by the search engines. </span> <br><br> ');
1596
1597 printf('<img src="%s" id="local_seo_business_logo" width="300">', wp_get_attachment_image_src($local_seo_logo, 'medium')[0] ?? '');
1598
1599 $button_type = 'hidden';
1600 if ($local_seo_logo) {
1601 $button_type = 'button';
1602 }
1603 printf('<input type="%s" class="button-secondary" id="local_seo_logo_close_btn" value="X">', $button_type);
1604 }
1605
1606 public function local_seo_url_callback()
1607 {
1608 printf(
1609 '<input type="text" id="local_seo_url" name="' . Metasync_Admin::option_key . '[localseo][local_seo_url]" value="%s" size="50" />',
1610 isset(Metasync::get_option('localseo')['local_seo_url']) ? esc_attr(Metasync::get_option('localseo')['local_seo_url']) : home_url()
1611 );
1612
1613 printf(' <br> <span class="description"> URL of the item. </span>');
1614 }
1615
1616 public function local_seo_email_callback()
1617 {
1618 printf(
1619 '<input type="text" id="local_seo_email" name="' . Metasync_Admin::option_key . '[localseo][local_seo_email]" value="%s" size="50" />',
1620 isset(Metasync::get_option('localseo')['local_seo_email']) ? esc_attr(Metasync::get_option('localseo')['local_seo_email']) : ''
1621 );
1622
1623 printf(' <br> <span class="description"> Search engines display your email address. </span>');
1624 }
1625
1626 public function local_seo_phone_callback()
1627 {
1628 printf(
1629 '<input type="text" id="local_seo_phone" name="' . Metasync_Admin::option_key . '[localseo][local_seo_phone]" value="%s" size="50" />',
1630 isset(Metasync::get_option('localseo')['local_seo_phone']) ? esc_attr(Metasync::get_option('localseo')['local_seo_phone']) : ''
1631 );
1632
1633 printf(' <br> <span class="description"> Search engines may prominently display your contact phone number for mobile users. </span>');
1634 }
1635
1636 public function local_seo_address_callback()
1637 {
1638 printf(
1639 '<input type="text" id="local_seo_address_street" name="' . Metasync_Admin::option_key . '[localseo][address][street]" value="%s" size="50" placeholder="Street Address"/> <br>',
1640 isset(Metasync::get_option('localseo')['address']['street']) ? esc_attr(Metasync::get_option('localseo')['address']['street']) : ''
1641 );
1642
1643 printf(
1644 '<input type="text" id="local_seo_address_locality" name="' . Metasync_Admin::option_key . '[localseo][address][locality]" value="%s" size="50" placeholder="Locality"/> <br>',
1645 isset(Metasync::get_option('localseo')['address']['locality']) ? esc_attr(Metasync::get_option('localseo')['address']['locality']) : ''
1646 );
1647
1648 printf(
1649 '<input type="text" id="local_seo_address_region" name="' . Metasync_Admin::option_key . '[localseo][address][region]" value="%s" size="50" placeholder="Region"/> <br>',
1650 isset(Metasync::get_option('localseo')['address']['region']) ? esc_attr(Metasync::get_option('localseo')['address']['region']) : ''
1651 );
1652
1653 printf(
1654 '<input type="text" id="local_seo_address_postalcode" name="' . Metasync_Admin::option_key . '[localseo][address][postalcode]" value="%s" size="50" placeholder="Postal Code"/> <br>',
1655 isset(Metasync::get_option('localseo')['address']['postalcode']) ? esc_attr(Metasync::get_option('localseo')['address']['postalcode']) : ''
1656 );
1657
1658 printf(
1659 '<input type="text" id="local_seo_address_country" name="' . Metasync_Admin::option_key . '[localseo][address][country]" value="%s" size="50" placeholder="Country"/> <br>',
1660 isset(Metasync::get_option('localseo')['address']['country']) ? esc_attr(Metasync::get_option('localseo')['address']['country']) : ''
1661 );
1662 }
1663
1664 public function local_seo_business_type_callback()
1665 {
1666 $types = self::get_business_types();
1667 sort($types);
1668
1669 $business_type = Metasync::get_option('localseo')['local_seo_business_type'] ?? '';
1670
1671 ?>
1672 <select name="<?php echo esc_attr(Metasync_Admin::option_key . '[localseo][local_seo_business_type]') ?>">
1673 <option value='0'>Select Business Type</option>
1674 <?php
1675 foreach ($types as $type) {
1676 printf('<option value="%s" %s >%s</option>', $type, selected($type, esc_attr($business_type)), $type);
1677 }
1678 ?>
1679 </select>
1680 <?php
1681 }
1682
1683 public function local_seo_opening_hours_callback()
1684 {
1685 $days_name = ['Monday', 'Tuseday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
1686 $days = isset(Metasync::get_option('localseo')['days']) ? Metasync::get_option('localseo')['days'] : '';
1687 $times = isset(Metasync::get_option('localseo')['times']) ? Metasync::get_option('localseo')['times'] : '';
1688
1689 ?>
1690 <ul id="daysTime">
1691 <?php
1692 $opening_days = [];
1693 if ($days && $times) {
1694 $opening_days = array_combine($days, $times);
1695 }
1696 foreach ($opening_days as $day_name => $day_time) {
1697 ?>
1698 <li>
1699 <select name="<?php echo esc_attr(Metasync_Admin::option_key . '[localseo][days][]') ?>">
1700 <?php
1701 foreach ($days_name as $name) {
1702 printf('<option value="%s" %s >%s</option>', $name, selected(esc_attr($name), esc_attr($day_name)), esc_attr($name));
1703 }
1704 ?>
1705 </select>
1706 <input type="text" name="<?php echo esc_attr(Metasync_Admin::option_key . '[localseo][times][]') ?>" value="<?php echo esc_attr($day_time) ?>">
1707 <button id="timeDelete">Delete</button>
1708 </li>
1709 <?php } ?>
1710 <?php if (empty($opening_days)) { ?>
1711 <li>
1712 <select name="<?php echo esc_attr(Metasync_Admin::option_key . '[localseo][days][]') ?>">
1713 <?php
1714 foreach ($days_name as $name) {
1715 printf('<option value="%s" >%s</option>', esc_attr($name), esc_attr($name));
1716 }
1717 ?>
1718 </select>
1719 <input type="text" name="<?php echo esc_attr(Metasync_Admin::option_key . '[localseo][times][]') ?>" value="">
1720 <button id="timeDelete">Delete</button>
1721 </li>
1722 <?php } ?>
1723 </ul>
1724 <?php
1725
1726 printf(' <input type="hidden" id="days_time_count" value="%s"/>', count($opening_days));
1727 printf(' <input class="button-secondary" type="button" id="addNewTime" value="Add Time">');
1728 printf(' <br> <span class="description"> Select opening hours. You can add multiple sets if you have different opening or closing hours on some days or if you have a mid-day break. Times are specified using 24:00 time. </span>');
1729 }
1730
1731 public function local_seo_phone_numbers_callback()
1732 {
1733 $number_types = ['Customer Service', 'Technical Support', 'Billing Support', 'Bill Payment', 'Sales', 'Reservations', 'Credit Card Support', 'Emergency', 'Baggage Tracking', 'Roadside Assistance', 'Package Tracking'];
1734 $types = isset(Metasync::get_option('localseo')['phonetype']) ? Metasync::get_option('localseo')['phonetype'] : '';
1735 $numbers = isset(Metasync::get_option('localseo')['phonenumber']) ? Metasync::get_option('localseo')['phonenumber'] : '';
1736
1737 ?>
1738
1739 <ul id="phone-numbers">
1740 <?php
1741 $phone_numbers = [];
1742 if ($types && $numbers) {
1743 $phone_numbers = array_combine($types, $numbers);
1744 }
1745 foreach ($phone_numbers as $phone_type => $phone_number) {
1746 ?>
1747 <li>
1748 <select name="<?php echo esc_attr(Metasync_Admin::option_key . '[localseo][phonetype][]') ?>">
1749 <?php
1750 foreach ($number_types as $type) {
1751 printf('<option value="%s" %s >%s</option>', esc_attr($type), selected(esc_attr($type), esc_attr($phone_type)), esc_attr($type));
1752 }
1753 ?>
1754 </select>
1755 <input type="text" name="<?php echo esc_attr(Metasync_Admin::option_key . '[localseo][phonenumber][]') ?>" value="<?php echo esc_attr($phone_number) ?>">
1756 <button id="number-delete">Delete</button>
1757 </li>
1758 <?php } ?>
1759 <?php if (empty($phone_numbers)) { ?>
1760 <li>
1761 <select name="<?php echo esc_attr(Metasync_Admin::option_key . '[localseo][phonetype][]') ?>">
1762 <?php
1763 foreach ($number_types as $type) {
1764 printf('<option value="%s" >%s</option>', esc_attr($type), esc_attr($type));
1765 }
1766 ?>
1767 </select>
1768 <input type="text" name="<?php echo esc_attr(Metasync_Admin::option_key . '[localseo][phonenumber][]') ?>" value="">
1769 <button id="number-delete">Delete</button>
1770 </li>
1771 <?php } ?>
1772 </ul>
1773 <?php
1774
1775 printf(' <input type="hidden" id="phone_number_count" value="%s"/>', count($phone_numbers));
1776 printf(' <input class="button-secondary" type="button" id="addNewNumber" value="Add Number">');
1777 printf(' <br> <span class="description"> Search engines may prominently display your contact phone number for mobile users. </span>');
1778 }
1779
1780 public function local_seo_price_range_callback()
1781 {
1782 printf(
1783 '<input type="text" id="local_seo_price_range" name="' . Metasync_Admin::option_key . '[localseo][local_seo_price_range]" value="%s" size="50" />',
1784 isset(Metasync::get_option('localseo')['local_seo_price_range']) ? esc_attr(Metasync::get_option('localseo')['local_seo_price_range']) : ''
1785 );
1786 printf(' <br> <span class="description"> The price range of the business, for example $$$. </span>');
1787 }
1788
1789 public function local_seo_about_page_callback()
1790 {
1791 ?>
1792 <select name="<?php echo esc_attr(Metasync_Admin::option_key . '[localseo][local_seo_about_page]') ?>">
1793 <option value='0'>Select About Page</option>
1794 <?php
1795 $about_page = Metasync::get_option('localseo')['local_seo_about_page'] ?? '';
1796 $pages = get_pages();
1797 foreach ($pages as $page) {
1798 printf('<option value="%s" %s >%s</option>', $page->ID, selected($page->ID, esc_attr($about_page)), $page->post_title);
1799 }
1800 ?>
1801 </select>
1802 <?php
1803 printf(' <br> <span class="description"> Search engines tag your about us page. </span>');
1804 }
1805
1806 public function local_seo_contact_page_callback()
1807 {
1808 ?>
1809 <select name="<?php echo esc_attr(Metasync_Admin::option_key . '[localseo][local_seo_contact_page]') ?>">
1810 <option value='0'>Select Contact Page</option>
1811 <?php
1812 $contact_page = Metasync::get_option('localseo')['local_seo_contact_page'] ?? '';
1813 $pages = get_pages();
1814 foreach ($pages as $page) {
1815 printf('<option value="%s" %s >%s</option>', $page->ID, selected($page->ID, esc_attr($contact_page)), $page->post_title);
1816 }
1817 ?>
1818 </select>
1819 <?php
1820 printf(' <br> <span class="description"> Search engines tag your contact page. </span>');
1821 }
1822
1823 public function local_seo_map_key_callback()
1824 {
1825 printf(
1826 '<input type="text" id="local_seo_map_key" name="' . Metasync_Admin::option_key . '[localseo][local_seo_map_key]" value="%s" size="50" />',
1827 isset(Metasync::get_option('localseo')['local_seo_map_key']) ? esc_attr(Metasync::get_option('localseo')['local_seo_map_key']) : ''
1828 );
1829
1830 printf(' <br> <span class="description"> An API Key is required to display embedded Google Maps on your site. </span>');
1831 }
1832
1833 public function local_seo_geo_coordinates_callback()
1834 {
1835 printf(
1836 '<input type="text" id="local_seo_geo_coordinates" name="' . Metasync_Admin::option_key . '[localseo][local_seo_geo_coordinates]" value="%s" size="50" />',
1837 isset(Metasync::get_option('localseo')['local_seo_geo_coordinates']) ? esc_attr(Metasync::get_option('localseo')['local_seo_geo_coordinates']) : ''
1838 );
1839
1840 printf(' <br> <span class="description"> Latitude and longitude values separated by comma. </span>');
1841 }
1842
1843 public function header_snippets_callback()
1844 {
1845 printf(
1846 '<textarea class="wide-text" id="header_snippets" rows="8" name="' . Metasync_Admin::option_key . '[codesnippets][header_snippet]" >%s</textarea>',
1847 isset(Metasync::get_option('codesnippets')['header_snippet']) ? esc_attr(Metasync::get_option('codesnippets')['header_snippet']) : ''
1848 );
1849 }
1850
1851 public function footer_snippets_callback()
1852 {
1853 printf(
1854 '<textarea class="wide-text" id="footer_snippets" rows="8" name="' . Metasync_Admin::option_key . '[codesnippets][footer_snippet]" >%s</textarea>',
1855 isset(Metasync::get_option('codesnippets')['footer_snippet']) ? esc_attr(Metasync::get_option('codesnippets')['footer_snippet']) : ''
1856 );
1857 }
1858
1859 public function no_index_posts_callback()
1860 {
1861 printf(
1862 '<input type="checkbox" id="no_index_posts" name="' . Metasync_Admin::option_key . '[optimal_settings][no_index_posts]" value="true" %s />',
1863 isset(Metasync::get_option('optimal_settings')['no_index_posts']) && Metasync::get_option('optimal_settings')['no_index_posts'] == 'true' ? 'checked' : ''
1864 );
1865
1866 printf(' <br> <span class="description"> Setting empty archives to <code>noindex</code> is useful for avoiding indexation of thin content pages and dilution of page rank. As soon as a post is added, the page is updated to index. </span>');
1867 }
1868
1869 public function no_follow_links_callback()
1870 {
1871 printf(
1872 '<input type="checkbox" id="no_follow_links" name="' . Metasync_Admin::option_key . '[optimal_settings][no_follow_links]" value="true" %s />',
1873 isset(Metasync::get_option('optimal_settings')['no_follow_links']) && Metasync::get_option('optimal_settings')['no_follow_links'] == 'true' ? 'checked' : ''
1874 );
1875
1876 printf(' <br> <span class="description"> Automatically add <code>rel="nofollow"</code> attribute to external links appearing in your posts, pages, and other post types. The attribute is dynamically applied when the url is displayed</span>');
1877 }
1878
1879 public function open_external_links_callback()
1880 {
1881 printf(
1882 '<input type="checkbox" id="open_external_links" name="' . Metasync_Admin::option_key . '[optimal_settings][open_external_links]" value="true" %s />',
1883 isset(Metasync::get_option('optimal_settings')['open_external_links']) && Metasync::get_option('optimal_settings')['open_external_links'] == 'true' ? 'checked' : ''
1884 );
1885
1886 printf(' <br> <span class="description"> Automatically add <code>target="_blank"</code> attribute to external links appearing in your posts, pages, and other post types. The attribute is applied when the url is displayed.</span>');
1887 }
1888
1889 public function add_alt_image_tags_callback()
1890 {
1891 printf(
1892 '<input type="checkbox" name="' . Metasync_Admin::option_key . '[optimal_settings][add_alt_image_tags]" value="true" %s />',
1893 isset(Metasync::get_option('optimal_settings')['add_alt_image_tags']) && Metasync::get_option('optimal_settings')['add_alt_image_tags'] == 'true' ? 'checked' : ''
1894 );
1895
1896 printf(' <br> <span class="description"> Automatically add <code>alt</code> attribute to Image Tags appearing in your posts, pages, and other post types. The attribute is applied when the content is displayed.</span>');
1897 }
1898
1899 public function add_title_image_tags_callback()
1900 {
1901 printf(
1902 '<input type="checkbox" name="' . Metasync_Admin::option_key . '[optimal_settings][add_title_image_tags]" value="true" %s />',
1903 isset(Metasync::get_option('optimal_settings')['add_title_image_tags']) && Metasync::get_option('optimal_settings')['add_title_image_tags'] == 'true' ? 'checked' : ''
1904 );
1905
1906 printf(' <br> <span class="description"> Automatically add <code>title</code> attribute to Image Tags appearing in your posts, pages, and other post types. The attribute is applied when the content is displayed.</span>');
1907 }
1908
1909 public function site_type_callback()
1910 {
1911
1912 $site_type = Metasync::get_option('optimal_settings')['site_info']['type'] ?? '';
1913
1914 $types = [
1915 ['name' => 'Personal Blog', 'value' => 'blog'],
1916 ['name' => 'Community Blog/News Site', 'value' => 'news'],
1917 ['name' => 'Personal Portfolio', 'value' => 'portfolio'],
1918 ['name' => 'Small Business Site', 'value' => 'business'],
1919 ['name' => 'Webshop', 'value' => 'webshop'],
1920 ['name' => 'Other Personal Website', 'value' => 'otherpersonal'],
1921 ['name' => 'Other Business Website', 'value' => 'otherbusiness'],
1922 ];
1923
1924 ?>
1925 <select name="<?php echo esc_attr(Metasync_Admin::option_key . '[optimal_settings][site_info][type]') ?>" id="site_info_type">
1926 <?php
1927 foreach ($types as $type) {
1928 printf('<option value="%s" %s >%s</option>', esc_attr($type['value']), selected(esc_attr($type['value']), esc_attr($site_type)), ($type['name']));
1929 }
1930 ?>
1931 </select>
1932 <?php
1933
1934 }
1935
1936 public function site_business_type_callback()
1937 {
1938
1939 $business_type = Metasync::get_option('optimal_settings')['site_info']['business_type'] ?? '';
1940
1941 $types = self::get_business_types();
1942 sort($types);
1943
1944 ?>
1945 <select name="<?php echo esc_attr(Metasync_Admin::option_key . '[optimal_settings][site_info][business_type]') ?>">
1946 <option value='0'>Select Business Type</option>
1947 <?php
1948 foreach ($types as $type) {
1949 printf('<option value="%s" %s >%s</option>', esc_attr($type), selected(esc_attr($type), esc_attr($business_type)), esc_attr($type));
1950 }
1951 ?>
1952 </select>
1953 <?php
1954 }
1955
1956 public function site_company_name_callback()
1957 {
1958
1959 $company_name = Metasync::get_option('optimal_settings')['site_info']['company_name'] ?? get_bloginfo('name');
1960
1961 printf(
1962 '<input type="text" name="' . Metasync_Admin::option_key . '[optimal_settings][site_info][company_name]" value="%s" size="50" />',
1963 $company_name ? $company_name : get_bloginfo('name')
1964 );
1965 }
1966
1967 public function site_google_logo_callback()
1968 {
1969
1970 $google_logo = Metasync::get_option('optimal_settings')['site_info']['google_logo'] ?? '';
1971
1972 printf(
1973 '<input type="hidden" id="site_google_logo" name="' . Metasync_Admin::option_key . '[optimal_settings][site_info][google_logo]" value="%s" size="50" />',
1974 $google_logo
1975 );
1976
1977 printf(' <br> <input class="button-secondary" type="button" id="google_logo_btn" value="Add or Upload File">');
1978 printf(' <br><br> <span class="description bold"> Min Size: 160X90px, Max Size: 1920X1080px. </span> <br> <span class="description"> A squared image is preferred by the search engines. </span> <br><br> ');
1979 printf('<img src="%s" id="site_google_logo_img" width="300">', wp_get_attachment_image_src($google_logo, 'medium')[0] ?? '');
1980
1981 $button_type = 'hidden';
1982 if ($google_logo) {
1983 $button_type = 'button';
1984 }
1985 printf('<input type="%s" class="button-secondary" id="site_google_logo_close_btn" value="X">', $button_type);
1986 }
1987
1988 public function site_social_share_image_callback()
1989 {
1990
1991 $social_share_image = Metasync::get_option('optimal_settings')['site_info']['social_share_image'] ?? '';
1992
1993 printf(
1994 '<input type="hidden" id="site_social_share_image" name="' . Metasync_Admin::option_key . '[optimal_settings][site_info][social_share_image]" value="%s" size="50" />',
1995 $social_share_image
1996 );
1997
1998 printf(' <br> <input class="button-secondary" type="button" id="social_share_image_btn" value="Add or Upload File">');
1999 printf(' <br><br> <span class="description bold"> The recommended image size is 1200 x 630 pixels. </span> <br> <span class="description"> When a featured image or an OpenGraph Image is not set for individual posts/pages/CPTs, this image will be used as a fallback thumbnail when your post is shared on Facebook. </span> <br><br> ');
2000 printf('<img src="%s" id="site_social_share_img" width="300">', wp_get_attachment_image_src($social_share_image, 'medium')[0] ?? '');
2001
2002 $button_type = 'hidden';
2003 if ($social_share_image) {
2004 $button_type = 'button';
2005 }
2006 printf('<input type="%s" class="button-secondary" id="site_social_image_close_btn" value="X">', $button_type);
2007 }
2008
2009 public function common_robot_meta_tags_callback()
2010 {
2011 $common_robots_meta = Metasync::get_option('common_robots_meta') ?? Metasync::get_option('common_robots_mata') ?? '';
2012
2013 ?>
2014 <ul class="checkbox-list">
2015 <li>
2016 <input type="checkbox" name="<?php echo esc_attr(Metasync_Admin::option_key . '[common_robots_meta][index]') ?>" id="robots_common1" value="index" <?php isset($common_robots_meta['index']) ? checked('index', $common_robots_meta['index']) : '' ?>>
2017 <label for="robots_common1">Index </br>
2018 <span class="description">
2019 <span>Search engines to index and show these pages in the search results.</span>
2020 </span>
2021 </label>
2022 </li>
2023 <li>
2024 <input type="checkbox" name="<?php echo esc_attr(Metasync_Admin::option_key . '[common_robots_meta][noindex]') ?>" id="robots_common2" value="noindex" <?php isset($common_robots_meta['noindex']) ? checked('noindex', $common_robots_meta['noindex']) : '' ?>>
2025 <label for="robots_common2">No Index </br>
2026 <span class="description">
2027 <span>Search engines not indexed and displayed this pages in search engine results</span>
2028 </span>
2029 </label>
2030 </li>
2031 <li>
2032 <input type="checkbox" name="<?php echo esc_attr(Metasync_Admin::option_key . '[common_robots_meta][nofollow]') ?>" id="robots_common3" value="nofollow" <?php isset($common_robots_meta['nofollow']) ? checked('nofollow', $common_robots_meta['nofollow']) : '' ?>>
2033 <label for="robots_common3">No Follow </br>
2034 <span class="description">
2035 <span>Search engines not follow the links on the pages</span>
2036 </span>
2037 </label>
2038 </li>
2039 <li>
2040 <input type="checkbox" name="<?php echo esc_attr(Metasync_Admin::option_key . '[common_robots_meta][noarchive]') ?>" id="robots_common4" value="noarchive" <?php isset($common_robots_meta['noarchive']) ? checked('noarchive', $common_robots_meta['noarchive']) : '' ?>>
2041 <label for="robots_common4">No Archive </br>
2042 <span class="description">
2043 <span>Search engines not showing Cached links for pages</span>
2044 </span>
2045 </label>
2046 </li>
2047 <li>
2048 <input type="checkbox" name="<?php echo esc_attr(Metasync_Admin::option_key . '[common_robots_meta][noimageindex]') ?>" id="robots_common5" value="noimageindex" <?php isset($common_robots_meta['noimageindex']) ? checked('noimageindex', $common_robots_meta['noimageindex']) : '' ?>>
2049 <label for="robots_common5">No Image Index </br>
2050 <span class="description">
2051 <span>If you do not want to apear your pages as the referring page for images that appear in image search results</span>
2052 </span>
2053 </label>
2054 </li>
2055 <li>
2056 <input type="checkbox" name="<?php echo esc_attr(Metasync_Admin::option_key . '[common_robots_meta][nosnippet]') ?>" id="robots_common6" value="nosnippet" <?php isset($common_robots_meta['nosnippet']) ? checked('nosnippet', $common_robots_meta['nosnippet']) : '' ?>>
2057 <label for="robots_common6">No Snippet </br>
2058 <span class="description">
2059 <span>Search engines not snippet to show in the search results</span>
2060 </span>
2061 </label>
2062 </li>
2063 </ul>
2064 <?php
2065 }
2066
2067 /**
2068 * @deprecated Use common_robot_meta_tags_callback() instead
2069 */
2070 public function common_robot_mata_tags_callback()
2071 {
2072 return $this->common_robot_meta_tags_callback();
2073 }
2074
2075 public function advance_robot_meta_tags_callback()
2076 {
2077 $advance_robots_meta = Metasync::get_option('advance_robots_meta') ?? Metasync::get_option('advance_robots_mata') ?? '';
2078
2079 $snippet_advance_robots_enable = $advance_robots_meta['max-snippet']['enable'] ?? '';
2080 $snippet_advance_robots_length = $advance_robots_meta['max-snippet']['length'] ?? '-1';
2081 $video_advance_robots_enable = $advance_robots_meta['max-video-preview']['enable'] ?? '';
2082 $video_advance_robots_length = $advance_robots_meta['max-video-preview']['length'] ?? '-1';
2083 $image_advance_robots_enable = $advance_robots_meta['max-image-preview']['enable'] ?? '';
2084 $image_advance_robots_length = $advance_robots_meta['max-image-preview']['length'] ?? '';
2085
2086 ?>
2087 <ul class="checkbox-list">
2088 <li>
2089 <label for="advanced_robots_snippet">
2090 <input type="checkbox" name="<?php echo esc_attr(Metasync_Admin::option_key . '[advance_robots_meta][max-snippet][enable]') ?>" id="advanced_robots_snippet" value="1" <?php checked('1', esc_attr($snippet_advance_robots_enable)) ?>>
2091 Snippet </br>
2092 <input type="number" class="input-length" name="<?php echo esc_attr(Metasync_Admin::option_key . '[advance_robots_meta][max-snippet][length]') ?>" id="advanced_robots_snippet_value" value="<?php echo esc_attr($snippet_advance_robots_length); ?>" min="-1"> </br>
2093 <span class="description">
2094 <span>Add maximum text-length, in characters, of a snippet for your page.</span>
2095 </span>
2096 </label>
2097 </li>
2098 <li>
2099 <label for="advanced_robots_video">
2100 <input type="checkbox" name="<?php echo esc_attr(Metasync_Admin::option_key . '[advance_robots_meta][max-video-preview][enable]') ?>" id="advanced_robots_video" value="1" <?php checked('1', esc_attr($video_advance_robots_enable)) ?>>
2101 Video Preview </br>
2102 <input type="number" class="input-length" name="<?php echo esc_attr(Metasync_Admin::option_key . '[advance_robots_meta][max-video-preview][length]') ?>" id="advanced_robots_video_value" value="<?php echo esc_attr($video_advance_robots_length); ?>" min="-1"> </br>
2103 <span class="description">
2104 <span>Add maximum duration in seconds of an animated video preview.</span>
2105 </span>
2106 </label>
2107 </li>
2108 <li>
2109 <label for="advanced_robots_image">
2110 <input type="checkbox" name="<?php echo esc_attr(Metasync_Admin::option_key . '[advance_robots_meta][max-image-preview][enable]') ?>" id="advanced_robots_image" value="1" <?php checked('1', esc_attr($image_advance_robots_enable)); ?>>
2111 Image Preview </br>
2112 <select class="input-length" name="<?php echo esc_attr(Metasync_Admin::option_key . '[advance_robots_meta][max-image-preview][length]') ?>" id="advanced_robots_image_value">
2113 <option value="large" <?php selected('large', esc_attr($image_advance_robots_length)) ?>>Large</option>
2114 <option value="standard" <?php selected('standard', esc_attr($image_advance_robots_length)) ?>>Standard</option>
2115 <option value="none" <?php selected('none', esc_attr($image_advance_robots_length)) ?>>None</option>
2116 </select>
2117 </br>
2118 <span class="description">
2119 <span>Add maximum size of image preview to show the images on this page.</span>
2120 </span>
2121 </label>
2122 </li>
2123 </ul>
2124 <?php
2125 }
2126
2127 /**
2128 * @deprecated Use advance_robot_meta_tags_callback() instead
2129 */
2130 public function advance_robot_mata_tags_callback()
2131 {
2132 return $this->advance_robot_meta_tags_callback();
2133 }
2134
2135 public function global_twitter_card_type_callback()
2136 {
2137 $twitter_card_type = Metasync::get_option('twitter_card_type') ?? '';
2138 ?>
2139
2140 <select class="input-length" name="<?php echo esc_attr(Metasync_Admin::option_key . '[twitter_card_type]') ?>" id="twitter_card_type">
2141 <option value="summary_large_image" <?php selected('summary_large_image', esc_attr($twitter_card_type)) ?>>Summary Large Image</option>
2142 <option value="summary_card" <?php selected('summary_card', esc_attr($twitter_card_type)) ?>>Summary Card</option>
2143 </select>
2144
2145 <?php
2146 }
2147
2148 public function global_open_graph_meta_callback()
2149 {
2150 printf(
2151 '<input type="checkbox" name="' . Metasync_Admin::option_key . '[common_meta_settings][open_graph_meta_tags]" value="true" %s />',
2152 isset(Metasync::get_option('common_meta_settings')['open_graph_meta_tags']) && Metasync::get_option('common_meta_settings')['open_graph_meta_tags'] == 'true' ? 'checked' : ''
2153 );
2154 printf(' <br> <span class="description"> Automatically add the Open Graph meta tags in a page or post.</span>');
2155 }
2156
2157 public function global_facebook_meta_callback()
2158 {
2159 printf(
2160 '<input type="checkbox" name="' . Metasync_Admin::option_key . '[common_meta_settings][facebook_meta_tags]" value="true" %s />',
2161 isset(Metasync::get_option('common_meta_settings')['facebook_meta_tags']) && Metasync::get_option('common_meta_settings')['facebook_meta_tags'] == 'true' ? 'checked' : ''
2162 );
2163 printf(' <br> <span class="description"> Automatically add the Facebook meta tags in a page or post.</span>');
2164 }
2165
2166 public function global_twitter_meta_callback()
2167 {
2168 printf(
2169 '<input type="checkbox" name="' . Metasync_Admin::option_key . '[common_meta_settings][twitter_meta_tags]" value="true" %s />',
2170 isset(Metasync::get_option('common_meta_settings')['twitter_meta_tags']) && Metasync::get_option('common_meta_settings')['twitter_meta_tags'] == 'true' ? 'checked' : ''
2171 );
2172 printf(' <br> <span class="description"> Automatically add the Twitter meta tags in a page or post.</span>');
2173 }
2174
2175 public function facebook_page_url_callback()
2176 {
2177 $facebook_page_url = Metasync::get_option('social_meta')['facebook_page_url'] ?? '';
2178 printf('<input type="text" name="' . Metasync_Admin::option_key . '[social_meta][facebook_page_url]" value="%s" size="50" />', esc_attr($facebook_page_url));
2179 printf('<br><span class="description"> Enter your Facebook page URL. eg: <code>https://www.facebook.com/MetaSync/</code> </span>');
2180 }
2181
2182 public function facebook_authorship_callback()
2183 {
2184 $facebook_authorship = Metasync::get_option('social_meta')['facebook_authorship'] ?? '';
2185 printf('<input type="text" name="' . Metasync_Admin::option_key . '[social_meta][facebook_authorship]" value="%s" size="50" />', esc_attr($facebook_authorship));
2186 printf('<br><span class="description"> Enter Facebook profile URL to show Facebook Authorship when your articles are being shared on Facebook. eg: <code>https://www.facebook.com/shahrukh/</code> </span>');
2187 }
2188
2189 public function facebook_admin_callback()
2190 {
2191 $facebook_admin = Metasync::get_option('social_meta')['facebook_admin'] ?? '';
2192 printf('<input type="text" name="' . Metasync_Admin::option_key . '[social_meta][facebook_admin]" value="%s" size="50" />', esc_attr($facebook_admin));
2193 printf(' <br> <span class="description"> Enter numeric user ID of Facebook. </span>');
2194 }
2195
2196 public function facebook_app_callback()
2197 {
2198 $facebook_app = Metasync::get_option('social_meta')['facebook_app'] ?? '';
2199 printf('<input type="text" name="' . Metasync_Admin::option_key . '[social_meta][facebook_app]" value="%s" size="50" />', esc_attr($facebook_app));
2200 printf(' <br> <span class="description"> Enter numeric app ID of Facebook </span>');
2201 }
2202
2203 public function facebook_secret_callback()
2204 {
2205 $facebook_secret = Metasync::get_option('social_meta')['facebook_secret'] ?? '';
2206 printf('<input type="text" name="' . Metasync_Admin::option_key . '[social_meta][facebook_secret]" value="%s" size="50" />', esc_attr($facebook_secret));
2207 printf(' <br> <span class="description"> Enter alphanumeric access token from Facebook. </span>');
2208 }
2209
2210 public function twitter_username_callback()
2211 {
2212 $twitter_username = Metasync::get_option('social_meta')['twitter_username'] ?? '';
2213 printf('<input type="text" name="' . Metasync_Admin::option_key . '[social_meta][twitter_username]" value="%s" size="50" />', esc_attr($twitter_username));
2214 printf(' <br> <span class="description"> Twitter username of the author to add <code>twitter:creator</code> tag to post. eg: <code>MetaSync</code> </span>');
2215 }
2216
2217 public static function get_business_types()
2218 {
2219 $business_type = [
2220 'Airline',
2221 'Consortium',
2222 'Corporation',
2223 'Educational Organization',
2224 'College Or University',
2225 'Elementary School',
2226 'High School',
2227 'Middle School',
2228 'Preschool',
2229 'School',
2230 'Funding Scheme',
2231 'Government Organization',
2232 'Library System',
2233 'Local Business',
2234 'Animal Shelter',
2235 'Archive Organization',
2236 'Automotive Business',
2237 'Auto Body Shop',
2238 'Auto Dealer',
2239 'Auto Parts Store',
2240 'Auto Rental',
2241 'Auto Repair',
2242 'Auto Wash',
2243 'Gas Station',
2244 'Motorcycle Dealer',
2245 'Motorcycle Repair',
2246 'Child Care',
2247 'Dry Cleaning Or Laundry',
2248 'Emergency Service',
2249 'Fire Station',
2250 'Hospital',
2251 'Police Station',
2252 'Employment Agency',
2253 'Entertainment Business',
2254 'Adult Entertainment',
2255 'Amusement Park',
2256 'Art Gallery',
2257 'Casino',
2258 'Comedy Club',
2259 'Movie Theater',
2260 'Night Club',
2261 'Financial Service',
2262 'Accounting Service',
2263 'Automated Teller',
2264 'Bank Or CreditUnion',
2265 'Insurance Agency',
2266 'Food Establishment',
2267 'Bakery',
2268 'Bar Or Pub',
2269 'Brewery',
2270 'Cafe Or CoffeeShop',
2271 'Distillery',
2272 'Fast Food Restaurant',
2273 'IceCream Shop',
2274 'Restaurant',
2275 'Winery',
2276 'Government Office',
2277 'Post Office',
2278 'Health And Beauty Business',
2279 'Beauty Salon',
2280 'Day Spa',
2281 'Hair Salon',
2282 'Health Club',
2283 'Nail Salon',
2284 'Tattoo Parlor',
2285 'Home And Construction Business',
2286 'Electrician',
2287 'General Contractor',
2288 'HVAC Business',
2289 'House Painter',
2290 'Locksmith',
2291 'Moving Company',
2292 'Plumber',
2293 'Roofing Contractor',
2294 'Internet Cafe',
2295 'Legal Service',
2296 'Attorney',
2297 'Notary',
2298 'Library',
2299 'Lodging Business',
2300 'Bed And Breakfast',
2301 'Campground',
2302 'Hostel',
2303 'Hotel',
2304 'Motel',
2305 'Resort',
2306 'Ski Resort',
2307 'Medical Business',
2308 'Community Health',
2309 'Dentist',
2310 'Dermatology',
2311 'Diet Nutrition',
2312 'Emergency',
2313 'Geriatric',
2314 'Gynecologic',
2315 'Medical Clinic',
2316 'Optician',
2317 'Pharmacy',
2318 'Physician',
2319 'Professional Service',
2320 'Radio Station',
2321 'Real Estate Agent',
2322 'Recycling Center',
2323 'Self Storage',
2324 'Shopping Center',
2325 'Sports Activity Location',
2326 'Bowling Alley',
2327 'Exercise Gym',
2328 'Golf Course',
2329 'Public Swimming Pool',
2330 'Ski Resort',
2331 'Sports Club',
2332 'Stadium Or Arena',
2333 'Tennis Complex',
2334 'Store',
2335 'Bike Store',
2336 'Book Store',
2337 'Clothing Store',
2338 'Computer Store',
2339 'Convenience Store',
2340 'Department Store',
2341 'Electronics Store',
2342 'Florist',
2343 'Furniture Store',
2344 'Garden Store',
2345 'Grocery Store',
2346 'Hardware Store',
2347 'Hobby Shop',
2348 'Home Goods Store',
2349 'Jewelry Store',
2350 'Liquor Store',
2351 'Mens Clothing Store',
2352 'Mobile Phone Store',
2353 'Movie Rental Store',
2354 'Music Store',
2355 'Office Equipment Store',
2356 'Outlet Store',
2357 'Pawn Shop',
2358 'Pet Store',
2359 'Shoe Store',
2360 'Sporting GoodsStore',
2361 'Tire Shop',
2362 'Toy Store',
2363 'Wholesale Store',
2364 'Television Station',
2365 'Tourist Information Center',
2366 'Travel Agency',
2367 'Tree Services',
2368 'Medical Organization',
2369 'Diagnostic Lab',
2370 'Veterinary Care',
2371 'NGO',
2372 'News Media Organization',
2373 'Performing Group',
2374 'Dance Group',
2375 'Music Group',
2376 'Theater Group',
2377 'Project',
2378 'Funding Agency',
2379 'Research Project',
2380 'Sports Organization',
2381 'Sports Team',
2382 'Workers Union',
2383 ];
2384
2385 return $business_type;
2386 }
2387 }
2388