@lynxflow/seo-engine 1.6.9 → 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,9 +1,9 @@
|
|
|
1
1
|
<?php
|
|
2
2
|
/**
|
|
3
|
-
* Plugin Name: LynxSEO Studio —
|
|
3
|
+
* Plugin Name: LynxSEO Studio — Complete Enterprise SEO & Programmatic AI Suite
|
|
4
4
|
* Plugin URI: https://lynxseo.studio
|
|
5
|
-
* Description: The ultimate WordPress SEO &
|
|
6
|
-
* Version: 3.
|
|
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
7
|
* Author: LynxSEO / LynxFlow Technologies
|
|
8
8
|
* Author URI: https://lynxseo.studio
|
|
9
9
|
* Text Domain: lynxseo
|
|
@@ -12,63 +12,53 @@
|
|
|
12
12
|
|
|
13
13
|
if (!defined('ABSPATH')) exit;
|
|
14
14
|
|
|
15
|
-
class
|
|
16
|
-
private $option_name = '
|
|
17
|
-
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';
|
|
18
20
|
|
|
19
21
|
public function __construct() {
|
|
20
|
-
//
|
|
21
|
-
add_action('init', array($this, '
|
|
22
|
-
add_action('
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
add_action('
|
|
29
|
-
add_action('save_post', array($this, 'save_seo_metabox'));
|
|
30
|
-
add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_assets'));
|
|
31
|
-
|
|
32
|
-
// Admin Menus & Settings
|
|
33
|
-
add_action('admin_menu', array($this, 'register_admin_menu'));
|
|
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'));
|
|
34
31
|
add_action('admin_init', array($this, 'register_settings'));
|
|
35
32
|
|
|
36
|
-
// Instant IndexNow
|
|
37
|
-
add_action('wp_after_insert_post', array($this, '
|
|
33
|
+
// Instant IndexNow & Search Engine Pinging
|
|
34
|
+
add_action('wp_after_insert_post', array($this, 'trigger_instant_indexing'), 10, 2);
|
|
38
35
|
|
|
39
|
-
//
|
|
40
|
-
add_shortcode('lynxseo_roi_calculator', array($this, '
|
|
41
|
-
add_shortcode('lynxseo_serp_simulator', array($this, '
|
|
42
|
-
add_shortcode('lynxseo_reviews', array($this, '
|
|
43
|
-
add_shortcode('lynxseo_breadcrumbs', array($this, '
|
|
44
|
-
add_shortcode('lynxseo_geolinks', array($this, '
|
|
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'));
|
|
45
43
|
}
|
|
46
44
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
// XML Sitemaps
|
|
52
|
-
add_rewrite_rule('^sitemap-pseo\.xml$', 'index.php?lynx_sitemap=1', 'top');
|
|
53
|
-
add_rewrite_rule('^sitemap-solutions\.xml$', 'index.php?lynx_sitemap=1', 'top');
|
|
54
|
-
|
|
55
|
-
// AI Search Feeds (/llms.txt & /llms-full.txt)
|
|
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');
|
|
56
49
|
add_rewrite_rule('^llms\.txt$', 'index.php?lynx_llms=1', 'top');
|
|
57
|
-
add_rewrite_rule('^llms-full\.txt$', 'index.php?lynx_llms=full', 'top');
|
|
58
50
|
|
|
59
|
-
//
|
|
51
|
+
// Programmatic Matrices rewrite rules
|
|
60
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');
|
|
61
53
|
add_rewrite_rule('^comparatif/([^/]+)/?', 'index.php?lynx_seo=1&lynx_type=vs&lynx_p1=$matches[1]', 'top');
|
|
62
54
|
add_rewrite_rule('^vs/([^/]+)/?', 'index.php?lynx_seo=1&lynx_type=vs&lynx_p1=$matches[1]', 'top');
|
|
63
55
|
add_rewrite_rule('^alternatives/([^/]+)/?', 'index.php?lynx_seo=1&lynx_type=alt&lynx_p1=$matches[1]', 'top');
|
|
64
56
|
add_rewrite_rule('^secteurs/([^/]+)/?', 'index.php?lynx_seo=1&lynx_type=industry&lynx_p1=$matches[1]', 'top');
|
|
65
|
-
add_rewrite_rule('^industries/([^/]+)/?', 'index.php?lynx_seo=1&lynx_type=industry&lynx_p1=$matches[1]', 'top');
|
|
66
57
|
add_rewrite_rule('^integrations/([^/]+)/?', 'index.php?lynx_seo=1&lynx_type=integration&lynx_p1=$matches[1]', 'top');
|
|
67
58
|
add_rewrite_rule('^roles/([^/]+)/?', 'index.php?lynx_seo=1&lynx_type=role&lynx_p1=$matches[1]', 'top');
|
|
68
59
|
add_rewrite_rule('^cas-usage/([^/]+)/?', 'index.php?lynx_seo=1&lynx_type=usecase&lynx_p1=$matches[1]', 'top');
|
|
69
60
|
add_rewrite_rule('^outils/([^/]+)/?', 'index.php?lynx_seo=1&lynx_type=tool&lynx_p1=$matches[1]', 'top');
|
|
70
61
|
|
|
71
|
-
// Rewrite tags
|
|
72
62
|
add_rewrite_tag('%lynx_sitemap%', '([^&]+)');
|
|
73
63
|
add_rewrite_tag('%lynx_llms%', '([^&]+)');
|
|
74
64
|
add_rewrite_tag('%lynx_seo%', '([^&]+)');
|
|
@@ -78,236 +68,61 @@ class LynxSeoWordPressMasterPlugin {
|
|
|
78
68
|
add_rewrite_tag('%lynx_p3%', '([^&]+)');
|
|
79
69
|
}
|
|
80
70
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
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), '/');
|
|
87
76
|
|
|
88
|
-
//
|
|
89
|
-
|
|
90
|
-
|
|
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);
|
|
91
81
|
exit;
|
|
92
82
|
}
|
|
93
83
|
|
|
94
|
-
//
|
|
95
|
-
if (
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
$clean_service = ucwords(str_replace('-', ' ', $p1));
|
|
107
|
-
$clean_city = ucwords(str_replace('-', ' ', $p3));
|
|
108
|
-
$clean_country = strtoupper($p2 ?: 'FR');
|
|
109
|
-
$year = date('Y');
|
|
110
|
-
|
|
111
|
-
$page_data = $this->build_programmatic_page_data($type, $clean_service, $clean_city, $clean_country, $brand_name, $year);
|
|
112
|
-
|
|
113
|
-
// Render page with Theme Template or Clean Standalone
|
|
114
|
-
$use_theme = !empty($settings['use_theme_header_footer']);
|
|
115
|
-
status_header(200);
|
|
116
|
-
header('Content-Type: text/html; charset=utf-8');
|
|
117
|
-
|
|
118
|
-
if ($use_theme) {
|
|
119
|
-
get_header();
|
|
120
|
-
echo '<div class="lynxseo-container" style="max-width:960px;margin:40px auto;padding:0 20px;">';
|
|
121
|
-
$this->render_page_content($page_data, $brand_name, $site_url);
|
|
122
|
-
echo '</div>';
|
|
123
|
-
get_footer();
|
|
124
|
-
} else {
|
|
125
|
-
$this->render_standalone_html($page_data, $brand_name, $site_url);
|
|
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);
|
|
126
96
|
}
|
|
127
|
-
exit;
|
|
128
|
-
}
|
|
129
97
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
'title' => "{$service} à {$city} ({$country}) — {$brand}",
|
|
135
|
-
'h1' => "{$service} à {$city}",
|
|
136
|
-
'desc' => "Découvrez la solution de {$service} à {$city}. Automatisez vos opérations, optimisez vos flux et boostez votre croissance.",
|
|
137
|
-
'direct_answer' => "{$brand} est la solution de référence pour {$service} à {$city} ({$country}). Déploiement en 10 minutes avec avis clients vérifiés et support dédié.",
|
|
138
|
-
'schema_type' => 'LocalBusiness'
|
|
139
|
-
);
|
|
140
|
-
case 'vs':
|
|
141
|
-
return array(
|
|
142
|
-
'title' => "{$brand} vs {$service} : Comparatif Complet & Tarifs {$year}",
|
|
143
|
-
'h1' => "Comparatif : {$brand} vs {$service}",
|
|
144
|
-
'desc' => "Pourquoi choisir {$brand} plutôt que {$service} ? Découvrez le comparatif des fonctionnalités, prix sans engagement et retours d'expérience.",
|
|
145
|
-
'direct_answer' => "{$brand} se distingue de {$service} par une tarification transparente sans engagement, une automatisation IA native 24/7 et un support réactif.",
|
|
146
|
-
'schema_type' => 'Product'
|
|
147
|
-
);
|
|
148
|
-
case 'alt':
|
|
149
|
-
return array(
|
|
150
|
-
'title' => "Meilleure Alternative à {$service} en {$year} — {$brand}",
|
|
151
|
-
'h1' => "Alternative moderne à {$service}",
|
|
152
|
-
'desc' => "Vous cherchez une alternative moderne à {$service} ? Découvrez pourquoi les entreprises migrent vers {$brand}.",
|
|
153
|
-
'direct_answer' => "La meilleure alternative à {$service} est {$brand}, offrant une flexibilité totale, des fonctionnalités IA avancées et jusqu'à 60% d'économies.",
|
|
154
|
-
'schema_type' => 'SoftwareApplication'
|
|
155
|
-
);
|
|
156
|
-
case 'industry':
|
|
157
|
-
return array(
|
|
158
|
-
'title' => "Solution {$service} pour le secteur {$city} — {$brand}",
|
|
159
|
-
'h1' => "{$service} conçu pour {$city}",
|
|
160
|
-
'desc' => "Logiciel métier adapté aux exigences des professionnels du secteur {$city}.",
|
|
161
|
-
'direct_answer' => "{$brand} fournit une infrastructure complète de {$service} pour le secteur {$city}, conforme RGPD et déployable sans code.",
|
|
162
|
-
'schema_type' => 'BusinessAudience'
|
|
163
|
-
);
|
|
164
|
-
case 'integration':
|
|
165
|
-
return array(
|
|
166
|
-
'title' => "Intégration {$service} & {$brand} — Connexion en 1 Clic",
|
|
167
|
-
'h1' => "Connectez {$service} à votre compte {$brand}",
|
|
168
|
-
'desc' => "Synchronisation bidirectionnelle instantanée entre {$service} et {$brand}.",
|
|
169
|
-
'direct_answer' => "L'intégration native entre {$service} et {$brand} permet de synchroniser contacts et données en temps réel sans code.",
|
|
170
|
-
'schema_type' => 'SoftwareApplication'
|
|
171
|
-
);
|
|
172
|
-
default:
|
|
173
|
-
return array(
|
|
174
|
-
'title' => "{$service} — Solution {$brand}",
|
|
175
|
-
'h1' => $service,
|
|
176
|
-
'desc' => "Découvrez la plateforme de {$service} avec {$brand}. Automatisation intelligente et gain de temps mesurable.",
|
|
177
|
-
'direct_answer' => "{$brand} automatise {$service} en moins de 10 minutes avec synchronisation de vos données.",
|
|
178
|
-
'schema_type' => 'SoftwareApplication'
|
|
179
|
-
);
|
|
98
|
+
// Sitemaps Render
|
|
99
|
+
if (get_query_var('lynx_sitemap')) {
|
|
100
|
+
$this->render_xml_sitemaps();
|
|
101
|
+
exit;
|
|
180
102
|
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
private function render_page_content($data, $brand, $site_url) {
|
|
184
|
-
$settings = get_option($this->option_name, array());
|
|
185
|
-
$rating = !empty($settings['google_rating']) ? floatval($settings['google_rating']) : 4.9;
|
|
186
|
-
$review_count = !empty($settings['google_reviews_count']) ? intval($settings['google_reviews_count']) : 128;
|
|
187
|
-
|
|
188
|
-
echo '<div style="background:#eff6ff;color:#1e40af;padding:6px 14px;border-radius:999px;display:inline-block;font-size:12px;font-weight:700;margin-bottom:16px;">⚡ Solution Certifiée & Synchro Directe</div>';
|
|
189
|
-
echo '<h1 style="font-size:2.25rem;font-weight:800;color:#0f172a;margin-bottom:12px;">' . esc_html($data['h1']) . '</h1>';
|
|
190
|
-
echo '<p style="font-size:1.15rem;color:#475569;margin-bottom:24px;">' . esc_html($data['desc']) . '</p>';
|
|
191
|
-
|
|
192
|
-
// AEO / Search IA Direct Answer Callout
|
|
193
|
-
echo '<div style="background:#f8fafc;border-left:4px solid #2563eb;padding:20px;border-radius:8px;margin:24px 0;">';
|
|
194
|
-
echo '<strong style="color:#0f172a;">🤖 En Résumé (Réponse Directe IA) :</strong>';
|
|
195
|
-
echo '<p style="margin:8px 0 0;color:#334155;">' . esc_html($data['direct_answer']) . '</p>';
|
|
196
|
-
echo '</div>';
|
|
197
103
|
|
|
198
|
-
//
|
|
199
|
-
if (
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
echo '</div>';
|
|
104
|
+
// /llms.txt AI Search Feed Render
|
|
105
|
+
if (get_query_var('lynx_llms')) {
|
|
106
|
+
$this->render_llms_txt();
|
|
107
|
+
exit;
|
|
203
108
|
}
|
|
204
109
|
|
|
205
|
-
//
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
echo '<a href="' . esc_url($site_url) . '" style="display:inline-block;background:#2563eb;color:#fff;font-weight:600;padding:12px 28px;border-radius:8px;text-decoration:none;">Découvrir l\'Offre →</a>';
|
|
210
|
-
echo '</div>';
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
private function render_standalone_html($data, $brand, $site_url) {
|
|
214
|
-
$settings = get_option($this->option_name, array());
|
|
215
|
-
$rating = !empty($settings['google_rating']) ? floatval($settings['google_rating']) : 4.9;
|
|
216
|
-
$review_count = !empty($settings['google_reviews_count']) ? intval($settings['google_reviews_count']) : 128;
|
|
217
|
-
|
|
218
|
-
$json_ld = array(
|
|
219
|
-
"@context" => "https://schema.org",
|
|
220
|
-
"@type" => $data['schema_type'],
|
|
221
|
-
"name" => $data['h1'],
|
|
222
|
-
"description" => $data['desc'],
|
|
223
|
-
"aggregateRating" => array(
|
|
224
|
-
"@type" => "AggregateRating",
|
|
225
|
-
"ratingValue" => strval($rating),
|
|
226
|
-
"reviewCount" => strval($review_count),
|
|
227
|
-
"bestRating" => "5"
|
|
228
|
-
),
|
|
229
|
-
"offers" => array(
|
|
230
|
-
"@type" => "Offer",
|
|
231
|
-
"priceCurrency" => "EUR",
|
|
232
|
-
"price" => "99.00",
|
|
233
|
-
"availability" => "https://schema.org/InStock"
|
|
234
|
-
)
|
|
235
|
-
);
|
|
236
|
-
|
|
237
|
-
echo '<!DOCTYPE html><html lang="fr"><head>';
|
|
238
|
-
echo '<meta charset="UTF-8">';
|
|
239
|
-
echo '<meta name="viewport" content="width=device-width, initial-scale=1.0">';
|
|
240
|
-
echo '<title>' . esc_html($data['title']) . '</title>';
|
|
241
|
-
echo '<meta name="description" content="' . esc_attr($data['desc']) . '">';
|
|
242
|
-
echo '<script type="application/ld+json">' . json_encode($json_ld, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . '</script>';
|
|
243
|
-
echo '<style>
|
|
244
|
-
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; line-height: 1.6; color: #1e293b; max-width: 900px; margin: 40px auto; padding: 0 20px; }
|
|
245
|
-
h1 { font-size: 2.25rem; font-weight: 800; color: #0f172a; margin-bottom: 12px; }
|
|
246
|
-
.desc { font-size: 1.15rem; color: #475569; margin-bottom: 24px; }
|
|
247
|
-
.direct-answer { background: #eff6ff; border-left: 4px solid #2563eb; padding: 20px; border-radius: 8px; margin: 24px 0; font-size: 1.05rem; color: #1e3a8a; }
|
|
248
|
-
.cta-box { background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 12px; padding: 28px; text-align: center; margin-top: 32px; }
|
|
249
|
-
.cta-btn { display: inline-block; background: #2563eb; color: #ffffff; padding: 12px 28px; border-radius: 8px; font-weight: 600; text-decoration: none; margin-top: 12px; }
|
|
250
|
-
</style>';
|
|
251
|
-
echo '</head><body>';
|
|
252
|
-
$this->render_page_content($data, $brand, $site_url);
|
|
253
|
-
echo '</body></html>';
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
private function render_dynamic_sitemap() {
|
|
257
|
-
header('Content-Type: application/xml; charset=utf-8');
|
|
258
|
-
$site_url = get_site_url();
|
|
259
|
-
$urls = array(
|
|
260
|
-
'/solutions/crm/fr/paris',
|
|
261
|
-
'/solutions/crm/fr/lyon',
|
|
262
|
-
'/solutions/crm/fr/marseille',
|
|
263
|
-
'/solutions/facturation/fr/paris',
|
|
264
|
-
'/comparatif/hubspot',
|
|
265
|
-
'/comparatif/salesforce',
|
|
266
|
-
'/alternatives/alternative-a-aircall',
|
|
267
|
-
'/secteurs/avocats',
|
|
268
|
-
'/secteurs/agences-immobilieres',
|
|
269
|
-
'/integrations/shopify',
|
|
270
|
-
'/integrations/woocommerce',
|
|
271
|
-
'/roles/directeur-commercial',
|
|
272
|
-
'/cas-usage/relance-devis',
|
|
273
|
-
'/outils/simulateur-roi'
|
|
274
|
-
);
|
|
275
|
-
|
|
276
|
-
echo '<?xml version="1.0" encoding="UTF-8"?>';
|
|
277
|
-
echo '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
|
|
278
|
-
foreach ($urls as $u) {
|
|
279
|
-
echo '<url><loc>' . esc_url($site_url . $u) . '</loc><changefreq>weekly</changefreq><priority>0.8</priority></url>';
|
|
110
|
+
// Programmatic Routing
|
|
111
|
+
if (get_query_var('lynx_seo') == '1') {
|
|
112
|
+
$this->render_programmatic_page();
|
|
113
|
+
exit;
|
|
280
114
|
}
|
|
281
|
-
echo '</urlset>';
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
private function render_llms_feed() {
|
|
285
|
-
header('Content-Type: text/plain; charset=utf-8');
|
|
286
|
-
$settings = get_option($this->option_name, array());
|
|
287
|
-
$brand_name = !empty($settings['brand_name']) ? $settings['brand_name'] : get_bloginfo('name');
|
|
288
|
-
$site_url = get_site_url();
|
|
289
|
-
|
|
290
|
-
echo "# " . $brand_name . " — AI Search & LLM Index\n\n";
|
|
291
|
-
echo "> " . get_bloginfo('description') . "\n\n";
|
|
292
|
-
echo "## Core Capabilities\n";
|
|
293
|
-
echo "- Platform URL: " . $site_url . "\n";
|
|
294
|
-
echo "- SEO Architecture: In-Memory Programmatic Matrix with LynxSEO Engine\n";
|
|
295
|
-
echo "- Verified Reviews: Google Places & Trustpilot Synchronized\n";
|
|
296
|
-
echo "- API & Docs: " . $site_url . "/llms.txt\n\n";
|
|
297
|
-
echo "## Key Topics & Solutions\n";
|
|
298
|
-
echo "- Solutions Matrix: " . $site_url . "/sitemap-pseo.xml\n";
|
|
299
115
|
}
|
|
300
116
|
|
|
301
117
|
/* ────────────────────────────────────────────────────────────────────────
|
|
302
|
-
* 2. ON-PAGE META BOX (Rank Math & Yoast Parity
|
|
118
|
+
* 2. ON-PAGE META BOX (30 Algorithmic Checks: Rank Math & Yoast Parity)
|
|
303
119
|
* ──────────────────────────────────────────────────────────────────────── */
|
|
304
|
-
public function
|
|
305
|
-
|
|
306
|
-
foreach ($screens as $screen) {
|
|
120
|
+
public function register_seo_metaboxes() {
|
|
121
|
+
foreach (array('post', 'page', 'product') as $screen) {
|
|
307
122
|
add_meta_box(
|
|
308
|
-
'
|
|
309
|
-
'⚡ LynxSEO Studio —
|
|
310
|
-
array($this, '
|
|
123
|
+
'lynxseo_full_metabox',
|
|
124
|
+
'⚡ LynxSEO Studio — Audit On-Page & Simulateur SERP (Parité Rank Math/Yoast)',
|
|
125
|
+
array($this, 'render_metabox_ui'),
|
|
311
126
|
$screen,
|
|
312
127
|
'normal',
|
|
313
128
|
'high'
|
|
@@ -315,208 +130,338 @@ class LynxSeoWordPressMasterPlugin {
|
|
|
315
130
|
}
|
|
316
131
|
}
|
|
317
132
|
|
|
318
|
-
public function
|
|
319
|
-
wp_nonce_field('
|
|
320
|
-
$
|
|
321
|
-
$
|
|
322
|
-
$
|
|
323
|
-
$
|
|
324
|
-
$
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
$
|
|
328
|
-
$
|
|
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');
|
|
329
145
|
?>
|
|
330
|
-
<div
|
|
331
|
-
<!-- Score
|
|
332
|
-
<div style="display:
|
|
333
|
-
<div style="display:
|
|
334
|
-
<div style="background
|
|
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;">
|
|
335
151
|
<?php echo $score; ?>
|
|
336
152
|
</div>
|
|
337
153
|
<div>
|
|
338
|
-
<strong style="font-size:
|
|
339
|
-
<div style="font-size:
|
|
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>
|
|
340
156
|
</div>
|
|
341
157
|
</div>
|
|
342
|
-
<div style="font-size:
|
|
343
|
-
<?php echo $score >= 80 ? '🟢
|
|
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'); ?>
|
|
344
160
|
</div>
|
|
345
161
|
</div>
|
|
346
162
|
|
|
347
|
-
<!--
|
|
348
|
-
<div style="margin-bottom:
|
|
349
|
-
<
|
|
350
|
-
<
|
|
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>
|
|
351
169
|
</div>
|
|
352
170
|
|
|
353
|
-
<!-- SERP
|
|
354
|
-
<div
|
|
355
|
-
<div style="
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
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>
|
|
359
196
|
</div>
|
|
360
197
|
|
|
361
|
-
<!--
|
|
362
|
-
<div
|
|
363
|
-
<div>
|
|
364
|
-
|
|
365
|
-
|
|
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; ?>
|
|
366
209
|
</div>
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
<option value="
|
|
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>
|
|
375
223
|
</select>
|
|
376
224
|
</div>
|
|
377
225
|
</div>
|
|
378
226
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
<
|
|
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>
|
|
382
239
|
</div>
|
|
383
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>
|
|
384
256
|
<?php
|
|
385
257
|
}
|
|
386
258
|
|
|
387
|
-
private function
|
|
388
|
-
$
|
|
389
|
-
$
|
|
390
|
-
$
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
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;
|
|
400
294
|
} else {
|
|
401
|
-
$
|
|
295
|
+
$checks[] = array('label' => 'Renseigner un mot-clé principal cible', 'pass' => false, 'points' => 35);
|
|
402
296
|
}
|
|
403
297
|
|
|
404
|
-
|
|
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);
|
|
405
304
|
}
|
|
406
305
|
|
|
407
|
-
public function
|
|
408
|
-
if (!isset($_POST['
|
|
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;
|
|
409
308
|
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
|
|
410
309
|
if (!current_user_can('edit_post', $post_id)) return;
|
|
411
310
|
|
|
412
|
-
if (isset($_POST['
|
|
413
|
-
if (isset($_POST['
|
|
414
|
-
if (isset($_POST['
|
|
415
|
-
if (isset($_POST['
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
public function enqueue_admin_assets() {
|
|
419
|
-
// Enqueue styles if necessary
|
|
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);
|
|
420
317
|
}
|
|
421
318
|
|
|
422
319
|
/* ────────────────────────────────────────────────────────────────────────
|
|
423
|
-
* 3. HEAD INJECTION (
|
|
320
|
+
* 3. HEAD INJECTION (OpenGraph, Twitter Cards, Schema JSON-LD)
|
|
424
321
|
* ──────────────────────────────────────────────────────────────────────── */
|
|
425
|
-
public function
|
|
322
|
+
public function inject_seo_header_metadata() {
|
|
426
323
|
if (is_singular()) {
|
|
427
324
|
global $post;
|
|
428
|
-
$
|
|
429
|
-
$
|
|
430
|
-
$
|
|
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
|
+
}
|
|
431
336
|
|
|
432
|
-
|
|
433
|
-
$brand = !empty($settings['brand_name']) ? $settings['brand_name'] : get_bloginfo('name');
|
|
434
|
-
$rating = !empty($settings['google_rating']) ? floatval($settings['google_rating']) : 4.9;
|
|
435
|
-
$reviews_count = !empty($settings['google_reviews_count']) ? intval($settings['google_reviews_count']) : 128;
|
|
337
|
+
echo '<link rel="canonical" href="' . esc_url($canonical) . "\" />\n";
|
|
436
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
|
|
437
347
|
$graph = array(
|
|
438
348
|
"@context" => "https://schema.org",
|
|
439
349
|
"@type" => $schema_type,
|
|
440
|
-
"headline" => $
|
|
441
|
-
"description" => $
|
|
442
|
-
"
|
|
443
|
-
"@type" => "Organization",
|
|
444
|
-
"name" => $brand
|
|
445
|
-
),
|
|
350
|
+
"headline" => $title,
|
|
351
|
+
"description" => $desc,
|
|
352
|
+
"url" => $canonical,
|
|
446
353
|
"datePublished" => get_the_date('c', $post->ID),
|
|
447
|
-
"dateModified" => get_the_modified_date('c', $post->ID)
|
|
354
|
+
"dateModified" => get_the_modified_date('c', $post->ID),
|
|
448
355
|
);
|
|
449
356
|
|
|
450
|
-
|
|
357
|
+
$settings = get_option($this->option_name, array());
|
|
358
|
+
if (!empty($settings['enable_reviews'])) {
|
|
451
359
|
$graph['aggregateRating'] = array(
|
|
452
360
|
"@type" => "AggregateRating",
|
|
453
|
-
"ratingValue" => strval($
|
|
454
|
-
"reviewCount" => strval($
|
|
361
|
+
"ratingValue" => strval($settings['rating_val'] ?: '4.9'),
|
|
362
|
+
"reviewCount" => strval($settings['rating_count'] ?: '128'),
|
|
455
363
|
"bestRating" => "5"
|
|
456
364
|
);
|
|
457
365
|
}
|
|
458
366
|
|
|
459
|
-
echo "\n<!-- LynxSEO Studio JSON-LD
|
|
460
|
-
echo '<script type="application/ld+json">' . json_encode($graph, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . "</script>\n";
|
|
461
|
-
echo "<!-- /LynxSEO Studio -->\n\n";
|
|
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";
|
|
462
368
|
}
|
|
463
369
|
}
|
|
464
370
|
|
|
465
371
|
/* ────────────────────────────────────────────────────────────────────────
|
|
466
|
-
* 4. INSTANT
|
|
372
|
+
* 4. INSTANT INDEXING (IndexNow API)
|
|
467
373
|
* ──────────────────────────────────────────────────────────────────────── */
|
|
468
|
-
public function
|
|
374
|
+
public function trigger_instant_indexing($post_id, $post) {
|
|
469
375
|
if ($post->post_status !== 'publish' || $post->post_type === 'revision') return;
|
|
470
|
-
|
|
471
376
|
$settings = get_option($this->option_name, array());
|
|
472
377
|
if (empty($settings['enable_indexnow'])) return;
|
|
473
378
|
|
|
474
|
-
$url = get_permalink($post_id);
|
|
475
379
|
$host = parse_url(get_site_url(), PHP_URL_HOST);
|
|
476
|
-
$key = !empty($settings['indexnow_key']) ? $settings['indexnow_key'] : md5($host);
|
|
477
|
-
|
|
478
380
|
$payload = array(
|
|
479
381
|
'host' => $host,
|
|
480
|
-
'key' => $
|
|
481
|
-
'urlList' => array($
|
|
382
|
+
'key' => md5($host),
|
|
383
|
+
'urlList' => array(get_permalink($post_id))
|
|
482
384
|
);
|
|
483
385
|
|
|
484
386
|
wp_remote_post('https://api.indexnow.org/indexnow', array(
|
|
485
387
|
'headers' => array('Content-Type' => 'application/json; charset=utf-8'),
|
|
486
388
|
'body' => json_encode($payload),
|
|
487
|
-
'timeout' => 5,
|
|
488
389
|
'blocking' => false
|
|
489
390
|
));
|
|
490
391
|
}
|
|
491
392
|
|
|
492
393
|
/* ────────────────────────────────────────────────────────────────────────
|
|
493
|
-
* 5.
|
|
394
|
+
* 5. SITEMAPS XML & /llms.txt GENERATOR
|
|
494
395
|
* ──────────────────────────────────────────────────────────────────────── */
|
|
495
|
-
|
|
496
|
-
|
|
396
|
+
private function render_xml_sitemaps() {
|
|
397
|
+
header('Content-Type: application/xml; charset=utf-8');
|
|
398
|
+
$site = get_site_url();
|
|
399
|
+
echo '<?xml version="1.0" encoding="UTF-8"?>';
|
|
400
|
+
echo '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
|
|
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>';
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
echo '</urlset>';
|
|
414
|
+
}
|
|
415
|
+
|
|
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";
|
|
424
|
+
}
|
|
425
|
+
|
|
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();
|
|
440
|
+
}
|
|
441
|
+
|
|
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);
|
|
497
447
|
ob_start();
|
|
498
448
|
?>
|
|
499
|
-
<div
|
|
500
|
-
<h3 style="margin:0 0 12px;font-size:18px;
|
|
501
|
-
<p style="font-size:13px;color:#64748b;margin-bottom:
|
|
502
|
-
<
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
<div style="
|
|
507
|
-
<
|
|
508
|
-
<
|
|
509
|
-
</div>
|
|
510
|
-
<div style="background:#eff6ff;padding:16px;border-radius:8px;text-align:center;">
|
|
511
|
-
<div style="font-size:12px;color:#1e40af;font-weight:600;">ÉCONOMIES FINANCIÈRES ESTIMÉES :</div>
|
|
512
|
-
<div id="lynx_roi_result" style="font-size:24px;font-weight:800;color:#2563eb;margin-top:4px;">1 720 € / mois</div>
|
|
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>
|
|
513
459
|
</div>
|
|
514
460
|
<script>
|
|
515
|
-
function
|
|
516
|
-
var h = parseFloat(document.getElementById('
|
|
517
|
-
var r = parseFloat(document.getElementById('
|
|
518
|
-
|
|
519
|
-
document.getElementById('lynx_roi_result').innerText = (res > 0 ? res.toLocaleString() : 0) + ' € / mois';
|
|
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';
|
|
520
465
|
}
|
|
521
466
|
</script>
|
|
522
467
|
</div>
|
|
@@ -524,187 +469,167 @@ class LynxSeoWordPressMasterPlugin {
|
|
|
524
469
|
return ob_get_clean();
|
|
525
470
|
}
|
|
526
471
|
|
|
527
|
-
public function
|
|
528
|
-
|
|
529
|
-
?>
|
|
530
|
-
<div class="lynxseo-serp-sim" style="background:#fff;border:1px solid #dadce0;border-radius:12px;padding:20px;max-width:600px;font-family:Arial,sans-serif;">
|
|
531
|
-
<div style="font-size:11px;color:#70757a;text-transform:uppercase;margin-bottom:8px;font-weight:bold;">Simulateur Google SERP (Pixel Width 600px)</div>
|
|
532
|
-
<div style="font-size:12px;color:#202124;"><?php echo esc_url(get_site_url()); ?>/exemple-solution</div>
|
|
533
|
-
<div style="font-size:18px;color:#1a0dab;cursor:pointer;margin:4px 0;"><?php echo esc_html(get_bloginfo('name')); ?> — Solution N°1 Programmatic SEO</div>
|
|
534
|
-
<div style="font-size:13px;color:#4d5156;line-height:1.4;">Découvrez notre moteur de génération SEO ultra-rapide (< 0.05ms) avec parité complète Rank Math & Yoast.</div>
|
|
535
|
-
</div>
|
|
536
|
-
<?php
|
|
537
|
-
return ob_get_clean();
|
|
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>';
|
|
538
474
|
}
|
|
539
475
|
|
|
540
|
-
public function
|
|
476
|
+
public function sc_reviews_badge() {
|
|
541
477
|
$settings = get_option($this->option_name, array());
|
|
542
|
-
$
|
|
543
|
-
$
|
|
544
|
-
|
|
545
|
-
?>
|
|
546
|
-
<div class="lynxseo-reviews-badge" style="display:inline-flex;align-items:center;gap:10px;background:#f8fafc;border:1px solid #e2e8f0;padding:8px 16px;border-radius:999px;font-family:sans-serif;font-size:13px;">
|
|
547
|
-
<span style="color:#f59e0b;font-size:14px;">★ ★ ★ ★ ★</span>
|
|
548
|
-
<strong style="color:#0f172a;"><?php echo esc_html($rating); ?>/5</strong>
|
|
549
|
-
<span style="color:#64748b;">(Basé sur <?php echo esc_html($review_count); ?> avis vérifiés Google Places)</span>
|
|
550
|
-
</div>
|
|
551
|
-
<?php
|
|
552
|
-
return ob_get_clean();
|
|
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>';
|
|
553
481
|
}
|
|
554
482
|
|
|
555
|
-
public function
|
|
556
|
-
|
|
557
|
-
ob_start();
|
|
558
|
-
?>
|
|
559
|
-
<nav aria-label="Breadcrumb" style="font-size:12px;color:#64748b;margin:12px 0;">
|
|
560
|
-
<a href="<?php echo esc_url($site_url); ?>" style="color:#2563eb;text-decoration:none;">Accueil</a> ›
|
|
561
|
-
<span><?php echo esc_html(get_the_title()); ?></span>
|
|
562
|
-
</nav>
|
|
563
|
-
<?php
|
|
564
|
-
return ob_get_clean();
|
|
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>';
|
|
565
485
|
}
|
|
566
486
|
|
|
567
|
-
public function
|
|
568
|
-
$cities = array('Paris', 'Lyon', 'Marseille', 'Bordeaux', 'Toulouse', 'Nantes', 'Lille'
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
<?php endforeach; ?>
|
|
579
|
-
</div>
|
|
580
|
-
</div>
|
|
581
|
-
<?php
|
|
582
|
-
return ob_get_clean();
|
|
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>';
|
|
583
498
|
}
|
|
584
499
|
|
|
585
500
|
/* ────────────────────────────────────────────────────────────────────────
|
|
586
|
-
*
|
|
501
|
+
* 7. ADMIN TABBED DASHBOARD (10 Settings Tabs: Full Suite)
|
|
587
502
|
* ──────────────────────────────────────────────────────────────────────── */
|
|
588
|
-
public function
|
|
589
|
-
add_menu_page('LynxSEO Studio', 'LynxSEO Studio', 'manage_options', 'lynxseo-
|
|
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'));
|
|
590
508
|
}
|
|
591
509
|
|
|
592
510
|
public function register_settings() {
|
|
593
|
-
register_setting('
|
|
511
|
+
register_setting('lynxseo_full_group', $this->option_name);
|
|
594
512
|
}
|
|
595
513
|
|
|
596
|
-
public function
|
|
597
|
-
$
|
|
598
|
-
$brand_name = !empty($settings['brand_name']) ? $settings['brand_name'] : get_bloginfo('name');
|
|
599
|
-
$google_place_id = !empty($settings['google_place_id']) ? $settings['google_place_id'] : '';
|
|
600
|
-
$google_rating = !empty($settings['google_rating']) ? $settings['google_rating'] : '4.9';
|
|
601
|
-
$google_reviews_count = !empty($settings['google_reviews_count']) ? $settings['google_reviews_count'] : '128';
|
|
602
|
-
$enable_indexnow = !empty($settings['enable_indexnow']);
|
|
603
|
-
$enable_verified_reviews = !empty($settings['enable_verified_reviews']);
|
|
604
|
-
$use_theme = !empty($settings['use_theme_header_footer']);
|
|
514
|
+
public function render_admin_view() {
|
|
515
|
+
$s = get_option($this->option_name, array());
|
|
605
516
|
?>
|
|
606
|
-
<div class="wrap" style="max-width:
|
|
607
|
-
<div style="background
|
|
608
|
-
|
|
609
|
-
<div style="display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid #e2e8f0; padding-bottom: 20px; margin-bottom: 24px;">
|
|
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;">
|
|
610
520
|
<div>
|
|
611
|
-
<h1 style="font-size:
|
|
612
|
-
|
|
613
|
-
</h1>
|
|
614
|
-
<p style="color: #64748b; font-size: 13px; margin-top: 4px;">Architecture SEO Programmatique in-memory, Parité Rank Math/Yoast & Search IA (AEO).</p>
|
|
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>
|
|
615
523
|
</div>
|
|
616
|
-
<span style="background
|
|
617
|
-
v<?php echo $this->version; ?> • Moteur Actif
|
|
618
|
-
</span>
|
|
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>
|
|
619
525
|
</div>
|
|
620
526
|
|
|
621
527
|
<form method="post" action="options.php">
|
|
622
|
-
<?php settings_fields('
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
<
|
|
626
|
-
<table class="form-table" style="margin-bottom: 24px;">
|
|
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>
|
|
531
|
+
<table class="form-table">
|
|
627
532
|
<tr>
|
|
628
533
|
<th scope="row">Nom de la Marque</th>
|
|
629
|
-
<td>
|
|
630
|
-
<input type="text" name="<?php echo $this->option_name; ?>[brand_name]" value="<?php echo esc_attr($brand_name); ?>" class="regular-text" style="border-radius: 6px;" />
|
|
631
|
-
<p class="description">Le nom utilisé dans les balises Title, Schemas JSON-LD et blocs direct-answer.</p>
|
|
632
|
-
</td>
|
|
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>
|
|
633
535
|
</tr>
|
|
634
536
|
<tr>
|
|
635
|
-
<th scope="row">
|
|
537
|
+
<th scope="row">IndexNow Auto-Ping</th>
|
|
636
538
|
<td>
|
|
637
539
|
<label>
|
|
638
|
-
<input type="checkbox" name="<?php echo $this->option_name; ?>[
|
|
639
|
-
|
|
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
|
|
640
542
|
</label>
|
|
641
543
|
</td>
|
|
642
544
|
</tr>
|
|
643
545
|
</table>
|
|
644
546
|
|
|
645
|
-
<hr style="border:
|
|
547
|
+
<hr style="border:0;border-top:1px solid #e2e8f0;margin:24px 0;" />
|
|
646
548
|
|
|
647
|
-
|
|
648
|
-
<
|
|
649
|
-
<table class="form-table" style="margin-bottom: 24px;">
|
|
549
|
+
<h3 style="font-size:16px;color:#0f172a;">⭐ 2. Preuve Sociale & Avis Réels Google Places</h3>
|
|
550
|
+
<table class="form-table">
|
|
650
551
|
<tr>
|
|
651
|
-
<th scope="row">Activer
|
|
552
|
+
<th scope="row">Activer Avis Réels</th>
|
|
652
553
|
<td>
|
|
653
554
|
<label>
|
|
654
|
-
<input type="checkbox" name="<?php echo $this->option_name; ?>[
|
|
655
|
-
|
|
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
|
|
656
557
|
</label>
|
|
657
558
|
</td>
|
|
658
559
|
</tr>
|
|
659
|
-
<tr>
|
|
660
|
-
<th scope="row">Google Places Place ID</th>
|
|
661
|
-
<td>
|
|
662
|
-
<input type="text" name="<?php echo $this->option_name; ?>[google_place_id]" value="<?php echo esc_attr($google_place_id); ?>" class="regular-text" placeholder="ChIJN1t_tDeuEmsRUsoyG83frY4" />
|
|
663
|
-
</td>
|
|
664
|
-
</tr>
|
|
665
560
|
<tr>
|
|
666
561
|
<th scope="row">Note Moyenne & Nombre d'Avis</th>
|
|
667
562
|
<td>
|
|
668
|
-
<input type="text" name="<?php echo $this->option_name; ?>[
|
|
669
|
-
|
|
670
|
-
<input type="number" name="<?php echo $this->option_name; ?>[google_reviews_count]" value="<?php echo esc_attr($google_reviews_count); ?>" style="width: 100px;" /> avis clients
|
|
671
|
-
</td>
|
|
672
|
-
</tr>
|
|
673
|
-
</table>
|
|
674
|
-
|
|
675
|
-
<hr style="border: 0; border-top: 1px solid #e2e8f0; margin: 24px 0;" />
|
|
676
|
-
|
|
677
|
-
<!-- Section 3 : IndexNow & Search IA -->
|
|
678
|
-
<h3 style="font-size: 16px; color: #0f172a;">🤖 3. IndexNow & Optimisation Search IA (AEO / GEO)</h3>
|
|
679
|
-
<table class="form-table">
|
|
680
|
-
<tr>
|
|
681
|
-
<th scope="row">IndexNow Auto-Ping</th>
|
|
682
|
-
<td>
|
|
683
|
-
<label>
|
|
684
|
-
<input type="checkbox" name="<?php echo $this->option_name; ?>[enable_indexnow]" value="1" <?php checked($enable_indexnow, 1); ?> />
|
|
685
|
-
Notifier instantanément les moteurs de recherche (Google, Bing, Yandex) à chaque publication d'article
|
|
686
|
-
</label>
|
|
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
|
|
687
565
|
</td>
|
|
688
566
|
</tr>
|
|
689
567
|
</table>
|
|
690
568
|
|
|
691
569
|
<?php submit_button('Enregistrer Tous les Paramètres'); ?>
|
|
692
570
|
</form>
|
|
571
|
+
</div>
|
|
572
|
+
</div>
|
|
573
|
+
<?php
|
|
574
|
+
}
|
|
575
|
+
|
|
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
|
+
}
|
|
693
612
|
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
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>
|
|
704
629
|
</div>
|
|
705
630
|
</div>
|
|
706
631
|
<?php
|
|
707
632
|
}
|
|
708
633
|
}
|
|
709
634
|
|
|
710
|
-
new
|
|
635
|
+
new LynxSeoMasterEnterprisePlugin();
|
|
Binary file
|