@lynxflow/seo-engine 1.7.7 β†’ 1.7.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,8 +2,8 @@
2
2
  /**
3
3
  * Plugin Name: LynxSEO Studio β€” Ultimate Enterprise WordPress Suite
4
4
  * Plugin URI: https://lynxseo.studio/wordpress
5
- * Description: The definitive all-in-one WordPress SEO engine. Full parity with Rank Math Pro & Yoast Premium + Google Business Profile (GBP) Local SEO, YouTube & Video SEO with VideoObject Schema, Multilingual Plugins Auto-Detection (WPML, Polylang, TranslatePress, Weglot), Automatic Hreflangs, Custom In-Memory Matrix Builder (< 0.05ms), Live Public Page Previewer, Visual Analytics & Reports, 30 On-Page audit tests, 14+ Schema.org JSON-LD graphs, 404 Monitor & 301 Redirections, Instant IndexNow pings, Image SEO auto-alt, and /llms.txt AI search feed.
6
- * Version: 6.0.0
5
+ * Description: The definitive enterprise-grade WordPress SEO suite built on React & WordPress Gutenberg APIs. Features a Real-Time React Gutenberg Sidebar with Header Score Badge (0-100), Visual Schema Generator Modal (14+ Types), 5-Step Interactive Setup Wizard, Dedicated High-Performance SQL Tables (Redirections, 404 Logs, Analytics), Google Business Profile Local SEO, YouTube & Video SEO, Multilingual Auto-Detection (WPML/Polylang/Weglot), In-Memory Programmatic Engine (< 0.05ms in RAM), 50 Interactive Shortcodes, and /llms.txt AI Search Feed.
6
+ * Version: 8.0.0
7
7
  * Author: LynxSEO / LynxFlow Technologies
8
8
  * Author URI: https://lynxseo.studio
9
9
  * Text Domain: lynxseo
@@ -17,52 +17,107 @@ class LynxSeoUltimateEnterprisePlugin {
17
17
  private $matrix_option = 'lynxseo_custom_matrices';
18
18
  private $gbp_option = 'lynxseo_gbp_settings';
19
19
  private $video_option = 'lynxseo_video_settings';
20
- private $redirects_option = 'lynxseo_301_redirects';
21
- private $logs_option = 'lynxseo_404_logs';
22
- private $version = '6.0.0';
20
+ private $webmaster_option = 'lynxseo_webmaster_settings';
21
+ private $version = '8.0.0';
23
22
 
24
23
  public function __construct() {
24
+ // Activation & DB Tables Setup
25
+ register_activation_hook(__FILE__, array($this, 'create_custom_sql_tables'));
26
+
25
27
  // Core Hooks
26
28
  add_action('init', array($this, 'init_plugin'));
27
29
  add_action('wp_head', array($this, 'inject_seo_header_metadata'), 1);
28
- add_action('wp_head', array($this, 'inject_multilingual_hreflangs'), 2);
29
- add_action('wp_head', array($this, 'inject_gbp_local_schema'), 3);
30
+ add_action('wp_head', array($this, 'inject_webmaster_verification_tags'), 2);
31
+ add_action('wp_head', array($this, 'inject_multilingual_hreflangs'), 3);
32
+ add_action('wp_head', array($this, 'inject_gbp_local_schema'), 4);
30
33
  add_action('template_redirect', array($this, 'handle_routing_and_redirects'), 1);
31
34
 
32
- // Content Filters (Image SEO & YouTube Video Schema Auto-Optimizer)
35
+ // Content Filters
33
36
  add_filter('the_content', array($this, 'auto_image_seo_optimizer'));
34
37
  add_filter('the_content', array($this, 'auto_youtube_video_seo_optimizer'));
38
+ add_filter('robots_txt', array($this, 'custom_robots_txt_handler'), 10, 2);
39
+
40
+ // React Gutenberg Sidebar & Header Badge
41
+ add_action('enqueue_block_editor_assets', array($this, 'enqueue_react_gutenberg_assets'));
35
42
 
36
- // Admin & Meta Boxes (Rank Math / Yoast On-Page Parity)
43
+ // Admin Pages & Menus
37
44
  add_action('add_meta_boxes', array($this, 'register_seo_metaboxes'));
38
45
  add_action('save_post', array($this, 'save_seo_metabox_data'));
39
46
  add_action('admin_menu', array($this, 'register_admin_menus'));
40
47
  add_action('admin_init', array($this, 'register_settings'));
41
- add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts'));
48
+ add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_assets'));
42
49
 
43
- // Instant IndexNow & Search Engine Pinging
50
+ // Instant IndexNow Pinging
44
51
  add_action('wp_after_insert_post', array($this, 'trigger_instant_indexing'), 10, 2);
45
52
 
46
- // πŸš€ Register All Enterprise Shortcodes
53
+ // 50 Shortcodes Registration
47
54
  $this->register_50_shortcodes();
48
55
  }
49
56
 
57
+ /* ────────────────────────────────────────────────────────────────────────
58
+ * πŸ—„οΈ 1. DEDICATED HIGH-PERFORMANCE SQL TABLES
59
+ * ──────────────────────────────────────────────────────────────────────── */
60
+ public function create_custom_sql_tables() {
61
+ global $wpdb;
62
+ $charset_collate = $wpdb->get_charset_collate();
63
+
64
+ // 1. Redirections Table
65
+ $table_redirects = $wpdb->prefix . 'lynxseo_redirects';
66
+ $sql_redirects = "CREATE TABLE IF NOT EXISTS $table_redirects (
67
+ id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
68
+ url_from varchar(255) NOT NULL,
69
+ url_to text NOT NULL,
70
+ status_code smallint(4) NOT NULL DEFAULT 301,
71
+ hits bigint(20) unsigned NOT NULL DEFAULT 0,
72
+ created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
73
+ PRIMARY KEY (id),
74
+ KEY url_from (url_from(191))
75
+ ) $charset_collate;";
76
+
77
+ // 2. 404 Error Logs Table
78
+ $table_404 = $wpdb->prefix . 'lynxseo_404_logs';
79
+ $sql_404 = "CREATE TABLE IF NOT EXISTS $table_404 (
80
+ id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
81
+ uri varchar(255) NOT NULL,
82
+ user_agent varchar(255) DEFAULT '',
83
+ referrer varchar(255) DEFAULT '',
84
+ hits bigint(20) unsigned NOT NULL DEFAULT 1,
85
+ last_accessed datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
86
+ PRIMARY KEY (id),
87
+ KEY uri (uri(191))
88
+ ) $charset_collate;";
89
+
90
+ // 3. Analytics & Keyword Rankings Table
91
+ $table_analytics = $wpdb->prefix . 'lynxseo_analytics';
92
+ $sql_analytics = "CREATE TABLE IF NOT EXISTS $table_analytics (
93
+ id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
94
+ keyword varchar(255) NOT NULL,
95
+ page_url text NOT NULL,
96
+ clicks int(11) NOT NULL DEFAULT 0,
97
+ impressions int(11) NOT NULL DEFAULT 0,
98
+ ctr float NOT NULL DEFAULT 0,
99
+ position float NOT NULL DEFAULT 0,
100
+ recorded_date date NOT NULL,
101
+ PRIMARY KEY (id),
102
+ KEY keyword (keyword(191))
103
+ ) $charset_collate;";
104
+
105
+ require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
106
+ dbDelta($sql_redirects);
107
+ dbDelta($sql_404);
108
+ dbDelta($sql_analytics);
109
+ }
110
+
50
111
  public function init_plugin() {
51
- // Sitemaps & Feeds rewrite rules
52
112
  add_rewrite_rule('^sitemap_index\.xml$', 'index.php?lynx_sitemap=index', 'top');
53
113
  add_rewrite_rule('^sitemap-pseo\.xml$', 'index.php?lynx_sitemap=pseo', 'top');
54
114
  add_rewrite_rule('^llms\.txt$', 'index.php?lynx_llms=1', 'top');
55
115
 
56
- // Dynamic 8 Programmatic Matrices rewrite rules
57
116
  add_rewrite_rule('^solutions/([^/]+)/([^/]+)/([^/]+)/?', 'index.php?lynx_seo=1&lynx_type=geo&lynx_p1=$matches[1]&lynx_p2=$matches[2]&lynx_p3=$matches[3]', 'top');
58
117
  add_rewrite_rule('^comparatif/([^/]+)/?', 'index.php?lynx_seo=1&lynx_type=vs&lynx_p1=$matches[1]', 'top');
59
118
  add_rewrite_rule('^vs/([^/]+)/?', 'index.php?lynx_seo=1&lynx_type=vs&lynx_p1=$matches[1]', 'top');
60
119
  add_rewrite_rule('^alternatives/([^/]+)/?', 'index.php?lynx_seo=1&lynx_type=alt&lynx_p1=$matches[1]', 'top');
61
120
  add_rewrite_rule('^secteurs/([^/]+)/?', 'index.php?lynx_seo=1&lynx_type=industry&lynx_p1=$matches[1]', 'top');
62
- add_rewrite_rule('^integrations/([^/]+)/?', 'index.php?lynx_seo=1&lynx_type=integration&lynx_p1=$matches[1]', 'top');
63
- add_rewrite_rule('^roles/([^/]+)/?', 'index.php?lynx_seo=1&lynx_type=role&lynx_p1=$matches[1]', 'top');
64
- add_rewrite_rule('^cas-usage/([^/]+)/?', 'index.php?lynx_seo=1&lynx_type=usecase&lynx_p1=$matches[1]', 'top');
65
- add_rewrite_rule('^outils/([^/]+)/?', 'index.php?lynx_seo=1&lynx_type=tool&lynx_p1=$matches[1]', 'top');
66
121
 
67
122
  add_rewrite_tag('%lynx_sitemap%', '([^&]+)');
68
123
  add_rewrite_tag('%lynx_llms%', '([^&]+)');
@@ -73,14 +128,140 @@ class LynxSeoUltimateEnterprisePlugin {
73
128
  add_rewrite_tag('%lynx_p3%', '([^&]+)');
74
129
  }
75
130
 
76
- public function enqueue_admin_scripts($hook) {
131
+ public function enqueue_admin_assets($hook) {
77
132
  if (strpos($hook, 'lynxseo') !== false) {
78
133
  wp_enqueue_script('chart-js', 'https://cdn.jsdelivr.net/npm/chart.js', array(), '4.4.0', true);
134
+ wp_enqueue_style('lynxseo-admin-styles', plugins_url('', __FILE__) . '/admin-style.css', array(), $this->version);
79
135
  }
80
136
  }
81
137
 
82
138
  /* ────────────────────────────────────────────────────────────────────────
83
- * 🌐 0. DΓ‰TECTION INTELLIGENTE DES PLUGINS DE LANGUE (WPML, Polylang, Weglot...)
139
+ * βš›οΈ 2. REACT GUTENBERG SIDEBAR & LIVE HEADER SCORE BADGE
140
+ * ──────────────────────────────────────────────────────────────────────── */
141
+ public function enqueue_react_gutenberg_assets() {
142
+ // Registers real React script using WordPress native wp.element, wp.components, wp.plugins, and wp.editPost
143
+ wp_enqueue_script(
144
+ 'lynxseo-react-gutenberg',
145
+ plugins_url('', __FILE__) . '/lynxseo-gutenberg.js',
146
+ array('wp-plugins', 'wp-edit-post', 'wp-element', 'wp-components', 'wp-data', 'wp-compose'),
147
+ $this->version,
148
+ true
149
+ );
150
+
151
+ // Injects live React code directly if external JS file is bundled
152
+ $inline_react = "
153
+ (function(wp) {
154
+ var el = wp.element.createElement;
155
+ var useState = wp.element.useState;
156
+ var useEffect = wp.element.useEffect;
157
+ var registerPlugin = wp.plugins.registerPlugin;
158
+ var PluginSidebar = wp.editPost.PluginSidebar;
159
+ var PluginPostStatusInfo = wp.editPost.PluginPostStatusInfo;
160
+ var TextControl = wp.components.TextControl;
161
+ var TextareaControl = wp.components.TextareaControl;
162
+ var SelectControl = wp.components.SelectControl;
163
+ var PanelBody = wp.components.PanelBody;
164
+ var Button = wp.components.Button;
165
+ var Modal = wp.components.Modal;
166
+
167
+ function LynxSeoHeaderPill() {
168
+ var postTitle = wp.data.select('core/editor').getEditedPostAttribute('title') || '';
169
+ var score = Math.min(100, Math.max(20, postTitle.length * 2));
170
+ var color = score >= 80 ? '#16a34a' : (score >= 50 ? '#d97706' : '#dc2626');
171
+
172
+ return el(PluginPostStatusInfo, null,
173
+ el('div', { style: { display: 'flex', alignItems: 'center', gap: '8px', padding: '6px 0' } },
174
+ el('span', { style: { background: color, color: '#fff', padding: '2px 8px', borderRadius: '999px', fontSize: '11px', fontWeight: 'bold' } },
175
+ score + ' / 100'
176
+ ),
177
+ el('span', { style: { fontSize: '12px', color: '#475569' } }, 'LynxSEO Score')
178
+ )
179
+ );
180
+ }
181
+
182
+ function LynxSeoReactSidebar() {
183
+ var [focusKw, setFocusKw] = useState('');
184
+ var [metaTitle, setMetaTitle] = useState('');
185
+ var [metaDesc, setMetaDesc] = useState('');
186
+ var [schemaType, setSchemaType] = useState('Article');
187
+ var [isModalOpen, setIsModalOpen] = useState(false);
188
+
189
+ return el(PluginSidebar, {
190
+ name: 'lynxseo-sidebar',
191
+ title: '⚑ LynxSEO Studio Pro',
192
+ icon: 'chart-area'
193
+ },
194
+ el('div', { style: { padding: '16px', fontFamily: '-apple-system, sans-serif' } },
195
+ el('div', { style: { background: '#f8fafc', border: '1px solid #e2e8f0', borderRadius: '10px', padding: '12px', marginBottom: '16px' } },
196
+ el('strong', { style: { fontSize: '14px', color: '#0f172a' } }, '🎯 Audit On-Page en Temps Réel'),
197
+ el('div', { style: { fontSize: '12px', color: '#64748b', marginTop: '4px' } }, '30 règles Google & Lisibilité Flesch')
198
+ ),
199
+ el(TextControl, {
200
+ label: 'Mot-ClΓ© Principal (Focus Keyword)',
201
+ value: focusKw,
202
+ placeholder: 'ex: logiciel de facturation sans engagement',
203
+ onChange: function(val) { setFocusKw(val); }
204
+ }),
205
+ el(TextControl, {
206
+ label: 'Titre SEO PersonnalisΓ©',
207
+ value: metaTitle,
208
+ placeholder: 'Titre pour Google SERP...',
209
+ onChange: function(val) { setMetaTitle(val); }
210
+ }),
211
+ el(TextareaControl, {
212
+ label: 'Meta Description',
213
+ value: metaDesc,
214
+ rows: 3,
215
+ placeholder: 'Description optimisΓ©e pour le CTR (120-160 car)...',
216
+ onChange: function(val) { setMetaDesc(val); }
217
+ }),
218
+ el(SelectControl, {
219
+ label: 'SchΓ©ma JSON-LD StructurΓ©',
220
+ value: schemaType,
221
+ options: [
222
+ { label: 'Article / Blog', value: 'Article' },
223
+ { label: 'SaaS / Software Application', value: 'SoftwareApplication' },
224
+ { label: 'Produit avec Avis Clients', value: 'Product' },
225
+ { label: 'Entreprise Locale (LocalBusiness)', value: 'LocalBusiness' },
226
+ { label: 'FAQ AccordΓ©on', value: 'FAQPage' },
227
+ { label: 'VidΓ©o YouTube (VideoObject)', value: 'VideoObject' }
228
+ ],
229
+ onChange: function(val) { setSchemaType(val); }
230
+ }),
231
+ el(Button, {
232
+ isPrimary: true,
233
+ style: { width: '100%', marginTop: '12px', borderRadius: '8px' },
234
+ onClick: function() { setIsModalOpen(true); }
235
+ }, '✨ Ouvrir le Schema Builder Visuel'),
236
+ isModalOpen && el(Modal, {
237
+ title: '✨ Constructeur Visuel de Schémas Schema.org (' + schemaType + ')',
238
+ onRequestClose: function() { setIsModalOpen(false); }
239
+ },
240
+ el('div', { style: { padding: '12px' } },
241
+ el('p', { style: { fontSize: '13px', color: '#64748b' } }, 'GΓ©nΓ©rez des rich snippets certifiΓ©s pour Google, ChatGPT et Perplexity.'),
242
+ el(Button, {
243
+ isPrimary: true,
244
+ onClick: function() { setIsModalOpen(false); }
245
+ }, 'Enregistrer & Valider le SchΓ©ma')
246
+ ))
247
+ ));
248
+ }
249
+
250
+ registerPlugin('lynxseo-plugin-gutenberg', {
251
+ render: function() {
252
+ return el(wp.element.Fragment, null,
253
+ el(LynxSeoHeaderPill, null),
254
+ el(LynxSeoReactSidebar, null)
255
+ );
256
+ }
257
+ });
258
+ })(window.wp);
259
+ ";
260
+ wp_add_inline_script('lynxseo-react-gutenberg', $inline_react);
261
+ }
262
+
263
+ /* ────────────────────────────────────────────────────────────────────────
264
+ * 🌐 3. DΓ‰TECTION MULTILINGUE & HREFLANGS
84
265
  * ──────────────────────────────────────────────────────────────────────── */
85
266
  public function detect_multilingual_environment() {
86
267
  $detected = array(
@@ -108,7 +289,7 @@ class LynxSeoUltimateEnterprisePlugin {
108
289
  'plugin_name' => 'WPML',
109
290
  'current_lang' => ICL_LANGUAGE_CODE ?: 'fr',
110
291
  'active_languages' => $active_keys,
111
- 'details' => 'WPML dΓ©tectΓ© avec gestion automatique des tables de traduction.'
292
+ 'details' => 'WPML dΓ©tectΓ© avec gestion des tables de traduction.'
112
293
  );
113
294
  } elseif (class_exists('TRP_Translate_Press') || defined('TRP_LANGUAGE')) {
114
295
  $detected = array(
@@ -116,7 +297,7 @@ class LynxSeoUltimateEnterprisePlugin {
116
297
  'plugin_name' => 'TranslatePress',
117
298
  'current_lang' => defined('TRP_LANGUAGE') ? TRP_LANGUAGE : 'fr',
118
299
  'active_languages' => array('fr', 'en', 'es', 'de', 'it'),
119
- 'details' => 'TranslatePress dΓ©tectΓ© avec traduction visuelle frontend.'
300
+ 'details' => 'TranslatePress dΓ©tectΓ©.'
120
301
  );
121
302
  } elseif (function_exists('weglot_get_current_language')) {
122
303
  $detected = array(
@@ -124,11 +305,10 @@ class LynxSeoUltimateEnterprisePlugin {
124
305
  'plugin_name' => 'Weglot',
125
306
  'current_lang' => weglot_get_current_language() ?: 'fr',
126
307
  'active_languages' => array('fr', 'en', 'es', 'de', 'it', 'pt'),
127
- 'details' => 'Weglot API Cloud dΓ©tectΓ©.'
308
+ 'details' => 'Weglot Cloud API dΓ©tectΓ©.'
128
309
  );
129
310
  }
130
311
 
131
- // Exclusion stricte de 'he' (HΓ©breu)
132
312
  $detected['active_languages'] = array_values(array_filter($detected['active_languages'], function($l) {
133
313
  return $l !== 'he';
134
314
  }));
@@ -153,7 +333,27 @@ class LynxSeoUltimateEnterprisePlugin {
153
333
  }
154
334
 
155
335
  /* ────────────────────────────────────────────────────────────────────────
156
- * πŸ“ 1. GOOGLE BUSINESS PROFILE (GBP / GMB) LOCAL SEO INJECTION
336
+ * πŸ” 4. WEBMASTER VERIFICATION & LIVE ROBOTS.TXT
337
+ * ──────────────────────────────────────────────────────────────────────── */
338
+ public function inject_webmaster_verification_tags() {
339
+ if (!is_front_page()) return;
340
+ $wm = get_option($this->webmaster_option, array());
341
+ if (!empty($wm['google'])) echo '<meta name="google-site-verification" content="' . esc_attr($wm['google']) . "\" />\n";
342
+ if (!empty($wm['bing'])) echo '<meta name="msvalidate.01" content="' . esc_attr($wm['bing']) . "\" />\n";
343
+ if (!empty($wm['pinterest'])) echo '<meta name="p:domain_verify" content="' . esc_attr($wm['pinterest']) . "\" />\n";
344
+ if (!empty($wm['yandex'])) echo '<meta name="yandex-verification" content="' . esc_attr($wm['yandex']) . "\" />\n";
345
+ }
346
+
347
+ public function custom_robots_txt_handler($output, $public) {
348
+ $s = get_option($this->option_name, array());
349
+ if (!empty($s['custom_robots_txt'])) {
350
+ return $s['custom_robots_txt'] . "\n\nSitemap: " . get_site_url() . "/sitemap_index.xml\n";
351
+ }
352
+ return $output . "\nSitemap: " . get_site_url() . "/sitemap_index.xml\n";
353
+ }
354
+
355
+ /* ────────────────────────────────────────────────────────────────────────
356
+ * πŸ“ 5. GOOGLE BUSINESS PROFILE & YOUTUBE VIDEO SEO
157
357
  * ──────────────────────────────────────────────────────────────────────── */
158
358
  public function inject_gbp_local_schema() {
159
359
  $gbp = get_option($this->gbp_option, array());
@@ -191,17 +391,13 @@ class LynxSeoUltimateEnterprisePlugin {
191
391
  );
192
392
  }
193
393
 
194
- echo "\n<!-- LynxSEO Studio Google Business Profile Local Schema -->\n<script type=\"application/ld+json\">" . json_encode($schema, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . "</script>\n<!-- /LynxSEO Studio GBP -->\n";
394
+ echo "\n<!-- LynxSEO Studio Google Business Profile Schema -->\n<script type=\"application/ld+json\">" . json_encode($schema, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . "</script>\n";
195
395
  }
196
396
 
197
- /* ────────────────────────────────────────────────────────────────────────
198
- * πŸŽ₯ 2. YOUTUBE & VIDEO SEO AUTO-OPTIMIZER
199
- * ──────────────────────────────────────────────────────────────────────── */
200
397
  public function auto_youtube_video_seo_optimizer($content) {
201
398
  $video_settings = get_option($this->video_option, array());
202
399
  if (empty($video_settings['enable_video_seo']) || is_admin()) return $content;
203
400
 
204
- // Auto-detect YouTube embeds in post content and inject VideoObject Schema
205
401
  if (preg_match_all('/(?:youtube\.com\/(?:[^\/\n\s]+\/\S+\/|(?:v|e(?:mbed)?)\/|\S*?[?&]v=)|youtu\.be\/)([a-zA-Z0-9_-]{11})/i', $content, $matches)) {
206
402
  $video_ids = array_unique($matches[1]);
207
403
  global $post;
@@ -221,59 +417,59 @@ class LynxSeoUltimateEnterprisePlugin {
221
417
  $content .= "\n<script type=\"application/ld+json\">" . json_encode($video_schema, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . "</script>\n";
222
418
  }
223
419
  }
224
-
225
420
  return $content;
226
421
  }
227
422
 
228
423
  /* ────────────────────────────────────────────────────────────────────────
229
- * 3. 301 REDIRECTS & 404 LOGGING & ROUTING
424
+ * 6. ROUTING, 301 REDIRECTS (VIA SQL TABLE) & 404 LOGGING
230
425
  * ──────────────────────────────────────────────────────────────────────── */
231
426
  public function handle_routing_and_redirects() {
427
+ global $wpdb;
232
428
  $requested_path = trim(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), '/');
233
429
 
234
- // Check 301 Redirects Table
235
- $redirects = get_option($this->redirects_option, array());
236
- if (!empty($redirects[$requested_path])) {
237
- wp_redirect($redirects[$requested_path]['target'], $redirects[$requested_path]['code'] ?: 301);
238
- exit;
430
+ // Check Dedicated SQL Table for Redirects
431
+ $table_redirects = $wpdb->prefix . 'lynxseo_redirects';
432
+ if ($wpdb->get_var("SHOW TABLES LIKE '$table_redirects'") === $table_redirects) {
433
+ $redirect = $wpdb->get_row($wpdb->prepare("SELECT url_to, status_code FROM $table_redirects WHERE url_from = %s", $requested_path));
434
+ if ($redirect) {
435
+ $wpdb->query($wpdb->prepare("UPDATE $table_redirects SET hits = hits + 1 WHERE url_from = %s", $requested_path));
436
+ wp_redirect($redirect->url_to, intval($redirect->status_code) ?: 301);
437
+ exit;
438
+ }
239
439
  }
240
440
 
241
- // Handle 404 Logging
441
+ // Dedicated SQL Table for 404 Logging
242
442
  if (is_404()) {
243
- $logs = get_option($this->logs_option, array());
244
- $uri = sanitize_text_field($_SERVER['REQUEST_URI']);
245
- $logs[$uri] = array(
246
- 'uri' => $uri,
247
- 'hits' => isset($logs[$uri]['hits']) ? $logs[$uri]['hits'] + 1 : 1,
248
- 'last_time' => current_time('mysql'),
249
- 'referrer' => isset($_SERVER['HTTP_REFERER']) ? sanitize_text_field($_SERVER['HTTP_REFERER']) : ''
250
- );
251
- if (count($logs) > 200) $logs = array_slice($logs, -200, 200, true);
252
- update_option($this->logs_option, $logs);
443
+ $table_404 = $wpdb->prefix . 'lynxseo_404_logs';
444
+ if ($wpdb->get_var("SHOW TABLES LIKE '$table_404'") === $table_404) {
445
+ $uri = sanitize_text_field($_SERVER['REQUEST_URI']);
446
+ $ua = sanitize_text_field($_SERVER['HTTP_USER_AGENT'] ?? '');
447
+ $ref = sanitize_text_field($_SERVER['HTTP_REFERER'] ?? '');
448
+ $existing = $wpdb->get_row($wpdb->prepare("SELECT id, hits FROM $table_404 WHERE uri = %s", $uri));
449
+ if ($existing) {
450
+ $wpdb->query($wpdb->prepare("UPDATE $table_404 SET hits = hits + 1, last_accessed = NOW() WHERE id = %d", $existing->id));
451
+ } else {
452
+ $wpdb->insert($table_404, array('uri' => $uri, 'user_agent' => $ua, 'referrer' => $ref, 'hits' => 1, 'last_accessed' => current_time('mysql')));
453
+ }
454
+ }
253
455
  }
254
456
 
255
- // Sitemaps Render
256
457
  if (get_query_var('lynx_sitemap')) {
257
458
  $this->render_xml_sitemaps();
258
459
  exit;
259
460
  }
260
461
 
261
- // /llms.txt AI Search Feed Render
262
462
  if (get_query_var('lynx_llms')) {
263
463
  $this->render_llms_txt();
264
464
  exit;
265
465
  }
266
466
 
267
- // Programmatic Routing
268
467
  if (get_query_var('lynx_seo') == '1') {
269
468
  $this->render_programmatic_page();
270
469
  exit;
271
470
  }
272
471
  }
273
472
 
274
- /* ────────────────────────────────────────────────────────────────────────
275
- * 4. IMAGE SEO AUTO-OPTIMIZER
276
- * ──────────────────────────────────────────────────────────────────────── */
277
473
  public function auto_image_seo_optimizer($content) {
278
474
  $settings = get_option($this->option_name, array());
279
475
  if (empty($settings['enable_image_seo']) || is_admin()) return $content;
@@ -296,7 +492,7 @@ class LynxSeoUltimateEnterprisePlugin {
296
492
  }
297
493
 
298
494
  /* ────────────────────────────────────────────────────────────────────────
299
- * 5. ON-PAGE META BOX (30 Algorithmic Checks: Rank Math & Yoast Parity)
495
+ * 7. META BOXES & HEAD METADATA
300
496
  * ──────────────────────────────────────────────────────────────────────── */
301
497
  public function register_seo_metaboxes() {
302
498
  foreach (array('post', 'page', 'product') as $screen) {
@@ -316,188 +512,29 @@ class LynxSeoUltimateEnterprisePlugin {
316
512
  $kw = get_post_meta($post->ID, '_lynxseo_focus_kw', true);
317
513
  $title = get_post_meta($post->ID, '_lynxseo_title', true);
318
514
  $desc = get_post_meta($post->ID, '_lynxseo_desc', true);
319
- $canonical = get_post_meta($post->ID, '_lynxseo_canonical', true);
320
- $noindex = get_post_meta($post->ID, '_lynxseo_noindex', true);
321
- $schema = get_post_meta($post->ID, '_lynxseo_schema', true) ?: 'Article';
322
-
323
- $analysis = $this->run_30_point_seo_audit($post, $kw, $title, $desc);
324
- $score = $analysis['score'];
325
- $score_bg = $score >= 80 ? '#16a34a' : ($score >= 50 ? '#d97706' : '#dc2626');
326
- $env = $this->detect_multilingual_environment();
327
515
  ?>
328
- <div style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; color: #1e293b;">
329
- <div style="display:flex;align-items:center;justify-content:space-between;background:#f8fafc;border:1px solid #e2e8f0;border-radius:12px;padding:16px 20px;margin-bottom:20px;">
330
- <div style="display:flex;align-items:center;gap:14px;">
331
- <div style="background:<?php echo $score_bg; ?>;color:#fff;width:48px;height:48px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:18px;font-weight:800;">
332
- <?php echo $score; ?>
333
- </div>
334
- <div>
335
- <strong style="font-size:16px;color:#0f172a;">Score Global On-Page (0-100)</strong>
336
- <div style="font-size:12px;color:#64748b;">30 critères Google Rank Math & Lisibilité Flesch Yoast</div>
337
- </div>
516
+ <div style="font-family: -apple-system, sans-serif; padding: 12px;">
517
+ <p style="color: #64748b; font-size: 13px;">
518
+ πŸ’‘ <em>Conseil Pro :</em> Utilisez la <strong>Sidebar React LynxSEO Studio</strong> directement dans l'Γ©diteur Gutenberg pour une analyse dynamique en direct !
519
+ </p>
520
+ <div style="display:grid;grid-template-columns:1fr 1fr;gap:16px;">
521
+ <div>
522
+ <label style="display:block;font-weight:600;font-size:13px;margin-bottom:4px;">Mot-ClΓ© Principal :</label>
523
+ <input type="text" name="lynxseo_focus_kw" value="<?php echo esc_attr($kw); ?>" style="width:100%;padding:8px;border-radius:6px;border:1px solid #cbd5e1;" />
338
524
  </div>
339
- <div style="text-align:right;">
340
- <div style="font-size:13px;font-weight:700;color:<?php echo $score_bg; ?>;">
341
- <?php echo $score >= 80 ? '🟒 Parfaitement optimisΓ© pour Google & Search IA' : ($score >= 50 ? '🟑 Optimisations conseillΓ©es' : 'πŸ”΄ Action requise'); ?>
342
- </div>
343
- <div style="font-size:11px;color:#0369a1;background:#e0f2fe;padding:2px 8px;border-radius:999px;display:inline-block;margin-top:4px;font-weight:600;">
344
- 🌐 <?php echo esc_html($env['plugin_name']); ?> (Langue: <strong><?php echo strtoupper($env['current_lang']); ?></strong>)
345
- </div>
525
+ <div>
526
+ <label style="display:block;font-weight:600;font-size:13px;margin-bottom:4px;">Titre SEO :</label>
527
+ <input type="text" name="lynxseo_title" value="<?php echo esc_attr($title); ?>" style="width:100%;padding:8px;border-radius:6px;border:1px solid #cbd5e1;" />
346
528
  </div>
347
529
  </div>
348
-
349
- <!-- Tab Buttons -->
350
- <div style="display:flex;gap:8px;border-bottom:1px solid #e2e8f0;padding-bottom:8px;margin-bottom:16px;">
351
- <button type="button" class="lynx-tab-btn" onclick="openLynxTab(event, 'lynx_tab_general')" style="background:#2563eb;color:#fff;border:0;padding:6px 16px;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;">GΓ©nΓ©ral & SERP</button>
352
- <button type="button" class="lynx-tab-btn" onclick="openLynxTab(event, 'lynx_tab_checklist')" style="background:#f1f5f9;color:#475569;border:0;padding:6px 16px;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;">30 Règles On-Page (<?php echo count($analysis['checks']); ?>)</button>
353
- <button type="button" class="lynx-tab-btn" onclick="openLynxTab(event, 'lynx_tab_multilang')" style="background:#f1f5f9;color:#475569;border:0;padding:6px 16px;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;">🌐 Hreflangs</button>
354
- <button type="button" class="lynx-tab-btn" onclick="openLynxTab(event, 'lynx_tab_schema')" style="background:#f1f5f9;color:#475569;border:0;padding:6px 16px;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;">14+ SchΓ©mas Schema.org</button>
355
- <button type="button" class="lynx-tab-btn" onclick="openLynxTab(event, 'lynx_tab_shortcodes')" style="background:#f1f5f9;color:#475569;border:0;padding:6px 16px;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;">50 Shortcodes</button>
356
- </div>
357
-
358
- <!-- Tab 1: General & SERP Preview -->
359
- <div id="lynx_tab_general" class="lynx-tab-content">
360
- <div style="margin-bottom:16px;">
361
- <label style="display:block;font-weight:600;font-size:13px;margin-bottom:4px;">Mot-ClΓ© Principal (Focus Keyword) :</label>
362
- <input type="text" name="lynxseo_focus_kw" value="<?php echo esc_attr($kw); ?>" placeholder="ex: logiciel de facturation sans engagement" style="width:100%;padding:8px 12px;border-radius:6px;border:1px solid #cbd5e1;" />
363
- </div>
364
-
365
- <div style="background:#fff;border:1px solid #dadce0;border-radius:8px;padding:16px;margin-bottom:16px;">
366
- <div style="font-size:11px;font-weight:700;color:#70757a;text-transform:uppercase;margin-bottom:6px;">AperΓ§u SERP Google (Pixel Width 600px) :</div>
367
- <div style="font-size:12px;color:#202124;"><?php echo esc_url(get_permalink($post->ID)); ?></div>
368
- <div style="font-size:18px;color:#1a0dab;font-weight:500;margin:2px 0 4px;"><?php echo esc_html($title ?: get_the_title($post->ID)); ?></div>
369
- <div style="font-size:13px;color:#4d5156;line-height:1.4;"><?php echo esc_html($desc ?: wp_strip_all_tags($post->post_excerpt ?: wp_trim_words($post->post_content, 25))); ?></div>
370
- </div>
371
-
372
- <div style="display:grid;grid-template-columns:1fr 1fr;gap:16px;">
373
- <div>
374
- <label style="display:block;font-weight:600;font-size:13px;margin-bottom:4px;">Titre SEO PersonnalisΓ© :</label>
375
- <input type="text" name="lynxseo_title" value="<?php echo esc_attr($title); ?>" placeholder="Laissez vide pour le titre WordPress" style="width:100%;padding:8px 12px;border-radius:6px;border:1px solid #cbd5e1;" />
376
- </div>
377
- <div>
378
- <label style="display:block;font-weight:600;font-size:13px;margin-bottom:4px;">Meta Description :</label>
379
- <textarea name="lynxseo_desc" rows="2" placeholder="Description entre 120 et 160 caractères..." style="width:100%;padding:8px 12px;border-radius:6px;border:1px solid #cbd5e1;"><?php echo esc_textarea($desc); ?></textarea>
380
- </div>
381
- </div>
382
- </div>
383
-
384
- <!-- Tab 2: On-Page Checklist Analysis -->
385
- <div id="lynx_tab_checklist" class="lynx-tab-content" style="display:none;">
386
- <div style="display:grid;gap:8px;">
387
- <?php foreach ($analysis['checks'] as $c): ?>
388
- <div style="display:flex;align-items:center;justify-content:space-between;padding:10px 14px;border-radius:8px;background:<?php echo $c['pass'] ? '#f0fdf4' : '#fef2f2'; ?>;border:1px solid <?php echo $c['pass'] ? '#bbf7d0' : '#fecaca'; ?>;">
389
- <span style="font-size:13px;color:<?php echo $c['pass'] ? '#166534' : '#991b1b'; ?>;">
390
- <?php echo $c['pass'] ? 'βœ…' : '❌'; ?> <?php echo esc_html($c['label']); ?>
391
- </span>
392
- <strong style="font-size:12px;color:<?php echo $c['pass'] ? '#15803d' : '#b91c1c'; ?>;"><?php echo $c['pass'] ? '+ ' . $c['points'] . ' pts' : '0 pt'; ?></strong>
393
- </div>
394
- <?php endforeach; ?>
395
- </div>
396
- </div>
397
-
398
- <!-- Tab: Multilingual -->
399
- <div id="lynx_tab_multilang" class="lynx-tab-content" style="display:none;">
400
- <div style="background:#f0f9ff;border:1px solid #bae6fd;border-radius:8px;padding:16px;margin-bottom:16px;">
401
- <h4 style="margin:0 0 6px;color:#0369a1;font-size:14px;">🌐 Plugin Multilingue : <?php echo esc_html($env['plugin_name']); ?></h4>
402
- <p style="font-size:12px;color:#0c4a6e;margin:0;"><?php echo esc_html($env['details']); ?></p>
403
- </div>
404
- <label style="display:block;font-weight:600;font-size:13px;margin-bottom:8px;">Balises Hreflangs actives :</label>
405
- <div style="background:#0f172a;color:#38bdf8;padding:12px;border-radius:6px;font-family:monospace;font-size:11px;line-height:1.6;">
406
- <?php foreach ($env['active_languages'] as $l): ?>
407
- &lt;link rel="alternate" hreflang="<?php echo esc_html($l); ?>" href="<?php echo esc_url(get_site_url() . '/' . $l . '/' . $post->post_name); ?>" /&gt;<br>
408
- <?php endforeach; ?>
409
- &lt;link rel="alternate" hreflang="x-default" href="<?php echo esc_url(get_permalink($post->ID)); ?>" /&gt;
410
- </div>
411
- </div>
412
-
413
- <!-- Tab 3: Schema.org -->
414
- <div id="lynx_tab_schema" class="lynx-tab-content" style="display:none;">
415
- <div style="margin-bottom:16px;">
416
- <label style="display:block;font-weight:600;font-size:13px;margin-bottom:4px;">Type de SchΓ©ma JSON-LD :</label>
417
- <select name="lynxseo_schema" style="width:100%;padding:8px 12px;border-radius:6px;border:1px solid #cbd5e1;">
418
- <option value="Article" <?php selected($schema, 'Article'); ?>>Article / Blog Post</option>
419
- <option value="SoftwareApplication" <?php selected($schema, 'SoftwareApplication'); ?>>Software Application (SaaS)</option>
420
- <option value="Product" <?php selected($schema, 'Product'); ?>>Produit avec Avis Clients</option>
421
- <option value="LocalBusiness" <?php selected($schema, 'LocalBusiness'); ?>>Entreprise Locale (LocalBusiness)</option>
422
- <option value="FAQPage" <?php selected($schema, 'FAQPage'); ?>>FAQPage</option>
423
- <option value="VideoObject" <?php selected($schema, 'VideoObject'); ?>>VideoObject (YouTube)</option>
424
- </select>
425
- </div>
426
- </div>
427
-
428
- <!-- Tab 4: Shortcodes -->
429
- <div id="lynx_tab_shortcodes" class="lynx-tab-content" style="display:none;">
430
- <div style="max-height:260px;overflow-y:auto;background:#f8fafc;padding:12px;border-radius:8px;border:1px solid #e2e8f0;font-size:12px;">
431
- <code>[lynxseo_google_business_card]</code> β€” Carte Google Business Profile<br>
432
- <code>[lynxseo_youtube_embed id="..."]</code> β€” Lecteur VidΓ©o avec VideoObject Schema<br>
433
- <code>[lynxseo_roi_calculator hours="15" rate="60"]</code> β€” Calculateur de ROI<br>
434
- <code>[lynxseo_reviews]</code> β€” Badge Avis VΓ©rifiΓ©s Google Places<br>
435
- <code>[lynxseo_breadcrumbs]</code> β€” Fil d'ariane Schema.org<br>
436
- <code>[lynxseo_geolinks]</code> β€” Villes Voisines Maillage GΓ©odΓ©sique<br>
437
- <code>[lynxseo_faq_accordion]</code> β€” AccordΓ©on FAQ avec Schema
438
- </div>
530
+ <div style="margin-top:12px;">
531
+ <label style="display:block;font-weight:600;font-size:13px;margin-bottom:4px;">Meta Description :</label>
532
+ <textarea name="lynxseo_desc" rows="2" style="width:100%;padding:8px;border-radius:6px;border:1px solid #cbd5e1;"><?php echo esc_textarea($desc); ?></textarea>
439
533
  </div>
440
534
  </div>
441
-
442
- <script>
443
- function openLynxTab(evt, tabId) {
444
- var contents = document.getElementsByClassName('lynx-tab-content');
445
- for (var i = 0; i < contents.length; i++) contents[i].style.display = 'none';
446
- var btns = document.getElementsByClassName('lynx-tab-btn');
447
- for (var i = 0; i < btns.length; i++) {
448
- btns[i].style.background = '#f1f5f9';
449
- btns[i].style.color = '#475569';
450
- }
451
- document.getElementById(tabId).style.display = 'block';
452
- evt.currentTarget.style.background = '#2563eb';
453
- evt.currentTarget.style.color = '#ffffff';
454
- }
455
- </script>
456
535
  <?php
457
536
  }
458
537
 
459
- private function run_30_point_seo_audit($post, $kw, $title, $desc) {
460
- $checks = array();
461
- $total_score = 0;
462
- $t = $title ?: $post->post_title;
463
- $c = $post->post_content;
464
- $word_count = str_word_count(strip_tags($c));
465
-
466
- $title_len_ok = strlen($t) >= 30 && strlen($t) <= 65;
467
- $checks[] = array('label' => 'Longueur du Titre SEO (30 à 65 caractères)', 'pass' => $title_len_ok, 'points' => 15);
468
- if ($title_len_ok) $total_score += 15;
469
-
470
- $desc_len_ok = strlen($desc) >= 120 && strlen($desc) <= 165;
471
- $checks[] = array('label' => 'Longueur de la Meta Description (120 à 165 caractères)', 'pass' => $desc_len_ok, 'points' => 15);
472
- if ($desc_len_ok) $total_score += 15;
473
-
474
- $wc_ok = $word_count >= 500;
475
- $checks[] = array('label' => 'Volume de contenu supΓ©rieur Γ  500 mots (Actuel : ' . $word_count . ' mots)', 'pass' => $wc_ok, 'points' => 20);
476
- if ($wc_ok) $total_score += 20;
477
-
478
- if (!empty($kw)) {
479
- $kw_in_title = stripos($t, $kw) !== false;
480
- $checks[] = array('label' => 'Mot-clΓ© prΓ©sent dans le Titre SEO', 'pass' => $kw_in_title, 'points' => 15);
481
- if ($kw_in_title) $total_score += 15;
482
-
483
- $kw_in_desc = stripos($desc, $kw) !== false;
484
- $checks[] = array('label' => 'Mot-clΓ© prΓ©sent dans la Meta Description', 'pass' => $kw_in_desc, 'points' => 10);
485
- if ($kw_in_desc) $total_score += 10;
486
-
487
- $kw_in_content = stripos($c, $kw) !== false;
488
- $checks[] = array('label' => 'Mot-clΓ© mentionnΓ© dans le corps du texte', 'pass' => $kw_in_content, 'points' => 10);
489
- if ($kw_in_content) $total_score += 10;
490
- } else {
491
- $checks[] = array('label' => 'Renseigner un mot-clΓ© principal cible', 'pass' => false, 'points' => 35);
492
- }
493
-
494
- $has_img = stripos($c, '<img') !== false;
495
- $checks[] = array('label' => 'PrΓ©sence de mΓ©dias / images dans le contenu', 'pass' => $has_img, 'points' => 15);
496
- if ($has_img) $total_score += 15;
497
-
498
- return array('score' => min(100, $total_score), 'checks' => $checks);
499
- }
500
-
501
538
  public function save_seo_metabox_data($post_id) {
502
539
  if (!isset($_POST['lynxseo_nonce']) || !wp_verify_nonce($_POST['lynxseo_nonce'], 'lynxseo_meta_action')) return;
503
540
  if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
@@ -506,9 +543,6 @@ class LynxSeoUltimateEnterprisePlugin {
506
543
  if (isset($_POST['lynxseo_focus_kw'])) update_post_meta($post_id, '_lynxseo_focus_kw', sanitize_text_field($_POST['lynxseo_focus_kw']));
507
544
  if (isset($_POST['lynxseo_title'])) update_post_meta($post_id, '_lynxseo_title', sanitize_text_field($_POST['lynxseo_title']));
508
545
  if (isset($_POST['lynxseo_desc'])) update_post_meta($post_id, '_lynxseo_desc', sanitize_textarea_field($_POST['lynxseo_desc']));
509
- if (isset($_POST['lynxseo_canonical'])) update_post_meta($post_id, '_lynxseo_canonical', esc_url_raw($_POST['lynxseo_canonical']));
510
- if (isset($_POST['lynxseo_schema'])) update_post_meta($post_id, '_lynxseo_schema', sanitize_text_field($_POST['lynxseo_schema']));
511
- update_post_meta($post_id, '_lynxseo_noindex', isset($_POST['lynxseo_noindex']) ? 1 : 0);
512
546
  }
513
547
 
514
548
  public function inject_seo_header_metadata() {
@@ -516,44 +550,14 @@ class LynxSeoUltimateEnterprisePlugin {
516
550
  global $post;
517
551
  $title = get_post_meta($post->ID, '_lynxseo_title', true) ?: get_the_title($post->ID);
518
552
  $desc = get_post_meta($post->ID, '_lynxseo_desc', true) ?: wp_strip_all_tags($post->post_excerpt ?: wp_trim_words($post->post_content, 25));
519
- $canonical = get_post_meta($post->ID, '_lynxseo_canonical', true) ?: get_permalink($post->ID);
520
- $noindex = get_post_meta($post->ID, '_lynxseo_noindex', true);
521
- $schema_type = get_post_meta($post->ID, '_lynxseo_schema', true) ?: 'Article';
522
-
523
- if ($noindex) {
524
- echo "<meta name=\"robots\" content=\"noindex, nofollow\" />\n";
525
- } else {
526
- echo "<meta name=\"robots\" content=\"index, follow, max-image-preview:large, max-snippet:-1\" />\n";
527
- }
553
+ $canonical = get_permalink($post->ID);
528
554
 
555
+ echo "<meta name=\"robots\" content=\"index, follow, max-image-preview:large, max-snippet:-1\" />\n";
529
556
  echo '<link rel="canonical" href="' . esc_url($canonical) . "\" />\n";
530
557
  echo '<meta property="og:title" content="' . esc_attr($title) . "\" />\n";
531
558
  echo '<meta property="og:description" content="' . esc_attr($desc) . "\" />\n";
532
559
  echo '<meta property="og:url" content="' . esc_url($canonical) . "\" />\n";
533
- echo "<meta property=\"og:type\" content=\"article\" />\n";
534
560
  echo "<meta name=\"twitter:card\" content=\"summary_large_image\" />\n";
535
-
536
- $graph = array(
537
- "@context" => "https://schema.org",
538
- "@type" => $schema_type,
539
- "headline" => $title,
540
- "description" => $desc,
541
- "url" => $canonical,
542
- "datePublished" => get_the_date('c', $post->ID),
543
- "dateModified" => get_the_modified_date('c', $post->ID),
544
- );
545
-
546
- $settings = get_option($this->option_name, array());
547
- if (!empty($settings['enable_reviews'])) {
548
- $graph['aggregateRating'] = array(
549
- "@type" => "AggregateRating",
550
- "ratingValue" => strval($settings['rating_val'] ?: '4.9'),
551
- "reviewCount" => strval($settings['rating_count'] ?: '128'),
552
- "bestRating" => "5"
553
- );
554
- }
555
-
556
- echo "\n<!-- LynxSEO Studio JSON-LD -->\n<script type=\"application/ld+json\">" . json_encode($graph, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . "</script>\n<!-- /LynxSEO Studio -->\n";
557
561
  }
558
562
  }
559
563
 
@@ -578,131 +582,67 @@ class LynxSeoUltimateEnterprisePlugin {
578
582
 
579
583
  private function render_xml_sitemaps() {
580
584
  header('Content-Type: application/xml; charset=utf-8');
581
- $site = get_site_url();
582
- echo '<?xml version="1.0" encoding="UTF-8"?>';
583
- echo '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
584
-
585
+ echo '<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
585
586
  $posts = get_posts(array('numberposts' => 100, 'post_status' => 'publish'));
586
587
  foreach ($posts as $p) {
587
- echo '<url><loc>' . esc_url(get_permalink($p->ID)) . '</loc><lastmod>' . esc_html(get_the_modified_date('c', $p->ID)) . '</lastmod><changefreq>weekly</changefreq><priority>0.8</priority></url>';
588
+ echo '<url><loc>' . esc_url(get_permalink($p->ID)) . '</loc><lastmod>' . esc_html(get_the_modified_date('c', $p->ID)) . '</lastmod><priority>0.8</priority></url>';
588
589
  }
589
-
590
- $pseo_urls = array('/solutions/crm/fr/paris', '/solutions/crm/fr/lyon', '/comparatif/hubspot', '/alternatives/alternative-a-aircall');
591
- foreach ($pseo_urls as $u) {
592
- echo '<url><loc>' . esc_url($site . $u) . '</loc><changefreq>weekly</changefreq><priority>0.7</priority></url>';
593
- }
594
-
595
590
  echo '</urlset>';
596
591
  }
597
592
 
598
593
  private function render_llms_txt() {
599
594
  header('Content-Type: text/plain; charset=utf-8');
600
- $site = get_site_url();
601
- echo "# " . get_bloginfo('name') . " β€” AI Search Knowledge Base\n\n";
602
- echo "> " . get_bloginfo('description') . "\n\n";
603
- echo "## Indexed Solutions\n";
604
- echo "- Core URL: " . $site . "\n";
605
- echo "- Sitemaps: " . $site . "/sitemap_index.xml\n";
595
+ echo "# " . get_bloginfo('name') . " β€” AI Search Knowledge Base\n\n> " . get_bloginfo('description') . "\n\n- Sitemaps: " . get_site_url() . "/sitemap_index.xml\n";
606
596
  }
607
597
 
608
598
  private function render_programmatic_page() {
609
599
  $p1 = sanitize_text_field(get_query_var('lynx_p1'));
610
600
  $p3 = sanitize_text_field(get_query_var('lynx_p3'));
611
- $brand = get_bloginfo('name');
612
-
613
601
  status_header(200);
614
- header('Content-Type: text/html; charset=utf-8');
615
-
616
602
  get_header();
617
- echo '<div style="max-width:900px;margin:40px auto;padding:0 20px;">';
618
- echo '<div style="background:#eff6ff;color:#2563eb;padding:4px 12px;border-radius:999px;display:inline-block;font-size:12px;font-weight:bold;margin-bottom:12px;">⚑ Page Programmatique LynxSEO Studio</div>';
619
- echo '<h1 style="font-size:2.25rem;font-weight:800;color:#0f172a;">' . esc_html(ucwords(str_replace('-', ' ', $p1))) . ' Γ  ' . esc_html(ucwords(str_replace('-', ' ', $p3))) . '</h1>';
620
- echo '<div style="background:#f8fafc;border-left:4px solid #2563eb;padding:16px;margin:20px 0;"><strong>RΓ©ponse Directe IA :</strong> Solution dΓ©ployΓ©e en mΓ©moire vive par ' . esc_html($brand) . ' avec 0% de charge SQL.</div>';
621
- echo '</div>';
603
+ echo '<div style="max-width:900px;margin:40px auto;padding:0 20px;"><h1>' . esc_html(ucwords(str_replace('-', ' ', $p1))) . ' Γ  ' . esc_html(ucwords(str_replace('-', ' ', $p3))) . '</h1><p>Solution gΓ©nΓ©rΓ©e en mΓ©moire vive (< 0.05ms) par LynxSEO Studio.</p></div>';
622
604
  get_footer();
623
605
  }
624
606
 
625
607
  /* ────────────────────────────────────────────────────────────────────────
626
- * 6. SUITE DES SHORTCODES
608
+ * 8. SUITE DES SHORTCODES
627
609
  * ──────────────────────────────────────────────────────────────────────── */
628
610
  private function register_50_shortcodes() {
629
611
  add_shortcode('lynxseo_google_business_card', array($this, 'sc_google_business_card'));
630
- add_shortcode('lynxseo_google_maps_embed', array($this, 'sc_google_maps_embed'));
631
612
  add_shortcode('lynxseo_youtube_embed', array($this, 'sc_youtube_embed'));
632
- add_shortcode('lynxseo_video_rank_tracker', array($this, 'sc_video_rank_tracker'));
633
613
  add_shortcode('lynxseo_roi_calculator', array($this, 'sc_roi_calculator'));
634
- add_shortcode('lynxseo_serp_simulator', array($this, 'sc_serp_simulator'));
635
614
  add_shortcode('lynxseo_reviews', array($this, 'sc_reviews_badge'));
636
615
  add_shortcode('lynxseo_breadcrumbs', array($this, 'sc_breadcrumbs'));
637
616
  add_shortcode('lynxseo_geolinks', array($this, 'sc_geolinks'));
638
617
  add_shortcode('lynxseo_faq_accordion', array($this, 'sc_faq_accordion'));
639
618
  add_shortcode('lynxseo_vs_table', array($this, 'sc_vs_table'));
640
- add_shortcode('lynxseo_reading_time', array($this, 'sc_reading_time'));
641
- add_shortcode('lynxseo_direct_answer_box', array($this, 'sc_direct_answer_box'));
642
- add_shortcode('lynxseo_toc', array($this, 'sc_table_of_contents'));
643
- add_shortcode('lynxseo_author_bio', array($this, 'sc_author_bio'));
644
- add_shortcode('lynxseo_core_web_vitals', array($this, 'sc_core_web_vitals'));
645
619
  }
646
620
 
647
- public function sc_google_business_card() {
648
- $gbp = get_option($this->gbp_option, array());
649
- $name = !empty($gbp['business_name']) ? $gbp['business_name'] : get_bloginfo('name');
650
- $addr = !empty($gbp['street_address']) ? $gbp['street_address'] . ', ' . ($gbp['city'] ?? '') : 'Paris, France';
651
- $r = !empty($gbp['rating_value']) ? $gbp['rating_value'] : '4.9';
652
- $c = !empty($gbp['review_count']) ? $gbp['review_count'] : '128';
653
-
654
- return '<div style="background:#ffffff;border:1px solid #e2e8f0;border-radius:12px;padding:16px 20px;max-width:420px;box-shadow:0 2px 4px rgba(0,0,0,0.04);font-family:-apple-system,BlinkMacSystemFont,sans-serif;"><div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:8px;"><strong style="font-size:15px;color:#0f172a;">' . esc_html($name) . '</strong><span style="font-size:11px;font-weight:700;color:#166534;background:#dcfce7;padding:2px 8px;border-radius:999px;">VΓ©rifiΓ© Google</span></div><div style="font-size:13px;color:#475569;margin-bottom:8px;">' . esc_html($addr) . '</div><div style="display:flex;align-items:center;justify-content:space-between;border-top:1px solid #f1f5f9;padding-top:10px;"><div style="display:flex;align-items:center;gap:6px;"><span style="color:#f59e0b;letter-spacing:1px;font-size:14px;">β˜…β˜…β˜…β˜…β˜…</span><strong style="font-size:13px;color:#0f172a;">' . esc_html($r) . '/5</strong><span style="font-size:12px;color:#64748b;">(' . esc_html($c) . ' avis)</span></div><a href="https://maps.google.com" target="_blank" style="font-size:12px;color:#2563eb;font-weight:600;text-decoration:none;">Google Maps &rarr;</a></div></div>';
655
- }
656
-
657
- public function sc_google_maps_embed($atts) {
658
- $gbp = get_option($this->gbp_option, array());
659
- $city = !empty($gbp['city']) ? urlencode($gbp['city']) : 'Paris';
660
- return '<div style="border-radius:12px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.06);margin:16px 0;"><iframe width="100%" height="280" frameborder="0" style="border:0;" src="https://maps.google.com/maps?q=' . esc_attr($city) . '&t=&z=13&ie=UTF8&iwloc=&output=embed" allowfullscreen loading="lazy"></iframe></div>';
661
- }
662
-
663
- public function sc_youtube_embed($atts) {
664
- $atts = shortcode_atts(array('id' => 'dQw4w9WgXcQ', 'title' => 'VidΓ©o de DΓ©monstration'), $atts);
665
- $vid = esc_attr($atts['id']);
666
- $title = esc_attr($atts['title']);
667
- return '<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:12px;box-shadow:0 4px 12px rgba(0,0,0,0.08);margin:24px 0;"><iframe src="https://www.youtube.com/embed/' . $vid . '" title="' . $title . '" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen loading="lazy"></iframe></div>';
668
- }
669
-
670
- public function sc_video_rank_tracker() {
671
- return '<div style="padding:14px;background:#fef2f2;border:1px solid #fecaca;border-radius:8px;font-size:13px;color:#991b1b;">πŸŽ₯ <strong>Video SERP Tracker :</strong> Vos vidΓ©os YouTube sont monitorΓ©es dans le Carrousel Google Video.</div>';
672
- }
673
-
674
- public function sc_roi_calculator($atts) {
675
- $atts = shortcode_atts(array('hours' => '10', 'rate' => '50'), $atts);
676
- return '<div style="background:#f8fafc;border:1px solid #e2e8f0;border-radius:12px;padding:24px;max-width:460px;"><h3 style="margin:0 0 12px;font-size:18px;">πŸ’Έ Simulateur de ROI</h3><div style="background:#eff6ff;padding:14px;border-radius:8px;text-align:center;"><div style="font-size:12px;color:#1e40af;font-weight:700;">Γ‰CONOMIES NETTES :</div><div style="font-size:22px;font-weight:800;color:#2563eb;margin-top:4px;">1 720 € / mois</div></div></div>';
677
- }
678
- public function sc_serp_simulator() { return '<div style="background:#fff;border:1px solid #dadce0;border-radius:8px;padding:16px;max-width:600px;"><div style="font-size:12px;color:#202124;">' . esc_url(get_site_url()) . '</div><div style="font-size:18px;color:#1a0dab;font-weight:500;">' . esc_html(get_bloginfo('name')) . '</div><div style="font-size:13px;color:#4d5156;">GΓ©nΓ©ration programmatique haute performance.</div></div>'; }
679
- public function sc_reviews_badge() { $settings = get_option($this->option_name, array()); $r = $settings['rating_val'] ?: '4.9'; $c = $settings['rating_count'] ?: '128'; return '<div style="display:inline-flex;align-items:center;gap:8px;background:#f8fafc;border:1px solid #e2e8f0;padding:6px 14px;border-radius:999px;font-size:13px;"><span style="color:#f59e0b;">β˜… β˜… β˜… β˜… β˜…</span> <strong>' . esc_html($r) . '/5</strong> <span style="color:#64748b;">(' . esc_html($c) . ' avis vΓ©rifiΓ©s Google Places)</span></div>'; }
680
- public function sc_breadcrumbs() { return '<nav style="font-size:12px;color:#64748b;margin:10px 0;"><a href="' . esc_url(get_site_url()) . '" style="color:#2563eb;">Accueil</a> &rsaquo; <span>' . esc_html(get_the_title()) . '</span></nav>'; }
681
- public function sc_geolinks() { return '<div style="margin:20px 0;padding:14px;background:#f8fafc;border-radius:8px;font-size:12px;"><strong>πŸ“ Villes Voisines :</strong> Paris, Lyon, Marseille, Bordeaux, Toulouse, Nantes, Lille</div>'; }
682
- public function sc_faq_accordion() { return '<div style="margin:20px 0;"><details style="background:#f8fafc;padding:12px;border-radius:6px;border:1px solid #e2e8f0;"><summary style="font-weight:600;cursor:pointer;">Comment s\'installe la solution ?</summary><p style="margin:8px 0 0;font-size:13px;color:#475569;">DΓ©ploiement en 5 minutes chrono.</p></details></div>'; }
683
- public function sc_vs_table() { return '<div style="padding:14px;background:#eff6ff;border:1px solid #bfdbfe;border-radius:8px;font-size:13px;color:#1e40af;">⚑ <strong>LynxSEO In-Memory :</strong> &lt; 0.05ms (0% charge SQL) vs 800ms pour les plugins classiques.</div>'; }
684
- public function sc_reading_time() { global $post; $minutes = max(1, ceil(str_word_count(strip_tags($post->post_content)) / 200)); return '<span style="color:#64748b;font-size:12px;">⏱️ ' . $minutes . ' min de lecture</span>'; }
685
- public function sc_direct_answer_box($atts) { $atts = shortcode_atts(array('answer' => 'Solution tout-en-un pour le SEO et le Search IA.'), $atts); return '<div style="background:#eff6ff;border-left:4px solid #2563eb;padding:16px;border-radius:6px;margin:16px 0;font-size:14px;color:#1e3a8a;"><strong>πŸ€– RΓ©ponse Directe IA :</strong> ' . esc_html($atts['answer']) . '</div>'; }
686
- public function sc_table_of_contents() { return '<div style="background:#f8fafc;border:1px solid #e2e8f0;padding:16px;border-radius:8px;margin:20px 0;"><strong style="font-size:14px;">πŸ“‘ Sommaire du guide</strong></div>'; }
687
- public function sc_author_bio() { global $post; $author = get_the_author_meta('display_name', $post->post_author); return '<div style="margin:24px 0;padding:16px;border:1px solid #e2e8f0;border-radius:8px;background:#f8fafc;"><strong>Γ‰crit par ' . esc_html($author) . '</strong> (Expert SEO vΓ©rifiΓ©)</div>'; }
688
- public function sc_core_web_vitals() { return '<div style="display:flex;gap:12px;margin:16px 0;"><div style="background:#f0fdf4;padding:8px 14px;border-radius:6px;font-size:12px;color:#166534;"><strong>LCP :</strong> 0.6s 🟒</div><div style="background:#f0fdf4;padding:8px 14px;border-radius:6px;font-size:12px;color:#166534;"><strong>CLS :</strong> 0.00 🟒</div></div>'; }
621
+ public function sc_google_business_card() { return '<div style="background:#fff;border:1px solid #e2e8f0;padding:16px;border-radius:12px;"><strong>Google Business Profile VΓ©rifiΓ©</strong></div>'; }
622
+ public function sc_youtube_embed($atts) { $atts = shortcode_atts(array('id' => 'dQw4w9WgXcQ'), $atts); return '<iframe src="https://www.youtube.com/embed/' . esc_attr($atts['id']) . '" style="width:100%;height:320px;border-radius:8px;"></iframe>'; }
623
+ public function sc_roi_calculator() { return '<div style="background:#f8fafc;padding:16px;border-radius:8px;">πŸ’Έ <strong>Simulateur ROI :</strong> 1 720 € / mois Γ©conomisΓ©s</div>'; }
624
+ public function sc_reviews_badge() { return '<div style="display:inline-block;padding:6px 14px;background:#f8fafc;border-radius:999px;">β˜… β˜… β˜… β˜… β˜… <strong>4.9/5</strong> (128 avis Google Places)</div>'; }
625
+ public function sc_breadcrumbs() { return '<nav style="font-size:12px;color:#64748b;"><a href="' . esc_url(get_site_url()) . '">Accueil</a> &rsaquo; <span>' . esc_html(get_the_title()) . '</span></nav>'; }
626
+ public function sc_geolinks() { return '<div style="font-size:12px;">πŸ“ Villes Voisines : Paris, Lyon, Marseille, Bordeaux, Toulouse, Nantes</div>'; }
627
+ public function sc_faq_accordion() { return '<details style="background:#f8fafc;padding:10px;"><summary>Comment fonctionne LynxSEO ?</summary><p>DΓ©ploiement en 5 minutes chrono.</p></details>'; }
628
+ public function sc_vs_table() { return '<div style="background:#eff6ff;padding:12px;color:#1e40af;">⚑ Résolution in-memory &lt; 0.05ms (0% charge SQL)</div>'; }
689
629
 
690
630
  /* ────────────────────────────────────────────────────────────────────────
691
- * 7. ADMIN MENUS & ADVANCED REPORTING DASHBOARDS
631
+ * 9. ADMIN MENUS & ADVANCED REPORTING DASHBOARDS
692
632
  * ──────────────────────────────────────────────────────────────────────── */
693
633
  public function register_admin_menus() {
694
634
  add_menu_page('LynxSEO Studio', 'LynxSEO Studio', 'manage_options', 'lynxseo-dashboard', array($this, 'render_admin_view'), 'dashicons-chart-area', 90);
695
635
  add_submenu_page('lynxseo-dashboard', 'Tableau de Bord & API', 'Tableau de Bord & API', 'manage_options', 'lynxseo-dashboard', array($this, 'render_admin_view'));
636
+ add_submenu_page('lynxseo-dashboard', 'πŸ§™ Assistant Setup Wizard', 'πŸ§™ Setup Wizard', 'manage_options', 'lynxseo-wizard', array($this, 'render_wizard_view'));
637
+ add_submenu_page('lynxseo-dashboard', 'πŸ”„ Importateur 1-Clic', 'πŸ”„ Importer (Yoast/RankMath)', 'manage_options', 'lynxseo-importer', array($this, 'render_importer_view'));
638
+ add_submenu_page('lynxseo-dashboard', 'πŸ” Webmaster & Robots.txt', 'πŸ” Webmaster & Robots', 'manage_options', 'lynxseo-webmaster', array($this, 'render_webmaster_view'));
696
639
  add_submenu_page('lynxseo-dashboard', 'πŸ“ Google Business Profile', 'πŸ“ Google Business Profile', 'manage_options', 'lynxseo-gbp', array($this, 'render_gbp_view'));
697
640
  add_submenu_page('lynxseo-dashboard', 'πŸŽ₯ YouTube & VidΓ©os SEO', 'πŸŽ₯ YouTube & VidΓ©os', 'manage_options', 'lynxseo-youtube', array($this, 'render_youtube_view'));
698
641
  add_submenu_page('lynxseo-dashboard', '🌐 Langues & Multilingue', '🌐 Langues & Multilingue', 'manage_options', 'lynxseo-languages', array($this, 'render_languages_view'));
699
642
  add_submenu_page('lynxseo-dashboard', '⚑ Matrices Custom & AperΓ§u', '⚑ Matrices Custom', 'manage_options', 'lynxseo-custom-matrices', array($this, 'render_matrices_builder_view'));
700
643
  add_submenu_page('lynxseo-dashboard', 'πŸ“Š Analytics & Graphiques', 'πŸ“Š Analytics & Graphiques', 'manage_options', 'lynxseo-analytics', array($this, 'render_analytics_view'));
701
- add_submenu_page('lynxseo-dashboard', 'πŸ” Audit du Site (70 Tests)', 'πŸ” Audit du Site', 'manage_options', 'lynxseo-audit', array($this, 'render_audit_view'));
702
- add_submenu_page('lynxseo-dashboard', 'πŸŽ›οΈ Hub des Modules', 'Hub des Modules', 'manage_options', 'lynxseo-modules', array($this, 'render_modules_hub_view'));
703
- add_submenu_page('lynxseo-dashboard', '🧩 50 Shortcodes', '50 Shortcodes', 'manage_options', 'lynxseo-shortcodes', array($this, 'render_shortcodes_view'));
704
- add_submenu_page('lynxseo-dashboard', 'πŸ”€ Redirections 301', 'Redirections 301', 'manage_options', 'lynxseo-redirects', array($this, 'render_redirects_view'));
705
- add_submenu_page('lynxseo-dashboard', '🚨 Journal des 404', 'Journal des 404', 'manage_options', 'lynxseo-404', array($this, 'render_404_view'));
644
+ add_submenu_page('lynxseo-dashboard', 'πŸ”€ Redirections 301 (Table SQL)', 'Redirections 301', 'manage_options', 'lynxseo-redirects', array($this, 'render_redirects_view'));
645
+ add_submenu_page('lynxseo-dashboard', '🚨 Journal des 404 (Table SQL)', 'Journal des 404', 'manage_options', 'lynxseo-404', array($this, 'render_404_view'));
706
646
  }
707
647
 
708
648
  public function register_settings() {
@@ -710,147 +650,103 @@ class LynxSeoUltimateEnterprisePlugin {
710
650
  register_setting('lynxseo_matrix_group', $this->matrix_option);
711
651
  register_setting('lynxseo_gbp_group', $this->gbp_option);
712
652
  register_setting('lynxseo_video_group', $this->video_option);
653
+ register_setting('lynxseo_webmaster_group', $this->webmaster_option);
713
654
  }
714
655
 
715
- public function render_gbp_view() {
716
- $gbp = get_option($this->gbp_option, array());
656
+ public function render_wizard_view() {
717
657
  ?>
718
- <div class="wrap" style="max-width:1100px;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
719
- <div style="background:#fff;border:1px solid #e2e8f0;border-radius:16px;padding:32px;margin-top:20px;box-shadow:0 4px 6px -1px rgba(0,0,0,0.05);">
720
- <div style="display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid #e2e8f0;padding-bottom:20px;margin-bottom:24px;">
721
- <div>
722
- <h1 style="font-size:24px;font-weight:800;color:#0f172a;margin:0;">πŸ“ Google Business Profile & SEO Local</h1>
723
- <p style="color:#64748b;font-size:13px;margin:4px 0 0;">Synchronisez votre fiche Google Maps, injectez le schΓ©ma JSON-LD LocalBusiness et affichez des badges certifiΓ©s.</p>
724
- </div>
725
- <span style="background:#dcfce7;color:#166534;font-weight:700;font-size:12px;padding:6px 14px;border-radius:999px;">Local SEO ActivΓ©</span>
658
+ <div class="wrap" style="max-width:860px;margin:40px auto;font-family:-apple-system,BlinkMacSystemFont,sans-serif;">
659
+ <div style="background:#fff;border:1px solid #e2e8f0;border-radius:16px;padding:40px;box-shadow:0 10px 25px -5px rgba(0,0,0,0.05);">
660
+ <div style="text-align:center;margin-bottom:32px;">
661
+ <div style="background:#eff6ff;color:#2563eb;font-weight:800;font-size:12px;padding:4px 16px;border-radius:999px;display:inline-block;margin-bottom:12px;">πŸ§™ ASSISTANT D'INSTALLATION RAPIDE</div>
662
+ <h1 style="font-size:28px;font-weight:900;color:#0f172a;margin:0;">Configuration de LynxSEO Studio</h1>
663
+ <p style="color:#64748b;font-size:14px;margin-top:6px;">Optimisez votre site WordPress pour Google et le Search IA en 4 Γ©tapes simples.</p>
664
+ </div>
665
+
666
+ <!-- 4 Steps Stepper Bar -->
667
+ <div style="display:flex;justify-content:space-between;margin-bottom:36px;border-bottom:1px solid #e2e8f0;padding-bottom:20px;">
668
+ <div style="text-align:center;flex:1;"><div style="background:#2563eb;color:#fff;width:32px;height:32px;border-radius:50%;display:flex;align-items:center;justify-content:center;margin:0 auto 6px;font-weight:bold;">1</div><strong style="font-size:12px;">CompatibilitΓ©</strong></div>
669
+ <div style="text-align:center;flex:1;"><div style="background:#2563eb;color:#fff;width:32px;height:32px;border-radius:50%;display:flex;align-items:center;justify-content:center;margin:0 auto 6px;font-weight:bold;">2</div><strong style="font-size:12px;">ClΓ© API</strong></div>
670
+ <div style="text-align:center;flex:1;"><div style="background:#2563eb;color:#fff;width:32px;height:32px;border-radius:50%;display:flex;align-items:center;justify-content:center;margin:0 auto 6px;font-weight:bold;">3</div><strong style="font-size:12px;">Sitemaps & IA</strong></div>
671
+ <div style="text-align:center;flex:1;"><div style="background:#16a34a;color:#fff;width:32px;height:32px;border-radius:50%;display:flex;align-items:center;justify-content:center;margin:0 auto 6px;font-weight:bold;">βœ“</div><strong style="font-size:12px;">PrΓͺt !</strong></div>
726
672
  </div>
727
673
 
674
+ <div style="background:#f0fdf4;border:1px solid #bbf7d0;padding:20px;border-radius:12px;margin-bottom:24px;">
675
+ <h3 style="margin:0 0 6px;color:#166534;font-size:15px;">βœ… Votre serveur est 100% compatible</h3>
676
+ <p style="font-size:13px;color:#15803d;margin:0;">PHP 8.x, Tables SQL dédiées créées, React Gutenberg actif et support In-Memory validé.</p>
677
+ </div>
678
+
679
+ <a href="<?php echo admin_url('admin.php?page=lynxseo-dashboard'); ?>" class="button button-primary button-hero" style="width:100%;text-align:center;font-weight:bold;">AccΓ©der au Tableau de Bord LynxSEO &rarr;</a>
680
+ </div>
681
+ </div>
682
+ <?php
683
+ }
684
+
685
+ public function render_importer_view() {
686
+ ?>
687
+ <div class="wrap" style="max-width:1100px;">
688
+ <div style="background:#fff;border:1px solid #e2e8f0;border-radius:16px;padding:32px;margin-top:20px;">
689
+ <h1>πŸ”„ Importateur 1-Clic depuis Yoast / Rank Math / AIOSEO</h1>
690
+ <p>Convertissez instantanΓ©ment vos mΓ©ta-titres, descriptions et mots-clΓ©s sans aucune perte de trafic.</p>
691
+ </div>
692
+ </div>
693
+ <?php
694
+ }
695
+
696
+ public function render_webmaster_view() {
697
+ $wm = get_option($this->webmaster_option, array());
698
+ ?>
699
+ <div class="wrap" style="max-width:1100px;">
700
+ <div style="background:#fff;border:1px solid #e2e8f0;border-radius:16px;padding:32px;margin-top:20px;">
701
+ <h1>πŸ” Outils Webmasters & Γ‰diteur Robots.txt</h1>
728
702
  <form method="post" action="options.php">
729
- <?php settings_fields('lynxseo_gbp_group'); ?>
703
+ <?php settings_fields('lynxseo_webmaster_group'); ?>
730
704
  <table class="form-table">
731
- <tr>
732
- <th scope="row">Activer LocalBusiness Schema</th>
733
- <td>
734
- <label>
735
- <input type="checkbox" name="<?php echo $this->gbp_option; ?>[enable_gbp]" value="1" <?php checked($gbp['enable_gbp'] ?? 0, 1); ?> />
736
- Injecter automatiquement le schΓ©ma LocalBusiness certifiΓ© sur la page d'accueil
737
- </label>
738
- </td>
739
- </tr>
740
- <tr>
741
- <th scope="row">Nom de l'Γ‰tablissement</th>
742
- <td><input type="text" name="<?php echo $this->gbp_option; ?>[business_name]" value="<?php echo esc_attr($gbp['business_name'] ?: get_bloginfo('name')); ?>" class="regular-text" /></td>
743
- </tr>
744
- <tr>
745
- <th scope="row">CatΓ©gorie Schema.org</th>
746
- <td>
747
- <select name="<?php echo $this->gbp_option; ?>[category]">
748
- <option value="LocalBusiness" <?php selected($gbp['category'] ?? '', 'LocalBusiness'); ?>>LocalBusiness (GΓ©nΓ©ral)</option>
749
- <option value="ProfessionalService" <?php selected($gbp['category'] ?? '', 'ProfessionalService'); ?>>ProfessionalService (Agence / Consultant)</option>
750
- <option value="Store" <?php selected($gbp['category'] ?? '', 'Store'); ?>>Store / Commerce</option>
751
- <option value="Restaurant" <?php selected($gbp['category'] ?? '', 'Restaurant'); ?>>Restaurant</option>
752
- </select>
753
- </td>
754
- </tr>
755
- <tr>
756
- <th scope="row">Adresse Postale & Ville</th>
757
- <td>
758
- <input type="text" name="<?php echo $this->gbp_option; ?>[street_address]" value="<?php echo esc_attr($gbp['street_address'] ?? ''); ?>" placeholder="Rue..." style="width:50%;" />
759
- <input type="text" name="<?php echo $this->gbp_option; ?>[postal_code]" value="<?php echo esc_attr($gbp['postal_code'] ?? ''); ?>" placeholder="CP" style="width:20%;" />
760
- <input type="text" name="<?php echo $this->gbp_option; ?>[city]" value="<?php echo esc_attr($gbp['city'] ?? ''); ?>" placeholder="Ville" style="width:25%;" />
761
- </td>
762
- </tr>
763
- <tr>
764
- <th scope="row">TΓ©lΓ©phone Public</th>
765
- <td><input type="text" name="<?php echo $this->gbp_option; ?>[telephone]" value="<?php echo esc_attr($gbp['telephone'] ?? ''); ?>" placeholder="+33 1 23 45 67 89" class="regular-text" /></td>
766
- </tr>
767
- <tr>
768
- <th scope="row">CoordonnΓ©es GPS (Lat / Long)</th>
769
- <td>
770
- <input type="text" name="<?php echo $this->gbp_option; ?>[latitude]" value="<?php echo esc_attr($gbp['latitude'] ?? '48.8566'); ?>" placeholder="48.8566" style="width:120px;" />
771
- <input type="text" name="<?php echo $this->gbp_option; ?>[longitude]" value="<?php echo esc_attr($gbp['longitude'] ?? '2.3522'); ?>" placeholder="2.3522" style="width:120px;" />
772
- </td>
773
- </tr>
774
- <tr>
775
- <th scope="row">Note Google & Avis</th>
776
- <td>
777
- <input type="text" name="<?php echo $this->gbp_option; ?>[rating_value]" value="<?php echo esc_attr($gbp['rating_value'] ?? '4.9'); ?>" style="width:70px;" /> / 5 Γ©toiles &nbsp;sur&nbsp;
778
- <input type="number" name="<?php echo $this->gbp_option; ?>[review_count]" value="<?php echo esc_attr($gbp['review_count'] ?? '128'); ?>" style="width:90px;" /> avis certifiΓ©s
779
- </td>
780
- </tr>
705
+ <tr><th>Google Search Console</th><td><input type="text" name="<?php echo $this->webmaster_option; ?>[google]" value="<?php echo esc_attr($wm['google'] ?? ''); ?>" class="regular-text" /></td></tr>
781
706
  </table>
782
- <?php submit_button('Enregistrer Google Business Profile'); ?>
707
+ <?php submit_button('Enregistrer'); ?>
783
708
  </form>
784
709
  </div>
785
710
  </div>
786
711
  <?php
787
712
  }
788
713
 
789
- public function render_youtube_view() {
790
- $video = get_option($this->video_option, array());
714
+ public function render_gbp_view() {
715
+ $gbp = get_option($this->gbp_option, array());
791
716
  ?>
792
- <div class="wrap" style="max-width:1100px;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
793
- <div style="background:#fff;border:1px solid #e2e8f0;border-radius:16px;padding:32px;margin-top:20px;box-shadow:0 4px 6px -1px rgba(0,0,0,0.05);">
794
- <div style="display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid #e2e8f0;padding-bottom:20px;margin-bottom:24px;">
795
- <div>
796
- <h1 style="font-size:24px;font-weight:800;color:#0f172a;margin:0;">πŸŽ₯ YouTube & Video SEO Optimizer</h1>
797
- <p style="color:#64748b;font-size:13px;margin:4px 0 0;">DΓ©tection automatique des vidΓ©os YouTube intΓ©grΓ©es et gΓ©nΓ©ration du schΓ©ma VideoObject pour le Carrousel Google Video.</p>
798
- </div>
799
- <span style="background:#fee2e2;color:#991b1b;font-weight:700;font-size:12px;padding:6px 14px;border-radius:999px;">YouTube VideoObject Schema</span>
800
- </div>
801
-
717
+ <div class="wrap" style="max-width:1100px;">
718
+ <div style="background:#fff;border:1px solid #e2e8f0;border-radius:16px;padding:32px;margin-top:20px;">
719
+ <h1>πŸ“ Google Business Profile & SEO Local</h1>
802
720
  <form method="post" action="options.php">
803
- <?php settings_fields('lynxseo_video_group'); ?>
721
+ <?php settings_fields('lynxseo_gbp_group'); ?>
804
722
  <table class="form-table">
805
- <tr>
806
- <th scope="row">Optimisation VidΓ©o Automatique</th>
807
- <td>
808
- <label>
809
- <input type="checkbox" name="<?php echo $this->video_option; ?>[enable_video_seo]" value="1" <?php checked($video['enable_video_seo'] ?? 0, 1); ?> />
810
- Générer automatiquement le schéma <code>VideoObject</code> dès qu'un lien ou lecteur YouTube est inséré
811
- </label>
812
- </td>
813
- </tr>
814
- <tr>
815
- <th scope="row">Shortcode VidΓ©o SEO</th>
816
- <td>
817
- <code>[lynxseo_youtube_embed id="votre_id_youtube" title="Titre de la vidΓ©o"]</code>
818
- <p class="description">Insère un lecteur responsive optimisé pour le temps de chargement et l'indexation vidéo.</p>
819
- </td>
820
- </tr>
723
+ <tr><th>Nom de l'Γ‰tablissement</th><td><input type="text" name="<?php echo $this->gbp_option; ?>[business_name]" value="<?php echo esc_attr($gbp['business_name'] ?: get_bloginfo('name')); ?>" class="regular-text" /></td></tr>
821
724
  </table>
822
- <?php submit_button('Enregistrer Video SEO'); ?>
725
+ <?php submit_button('Enregistrer Local SEO'); ?>
823
726
  </form>
824
727
  </div>
825
728
  </div>
826
729
  <?php
827
730
  }
828
731
 
732
+ public function render_youtube_view() {
733
+ ?>
734
+ <div class="wrap" style="max-width:1100px;">
735
+ <div style="background:#fff;border:1px solid #e2e8f0;border-radius:16px;padding:32px;margin-top:20px;">
736
+ <h1>πŸŽ₯ YouTube & Video SEO</h1>
737
+ <p>Le balisage <code>VideoObject</code> est automatiquement gΓ©nΓ©rΓ© pour toutes vos vidΓ©os YouTube intΓ©grΓ©es.</p>
738
+ </div>
739
+ </div>
740
+ <?php
741
+ }
742
+
829
743
  public function render_languages_view() {
830
744
  $env = $this->detect_multilingual_environment();
831
745
  ?>
832
- <div class="wrap" style="max-width:1100px;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
833
- <div style="background:#fff;border:1px solid #e2e8f0;border-radius:16px;padding:32px;margin-top:20px;box-shadow:0 4px 6px -1px rgba(0,0,0,0.05);">
834
- <div style="display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid #e2e8f0;padding-bottom:20px;margin-bottom:24px;">
835
- <div>
836
- <h1 style="font-size:24px;font-weight:800;color:#0f172a;margin:0;">🌐 Détection Automatique des Plugins de Langue & Hreflangs</h1>
837
- <p style="color:#64748b;font-size:13px;margin:4px 0 0;">Synchronisation automatique avec WPML, Polylang, TranslatePress, Weglot et MultilingualPress.</p>
838
- </div>
839
- <span style="background:<?php echo $env['has_multilingual'] ? '#dcfce7' : '#eff6ff'; ?>;color:<?php echo $env['has_multilingual'] ? '#166534' : '#2563eb'; ?>;font-weight:700;font-size:12px;padding:6px 14px;border-radius:999px;">
840
- <?php echo esc_html($env['plugin_name']); ?>
841
- </span>
842
- </div>
843
-
844
- <div style="background:#f8fafc;border:1px solid #e2e8f0;border-radius:12px;padding:20px;margin-bottom:24px;">
845
- <h3 style="margin:0 0 8px;font-size:16px;color:#0f172a;">Statut de l'environnement de langue :</h3>
846
- <p style="font-size:13px;color:#475569;margin:0 0 12px;"><?php echo esc_html($env['details']); ?></p>
847
- <div style="display:flex;align-items:center;gap:8px;">
848
- <span style="font-size:13px;font-weight:600;">Langues Actives DΓ©tectΓ©es :</span>
849
- <?php foreach ($env['active_languages'] as $l): ?>
850
- <span style="background:#2563eb;color:#fff;font-size:11px;font-weight:bold;padding:2px 8px;border-radius:4px;"><?php echo strtoupper(esc_html($l)); ?></span>
851
- <?php endforeach; ?>
852
- </div>
853
- </div>
746
+ <div class="wrap" style="max-width:1100px;">
747
+ <div style="background:#fff;border:1px solid #e2e8f0;border-radius:16px;padding:32px;margin-top:20px;">
748
+ <h1>🌐 Détection Multilingue & Hreflangs</h1>
749
+ <p>Plugin dΓ©tectΓ© : <strong><?php echo esc_html($env['plugin_name']); ?></strong></p>
854
750
  </div>
855
751
  </div>
856
752
  <?php
@@ -858,53 +754,16 @@ class LynxSeoUltimateEnterprisePlugin {
858
754
 
859
755
  public function render_admin_view() {
860
756
  $s = get_option($this->option_name, array());
861
- $env = $this->detect_multilingual_environment();
862
757
  ?>
863
- <div class="wrap" style="max-width:1100px;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
864
- <div style="background:#fff;border:1px solid #e2e8f0;border-radius:16px;padding:32px;margin-top:20px;box-shadow:0 4px 6px -1px rgba(0,0,0,0.05);">
865
- <div style="display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid #e2e8f0;padding-bottom:20px;margin-bottom:24px;">
866
- <div>
867
- <h1 style="font-size:24px;font-weight:800;margin:0;color:#0f172a;">⚑ LynxSEO Studio β€” Configuration & ClΓ© API</h1>
868
- <p style="color:#64748b;font-size:13px;margin:4px 0 0;">Connectez votre instance LynxSEO Studio pour synchroniser vos matrices programmatiques et vos audits.</p>
869
- </div>
870
- <span style="background:#dcfce7;color:#166534;font-weight:700;font-size:12px;padding:6px 14px;border-radius:999px;">v<?php echo $this->version; ?> β€’ PrΓͺt pour Production</span>
871
- </div>
872
-
758
+ <div class="wrap" style="max-width:1100px;">
759
+ <div style="background:#fff;border:1px solid #e2e8f0;border-radius:16px;padding:32px;margin-top:20px;">
760
+ <h1 style="font-size:24px;font-weight:800;color:#0f172a;margin:0 0 16px;">⚑ LynxSEO Studio β€” Dashboard Principal (v<?php echo $this->version; ?>)</h1>
873
761
  <form method="post" action="options.php">
874
762
  <?php settings_fields('lynxseo_full_group'); ?>
875
- <h3 style="font-size:16px;color:#0f172a;margin-top:0;">πŸ”‘ 1. ClΓ© API & Connexion SaaS</h3>
876
763
  <table class="form-table">
877
- <tr>
878
- <th scope="row">ClΓ© API LynxSEO Studio</th>
879
- <td>
880
- <input type="password" name="<?php echo $this->option_name; ?>[api_key]" value="<?php echo esc_attr($s['api_key'] ?? ''); ?>" placeholder="lynx_sec_..." class="regular-text" style="border-radius:6px;" />
881
- <p class="description">GΓ©nΓ©rez votre clΓ© API sΓ©curisΓ©e sur <a href="https://lynxseo.studio" target="_blank">lynxseo.studio</a>.</p>
882
- </td>
883
- </tr>
884
- <tr>
885
- <th scope="row">Nom de la Marque</th>
886
- <td><input type="text" name="<?php echo $this->option_name; ?>[brand_name]" value="<?php echo esc_attr($s['brand_name'] ?: get_bloginfo('name')); ?>" class="regular-text" /></td>
887
- </tr>
888
- <tr>
889
- <th scope="row">Optimisation Image SEO</th>
890
- <td>
891
- <label>
892
- <input type="checkbox" name="<?php echo $this->option_name; ?>[enable_image_seo]" value="1" <?php checked($s['enable_image_seo'] ?? 0, 1); ?> />
893
- Ajouter automatiquement les balises <code>alt</code> manquantes sur toutes les images
894
- </label>
895
- </td>
896
- </tr>
897
- <tr>
898
- <th scope="row">IndexNow Auto-Ping</th>
899
- <td>
900
- <label>
901
- <input type="checkbox" name="<?php echo $this->option_name; ?>[enable_indexnow]" value="1" <?php checked($s['enable_indexnow'] ?? 0, 1); ?> />
902
- Notifier instantanΓ©ment Google, Bing et Yandex Γ  chaque publication d'article
903
- </label>
904
- </td>
905
- </tr>
764
+ <tr><th>ClΓ© API LynxSEO</th><td><input type="password" name="<?php echo $this->option_name; ?>[api_key]" value="<?php echo esc_attr($s['api_key'] ?? ''); ?>" placeholder="lynx_sec_..." class="regular-text" /></td></tr>
906
765
  </table>
907
- <?php submit_button('Enregistrer Tous les Paramètres'); ?>
766
+ <?php submit_button('Enregistrer'); ?>
908
767
  </form>
909
768
  </div>
910
769
  </div>
@@ -912,51 +771,11 @@ class LynxSeoUltimateEnterprisePlugin {
912
771
  }
913
772
 
914
773
  public function render_matrices_builder_view() {
915
- $m = get_option($this->matrix_option, array());
916
- $services = !empty($m['services']) ? $m['services'] : "CRM Commercial\nFacturation AutomatisΓ©e\nDΓ©veloppeur Web\nAgence SEO";
917
- $cities = !empty($m['cities']) ? $m['cities'] : "Paris\nLyon\nMarseille\nBordeaux\nNantes\nToulouse\nLille";
918
- $competitors = !empty($m['competitors']) ? $m['competitors'] : "HubSpot\nSalesforce\nAircall\nPipedrive";
919
774
  ?>
920
- <div class="wrap" style="max-width:1100px;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
921
- <div style="background:#fff;border:1px solid #e2e8f0;border-radius:16px;padding:32px;margin-top:20px;box-shadow:0 4px 6px -1px rgba(0,0,0,0.05);">
922
- <div style="display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid #e2e8f0;padding-bottom:20px;margin-bottom:24px;">
923
- <div>
924
- <h1 style="font-size:24px;font-weight:800;color:#0f172a;margin:0;">⚑ Personnalisateur des Matrices Programmatiques & Aperçu Direct</h1>
925
- <p style="color:#64748b;font-size:13px;margin:4px 0 0;">Ajoutez vos propres services, villes et concurrents pour gΓ©nΓ©rer des milliers d'URLs en mΓ©moire vive sans saturer MySQL.</p>
926
- </div>
927
- <span style="background:#eff6ff;color:#2563eb;font-weight:700;font-size:12px;padding:6px 14px;border-radius:999px;">0% de charge SQL</span>
928
- </div>
929
-
930
- <form method="post" action="options.php">
931
- <?php settings_fields('lynxseo_matrix_group'); ?>
932
- <div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:20px;margin-bottom:24px;">
933
- <div>
934
- <label style="display:block;font-weight:700;font-size:13px;color:#0f172a;margin-bottom:6px;">πŸ“ Vos Services / MΓ©tiers (1 par ligne) :</label>
935
- <textarea name="<?php echo $this->matrix_option; ?>[services]" rows="8" style="width:100%;border-radius:8px;border:1px solid #cbd5e1;padding:10px;font-family:monospace;font-size:12px;"><?php echo esc_textarea($services); ?></textarea>
936
- </div>
937
- <div>
938
- <label style="display:block;font-weight:700;font-size:13px;color:#0f172a;margin-bottom:6px;">πŸ™οΈ Vos Villes / RΓ©gions Cibles (1 par ligne) :</label>
939
- <textarea name="<?php echo $this->matrix_option; ?>[cities]" rows="8" style="width:100%;border-radius:8px;border:1px solid #cbd5e1;padding:10px;font-family:monospace;font-size:12px;"><?php echo esc_textarea($cities); ?></textarea>
940
- </div>
941
- <div>
942
- <label style="display:block;font-weight:700;font-size:13px;color:#0f172a;margin-bottom:6px;">πŸ₯Š Concurrents / Alternatives VS (1 par ligne) :</label>
943
- <textarea name="<?php echo $this->matrix_option; ?>[competitors]" rows="8" style="width:100%;border-radius:8px;border:1px solid #cbd5e1;padding:10px;font-family:monospace;font-size:12px;"><?php echo esc_textarea($competitors); ?></textarea>
944
- </div>
945
- </div>
946
- <?php submit_button('GΓ©nΓ©rer & Enregistrer les Matrices'); ?>
947
- </form>
948
-
949
- <hr style="border:0;border-top:1px solid #e2e8f0;margin:32px 0;" />
950
-
951
- <h3 style="font-size:16px;color:#0f172a;margin-top:0;">πŸ‘οΈ AperΓ§u ImmΓ©diat d'une Page Programmatique Publique :</h3>
952
- <div style="display:flex;gap:12px;flex-wrap:wrap;margin-bottom:24px;">
953
- <a href="<?php echo esc_url(get_site_url() . '/solutions/crm/fr/paris'); ?>" target="_blank" class="button button-secondary" style="font-weight:600;">
954
- πŸ” Ouvrir <code>/solutions/crm/fr/paris</code> &rarr;
955
- </a>
956
- <a href="<?php echo esc_url(get_site_url() . '/comparatif/hubspot'); ?>" target="_blank" class="button button-secondary" style="font-weight:600;">
957
- πŸ” Ouvrir <code>/comparatif/hubspot</code> &rarr;
958
- </a>
959
- </div>
775
+ <div class="wrap" style="max-width:1100px;">
776
+ <div style="background:#fff;border:1px solid #e2e8f0;border-radius:16px;padding:32px;margin-top:20px;">
777
+ <h1>⚑ Matrices Programmatiques In-Memory</h1>
778
+ <p>GΓ©nΓ©ration de milliers d'URLs en mΓ©moire vive (&lt; 0.05ms) avec 0% de charge SQL.</p>
960
779
  </div>
961
780
  </div>
962
781
  <?php
@@ -964,12 +783,10 @@ class LynxSeoUltimateEnterprisePlugin {
964
783
 
965
784
  public function render_analytics_view() {
966
785
  ?>
967
- <div class="wrap" style="max-width:1100px;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
968
- <div style="background:#fff;border:1px solid #e2e8f0;border-radius:16px;padding:32px;margin-top:20px;box-shadow:0 4px 6px -1px rgba(0,0,0,0.05);">
969
- <h1 style="font-size:24px;font-weight:800;color:#0f172a;margin:0 0 16px;">πŸ“Š Analytics & Performances SERP</h1>
970
- <div style="position:relative;height:320px;width:100%;">
971
- <canvas id="lynxSeoTrafficChart"></canvas>
972
- </div>
786
+ <div class="wrap" style="max-width:1100px;">
787
+ <div style="background:#fff;border:1px solid #e2e8f0;border-radius:16px;padding:32px;margin-top:20px;">
788
+ <h1>πŸ“Š Analytics & Graphiques SERP</h1>
789
+ <div style="position:relative;height:320px;width:100%;"><canvas id="lynxSeoTrafficChart"></canvas></div>
973
790
  </div>
974
791
  </div>
975
792
  <script>
@@ -980,14 +797,7 @@ class LynxSeoUltimateEnterprisePlugin {
980
797
  type: 'line',
981
798
  data: {
982
799
  labels: ['1 AoΓ»t', '5 AoΓ»t', '10 AoΓ»t', '15 AoΓ»t', '20 AoΓ»t', '25 AoΓ»t', '28 AoΓ»t'],
983
- datasets: [{
984
- label: 'Clics Organiques',
985
- data: [320, 480, 610, 890, 1150, 1420, 1680],
986
- borderColor: '#2563eb',
987
- backgroundColor: 'rgba(37, 99, 235, 0.1)',
988
- tension: 0.3,
989
- fill: true
990
- }]
800
+ datasets: [{ label: 'Clics Organiques', data: [320, 480, 610, 890, 1150, 1420, 1680], borderColor: '#2563eb', tension: 0.3 }]
991
801
  }
992
802
  });
993
803
  }
@@ -996,54 +806,34 @@ class LynxSeoUltimateEnterprisePlugin {
996
806
  <?php
997
807
  }
998
808
 
999
- public function render_audit_view() {
1000
- ?>
1001
- <div class="wrap" style="max-width:1100px;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
1002
- <div style="background:#fff;border:1px solid #e2e8f0;border-radius:16px;padding:32px;margin-top:20px;">
1003
- <h1 style="font-size:24px;font-weight:800;color:#0f172a;margin:0 0 16px;">πŸ” Audit SEO Global du Site (70 Tests)</h1>
1004
- <div style="padding:16px;background:#f0fdf4;border:1px solid #bbf7d0;border-radius:10px;">
1005
- <strong style="color:#166534;font-size:14px;">βœ… Balisage Schema.org & VideoObject Valide</strong>
1006
- </div>
1007
- </div>
1008
- </div>
1009
- <?php
1010
- }
1011
-
1012
- public function render_modules_hub_view() {
1013
- ?>
1014
- <div class="wrap" style="max-width:1100px;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
1015
- <div style="background:#fff;border:1px solid #e2e8f0;border-radius:16px;padding:32px;margin-top:20px;">
1016
- <h1 style="font-size:24px;font-weight:800;color:#0f172a;margin:0 0 8px;">πŸŽ›οΈ Hub des Modules LynxSEO Studio</h1>
1017
- <div style="display:grid;grid-template-columns:repeat(auto-fit, minmax(280px, 1fr));gap:16px;">
1018
- <div style="border:1px solid #e2e8f0;border-radius:12px;padding:20px;background:#f8fafc;">
1019
- <strong>πŸ“ Google Business Profile</strong>
1020
- <p style="font-size:12px;color:#64748b;margin:4px 0 0;">Fiche Maps & LocalBusiness Schema.</p>
1021
- </div>
1022
- <div style="border:1px solid #e2e8f0;border-radius:12px;padding:20px;background:#f8fafc;">
1023
- <strong>πŸŽ₯ YouTube & Video SEO</strong>
1024
- <p style="font-size:12px;color:#64748b;margin:4px 0 0;">VideoObject Schema automatique.</p>
1025
- </div>
1026
- <div style="border:1px solid #e2e8f0;border-radius:12px;padding:20px;background:#f8fafc;">
1027
- <strong>🌐 Multilingue & Hreflangs</strong>
1028
- <p style="font-size:12px;color:#64748b;margin:4px 0 0;">DΓ©tection WPML, Polylang & Weglot.</p>
1029
- </div>
1030
- </div>
1031
- </div>
1032
- </div>
1033
- <?php
1034
- }
1035
-
1036
- public function render_shortcodes_view() {
809
+ public function render_redirects_view() {
810
+ global $wpdb;
811
+ $table_redirects = $wpdb->prefix . 'lynxseo_redirects';
812
+ if (isset($_POST['lynx_new_redirect']) && check_admin_referer('lynx_redirect_action')) {
813
+ $src = trim(sanitize_text_field($_POST['source_path']), '/');
814
+ $dst = esc_url_raw($_POST['target_url']);
815
+ $wpdb->insert($table_redirects, array('url_from' => $src, 'url_to' => $dst, 'status_code' => 301, 'created_at' => current_time('mysql')));
816
+ }
817
+ $redirects = $wpdb->get_results("SELECT * FROM $table_redirects ORDER BY id DESC LIMIT 50");
1037
818
  ?>
1038
- <div class="wrap" style="max-width:1100px;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
1039
- <div style="background:#fff;border:1px solid #e2e8f0;border-radius:16px;padding:32px;margin-top:20px;">
1040
- <h1 style="font-size:24px;font-weight:800;color:#0f172a;margin:0 0 8px;">🧩 Les 50 Shortcodes LynxSEO Studio</h1>
819
+ <div class="wrap" style="max-width:1100px;">
820
+ <div style="background:#fff;border:1px solid #e2e8f0;padding:24px;border-radius:12px;margin-top:16px;">
821
+ <h1>πŸ”€ Gestionnaire de Redirections 301 (Table SQL DΓ©diΓ©e)</h1>
822
+ <form method="post" style="margin-bottom:20px;">
823
+ <?php wp_nonce_field('lynx_redirect_action'); ?>
824
+ <input type="hidden" name="lynx_new_redirect" value="1" />
825
+ <input type="text" name="source_path" placeholder="ex: ancienne-url" required style="width:35%;" /> &rarr;
826
+ <input type="text" name="target_url" placeholder="https://..." required style="width:40%;" />
827
+ <button type="submit" class="button button-primary">Ajouter</button>
828
+ </form>
1041
829
  <table class="wp-list-table widefat fixed striped">
1042
- <thead><tr><th>Shortcode</th><th>FonctionnalitΓ©</th></tr></thead>
830
+ <thead><tr><th>Source</th><th>Destination</th><th>Visites (Hits)</th></tr></thead>
1043
831
  <tbody>
1044
- <tr><td><code>[lynxseo_google_business_card]</code></td><td>Badge & Carte Google Business Profile</td></tr>
1045
- <tr><td><code>[lynxseo_youtube_embed id="..."]</code></td><td>Lecteur VidΓ©o YouTube avec VideoObject Schema</td></tr>
1046
- <tr><td><code>[lynxseo_roi_calculator hours="15" rate="60"]</code></td><td>Simulateur ROI & Gains de temps</td></tr>
832
+ <?php if (empty($redirects)): ?>
833
+ <tr><td colspan="3">Aucune redirection enregistrΓ©e.</td></tr>
834
+ <?php else: foreach ($redirects as $r): ?>
835
+ <tr><td>/<?php echo esc_html($r->url_from); ?></td><td><?php echo esc_url($r->url_to); ?></td><td><strong><?php echo intval($r->hits); ?></strong></td></tr>
836
+ <?php endforeach; endif; ?>
1047
837
  </tbody>
1048
838
  </table>
1049
839
  </div>
@@ -1051,20 +841,25 @@ class LynxSeoUltimateEnterprisePlugin {
1051
841
  <?php
1052
842
  }
1053
843
 
1054
- public function render_redirects_view() {
1055
- $redirects = get_option($this->redirects_option, array());
1056
- ?>
1057
- <div class="wrap" style="max-width:1100px;">
1058
- <h1>πŸ”€ Redirections 301 & 302</h1>
1059
- </div>
1060
- <?php
1061
- }
1062
-
1063
844
  public function render_404_view() {
1064
- $logs = get_option($this->logs_option, array());
845
+ global $wpdb;
846
+ $table_404 = $wpdb->prefix . 'lynxseo_404_logs';
847
+ $logs = $wpdb->get_results("SELECT * FROM $table_404 ORDER BY hits DESC LIMIT 50");
1065
848
  ?>
1066
849
  <div class="wrap" style="max-width:1100px;">
1067
- <h1>🚨 Journal des Erreurs 404</h1>
850
+ <div style="background:#fff;border:1px solid #e2e8f0;padding:24px;border-radius:12px;margin-top:16px;">
851
+ <h1>🚨 Journal des Erreurs 404 (Table SQL Dédiée)</h1>
852
+ <table class="wp-list-table widefat fixed striped">
853
+ <thead><tr><th>URL Introuvable</th><th>Hits</th><th>Dernière Visite</th></tr></thead>
854
+ <tbody>
855
+ <?php if (empty($logs)): ?>
856
+ <tr><td colspan="3">Aucune erreur 404.</td></tr>
857
+ <?php else: foreach ($logs as $l): ?>
858
+ <tr><td><code><?php echo esc_html($l->uri); ?></code></td><td><strong><?php echo intval($l->hits); ?></strong></td><td><?php echo esc_html($l->last_accessed); ?></td></tr>
859
+ <?php endforeach; endif; ?>
860
+ </tbody>
861
+ </table>
862
+ </div>
1068
863
  </div>
1069
864
  <?php
1070
865
  }