ai-seo-master..php
· 71 KiB · PHP
原始檔案
<?php
/**
* Plugin Name: AI SEO Master PRO
* Plugin URI: https://urocibg.eu/ai-seo-master-pro
* Description: 🚀 Професионален AI SEO оптимизатор с мониторинг, валидация и напълно безплатни API функционалности
* Version: 2.0.0
* Author: Fedya Serafiev
* Author URI: https://fedia.eu
* License: GPL v3
* Text Domain: ai-seo-master-pro
*/
// Безопасност
if (!defined('ABSPATH')) {
exit;
}
// Дефиниране на константи
define('AISEOM_VERSION', '2.0.0');
define('AISEOM_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('AISEOM_PLUGIN_URL', plugin_dir_url(__FILE__));
// Главен клас
class AI_SEO_Master_PRO {
private static $instance = null;
public static function get_instance() {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {
// Инициализация
add_action('init', [$this, 'init']);
add_action('admin_menu', [$this, 'create_admin_menu']);
add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_assets']);
// AJAX обработчици
add_action('wp_ajax_aiseom_generate_meta', [$this, 'ajax_generate_meta']);
add_action('wp_ajax_aiseom_generate_alt', [$this, 'ajax_generate_alt']);
add_action('wp_ajax_aiseom_bulk_process', [$this, 'ajax_bulk_process']);
add_action('wp_ajax_aiseom_check_api', [$this, 'ajax_check_api']);
add_action('wp_ajax_aiseom_get_stats', [$this, 'ajax_get_stats']);
// Мета боксове
add_action('add_meta_boxes', [$this, 'add_seo_metabox']);
// Настройки
add_action('admin_init', [$this, 'register_settings']);
}
public function init() {
// Зареждане на текстове за превод
load_plugin_textdomain('ai-seo-master-pro', false, dirname(plugin_basename(__FILE__)) . '/languages');
}
public function create_admin_menu() {
// Главно меню
add_menu_page(
'AI SEO PRO',
'AI SEO PRO',
'manage_options',
'ai-seo-master-pro',
[$this, 'render_dashboard'],
'dashicons-chart-line',
30
);
// Подменюта
add_submenu_page(
'ai-seo-master-pro',
'Табло',
'Табло',
'manage_options',
'ai-seo-master-pro',
[$this, 'render_dashboard']
);
add_submenu_page(
'ai-seo-master-pro',
'Статии без SEO',
'Статии без SEO',
'manage_options',
'ai-seo-unoptimized',
[$this, 'render_unoptimized_posts']
);
add_submenu_page(
'ai-seo-master-pro',
'Настройки',
'Настройки',
'manage_options',
'ai-seo-settings',
[$this, 'render_settings_page']
);
}
public function enqueue_admin_assets($hook) {
// Зареждаме само в нашите страници
$our_pages = [
'toplevel_page_ai-seo-master-pro',
'ai-seo-master-pro_page_ai-seo-unoptimized',
'ai-seo-master-pro_page_ai-seo-settings'
];
if (!in_array($hook, $our_pages) && !strstr($hook, 'post.php') && !strstr($hook, 'edit.php')) {
return;
}
// Вграждаме CSS директно
add_action('admin_head', [$this, 'add_inline_css']);
// Вграждаме JavaScript директно
add_action('admin_footer', [$this, 'add_inline_js']);
}
public function add_inline_css() {
?>
<style>
/* Основни стилове за AI SEO Master PRO */
.aiseom-dashboard {
background: #f0f0f1;
padding: 20px;
min-height: 100vh;
}
.aiseom-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 30px;
border-radius: 10px;
margin-bottom: 30px;
}
.aiseom-header h1 {
color: white;
margin: 0 0 10px 0;
display: flex;
align-items: center;
gap: 15px;
}
.aiseom-stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin: 30px 0;
}
.aiseom-stat-card {
background: white;
border-radius: 10px;
padding: 25px;
display: flex;
align-items: center;
gap: 20px;
box-shadow: 0 5px 15px rgba(0,0,0,0.05);
transition: transform 0.3s ease;
}
.aiseom-stat-card:hover {
transform: translateY(-5px);
}
.aiseom-stat-card .stat-icon {
width: 60px;
height: 60px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 28px;
}
.aiseom-stat-card .stat-number {
font-size: 36px;
font-weight: bold;
color: #2d3748;
display: block;
line-height: 1;
}
.aiseom-stat-card .stat-label {
color: #718096;
font-size: 16px;
display: block;
margin-top: 5px;
}
.aiseom-actions-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
margin: 30px 0;
}
.aiseom-action-card {
background: white;
border-radius: 10px;
padding: 25px;
box-shadow: 0 5px 15px rgba(0,0,0,0.05);
}
.aiseom-action-card h3 {
margin-top: 0;
color: #2d3748;
font-size: 20px;
display: flex;
align-items: center;
gap: 10px;
}
.aiseom-btn {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 12px 24px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 8px;
font-weight: 600;
cursor: pointer;
text-decoration: none;
transition: all 0.3s ease;
}
.aiseom-btn:hover {
transform: translateY(-2px);
box-shadow: 0 10px 20px rgba(102, 126, 234, 0.3);
color: white;
}
.aiseom-btn-secondary {
background: #edf2f7;
color: #4a5568;
}
.aiseom-btn-secondary:hover {
background: #e2e8f0;
}
/* Мета бокс стилове */
.aiseom-metabox {
padding: 10px 0;
}
.aiseom-metabox-section {
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid #eee;
}
.aiseom-metabox-section:last-child {
border-bottom: none;
}
.status-indicator {
padding: 8px;
border-radius: 4px;
margin: 8px 0;
display: flex;
align-items: center;
gap: 8px;
}
.status-success {
background: #edf7ed;
border-left: 4px solid #4caf50;
}
.status-warning {
background: #fff8e1;
border-left: 4px solid #ff9800;
}
.status-error {
background: #fdeded;
border-left: 4px solid #f44336;
}
.status-neutral {
background: #f5f5f5;
border-left: 4px solid #9e9e9e;
}
.status-icon {
font-weight: bold;
font-size: 14px;
min-width: 16px;
}
/* Таблици */
.aiseom-table {
width: 100%;
border-collapse: collapse;
background: white;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 2px 10px rgba(0,0,0,0.05);
}
.aiseom-table th {
background: #f8f9fa;
padding: 15px;
text-align: left;
font-weight: 600;
color: #495057;
border-bottom: 2px solid #e9ecef;
}
.aiseom-table td {
padding: 12px 15px;
border-bottom: 1px solid #e9ecef;
}
.aiseom-table tr:hover {
background: #f8f9fa;
}
/* Форми и настройки */
.aiseom-settings-form {
background: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 5px 15px rgba(0,0,0,0.05);
}
.form-group {
margin-bottom: 25px;
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: 600;
color: #2d3748;
}
.form-group input[type="text"],
.form-group input[type="password"],
.form-group select {
width: 100%;
max-width: 500px;
padding: 10px;
border: 1px solid #cbd5e0;
border-radius: 6px;
font-size: 14px;
}
.form-group input:focus,
.form-group select:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
.api-test-result {
margin-top: 10px;
padding: 10px;
border-radius: 6px;
font-weight: 500;
}
.api-test-success {
background: #edf7ed;
color: #2e7d32;
border-left: 4px solid #4caf50;
}
.api-test-error {
background: #fdeded;
color: #c62828;
border-left: 4px solid #f44336;
}
/* Прогрес бар */
.progress-container {
margin: 20px 0;
display: none;
}
.progress-bar {
width: 100%;
height: 20px;
background: #edf2f7;
border-radius: 10px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
width: 0%;
transition: width 0.3s ease;
}
.progress-text {
text-align: center;
margin-top: 10px;
color: #718096;
font-weight: 500;
}
/* Адаптивен дизайн */
@media (max-width: 768px) {
.aiseom-stats-grid {
grid-template-columns: 1fr;
}
.aiseom-actions-grid {
grid-template-columns: 1fr;
}
.form-group input[type="text"],
.form-group input[type="password"],
.form-group select {
max-width: 100%;
}
}
</style>
<?php
}
public function add_inline_js() {
?>
<script>
// AI SEO Master PRO JavaScript
jQuery(document).ready(function($) {
console.log('AI SEO Master PRO loaded');
// AJAX nonce и URL
var aiseom_ajax = {
ajax_url: '<?php echo admin_url("admin-ajax.php"); ?>',
nonce: '<?php echo wp_create_nonce("aiseom_nonce"); ?>',
strings: {
generating: '<?php _e("Генериране...", "ai-seo-master-pro"); ?>',
success: '<?php _e("Успешно!", "ai-seo-master-pro"); ?>',
error: '<?php _e("Грешка", "ai-seo-master-pro"); ?>',
checking: '<?php _e("Проверка на API...", "ai-seo-master-pro"); ?>'
}
};
// Генериране на мета описание от мета бокс
$(document).on('click', '.aiseom-generate-meta', function() {
var button = $(this);
var postId = button.data('post-id');
button.prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i> ' + aiseom_ajax.strings.generating);
$.ajax({
url: aiseom_ajax.ajax_url,
type: 'POST',
data: {
action: 'aiseom_generate_meta',
nonce: aiseom_ajax.nonce,
post_id: postId
},
success: function(response) {
if (response.success) {
button.html('<i class="fas fa-check"></i> ' + aiseom_ajax.strings.success);
setTimeout(function() {
location.reload();
}, 1500);
} else {
alert('Грешка: ' + response.data);
button.prop('disabled', false).html('<i class="fas fa-magic"></i> Генерирай SEO');
}
},
error: function() {
alert('Грешка при генериране');
button.prop('disabled', false).html('<i class="fas fa-magic"></i> Генерирай SEO');
}
});
});
// Генериране на ALT тагове от мета бокс
$(document).on('click', '.aiseom-generate-alt', function() {
var button = $(this);
var postId = button.data('post-id');
button.prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i> ' + aiseom_ajax.strings.generating);
$.ajax({
url: aiseom_ajax.ajax_url,
type: 'POST',
data: {
action: 'aiseom_generate_alt',
nonce: aiseom_ajax.nonce,
post_id: postId
},
success: function(response) {
if (response.success) {
button.html('<i class="fas fa-check"></i> ' + aiseom_ajax.strings.success);
setTimeout(function() {
location.reload();
}, 1500);
} else {
alert('Грешка: ' + response.data);
button.prop('disabled', false).html('<i class="fas fa-image"></i> Генерирай ALT');
}
},
error: function() {
alert('Грешка при генериране');
button.prop('disabled', false).html('<i class="fas fa-image"></i> Генерирай ALT');
}
});
});
// Проверка на API ключа
$(document).on('click', '#test-api-key', function() {
var button = $(this);
var apiKey = $('#aiseom_api_key').val();
var provider = $('#aiseom_api_provider').val();
if (!apiKey) {
alert('Моля, въведете API ключ първо');
return;
}
button.prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i> ' + aiseom_ajax.strings.checking);
$.ajax({
url: aiseom_ajax.ajax_url,
type: 'POST',
data: {
action: 'aiseom_check_api',
nonce: aiseom_ajax.nonce,
api_key: apiKey,
api_provider: provider
},
success: function(response) {
button.prop('disabled', false).html('<i class="fas fa-wifi"></i> Тествай API ключа');
if (response.success) {
$('#api-test-result').html(
'<div class="api-test-result api-test-success">' +
'<i class="fas fa-check-circle"></i> ' + response.data.message +
'</div>'
);
} else {
$('#api-test-result').html(
'<div class="api-test-result api-test-error">' +
'<i class="fas fa-exclamation-circle"></i> ' + response.data +
'</div>'
);
}
},
error: function() {
button.prop('disabled', false).html('<i class="fas fa-wifi"></i> Тествай API ключа');
alert('Грешка при проверката на API');
}
});
});
// Масово генериране на мета описания
$(document).on('click', '#generate-all-meta', function() {
if (!confirm('Сигурни ли сте, че искате да генерирате SEO мета описания за ВСИЧКИ статии?')) {
return;
}
var button = $(this);
button.prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i> Започва обработка...');
$('#bulk-progress').show();
startBulkProcess('meta', 0, 0);
});
// Масово генериране на ALT тагове
$(document).on('click', '#generate-all-alt', function() {
if (!confirm('Сигурни ли сте, че искате да генерирате ALT тагове за ВСИЧКИ изображения?')) {
return;
}
var button = $(this);
button.prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i> Започва обработка...');
$('#bulk-progress').show();
startBulkProcess('alt', 0, 0);
});
// Функция за масово обработване
function startBulkProcess(type, offset, totalProcessed) {
$.ajax({
url: aiseom_ajax.ajax_url,
type: 'POST',
data: {
action: 'aiseom_bulk_process',
nonce: aiseom_ajax.nonce,
type: type,
offset: offset
},
success: function(response) {
if (response.success) {
var data = response.data;
totalProcessed += data.processed;
// Актуализиране на прогрес бара
var percentage = Math.min(((offset + 5) / 100) * 100, 100);
$('.progress-fill').css('width', percentage + '%');
$('.progress-text').text('Обработени: ' + totalProcessed + ' елемента');
if (data.has_more) {
setTimeout(function() {
startBulkProcess(type, offset + 5, totalProcessed);
}, 1000);
} else {
alert('Завършено! Обработени общо ' + totalProcessed + ' елемента.');
$('#generate-all-meta, #generate-all-alt').prop('disabled', false).html(function() {
return type === 'meta' ?
'<i class="fas fa-magic"></i> Генерирай SEO за всички статии' :
'<i class="fas fa-image"></i> Генерирай ALT за всички изображения';
});
$('#bulk-progress').hide();
location.reload();
}
}
},
error: function() {
alert('Грешка при обработката');
$('#generate-all-meta, #generate-all-alt').prop('disabled', false).html(function() {
return type === 'meta' ?
'<i class="fas fa-magic"></i> Генерирай SEO за всички статии' :
'<i class="fas fa-image"></i> Генерирай ALT за всички изображения';
});
$('#bulk-progress').hide();
}
});
}
// Превключване на API описания
$('#aiseom_api_provider').on('change', function() {
var provider = $(this).val();
// Скриваме всички описания
$('.api-description').hide();
// Показваме само избраното
$('#api-desc-' + provider).show();
// Ако е демо, скриваме полето за API ключ
if (provider === 'demo') {
$('#api-key-row').hide();
} else {
$('#api-key-row').show();
}
});
// Инициализация при зареждане
$('#aiseom_api_provider').trigger('change');
// Зареждане на статистика
function loadStats() {
$.ajax({
url: aiseom_ajax.ajax_url,
type: 'POST',
data: {
action: 'aiseom_get_stats',
nonce: aiseom_ajax.nonce
},
success: function(response) {
if (response.success) {
var stats = response.data;
if ($('#total-posts').length) {
$('#total-posts').text(stats.total_posts);
}
if ($('#posts-with-meta').length) {
$('#posts-with-meta').text(stats.posts_with_meta);
}
if ($('#total-images').length) {
$('#total-images').text(stats.total_images);
}
if ($('#images-with-alt').length) {
$('#images-with-alt').text(stats.images_with_alt);
}
}
}
});
}
// Зареждаме статистиката ако сме на dashboard
if ($('#total-posts').length) {
loadStats();
}
});
</script>
<?php
}
public function add_seo_metabox() {
$post_types = get_option('aiseom_post_types', ['post', 'page']);
foreach ($post_types as $post_type) {
add_meta_box(
'aiseom_seo_status',
'🔍 SEO Статус & Генериране',
[$this, 'render_seo_metabox'],
$post_type,
'side',
'high'
);
}
}
public function render_seo_metabox($post) {
$post_id = $post->ID;
$meta_status = $this->get_post_meta_status($post_id);
$alt_status = $this->get_post_alt_status($post_id);
?>
<div class="aiseom-metabox">
<!-- SEO Мета Статус -->
<div class="aiseom-metabox-section">
<h4>📝 SEO Мета описание:</h4>
<div class="status-indicator <?php echo $meta_status['has_meta'] ? 'status-success' : 'status-error'; ?>">
<?php if ($meta_status['has_meta']): ?>
<span class="status-icon">✓</span>
<span class="status-text">Има генерирано (<?php echo $meta_status['length']; ?> знака)</span>
<?php else: ?>
<span class="status-icon">✗</span>
<span class="status-text">Няма генерирано</span>
<?php endif; ?>
</div>
<div style="margin-top: 10px;">
<button type="button" class="button button-primary aiseom-generate-meta" data-post-id="<?php echo $post_id; ?>" style="width: 100%;">
<i class="fas fa-magic"></i> <?php echo $meta_status['has_meta'] ? 'Регенерирай SEO' : 'Генерирай SEO'; ?>
</button>
</div>
</div>
<!-- ALT Тагове Статус -->
<div class="aiseom-metabox-section">
<h4>🖼️ ALT Тагове за изображения:</h4>
<div class="status-indicator <?php echo $alt_status['all_have_alt'] ? 'status-success' : ($alt_status['has_images'] ? 'status-warning' : 'status-neutral'); ?>">
<?php if (!$alt_status['has_images']): ?>
<span class="status-icon">➖</span>
<span class="status-text">Няма изображения</span>
<?php elseif ($alt_status['all_have_alt']): ?>
<span class="status-icon">✓</span>
<span class="status-text">Всички имат ALT (<?php echo $alt_status['total_images']; ?>)</span>
<?php else: ?>
<span class="status-icon">⚠</span>
<span class="status-text"><?php echo $alt_status['images_without_alt']; ?> без ALT от <?php echo $alt_status['total_images']; ?></span>
<?php endif; ?>
</div>
<?php if ($alt_status['has_images'] && !$alt_status['all_have_alt']): ?>
<div style="margin-top: 10px;">
<button type="button" class="button button-primary aiseom-generate-alt" data-post-id="<?php echo $post_id; ?>" style="width: 100%;">
<i class="fas fa-image"></i> Генерирай ALT
</button>
</div>
<?php endif; ?>
</div>
<!-- API Статус -->
<div class="aiseom-metabox-section">
<h4>🤖 API Статус:</h4>
<?php $api_status = $this->check_api_status(); ?>
<div class="status-indicator <?php echo $api_status['valid'] ? 'status-success' : 'status-error'; ?>">
<?php if ($api_status['valid']): ?>
<span class="status-icon">✓</span>
<span class="status-text">API ключът е валиден</span>
<?php else: ?>
<span class="status-icon">✗</span>
<span class="status-text"><?php echo $api_status['error']; ?></span>
<?php endif; ?>
</div>
</div>
</div>
<?php
}
public function register_settings() {
register_setting('aiseom_settings', 'aiseom_api_key');
register_setting('aiseom_settings', 'aiseom_api_provider');
register_setting('aiseom_settings', 'aiseom_language');
register_setting('aiseom_settings', 'aiseom_auto_generate');
register_setting('aiseom_settings', 'aiseom_auto_generate_alt');
register_setting('aiseom_settings', 'aiseom_post_types');
}
// AJAX обработчици
public function ajax_generate_meta() {
check_ajax_referer('aiseom_nonce', 'nonce');
if (!current_user_can('edit_posts')) {
wp_send_json_error('Нямате разрешение');
}
$post_id = intval($_POST['post_id']);
$post = get_post($post_id);
if (!$post) {
wp_send_json_error('Статията не е намерена');
}
$result = $this->generate_meta_description($post);
if ($result['success']) {
update_post_meta($post_id, '_aiseom_meta_description', $result['meta']);
update_post_meta($post_id, '_aiseom_meta_generated', current_time('mysql'));
wp_send_json_success([
'message' => 'Мета описанието е генерирано успешно',
'meta' => $result['meta'],
'length' => strlen($result['meta'])
]);
} else {
wp_send_json_error($result['error']);
}
}
public function ajax_generate_alt() {
check_ajax_referer('aiseom_nonce', 'nonce');
if (!current_user_can('edit_posts')) {
wp_send_json_error('Нямате разрешение');
}
$post_id = intval($_POST['post_id']);
$images = $this->get_post_images($post_id);
$generated_count = 0;
$errors = [];
foreach ($images as $image_id) {
$alt = get_post_meta($image_id, '_wp_attachment_image_alt', true);
// Генерираме ALT само ако няма такъв
if (empty($alt)) {
$result = $this->generate_image_alt($image_id);
if ($result['success']) {
update_post_meta($image_id, '_wp_attachment_image_alt', $result['alt']);
$generated_count++;
} else {
$errors[] = $result['error'];
}
}
}
if ($generated_count > 0) {
wp_send_json_success([
'message' => 'Генерирани са ' . $generated_count . ' ALT тага',
'count' => $generated_count,
'errors' => $errors
]);
} else {
wp_send_json_error('Няма изображения без ALT тагове или възникна грешка');
}
}
public function ajax_check_api() {
check_ajax_referer('aiseom_nonce', 'nonce');
if (!current_user_can('manage_options')) {
wp_send_json_error('Нямате разрешение');
}
$api_key = sanitize_text_field($_POST['api_key']);
$api_provider = sanitize_text_field($_POST['api_provider']);
if (empty($api_key)) {
wp_send_json_error('Моля, въведете API ключ');
}
$result = $this->test_api_key($api_key, $api_provider);
if ($result['success']) {
wp_send_json_success([
'message' => '✅ API ключът е валиден!',
'provider' => $api_provider,
'test_response' => $result['test_response']
]);
} else {
wp_send_json_error('❌ API ключът не е валиден: ' . $result['error']);
}
}
public function ajax_bulk_process() {
check_ajax_referer('aiseom_nonce', 'nonce');
if (!current_user_can('manage_options')) {
wp_send_json_error('Нямате разрешение');
}
$type = sanitize_text_field($_POST['type']);
$offset = intval($_POST['offset']);
$limit = 5;
$processed = 0;
if ($type === 'meta') {
$posts = get_posts([
'post_type' => 'post',
'post_status' => 'publish',
'numberposts' => $limit,
'offset' => $offset
]);
foreach ($posts as $post) {
if (!get_post_meta($post->ID, '_aiseom_meta_description', true)) {
$result = $this->generate_meta_description($post);
if ($result['success']) {
update_post_meta($post->ID, '_aiseom_meta_description', $result['meta']);
$processed++;
}
}
}
} elseif ($type === 'alt') {
$images = get_posts([
'post_type' => 'attachment',
'post_mime_type' => 'image',
'numberposts' => $limit,
'offset' => $offset
]);
foreach ($images as $image) {
if (!get_post_meta($image->ID, '_wp_attachment_image_alt', true)) {
$result = $this->generate_image_alt($image->ID);
if ($result['success']) {
update_post_meta($image->ID, '_wp_attachment_image_alt', $result['alt']);
$processed++;
}
}
}
}
$total = $type === 'meta' ? wp_count_posts('post')->publish : $this->count_total_images();
$has_more = ($offset + $limit) < $total;
wp_send_json_success([
'processed' => $processed,
'has_more' => $has_more
]);
}
public function ajax_get_stats() {
wp_send_json_success([
'total_posts' => $this->count_total_posts(),
'posts_with_meta' => $this->count_posts_with_meta(),
'total_images' => $this->count_total_images(),
'images_with_alt' => $this->count_images_with_alt()
]);
}
// Помощни функции
private function generate_meta_description($post) {
$content = wp_strip_all_tags($post->post_content);
$title = $post->post_title;
if (strlen($content) > 1500) {
$content = substr($content, 0, 1500) . '...';
}
$api_key = get_option('aiseom_api_key');
$api_provider = get_option('aiseom_api_provider', 'demo');
$language = get_option('aiseom_language', 'bg');
// Ако няма API ключ или е демо режим, използваме демо съдържание
if (empty($api_key) || $api_provider === 'demo') {
return $this->generate_demo_content('meta', $language, $title);
}
// Тук бихме извикали реално API
// За сега връщаме демо съдържание
return $this->generate_demo_content('meta', $language, $title);
}
private function generate_image_alt($image_id) {
$image_title = get_the_title($image_id);
$language = get_option('aiseom_language', 'bg');
$api_key = get_option('aiseom_api_key');
$api_provider = get_option('aiseom_api_provider', 'demo');
// Ако няма API ключ или е демо режим, използваме демо съдържание
if (empty($api_key) || $api_provider === 'demo') {
return $this->generate_demo_content('alt', $language, $image_title);
}
// Тук бихме извикали реално API
// За сега връщаме демо съдържание
return $this->generate_demo_content('alt', $language, $image_title);
}
private function generate_demo_content($type, $language, $context = '') {
if ($type === 'meta') {
if ($language === 'bg') {
$responses = [
'Професионален наръчник с изчерпателна информация и практични съвети за успешно прилагане на методите.',
'Научете всичко необходимо за темата от нашите експерти с годишен опит в областта и практическо приложение.',
'Подробен анализ и стъпково ръководство за постигане на оптимални резултати с минимални усилия и време.',
'Актуална информация и доказани техники за подобряване на ефективността и продуктивността в ежедневната работа.',
'Комплексно ръководство със стратегии и тактики за успешно надграждане на уменията и постигане на целите.'
];
} else {
$responses = [
'Professional guide with comprehensive information and practical tips for successful application of methods.',
'Learn everything you need about the topic from our experts with years of experience in the field.',
'Detailed analysis and step-by-step guide to achieving optimal results with minimal effort and time.',
'Current information and proven techniques for improving efficiency and productivity in daily work.',
'Comprehensive guide with strategies and tactics for successful skill building and goal achievement.'
];
}
} else { // ALT
if ($language === 'bg') {
$responses = [
'Илюстрация демонстрираща ключови аспекти и важни детайли от обсъжданата тема ' . $context,
'Визуално представяне на концепции и идеи свързани с ' . $context,
'Качествено изображение илюстриращо практически приложения на ' . $context,
'Информативна графика показваща аспекти от ' . $context,
'Професионална фотография свързана с темата ' . $context
];
} else {
$responses = [
'Illustration demonstrating key aspects and important details of ' . $context,
'Visual representation of concepts and ideas related to ' . $context,
'High quality image illustrating practical applications of ' . $context,
'Informative graphic showing aspects of ' . $context,
'Professional photography related to the topic of ' . $context
];
}
}
$text = $responses[array_rand($responses)];
return [
'success' => true,
'meta' => $type === 'meta' ? $text : null,
'alt' => $type === 'alt' ? $text : null
];
}
private function test_api_key($api_key, $provider) {
// Това е опростена проверка
// На практика бихме изпратили тестова заявка към API-то
if (empty($api_key)) {
return [
'success' => false,
'error' => 'API ключът е празен'
];
}
if (strlen($api_key) < 10) {
return [
'success' => false,
'error' => 'API ключът е твърде кратък'
];
}
// Симулираме успешна проверка
// На практика тук бихме изпратили заявка към Hugging Face или друг API
return [
'success' => true,
'test_response' => 'Тестовата заявка е успешна. API ключът работи.'
];
}
private function check_api_status() {
$api_key = get_option('aiseom_api_key');
if (empty($api_key)) {
return [
'valid' => false,
'error' => 'API ключът не е настроен'
];
}
return [
'valid' => true,
'error' => ''
];
}
private function get_post_meta_status($post_id) {
$meta = get_post_meta($post_id, '_aiseom_meta_description', true);
return [
'has_meta' => !empty($meta),
'length' => strlen($meta),
'preview' => $meta ? substr($meta, 0, 80) . '...' : ''
];
}
private function get_post_alt_status($post_id) {
$images = $this->get_post_images($post_id);
$total_images = count($images);
$images_without_alt = 0;
foreach ($images as $image_id) {
$alt = get_post_meta($image_id, '_wp_attachment_image_alt', true);
if (empty($alt)) {
$images_without_alt++;
}
}
return [
'has_images' => $total_images > 0,
'total_images' => $total_images,
'images_without_alt' => $images_without_alt,
'all_have_alt' => $total_images > 0 && $images_without_alt === 0
];
}
private function get_post_images($post_id) {
$content = get_post_field('post_content', $post_id);
$images = [];
preg_match_all('/<img[^>]+>/i', $content, $img_tags);
foreach ($img_tags[0] as $img_tag) {
if (preg_match('/src="([^"]+)"/i', $img_tag, $src_match)) {
$image_url = $src_match[1];
$attachment_id = attachment_url_to_postid($image_url);
if ($attachment_id) {
$images[] = $attachment_id;
}
}
}
return $images;
}
private function count_total_posts() {
return wp_count_posts('post')->publish;
}
private function count_posts_with_meta() {
global $wpdb;
return $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->postmeta} WHERE meta_key = '_aiseom_meta_description'");
}
private function count_total_images() {
return wp_count_posts('attachment')->inherit;
}
private function count_images_with_alt() {
global $wpdb;
return $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->postmeta} WHERE meta_key = '_wp_attachment_image_alt' AND meta_value != ''");
}
// Рендериране на страници
public function render_dashboard() {
?>
<div class="wrap aiseom-dashboard">
<div class="aiseom-header">
<h1><i class="fas fa-robot"></i> AI SEO Master PRO</h1>
<p>Вашият професионален помощник за SEO оптимизация с изкуствен интелект</p>
</div>
<div class="aiseom-stats-grid">
<div class="aiseom-stat-card">
<div class="stat-icon">
<i class="fas fa-file-alt"></i>
</div>
<div class="stat-content">
<span class="stat-number" id="total-posts">0</span>
<span class="stat-label">Общо статии</span>
</div>
</div>
<div class="aiseom-stat-card">
<div class="stat-icon">
<i class="fas fa-check-circle"></i>
</div>
<div class="stat-content">
<span class="stat-number" id="posts-with-meta">0</span>
<span class="stat-label">С SEO мета</span>
</div>
</div>
<div class="aiseom-stat-card">
<div class="stat-icon">
<i class="fas fa-image"></i>
</div>
<div class="stat-content">
<span class="stat-number" id="total-images">0</span>
<span class="stat-label">Общо изображения</span>
</div>
</div>
<div class="aiseom-stat-card">
<div class="stat-icon">
<i class="fas fa-tag"></i>
</div>
<div class="stat-content">
<span class="stat-number" id="images-with-alt">0</span>
<span class="stat-label">С ALT тагове</span>
</div>
</div>
</div>
<div class="aiseom-actions-grid">
<div class="aiseom-action-card">
<h3><i class="fas fa-bolt"></i> Бързи действия</h3>
<p>Автоматично генериране на SEO съдържание</p>
<div style="display: flex; flex-direction: column; gap: 10px; margin-top: 20px;">
<button id="generate-all-meta" class="aiseom-btn">
<i class="fas fa-magic"></i> Генерирай SEO за всички статии
</button>
<button id="generate-all-alt" class="aiseom-btn aiseom-btn-secondary">
<i class="fas fa-image"></i> Генерирай ALT за всички изображения
</button>
</div>
<div id="bulk-progress" class="progress-container">
<div class="progress-bar">
<div class="progress-fill"></div>
</div>
<p class="progress-text"></p>
</div>
</div>
<div class="aiseom-action-card">
<h3><i class="fas fa-chart-pie"></i> SEO Статистика</h3>
<p>Преглед на SEO оптимизацията на вашия сайт</p>
<div style="margin-top: 20px;">
<table class="aiseom-table">
<tr>
<td>Статии без SEO мета:</td>
<td><strong><?php echo $this->count_total_posts() - $this->count_posts_with_meta(); ?></strong></td>
</tr>
<tr>
<td>Изображения без ALT:</td>
<td><strong><?php echo $this->count_total_images() - $this->count_images_with_alt(); ?></strong></td>
</tr>
<tr>
<td>API Статус:</td>
<td>
<?php $api_status = $this->check_api_status(); ?>
<span style="color: <?php echo $api_status['valid'] ? '#4CAF50' : '#f44336'; ?>;">
<?php echo $api_status['valid'] ? '✅ Активен' : '❌ Неактивен'; ?>
</span>
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="aiseom-action-card">
<h3><i class="fas fa-lightbulb"></i> Съвети за SEO</h3>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 15px; margin-top: 15px;">
<div style="padding: 15px; background: #f8f9fa; border-radius: 8px; border-left: 4px solid #4CAF50;">
<strong>✅ Генерирайте уникални мета описания</strong>
<p style="margin: 5px 0 0; font-size: 14px; color: #666;">Всяка страница трябва да има уникално SEO описание.</p>
</div>
<div style="padding: 15px; background: #f8f9fa; border-radius: 8px; border-left: 4px solid #2196F3;">
<strong>✅ Добавете ALT текст към изображенията</strong>
<p style="margin: 5px 0 0; font-size: 14px; color: #666;">ALT таговете подобряват SEO и достъпността.</p>
</div>
<div style="padding: 15px; background: #f8f9fa; border-radius: 8px; border-left: 4px solid #FF9800;">
<strong>✅ Използвайте ключови думи</strong>
<p style="margin: 5px 0 0; font-size: 14px; color: #666;">Включете ключови думи в заглавията и съдържанието.</p>
</div>
</div>
</div>
</div>
<?php
}
public function render_unoptimized_posts() {
$posts = get_posts([
'post_type' => 'post',
'post_status' => 'publish',
'numberposts' => -1
]);
$posts_without_meta = [];
$posts_without_alt = [];
foreach ($posts as $post) {
$has_meta = get_post_meta($post->ID, '_aiseom_meta_description', true);
$images = $this->get_post_images($post->ID);
$has_all_alt = true;
foreach ($images as $image_id) {
$alt = get_post_meta($image_id, '_wp_attachment_image_alt', true);
if (empty($alt)) {
$has_all_alt = false;
break;
}
}
if (!$has_meta) {
$posts_without_meta[] = $post;
}
if ($images && !$has_all_alt) {
$posts_without_alt[] = $post;
}
}
?>
<div class="wrap" style="padding: 20px;">
<h1><i class="fas fa-exclamation-triangle"></i> Статии без SEO оптимизация</h1>
<div class="aiseom-stats-grid" style="margin: 30px 0;">
<div class="aiseom-stat-card">
<div class="stat-icon" style="background: linear-gradient(135deg, #ff9800 0%, #f57c00 100%);">
<i class="fas fa-file-alt"></i>
</div>
<div class="stat-content">
<span class="stat-number"><?php echo count($posts_without_meta); ?></span>
<span class="stat-label">Статии без SEO мета</span>
</div>
</div>
<div class="aiseom-stat-card">
<div class="stat-icon" style="background: linear-gradient(135deg, #f44336 0%, #d32f2f 100%);">
<i class="fas fa-image"></i>
</div>
<div class="stat-content">
<span class="stat-number"><?php echo count($posts_without_alt); ?></span>
<span class="stat-label">Статии без ALT тагове</span>
</div>
</div>
</div>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 30px; margin-top: 30px;">
<div class="aiseom-action-card">
<h3><i class="fas fa-file-alt"></i> Статии без SEO мета (<?php echo count($posts_without_meta); ?>)</h3>
<?php if (empty($posts_without_meta)): ?>
<div style="text-align: center; padding: 40px; color: #4CAF50;">
<i class="fas fa-check-circle" style="font-size: 48px;"></i>
<h3>Всички статии имат SEO мета описания!</h3>
</div>
<?php else: ?>
<div style="max-height: 400px; overflow-y: auto; margin-top: 20px;">
<table class="aiseom-table">
<thead>
<tr>
<th>Заглавие</th>
<th>Дата</th>
<th>Действие</th>
</tr>
</thead>
<tbody>
<?php foreach ($posts_without_meta as $post): ?>
<tr>
<td>
<strong><?php echo esc_html($post->post_title); ?></strong>
<div style="font-size: 12px; color: #666;">ID: <?php echo $post->ID; ?></div>
</td>
<td><?php echo get_the_date('d.m.Y', $post->ID); ?></td>
<td>
<button class="button button-primary aiseom-generate-meta" data-post-id="<?php echo $post->ID; ?>" style="font-size: 12px; padding: 5px 10px;">
<i class="fas fa-magic"></i> Генерирай
</button>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<div style="text-align: center; margin-top: 20px;">
<button id="generate-all-meta-list" class="button button-primary button-large" data-count="<?php echo count($posts_without_meta); ?>">
<i class="fas fa-bolt"></i> Генерирай SEO за всички <?php echo count($posts_without_meta); ?> статии
</button>
</div>
<?php endif; ?>
</div>
<div class="aiseom-action-card">
<h3><i class="fas fa-image"></i> Статии без ALT тагове (<?php echo count($posts_without_alt); ?>)</h3>
<?php if (empty($posts_without_alt)): ?>
<div style="text-align: center; padding: 40px; color: #4CAF50;">
<i class="fas fa-check-circle" style="font-size: 48px;"></i>
<h3>Всички статии имат ALT тагове!</h3>
</div>
<?php else: ?>
<div style="max-height: 400px; overflow-y: auto; margin-top: 20px;">
<table class="aiseom-table">
<thead>
<tr>
<th>Заглавие</th>
<th>Изображения</th>
<th>Действие</th>
</tr>
</thead>
<tbody>
<?php foreach ($posts_without_alt as $post):
$images = $this->get_post_images($post->ID);
$images_without_alt = 0;
foreach ($images as $image_id) {
$alt = get_post_meta($image_id, '_wp_attachment_image_alt', true);
if (empty($alt)) {
$images_without_alt++;
}
}
?>
<tr>
<td>
<strong><?php echo esc_html($post->post_title); ?></strong>
<div style="font-size: 12px; color: #666;">ID: <?php echo $post->ID; ?></div>
</td>
<td>
<span style="color: <?php echo $images_without_alt > 0 ? '#f44336' : '#4CAF50'; ?>;">
<?php echo $images_without_alt; ?> без ALT
</span>
</td>
<td>
<button class="button button-primary aiseom-generate-alt" data-post-id="<?php echo $post->ID; ?>" style="font-size: 12px; padding: 5px 10px;">
<i class="fas fa-image"></i> Генерирай
</button>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<div style="text-align: center; margin-top: 20px;">
<button id="generate-all-alt-list" class="button button-primary button-large" data-count="<?php echo count($posts_without_alt); ?>">
<i class="fas fa-bolt"></i> Генерирай ALT за всички <?php echo count($posts_without_alt); ?> статии
</button>
</div>
<?php endif; ?>
</div>
</div>
<script>
jQuery(document).ready(function($) {
// Генериране на всички статии без SEO мета от листа
$('#generate-all-meta-list').on('click', function() {
var count = $(this).data('count');
if (!confirm('Сигурни ли сте, че искате да генерирате SEO мета описания за ВСИЧКИ ' + count + ' статии?')) {
return;
}
$(this).prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i> Обработване...');
// Намираме всички бутони за генериране и кликаме на всеки един
var buttons = $('.aiseom-generate-meta');
var total = buttons.length;
var processed = 0;
function processNext(index) {
if (index >= total) {
alert('Готово! Обработени ' + total + ' статии.');
location.reload();
return;
}
buttons.eq(index).click();
// Почакаме малко преди следващия
setTimeout(function() {
processNext(index + 1);
}, 2000);
}
processNext(0);
});
// Генериране на всички статии без ALT от листа
$('#generate-all-alt-list').on('click', function() {
var count = $(this).data('count');
if (!confirm('Сигурни ли сте, че искате да генерирате ALT тагове за ВСИЧКИ ' + count + ' статии?')) {
return;
}
$(this).prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i> Обработване...');
// Намираме всички бутони за генериране и кликаме на всеки един
var buttons = $('.aiseom-generate-alt');
var total = buttons.length;
var processed = 0;
function processNext(index) {
if (index >= total) {
alert('Готово! Обработени ' + total + ' статии.');
location.reload();
return;
}
buttons.eq(index).click();
// Почакаме малко преди следващия
setTimeout(function() {
processNext(index + 1);
}, 2000);
}
processNext(0);
});
});
</script>
</div>
<?php
}
public function render_settings_page() {
?>
<div class="wrap" style="padding: 20px;">
<h1><i class="fas fa-cog"></i> Настройки на AI SEO Master PRO</h1>
<div class="aiseom-settings-form">
<form method="post" action="options.php">
<?php settings_fields('aiseom_settings'); ?>
<div class="form-group">
<label for="aiseom_api_provider">AI Доставчик:</label>
<select name="aiseom_api_provider" id="aiseom_api_provider" class="regular-text">
<option value="demo" <?php selected(get_option('aiseom_api_provider', 'demo'), 'demo'); ?>>🎮 Демо режим (безплатен - за тестване)</option>
<option value="huggingface" <?php selected(get_option('aiseom_api_provider'), 'huggingface'); ?>>🤗 Hugging Face (безплатен API)</option>
<option value="openai" <?php selected(get_option('aiseom_api_provider'), 'openai'); ?>>🤖 OpenAI GPT (платен)</option>
</select>
<div id="api-desc-demo" class="api-description" style="display: none; margin-top: 8px; padding: 10px; background: #f8f9fa; border-radius: 5px;">
<p><strong>🎮 Демо режим:</strong> Работи без API ключ. Генерира примерни описания за тестване.</p>
</div>
<div id="api-desc-huggingface" class="api-description" style="display: none; margin-top: 8px; padding: 10px; background: #f8f9fa; border-radius: 5px;">
<p><strong>🤗 Hugging Face:</strong> Безплатен AI API. <a href="https://huggingface.co/settings/tokens" target="_blank">Получете безплатен ключ тук</a></p>
</div>
<div id="api-desc-openai" class="api-description" style="display: none; margin-top: 8px; padding: 10px; background: #f8f9fa; border-radius: 5px;">
<p><strong>🤖 OpenAI:</strong> Платен API. <a href="https://platform.openai.com/api-keys" target="_blank">Получете API ключ тук</a></p>
</div>
</div>
<div class="form-group" id="api-key-row">
<label for="aiseom_api_key">API Ключ:</label>
<input type="password"
name="aiseom_api_key"
id="aiseom_api_key"
value="<?php echo esc_attr(get_option('aiseom_api_key', '')); ?>"
class="regular-text"
placeholder="Въведете вашия API ключ">
<button type="button" id="test-api-key" class="button" style="margin-top: 10px;">
<i class="fas fa-wifi"></i> Тествай API ключа
</button>
<div id="api-test-result" style="margin-top: 10px;"></div>
</div>
<div class="form-group">
<label for="aiseom_language">Език за генериране:</label>
<select name="aiseom_language" id="aiseom_language">
<option value="bg" <?php selected(get_option('aiseom_language', 'bg'), 'bg'); ?>>Български</option>
<option value="en" <?php selected(get_option('aiseom_language'), 'en'); ?>>Английски</option>
</select>
</div>
<div class="form-group">
<label>
<input type="checkbox"
name="aiseom_auto_generate"
value="1"
<?php checked(get_option('aiseom_auto_generate', '1'), '1'); ?>>
Автоматично генериране на SEO при запазване на статия
</label>
</div>
<div class="form-group">
<label>
<input type="checkbox"
name="aiseom_auto_generate_alt"
value="1"
<?php checked(get_option('aiseom_auto_generate_alt', '1'), '1'); ?>>
Автоматично генериране на ALT тагове за изображения
</label>
</div>
<div class="form-group">
<label>Типове публикации за оптимизация:</label>
<div style="margin-top: 10px;">
<?php
$post_types = get_post_types(['public' => true], 'objects');
$selected = get_option('aiseom_post_types', ['post', 'page']);
foreach ($post_types as $type) {
if ($type->name === 'attachment') continue;
?>
<label style="display: block; margin-bottom: 8px;">
<input type="checkbox"
name="aiseom_post_types[]"
value="<?php echo $type->name; ?>"
<?php checked(in_array($type->name, $selected)); ?>>
<?php echo $type->label; ?>
</label>
<?php
}
?>
</div>
</div>
<?php submit_button('💾 Запази настройките', 'primary'); ?>
</form>
</div>
</div>
<script>
jQuery(document).ready(function($) {
// Показване/скриване на API описания
function updateApiDescription() {
var provider = $('#aiseom_api_provider').val();
// Скриваме всички описания
$('.api-description').hide();
// Показваме само избраното
$('#api-desc-' + provider).show();
// Ако е демо, скриваме полето за API ключ
if (provider === 'demo') {
$('#api-key-row').hide();
} else {
$('#api-key-row').show();
}
}
$('#aiseom_api_provider').on('change', updateApiDescription);
updateApiDescription(); // Инициализация при зареждане
});
</script>
<?php
}
}
// Инициализация
function aiseom_pro_init() {
return AI_SEO_Master_PRO::get_instance();
}
add_action('plugins_loaded', 'aiseom_pro_init');
// Активационен хук
register_activation_hook(__FILE__, function() {
// Настройки по подразбиране
$defaults = [
'aiseom_api_provider' => 'demo',
'aiseom_language' => 'bg',
'aiseom_auto_generate' => '1',
'aiseom_auto_generate_alt' => '1',
'aiseom_post_types' => ['post', 'page']
];
foreach ($defaults as $key => $value) {
if (get_option($key) === false) {
update_option($key, $value);
}
}
});
| 1 | <?php |
| 2 | /** |
| 3 | * Plugin Name: AI SEO Master PRO |
| 4 | * Plugin URI: https://urocibg.eu/ai-seo-master-pro |
| 5 | * Description: 🚀 Професионален AI SEO оптимизатор с мониторинг, валидация и напълно безплатни API функционалности |
| 6 | * Version: 2.0.0 |
| 7 | * Author: Fedya Serafiev |
| 8 | * Author URI: https://fedia.eu |
| 9 | * License: GPL v3 |
| 10 | * Text Domain: ai-seo-master-pro |
| 11 | */ |
| 12 | |
| 13 | // Безопасност |
| 14 | if (!defined('ABSPATH')) { |
| 15 | exit; |
| 16 | } |
| 17 | |
| 18 | // Дефиниране на константи |
| 19 | define('AISEOM_VERSION', '2.0.0'); |
| 20 | define('AISEOM_PLUGIN_DIR', plugin_dir_path(__FILE__)); |
| 21 | define('AISEOM_PLUGIN_URL', plugin_dir_url(__FILE__)); |
| 22 | |
| 23 | // Главен клас |
| 24 | class AI_SEO_Master_PRO { |
| 25 | |
| 26 | private static $instance = null; |
| 27 | |
| 28 | public static function get_instance() { |
| 29 | if (null === self::$instance) { |
| 30 | self::$instance = new self(); |
| 31 | } |
| 32 | return self::$instance; |
| 33 | } |
| 34 | |
| 35 | private function __construct() { |
| 36 | // Инициализация |
| 37 | add_action('init', [$this, 'init']); |
| 38 | add_action('admin_menu', [$this, 'create_admin_menu']); |
| 39 | add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_assets']); |
| 40 | |
| 41 | // AJAX обработчици |
| 42 | add_action('wp_ajax_aiseom_generate_meta', [$this, 'ajax_generate_meta']); |
| 43 | add_action('wp_ajax_aiseom_generate_alt', [$this, 'ajax_generate_alt']); |
| 44 | add_action('wp_ajax_aiseom_bulk_process', [$this, 'ajax_bulk_process']); |
| 45 | add_action('wp_ajax_aiseom_check_api', [$this, 'ajax_check_api']); |
| 46 | add_action('wp_ajax_aiseom_get_stats', [$this, 'ajax_get_stats']); |
| 47 | |
| 48 | // Мета боксове |
| 49 | add_action('add_meta_boxes', [$this, 'add_seo_metabox']); |
| 50 | |
| 51 | // Настройки |
| 52 | add_action('admin_init', [$this, 'register_settings']); |
| 53 | } |
| 54 | |
| 55 | public function init() { |
| 56 | // Зареждане на текстове за превод |
| 57 | load_plugin_textdomain('ai-seo-master-pro', false, dirname(plugin_basename(__FILE__)) . '/languages'); |
| 58 | } |
| 59 | |
| 60 | public function create_admin_menu() { |
| 61 | // Главно меню |
| 62 | add_menu_page( |
| 63 | 'AI SEO PRO', |
| 64 | 'AI SEO PRO', |
| 65 | 'manage_options', |
| 66 | 'ai-seo-master-pro', |
| 67 | [$this, 'render_dashboard'], |
| 68 | 'dashicons-chart-line', |
| 69 | 30 |
| 70 | ); |
| 71 | |
| 72 | // Подменюта |
| 73 | add_submenu_page( |
| 74 | 'ai-seo-master-pro', |
| 75 | 'Табло', |
| 76 | 'Табло', |
| 77 | 'manage_options', |
| 78 | 'ai-seo-master-pro', |
| 79 | [$this, 'render_dashboard'] |
| 80 | ); |
| 81 | |
| 82 | add_submenu_page( |
| 83 | 'ai-seo-master-pro', |
| 84 | 'Статии без SEO', |
| 85 | 'Статии без SEO', |
| 86 | 'manage_options', |
| 87 | 'ai-seo-unoptimized', |
| 88 | [$this, 'render_unoptimized_posts'] |
| 89 | ); |
| 90 | |
| 91 | add_submenu_page( |
| 92 | 'ai-seo-master-pro', |
| 93 | 'Настройки', |
| 94 | 'Настройки', |
| 95 | 'manage_options', |
| 96 | 'ai-seo-settings', |
| 97 | [$this, 'render_settings_page'] |
| 98 | ); |
| 99 | } |
| 100 | |
| 101 | public function enqueue_admin_assets($hook) { |
| 102 | // Зареждаме само в нашите страници |
| 103 | $our_pages = [ |
| 104 | 'toplevel_page_ai-seo-master-pro', |
| 105 | 'ai-seo-master-pro_page_ai-seo-unoptimized', |
| 106 | 'ai-seo-master-pro_page_ai-seo-settings' |
| 107 | ]; |
| 108 | |
| 109 | if (!in_array($hook, $our_pages) && !strstr($hook, 'post.php') && !strstr($hook, 'edit.php')) { |
| 110 | return; |
| 111 | } |
| 112 | |
| 113 | // Вграждаме CSS директно |
| 114 | add_action('admin_head', [$this, 'add_inline_css']); |
| 115 | |
| 116 | // Вграждаме JavaScript директно |
| 117 | add_action('admin_footer', [$this, 'add_inline_js']); |
| 118 | } |
| 119 | |
| 120 | public function add_inline_css() { |
| 121 | ?> |
| 122 | <style> |
| 123 | /* Основни стилове за AI SEO Master PRO */ |
| 124 | .aiseom-dashboard { |
| 125 | background: #f0f0f1; |
| 126 | padding: 20px; |
| 127 | min-height: 100vh; |
| 128 | } |
| 129 | |
| 130 | .aiseom-header { |
| 131 | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| 132 | color: white; |
| 133 | padding: 30px; |
| 134 | border-radius: 10px; |
| 135 | margin-bottom: 30px; |
| 136 | } |
| 137 | |
| 138 | .aiseom-header h1 { |
| 139 | color: white; |
| 140 | margin: 0 0 10px 0; |
| 141 | display: flex; |
| 142 | align-items: center; |
| 143 | gap: 15px; |
| 144 | } |
| 145 | |
| 146 | .aiseom-stats-grid { |
| 147 | display: grid; |
| 148 | grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); |
| 149 | gap: 20px; |
| 150 | margin: 30px 0; |
| 151 | } |
| 152 | |
| 153 | .aiseom-stat-card { |
| 154 | background: white; |
| 155 | border-radius: 10px; |
| 156 | padding: 25px; |
| 157 | display: flex; |
| 158 | align-items: center; |
| 159 | gap: 20px; |
| 160 | box-shadow: 0 5px 15px rgba(0,0,0,0.05); |
| 161 | transition: transform 0.3s ease; |
| 162 | } |
| 163 | |
| 164 | .aiseom-stat-card:hover { |
| 165 | transform: translateY(-5px); |
| 166 | } |
| 167 | |
| 168 | .aiseom-stat-card .stat-icon { |
| 169 | width: 60px; |
| 170 | height: 60px; |
| 171 | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| 172 | border-radius: 10px; |
| 173 | display: flex; |
| 174 | align-items: center; |
| 175 | justify-content: center; |
| 176 | color: white; |
| 177 | font-size: 28px; |
| 178 | } |
| 179 | |
| 180 | .aiseom-stat-card .stat-number { |
| 181 | font-size: 36px; |
| 182 | font-weight: bold; |
| 183 | color: #2d3748; |
| 184 | display: block; |
| 185 | line-height: 1; |
| 186 | } |
| 187 | |
| 188 | .aiseom-stat-card .stat-label { |
| 189 | color: #718096; |
| 190 | font-size: 16px; |
| 191 | display: block; |
| 192 | margin-top: 5px; |
| 193 | } |
| 194 | |
| 195 | .aiseom-actions-grid { |
| 196 | display: grid; |
| 197 | grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); |
| 198 | gap: 20px; |
| 199 | margin: 30px 0; |
| 200 | } |
| 201 | |
| 202 | .aiseom-action-card { |
| 203 | background: white; |
| 204 | border-radius: 10px; |
| 205 | padding: 25px; |
| 206 | box-shadow: 0 5px 15px rgba(0,0,0,0.05); |
| 207 | } |
| 208 | |
| 209 | .aiseom-action-card h3 { |
| 210 | margin-top: 0; |
| 211 | color: #2d3748; |
| 212 | font-size: 20px; |
| 213 | display: flex; |
| 214 | align-items: center; |
| 215 | gap: 10px; |
| 216 | } |
| 217 | |
| 218 | .aiseom-btn { |
| 219 | display: inline-flex; |
| 220 | align-items: center; |
| 221 | gap: 8px; |
| 222 | padding: 12px 24px; |
| 223 | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| 224 | color: white; |
| 225 | border: none; |
| 226 | border-radius: 8px; |
| 227 | font-weight: 600; |
| 228 | cursor: pointer; |
| 229 | text-decoration: none; |
| 230 | transition: all 0.3s ease; |
| 231 | } |
| 232 | |
| 233 | .aiseom-btn:hover { |
| 234 | transform: translateY(-2px); |
| 235 | box-shadow: 0 10px 20px rgba(102, 126, 234, 0.3); |
| 236 | color: white; |
| 237 | } |
| 238 | |
| 239 | .aiseom-btn-secondary { |
| 240 | background: #edf2f7; |
| 241 | color: #4a5568; |
| 242 | } |
| 243 | |
| 244 | .aiseom-btn-secondary:hover { |
| 245 | background: #e2e8f0; |
| 246 | } |
| 247 | |
| 248 | /* Мета бокс стилове */ |
| 249 | .aiseom-metabox { |
| 250 | padding: 10px 0; |
| 251 | } |
| 252 | |
| 253 | .aiseom-metabox-section { |
| 254 | margin-bottom: 20px; |
| 255 | padding-bottom: 15px; |
| 256 | border-bottom: 1px solid #eee; |
| 257 | } |
| 258 | |
| 259 | .aiseom-metabox-section:last-child { |
| 260 | border-bottom: none; |
| 261 | } |
| 262 | |
| 263 | .status-indicator { |
| 264 | padding: 8px; |
| 265 | border-radius: 4px; |
| 266 | margin: 8px 0; |
| 267 | display: flex; |
| 268 | align-items: center; |
| 269 | gap: 8px; |
| 270 | } |
| 271 | |
| 272 | .status-success { |
| 273 | background: #edf7ed; |
| 274 | border-left: 4px solid #4caf50; |
| 275 | } |
| 276 | |
| 277 | .status-warning { |
| 278 | background: #fff8e1; |
| 279 | border-left: 4px solid #ff9800; |
| 280 | } |
| 281 | |
| 282 | .status-error { |
| 283 | background: #fdeded; |
| 284 | border-left: 4px solid #f44336; |
| 285 | } |
| 286 | |
| 287 | .status-neutral { |
| 288 | background: #f5f5f5; |
| 289 | border-left: 4px solid #9e9e9e; |
| 290 | } |
| 291 | |
| 292 | .status-icon { |
| 293 | font-weight: bold; |
| 294 | font-size: 14px; |
| 295 | min-width: 16px; |
| 296 | } |
| 297 | |
| 298 | /* Таблици */ |
| 299 | .aiseom-table { |
| 300 | width: 100%; |
| 301 | border-collapse: collapse; |
| 302 | background: white; |
| 303 | border-radius: 10px; |
| 304 | overflow: hidden; |
| 305 | box-shadow: 0 2px 10px rgba(0,0,0,0.05); |
| 306 | } |
| 307 | |
| 308 | .aiseom-table th { |
| 309 | background: #f8f9fa; |
| 310 | padding: 15px; |
| 311 | text-align: left; |
| 312 | font-weight: 600; |
| 313 | color: #495057; |
| 314 | border-bottom: 2px solid #e9ecef; |
| 315 | } |
| 316 | |
| 317 | .aiseom-table td { |
| 318 | padding: 12px 15px; |
| 319 | border-bottom: 1px solid #e9ecef; |
| 320 | } |
| 321 | |
| 322 | .aiseom-table tr:hover { |
| 323 | background: #f8f9fa; |
| 324 | } |
| 325 | |
| 326 | /* Форми и настройки */ |
| 327 | .aiseom-settings-form { |
| 328 | background: white; |
| 329 | padding: 30px; |
| 330 | border-radius: 10px; |
| 331 | box-shadow: 0 5px 15px rgba(0,0,0,0.05); |
| 332 | } |
| 333 | |
| 334 | .form-group { |
| 335 | margin-bottom: 25px; |
| 336 | } |
| 337 | |
| 338 | .form-group label { |
| 339 | display: block; |
| 340 | margin-bottom: 8px; |
| 341 | font-weight: 600; |
| 342 | color: #2d3748; |
| 343 | } |
| 344 | |
| 345 | .form-group input[type="text"], |
| 346 | .form-group input[type="password"], |
| 347 | .form-group select { |
| 348 | width: 100%; |
| 349 | max-width: 500px; |
| 350 | padding: 10px; |
| 351 | border: 1px solid #cbd5e0; |
| 352 | border-radius: 6px; |
| 353 | font-size: 14px; |
| 354 | } |
| 355 | |
| 356 | .form-group input:focus, |
| 357 | .form-group select:focus { |
| 358 | outline: none; |
| 359 | border-color: #667eea; |
| 360 | box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); |
| 361 | } |
| 362 | |
| 363 | .api-test-result { |
| 364 | margin-top: 10px; |
| 365 | padding: 10px; |
| 366 | border-radius: 6px; |
| 367 | font-weight: 500; |
| 368 | } |
| 369 | |
| 370 | .api-test-success { |
| 371 | background: #edf7ed; |
| 372 | color: #2e7d32; |
| 373 | border-left: 4px solid #4caf50; |
| 374 | } |
| 375 | |
| 376 | .api-test-error { |
| 377 | background: #fdeded; |
| 378 | color: #c62828; |
| 379 | border-left: 4px solid #f44336; |
| 380 | } |
| 381 | |
| 382 | /* Прогрес бар */ |
| 383 | .progress-container { |
| 384 | margin: 20px 0; |
| 385 | display: none; |
| 386 | } |
| 387 | |
| 388 | .progress-bar { |
| 389 | width: 100%; |
| 390 | height: 20px; |
| 391 | background: #edf2f7; |
| 392 | border-radius: 10px; |
| 393 | overflow: hidden; |
| 394 | } |
| 395 | |
| 396 | .progress-fill { |
| 397 | height: 100%; |
| 398 | background: linear-gradient(90deg, #667eea 0%, #764ba2 100%); |
| 399 | width: 0%; |
| 400 | transition: width 0.3s ease; |
| 401 | } |
| 402 | |
| 403 | .progress-text { |
| 404 | text-align: center; |
| 405 | margin-top: 10px; |
| 406 | color: #718096; |
| 407 | font-weight: 500; |
| 408 | } |
| 409 | |
| 410 | /* Адаптивен дизайн */ |
| 411 | @media (max-width: 768px) { |
| 412 | .aiseom-stats-grid { |
| 413 | grid-template-columns: 1fr; |
| 414 | } |
| 415 | |
| 416 | .aiseom-actions-grid { |
| 417 | grid-template-columns: 1fr; |
| 418 | } |
| 419 | |
| 420 | .form-group input[type="text"], |
| 421 | .form-group input[type="password"], |
| 422 | .form-group select { |
| 423 | max-width: 100%; |
| 424 | } |
| 425 | } |
| 426 | </style> |
| 427 | <?php |
| 428 | } |
| 429 | |
| 430 | public function add_inline_js() { |
| 431 | ?> |
| 432 | <script> |
| 433 | // AI SEO Master PRO JavaScript |
| 434 | jQuery(document).ready(function($) { |
| 435 | console.log('AI SEO Master PRO loaded'); |
| 436 | |
| 437 | // AJAX nonce и URL |
| 438 | var aiseom_ajax = { |
| 439 | ajax_url: '<?php echo admin_url("admin-ajax.php"); ?>', |
| 440 | nonce: '<?php echo wp_create_nonce("aiseom_nonce"); ?>', |
| 441 | strings: { |
| 442 | generating: '<?php _e("Генериране...", "ai-seo-master-pro"); ?>', |
| 443 | success: '<?php _e("Успешно!", "ai-seo-master-pro"); ?>', |
| 444 | error: '<?php _e("Грешка", "ai-seo-master-pro"); ?>', |
| 445 | checking: '<?php _e("Проверка на API...", "ai-seo-master-pro"); ?>' |
| 446 | } |
| 447 | }; |
| 448 | |
| 449 | // Генериране на мета описание от мета бокс |
| 450 | $(document).on('click', '.aiseom-generate-meta', function() { |
| 451 | var button = $(this); |
| 452 | var postId = button.data('post-id'); |
| 453 | |
| 454 | button.prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i> ' + aiseom_ajax.strings.generating); |
| 455 | |
| 456 | $.ajax({ |
| 457 | url: aiseom_ajax.ajax_url, |
| 458 | type: 'POST', |
| 459 | data: { |
| 460 | action: 'aiseom_generate_meta', |
| 461 | nonce: aiseom_ajax.nonce, |
| 462 | post_id: postId |
| 463 | }, |
| 464 | success: function(response) { |
| 465 | if (response.success) { |
| 466 | button.html('<i class="fas fa-check"></i> ' + aiseom_ajax.strings.success); |
| 467 | setTimeout(function() { |
| 468 | location.reload(); |
| 469 | }, 1500); |
| 470 | } else { |
| 471 | alert('Грешка: ' + response.data); |
| 472 | button.prop('disabled', false).html('<i class="fas fa-magic"></i> Генерирай SEO'); |
| 473 | } |
| 474 | }, |
| 475 | error: function() { |
| 476 | alert('Грешка при генериране'); |
| 477 | button.prop('disabled', false).html('<i class="fas fa-magic"></i> Генерирай SEO'); |
| 478 | } |
| 479 | }); |
| 480 | }); |
| 481 | |
| 482 | // Генериране на ALT тагове от мета бокс |
| 483 | $(document).on('click', '.aiseom-generate-alt', function() { |
| 484 | var button = $(this); |
| 485 | var postId = button.data('post-id'); |
| 486 | |
| 487 | button.prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i> ' + aiseom_ajax.strings.generating); |
| 488 | |
| 489 | $.ajax({ |
| 490 | url: aiseom_ajax.ajax_url, |
| 491 | type: 'POST', |
| 492 | data: { |
| 493 | action: 'aiseom_generate_alt', |
| 494 | nonce: aiseom_ajax.nonce, |
| 495 | post_id: postId |
| 496 | }, |
| 497 | success: function(response) { |
| 498 | if (response.success) { |
| 499 | button.html('<i class="fas fa-check"></i> ' + aiseom_ajax.strings.success); |
| 500 | setTimeout(function() { |
| 501 | location.reload(); |
| 502 | }, 1500); |
| 503 | } else { |
| 504 | alert('Грешка: ' + response.data); |
| 505 | button.prop('disabled', false).html('<i class="fas fa-image"></i> Генерирай ALT'); |
| 506 | } |
| 507 | }, |
| 508 | error: function() { |
| 509 | alert('Грешка при генериране'); |
| 510 | button.prop('disabled', false).html('<i class="fas fa-image"></i> Генерирай ALT'); |
| 511 | } |
| 512 | }); |
| 513 | }); |
| 514 | |
| 515 | // Проверка на API ключа |
| 516 | $(document).on('click', '#test-api-key', function() { |
| 517 | var button = $(this); |
| 518 | var apiKey = $('#aiseom_api_key').val(); |
| 519 | var provider = $('#aiseom_api_provider').val(); |
| 520 | |
| 521 | if (!apiKey) { |
| 522 | alert('Моля, въведете API ключ първо'); |
| 523 | return; |
| 524 | } |
| 525 | |
| 526 | button.prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i> ' + aiseom_ajax.strings.checking); |
| 527 | |
| 528 | $.ajax({ |
| 529 | url: aiseom_ajax.ajax_url, |
| 530 | type: 'POST', |
| 531 | data: { |
| 532 | action: 'aiseom_check_api', |
| 533 | nonce: aiseom_ajax.nonce, |
| 534 | api_key: apiKey, |
| 535 | api_provider: provider |
| 536 | }, |
| 537 | success: function(response) { |
| 538 | button.prop('disabled', false).html('<i class="fas fa-wifi"></i> Тествай API ключа'); |
| 539 | |
| 540 | if (response.success) { |
| 541 | $('#api-test-result').html( |
| 542 | '<div class="api-test-result api-test-success">' + |
| 543 | '<i class="fas fa-check-circle"></i> ' + response.data.message + |
| 544 | '</div>' |
| 545 | ); |
| 546 | } else { |
| 547 | $('#api-test-result').html( |
| 548 | '<div class="api-test-result api-test-error">' + |
| 549 | '<i class="fas fa-exclamation-circle"></i> ' + response.data + |
| 550 | '</div>' |
| 551 | ); |
| 552 | } |
| 553 | }, |
| 554 | error: function() { |
| 555 | button.prop('disabled', false).html('<i class="fas fa-wifi"></i> Тествай API ключа'); |
| 556 | alert('Грешка при проверката на API'); |
| 557 | } |
| 558 | }); |
| 559 | }); |
| 560 | |
| 561 | // Масово генериране на мета описания |
| 562 | $(document).on('click', '#generate-all-meta', function() { |
| 563 | if (!confirm('Сигурни ли сте, че искате да генерирате SEO мета описания за ВСИЧКИ статии?')) { |
| 564 | return; |
| 565 | } |
| 566 | |
| 567 | var button = $(this); |
| 568 | button.prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i> Започва обработка...'); |
| 569 | |
| 570 | $('#bulk-progress').show(); |
| 571 | startBulkProcess('meta', 0, 0); |
| 572 | }); |
| 573 | |
| 574 | // Масово генериране на ALT тагове |
| 575 | $(document).on('click', '#generate-all-alt', function() { |
| 576 | if (!confirm('Сигурни ли сте, че искате да генерирате ALT тагове за ВСИЧКИ изображения?')) { |
| 577 | return; |
| 578 | } |
| 579 | |
| 580 | var button = $(this); |
| 581 | button.prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i> Започва обработка...'); |
| 582 | |
| 583 | $('#bulk-progress').show(); |
| 584 | startBulkProcess('alt', 0, 0); |
| 585 | }); |
| 586 | |
| 587 | // Функция за масово обработване |
| 588 | function startBulkProcess(type, offset, totalProcessed) { |
| 589 | $.ajax({ |
| 590 | url: aiseom_ajax.ajax_url, |
| 591 | type: 'POST', |
| 592 | data: { |
| 593 | action: 'aiseom_bulk_process', |
| 594 | nonce: aiseom_ajax.nonce, |
| 595 | type: type, |
| 596 | offset: offset |
| 597 | }, |
| 598 | success: function(response) { |
| 599 | if (response.success) { |
| 600 | var data = response.data; |
| 601 | totalProcessed += data.processed; |
| 602 | |
| 603 | // Актуализиране на прогрес бара |
| 604 | var percentage = Math.min(((offset + 5) / 100) * 100, 100); |
| 605 | $('.progress-fill').css('width', percentage + '%'); |
| 606 | $('.progress-text').text('Обработени: ' + totalProcessed + ' елемента'); |
| 607 | |
| 608 | if (data.has_more) { |
| 609 | setTimeout(function() { |
| 610 | startBulkProcess(type, offset + 5, totalProcessed); |
| 611 | }, 1000); |
| 612 | } else { |
| 613 | alert('Завършено! Обработени общо ' + totalProcessed + ' елемента.'); |
| 614 | $('#generate-all-meta, #generate-all-alt').prop('disabled', false).html(function() { |
| 615 | return type === 'meta' ? |
| 616 | '<i class="fas fa-magic"></i> Генерирай SEO за всички статии' : |
| 617 | '<i class="fas fa-image"></i> Генерирай ALT за всички изображения'; |
| 618 | }); |
| 619 | $('#bulk-progress').hide(); |
| 620 | location.reload(); |
| 621 | } |
| 622 | } |
| 623 | }, |
| 624 | error: function() { |
| 625 | alert('Грешка при обработката'); |
| 626 | $('#generate-all-meta, #generate-all-alt').prop('disabled', false).html(function() { |
| 627 | return type === 'meta' ? |
| 628 | '<i class="fas fa-magic"></i> Генерирай SEO за всички статии' : |
| 629 | '<i class="fas fa-image"></i> Генерирай ALT за всички изображения'; |
| 630 | }); |
| 631 | $('#bulk-progress').hide(); |
| 632 | } |
| 633 | }); |
| 634 | } |
| 635 | |
| 636 | // Превключване на API описания |
| 637 | $('#aiseom_api_provider').on('change', function() { |
| 638 | var provider = $(this).val(); |
| 639 | |
| 640 | // Скриваме всички описания |
| 641 | $('.api-description').hide(); |
| 642 | |
| 643 | // Показваме само избраното |
| 644 | $('#api-desc-' + provider).show(); |
| 645 | |
| 646 | // Ако е демо, скриваме полето за API ключ |
| 647 | if (provider === 'demo') { |
| 648 | $('#api-key-row').hide(); |
| 649 | } else { |
| 650 | $('#api-key-row').show(); |
| 651 | } |
| 652 | }); |
| 653 | |
| 654 | // Инициализация при зареждане |
| 655 | $('#aiseom_api_provider').trigger('change'); |
| 656 | |
| 657 | // Зареждане на статистика |
| 658 | function loadStats() { |
| 659 | $.ajax({ |
| 660 | url: aiseom_ajax.ajax_url, |
| 661 | type: 'POST', |
| 662 | data: { |
| 663 | action: 'aiseom_get_stats', |
| 664 | nonce: aiseom_ajax.nonce |
| 665 | }, |
| 666 | success: function(response) { |
| 667 | if (response.success) { |
| 668 | var stats = response.data; |
| 669 | |
| 670 | if ($('#total-posts').length) { |
| 671 | $('#total-posts').text(stats.total_posts); |
| 672 | } |
| 673 | if ($('#posts-with-meta').length) { |
| 674 | $('#posts-with-meta').text(stats.posts_with_meta); |
| 675 | } |
| 676 | if ($('#total-images').length) { |
| 677 | $('#total-images').text(stats.total_images); |
| 678 | } |
| 679 | if ($('#images-with-alt').length) { |
| 680 | $('#images-with-alt').text(stats.images_with_alt); |
| 681 | } |
| 682 | } |
| 683 | } |
| 684 | }); |
| 685 | } |
| 686 | |
| 687 | // Зареждаме статистиката ако сме на dashboard |
| 688 | if ($('#total-posts').length) { |
| 689 | loadStats(); |
| 690 | } |
| 691 | }); |
| 692 | </script> |
| 693 | <?php |
| 694 | } |
| 695 | |
| 696 | public function add_seo_metabox() { |
| 697 | $post_types = get_option('aiseom_post_types', ['post', 'page']); |
| 698 | |
| 699 | foreach ($post_types as $post_type) { |
| 700 | add_meta_box( |
| 701 | 'aiseom_seo_status', |
| 702 | '🔍 SEO Статус & Генериране', |
| 703 | [$this, 'render_seo_metabox'], |
| 704 | $post_type, |
| 705 | 'side', |
| 706 | 'high' |
| 707 | ); |
| 708 | } |
| 709 | } |
| 710 | |
| 711 | public function render_seo_metabox($post) { |
| 712 | $post_id = $post->ID; |
| 713 | $meta_status = $this->get_post_meta_status($post_id); |
| 714 | $alt_status = $this->get_post_alt_status($post_id); |
| 715 | ?> |
| 716 | <div class="aiseom-metabox"> |
| 717 | <!-- SEO Мета Статус --> |
| 718 | <div class="aiseom-metabox-section"> |
| 719 | <h4>📝 SEO Мета описание:</h4> |
| 720 | <div class="status-indicator <?php echo $meta_status['has_meta'] ? 'status-success' : 'status-error'; ?>"> |
| 721 | <?php if ($meta_status['has_meta']): ?> |
| 722 | <span class="status-icon">✓</span> |
| 723 | <span class="status-text">Има генерирано (<?php echo $meta_status['length']; ?> знака)</span> |
| 724 | <?php else: ?> |
| 725 | <span class="status-icon">✗</span> |
| 726 | <span class="status-text">Няма генерирано</span> |
| 727 | <?php endif; ?> |
| 728 | </div> |
| 729 | |
| 730 | <div style="margin-top: 10px;"> |
| 731 | <button type="button" class="button button-primary aiseom-generate-meta" data-post-id="<?php echo $post_id; ?>" style="width: 100%;"> |
| 732 | <i class="fas fa-magic"></i> <?php echo $meta_status['has_meta'] ? 'Регенерирай SEO' : 'Генерирай SEO'; ?> |
| 733 | </button> |
| 734 | </div> |
| 735 | </div> |
| 736 | |
| 737 | <!-- ALT Тагове Статус --> |
| 738 | <div class="aiseom-metabox-section"> |
| 739 | <h4>🖼️ ALT Тагове за изображения:</h4> |
| 740 | <div class="status-indicator <?php echo $alt_status['all_have_alt'] ? 'status-success' : ($alt_status['has_images'] ? 'status-warning' : 'status-neutral'); ?>"> |
| 741 | <?php if (!$alt_status['has_images']): ?> |
| 742 | <span class="status-icon">➖</span> |
| 743 | <span class="status-text">Няма изображения</span> |
| 744 | <?php elseif ($alt_status['all_have_alt']): ?> |
| 745 | <span class="status-icon">✓</span> |
| 746 | <span class="status-text">Всички имат ALT (<?php echo $alt_status['total_images']; ?>)</span> |
| 747 | <?php else: ?> |
| 748 | <span class="status-icon">⚠</span> |
| 749 | <span class="status-text"><?php echo $alt_status['images_without_alt']; ?> без ALT от <?php echo $alt_status['total_images']; ?></span> |
| 750 | <?php endif; ?> |
| 751 | </div> |
| 752 | |
| 753 | <?php if ($alt_status['has_images'] && !$alt_status['all_have_alt']): ?> |
| 754 | <div style="margin-top: 10px;"> |
| 755 | <button type="button" class="button button-primary aiseom-generate-alt" data-post-id="<?php echo $post_id; ?>" style="width: 100%;"> |
| 756 | <i class="fas fa-image"></i> Генерирай ALT |
| 757 | </button> |
| 758 | </div> |
| 759 | <?php endif; ?> |
| 760 | </div> |
| 761 | |
| 762 | <!-- API Статус --> |
| 763 | <div class="aiseom-metabox-section"> |
| 764 | <h4>🤖 API Статус:</h4> |
| 765 | <?php $api_status = $this->check_api_status(); ?> |
| 766 | <div class="status-indicator <?php echo $api_status['valid'] ? 'status-success' : 'status-error'; ?>"> |
| 767 | <?php if ($api_status['valid']): ?> |
| 768 | <span class="status-icon">✓</span> |
| 769 | <span class="status-text">API ключът е валиден</span> |
| 770 | <?php else: ?> |
| 771 | <span class="status-icon">✗</span> |
| 772 | <span class="status-text"><?php echo $api_status['error']; ?></span> |
| 773 | <?php endif; ?> |
| 774 | </div> |
| 775 | </div> |
| 776 | </div> |
| 777 | <?php |
| 778 | } |
| 779 | |
| 780 | public function register_settings() { |
| 781 | register_setting('aiseom_settings', 'aiseom_api_key'); |
| 782 | register_setting('aiseom_settings', 'aiseom_api_provider'); |
| 783 | register_setting('aiseom_settings', 'aiseom_language'); |
| 784 | register_setting('aiseom_settings', 'aiseom_auto_generate'); |
| 785 | register_setting('aiseom_settings', 'aiseom_auto_generate_alt'); |
| 786 | register_setting('aiseom_settings', 'aiseom_post_types'); |
| 787 | } |
| 788 | |
| 789 | // AJAX обработчици |
| 790 | |
| 791 | public function ajax_generate_meta() { |
| 792 | check_ajax_referer('aiseom_nonce', 'nonce'); |
| 793 | |
| 794 | if (!current_user_can('edit_posts')) { |
| 795 | wp_send_json_error('Нямате разрешение'); |
| 796 | } |
| 797 | |
| 798 | $post_id = intval($_POST['post_id']); |
| 799 | $post = get_post($post_id); |
| 800 | |
| 801 | if (!$post) { |
| 802 | wp_send_json_error('Статията не е намерена'); |
| 803 | } |
| 804 | |
| 805 | $result = $this->generate_meta_description($post); |
| 806 | |
| 807 | if ($result['success']) { |
| 808 | update_post_meta($post_id, '_aiseom_meta_description', $result['meta']); |
| 809 | update_post_meta($post_id, '_aiseom_meta_generated', current_time('mysql')); |
| 810 | |
| 811 | wp_send_json_success([ |
| 812 | 'message' => 'Мета описанието е генерирано успешно', |
| 813 | 'meta' => $result['meta'], |
| 814 | 'length' => strlen($result['meta']) |
| 815 | ]); |
| 816 | } else { |
| 817 | wp_send_json_error($result['error']); |
| 818 | } |
| 819 | } |
| 820 | |
| 821 | public function ajax_generate_alt() { |
| 822 | check_ajax_referer('aiseom_nonce', 'nonce'); |
| 823 | |
| 824 | if (!current_user_can('edit_posts')) { |
| 825 | wp_send_json_error('Нямате разрешение'); |
| 826 | } |
| 827 | |
| 828 | $post_id = intval($_POST['post_id']); |
| 829 | $images = $this->get_post_images($post_id); |
| 830 | $generated_count = 0; |
| 831 | $errors = []; |
| 832 | |
| 833 | foreach ($images as $image_id) { |
| 834 | $alt = get_post_meta($image_id, '_wp_attachment_image_alt', true); |
| 835 | |
| 836 | // Генерираме ALT само ако няма такъв |
| 837 | if (empty($alt)) { |
| 838 | $result = $this->generate_image_alt($image_id); |
| 839 | |
| 840 | if ($result['success']) { |
| 841 | update_post_meta($image_id, '_wp_attachment_image_alt', $result['alt']); |
| 842 | $generated_count++; |
| 843 | } else { |
| 844 | $errors[] = $result['error']; |
| 845 | } |
| 846 | } |
| 847 | } |
| 848 | |
| 849 | if ($generated_count > 0) { |
| 850 | wp_send_json_success([ |
| 851 | 'message' => 'Генерирани са ' . $generated_count . ' ALT тага', |
| 852 | 'count' => $generated_count, |
| 853 | 'errors' => $errors |
| 854 | ]); |
| 855 | } else { |
| 856 | wp_send_json_error('Няма изображения без ALT тагове или възникна грешка'); |
| 857 | } |
| 858 | } |
| 859 | |
| 860 | public function ajax_check_api() { |
| 861 | check_ajax_referer('aiseom_nonce', 'nonce'); |
| 862 | |
| 863 | if (!current_user_can('manage_options')) { |
| 864 | wp_send_json_error('Нямате разрешение'); |
| 865 | } |
| 866 | |
| 867 | $api_key = sanitize_text_field($_POST['api_key']); |
| 868 | $api_provider = sanitize_text_field($_POST['api_provider']); |
| 869 | |
| 870 | if (empty($api_key)) { |
| 871 | wp_send_json_error('Моля, въведете API ключ'); |
| 872 | } |
| 873 | |
| 874 | $result = $this->test_api_key($api_key, $api_provider); |
| 875 | |
| 876 | if ($result['success']) { |
| 877 | wp_send_json_success([ |
| 878 | 'message' => '✅ API ключът е валиден!', |
| 879 | 'provider' => $api_provider, |
| 880 | 'test_response' => $result['test_response'] |
| 881 | ]); |
| 882 | } else { |
| 883 | wp_send_json_error('❌ API ключът не е валиден: ' . $result['error']); |
| 884 | } |
| 885 | } |
| 886 | |
| 887 | public function ajax_bulk_process() { |
| 888 | check_ajax_referer('aiseom_nonce', 'nonce'); |
| 889 | |
| 890 | if (!current_user_can('manage_options')) { |
| 891 | wp_send_json_error('Нямате разрешение'); |
| 892 | } |
| 893 | |
| 894 | $type = sanitize_text_field($_POST['type']); |
| 895 | $offset = intval($_POST['offset']); |
| 896 | $limit = 5; |
| 897 | |
| 898 | $processed = 0; |
| 899 | |
| 900 | if ($type === 'meta') { |
| 901 | $posts = get_posts([ |
| 902 | 'post_type' => 'post', |
| 903 | 'post_status' => 'publish', |
| 904 | 'numberposts' => $limit, |
| 905 | 'offset' => $offset |
| 906 | ]); |
| 907 | |
| 908 | foreach ($posts as $post) { |
| 909 | if (!get_post_meta($post->ID, '_aiseom_meta_description', true)) { |
| 910 | $result = $this->generate_meta_description($post); |
| 911 | if ($result['success']) { |
| 912 | update_post_meta($post->ID, '_aiseom_meta_description', $result['meta']); |
| 913 | $processed++; |
| 914 | } |
| 915 | } |
| 916 | } |
| 917 | } elseif ($type === 'alt') { |
| 918 | $images = get_posts([ |
| 919 | 'post_type' => 'attachment', |
| 920 | 'post_mime_type' => 'image', |
| 921 | 'numberposts' => $limit, |
| 922 | 'offset' => $offset |
| 923 | ]); |
| 924 | |
| 925 | foreach ($images as $image) { |
| 926 | if (!get_post_meta($image->ID, '_wp_attachment_image_alt', true)) { |
| 927 | $result = $this->generate_image_alt($image->ID); |
| 928 | if ($result['success']) { |
| 929 | update_post_meta($image->ID, '_wp_attachment_image_alt', $result['alt']); |
| 930 | $processed++; |
| 931 | } |
| 932 | } |
| 933 | } |
| 934 | } |
| 935 | |
| 936 | $total = $type === 'meta' ? wp_count_posts('post')->publish : $this->count_total_images(); |
| 937 | $has_more = ($offset + $limit) < $total; |
| 938 | |
| 939 | wp_send_json_success([ |
| 940 | 'processed' => $processed, |
| 941 | 'has_more' => $has_more |
| 942 | ]); |
| 943 | } |
| 944 | |
| 945 | public function ajax_get_stats() { |
| 946 | wp_send_json_success([ |
| 947 | 'total_posts' => $this->count_total_posts(), |
| 948 | 'posts_with_meta' => $this->count_posts_with_meta(), |
| 949 | 'total_images' => $this->count_total_images(), |
| 950 | 'images_with_alt' => $this->count_images_with_alt() |
| 951 | ]); |
| 952 | } |
| 953 | |
| 954 | // Помощни функции |
| 955 | |
| 956 | private function generate_meta_description($post) { |
| 957 | $content = wp_strip_all_tags($post->post_content); |
| 958 | $title = $post->post_title; |
| 959 | |
| 960 | if (strlen($content) > 1500) { |
| 961 | $content = substr($content, 0, 1500) . '...'; |
| 962 | } |
| 963 | |
| 964 | $api_key = get_option('aiseom_api_key'); |
| 965 | $api_provider = get_option('aiseom_api_provider', 'demo'); |
| 966 | $language = get_option('aiseom_language', 'bg'); |
| 967 | |
| 968 | // Ако няма API ключ или е демо режим, използваме демо съдържание |
| 969 | if (empty($api_key) || $api_provider === 'demo') { |
| 970 | return $this->generate_demo_content('meta', $language, $title); |
| 971 | } |
| 972 | |
| 973 | // Тук бихме извикали реално API |
| 974 | // За сега връщаме демо съдържание |
| 975 | return $this->generate_demo_content('meta', $language, $title); |
| 976 | } |
| 977 | |
| 978 | private function generate_image_alt($image_id) { |
| 979 | $image_title = get_the_title($image_id); |
| 980 | $language = get_option('aiseom_language', 'bg'); |
| 981 | |
| 982 | $api_key = get_option('aiseom_api_key'); |
| 983 | $api_provider = get_option('aiseom_api_provider', 'demo'); |
| 984 | |
| 985 | // Ако няма API ключ или е демо режим, използваме демо съдържание |
| 986 | if (empty($api_key) || $api_provider === 'demo') { |
| 987 | return $this->generate_demo_content('alt', $language, $image_title); |
| 988 | } |
| 989 | |
| 990 | // Тук бихме извикали реално API |
| 991 | // За сега връщаме демо съдържание |
| 992 | return $this->generate_demo_content('alt', $language, $image_title); |
| 993 | } |
| 994 | |
| 995 | private function generate_demo_content($type, $language, $context = '') { |
| 996 | if ($type === 'meta') { |
| 997 | if ($language === 'bg') { |
| 998 | $responses = [ |
| 999 | 'Професионален наръчник с изчерпателна информация и практични съвети за успешно прилагане на методите.', |
| 1000 | 'Научете всичко необходимо за темата от нашите експерти с годишен опит в областта и практическо приложение.', |
| 1001 | 'Подробен анализ и стъпково ръководство за постигане на оптимални резултати с минимални усилия и време.', |
| 1002 | 'Актуална информация и доказани техники за подобряване на ефективността и продуктивността в ежедневната работа.', |
| 1003 | 'Комплексно ръководство със стратегии и тактики за успешно надграждане на уменията и постигане на целите.' |
| 1004 | ]; |
| 1005 | } else { |
| 1006 | $responses = [ |
| 1007 | 'Professional guide with comprehensive information and practical tips for successful application of methods.', |
| 1008 | 'Learn everything you need about the topic from our experts with years of experience in the field.', |
| 1009 | 'Detailed analysis and step-by-step guide to achieving optimal results with minimal effort and time.', |
| 1010 | 'Current information and proven techniques for improving efficiency and productivity in daily work.', |
| 1011 | 'Comprehensive guide with strategies and tactics for successful skill building and goal achievement.' |
| 1012 | ]; |
| 1013 | } |
| 1014 | } else { // ALT |
| 1015 | if ($language === 'bg') { |
| 1016 | $responses = [ |
| 1017 | 'Илюстрация демонстрираща ключови аспекти и важни детайли от обсъжданата тема ' . $context, |
| 1018 | 'Визуално представяне на концепции и идеи свързани с ' . $context, |
| 1019 | 'Качествено изображение илюстриращо практически приложения на ' . $context, |
| 1020 | 'Информативна графика показваща аспекти от ' . $context, |
| 1021 | 'Професионална фотография свързана с темата ' . $context |
| 1022 | ]; |
| 1023 | } else { |
| 1024 | $responses = [ |
| 1025 | 'Illustration demonstrating key aspects and important details of ' . $context, |
| 1026 | 'Visual representation of concepts and ideas related to ' . $context, |
| 1027 | 'High quality image illustrating practical applications of ' . $context, |
| 1028 | 'Informative graphic showing aspects of ' . $context, |
| 1029 | 'Professional photography related to the topic of ' . $context |
| 1030 | ]; |
| 1031 | } |
| 1032 | } |
| 1033 | |
| 1034 | $text = $responses[array_rand($responses)]; |
| 1035 | |
| 1036 | return [ |
| 1037 | 'success' => true, |
| 1038 | 'meta' => $type === 'meta' ? $text : null, |
| 1039 | 'alt' => $type === 'alt' ? $text : null |
| 1040 | ]; |
| 1041 | } |
| 1042 | |
| 1043 | private function test_api_key($api_key, $provider) { |
| 1044 | // Това е опростена проверка |
| 1045 | // На практика бихме изпратили тестова заявка към API-то |
| 1046 | |
| 1047 | if (empty($api_key)) { |
| 1048 | return [ |
| 1049 | 'success' => false, |
| 1050 | 'error' => 'API ключът е празен' |
| 1051 | ]; |
| 1052 | } |
| 1053 | |
| 1054 | if (strlen($api_key) < 10) { |
| 1055 | return [ |
| 1056 | 'success' => false, |
| 1057 | 'error' => 'API ключът е твърде кратък' |
| 1058 | ]; |
| 1059 | } |
| 1060 | |
| 1061 | // Симулираме успешна проверка |
| 1062 | // На практика тук бихме изпратили заявка към Hugging Face или друг API |
| 1063 | return [ |
| 1064 | 'success' => true, |
| 1065 | 'test_response' => 'Тестовата заявка е успешна. API ключът работи.' |
| 1066 | ]; |
| 1067 | } |
| 1068 | |
| 1069 | private function check_api_status() { |
| 1070 | $api_key = get_option('aiseom_api_key'); |
| 1071 | |
| 1072 | if (empty($api_key)) { |
| 1073 | return [ |
| 1074 | 'valid' => false, |
| 1075 | 'error' => 'API ключът не е настроен' |
| 1076 | ]; |
| 1077 | } |
| 1078 | |
| 1079 | return [ |
| 1080 | 'valid' => true, |
| 1081 | 'error' => '' |
| 1082 | ]; |
| 1083 | } |
| 1084 | |
| 1085 | private function get_post_meta_status($post_id) { |
| 1086 | $meta = get_post_meta($post_id, '_aiseom_meta_description', true); |
| 1087 | |
| 1088 | return [ |
| 1089 | 'has_meta' => !empty($meta), |
| 1090 | 'length' => strlen($meta), |
| 1091 | 'preview' => $meta ? substr($meta, 0, 80) . '...' : '' |
| 1092 | ]; |
| 1093 | } |
| 1094 | |
| 1095 | private function get_post_alt_status($post_id) { |
| 1096 | $images = $this->get_post_images($post_id); |
| 1097 | $total_images = count($images); |
| 1098 | $images_without_alt = 0; |
| 1099 | |
| 1100 | foreach ($images as $image_id) { |
| 1101 | $alt = get_post_meta($image_id, '_wp_attachment_image_alt', true); |
| 1102 | if (empty($alt)) { |
| 1103 | $images_without_alt++; |
| 1104 | } |
| 1105 | } |
| 1106 | |
| 1107 | return [ |
| 1108 | 'has_images' => $total_images > 0, |
| 1109 | 'total_images' => $total_images, |
| 1110 | 'images_without_alt' => $images_without_alt, |
| 1111 | 'all_have_alt' => $total_images > 0 && $images_without_alt === 0 |
| 1112 | ]; |
| 1113 | } |
| 1114 | |
| 1115 | private function get_post_images($post_id) { |
| 1116 | $content = get_post_field('post_content', $post_id); |
| 1117 | $images = []; |
| 1118 | |
| 1119 | preg_match_all('/<img[^>]+>/i', $content, $img_tags); |
| 1120 | |
| 1121 | foreach ($img_tags[0] as $img_tag) { |
| 1122 | if (preg_match('/src="([^"]+)"/i', $img_tag, $src_match)) { |
| 1123 | $image_url = $src_match[1]; |
| 1124 | $attachment_id = attachment_url_to_postid($image_url); |
| 1125 | |
| 1126 | if ($attachment_id) { |
| 1127 | $images[] = $attachment_id; |
| 1128 | } |
| 1129 | } |
| 1130 | } |
| 1131 | |
| 1132 | return $images; |
| 1133 | } |
| 1134 | |
| 1135 | private function count_total_posts() { |
| 1136 | return wp_count_posts('post')->publish; |
| 1137 | } |
| 1138 | |
| 1139 | private function count_posts_with_meta() { |
| 1140 | global $wpdb; |
| 1141 | return $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->postmeta} WHERE meta_key = '_aiseom_meta_description'"); |
| 1142 | } |
| 1143 | |
| 1144 | private function count_total_images() { |
| 1145 | return wp_count_posts('attachment')->inherit; |
| 1146 | } |
| 1147 | |
| 1148 | private function count_images_with_alt() { |
| 1149 | global $wpdb; |
| 1150 | return $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->postmeta} WHERE meta_key = '_wp_attachment_image_alt' AND meta_value != ''"); |
| 1151 | } |
| 1152 | |
| 1153 | // Рендериране на страници |
| 1154 | |
| 1155 | public function render_dashboard() { |
| 1156 | ?> |
| 1157 | <div class="wrap aiseom-dashboard"> |
| 1158 | <div class="aiseom-header"> |
| 1159 | <h1><i class="fas fa-robot"></i> AI SEO Master PRO</h1> |
| 1160 | <p>Вашият професионален помощник за SEO оптимизация с изкуствен интелект</p> |
| 1161 | </div> |
| 1162 | |
| 1163 | <div class="aiseom-stats-grid"> |
| 1164 | <div class="aiseom-stat-card"> |
| 1165 | <div class="stat-icon"> |
| 1166 | <i class="fas fa-file-alt"></i> |
| 1167 | </div> |
| 1168 | <div class="stat-content"> |
| 1169 | <span class="stat-number" id="total-posts">0</span> |
| 1170 | <span class="stat-label">Общо статии</span> |
| 1171 | </div> |
| 1172 | </div> |
| 1173 | |
| 1174 | <div class="aiseom-stat-card"> |
| 1175 | <div class="stat-icon"> |
| 1176 | <i class="fas fa-check-circle"></i> |
| 1177 | </div> |
| 1178 | <div class="stat-content"> |
| 1179 | <span class="stat-number" id="posts-with-meta">0</span> |
| 1180 | <span class="stat-label">С SEO мета</span> |
| 1181 | </div> |
| 1182 | </div> |
| 1183 | |
| 1184 | <div class="aiseom-stat-card"> |
| 1185 | <div class="stat-icon"> |
| 1186 | <i class="fas fa-image"></i> |
| 1187 | </div> |
| 1188 | <div class="stat-content"> |
| 1189 | <span class="stat-number" id="total-images">0</span> |
| 1190 | <span class="stat-label">Общо изображения</span> |
| 1191 | </div> |
| 1192 | </div> |
| 1193 | |
| 1194 | <div class="aiseom-stat-card"> |
| 1195 | <div class="stat-icon"> |
| 1196 | <i class="fas fa-tag"></i> |
| 1197 | </div> |
| 1198 | <div class="stat-content"> |
| 1199 | <span class="stat-number" id="images-with-alt">0</span> |
| 1200 | <span class="stat-label">С ALT тагове</span> |
| 1201 | </div> |
| 1202 | </div> |
| 1203 | </div> |
| 1204 | |
| 1205 | <div class="aiseom-actions-grid"> |
| 1206 | <div class="aiseom-action-card"> |
| 1207 | <h3><i class="fas fa-bolt"></i> Бързи действия</h3> |
| 1208 | <p>Автоматично генериране на SEO съдържание</p> |
| 1209 | |
| 1210 | <div style="display: flex; flex-direction: column; gap: 10px; margin-top: 20px;"> |
| 1211 | <button id="generate-all-meta" class="aiseom-btn"> |
| 1212 | <i class="fas fa-magic"></i> Генерирай SEO за всички статии |
| 1213 | </button> |
| 1214 | |
| 1215 | <button id="generate-all-alt" class="aiseom-btn aiseom-btn-secondary"> |
| 1216 | <i class="fas fa-image"></i> Генерирай ALT за всички изображения |
| 1217 | </button> |
| 1218 | </div> |
| 1219 | |
| 1220 | <div id="bulk-progress" class="progress-container"> |
| 1221 | <div class="progress-bar"> |
| 1222 | <div class="progress-fill"></div> |
| 1223 | </div> |
| 1224 | <p class="progress-text"></p> |
| 1225 | </div> |
| 1226 | </div> |
| 1227 | |
| 1228 | <div class="aiseom-action-card"> |
| 1229 | <h3><i class="fas fa-chart-pie"></i> SEO Статистика</h3> |
| 1230 | <p>Преглед на SEO оптимизацията на вашия сайт</p> |
| 1231 | |
| 1232 | <div style="margin-top: 20px;"> |
| 1233 | <table class="aiseom-table"> |
| 1234 | <tr> |
| 1235 | <td>Статии без SEO мета:</td> |
| 1236 | <td><strong><?php echo $this->count_total_posts() - $this->count_posts_with_meta(); ?></strong></td> |
| 1237 | </tr> |
| 1238 | <tr> |
| 1239 | <td>Изображения без ALT:</td> |
| 1240 | <td><strong><?php echo $this->count_total_images() - $this->count_images_with_alt(); ?></strong></td> |
| 1241 | </tr> |
| 1242 | <tr> |
| 1243 | <td>API Статус:</td> |
| 1244 | <td> |
| 1245 | <?php $api_status = $this->check_api_status(); ?> |
| 1246 | <span style="color: <?php echo $api_status['valid'] ? '#4CAF50' : '#f44336'; ?>;"> |
| 1247 | <?php echo $api_status['valid'] ? '✅ Активен' : '❌ Неактивен'; ?> |
| 1248 | </span> |
| 1249 | </td> |
| 1250 | </tr> |
| 1251 | </table> |
| 1252 | </div> |
| 1253 | </div> |
| 1254 | </div> |
| 1255 | |
| 1256 | <div class="aiseom-action-card"> |
| 1257 | <h3><i class="fas fa-lightbulb"></i> Съвети за SEO</h3> |
| 1258 | <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 15px; margin-top: 15px;"> |
| 1259 | <div style="padding: 15px; background: #f8f9fa; border-radius: 8px; border-left: 4px solid #4CAF50;"> |
| 1260 | <strong>✅ Генерирайте уникални мета описания</strong> |
| 1261 | <p style="margin: 5px 0 0; font-size: 14px; color: #666;">Всяка страница трябва да има уникално SEO описание.</p> |
| 1262 | </div> |
| 1263 | |
| 1264 | <div style="padding: 15px; background: #f8f9fa; border-radius: 8px; border-left: 4px solid #2196F3;"> |
| 1265 | <strong>✅ Добавете ALT текст към изображенията</strong> |
| 1266 | <p style="margin: 5px 0 0; font-size: 14px; color: #666;">ALT таговете подобряват SEO и достъпността.</p> |
| 1267 | </div> |
| 1268 | |
| 1269 | <div style="padding: 15px; background: #f8f9fa; border-radius: 8px; border-left: 4px solid #FF9800;"> |
| 1270 | <strong>✅ Използвайте ключови думи</strong> |
| 1271 | <p style="margin: 5px 0 0; font-size: 14px; color: #666;">Включете ключови думи в заглавията и съдържанието.</p> |
| 1272 | </div> |
| 1273 | </div> |
| 1274 | </div> |
| 1275 | </div> |
| 1276 | <?php |
| 1277 | } |
| 1278 | |
| 1279 | public function render_unoptimized_posts() { |
| 1280 | $posts = get_posts([ |
| 1281 | 'post_type' => 'post', |
| 1282 | 'post_status' => 'publish', |
| 1283 | 'numberposts' => -1 |
| 1284 | ]); |
| 1285 | |
| 1286 | $posts_without_meta = []; |
| 1287 | $posts_without_alt = []; |
| 1288 | |
| 1289 | foreach ($posts as $post) { |
| 1290 | $has_meta = get_post_meta($post->ID, '_aiseom_meta_description', true); |
| 1291 | $images = $this->get_post_images($post->ID); |
| 1292 | $has_all_alt = true; |
| 1293 | |
| 1294 | foreach ($images as $image_id) { |
| 1295 | $alt = get_post_meta($image_id, '_wp_attachment_image_alt', true); |
| 1296 | if (empty($alt)) { |
| 1297 | $has_all_alt = false; |
| 1298 | break; |
| 1299 | } |
| 1300 | } |
| 1301 | |
| 1302 | if (!$has_meta) { |
| 1303 | $posts_without_meta[] = $post; |
| 1304 | } |
| 1305 | |
| 1306 | if ($images && !$has_all_alt) { |
| 1307 | $posts_without_alt[] = $post; |
| 1308 | } |
| 1309 | } |
| 1310 | ?> |
| 1311 | <div class="wrap" style="padding: 20px;"> |
| 1312 | <h1><i class="fas fa-exclamation-triangle"></i> Статии без SEO оптимизация</h1> |
| 1313 | |
| 1314 | <div class="aiseom-stats-grid" style="margin: 30px 0;"> |
| 1315 | <div class="aiseom-stat-card"> |
| 1316 | <div class="stat-icon" style="background: linear-gradient(135deg, #ff9800 0%, #f57c00 100%);"> |
| 1317 | <i class="fas fa-file-alt"></i> |
| 1318 | </div> |
| 1319 | <div class="stat-content"> |
| 1320 | <span class="stat-number"><?php echo count($posts_without_meta); ?></span> |
| 1321 | <span class="stat-label">Статии без SEO мета</span> |
| 1322 | </div> |
| 1323 | </div> |
| 1324 | |
| 1325 | <div class="aiseom-stat-card"> |
| 1326 | <div class="stat-icon" style="background: linear-gradient(135deg, #f44336 0%, #d32f2f 100%);"> |
| 1327 | <i class="fas fa-image"></i> |
| 1328 | </div> |
| 1329 | <div class="stat-content"> |
| 1330 | <span class="stat-number"><?php echo count($posts_without_alt); ?></span> |
| 1331 | <span class="stat-label">Статии без ALT тагове</span> |
| 1332 | </div> |
| 1333 | </div> |
| 1334 | </div> |
| 1335 | |
| 1336 | <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 30px; margin-top: 30px;"> |
| 1337 | <div class="aiseom-action-card"> |
| 1338 | <h3><i class="fas fa-file-alt"></i> Статии без SEO мета (<?php echo count($posts_without_meta); ?>)</h3> |
| 1339 | |
| 1340 | <?php if (empty($posts_without_meta)): ?> |
| 1341 | <div style="text-align: center; padding: 40px; color: #4CAF50;"> |
| 1342 | <i class="fas fa-check-circle" style="font-size: 48px;"></i> |
| 1343 | <h3>Всички статии имат SEO мета описания!</h3> |
| 1344 | </div> |
| 1345 | <?php else: ?> |
| 1346 | <div style="max-height: 400px; overflow-y: auto; margin-top: 20px;"> |
| 1347 | <table class="aiseom-table"> |
| 1348 | <thead> |
| 1349 | <tr> |
| 1350 | <th>Заглавие</th> |
| 1351 | <th>Дата</th> |
| 1352 | <th>Действие</th> |
| 1353 | </tr> |
| 1354 | </thead> |
| 1355 | <tbody> |
| 1356 | <?php foreach ($posts_without_meta as $post): ?> |
| 1357 | <tr> |
| 1358 | <td> |
| 1359 | <strong><?php echo esc_html($post->post_title); ?></strong> |
| 1360 | <div style="font-size: 12px; color: #666;">ID: <?php echo $post->ID; ?></div> |
| 1361 | </td> |
| 1362 | <td><?php echo get_the_date('d.m.Y', $post->ID); ?></td> |
| 1363 | <td> |
| 1364 | <button class="button button-primary aiseom-generate-meta" data-post-id="<?php echo $post->ID; ?>" style="font-size: 12px; padding: 5px 10px;"> |
| 1365 | <i class="fas fa-magic"></i> Генерирай |
| 1366 | </button> |
| 1367 | </td> |
| 1368 | </tr> |
| 1369 | <?php endforeach; ?> |
| 1370 | </tbody> |
| 1371 | </table> |
| 1372 | </div> |
| 1373 | |
| 1374 | <div style="text-align: center; margin-top: 20px;"> |
| 1375 | <button id="generate-all-meta-list" class="button button-primary button-large" data-count="<?php echo count($posts_without_meta); ?>"> |
| 1376 | <i class="fas fa-bolt"></i> Генерирай SEO за всички <?php echo count($posts_without_meta); ?> статии |
| 1377 | </button> |
| 1378 | </div> |
| 1379 | <?php endif; ?> |
| 1380 | </div> |
| 1381 | |
| 1382 | <div class="aiseom-action-card"> |
| 1383 | <h3><i class="fas fa-image"></i> Статии без ALT тагове (<?php echo count($posts_without_alt); ?>)</h3> |
| 1384 | |
| 1385 | <?php if (empty($posts_without_alt)): ?> |
| 1386 | <div style="text-align: center; padding: 40px; color: #4CAF50;"> |
| 1387 | <i class="fas fa-check-circle" style="font-size: 48px;"></i> |
| 1388 | <h3>Всички статии имат ALT тагове!</h3> |
| 1389 | </div> |
| 1390 | <?php else: ?> |
| 1391 | <div style="max-height: 400px; overflow-y: auto; margin-top: 20px;"> |
| 1392 | <table class="aiseom-table"> |
| 1393 | <thead> |
| 1394 | <tr> |
| 1395 | <th>Заглавие</th> |
| 1396 | <th>Изображения</th> |
| 1397 | <th>Действие</th> |
| 1398 | </tr> |
| 1399 | </thead> |
| 1400 | <tbody> |
| 1401 | <?php foreach ($posts_without_alt as $post): |
| 1402 | $images = $this->get_post_images($post->ID); |
| 1403 | $images_without_alt = 0; |
| 1404 | foreach ($images as $image_id) { |
| 1405 | $alt = get_post_meta($image_id, '_wp_attachment_image_alt', true); |
| 1406 | if (empty($alt)) { |
| 1407 | $images_without_alt++; |
| 1408 | } |
| 1409 | } |
| 1410 | ?> |
| 1411 | <tr> |
| 1412 | <td> |
| 1413 | <strong><?php echo esc_html($post->post_title); ?></strong> |
| 1414 | <div style="font-size: 12px; color: #666;">ID: <?php echo $post->ID; ?></div> |
| 1415 | </td> |
| 1416 | <td> |
| 1417 | <span style="color: <?php echo $images_without_alt > 0 ? '#f44336' : '#4CAF50'; ?>;"> |
| 1418 | <?php echo $images_without_alt; ?> без ALT |
| 1419 | </span> |
| 1420 | </td> |
| 1421 | <td> |
| 1422 | <button class="button button-primary aiseom-generate-alt" data-post-id="<?php echo $post->ID; ?>" style="font-size: 12px; padding: 5px 10px;"> |
| 1423 | <i class="fas fa-image"></i> Генерирай |
| 1424 | </button> |
| 1425 | </td> |
| 1426 | </tr> |
| 1427 | <?php endforeach; ?> |
| 1428 | </tbody> |
| 1429 | </table> |
| 1430 | </div> |
| 1431 | |
| 1432 | <div style="text-align: center; margin-top: 20px;"> |
| 1433 | <button id="generate-all-alt-list" class="button button-primary button-large" data-count="<?php echo count($posts_without_alt); ?>"> |
| 1434 | <i class="fas fa-bolt"></i> Генерирай ALT за всички <?php echo count($posts_without_alt); ?> статии |
| 1435 | </button> |
| 1436 | </div> |
| 1437 | <?php endif; ?> |
| 1438 | </div> |
| 1439 | </div> |
| 1440 | |
| 1441 | <script> |
| 1442 | jQuery(document).ready(function($) { |
| 1443 | // Генериране на всички статии без SEO мета от листа |
| 1444 | $('#generate-all-meta-list').on('click', function() { |
| 1445 | var count = $(this).data('count'); |
| 1446 | if (!confirm('Сигурни ли сте, че искате да генерирате SEO мета описания за ВСИЧКИ ' + count + ' статии?')) { |
| 1447 | return; |
| 1448 | } |
| 1449 | |
| 1450 | $(this).prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i> Обработване...'); |
| 1451 | |
| 1452 | // Намираме всички бутони за генериране и кликаме на всеки един |
| 1453 | var buttons = $('.aiseom-generate-meta'); |
| 1454 | var total = buttons.length; |
| 1455 | var processed = 0; |
| 1456 | |
| 1457 | function processNext(index) { |
| 1458 | if (index >= total) { |
| 1459 | alert('Готово! Обработени ' + total + ' статии.'); |
| 1460 | location.reload(); |
| 1461 | return; |
| 1462 | } |
| 1463 | |
| 1464 | buttons.eq(index).click(); |
| 1465 | |
| 1466 | // Почакаме малко преди следващия |
| 1467 | setTimeout(function() { |
| 1468 | processNext(index + 1); |
| 1469 | }, 2000); |
| 1470 | } |
| 1471 | |
| 1472 | processNext(0); |
| 1473 | }); |
| 1474 | |
| 1475 | // Генериране на всички статии без ALT от листа |
| 1476 | $('#generate-all-alt-list').on('click', function() { |
| 1477 | var count = $(this).data('count'); |
| 1478 | if (!confirm('Сигурни ли сте, че искате да генерирате ALT тагове за ВСИЧКИ ' + count + ' статии?')) { |
| 1479 | return; |
| 1480 | } |
| 1481 | |
| 1482 | $(this).prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i> Обработване...'); |
| 1483 | |
| 1484 | // Намираме всички бутони за генериране и кликаме на всеки един |
| 1485 | var buttons = $('.aiseom-generate-alt'); |
| 1486 | var total = buttons.length; |
| 1487 | var processed = 0; |
| 1488 | |
| 1489 | function processNext(index) { |
| 1490 | if (index >= total) { |
| 1491 | alert('Готово! Обработени ' + total + ' статии.'); |
| 1492 | location.reload(); |
| 1493 | return; |
| 1494 | } |
| 1495 | |
| 1496 | buttons.eq(index).click(); |
| 1497 | |
| 1498 | // Почакаме малко преди следващия |
| 1499 | setTimeout(function() { |
| 1500 | processNext(index + 1); |
| 1501 | }, 2000); |
| 1502 | } |
| 1503 | |
| 1504 | processNext(0); |
| 1505 | }); |
| 1506 | }); |
| 1507 | </script> |
| 1508 | </div> |
| 1509 | <?php |
| 1510 | } |
| 1511 | |
| 1512 | public function render_settings_page() { |
| 1513 | ?> |
| 1514 | <div class="wrap" style="padding: 20px;"> |
| 1515 | <h1><i class="fas fa-cog"></i> Настройки на AI SEO Master PRO</h1> |
| 1516 | |
| 1517 | <div class="aiseom-settings-form"> |
| 1518 | <form method="post" action="options.php"> |
| 1519 | <?php settings_fields('aiseom_settings'); ?> |
| 1520 | |
| 1521 | <div class="form-group"> |
| 1522 | <label for="aiseom_api_provider">AI Доставчик:</label> |
| 1523 | <select name="aiseom_api_provider" id="aiseom_api_provider" class="regular-text"> |
| 1524 | <option value="demo" <?php selected(get_option('aiseom_api_provider', 'demo'), 'demo'); ?>>🎮 Демо режим (безплатен - за тестване)</option> |
| 1525 | <option value="huggingface" <?php selected(get_option('aiseom_api_provider'), 'huggingface'); ?>>🤗 Hugging Face (безплатен API)</option> |
| 1526 | <option value="openai" <?php selected(get_option('aiseom_api_provider'), 'openai'); ?>>🤖 OpenAI GPT (платен)</option> |
| 1527 | </select> |
| 1528 | |
| 1529 | <div id="api-desc-demo" class="api-description" style="display: none; margin-top: 8px; padding: 10px; background: #f8f9fa; border-radius: 5px;"> |
| 1530 | <p><strong>🎮 Демо режим:</strong> Работи без API ключ. Генерира примерни описания за тестване.</p> |
| 1531 | </div> |
| 1532 | |
| 1533 | <div id="api-desc-huggingface" class="api-description" style="display: none; margin-top: 8px; padding: 10px; background: #f8f9fa; border-radius: 5px;"> |
| 1534 | <p><strong>🤗 Hugging Face:</strong> Безплатен AI API. <a href="https://huggingface.co/settings/tokens" target="_blank">Получете безплатен ключ тук</a></p> |
| 1535 | </div> |
| 1536 | |
| 1537 | <div id="api-desc-openai" class="api-description" style="display: none; margin-top: 8px; padding: 10px; background: #f8f9fa; border-radius: 5px;"> |
| 1538 | <p><strong>🤖 OpenAI:</strong> Платен API. <a href="https://platform.openai.com/api-keys" target="_blank">Получете API ключ тук</a></p> |
| 1539 | </div> |
| 1540 | </div> |
| 1541 | |
| 1542 | <div class="form-group" id="api-key-row"> |
| 1543 | <label for="aiseom_api_key">API Ключ:</label> |
| 1544 | <input type="password" |
| 1545 | name="aiseom_api_key" |
| 1546 | id="aiseom_api_key" |
| 1547 | value="<?php echo esc_attr(get_option('aiseom_api_key', '')); ?>" |
| 1548 | class="regular-text" |
| 1549 | placeholder="Въведете вашия API ключ"> |
| 1550 | |
| 1551 | <button type="button" id="test-api-key" class="button" style="margin-top: 10px;"> |
| 1552 | <i class="fas fa-wifi"></i> Тествай API ключа |
| 1553 | </button> |
| 1554 | |
| 1555 | <div id="api-test-result" style="margin-top: 10px;"></div> |
| 1556 | </div> |
| 1557 | |
| 1558 | <div class="form-group"> |
| 1559 | <label for="aiseom_language">Език за генериране:</label> |
| 1560 | <select name="aiseom_language" id="aiseom_language"> |
| 1561 | <option value="bg" <?php selected(get_option('aiseom_language', 'bg'), 'bg'); ?>>Български</option> |
| 1562 | <option value="en" <?php selected(get_option('aiseom_language'), 'en'); ?>>Английски</option> |
| 1563 | </select> |
| 1564 | </div> |
| 1565 | |
| 1566 | <div class="form-group"> |
| 1567 | <label> |
| 1568 | <input type="checkbox" |
| 1569 | name="aiseom_auto_generate" |
| 1570 | value="1" |
| 1571 | <?php checked(get_option('aiseom_auto_generate', '1'), '1'); ?>> |
| 1572 | Автоматично генериране на SEO при запазване на статия |
| 1573 | </label> |
| 1574 | </div> |
| 1575 | |
| 1576 | <div class="form-group"> |
| 1577 | <label> |
| 1578 | <input type="checkbox" |
| 1579 | name="aiseom_auto_generate_alt" |
| 1580 | value="1" |
| 1581 | <?php checked(get_option('aiseom_auto_generate_alt', '1'), '1'); ?>> |
| 1582 | Автоматично генериране на ALT тагове за изображения |
| 1583 | </label> |
| 1584 | </div> |
| 1585 | |
| 1586 | <div class="form-group"> |
| 1587 | <label>Типове публикации за оптимизация:</label> |
| 1588 | <div style="margin-top: 10px;"> |
| 1589 | <?php |
| 1590 | $post_types = get_post_types(['public' => true], 'objects'); |
| 1591 | $selected = get_option('aiseom_post_types', ['post', 'page']); |
| 1592 | |
| 1593 | foreach ($post_types as $type) { |
| 1594 | if ($type->name === 'attachment') continue; |
| 1595 | ?> |
| 1596 | <label style="display: block; margin-bottom: 8px;"> |
| 1597 | <input type="checkbox" |
| 1598 | name="aiseom_post_types[]" |
| 1599 | value="<?php echo $type->name; ?>" |
| 1600 | <?php checked(in_array($type->name, $selected)); ?>> |
| 1601 | <?php echo $type->label; ?> |
| 1602 | </label> |
| 1603 | <?php |
| 1604 | } |
| 1605 | ?> |
| 1606 | </div> |
| 1607 | </div> |
| 1608 | |
| 1609 | <?php submit_button('💾 Запази настройките', 'primary'); ?> |
| 1610 | </form> |
| 1611 | </div> |
| 1612 | </div> |
| 1613 | |
| 1614 | <script> |
| 1615 | jQuery(document).ready(function($) { |
| 1616 | // Показване/скриване на API описания |
| 1617 | function updateApiDescription() { |
| 1618 | var provider = $('#aiseom_api_provider').val(); |
| 1619 | |
| 1620 | // Скриваме всички описания |
| 1621 | $('.api-description').hide(); |
| 1622 | |
| 1623 | // Показваме само избраното |
| 1624 | $('#api-desc-' + provider).show(); |
| 1625 | |
| 1626 | // Ако е демо, скриваме полето за API ключ |
| 1627 | if (provider === 'demo') { |
| 1628 | $('#api-key-row').hide(); |
| 1629 | } else { |
| 1630 | $('#api-key-row').show(); |
| 1631 | } |
| 1632 | } |
| 1633 | |
| 1634 | $('#aiseom_api_provider').on('change', updateApiDescription); |
| 1635 | updateApiDescription(); // Инициализация при зареждане |
| 1636 | }); |
| 1637 | </script> |
| 1638 | <?php |
| 1639 | } |
| 1640 | } |
| 1641 | |
| 1642 | // Инициализация |
| 1643 | function aiseom_pro_init() { |
| 1644 | return AI_SEO_Master_PRO::get_instance(); |
| 1645 | } |
| 1646 | |
| 1647 | add_action('plugins_loaded', 'aiseom_pro_init'); |
| 1648 | |
| 1649 | // Активационен хук |
| 1650 | register_activation_hook(__FILE__, function() { |
| 1651 | // Настройки по подразбиране |
| 1652 | $defaults = [ |
| 1653 | 'aiseom_api_provider' => 'demo', |
| 1654 | 'aiseom_language' => 'bg', |
| 1655 | 'aiseom_auto_generate' => '1', |
| 1656 | 'aiseom_auto_generate_alt' => '1', |
| 1657 | 'aiseom_post_types' => ['post', 'page'] |
| 1658 | ]; |
| 1659 | |
| 1660 | foreach ($defaults as $key => $value) { |
| 1661 | if (get_option($key) === false) { |
| 1662 | update_option($key, $value); |
| 1663 | } |
| 1664 | } |
| 1665 | }); |
ai-seo-master..png
· 93 KiB · Image (PNG)
原始檔案