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