You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

493 lines
19 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
/**
* SEO TDK 配置功能
* 支持设置不同页面的 SEO Title、Description、Keywords
*/
// 防止直接访问
if (!defined('ABSPATH')) {
exit;
}
/**
* 初始化 SEO 功能
*/
class NenghuiSEO {
public function __construct() {
add_action('init', array($this, 'init'));
}
public function init() {
// 添加后台菜单
add_action('admin_menu', array($this, 'add_admin_menu'));
// 添加文章和页面的SEO字段
add_action('add_meta_boxes', array($this, 'add_seo_meta_boxes'));
add_action('save_post', array($this, 'save_seo_meta_data'));
// 前端输出SEO标签
add_action('wp_head', array($this, 'output_seo_tags'), 1);
// 移除WordPress默认的title标签
remove_action('wp_head', '_wp_render_title_tag', 1);
// 注册设置
add_action('admin_init', array($this, 'register_settings'));
// 添加样式
add_action('admin_enqueue_scripts', array($this, 'admin_styles'));
}
/**
* 添加后台管理菜单
*/
public function add_admin_menu() {
add_menu_page(
'SEO设置',
'SEO设置',
'manage_options',
'nenghui-seo',
array($this, 'admin_page'),
'dashicons-search',
30
);
// 添加子菜单
add_submenu_page(
'nenghui-seo',
'全局SEO设置',
'全局设置',
'manage_options',
'nenghui-seo',
array($this, 'admin_page')
);
}
/**
* 注册设置选项
*/
public function register_settings() {
// 全局SEO设置
register_setting('nenghui_seo_global', 'nenghui_seo_global_title');
register_setting('nenghui_seo_global', 'nenghui_seo_global_description');
register_setting('nenghui_seo_global', 'nenghui_seo_global_keywords');
register_setting('nenghui_seo_global', 'nenghui_seo_separator');
register_setting('nenghui_seo_global', 'nenghui_seo_home_title');
register_setting('nenghui_seo_global', 'nenghui_seo_home_description');
register_setting('nenghui_seo_global', 'nenghui_seo_home_keywords');
}
/**
* 添加后台样式
*/
public function admin_styles($hook) {
if (strpos($hook, 'nenghui-seo') !== false || $hook == 'post.php' || $hook == 'post-new.php') {
echo '<style>
.seo-meta-box { background: #fff; border: 1px solid #ccd0d4; box-shadow: 0 1px 1px rgba(0,0,0,.04); }
.seo-field { margin-bottom: 15px; }
.seo-field label { display: block; font-weight: 600; margin-bottom: 5px; }
.seo-field input, .seo-field textarea { width: 100%; padding: 8px 12px; border: 1px solid #ddd; border-radius: 4px; }
.seo-field textarea { height: 80px; resize: vertical; }
.seo-field .description { font-size: 13px; color: #666; margin-top: 5px; }
.seo-preview { background: #f9f9f9; padding: 15px; border-radius: 4px; margin-top: 15px; }
.seo-preview h4 { margin: 0 0 10px 0; color: #333; }
.seo-title-preview { color: #1a0dab; font-size: 18px; text-decoration: none; }
.seo-url-preview { color: #006621; font-size: 14px; margin: 2px 0; }
.seo-desc-preview { color: #545454; font-size: 13px; line-height: 1.4; }
.char-count { float: right; font-size: 12px; color: #666; }
.char-count.warning { color: #d63638; }
</style>';
}
}
/**
* 后台管理页面
*/
public function admin_page() {
if (isset($_POST['submit'])) {
// 保存设置
update_option('nenghui_seo_global_title', sanitize_text_field($_POST['global_title']));
update_option('nenghui_seo_global_description', sanitize_textarea_field($_POST['global_description']));
update_option('nenghui_seo_global_keywords', sanitize_text_field($_POST['global_keywords']));
update_option('nenghui_seo_separator', sanitize_text_field($_POST['separator']));
update_option('nenghui_seo_home_title', sanitize_text_field($_POST['home_title']));
update_option('nenghui_seo_home_description', sanitize_textarea_field($_POST['home_description']));
update_option('nenghui_seo_home_keywords', sanitize_text_field($_POST['home_keywords']));
echo '<div class="notice notice-success"><p>设置已保存!</p></div>';
}
// 获取当前设置
$global_title = get_option('nenghui_seo_global_title', get_bloginfo('name'));
$global_description = get_option('nenghui_seo_global_description', get_bloginfo('description'));
$global_keywords = get_option('nenghui_seo_global_keywords', '');
$separator = get_option('nenghui_seo_separator', '-');
$home_title = get_option('nenghui_seo_home_title', '');
$home_description = get_option('nenghui_seo_home_description', '');
$home_keywords = get_option('nenghui_seo_home_keywords', '');
?>
<div class="wrap">
<h1>SEO 全局设置</h1>
<form method="post" action="">
<?php wp_nonce_field('nenghui_seo_nonce'); ?>
<table class="form-table">
<tr>
<th scope="row">标题分隔符</th>
<td>
<select name="separator">
<option value="-" <?php selected($separator, '-'); ?>>- (横线)</option>
<option value="|" <?php selected($separator, '|'); ?>>| (竖线)</option>
<option value="_" <?php selected($separator, '_'); ?>>_ (下划线)</option>
<option value="·" <?php selected($separator, '·'); ?>>· (中点)</option>
</select>
<p class="description">用于分隔页面标题和网站名称</p>
</td>
</tr>
<tr>
<th scope="row">全局网站标题</th>
<td>
<input type="text" name="global_title" value="<?php echo esc_attr($global_title); ?>" class="regular-text" />
<p class="description">网站的全局标题,会出现在所有页面标题的后面</p>
</td>
</tr>
<tr>
<th scope="row">全局网站描述</th>
<td>
<textarea name="global_description" rows="3" class="large-text"><?php echo esc_textarea($global_description); ?></textarea>
<p class="description">网站的全局描述,用于没有设置单独描述的页面</p>
</td>
</tr>
<tr>
<th scope="row">全局关键词</th>
<td>
<input type="text" name="global_keywords" value="<?php echo esc_attr($global_keywords); ?>" class="large-text" />
<p class="description">网站的全局关键词,用逗号分隔</p>
</td>
</tr>
</table>
<h2>首页专用设置</h2>
<table class="form-table">
<tr>
<th scope="row">首页标题</th>
<td>
<input type="text" name="home_title" value="<?php echo esc_attr($home_title); ?>" class="large-text" />
<p class="description">首页专用标题,留空则使用全局标题</p>
</td>
</tr>
<tr>
<th scope="row">首页描述</th>
<td>
<textarea name="home_description" rows="3" class="large-text"><?php echo esc_textarea($home_description); ?></textarea>
<p class="description">首页专用描述,留空则使用全局描述</p>
</td>
</tr>
<tr>
<th scope="row">首页关键词</th>
<td>
<input type="text" name="home_keywords" value="<?php echo esc_attr($home_keywords); ?>" class="large-text" />
<p class="description">首页专用关键词,留空则使用全局关键词</p>
</td>
</tr>
</table>
<?php submit_button('保存设置'); ?>
</form>
</div>
<?php
}
/**
* 添加文章和页面的SEO元框
*/
public function add_seo_meta_boxes() {
$post_types = array('post', 'page', 'products', 'cases', 'download_center', 'faq');
foreach ($post_types as $post_type) {
add_meta_box(
'nenghui_seo_meta',
'SEO 设置',
array($this, 'seo_meta_box_callback'),
$post_type,
'normal',
'high'
);
}
}
/**
* SEO元框回调函数
*/
public function seo_meta_box_callback($post) {
wp_nonce_field('nenghui_seo_meta_nonce', 'nenghui_seo_meta_nonce_field');
$seo_title = get_post_meta($post->ID, '_nenghui_seo_title', true);
$seo_description = get_post_meta($post->ID, '_nenghui_seo_description', true);
$seo_keywords = get_post_meta($post->ID, '_nenghui_seo_keywords', true);
?>
<div class="seo-meta-box">
<div class="seo-field">
<label for="seo_title">SEO 标题</label>
<input type="text" id="seo_title" name="seo_title" value="<?php echo esc_attr($seo_title); ?>" maxlength="60" />
<span class="char-count" id="title-count">0/60</span>
<div class="description">建议长度50-60个字符留空则使用文章标题</div>
</div>
<div class="seo-field">
<label for="seo_description">SEO 描述</label>
<textarea id="seo_description" name="seo_description" maxlength="160"><?php echo esc_textarea($seo_description); ?></textarea>
<span class="char-count" id="desc-count">0/160</span>
<div class="description">建议长度120-160个字符留空则自动提取文章摘要</div>
</div>
<div class="seo-field">
<label for="seo_keywords">SEO 关键词</label>
<input type="text" id="seo_keywords" name="seo_keywords" value="<?php echo esc_attr($seo_keywords); ?>" />
<div class="description">多个关键词用逗号分隔建议3-5个关键词</div>
</div>
<div class="seo-preview">
<h4>搜索结果预览</h4>
<div class="seo-title-preview" id="preview-title"><?php echo esc_html($seo_title ?: $post->post_title); ?></div>
<div class="seo-url-preview"><?php echo esc_url(get_permalink($post->ID)); ?></div>
<div class="seo-desc-preview" id="preview-desc"><?php echo esc_html($seo_description ?: wp_trim_words($post->post_content, 20)); ?></div>
</div>
</div>
<script>
jQuery(document).ready(function($) {
function updateCharCount(input, counter, max) {
var length = $(input).val().length;
$(counter).text(length + '/' + max);
if (length > max * 0.9) {
$(counter).addClass('warning');
} else {
$(counter).removeClass('warning');
}
}
function updatePreview() {
var title = $('#seo_title').val() || '<?php echo esc_js($post->post_title); ?>';
var desc = $('#seo_description').val() || '<?php echo esc_js(wp_trim_words($post->post_content, 20)); ?>';
$('#preview-title').text(title);
$('#preview-desc').text(desc);
}
$('#seo_title').on('input', function() {
updateCharCount(this, '#title-count', 60);
updatePreview();
}).trigger('input');
$('#seo_description').on('input', function() {
updateCharCount(this, '#desc-count', 160);
updatePreview();
}).trigger('input');
});
</script>
<?php
}
/**
* 保存SEO元数据
*/
public function save_seo_meta_data($post_id) {
if (!isset($_POST['nenghui_seo_meta_nonce_field']) ||
!wp_verify_nonce($_POST['nenghui_seo_meta_nonce_field'], 'nenghui_seo_meta_nonce')) {
return;
}
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
return;
}
if (!current_user_can('edit_post', $post_id)) {
return;
}
if (isset($_POST['seo_title'])) {
update_post_meta($post_id, '_nenghui_seo_title', sanitize_text_field($_POST['seo_title']));
}
if (isset($_POST['seo_description'])) {
update_post_meta($post_id, '_nenghui_seo_description', sanitize_textarea_field($_POST['seo_description']));
}
if (isset($_POST['seo_keywords'])) {
update_post_meta($post_id, '_nenghui_seo_keywords', sanitize_text_field($_POST['seo_keywords']));
}
}
/**
* 前端输出SEO标签
*/
public function output_seo_tags() {
$title = $this->get_seo_title();
$description = $this->get_seo_description();
$keywords = $this->get_seo_keywords();
// 输出标题
echo '<title>' . esc_html($title) . '</title>' . "\n";
// 输出描述
if ($description) {
echo '<meta name="description" content="' . esc_attr($description) . '" />' . "\n";
}
// 输出关键词
if ($keywords) {
echo '<meta name="keywords" content="' . esc_attr($keywords) . '" />' . "\n";
}
// Open Graph 标签
echo '<meta property="og:title" content="' . esc_attr($title) . '" />' . "\n";
if ($description) {
echo '<meta property="og:description" content="' . esc_attr($description) . '" />' . "\n";
}
echo '<meta property="og:url" content="' . esc_url($this->get_current_url()) . '" />' . "\n";
echo '<meta property="og:type" content="' . (is_single() ? 'article' : 'website') . '" />' . "\n";
// Twitter Card 标签
echo '<meta name="twitter:card" content="summary" />' . "\n";
echo '<meta name="twitter:title" content="' . esc_attr($title) . '" />' . "\n";
if ($description) {
echo '<meta name="twitter:description" content="' . esc_attr($description) . '" />' . "\n";
}
}
/**
* 获取SEO标题
*/
private function get_seo_title() {
$separator = get_option('nenghui_seo_separator', '-');
$global_title = get_option('nenghui_seo_global_title', get_bloginfo('name'));
if (is_home() || is_front_page()) {
$home_title = get_option('nenghui_seo_home_title');
return $home_title ?: $global_title;
}
if (is_singular()) {
global $post;
$custom_title = get_post_meta($post->ID, '_nenghui_seo_title', true);
if ($custom_title) {
return $custom_title;
}
return $post->post_title . ' ' . $separator . ' ' . $global_title;
}
if (is_category()) {
$category = get_queried_object();
return $category->name . ' ' . $separator . ' ' . $global_title;
}
if (is_tag()) {
$tag = get_queried_object();
return $tag->name . ' ' . $separator . ' ' . $global_title;
}
if (is_archive()) {
return get_the_archive_title() . ' ' . $separator . ' ' . $global_title;
}
if (is_search()) {
return '搜索: ' . get_search_query() . ' ' . $separator . ' ' . $global_title;
}
if (is_404()) {
return '页面未找到 ' . $separator . ' ' . $global_title;
}
return $global_title;
}
/**
* 获取SEO描述
*/
private function get_seo_description() {
$global_description = get_option('nenghui_seo_global_description', get_bloginfo('description'));
if (is_home() || is_front_page()) {
$home_description = get_option('nenghui_seo_home_description');
return $home_description ?: $global_description;
}
if (is_singular()) {
global $post;
$custom_description = get_post_meta($post->ID, '_nenghui_seo_description', true);
if ($custom_description) {
return $custom_description;
}
// 自动提取摘要
if ($post->post_excerpt) {
return wp_trim_words($post->post_excerpt, 25);
}
return wp_trim_words(strip_tags($post->post_content), 25);
}
if (is_category()) {
$category = get_queried_object();
return $category->description ?: $global_description;
}
if (is_tag()) {
$tag = get_queried_object();
return $tag->description ?: $global_description;
}
return $global_description;
}
/**
* 获取SEO关键词
*/
private function get_seo_keywords() {
$global_keywords = get_option('nenghui_seo_global_keywords');
if (is_home() || is_front_page()) {
$home_keywords = get_option('nenghui_seo_home_keywords');
return $home_keywords ?: $global_keywords;
}
if (is_singular()) {
global $post;
$custom_keywords = get_post_meta($post->ID, '_nenghui_seo_keywords', true);
if ($custom_keywords) {
return $custom_keywords;
}
// 自动提取标签作为关键词
$tags = get_the_tags($post->ID);
if ($tags) {
$tag_names = array();
foreach ($tags as $tag) {
$tag_names[] = $tag->name;
}
return implode(', ', $tag_names);
}
}
return $global_keywords;
}
/**
* 获取当前页面URL
*/
private function get_current_url() {
global $wp;
return home_url(add_query_arg(array(), $wp->request));
}
}
// 初始化SEO功能
new NenghuiSEO();