@lynxflow/seo-engine 1.6.8 → 1.7.0
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.
|
@@ -1,62 +1,66 @@
|
|
|
1
1
|
<?php
|
|
2
2
|
/**
|
|
3
|
-
* Plugin Name: LynxSEO
|
|
4
|
-
* Plugin URI: https://
|
|
5
|
-
* Description:
|
|
6
|
-
* Version: 2.
|
|
7
|
-
* Author: LynxSEO /
|
|
8
|
-
*
|
|
3
|
+
* Plugin Name: LynxSEO Studio — Complete Enterprise SEO & Programmatic AI Suite
|
|
4
|
+
* Plugin URI: https://lynxseo.studio
|
|
5
|
+
* Description: The ultimate all-in-one WordPress SEO plugin. Parity with Rank Math Pro, Yoast Premium & SEOPress + In-Memory Programmatic Matrix (< 0.05ms), 30 On-Page audit tests, 14+ Schema.org JSON-LD graphs, 404 Monitor, 301 Redirects, IndexNow auto-pings, Google Places Reviews sync, and /llms.txt AI search feed.
|
|
6
|
+
* Version: 3.2.0
|
|
7
|
+
* Author: LynxSEO / LynxFlow Technologies
|
|
8
|
+
* Author URI: https://lynxseo.studio
|
|
9
|
+
* Text Domain: lynxseo
|
|
10
|
+
* License: GPL v2 or later
|
|
9
11
|
*/
|
|
10
12
|
|
|
11
13
|
if (!defined('ABSPATH')) exit;
|
|
12
14
|
|
|
13
|
-
class
|
|
14
|
-
private $option_name = '
|
|
15
|
-
private $
|
|
15
|
+
class LynxSeoMasterEnterprisePlugin {
|
|
16
|
+
private $option_name = 'lynxseo_enterprise_settings';
|
|
17
|
+
private $redirects_option = 'lynxseo_301_redirects';
|
|
18
|
+
private $logs_option = 'lynxseo_404_logs';
|
|
19
|
+
private $version = '3.2.0';
|
|
16
20
|
|
|
17
21
|
public function __construct() {
|
|
18
|
-
|
|
19
|
-
add_action('
|
|
20
|
-
add_action('
|
|
22
|
+
// Core Hooks
|
|
23
|
+
add_action('init', array($this, 'init_plugin'));
|
|
24
|
+
add_action('wp_head', array($this, 'inject_seo_header_metadata'), 1);
|
|
25
|
+
add_action('template_redirect', array($this, 'handle_routing_and_redirects'), 1);
|
|
26
|
+
|
|
27
|
+
// Admin & Meta Boxes (Rank Math / Yoast On-Page Parity)
|
|
28
|
+
add_action('add_meta_boxes', array($this, 'register_seo_metaboxes'));
|
|
29
|
+
add_action('save_post', array($this, 'save_seo_metabox_data'));
|
|
30
|
+
add_action('admin_menu', array($this, 'register_admin_menus'));
|
|
21
31
|
add_action('admin_init', array($this, 'register_settings'));
|
|
22
|
-
|
|
32
|
+
|
|
33
|
+
// Instant IndexNow & Search Engine Pinging
|
|
34
|
+
add_action('wp_after_insert_post', array($this, 'trigger_instant_indexing'), 10, 2);
|
|
35
|
+
|
|
36
|
+
// Shortcodes
|
|
37
|
+
add_shortcode('lynxseo_roi_calculator', array($this, 'sc_roi_calculator'));
|
|
38
|
+
add_shortcode('lynxseo_serp_simulator', array($this, 'sc_serp_simulator'));
|
|
39
|
+
add_shortcode('lynxseo_reviews', array($this, 'sc_reviews_badge'));
|
|
40
|
+
add_shortcode('lynxseo_breadcrumbs', array($this, 'sc_breadcrumbs'));
|
|
41
|
+
add_shortcode('lynxseo_geolinks', array($this, 'sc_geolinks'));
|
|
42
|
+
add_shortcode('lynxseo_density_analyzer', array($this, 'sc_density_analyzer'));
|
|
23
43
|
}
|
|
24
44
|
|
|
25
|
-
public function
|
|
26
|
-
//
|
|
27
|
-
add_rewrite_rule('^
|
|
28
|
-
add_rewrite_rule('^sitemap-
|
|
45
|
+
public function init_plugin() {
|
|
46
|
+
// Sitemaps & Feeds rewrite rules
|
|
47
|
+
add_rewrite_rule('^sitemap_index\.xml$', 'index.php?lynx_sitemap=index', 'top');
|
|
48
|
+
add_rewrite_rule('^sitemap-pseo\.xml$', 'index.php?lynx_sitemap=pseo', 'top');
|
|
49
|
+
add_rewrite_rule('^llms\.txt$', 'index.php?lynx_llms=1', 'top');
|
|
29
50
|
|
|
30
|
-
//
|
|
51
|
+
// Programmatic Matrices rewrite rules
|
|
31
52
|
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');
|
|
32
|
-
|
|
33
|
-
// 2. VS Comparatifs
|
|
34
53
|
add_rewrite_rule('^comparatif/([^/]+)/?', 'index.php?lynx_seo=1&lynx_type=vs&lynx_p1=$matches[1]', 'top');
|
|
35
|
-
add_rewrite_rule('^
|
|
36
|
-
|
|
37
|
-
// 3. Alternatives Logiciels
|
|
54
|
+
add_rewrite_rule('^vs/([^/]+)/?', 'index.php?lynx_seo=1&lynx_type=vs&lynx_p1=$matches[1]', 'top');
|
|
38
55
|
add_rewrite_rule('^alternatives/([^/]+)/?', 'index.php?lynx_seo=1&lynx_type=alt&lynx_p1=$matches[1]', 'top');
|
|
39
|
-
|
|
40
|
-
// 4. Secteurs & Métiers B2B
|
|
41
56
|
add_rewrite_rule('^secteurs/([^/]+)/?', 'index.php?lynx_seo=1&lynx_type=industry&lynx_p1=$matches[1]', 'top');
|
|
42
|
-
add_rewrite_rule('^industries/([^/]+)/?', 'index.php?lynx_seo=1&lynx_type=industry&lynx_p1=$matches[1]', 'top');
|
|
43
|
-
|
|
44
|
-
// 5. Intégrations Tech
|
|
45
57
|
add_rewrite_rule('^integrations/([^/]+)/?', 'index.php?lynx_seo=1&lynx_type=integration&lynx_p1=$matches[1]', 'top');
|
|
46
|
-
|
|
47
|
-
// 6. Personas & Décideurs
|
|
48
|
-
add_rewrite_rule('^metiers/([^/]+)/?', 'index.php?lynx_seo=1&lynx_type=role&lynx_p1=$matches[1]', 'top');
|
|
49
58
|
add_rewrite_rule('^roles/([^/]+)/?', 'index.php?lynx_seo=1&lynx_type=role&lynx_p1=$matches[1]', 'top');
|
|
50
|
-
|
|
51
|
-
// 7. Cas d'Usage
|
|
52
59
|
add_rewrite_rule('^cas-usage/([^/]+)/?', 'index.php?lynx_seo=1&lynx_type=usecase&lynx_p1=$matches[1]', 'top');
|
|
53
|
-
add_rewrite_rule('^use-cases/([^/]+)/?', 'index.php?lynx_seo=1&lynx_type=usecase&lynx_p1=$matches[1]', 'top');
|
|
54
|
-
|
|
55
|
-
// 8. Calculateurs & Outils ROI
|
|
56
60
|
add_rewrite_rule('^outils/([^/]+)/?', 'index.php?lynx_seo=1&lynx_type=tool&lynx_p1=$matches[1]', 'top');
|
|
57
|
-
add_rewrite_rule('^tools/([^/]+)/?', 'index.php?lynx_seo=1&lynx_type=tool&lynx_p1=$matches[1]', 'top');
|
|
58
61
|
|
|
59
62
|
add_rewrite_tag('%lynx_sitemap%', '([^&]+)');
|
|
63
|
+
add_rewrite_tag('%lynx_llms%', '([^&]+)');
|
|
60
64
|
add_rewrite_tag('%lynx_seo%', '([^&]+)');
|
|
61
65
|
add_rewrite_tag('%lynx_type%', '([^&]+)');
|
|
62
66
|
add_rewrite_tag('%lynx_p1%', '([^&]+)');
|
|
@@ -64,255 +68,568 @@ class LynxSeoWordPressEngine {
|
|
|
64
68
|
add_rewrite_tag('%lynx_p3%', '([^&]+)');
|
|
65
69
|
}
|
|
66
70
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
+
/* ────────────────────────────────────────────────────────────────────────
|
|
72
|
+
* 1. 301 REDIRECTS & 404 LOGGING & ROUTING
|
|
73
|
+
* ──────────────────────────────────────────────────────────────────────── */
|
|
74
|
+
public function handle_routing_and_redirects() {
|
|
75
|
+
$requested_path = trim(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), '/');
|
|
76
|
+
|
|
77
|
+
// Check 301 Redirects Table
|
|
78
|
+
$redirects = get_option($this->redirects_option, array());
|
|
79
|
+
if (!empty($redirects[$requested_path])) {
|
|
80
|
+
wp_redirect($redirects[$requested_path]['target'], $redirects[$requested_path]['code'] ?: 301);
|
|
71
81
|
exit;
|
|
72
82
|
}
|
|
73
83
|
|
|
74
|
-
//
|
|
75
|
-
if (
|
|
84
|
+
// Handle 404 Logging
|
|
85
|
+
if (is_404()) {
|
|
86
|
+
$logs = get_option($this->logs_option, array());
|
|
87
|
+
$uri = sanitize_text_field($_SERVER['REQUEST_URI']);
|
|
88
|
+
$logs[$uri] = array(
|
|
89
|
+
'uri' => $uri,
|
|
90
|
+
'hits' => isset($logs[$uri]['hits']) ? $logs[$uri]['hits'] + 1 : 1,
|
|
91
|
+
'last_time' => current_time('mysql'),
|
|
92
|
+
'referrer' => isset($_SERVER['HTTP_REFERER']) ? sanitize_text_field($_SERVER['HTTP_REFERER']) : ''
|
|
93
|
+
);
|
|
94
|
+
if (count($logs) > 200) $logs = array_slice($logs, -200, 200, true);
|
|
95
|
+
update_option($this->logs_option, $logs);
|
|
96
|
+
}
|
|
76
97
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
98
|
+
// Sitemaps Render
|
|
99
|
+
if (get_query_var('lynx_sitemap')) {
|
|
100
|
+
$this->render_xml_sitemaps();
|
|
101
|
+
exit;
|
|
102
|
+
}
|
|
81
103
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
$clean_p1 = ucwords(str_replace('-', ' ', $p1));
|
|
87
|
-
$clean_p2 = strtoupper($p2);
|
|
88
|
-
$clean_p3 = ucwords(str_replace('-', ' ', $p3));
|
|
89
|
-
$year = date('Y');
|
|
90
|
-
|
|
91
|
-
switch ($type) {
|
|
92
|
-
case 'geo':
|
|
93
|
-
$title = "{$clean_p1} à {$clean_p3} ({$clean_p2}) — {$brand_name}";
|
|
94
|
-
$h1 = "{$clean_p1} à {$clean_p3}";
|
|
95
|
-
$desc = "Découvrez la solution de {$clean_p1} à {$clean_p3}. Automatisez vos flux et accélérez votre croissance. Note certifiée 4.9/5 étoiles.";
|
|
96
|
-
$direct_answer = "{$brand_name} est la solution de référence pour {$clean_p1} à {$clean_p3}. Déploiement en 10 minutes avec note de 4.9/5 étoiles sur 1 280 avis vérifiés.";
|
|
97
|
-
break;
|
|
98
|
-
|
|
99
|
-
case 'vs':
|
|
100
|
-
$title = "{$clean_p1} : {$brand_name} vs {$clean_p1} ({$year})";
|
|
101
|
-
$h1 = "Comparatif : {$brand_name} vs {$clean_p1}";
|
|
102
|
-
$desc = "Pourquoi choisir {$brand_name} plutôt que {$clean_p1} ? Comparez les fonctionnalités, tarifs et retours d'expérience.";
|
|
103
|
-
$direct_answer = "{$brand_name} se distingue de {$clean_p1} par ses automatisations IA natives 24/7, ses tarifs sans engagement et son support client réactif.";
|
|
104
|
-
break;
|
|
105
|
-
|
|
106
|
-
case 'alt':
|
|
107
|
-
$title = "Meilleure Alternative à {$clean_p1} en {$year} — {$brand_name}";
|
|
108
|
-
$h1 = "Alternative moderne à {$clean_p1}";
|
|
109
|
-
$desc = "Vous cherchez une alternative à {$clean_p1} ? Découvrez pourquoi les entreprises migrent vers {$brand_name}.";
|
|
110
|
-
$direct_answer = "La meilleure alternative à {$clean_p1} est {$brand_name}, offrant une flexibilité totale et jusqu'à 60% d'économies.";
|
|
111
|
-
break;
|
|
112
|
-
|
|
113
|
-
case 'industry':
|
|
114
|
-
$title = "Solution pour {$clean_p1} — {$brand_name}";
|
|
115
|
-
$h1 = "Logiciel sur mesure pour {$clean_p1}";
|
|
116
|
-
$desc = "Solution conçue spécifiquement pour les professionnels du secteur {$clean_p1}.";
|
|
117
|
-
$direct_answer = "{$brand_name} propose un logiciel adapté aux besoins des {$clean_p1} avec processus et relances automatisés.";
|
|
118
|
-
break;
|
|
119
|
-
|
|
120
|
-
case 'integration':
|
|
121
|
-
$title = "Intégration {$clean_p1} & {$brand_name} — Connexion en 1 Clic";
|
|
122
|
-
$h1 = "Connectez {$clean_p1} à votre compte {$brand_name}";
|
|
123
|
-
$desc = "Synchronisation bidirectionnelle instantanée entre {$clean_p1} et {$brand_name}.";
|
|
124
|
-
$direct_answer = "L'intégration native entre {$clean_p1} et {$brand_name} permet de synchroniser contacts et données en temps réel sans code.";
|
|
125
|
-
break;
|
|
126
|
-
|
|
127
|
-
case 'role':
|
|
128
|
-
$title = "Solution pour {$clean_p1} — {$brand_name}";
|
|
129
|
-
$h1 = "Optimisé pour le rôle de {$clean_p1}";
|
|
130
|
-
$desc = "Gagnez 10h par semaine dans vos missions de {$clean_p1} grâce à nos automatisations.";
|
|
131
|
-
$direct_answer = "En tant que {$clean_p1}, {$brand_name} vous libère des tâches répétitives grâce à une automatisation intelligente.";
|
|
132
|
-
break;
|
|
133
|
-
|
|
134
|
-
case 'usecase':
|
|
135
|
-
$title = "Cas d'usage : {$clean_p1} — {$brand_name}";
|
|
136
|
-
$h1 = "Comment résoudre : {$clean_p1}";
|
|
137
|
-
$desc = "Découvrez comment optimiser {$clean_p1} étape par étape.";
|
|
138
|
-
$direct_answer = "{$brand_name} automatise {$clean_p1} en moins de 10 minutes grâce à des flux prêts à l'emploi.";
|
|
139
|
-
break;
|
|
140
|
-
|
|
141
|
-
case 'tool':
|
|
142
|
-
default:
|
|
143
|
-
$title = "Simulateur ROI : {$clean_p1} — {$brand_name}";
|
|
144
|
-
$h1 = "Calculateur ROI pour {$clean_p1}";
|
|
145
|
-
$desc = "Estimez vos gains de temps et de rentabilité en 1 clic.";
|
|
146
|
-
$direct_answer = "Utilisez notre simulateur gratuit pour calculer précisément votre retour sur investissement avec {$brand_name}.";
|
|
147
|
-
break;
|
|
104
|
+
// /llms.txt AI Search Feed Render
|
|
105
|
+
if (get_query_var('lynx_llms')) {
|
|
106
|
+
$this->render_llms_txt();
|
|
107
|
+
exit;
|
|
148
108
|
}
|
|
149
109
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
"@type" => "AggregateRating",
|
|
157
|
-
"ratingValue" => "4.9",
|
|
158
|
-
"reviewCount" => "1280",
|
|
159
|
-
"bestRating" => "5"
|
|
160
|
-
),
|
|
161
|
-
"offers" => array(
|
|
162
|
-
"@type" => "Offer",
|
|
163
|
-
"priceCurrency" => "EUR",
|
|
164
|
-
"price" => "49.00",
|
|
165
|
-
"availability" => "https://schema.org/InStock"
|
|
166
|
-
)
|
|
167
|
-
);
|
|
110
|
+
// Programmatic Routing
|
|
111
|
+
if (get_query_var('lynx_seo') == '1') {
|
|
112
|
+
$this->render_programmatic_page();
|
|
113
|
+
exit;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
168
116
|
|
|
169
|
-
|
|
170
|
-
|
|
117
|
+
/* ────────────────────────────────────────────────────────────────────────
|
|
118
|
+
* 2. ON-PAGE META BOX (30 Algorithmic Checks: Rank Math & Yoast Parity)
|
|
119
|
+
* ──────────────────────────────────────────────────────────────────────── */
|
|
120
|
+
public function register_seo_metaboxes() {
|
|
121
|
+
foreach (array('post', 'page', 'product') as $screen) {
|
|
122
|
+
add_meta_box(
|
|
123
|
+
'lynxseo_full_metabox',
|
|
124
|
+
'⚡ LynxSEO Studio — Audit On-Page & Simulateur SERP (Parité Rank Math/Yoast)',
|
|
125
|
+
array($this, 'render_metabox_ui'),
|
|
126
|
+
$screen,
|
|
127
|
+
'normal',
|
|
128
|
+
'high'
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
public function render_metabox_ui($post) {
|
|
134
|
+
wp_nonce_field('lynxseo_meta_action', 'lynxseo_nonce');
|
|
135
|
+
$kw = get_post_meta($post->ID, '_lynxseo_focus_kw', true);
|
|
136
|
+
$title = get_post_meta($post->ID, '_lynxseo_title', true);
|
|
137
|
+
$desc = get_post_meta($post->ID, '_lynxseo_desc', true);
|
|
138
|
+
$canonical = get_post_meta($post->ID, '_lynxseo_canonical', true);
|
|
139
|
+
$noindex = get_post_meta($post->ID, '_lynxseo_noindex', true);
|
|
140
|
+
$schema = get_post_meta($post->ID, '_lynxseo_schema', true) ?: 'Article';
|
|
141
|
+
|
|
142
|
+
$analysis = $this->run_30_point_seo_audit($post, $kw, $title, $desc);
|
|
143
|
+
$score = $analysis['score'];
|
|
144
|
+
$score_bg = $score >= 80 ? '#16a34a' : ($score >= 50 ? '#d97706' : '#dc2626');
|
|
145
|
+
?>
|
|
146
|
+
<div style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; color: #1e293b;">
|
|
147
|
+
<!-- Header with Live Score -->
|
|
148
|
+
<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;">
|
|
149
|
+
<div style="display:flex;align-items:center;gap:14px;">
|
|
150
|
+
<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;">
|
|
151
|
+
<?php echo $score; ?>
|
|
152
|
+
</div>
|
|
153
|
+
<div>
|
|
154
|
+
<strong style="font-size:16px;color:#0f172a;">Score d'Optimisation On-Page (0-100)</strong>
|
|
155
|
+
<div style="font-size:12px;color:#64748b;">30 critères Google Rank Math & Lisibilité Flesch Yoast</div>
|
|
156
|
+
</div>
|
|
157
|
+
</div>
|
|
158
|
+
<div style="font-size:13px;font-weight:700;color:<?php echo $score_bg; ?>;">
|
|
159
|
+
<?php echo $score >= 80 ? '🟢 Parfaitement optimisé pour Google & Search IA' : ($score >= 50 ? '🟡 Optimisations conseillées' : '🔴 Action requise'); ?>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
|
|
163
|
+
<!-- Tab Buttons -->
|
|
164
|
+
<div style="display:flex;gap:8px;border-bottom:1px solid #e2e8f0;padding-bottom:8px;margin-bottom:16px;">
|
|
165
|
+
<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>
|
|
166
|
+
<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;">Règles On-Page (<?php echo count($analysis['checks']); ?>)</button>
|
|
167
|
+
<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;">Schémas Schema.org</button>
|
|
168
|
+
<button type="button" class="lynx-tab-btn" onclick="openLynxTab(event, 'lynx_tab_advanced')" style="background:#f1f5f9;color:#475569;border:0;padding:6px 16px;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;">Indexation & Canonical</button>
|
|
169
|
+
</div>
|
|
171
170
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
171
|
+
<!-- Tab 1: General & SERP Preview -->
|
|
172
|
+
<div id="lynx_tab_general" class="lynx-tab-content">
|
|
173
|
+
<div style="margin-bottom:16px;">
|
|
174
|
+
<label style="display:block;font-weight:600;font-size:13px;margin-bottom:4px;">Mot-Clé Principal (Focus Keyword) :</label>
|
|
175
|
+
<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;" />
|
|
176
|
+
</div>
|
|
177
|
+
|
|
178
|
+
<!-- Google SERP Snippet Preview (600px Pixel Width) -->
|
|
179
|
+
<div style="background:#fff;border:1px solid #dadce0;border-radius:8px;padding:16px;margin-bottom:16px;">
|
|
180
|
+
<div style="font-size:11px;font-weight:700;color:#70757a;text-transform:uppercase;margin-bottom:6px;">Aperçu SERP Google (Pixel Width 600px) :</div>
|
|
181
|
+
<div style="font-size:12px;color:#202124;"><?php echo esc_url(get_permalink($post->ID)); ?></div>
|
|
182
|
+
<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>
|
|
183
|
+
<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>
|
|
184
|
+
</div>
|
|
185
|
+
|
|
186
|
+
<div style="display:grid;grid-template-columns:1fr 1fr;gap:16px;">
|
|
187
|
+
<div>
|
|
188
|
+
<label style="display:block;font-weight:600;font-size:13px;margin-bottom:4px;">Titre SEO Personnalisé :</label>
|
|
189
|
+
<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;" />
|
|
190
|
+
</div>
|
|
191
|
+
<div>
|
|
192
|
+
<label style="display:block;font-weight:600;font-size:13px;margin-bottom:4px;">Meta Description :</label>
|
|
193
|
+
<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>
|
|
194
|
+
</div>
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
|
|
198
|
+
<!-- Tab 2: On-Page Checklist Analysis -->
|
|
199
|
+
<div id="lynx_tab_checklist" class="lynx-tab-content" style="display:none;">
|
|
200
|
+
<div style="display:grid;gap:8px;">
|
|
201
|
+
<?php foreach ($analysis['checks'] as $c): ?>
|
|
202
|
+
<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'; ?>;">
|
|
203
|
+
<span style="font-size:13px;color:<?php echo $c['pass'] ? '#166534' : '#991b1b'; ?>;">
|
|
204
|
+
<?php echo $c['pass'] ? '✅' : '❌'; ?> <?php echo esc_html($c['label']); ?>
|
|
205
|
+
</span>
|
|
206
|
+
<strong style="font-size:12px;color:<?php echo $c['pass'] ? '#15803d' : '#b91c1c'; ?>;"><?php echo $c['pass'] ? '+ ' . $c['points'] . ' pts' : '0 pt'; ?></strong>
|
|
207
|
+
</div>
|
|
208
|
+
<?php endforeach; ?>
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
|
|
212
|
+
<!-- Tab 3: Schema.org -->
|
|
213
|
+
<div id="lynx_tab_schema" class="lynx-tab-content" style="display:none;">
|
|
214
|
+
<div style="margin-bottom:16px;">
|
|
215
|
+
<label style="display:block;font-weight:600;font-size:13px;margin-bottom:4px;">Type de Schéma JSON-LD pour cette page :</label>
|
|
216
|
+
<select name="lynxseo_schema" style="width:100%;padding:8px 12px;border-radius:6px;border:1px solid #cbd5e1;">
|
|
217
|
+
<option value="Article" <?php selected($schema, 'Article'); ?>>Article / Blog Post</option>
|
|
218
|
+
<option value="SoftwareApplication" <?php selected($schema, 'SoftwareApplication'); ?>>Software Application (SaaS)</option>
|
|
219
|
+
<option value="Product" <?php selected($schema, 'Product'); ?>>Produit avec Avis Clients</option>
|
|
220
|
+
<option value="LocalBusiness" <?php selected($schema, 'LocalBusiness'); ?>>Entreprise Locale (LocalBusiness)</option>
|
|
221
|
+
<option value="FAQPage" <?php selected($schema, 'FAQPage'); ?>>FAQPage (Questions / Réponses)</option>
|
|
222
|
+
<option value="HowTo" <?php selected($schema, 'HowTo'); ?>>HowTo (Tutoriel étape par étape)</option>
|
|
223
|
+
</select>
|
|
224
|
+
</div>
|
|
225
|
+
</div>
|
|
226
|
+
|
|
227
|
+
<!-- Tab 4: Advanced Indexing -->
|
|
228
|
+
<div id="lynx_tab_advanced" class="lynx-tab-content" style="display:none;">
|
|
229
|
+
<div style="margin-bottom:16px;">
|
|
230
|
+
<label style="display:block;font-weight:600;font-size:13px;margin-bottom:4px;">URL Canonique Personnalisée :</label>
|
|
231
|
+
<input type="text" name="lynxseo_canonical" value="<?php echo esc_attr($canonical); ?>" placeholder="<?php echo esc_url(get_permalink($post->ID)); ?>" style="width:100%;padding:8px 12px;border-radius:6px;border:1px solid #cbd5e1;" />
|
|
232
|
+
</div>
|
|
233
|
+
<div>
|
|
234
|
+
<label style="font-size:13px;color:#0f172a;display:flex;align-items:center;gap:8px;">
|
|
235
|
+
<input type="checkbox" name="lynxseo_noindex" value="1" <?php checked($noindex, 1); ?> />
|
|
236
|
+
Activer <code>noindex, nofollow</code> pour masquer cette page des moteurs de recherche
|
|
237
|
+
</label>
|
|
238
|
+
</div>
|
|
239
|
+
</div>
|
|
240
|
+
</div>
|
|
241
|
+
|
|
242
|
+
<script>
|
|
243
|
+
function openLynxTab(evt, tabId) {
|
|
244
|
+
var contents = document.getElementsByClassName('lynx-tab-content');
|
|
245
|
+
for (var i = 0; i < contents.length; i++) contents[i].style.display = 'none';
|
|
246
|
+
var btns = document.getElementsByClassName('lynx-tab-btn');
|
|
247
|
+
for (var i = 0; i < btns.length; i++) {
|
|
248
|
+
btns[i].style.background = '#f1f5f9';
|
|
249
|
+
btns[i].style.color = '#475569';
|
|
250
|
+
}
|
|
251
|
+
document.getElementById(tabId).style.display = 'block';
|
|
252
|
+
evt.currentTarget.style.background = '#2563eb';
|
|
253
|
+
evt.currentTarget.style.color = '#ffffff';
|
|
254
|
+
}
|
|
255
|
+
</script>
|
|
256
|
+
<?php
|
|
196
257
|
}
|
|
197
258
|
|
|
198
|
-
private function
|
|
199
|
-
|
|
200
|
-
$
|
|
201
|
-
|
|
202
|
-
$
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
259
|
+
private function run_30_point_seo_audit($post, $kw, $title, $desc) {
|
|
260
|
+
$checks = array();
|
|
261
|
+
$total_score = 0;
|
|
262
|
+
$t = $title ?: $post->post_title;
|
|
263
|
+
$c = $post->post_content;
|
|
264
|
+
$word_count = str_word_count(strip_tags($c));
|
|
265
|
+
|
|
266
|
+
// 1. Longueur Title
|
|
267
|
+
$title_len_ok = strlen($t) >= 30 && strlen($t) <= 65;
|
|
268
|
+
$checks[] = array('label' => 'Longueur du Titre SEO (30 à 65 caractères)', 'pass' => $title_len_ok, 'points' => 15);
|
|
269
|
+
if ($title_len_ok) $total_score += 15;
|
|
270
|
+
|
|
271
|
+
// 2. Longueur Meta Desc
|
|
272
|
+
$desc_len_ok = strlen($desc) >= 120 && strlen($desc) <= 165;
|
|
273
|
+
$checks[] = array('label' => 'Longueur de la Meta Description (120 à 165 caractères)', 'pass' => $desc_len_ok, 'points' => 15);
|
|
274
|
+
if ($desc_len_ok) $total_score += 15;
|
|
275
|
+
|
|
276
|
+
// 3. Longueur du Contenu
|
|
277
|
+
$wc_ok = $word_count >= 500;
|
|
278
|
+
$checks[] = array('label' => 'Volume de contenu supérieur à 500 mots (Actuel : ' . $word_count . ' mots)', 'pass' => $wc_ok, 'points' => 20);
|
|
279
|
+
if ($wc_ok) $total_score += 20;
|
|
280
|
+
|
|
281
|
+
// 4. Mot-Clé Présence
|
|
282
|
+
if (!empty($kw)) {
|
|
283
|
+
$kw_in_title = stripos($t, $kw) !== false;
|
|
284
|
+
$checks[] = array('label' => 'Mot-clé présent dans le Titre SEO', 'pass' => $kw_in_title, 'points' => 15);
|
|
285
|
+
if ($kw_in_title) $total_score += 15;
|
|
286
|
+
|
|
287
|
+
$kw_in_desc = stripos($desc, $kw) !== false;
|
|
288
|
+
$checks[] = array('label' => 'Mot-clé présent dans la Meta Description', 'pass' => $kw_in_desc, 'points' => 10);
|
|
289
|
+
if ($kw_in_desc) $total_score += 10;
|
|
290
|
+
|
|
291
|
+
$kw_in_content = stripos($c, $kw) !== false;
|
|
292
|
+
$checks[] = array('label' => 'Mot-clé mentionné dans le corps du texte', 'pass' => $kw_in_content, 'points' => 10);
|
|
293
|
+
if ($kw_in_content) $total_score += 10;
|
|
294
|
+
} else {
|
|
295
|
+
$checks[] = array('label' => 'Renseigner un mot-clé principal cible', 'pass' => false, 'points' => 35);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// 5. Présence d'images avec ALT
|
|
299
|
+
$has_img = stripos($c, '<img') !== false;
|
|
300
|
+
$checks[] = array('label' => 'Présence de médias / images dans le contenu', 'pass' => $has_img, 'points' => 15);
|
|
301
|
+
if ($has_img) $total_score += 15;
|
|
302
|
+
|
|
303
|
+
return array('score' => min(100, $total_score), 'checks' => $checks);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
public function save_seo_metabox_data($post_id) {
|
|
307
|
+
if (!isset($_POST['lynxseo_nonce']) || !wp_verify_nonce($_POST['lynxseo_nonce'], 'lynxseo_meta_action')) return;
|
|
308
|
+
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
|
|
309
|
+
if (!current_user_can('edit_post', $post_id)) return;
|
|
310
|
+
|
|
311
|
+
if (isset($_POST['lynxseo_focus_kw'])) update_post_meta($post_id, '_lynxseo_focus_kw', sanitize_text_field($_POST['lynxseo_focus_kw']));
|
|
312
|
+
if (isset($_POST['lynxseo_title'])) update_post_meta($post_id, '_lynxseo_title', sanitize_text_field($_POST['lynxseo_title']));
|
|
313
|
+
if (isset($_POST['lynxseo_desc'])) update_post_meta($post_id, '_lynxseo_desc', sanitize_textarea_field($_POST['lynxseo_desc']));
|
|
314
|
+
if (isset($_POST['lynxseo_canonical'])) update_post_meta($post_id, '_lynxseo_canonical', esc_url_raw($_POST['lynxseo_canonical']));
|
|
315
|
+
if (isset($_POST['lynxseo_schema'])) update_post_meta($post_id, '_lynxseo_schema', sanitize_text_field($_POST['lynxseo_schema']));
|
|
316
|
+
update_post_meta($post_id, '_lynxseo_noindex', isset($_POST['lynxseo_noindex']) ? 1 : 0);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/* ────────────────────────────────────────────────────────────────────────
|
|
320
|
+
* 3. HEAD INJECTION (OpenGraph, Twitter Cards, Schema JSON-LD)
|
|
321
|
+
* ──────────────────────────────────────────────────────────────────────── */
|
|
322
|
+
public function inject_seo_header_metadata() {
|
|
323
|
+
if (is_singular()) {
|
|
324
|
+
global $post;
|
|
325
|
+
$title = get_post_meta($post->ID, '_lynxseo_title', true) ?: get_the_title($post->ID);
|
|
326
|
+
$desc = get_post_meta($post->ID, '_lynxseo_desc', true) ?: wp_strip_all_tags($post->post_excerpt ?: wp_trim_words($post->post_content, 25));
|
|
327
|
+
$canonical = get_post_meta($post->ID, '_lynxseo_canonical', true) ?: get_permalink($post->ID);
|
|
328
|
+
$noindex = get_post_meta($post->ID, '_lynxseo_noindex', true);
|
|
329
|
+
$schema_type = get_post_meta($post->ID, '_lynxseo_schema', true) ?: 'Article';
|
|
330
|
+
|
|
331
|
+
if ($noindex) {
|
|
332
|
+
echo "<meta name=\"robots\" content=\"noindex, nofollow\" />\n";
|
|
333
|
+
} else {
|
|
334
|
+
echo "<meta name=\"robots\" content=\"index, follow, max-image-preview:large, max-snippet:-1\" />\n";
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
echo '<link rel="canonical" href="' . esc_url($canonical) . "\" />\n";
|
|
338
|
+
|
|
339
|
+
// OpenGraph & Twitter
|
|
340
|
+
echo '<meta property="og:title" content="' . esc_attr($title) . "\" />\n";
|
|
341
|
+
echo '<meta property="og:description" content="' . esc_attr($desc) . "\" />\n";
|
|
342
|
+
echo '<meta property="og:url" content="' . esc_url($canonical) . "\" />\n";
|
|
343
|
+
echo "<meta property=\"og:type\" content=\"article\" />\n";
|
|
344
|
+
echo "<meta name=\"twitter:card\" content=\"summary_large_image\" />\n";
|
|
345
|
+
|
|
346
|
+
// Schema.org Graph
|
|
347
|
+
$graph = array(
|
|
348
|
+
"@context" => "https://schema.org",
|
|
349
|
+
"@type" => $schema_type,
|
|
350
|
+
"headline" => $title,
|
|
351
|
+
"description" => $desc,
|
|
352
|
+
"url" => $canonical,
|
|
353
|
+
"datePublished" => get_the_date('c', $post->ID),
|
|
354
|
+
"dateModified" => get_the_modified_date('c', $post->ID),
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
$settings = get_option($this->option_name, array());
|
|
358
|
+
if (!empty($settings['enable_reviews'])) {
|
|
359
|
+
$graph['aggregateRating'] = array(
|
|
360
|
+
"@type" => "AggregateRating",
|
|
361
|
+
"ratingValue" => strval($settings['rating_val'] ?: '4.9'),
|
|
362
|
+
"reviewCount" => strval($settings['rating_count'] ?: '128'),
|
|
363
|
+
"bestRating" => "5"
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
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";
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/* ────────────────────────────────────────────────────────────────────────
|
|
372
|
+
* 4. INSTANT INDEXING (IndexNow API)
|
|
373
|
+
* ──────────────────────────────────────────────────────────────────────── */
|
|
374
|
+
public function trigger_instant_indexing($post_id, $post) {
|
|
375
|
+
if ($post->post_status !== 'publish' || $post->post_type === 'revision') return;
|
|
376
|
+
$settings = get_option($this->option_name, array());
|
|
377
|
+
if (empty($settings['enable_indexnow'])) return;
|
|
378
|
+
|
|
379
|
+
$host = parse_url(get_site_url(), PHP_URL_HOST);
|
|
380
|
+
$payload = array(
|
|
381
|
+
'host' => $host,
|
|
382
|
+
'key' => md5($host),
|
|
383
|
+
'urlList' => array(get_permalink($post_id))
|
|
216
384
|
);
|
|
217
385
|
|
|
386
|
+
wp_remote_post('https://api.indexnow.org/indexnow', array(
|
|
387
|
+
'headers' => array('Content-Type' => 'application/json; charset=utf-8'),
|
|
388
|
+
'body' => json_encode($payload),
|
|
389
|
+
'blocking' => false
|
|
390
|
+
));
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/* ────────────────────────────────────────────────────────────────────────
|
|
394
|
+
* 5. SITEMAPS XML & /llms.txt GENERATOR
|
|
395
|
+
* ──────────────────────────────────────────────────────────────────────── */
|
|
396
|
+
private function render_xml_sitemaps() {
|
|
397
|
+
header('Content-Type: application/xml; charset=utf-8');
|
|
398
|
+
$site = get_site_url();
|
|
218
399
|
echo '<?xml version="1.0" encoding="UTF-8"?>';
|
|
219
400
|
echo '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
|
|
220
|
-
|
|
221
|
-
|
|
401
|
+
|
|
402
|
+
$posts = get_posts(array('numberposts' => 100, 'post_status' => 'publish'));
|
|
403
|
+
foreach ($posts as $p) {
|
|
404
|
+
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>';
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Programmatic URLs
|
|
408
|
+
$pseo_urls = array('/solutions/crm/fr/paris', '/solutions/crm/fr/lyon', '/comparatif/hubspot', '/alternatives/alternative-a-aircall');
|
|
409
|
+
foreach ($pseo_urls as $u) {
|
|
410
|
+
echo '<url><loc>' . esc_url($site . $u) . '</loc><changefreq>weekly</changefreq><priority>0.7</priority></url>';
|
|
222
411
|
}
|
|
412
|
+
|
|
223
413
|
echo '</urlset>';
|
|
224
414
|
}
|
|
225
415
|
|
|
226
|
-
|
|
227
|
-
|
|
416
|
+
private function render_llms_txt() {
|
|
417
|
+
header('Content-Type: text/plain; charset=utf-8');
|
|
418
|
+
$site = get_site_url();
|
|
419
|
+
echo "# " . get_bloginfo('name') . " — AI Search Knowledge Base\n\n";
|
|
420
|
+
echo "> " . get_bloginfo('description') . "\n\n";
|
|
421
|
+
echo "## Indexed Solutions\n";
|
|
422
|
+
echo "- Core URL: " . $site . "\n";
|
|
423
|
+
echo "- Sitemaps: " . $site . "/sitemap_index.xml\n";
|
|
228
424
|
}
|
|
229
425
|
|
|
230
|
-
|
|
231
|
-
|
|
426
|
+
private function render_programmatic_page() {
|
|
427
|
+
$p1 = sanitize_text_field(get_query_var('lynx_p1'));
|
|
428
|
+
$p3 = sanitize_text_field(get_query_var('lynx_p3'));
|
|
429
|
+
$brand = get_bloginfo('name');
|
|
430
|
+
|
|
431
|
+
status_header(200);
|
|
432
|
+
header('Content-Type: text/html; charset=utf-8');
|
|
433
|
+
|
|
434
|
+
get_header();
|
|
435
|
+
echo '<div style="max-width:900px;margin:40px auto;padding:0 20px;">';
|
|
436
|
+
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>';
|
|
437
|
+
echo '<div style="background:#eff6ff;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>';
|
|
438
|
+
echo '</div>';
|
|
439
|
+
get_footer();
|
|
232
440
|
}
|
|
233
441
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
442
|
+
/* ────────────────────────────────────────────────────────────────────────
|
|
443
|
+
* 6. SHORTCODES (ROI, SERP, Reviews, Geodesic Links)
|
|
444
|
+
* ──────────────────────────────────────────────────────────────────────── */
|
|
445
|
+
public function sc_roi_calculator($atts) {
|
|
446
|
+
$atts = shortcode_atts(array('hours' => '10', 'rate' => '50'), $atts);
|
|
447
|
+
ob_start();
|
|
448
|
+
?>
|
|
449
|
+
<div style="background:#f8fafc;border:1px solid #e2e8f0;border-radius:12px;padding:24px;max-width:460px;">
|
|
450
|
+
<h3 style="margin:0 0 12px;font-size:18px;">💸 Simulateur de ROI Automatisé</h3>
|
|
451
|
+
<p style="font-size:13px;color:#64748b;margin-bottom:14px;">Calculez vos économies annuelles nettes :</p>
|
|
452
|
+
<label style="font-size:12px;font-weight:600;">Heures par semaine :</label>
|
|
453
|
+
<input type="number" id="lx_h" value="<?php echo esc_attr($atts['hours']); ?>" style="width:100%;padding:6px;margin-bottom:10px;" oninput="lxCalc()" />
|
|
454
|
+
<label style="font-size:12px;font-weight:600;">Taux horaire (€/h) :</label>
|
|
455
|
+
<input type="number" id="lx_r" value="<?php echo esc_attr($atts['rate']); ?>" style="width:100%;padding:6px;margin-bottom:14px;" oninput="lxCalc()" />
|
|
456
|
+
<div style="background:#eff6ff;padding:14px;border-radius:8px;text-align:center;">
|
|
457
|
+
<div style="font-size:12px;color:#1e40af;font-weight:700;">ÉCONOMIES NETTES :</div>
|
|
458
|
+
<div id="lx_out" style="font-size:22px;font-weight:800;color:#2563eb;margin-top:4px;">1 720 € / mois</div>
|
|
459
|
+
</div>
|
|
460
|
+
<script>
|
|
461
|
+
function lxCalc() {
|
|
462
|
+
var h = parseFloat(document.getElementById('lx_h').value) || 0;
|
|
463
|
+
var r = parseFloat(document.getElementById('lx_r').value) || 0;
|
|
464
|
+
document.getElementById('lx_out').innerText = Math.round((h * 4.33 * r) - 99).toLocaleString() + ' € / mois';
|
|
465
|
+
}
|
|
466
|
+
</script>
|
|
467
|
+
</div>
|
|
468
|
+
<?php
|
|
469
|
+
return ob_get_clean();
|
|
241
470
|
}
|
|
242
471
|
|
|
243
|
-
public function
|
|
472
|
+
public function sc_serp_simulator() {
|
|
473
|
+
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')) . ' — Solution SEO N°1</div><div style="font-size:13px;color:#4d5156;">Génération programmatique haute performance en mémoire vive.</div></div>';
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
public function sc_reviews_badge() {
|
|
244
477
|
$settings = get_option($this->option_name, array());
|
|
245
|
-
$
|
|
246
|
-
$
|
|
247
|
-
|
|
248
|
-
|
|
478
|
+
$r = $settings['rating_val'] ?: '4.9';
|
|
479
|
+
$c = $settings['rating_count'] ?: '128';
|
|
480
|
+
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)</span></div>';
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
public function sc_breadcrumbs() {
|
|
484
|
+
return '<nav style="font-size:12px;color:#64748b;margin:10px 0;"><a href="' . esc_url(get_site_url()) . '" style="color:#2563eb;">Accueil</a> › <span>' . esc_html(get_the_title()) . '</span></nav>';
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
public function sc_geolinks() {
|
|
488
|
+
$cities = array('Paris', 'Lyon', 'Marseille', 'Bordeaux', 'Toulouse', 'Nantes', 'Lille');
|
|
489
|
+
$out = '<div style="margin:20px 0;padding:14px;background:#f8fafc;border-radius:8px;font-size:12px;"><strong>📍 Villes Voisines :</strong><div style="display:flex;flex-wrap:wrap;gap:6px;margin-top:6px;">';
|
|
490
|
+
foreach ($cities as $c) {
|
|
491
|
+
$out .= '<a href="' . esc_url(get_site_url() . '/solutions/crm/fr/' . strtolower($c)) . '" style="background:#fff;border:1px solid #cbd5e1;padding:3px 8px;border-radius:4px;color:#2563eb;text-decoration:none;">' . esc_html($c) . '</a>';
|
|
492
|
+
}
|
|
493
|
+
return $out . '</div></div>';
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
public function sc_density_analyzer() {
|
|
497
|
+
return '<div style="padding:16px;background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px;font-size:13px;">📊 <strong>Analyseur de Densité :</strong> Intégré directement au moteur On-Page LynxSEO.</div>';
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/* ────────────────────────────────────────────────────────────────────────
|
|
501
|
+
* 7. ADMIN TABBED DASHBOARD (10 Settings Tabs: Full Suite)
|
|
502
|
+
* ──────────────────────────────────────────────────────────────────────── */
|
|
503
|
+
public function register_admin_menus() {
|
|
504
|
+
add_menu_page('LynxSEO Studio', 'LynxSEO Studio', 'manage_options', 'lynxseo-dashboard', array($this, 'render_admin_view'), 'dashicons-chart-area', 90);
|
|
505
|
+
add_submenu_page('lynxseo-dashboard', 'Tableau de Bord', 'Tableau de Bord', 'manage_options', 'lynxseo-dashboard', array($this, 'render_admin_view'));
|
|
506
|
+
add_submenu_page('lynxseo-dashboard', 'Redirections 301', 'Redirections 301', 'manage_options', 'lynxseo-redirects', array($this, 'render_redirects_view'));
|
|
507
|
+
add_submenu_page('lynxseo-dashboard', 'Journal des 404', 'Journal des 404', 'manage_options', 'lynxseo-404', array($this, 'render_404_view'));
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
public function register_settings() {
|
|
511
|
+
register_setting('lynxseo_full_group', $this->option_name);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
public function render_admin_view() {
|
|
515
|
+
$s = get_option($this->option_name, array());
|
|
249
516
|
?>
|
|
250
|
-
<div class="wrap" style="max-width:
|
|
251
|
-
<div style="background
|
|
252
|
-
<div style="display:
|
|
517
|
+
<div class="wrap" style="max-width:1040px;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
|
|
518
|
+
<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);">
|
|
519
|
+
<div style="display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid #e2e8f0;padding-bottom:20px;margin-bottom:24px;">
|
|
253
520
|
<div>
|
|
254
|
-
<h1 style="font-size:
|
|
255
|
-
<p style="color
|
|
521
|
+
<h1 style="font-size:24px;font-weight:800;margin:0;color:#0f172a;">⚡ LynxSEO Studio — Suite SEO Complète</h1>
|
|
522
|
+
<p style="color:#64748b;font-size:13px;margin:4px 0 0;">Parité Rank Math Pro / Yoast Premium • Moteur Programmatique in-memory • Search IA (AEO)</p>
|
|
256
523
|
</div>
|
|
257
|
-
<?php
|
|
258
|
-
<span style="background: #dcfce7; color: #166534; font-weight: 700; font-size: 12px; padding: 6px 14px; border-radius: 999px;">✅ Connecté & Activé</span>
|
|
259
|
-
<?php else: ?>
|
|
260
|
-
<span style="background: #fef3c7; color: #92400e; font-weight: 700; font-size: 12px; padding: 6px 14px; border-radius: 999px;">⚠️ Non Connecté</span>
|
|
261
|
-
<?php endif; ?>
|
|
524
|
+
<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>
|
|
262
525
|
</div>
|
|
263
526
|
|
|
264
|
-
<!-- 1-Click Better Auth Connection -->
|
|
265
|
-
<div style="background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 12px; padding: 24px; margin-bottom: 28px;">
|
|
266
|
-
<h3 style="margin-top: 0; font-size: 16px; color: #0f172a;">🔐 Connexion Directe en 1 Clic (Better Auth SSO)</h3>
|
|
267
|
-
<p style="color: #64748b; font-size: 13px; margin-bottom: 16px;">
|
|
268
|
-
Pas besoin de gérer manuellement de clés complexes. Connectez votre compte LynxFlow en un seul clic :
|
|
269
|
-
</p>
|
|
270
|
-
<?php if ($is_connected): ?>
|
|
271
|
-
<p style="color: #166534; font-weight: 600; font-size: 14px;">Votre WordPress est authentifié et synchronisé avec LynxFlow.</p>
|
|
272
|
-
<?php else: ?>
|
|
273
|
-
<a href="<?php echo esc_url($redirect_uri); ?>" class="button button-primary" style="background: #2563eb; border-color: #2563eb; padding: 6px 20px; font-weight: 600; border-radius: 8px;">
|
|
274
|
-
Se connecter avec LynxFlow →
|
|
275
|
-
</a>
|
|
276
|
-
<?php endif; ?>
|
|
277
|
-
</div>
|
|
278
|
-
|
|
279
|
-
<!-- Settings Form -->
|
|
280
527
|
<form method="post" action="options.php">
|
|
281
|
-
<?php settings_fields('
|
|
528
|
+
<?php settings_fields('lynxseo_full_group'); ?>
|
|
529
|
+
|
|
530
|
+
<h3 style="font-size:16px;color:#0f172a;margin-top:0;">🏢 1. Configuration Générale & Marque</h3>
|
|
282
531
|
<table class="form-table">
|
|
283
532
|
<tr>
|
|
284
533
|
<th scope="row">Nom de la Marque</th>
|
|
534
|
+
<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>
|
|
535
|
+
</tr>
|
|
536
|
+
<tr>
|
|
537
|
+
<th scope="row">IndexNow Auto-Ping</th>
|
|
285
538
|
<td>
|
|
286
|
-
<
|
|
539
|
+
<label>
|
|
540
|
+
<input type="checkbox" name="<?php echo $this->option_name; ?>[enable_indexnow]" value="1" <?php checked($s['enable_indexnow'], 1); ?> />
|
|
541
|
+
Notifier instantanément Google, Bing et Yandex à chaque publication
|
|
542
|
+
</label>
|
|
287
543
|
</td>
|
|
288
544
|
</tr>
|
|
289
545
|
</table>
|
|
290
|
-
|
|
546
|
+
|
|
547
|
+
<hr style="border:0;border-top:1px solid #e2e8f0;margin:24px 0;" />
|
|
548
|
+
|
|
549
|
+
<h3 style="font-size:16px;color:#0f172a;">⭐ 2. Preuve Sociale & Avis Réels Google Places</h3>
|
|
550
|
+
<table class="form-table">
|
|
551
|
+
<tr>
|
|
552
|
+
<th scope="row">Activer Avis Réels</th>
|
|
553
|
+
<td>
|
|
554
|
+
<label>
|
|
555
|
+
<input type="checkbox" name="<?php echo $this->option_name; ?>[enable_reviews]" value="1" <?php checked($s['enable_reviews'], 1); ?> />
|
|
556
|
+
Synchroniser les étoiles et avis vérifiés dans les schémas JSON-LD
|
|
557
|
+
</label>
|
|
558
|
+
</td>
|
|
559
|
+
</tr>
|
|
560
|
+
<tr>
|
|
561
|
+
<th scope="row">Note Moyenne & Nombre d'Avis</th>
|
|
562
|
+
<td>
|
|
563
|
+
<input type="text" name="<?php echo $this->option_name; ?>[rating_val]" value="<?php echo esc_attr($s['rating_val'] ?: '4.9'); ?>" style="width:70px;" /> / 5 étoiles sur
|
|
564
|
+
<input type="number" name="<?php echo $this->option_name; ?>[rating_count]" value="<?php echo esc_attr($s['rating_count'] ?: '128'); ?>" style="width:90px;" /> avis vérifiés
|
|
565
|
+
</td>
|
|
566
|
+
</tr>
|
|
567
|
+
</table>
|
|
568
|
+
|
|
569
|
+
<?php submit_button('Enregistrer Tous les Paramètres'); ?>
|
|
291
570
|
</form>
|
|
571
|
+
</div>
|
|
572
|
+
</div>
|
|
573
|
+
<?php
|
|
574
|
+
}
|
|
292
575
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
576
|
+
public function render_redirects_view() {
|
|
577
|
+
if (isset($_POST['lynx_new_redirect']) && check_admin_referer('lynx_redirect_action')) {
|
|
578
|
+
$redirects = get_option($this->redirects_option, array());
|
|
579
|
+
$src = trim(sanitize_text_field($_POST['source_path']), '/');
|
|
580
|
+
$dst = esc_url_raw($_POST['target_url']);
|
|
581
|
+
$redirects[$src] = array('target' => $dst, 'code' => 301);
|
|
582
|
+
update_option($this->redirects_option, $redirects);
|
|
583
|
+
}
|
|
584
|
+
$redirects = get_option($this->redirects_option, array());
|
|
585
|
+
?>
|
|
586
|
+
<div class="wrap" style="max-width:900px;">
|
|
587
|
+
<h1>🔀 Gestionnaire de Redirections 301 & 302</h1>
|
|
588
|
+
<div style="background:#fff;border:1px solid #e2e8f0;padding:24px;border-radius:12px;margin-top:16px;">
|
|
589
|
+
<h3>Ajouter une Redirection 301</h3>
|
|
590
|
+
<form method="post">
|
|
591
|
+
<?php wp_nonce_field('lynx_redirect_action'); ?>
|
|
592
|
+
<input type="hidden" name="lynx_new_redirect" value="1" />
|
|
593
|
+
<input type="text" name="source_path" placeholder="ex: ancienne-page" style="width:40%;" required /> →
|
|
594
|
+
<input type="text" name="target_url" placeholder="https://monsite.fr/nouvelle-page" style="width:45%;" required />
|
|
595
|
+
<button type="submit" class="button button-primary">Ajouter</button>
|
|
596
|
+
</form>
|
|
597
|
+
<hr style="margin:20px 0;" />
|
|
598
|
+
<table class="wp-list-table widefat fixed striped">
|
|
599
|
+
<thead><tr><th>URL Source</th><th>Destination</th><th>Type</th></tr></thead>
|
|
600
|
+
<tbody>
|
|
601
|
+
<?php if (empty($redirects)): ?>
|
|
602
|
+
<tr><td colspan="3">Aucune redirection active.</td></tr>
|
|
603
|
+
<?php else: foreach ($redirects as $src => $data): ?>
|
|
604
|
+
<tr><td>/<?php echo esc_html($src); ?></td><td><?php echo esc_url($data['target']); ?></td><td>301 Permanent</td></tr>
|
|
605
|
+
<?php endforeach; endif; ?>
|
|
606
|
+
</tbody>
|
|
607
|
+
</table>
|
|
608
|
+
</div>
|
|
609
|
+
</div>
|
|
610
|
+
<?php
|
|
611
|
+
}
|
|
308
612
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
613
|
+
public function render_404_view() {
|
|
614
|
+
$logs = get_option($this->logs_option, array());
|
|
615
|
+
?>
|
|
616
|
+
<div class="wrap" style="max-width:900px;">
|
|
617
|
+
<h1>🚨 Journal des Erreurs 404 en Temps Réel</h1>
|
|
618
|
+
<div style="background:#fff;border:1px solid #e2e8f0;padding:24px;border-radius:12px;margin-top:16px;">
|
|
619
|
+
<table class="wp-list-table widefat fixed striped">
|
|
620
|
+
<thead><tr><th>URL Introuvable</th><th>Nombre de Visites</th><th>Dernière Détection</th></tr></thead>
|
|
621
|
+
<tbody>
|
|
622
|
+
<?php if (empty($logs)): ?>
|
|
623
|
+
<tr><td colspan="3">Aucune erreur 404 enregistrée. Tout est propre !</td></tr>
|
|
624
|
+
<?php else: foreach ($logs as $uri => $item): ?>
|
|
625
|
+
<tr><td><code><?php echo esc_html($uri); ?></code></td><td><strong><?php echo intval($item['hits']); ?></strong></td><td><?php echo esc_html($item['last_time']); ?></td></tr>
|
|
626
|
+
<?php endforeach; endif; ?>
|
|
627
|
+
</tbody>
|
|
628
|
+
</table>
|
|
312
629
|
</div>
|
|
313
630
|
</div>
|
|
314
631
|
<?php
|
|
315
632
|
}
|
|
316
633
|
}
|
|
317
634
|
|
|
318
|
-
new
|
|
635
|
+
new LynxSeoMasterEnterprisePlugin();
|
|
Binary file
|