Plugin Directory

Changeset 3625549


Ignore:
Timestamp:
07/28/2026 07:41:53 AM (3 months ago)
Author:
kelerkgibo
Message:

2026.7.28

Location:
baiduseo
Files:
64 added
1 deleted
8 edited

Legend:

Unmodified
Added
Removed
  • baiduseo/trunk/inc/admin/assets.php

    r3497150 r3625549  
    55        'name'=>'tagmanage_css_3',
    66        'type'=>'js',
    7         'url'=>'./assets/index-pmcy0olr.js',
     7        'url'=>'./assets/index.js',
    88   
    99],
  • baiduseo/trunk/inc/admin/get.php

    r3497150 r3625549  
    1313                add_action('wp_ajax_baiduseo_get_cate_seo', [$this,'baiduseo_get_cate_seo']);
    1414                add_action('wp_ajax_baiduseo_get_page_seo', [$this,'baiduseo_get_page_seo']);
     15                add_action('wp_ajax_baiduseo_get_geo', [$this,'baiduseo_get_geo']);
    1516                add_action('wp_ajax_baiduseo_get_wyc', [$this,'baiduseo_get_wyc']);
    1617                add_action('wp_ajax_baiduseo_get_yuanchuang', [$this,'yuanchuang']);
    … …  
    5051                add_action('wp_ajax_baiduseo_get_liuliang_sl', [$this,'baiduseo_get_liuliang_sl']);
    5152                add_action('wp_ajax_baiduseo_get_liuliang_list', [$this,'baiduseo_get_liuliang_list']);
     53                add_action('wp_ajax_baiduseo_liuliang_export', [$this,'baiduseo_liuliang_export']);
     54                add_action('wp_ajax_baiduseo_fix_ai_source', [$this,'baiduseo_fix_ai_source']);
    5255                add_action('wp_ajax_baiduseo_get_vip', [$this,'baiduseo_get_vip']);
    5356                add_action('wp_ajax_baiduseo_get_301', [$this,'baiduseo_get_301']);
    … …  
    8689                }
    8790            }
     91        }
     92        private function baiduseo_llms_default_form(){
     93            return [
     94                'siteName'=>get_option('blogname'),
     95                'language'=>'zh-CN',
     96                'sitemapUrl'=>trim(get_option('home'),'/').'/sitemap.xml',
     97                'siteIntro'=>get_option('blogdescription'),
     98                'businessScope'=>'',
     99                'targetUsers'=>'',
     100                'serviceArea'=>'',
     101            ];
     102        }
     103        private function baiduseo_llms_saved_map($items){
     104            $map = [];
     105            if(!is_array($items)){
     106                return $map;
     107            }
     108            foreach($items as $item){
     109                if(is_array($item) && isset($item['id'])){
     110                    $map[(string)$item['id']] = $item;
     111                }
     112            }
     113            return $map;
     114        }
     115        private function baiduseo_llms_merge_entries($entries,$saved_items){
     116            $saved_map = $this->baiduseo_llms_saved_map($saved_items);
     117            foreach($entries as $key=>$entry){
     118                $entry_id = isset($entry['id']) ? (string)$entry['id'] : '';
     119                if($entry_id !== '' && isset($saved_map[$entry_id])){
     120                    if(array_key_exists('checked',$saved_map[$entry_id])){
     121                        $entries[$key]['checked'] = (bool)$saved_map[$entry_id]['checked'];
     122                    }
     123                    if(array_key_exists('desc',$saved_map[$entry_id])){
     124                        $entries[$key]['desc'] = $saved_map[$entry_id]['desc'];
     125                    }
     126                }
     127            }
     128            return $entries;
     129        }
     130        private function baiduseo_llms_excerpt($content){
     131            $content = wp_strip_all_tags((string)$content);
     132            $content = trim(preg_replace('/\s+/u',' ',$content));
     133            if(function_exists('mb_substr')){
     134                return mb_substr($content,0,120);
     135            }
     136            return substr($content,0,120);
     137        }
     138        private function baiduseo_llms_posts($post_type){
     139            $posts = get_posts([
     140                'post_type'=>$post_type,
     141                'post_status'=>'publish',
     142                'numberposts'=>-1,
     143                'orderby'=>$post_type === 'page' ? 'menu_order title' : 'date',
     144                'order'=>$post_type === 'page' ? 'ASC' : 'DESC',
     145            ]);
     146            $data = [];
     147            foreach($posts as $post){
     148                $desc = $post->post_excerpt ? $this->baiduseo_llms_excerpt($post->post_excerpt) : '';
     149                $data[] = [
     150                    'id'=>(int)$post->ID,
     151                    'title'=>get_the_title($post),
     152                    'url'=>get_permalink($post),
     153                    'desc'=>$desc,
     154                    'checked'=>true,
     155                ];
     156            }
     157            return $data;
     158        }
     159        private function baiduseo_llms_categories(){
     160            $categories = get_categories(['hide_empty'=>false]);
     161            $data = [];
     162            foreach($categories as $category){
     163                $category_link = get_category_link($category->term_id);
     164                $data[] = [
     165                    'id'=>(int)$category->term_id,
     166                    'title'=>$category->name,
     167                    'url'=>is_wp_error($category_link) ? '' : $category_link,
     168                    'desc'=>$this->baiduseo_llms_excerpt($category->description),
     169                    'checked'=>true,
     170                ];
     171            }
     172            return $data;
     173        }
     174        private function baiduseo_llms_builder(){
     175            $saved = get_option('seo_llms_builder');
     176            if(!is_array($saved)){
     177                $saved = [];
     178            }
     179            $saved_form = isset($saved['form']) && is_array($saved['form']) ? $saved['form'] : [];
     180            $builder = [
     181                'form'=>array_merge($this->baiduseo_llms_default_form(),$saved_form),
     182                'pages'=>$this->baiduseo_llms_merge_entries($this->baiduseo_llms_posts('page'),isset($saved['pages']) ? $saved['pages'] : []),
     183                'posts'=>$this->baiduseo_llms_merge_entries($this->baiduseo_llms_posts('post'),isset($saved['posts']) ? $saved['posts'] : []),
     184                'categories'=>$this->baiduseo_llms_merge_entries($this->baiduseo_llms_categories(),isset($saved['categories']) ? $saved['categories'] : []),
     185                'manualLinks'=>isset($saved['manualLinks']) && is_array($saved['manualLinks']) ? $saved['manualLinks'] : [],
     186                'entryGroups'=>isset($saved['entryGroups']) && is_array($saved['entryGroups']) ? $saved['entryGroups'] : [],
     187                'customModules'=>isset($saved['customModules']) && is_array($saved['customModules']) ? $saved['customModules'] : [],
     188            ];
     189            return $builder;
    88190        }
    89191        public function baiduseo_get_beian(){
    … …  
    603705                }elseif(isset($_POST['source']) && (int)$_POST['source']==4){
    604706                    $sql .= '  and baiduseo_detail="自然流量"';
     707                }elseif(isset($_POST['source']) && (int)$_POST['source']==5){
     708                    $sql .= '  and baiduseo_detail="AI来源"';
    605709                }
    606710                if(isset($_POST['type']) && (int)$_POST['type']==1){
    … …  
    619723                $sql .= ' order by id desc limit %d,35 ';
    620724                if(empty($con1)){
     725                    // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter -- SQL clauses above are selected from fixed branches with no user placeholders in this branch.
    621726                    $count = $wpdb->query($sql1,ARRAY_A);
    622727                }else{
     728                    // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter -- SQL clauses above are selected from fixed branches; user values are prepared via placeholders.
    623729                    $count = $wpdb->query($wpdb->prepare($sql1,array_values($con1)),ARRAY_A);
    624730                }
    625731               
     732                // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter -- SQL clauses above are selected from fixed branches; user values are prepared via placeholders.
    626733                $list = $wpdb->get_results($wpdb->prepare($sql,array_values($con)),ARRAY_A);
    627734               
    … …  
    678785               
    679786           
     787        }
     788        public function baiduseo_fix_ai_source(){
     789            $this->check_permission('manage_options');
     790            global $wpdb;
     791
     792            $table = $wpdb->prefix . 'baiduseo_liuliang';
     793            $ai_sources = [
     794                'chatgpt.com',
     795                'chat.openai.com',
     796                'openai.com',
     797                'perplexity.ai',
     798                'pplx.ai',
     799                'claude.ai',
     800                'poe.com',
     801                'you.com',
     802                'gemini.google.com',
     803                'bard.google.com',
     804                'copilot.microsoft.com',
     805                'bing.com/chat',
     806                'metaso.cn',
     807                'kimi.moonshot.cn',
     808                'kimi.com',
     809                'doubao.com',
     810                'link.wttur.cn',
     811                'yuanbao.tencent.com',
     812                'ima.qq.com',
     813                'tongyi.aliyun.com',
     814                'qianwen.aliyun.com',
     815                'yiyan.baidu.com',
     816                'chat.baidu.com',
     817                'deepseek.com',
     818                'deepseek.ai',
     819                'chatglm.cn',
     820                'zhipuai.cn',
     821                'coze.cn',
     822                'grok.com',
     823                'x.ai'
     824            ];
     825
     826            $like_sql = [];
     827            $like_args = [];
     828            foreach ($ai_sources as $source) {
     829                $like = '%' . $wpdb->esc_like($source) . '%';
     830                $like_sql[] = '(source LIKE %s OR url LIKE %s)';
     831                $like_args[] = $like;
     832                $like_args[] = $like;
     833            }
     834
     835            if (empty($like_sql)) {
     836                echo wp_json_encode(['code'=>1,'count'=>0,'msg'=>'没有可修正的数据']);exit;
     837            }
     838
     839            $source_where = '(' . implode(' OR ', $like_sql) . ')';
     840            $detail_where = "(baiduseo_detail IS NULL OR baiduseo_detail = '' OR baiduseo_detail = '直接访问' OR baiduseo_detail = '外部链接')";
     841            $child_source_where = str_replace(['source LIKE', 'url LIKE'], ['child.source LIKE', 'child.url LIKE'], $source_where);
     842            $child_detail_where = str_replace('baiduseo_detail', 'child.baiduseo_detail', $detail_where);
     843
     844            $child_sql = "
     845                UPDATE {$table} child
     846                INNER JOIN {$table} parent ON child.pid = parent.id
     847                SET parent.baiduseo_detail = 'AI来源'
     848                WHERE child.pid > 0
     849                AND (parent.baiduseo_detail IS NULL OR parent.baiduseo_detail = '' OR parent.baiduseo_detail = '直接访问' OR parent.baiduseo_detail = '外部链接')
     850                AND (child.baiduseo_detail = 'AI来源' OR ({$child_detail_where} AND {$child_source_where}))
     851            ";
     852            // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter -- SQL is built from a hard-coded AI source list; LIKE values are prepared below.
     853            $parent_count = (int)$wpdb->query($wpdb->prepare($child_sql, $like_args));
     854
     855            $row_sql = "
     856                UPDATE {$table}
     857                SET baiduseo_detail = 'AI来源'
     858                WHERE {$detail_where}
     859                AND {$source_where}
     860            ";
     861            // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter -- SQL is built from a hard-coded AI source list; LIKE values are prepared below.
     862            $row_count = (int)$wpdb->query($wpdb->prepare($row_sql, $like_args));
     863            $total = $parent_count + $row_count;
     864
     865            echo wp_json_encode(['code'=>1,'count'=>$total,'parent_count'=>$parent_count,'row_count'=>$row_count,'msg'=>'AI来源修正完成']);exit;
     866        }
     867        public function baiduseo_liuliang_export(){
     868             $this->check_permission('manage_options');
     869                global $wpdb;
     870                $con = [];
     871                $sql ='select * from '.$wpdb->prefix . 'baiduseo_liuliang where status=1';
     872                if(!isset($_POST['fangke'])){
     873                    $_POST['fangke'] =1;
     874                }
     875                 if(!isset($_POST['pinci'])){
     876                    $_POST['pinci'] =1;
     877                }
     878                  if(!isset($_POST['shendu'])){
     879                    $_POST['shendu'] =1;
     880                }
     881                   if(!isset($_POST['shichang'])){
     882                    $_POST['shichang'] =1;
     883                }
     884                if(!isset($_POST['time'])){
     885                    $_POST['time'] =0;
     886                }
     887                if((int)$_POST['fangke']==1){
     888                    $sql .= '  and is_new=1';
     889                }elseif((int)$_POST['fangke']==2){
     890                    $sql .= '  and is_new=0';
     891                }
     892                if((int)$_POST['pinci']==1){
     893                    $sql .= '  and pinci=1';
     894                }elseif((int)$_POST['pinci']==2){
     895                     $sql .= '  and pinci=2';
     896                }elseif((int)$_POST['pinci']==3){
     897                    $sql .= '  and pinci=3';
     898                }elseif((int)$_POST['pinci']==4){
     899                    $sql .= '  and pinci=4';
     900                }elseif((int)$_POST['pinci']==5){
     901                     $sql .= '  and pinci>=5 and pinci<=10';
     902                }elseif((int)$_POST['pinci']==6){
     903                    $sql .= '  and pinci>=11 and pinci<=20';
     904                }elseif((int)$_POST['pinci']==7){
     905                    $sql .= '  and pinci>20';
     906                }
     907                if((int)$_POST['shendu']==1){
     908                    $sql .= ' and shendu=1';
     909                }elseif((int)$_POST['shendu']==2){
     910                    $sql .= ' and shendu=2';
     911                }elseif((int)$_POST['shendu']==3){
     912                    $sql .= ' and shendu=3';
     913                }elseif((int)$_POST['shendu']==4){
     914                    $sql .= ' and shendu=4';
     915                }elseif((int)$_POST['shendu']==5){
     916                    $sql .= ' and shendu>=5 and shendu<=10';
     917                }elseif((int)$_POST['shendu']==6){
     918                    $sql .= ' and shendu>=11 and shendu<=20';
     919                }elseif((int)$_POST['shendu']==7){
     920                    $sql .= ' and shendu>20';
     921                }
     922                if((int)$_POST['shichang']==1){
     923                    $sql .= ' and shichang<=30';
     924                }elseif((int)$_POST['shichang']==2){
     925                    $sql .= ' and shichang>30 and shichang<=60';
     926                }elseif((int)$_POST['shichang']==3){
     927                    $sql .= ' and shichang>60 and shichang<=90';
     928                }elseif((int)$_POST['shichang']==4){
     929                    $sql .= ' and shichang>90 and shichang<=180';
     930                }elseif((int)$_POST['shichang']==5){
     931                    $sql .= ' and shichang>180 and shichang<=300';
     932                }elseif((int)$_POST['shichang']==6){
     933                    $sql .= ' and shichang>300 and shichang<=600';
     934                }elseif((int)$_POST['shichang']==7){
     935                    $sql .= ' and shichang>600';
     936                }
     937                if(sanitize_text_field(wp_unslash($_POST['time']))){
     938                    $time = sanitize_text_field(wp_unslash($_POST['time']));
     939                    $timezone_offet = get_option( 'gmt_offset');
     940                    $sta1 = strtotime($time)-$timezone_offet*3600;
     941                    $end1 = strtotime($time)+24*3600-$timezone_offet*3600;
     942                    $sql .=' and unix_timestamp(time)>%d and  unix_timestamp(time)<%d';
     943                    $con = [$sta1,$end1];
     944                }
     945                if(isset($_POST['rukou']) && sanitize_text_field(wp_unslash($_POST['rukou']))){
     946                    $rukou = sanitize_text_field(wp_unslash($_POST['rukou']));
     947                    $sql .= ' and url=%s';
     948                    $con[] = $rukou;
     949                }
     950                 if(isset($_POST['ip']) && sanitize_text_field(wp_unslash($_POST['ip']))){
     951                     $ip = sanitize_text_field(wp_unslash($_POST['ip']));
     952                    $sql .= ' and ip=%s';
     953                    $con[] = $ip;
     954                }
     955                 if(isset($_POST['session']) && sanitize_text_field(wp_unslash($_POST['session']))){
     956                     $session = sanitize_text_field(wp_unslash($_POST['session']));
     957                    $sql .= ' and session=%s';
     958                    $con[] = $session;
     959                }
     960                if(isset($_POST['source']) && (int)$_POST['source']==1){
     961                    $sql .= '  and baiduseo_detail="直接访问"';
     962                }elseif(isset($_POST['source']) && (int)$_POST['source']==2){
     963                    $sql .= '  and baiduseo_detail="付费访问"';
     964                }elseif(isset($_POST['source']) && (int)$_POST['source']==3){
     965                    $sql .= '  and baiduseo_detail="外部链接"';
     966                }elseif(isset($_POST['source']) && (int)$_POST['source']==4){
     967                    $sql .= '  and baiduseo_detail="自然流量"';
     968                }elseif(isset($_POST['source']) && (int)$_POST['source']==5){
     969                    $sql .= '  and baiduseo_detail="AI来源"';
     970                }
     971                if(isset($_POST['type']) && (int)$_POST['type']==1){
     972                    $sql .= ' and type=1';
     973                }elseif(isset($_POST['type']) && (int)$_POST['type']==2){
     974                    $sql .= ' and type=2';
     975                }
     976                $sql .= ' order by id desc';
     977                if(empty($con)){
     978                    // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter -- SQL clauses above are selected from fixed branches with no user placeholders in this branch.
     979                    $list = $wpdb->get_results($sql,ARRAY_A);
     980                }else{
     981                    // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter -- SQL clauses above are selected from fixed branches; user values are prepared via placeholders.
     982                    $list = $wpdb->get_results($wpdb->prepare($sql,array_values($con)),ARRAY_A);
     983                }
     984
     985                $filename = '访客报表_' . current_time('Ymd_His') . '.csv';
     986                header('Content-Type: text/csv; charset=UTF-8');
     987                header('Content-Disposition: attachment; filename="' . rawurlencode($filename) . '"; filename*=UTF-8\'\'' . rawurlencode($filename));
     988                header('Pragma: no-cache');
     989                header('Expires: 0');
     990                echo "\xEF\xBB\xBF";
     991                $output = fopen('php://output', 'w');
     992                $csv_safe = function($value){
     993                    if(!is_scalar($value)){
     994                        return '';
     995                    }
     996                    $value = (string)$value;
     997                    return preg_match('/^[=+\-@]/', $value) ? "'" . $value : $value;
     998                };
     999                $format_duration = function($value){
     1000                    $seconds = (int)$value;
     1001                    if($seconds >= 60){
     1002                        $minutes = floor($seconds / 60);
     1003                        $remaining_seconds = $seconds % 60;
     1004                        return $minutes . "'" . ($remaining_seconds ? $remaining_seconds . '"' : '');
     1005                    }
     1006                    return $seconds . '"';
     1007                };
     1008                fputcsv($output, ['序号','首次访问','最后访问','地域','首次访问来源','入口页面','访问IP','访问时长','访问页数','访客类型','访客识别码','上一次访问时间','本次来源','最后停留在','操作系统','语言环境','浏览器','访问频次','访问路径']);
     1009                $export_count = count($list);
     1010                foreach($list as $index=>$val){
     1011                    $newlao = $wpdb->get_results($wpdb->prepare('select * from '.$wpdb->prefix . 'baiduseo_liuliang where session=%s and status=1 and  id<%d  order by  id desc limit 1',$val['session'],$val['id']),ARRAY_A);
     1012                    $lujing = $wpdb->get_results($wpdb->prepare('select * from '.$wpdb->prefix . 'baiduseo_liuliang where pid=%d or id=%d order by id asc',$val['id'],$val['id']),ARRAY_A);
     1013                    $last_time = '';
     1014                    $end_url = isset($val['url']) ? $val['url'] : '';
     1015                    $path_urls = [];
     1016                    $totaltime = 1;
     1017                    if(isset($val['updatetime']) && $val['updatetime']){
     1018                        $totaltime = strtotime($val['updatetime'])-strtotime($val['time']);
     1019                    }
     1020                    if(!empty($lujing)){
     1021                        foreach($lujing as $v){
     1022                            $last_time = isset($v['time']) ? $v['time'] : $last_time;
     1023                            $end_url = isset($v['url']) ? $v['url'] : $end_url;
     1024                            if(isset($v['url']) && $v['url']){
     1025                                $path_urls[] = $v['url'];
     1026                            }
     1027                            if(isset($v['shichang']) && $v['shichang']){
     1028                                $totaltime += (int)$v['shichang'];
     1029                            }elseif(isset($v['updatetime']) && $v['updatetime']){
     1030                                $totaltime += strtotime($v['updatetime'])-strtotime($v['time']);
     1031                            }else{
     1032                                $totaltime += 1;
     1033                            }
     1034                        }
     1035                    }
     1036                    $total_page = $wpdb->query($wpdb->prepare('select * from '.$wpdb->prefix . 'baiduseo_liuliang where pid=%d',$val['id']),ARRAY_A)+1;
     1037                    $visitor_type = isset($val['is_new']) && (int)$val['is_new'] === 0 ? '新访客' : '旧访客';
     1038                    $source_detail = isset($val['baiduseo_detail']) && $val['baiduseo_detail'] ? $val['baiduseo_detail'] : '直接访问';
     1039                    $duration = isset($val['shichang']) && $val['shichang'] ? $val['shichang'] : $totaltime;
     1040                    $row = [
     1041                        $export_count - $index,
     1042                        isset($val['time']) ? $val['time'] : '',
     1043                        count($lujing)>1 ? $last_time : '',
     1044                        isset($val['sheng']) ? $val['sheng'] : '',
     1045                        $source_detail,
     1046                        isset($val['url']) ? $val['url'] : '',
     1047                        isset($val['ip']) ? $val['ip'] : '',
     1048                        $format_duration($duration),
     1049                        $total_page,
     1050                        $visitor_type,
     1051                        isset($val['session']) ? $val['session'] : '',
     1052                        !empty($newlao) && isset($newlao[0]['time']) ? $newlao[0]['time'] : '',
     1053                        $source_detail,
     1054                        $end_url,
     1055                        isset($val['pla']) ? $val['pla'] : '',
     1056                        isset($val['lan']) ? $val['lan'] : '',
     1057                        isset($val['liulanqi']) ? $val['liulanqi'] : '',
     1058                        isset($val['pinci']) ? $val['pinci'] : '',
     1059                        implode(' -> ', $path_urls)
     1060                    ];
     1061                    fputcsv($output, array_map($csv_safe, $row));
     1062                }
     1063                exit;
    6801064        }
    6811065        public function baiduseo_get_liuliang_sl(){
    … …  
    16782062            $this->check_permission('manage_options');
    16792063                $page=isset($_POST['page'])?(int)$_POST['page']:0;
    1680                 $baiduseo_page = get_post_meta( $page, 'baiduseo_page', true );
     2064                if($page && $page === (int)get_option('page_on_front')){
     2065                    $baiduseo_page = get_option('seo_init');
     2066                }else{
     2067                    $baiduseo_page = get_post_meta( $page, 'baiduseo_page', true );
     2068                }
    16812069                 echo wp_json_encode(['code'=>1,'data'=>$baiduseo_page]);exit;
    1682            
     2070
    16832071        }
    16842072        public function baiduseo_get_cate_seo(){
    … …  
    16872075                $seo_init = get_option('baiduseo_cate_'.$cate);
    16882076                echo wp_json_encode(['code'=>1,'data'=>$seo_init]);exit;
     2077           
     2078        }
     2079        private function baiduseo_get_geo_image($post_id){
     2080            $image = get_the_post_thumbnail_url($post_id, 'full');
     2081            return $image ? $image : '';
     2082        }
     2083        private function baiduseo_geo_default_form(){
     2084            return [
     2085                'pageType'=>'',
     2086                'pageUrl'=>'',
     2087                'seoTitle'=>'',
     2088                'seoKeywords'=>'',
     2089                'seoDesc'=>'',
     2090                'shareImage'=>'',
     2091                'shareImageId'=>'',
     2092                'ogImage'=>'',
     2093                'ogImageId'=>'',
     2094                'twitterImage'=>'',
     2095                'twitterImageId'=>'',
     2096                'schemaType'=>'OrganizationWebSite',
     2097                'companyName'=>'',
     2098                'companyAddress'=>'',
     2099                'phone'=>'',
     2100                'email'=>'',
     2101                'author'=>'',
     2102                'published'=>'',
     2103                'modified'=>'',
     2104                'serviceName'=>'',
     2105                'areaServed'=>'',
     2106                'faqText'=>'',
     2107                'customSchemaJson'=>'',
     2108                'wechat'=>'',
     2109                'weibo'=>'',
     2110                'zhihu'=>'',
     2111                'bili'=>'',
     2112                'xurl'=>'',
     2113                'linkedin'=>'',
     2114                'facebook'=>'',
     2115                'youtube'=>'',
     2116                'baiduVerify'=>'',
     2117                'bingVerify'=>'',
     2118                'soVerify'=>'',
     2119                'sogouVerify'=>'',
     2120                'shenmaVerify'=>''
     2121            ];
     2122        }
     2123        private function baiduseo_geo_source_form_keys(){
     2124            return ['pageType','pageUrl','seoTitle','seoKeywords','seoDesc','shareImage','shareImageId','author','published','modified'];
     2125        }
     2126        private function baiduseo_geo_schema_source_form_map(){
     2127            return [
     2128                'title'=>'seoTitle',
     2129                'desc'=>'seoDesc',
     2130                'url'=>'pageUrl',
     2131                'image'=>'shareImage',
     2132                'author'=>'author',
     2133                'published'=>'published',
     2134                'modified'=>'modified'
     2135            ];
     2136        }
     2137        private function baiduseo_geo_filter_saved_form($form){
     2138            if(!is_array($form)) return [];
     2139            return $form;
     2140        }
     2141        private function baiduseo_geo_apply_manual_source_form($form, $saved_form, $schema_source){
     2142            if(!is_array($schema_source)) return $form;
     2143            foreach($this->baiduseo_geo_schema_source_form_map() as $source_key=>$form_key){
     2144                if(isset($schema_source[$source_key]) && $schema_source[$source_key] === 'manual'){
     2145                    $form[$form_key] = isset($saved_form[$form_key]) ? $saved_form[$form_key] : '';
     2146                    if($source_key === 'image'){
     2147                        $form['shareImageId'] = isset($saved_form['shareImageId']) ? $saved_form['shareImageId'] : '';
     2148                    }
     2149                }
     2150            }
     2151            return $form;
     2152        }
     2153        private function baiduseo_geo_verify_content($value){
     2154            $value = trim((string)$value);
     2155            if($value === '') return '';
     2156            $value = sanitize_text_field(html_entity_decode($value, ENT_QUOTES, get_bloginfo('charset')));
     2157            if($value === '' || strlen($value) > 256) return '';
     2158            if(!preg_match('/^[A-Za-z0-9._:-]+$/', $value)) return '';
     2159            return $value;
     2160        }
     2161        private function baiduseo_geo_webmaster_verify_form(){
     2162            $baiduseo_zz = baiduseo_set::baiduseo_get_option(baiduseo_kp::baiduseo_wzt_log(), 'baiduseo_zz');
     2163            if(empty($baiduseo_zz)) $baiduseo_zz = get_option('baiduseo_zz');
     2164            if(!is_array($baiduseo_zz)) return [];
     2165            $form = [];
     2166            $zz_link = isset($baiduseo_zz['zz_link']) ? trim((string)$baiduseo_zz['zz_link']) : '';
     2167            $baidu_token = '';
     2168            if($zz_link !== ''){
     2169                $query = wp_parse_url(str_replace('&#038;', '&', $zz_link), PHP_URL_QUERY);
     2170                if($query){
     2171                    parse_str($query, $params);
     2172                    $baidu_token = isset($params['token']) ? $this->baiduseo_geo_verify_content($params['token']) : '';
     2173                }
     2174            }
     2175            $form['baiduVerify'] = $baidu_token;
     2176            $form['bingVerify'] = isset($baiduseo_zz['bing_key']) ? $this->baiduseo_geo_verify_content($baiduseo_zz['bing_key']) : '';
     2177            return $form;
     2178        }
     2179        private function baiduseo_geo_home_form(){
     2180            $seo = get_option('seo_init');
     2181            $front_page_id = (int)get_option('page_on_front');
     2182            $title = get_bloginfo('name');
     2183            $keywords = is_array($seo) && isset($seo['keywords']) ? $seo['keywords'] : '';
     2184            $description = is_array($seo) && isset($seo['description']) ? $seo['description'] : '';
     2185            $image = '';
     2186            $author = '';
     2187            $published = '';
     2188            $modified = '';
     2189            if($front_page_id){
     2190                $front_page = get_post($front_page_id);
     2191                if($front_page && $front_page->post_type === 'page'){
     2192                    $title = get_post_field('post_title', $front_page_id, 'raw');
     2193                    $image = $this->baiduseo_get_geo_image($front_page_id);
     2194                    $author = get_the_author_meta('display_name', $front_page->post_author);
     2195                    $published = get_the_date('c', $front_page_id);
     2196                    $modified = get_the_modified_date('c', $front_page_id);
     2197                }
     2198            }
     2199            if(!$image){
     2200                $custom_logo_id = get_theme_mod('custom_logo');
     2201                if($custom_logo_id){
     2202                    $image = wp_get_attachment_image_url($custom_logo_id, 'full');
     2203                }
     2204            }
     2205            return [
     2206                'pageType'=>'website',
     2207                'pageUrl'=>home_url('/'),
     2208                'seoTitle'=>$title ? $title : '',
     2209                'seoKeywords'=>$keywords ? $keywords : '',
     2210                'seoDesc'=>$description ? $description : '',
     2211                'shareImage'=>$image ? $image : '',
     2212                'author'=>$author ? $author : '',
     2213                'published'=>$published ? $published : '',
     2214                'modified'=>$modified ? $modified : ''
     2215            ];
     2216        }
     2217        private function baiduseo_geo_category_form($id){
     2218            $term = $id ? get_term($id) : false;
     2219            if(!$term || is_wp_error($term)){
     2220                return [];
     2221            }
     2222            $seo = get_option('baiduseo_cate_'.$id);
     2223            $keywords = is_array($seo) && isset($seo['keywords']) ? $seo['keywords'] : '';
     2224            $description = is_array($seo) && isset($seo['description']) ? $seo['description'] : '';
     2225            if(!$description){
     2226                $description = term_description($id);
     2227                $description = $description ? wp_strip_all_tags($description) : '';
     2228            }
     2229            return [
     2230                'pageType'=>'category',
     2231                'pageUrl'=>get_term_link($term),
     2232                'seoTitle'=>$term->name,
     2233                'seoKeywords'=>$keywords ? $keywords : '',
     2234                'seoDesc'=>$description ? $description : '',
     2235                'shareImage'=>'',
     2236                'author'=>'',
     2237                'published'=>'',
     2238                'modified'=>''
     2239            ];
     2240        }
     2241        private function baiduseo_geo_page_form($id){
     2242            $post = $id ? get_post($id) : false;
     2243            if(!$post || $post->post_type !== 'page'){
     2244                return [];
     2245            }
     2246            $seo = get_post_meta($id, 'baiduseo_page', true);
     2247            $keywords = is_array($seo) && isset($seo['keywords']) ? $seo['keywords'] : '';
     2248            $description = is_array($seo) && isset($seo['description']) ? $seo['description'] : '';
     2249            if(!$description && $post->post_excerpt){
     2250                $description = wp_strip_all_tags($post->post_excerpt);
     2251            }
     2252            return [
     2253                'pageType'=>'page',
     2254                'pageUrl'=>get_permalink($id),
     2255                'seoTitle'=>get_post_field('post_title', $id, 'raw'),
     2256                'seoKeywords'=>$keywords ? $keywords : '',
     2257                'seoDesc'=>$description ? $description : '',
     2258                'shareImage'=>$this->baiduseo_get_geo_image($id),
     2259                'author'=>get_the_author_meta('display_name', $post->post_author),
     2260                'published'=>get_the_date('c', $id),
     2261                'modified'=>get_the_modified_date('c', $id)
     2262            ];
     2263        }
     2264        private function baiduseo_geo_apply_form($config, $target_form){
     2265            $has_config = is_array($config);
     2266            $config = is_array($config) ? $config : [];
     2267            $saved_form = isset($config['form']) && is_array($config['form']) ? $config['form'] : [];
     2268            $filtered_form = $this->baiduseo_geo_filter_saved_form($saved_form);
     2269            $base_form = $has_config ? $filtered_form : $target_form;
     2270            $config['form'] = array_merge($this->baiduseo_geo_default_form(), $base_form, $this->baiduseo_geo_webmaster_verify_form());
     2271            return $config;
     2272        }
     2273        public function baiduseo_get_geo(){
     2274            $this->check_permission('manage_options');
     2275                $type = isset($_POST['type']) ? sanitize_text_field(wp_unslash($_POST['type'])) : 'home';
     2276                $id = isset($_POST['id']) ? (int)$_POST['id'] : 0;
     2277                $reload = isset($_POST['reload']) ? (int)$_POST['reload'] : 0;
     2278                if($type === 'page' && $id && $id === (int)get_option('page_on_front')){
     2279                    $type = 'home';
     2280                }
     2281                if($type === 'home'){
     2282                    $data = get_option('baiduseo_geo_home');
     2283                    $target_form = $this->baiduseo_geo_home_form();
     2284                }elseif($type === 'category'){
     2285                    $data = $id ? get_option('baiduseo_geo_cate_'.$id) : [];
     2286                    $target_form = $this->baiduseo_geo_category_form($id);
     2287                }elseif($type === 'page'){
     2288                    $data = $id ? get_post_meta($id,'baiduseo_geo_page',true) : [];
     2289                    $target_form = $this->baiduseo_geo_page_form($id);
     2290                }else{
     2291                    echo wp_json_encode(['code'=>0,'msg'=>'类型错误']);exit;
     2292                }
     2293                if($reload){
     2294                    $data = false;
     2295                }
     2296                $data = $this->baiduseo_geo_apply_form($data, $target_form);
     2297                echo wp_json_encode(['code'=>1,'data'=>$data ? $data : []]);exit;
    16892298           
    16902299        }
    … …  
    17012310        public function baiduseo_get_seo(){
    17022311            $this->check_permission('manage_options');
     2312                $llms_mode = isset($_POST['llms_mode']) ? sanitize_text_field(wp_unslash($_POST['llms_mode'])) : '';
     2313                if($llms_mode === 'reset'){
     2314                    delete_option('seo_llms_builder');
     2315                }
    17032316               
    17042317                $seo_init = get_option('seo_init');
    … …  
    17112324                $seo_alt_auto['alt'] = get_option('seo_alt_auto');
    17122325                $rootbot['robots'] = get_option('seo_robots_sc');
     2326                $llms['llms'] = get_option('seo_llms_txt');
     2327                if(!is_array($llms['llms'])){
     2328                    $llms['llms'] = [
     2329                        'content'=>'',
     2330                        'time'=>'',
     2331                        'url'=>trim(get_option('home'),'/').'/llms.txt',
     2332                        'open'=>1,
     2333                    ];
     2334                }
     2335                $llms['llms']['open'] = isset($llms['llms']['open']) ? (int)$llms['llms']['open'] : 1;
     2336                $llms['llms']['builder'] = $this->baiduseo_llms_builder();
    17132337                $silian['silian'] = get_option('seo_baidu_silian');
    17142338                $blogname['biaoti'] = get_option('blogname');
    17152339                $blogdescription['fubiaoti'] = get_option('blogdescription');
    1716                 $data = array_merge($seo_init,$seo_301_404_url,$sitemap,$seo_alt_auto,$rootbot,$silian,$blogname,$blogdescription);
     2340                $data = array_merge($seo_init,$seo_301_404_url,$sitemap,$seo_alt_auto,$rootbot,$llms,$silian,$blogname,$blogdescription);
    17172341                echo wp_json_encode(['code'=>1,'data'=>$data]);exit;
    17182342           
  • baiduseo/trunk/inc/admin/post.php

    r3497150 r3625549  
    88            add_action('wp_ajax_'.$k, [$this,'baiduseo_ajax_post']);
    99        }
     10        add_action('wp_ajax_baiduseo_recommend_post_tags', [$this, 'baiduseo_recommend_post_tags']);
     11        add_action('wp_ajax_baiduseo_ignore_post_tag', [$this, 'baiduseo_ignore_post_tag']);
    1012    }
    1113    public function baiduseo_get_option(){
    … …  
    1315            'baiduseo_zhizhu'=>1,
    1416            'baiduseo_seo'=>1,
     17            'baiduseo_geo'=>1,
    1518            'baiduseo_zz'=>1,
    1619            'baiduseo_rank'=>1, 
    … …  
    185188        echo wp_json_encode(['code'=>0]);exit;
    186189    }
     190    public function baiduseo_recommend_post_tags(){
     191        if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'baiduseo')) {
     192            echo wp_json_encode(['code'=>0]);exit;
     193        }
     194        $post_id = isset($_POST['post_id']) ? (int)$_POST['post_id'] : 0;
     195        if (!$post_id || !current_user_can('edit_post', $post_id)) {
     196            echo wp_json_encode(['code'=>0]);exit;
     197        }
     198        $baiduseo_tag = get_option('baiduseo_tag');
     199        if (!isset($baiduseo_tag['auto']) || (int)$baiduseo_tag['auto'] !== 0) {
     200            echo wp_json_encode(['code'=>1,'data'=>[]]);exit;
     201        }
     202        $post = get_post($post_id);
     203        if (!$post) {
     204            echo wp_json_encode(['code'=>0]);exit;
     205        }
     206        $content_override = [];
     207        if (isset($_POST['title'])) {
     208            $content_override['title'] = sanitize_text_field(wp_unslash($_POST['title']));
     209        }
     210        if (isset($_POST['content'])) {
     211            $content_override['content'] = wp_kses_post(wp_unslash($_POST['content']));
     212        }
     213        echo wp_json_encode([
     214            'code'=>1,
     215            'data'=>baiduseo_common::baiduseo_get_post_tag_recommendations($post_id, $this->baiduseo_get_ignored_post_tags($post_id), $content_override)
     216        ]);exit;
     217    }
     218    public function baiduseo_ignore_post_tag(){
     219        if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'baiduseo')) {
     220            echo wp_json_encode(['code'=>0]);exit;
     221        }
     222        $post_id = isset($_POST['post_id']) ? (int)$_POST['post_id'] : 0;
     223        $term_id = isset($_POST['term_id']) ? (int)$_POST['term_id'] : 0;
     224        if (!$post_id || !$term_id || !current_user_can('edit_post', $post_id)) {
     225            echo wp_json_encode(['code'=>0]);exit;
     226        }
     227        $ignored = $this->baiduseo_get_ignored_post_tags($post_id);
     228        if (!in_array($term_id, $ignored, true)) {
     229            $ignored[] = $term_id;
     230        }
     231        update_post_meta($post_id, '_baiduseo_ignored_recommend_tags', $ignored);
     232        echo wp_json_encode([
     233            'code'=>1,
     234            'data'=>baiduseo_common::baiduseo_get_post_tag_recommendations($post_id, $ignored)
     235        ]);exit;
     236    }
     237    private function baiduseo_get_ignored_post_tags($post_id){
     238        $ignored = get_post_meta($post_id, '_baiduseo_ignored_recommend_tags', true);
     239        return is_array($ignored) ? array_map('intval', $ignored) : [];
     240    }
    187241    public function baiduseo_ajax_post(){
    188242        if (!current_user_can('manage_options')) {
    … …  
    200254        echo wp_json_encode(['code'=>0]);exit;
    201255       
     256    }
     257
     258    private function baiduseo_geo_sanitize_config($config){
     259        $data = json_decode($config, true);
     260        if(!is_array($data)){
     261            return false;
     262        }
     263        $out = [];
     264        if(isset($data['form']) && is_array($data['form'])){
     265            $out['form'] = [];
     266            foreach($data['form'] as $key=>$value){
     267                $key = preg_replace('/[^A-Za-z0-9_]/', '', $key);
     268                $out['form'][$key] = is_string($value) ? sanitize_textarea_field($value) : $value;
     269            }
     270        }
     271        if(isset($data['switchState']) && is_array($data['switchState'])){
     272            $out['switchState'] = [];
     273            foreach($data['switchState'] as $key=>$value){
     274                $key = preg_replace('/[^A-Za-z0-9_]/', '', $key);
     275                $out['switchState'][$key] = $value ? 1 : 0;
     276            }
     277        }
     278        if(isset($data['schemaSource']) && is_array($data['schemaSource'])){
     279            $out['schemaSource'] = [];
     280            foreach($data['schemaSource'] as $key=>$value){
     281                $key = preg_replace('/[^A-Za-z0-9_]/', '', $key);
     282                $value = sanitize_text_field($value);
     283                $out['schemaSource'][$key] = in_array($value, ['auto','manual','none'], true) ? $value : 'auto';
     284            }
     285        }
     286        if(isset($data['activeSchemaMode'])){
     287            $active_schema_mode = sanitize_text_field($data['activeSchemaMode']);
     288            $out['activeSchemaMode'] = in_array($active_schema_mode, ['builder','custom'], true) ? $active_schema_mode : 'builder';
     289        }
     290        return $out;
     291    }
     292
     293    private function baiduseo_geo_source_form_keys(){
     294        return ['pageType','pageUrl','seoTitle','seoKeywords','seoDesc','shareImage','shareImageId','author','published','modified'];
     295    }
     296
     297    private function baiduseo_geo_schema_source_form_map(){
     298        return [
     299            'title'=>'seoTitle',
     300            'desc'=>'seoDesc',
     301            'url'=>'pageUrl',
     302            'image'=>'shareImage',
     303            'author'=>'author',
     304            'published'=>'published',
     305            'modified'=>'modified'
     306        ];
     307    }
     308
     309    private function baiduseo_geo_strip_source_form($config){
     310        return $config;
     311    }
     312
     313    private function baiduseo_geo_form_text($config, $key){
     314        return isset($config['form'][$key]) ? sanitize_text_field($config['form'][$key]) : '';
     315    }
     316
     317    private function baiduseo_geo_form_textarea($config, $key){
     318        return isset($config['form'][$key]) ? sanitize_textarea_field($config['form'][$key]) : '';
     319    }
     320
     321    private function baiduseo_geo_sync_source_form($type, $id, $config){
     322        if(!isset($config['form']) || !is_array($config['form'])) return;
     323        $title = $this->baiduseo_geo_form_text($config, 'seoTitle');
     324        $keywords = $this->baiduseo_geo_form_text($config, 'seoKeywords');
     325        $description = $this->baiduseo_geo_form_textarea($config, 'seoDesc');
     326        if($type === 'home'){
     327            update_option('seo_init',['keywords'=>$keywords,'description'=>$description]);
     328            $front_page_id = (int)get_option('page_on_front');
     329            if($front_page_id && $title !== ''){
     330                wp_update_post(['ID'=>$front_page_id,'post_title'=>$title]);
     331            }
     332        }elseif($type === 'category' && $id){
     333            update_option('baiduseo_cate_'.$id,['keywords'=>$keywords,'description'=>$description]);
     334            if($title !== ''){
     335                wp_update_term($id, 'category', ['name'=>$title]);
     336            }
     337        }elseif($type === 'page' && $id){
     338            update_post_meta($id,'baiduseo_page',['keywords'=>$keywords,'description'=>$description]);
     339            if($title !== ''){
     340                wp_update_post(['ID'=>$id,'post_title'=>$title]);
     341            }
     342        }
     343    }
     344
     345    public function baiduseo_geo($log,$log1,$log2){
     346        if(isset($_POST['nonce']) && isset($_POST['action']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])),'baiduseo')){
     347            $type = isset($_POST['type']) ? sanitize_text_field(wp_unslash($_POST['type'])) : 'home';
     348            $id = isset($_POST['id']) ? (int)$_POST['id'] : 0;
     349            if($type === 'page' && $id && $id === (int)get_option('page_on_front')){
     350                $type = 'home';
     351            }
     352            $config_raw = isset($_POST['config']) ? sanitize_textarea_field(wp_unslash($_POST['config'])) : '';
     353            $config = $config_raw !== '' ? $this->baiduseo_geo_sanitize_config($config_raw) : false;
     354            if(!$config){
     355                echo wp_json_encode(['msg'=>'配置格式错误','code'=>0]);exit;
     356            }
     357            $this->baiduseo_geo_sync_source_form($type, $id, $config);
     358            $config = $this->baiduseo_geo_strip_source_form($config);
     359            if($type === 'home'){
     360                update_option('baiduseo_geo_home',$config);
     361            }elseif($type === 'category'){
     362                if(!$id){
     363                    echo wp_json_encode(['msg'=>'请选择分类','code'=>0]);exit;
     364                }
     365                update_option('baiduseo_geo_cate_'.$id,$config);
     366            }elseif($type === 'page'){
     367                if(!$id){
     368                    echo wp_json_encode(['msg'=>'请选择页面','code'=>0]);exit;
     369                }
     370                update_post_meta($id,'baiduseo_geo_page',$config);
     371            }else{
     372                echo wp_json_encode(['msg'=>'类型错误','code'=>0]);exit;
     373            }
     374            echo wp_json_encode(['msg'=>'保存成功','code'=>1]);exit;
     375        }
     376        echo wp_json_encode(['msg'=>'保存失败','code'=>0]);exit;
    202377    }
    203378   
    … …  
    19462121    $need = $tag_num - $existing;
    19472122    $added = 0;
    1948     $insert_sql = [];
     2123    $insert_rows = [];
    19492124
    19502125    foreach ($tags as $tag) {
    … …  
    19642139        if ($has) continue;
    19652140
    1966         $insert_sql[] = $wpdb->prepare("(%d,%d)", $post_id, $tag['term_taxonomy_id']);
     2141        $insert_rows[] = [
     2142            'object_id' => (int)$post_id,
     2143            'term_taxonomy_id' => (int)$tag['term_taxonomy_id'],
     2144        ];
    19672145        $added++;
    19682146    }
    19692147
    19702148    // 批量插入(仅1次数据库交互)
    1971     if (!empty($insert_sql)) {
    1972         $wpdb->query(
    1973             "INSERT INTO {$wpdb->prefix}term_relationships (object_id, term_taxonomy_id)
    1974              VALUES " . implode(',', $insert_sql)
    1975         );
     2149    if (!empty($insert_rows)) {
     2150        foreach ($insert_rows as $insert_row) {
     2151            $wpdb->insert(
     2152                $wpdb->prefix . 'term_relationships',
     2153                $insert_row,
     2154                ['%d', '%d']
     2155            );
     2156        }
    19762157    }
    19772158}
    … …  
    23922573        // 处理多文章类型参数(逗号分割)
    23932574        //判断是标签还是内敛
    2394         if(!isset($_POST['name']) && isset($_POST['content']) && is_string($_POST['content']) &&  strpos($_POST['content'],',') === false){
     2575        $tag_content = isset($_POST['content']) ? sanitize_textarea_field(wp_unslash($_POST['content'])) : '';
     2576        if(!isset($_POST['name']) && $tag_content !== '' && strpos($tag_content,',') === false){
    23952577            echo wp_json_encode(['msg' => '格式不正确,请更正后尝试。', 'code' => 0]);
    23962578                    exit;
    … …  
    24292611        // 原有验证逻辑
    24302612       
    2431             if (isset($_POST['content']) && sanitize_textarea_field(wp_unslash($_POST['content']))) {
     2613            if ($tag_content !== '') {
    24322614                $baiduseo_wzt_log = baiduseo_kp::baiduseo_wzt_log();
    24332615                if (!$baiduseo_wzt_log) {
    … …  
    24402622                    exit;
    24412623                }
    2442                 $content = explode("\n", sanitize_textarea_field(wp_unslash($_POST['content'])));
     2624                $content = explode("\n", $tag_content);
    24432625                $baiduseo_tag_manage = get_option('baiduseo_tag');
    24442626                if (!empty($content)) {
    … …  
    24872669                                        $taxonomy   // 动态标签分类法
    24882670                                    );
    2489    
     2671
    24902672                                    // 处理标签创建失败(如重复创建的错误)
    24912673                                    if (is_wp_error($term)) {
    24922674                                        continue;
    24932675                                    }
    2494    
     2676
    24952677                                    $term_id = $term['term_id'];
    24962678                                    $term_taxonomy_id = $term['term_taxonomy_id'];
    2497    
     2679
    24982680                                    // 处理标签别名(slug)
    24992681                                    if (!isset($baiduseo_tag_manage['taglink']) || !$baiduseo_tag_manage['taglink']) {
    … …  
    25062688                                        );
    25072689                                    }
    2508    
    2509                                     // 自动关联标签到对应文章类型的文章
    2510                                     if ($baiduseo_tag_manage && isset($baiduseo_tag_manage['auto']) && $baiduseo_tag_manage['auto']) {
    2511                                         // 查询当前文章类型的已发布文章
    2512                                         $articles = $wpdb->get_results(
    2513                                             $wpdb->prepare(
    2514                                                 'select * from ' . $wpdb->prefix . 'posts
    2515                                                 where post_status = "publish" and post_type = %s
    2516                                                 order by ID desc limit 1000',
    2517                                                 $post_type
    2518                                             ),
    2519                                             ARRAY_A
    2520                                         );
    2521    
    2522                                         if (!isset($baiduseo_tag_manage['num']) || !$baiduseo_tag_manage['num'] || $baiduseo_tag_manage['num'] == 11) {
    2523                                             foreach ($articles as $article) {
    2524                                                 $this->bind_tag_to_article($article, $term_taxonomy_id, $taxonomy, $tag[0], $baiduseo_tag_manage, $wpdb);
     2690                                } else {
     2691                                    $term_id = $terms[0]['term_id'];
     2692                                    $term_taxonomy = $wpdb->get_results($wpdb->prepare(
     2693                                        'select term_taxonomy_id from ' . $wpdb->prefix . 'term_taxonomy where taxonomy = %s and term_id = %d',
     2694                                        $taxonomy,
     2695                                        $term_id
     2696                                    ), ARRAY_A);
     2697                                    if (empty($term_taxonomy)) {
     2698                                        continue;
     2699                                    }
     2700                                    $term_taxonomy_id = $term_taxonomy[0]['term_taxonomy_id'];
     2701                                }
     2702
     2703                                // 自动模式下,无论标签是新建还是已存在,都执行关联逻辑
     2704                                if ($baiduseo_tag_manage && isset($baiduseo_tag_manage['auto']) && $baiduseo_tag_manage['auto']) {
     2705                                    // 查询当前文章类型的已发布文章
     2706                                    $articles = $wpdb->get_results(
     2707                                        $wpdb->prepare(
     2708                                            'select * from ' . $wpdb->prefix . 'posts
     2709                                            where post_status = "publish" and post_type = %s
     2710                                            order by ID desc limit 1000',
     2711                                            $post_type
     2712                                        ),
     2713                                        ARRAY_A
     2714                                    );
     2715
     2716                                    if (!isset($baiduseo_tag_manage['num']) || !$baiduseo_tag_manage['num'] || $baiduseo_tag_manage['num'] == 11) {
     2717                                        foreach ($articles as $article) {
     2718                                            $this->bind_tag_to_article($article, $term_taxonomy_id, $taxonomy, $tag[0], $baiduseo_tag_manage, $wpdb);
     2719                                        }
     2720                                    } else {
     2721                                        foreach ($articles as $article) {
     2722                                            // 检查文章在当前分类法下的已有标签数量
     2723                                            $tag_count = $wpdb->query($wpdb->prepare(
     2724                                                'select * from ' . $wpdb->prefix . 'term_relationships as a
     2725                                                left join ' . $wpdb->prefix . 'term_taxonomy as b on a.term_taxonomy_id = b.term_taxonomy_id
     2726                                                where b.taxonomy = %s and a.object_id = %d',
     2727                                                $taxonomy,
     2728                                                $article['ID']
     2729                                            ));
     2730                                            if ($tag_count >= $baiduseo_tag_manage['num']) {
     2731                                                break;
    25252732                                            }
    2526                                         } else {
    2527                                             foreach ($articles as $article) {
    2528                                                 // 检查文章在当前分类法下的已有标签数量
    2529                                                 $tag_count = $wpdb->query($wpdb->prepare(
    2530                                                     'select * from ' . $wpdb->prefix . 'term_relationships as a
    2531                                                     left join ' . $wpdb->prefix . 'term_taxonomy as b on a.term_taxonomy_id = b.term_taxonomy_id
    2532                                                     where b.taxonomy = %s and a.object_id = %d',
    2533                                                     $taxonomy,
    2534                                                     $article['ID']
    2535                                                 ));
    2536                                                 if ($tag_count >= $baiduseo_tag_manage['num']) {
    2537                                                     break;
    2538                                                 }
    2539                                                 $this->bind_tag_to_article($article, $term_taxonomy_id, $taxonomy, $tag[0], $baiduseo_tag_manage, $wpdb);
    2540                                             }
     2733                                            $this->bind_tag_to_article($article, $term_taxonomy_id, $taxonomy, $tag[0], $baiduseo_tag_manage, $wpdb);
    25412734                                        }
    25422735                                    }
    … …  
    25682761        // 匹配文章内容中的关键词,成功则关联标签
    25692762        if (preg_match($pattern, $article['post_content'], $matches)) {
     2763            $exists = $wpdb->get_var($wpdb->prepare(
     2764                'select object_id from ' . $wpdb->prefix . 'term_relationships where object_id = %d and term_taxonomy_id = %d',
     2765                $article['ID'],
     2766                $term_taxonomy_id
     2767            ));
     2768            if ($exists) {
     2769                return;
     2770            }
     2771
    25702772            // 关联标签到文章
    25712773            $wpdb->insert(
    … …  
    30193221                baiduseo_seo::robots(sanitize_textarea_field(wp_unslash($_POST['robots'])));
    30203222            }
     3223            $llms_open = isset($_POST['llms_open']) ? (int)$_POST['llms_open'] : 1;
     3224            if(isset($_POST['llms_open'])){
     3225                baiduseo_seo::llms_open($llms_open);
     3226            }
     3227            if($llms_open && isset($_POST['llms_txt'])){
     3228                baiduseo_seo::llms_txt(sanitize_textarea_field(wp_unslash($_POST['llms_txt'])));
     3229            }
     3230            if(isset($_POST['llms_builder'])){
     3231                baiduseo_seo::llms_builder(sanitize_textarea_field(wp_unslash($_POST['llms_builder'])));
     3232            }
    30213233           
    30223234            $sitemap = get_option('seo_baidu_sitemap');
  • baiduseo/trunk/inc/admin/seo.php

    r3497150 r3625549  
    475475        }else{
    476476            add_option('seo_robots_sc',$robot);
     477        }
     478    }
     479    public static function llms_txt($content){
     480        $url =trim(get_option('home'),'/').'/llms.txt';
     481        $currnetTime= current_time( 'Y/m/d H:i:s');
     482        $llms = [
     483            'content'=>$content,
     484            'time'=>$currnetTime,
     485            'url'=>$url,
     486            'open'=>1,
     487        ];
     488         WP_Filesystem();
     489        global $wp_filesystem;
     490
     491        $wp_filesystem->put_contents( ABSPATH .'/llms.txt',"\xEF\xBB\xBF".$content);
     492        $llms_txt = get_option('seo_llms_txt');
     493        if($llms_txt!==false){
     494           update_option('seo_llms_txt',$llms);
     495        }else{
     496            add_option('seo_llms_txt',$llms);
     497        }
     498    }
     499    public static function llms_open($open){
     500        $url =trim(get_option('home'),'/').'/llms.txt';
     501        $llms_txt = get_option('seo_llms_txt');
     502        if(!is_array($llms_txt)){
     503            $llms_txt = [
     504                'content'=>'',
     505                'time'=>'',
     506                'url'=>$url,
     507            ];
     508        }
     509        $llms_txt['open'] = $open ? 1 : 0;
     510        $llms_txt['url'] = $url;
     511        if(!$open){
     512            $llms_txt['content'] = '';
     513            $llms_txt['time'] = '';
     514            WP_Filesystem();
     515            global $wp_filesystem;
     516            if($wp_filesystem->exists(ABSPATH .'/llms.txt')){
     517                $wp_filesystem->delete(ABSPATH .'/llms.txt');
     518            }
     519        }
     520        if(get_option('seo_llms_txt')!==false){
     521           update_option('seo_llms_txt',$llms_txt);
     522        }else{
     523            add_option('seo_llms_txt',$llms_txt);
     524        }
     525    }
     526    private static function llms_clean_entry($item,$keep_identity = false){
     527        if(!is_array($item)){
     528            return [];
     529        }
     530        $entry = [
     531            'id'=>isset($item['id']) ? sanitize_text_field($item['id']) : '',
     532            'desc'=>isset($item['desc']) ? sanitize_textarea_field($item['desc']) : '',
     533            'checked'=>!empty($item['checked']),
     534        ];
     535        if($keep_identity){
     536            $entry['title'] = isset($item['title']) ? sanitize_text_field($item['title']) : '';
     537            $entry['url'] = isset($item['url']) ? esc_url_raw($item['url']) : '';
     538        }
     539        return $entry;
     540    }
     541    private static function llms_clean_entries($items,$keep_identity = false){
     542        $data = [];
     543        if(!is_array($items)){
     544            return $data;
     545        }
     546        foreach($items as $item){
     547            $entry = self::llms_clean_entry($item,$keep_identity);
     548            if($entry){
     549                $data[] = $entry;
     550            }
     551        }
     552        return $data;
     553    }
     554    private static function llms_clean_custom_modules($items){
     555        $data = [];
     556        if(!is_array($items)){
     557            return $data;
     558        }
     559        foreach($items as $item){
     560            if(!is_array($item)){
     561                continue;
     562            }
     563            $data[] = [
     564                'id'=>isset($item['id']) ? sanitize_text_field($item['id']) : '',
     565                'title'=>isset($item['title']) ? sanitize_text_field($item['title']) : '',
     566                'content'=>isset($item['content']) ? sanitize_textarea_field($item['content']) : '',
     567                'enabled'=>!empty($item['enabled']),
     568            ];
     569        }
     570        return $data;
     571    }
     572    private static function llms_clean_entry_groups($items){
     573        $data = [];
     574        if(!is_array($items)){
     575            return $data;
     576        }
     577        foreach($items as $item){
     578            if(!is_array($item)){
     579                continue;
     580            }
     581            $data[] = [
     582                'id'=>isset($item['id']) ? sanitize_text_field($item['id']) : '',
     583                'title'=>isset($item['title']) && sanitize_text_field($item['title']) ? sanitize_text_field($item['title']) : '自定义入口',
     584                'enabled'=>!empty($item['enabled']),
     585                'links'=>self::llms_clean_entries(isset($item['links']) ? $item['links'] : [],true),
     586            ];
     587        }
     588        return $data;
     589    }
     590    public static function llms_builder($builder){
     591        if(is_string($builder)){
     592            $builder = json_decode(sanitize_textarea_field($builder),true);
     593        }
     594        if(!is_array($builder)){
     595            $builder = [];
     596        }
     597        $form = isset($builder['form']) && is_array($builder['form']) ? $builder['form'] : [];
     598        $data = [
     599            'form'=>[
     600                'siteName'=>isset($form['siteName']) ? sanitize_text_field($form['siteName']) : '',
     601                'language'=>isset($form['language']) ? sanitize_text_field($form['language']) : 'zh-CN',
     602                'sitemapUrl'=>isset($form['sitemapUrl']) ? esc_url_raw($form['sitemapUrl']) : '',
     603                'siteIntro'=>isset($form['siteIntro']) ? sanitize_textarea_field($form['siteIntro']) : '',
     604                'businessScope'=>isset($form['businessScope']) ? sanitize_textarea_field($form['businessScope']) : '',
     605                'targetUsers'=>isset($form['targetUsers']) ? sanitize_textarea_field($form['targetUsers']) : '',
     606                'serviceArea'=>isset($form['serviceArea']) ? sanitize_textarea_field($form['serviceArea']) : '',
     607            ],
     608            'pages'=>self::llms_clean_entries(isset($builder['pages']) ? $builder['pages'] : []),
     609            'posts'=>self::llms_clean_entries(isset($builder['posts']) ? $builder['posts'] : []),
     610            'categories'=>self::llms_clean_entries(isset($builder['categories']) ? $builder['categories'] : []),
     611            'manualLinks'=>self::llms_clean_entries(isset($builder['manualLinks']) ? $builder['manualLinks'] : [],true),
     612            'entryGroups'=>self::llms_clean_entry_groups(isset($builder['entryGroups']) ? $builder['entryGroups'] : []),
     613            'customModules'=>self::llms_clean_custom_modules(isset($builder['customModules']) ? $builder['customModules'] : []),
     614        ];
     615        $llms_builder = get_option('seo_llms_builder');
     616        if($llms_builder!==false){
     617           update_option('seo_llms_builder',$data);
     618        }else{
     619            add_option('seo_llms_builder',$data);
    477620        }
    478621    }
  • baiduseo/trunk/inc/admin/zhizhu.php

    r3497150 r3625549  
    187187        );
    188188   
    189         $data_array['ip'] = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? ($_SERVER['REMOTE_ADDR'] ?? '');
     189        $forwarded_for = isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_X_FORWARDED_FOR'])) : '';
     190        $remote_addr = isset($_SERVER['REMOTE_ADDR']) ? sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR'])) : '';
     191        $data_array['ip'] = $forwarded_for !== '' ? $forwarded_for : $remote_addr;
    190192        $data_array['time'] = current_time('Y/m/d H:i:s');
    191193        $data_array['type'] = http_response_code();
    … …  
    219221        );
    220222   
    221         $data_array['ip'] = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? ($_SERVER['REMOTE_ADDR'] ?$_SERVER['REMOTE_ADDR']: '');
     223        $forwarded_for = isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_X_FORWARDED_FOR'])) : '';
     224        $remote_addr = isset($_SERVER['REMOTE_ADDR']) ? sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR'])) : '';
     225        $data_array['ip'] = $forwarded_for !== '' ? $forwarded_for : $remote_addr;
    222226        $data_array['time'] = current_time('Y/m/d H:i:s');
    223227        $data_array['type'] = http_response_code();
    … …  
    538542        }
    539543        $start = ($page-1)*$limit;
     544        // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter -- SQL clauses above are fixed branches; user values are prepared via placeholders.
    540545        $count = $wpdb->query($wpdb->prepare($sql,$search,$sta,$end,$type,$type2));
    541546        if($orders==1){
    … …  
    546551            $sql .= 'order by time desc limit %d,%d ';
    547552        }
     553        // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,PluginCheck.Security.DirectDB.UnescapedDBParameter -- SQL clauses above are fixed branches; user values are prepared via placeholders.
    548554        $zhizhu = $wpdb->get_results($wpdb->prepare($sql,$search,$sta,$end,$type,$type2,$start,$limit),ARRAY_A);
    549555        $baiduseo_zhizhu = get_option('baiduseo_zhizhu');
  • baiduseo/trunk/inc/common/index.php

    r3501273 r3625549  
    7878        if (isset($_POST['nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'baiduseo_front')) {
    7979           
    80             $userAgent = isset($_SERVER['HTTP_USER_AGENT']) ? sanitize_text_field(strtolower(wp_unslash($_SERVER['HTTP_USER_AGENT']))) : '';
     80            $userAgent = '';
     81            if (isset($_SERVER['HTTP_USER_AGENT'])) {
     82                $userAgent = sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT']));
     83                $userAgent = strtolower($userAgent);
     84            }
    8185           
    8286            if (empty($userAgent)) {
    … …  
    350354        var now = Date.now();
    351355
    352         var saved = localStorage.getItem(STORAGE_KEY);
    353         if (saved) {
    354             try {
    355                 var parsed = JSON.parse(saved);
    356                 if (parsed && parsed.type && parsed.timestamp) {
    357                     var ageDays = (now - parsed.timestamp) / (86400000);
    358                     if (ageDays < EXPIRY_DAYS) {
    359                         return parsed;
    360                     } else {
    361                         localStorage.removeItem(STORAGE_KEY);
    362                     }
    363                 }
    364             } catch (e) {
    365                 localStorage.removeItem(STORAGE_KEY);
    366             }
    367         }
    368 
    369356        var adParams = ['utm_medium', 'utm_source', 'utm_campaign', 'gclid', 'fbclid', 'ttclid', 'msclkid'];
    370357        var searchEngines = ['google.', 'bing.', 'yahoo.', 'baidu.', 'sogou.', 'so.com', 'duckduckgo.', 'yandex.', 'sm.cn', 'ecosia.'];
     358        var aiSources = ['chatgpt.com', 'chat.openai.com', 'openai.com', 'perplexity.ai', 'pplx.ai', 'claude.ai', 'poe.com', 'you.com', 'gemini.google.com', 'bard.google.com', 'copilot.microsoft.com', 'bing.com/chat', 'metaso.cn', 'kimi.moonshot.cn', 'kimi.com', 'doubao.com', 'link.wttur.cn', 'yuanbao.tencent.com', 'ima.qq.com', 'tongyi.aliyun.com', 'qianwen.aliyun.com', 'yiyan.baidu.com', 'chat.baidu.com', 'deepseek.com', 'deepseek.ai', 'chatglm.cn', 'zhipuai.cn', 'coze.cn', 'grok.com', 'x.ai'];
    371359        var traffic = { type: 'direct', referrer: null, detail: '直接访问', timestamp: now };
     360        var baiduseoSource = (params.get('baiduseo_source') || '').toLowerCase();
     361
     362        if (baiduseoSource === 'ai') {
     363            traffic = { type: 'ai', referrer: ref || null, detail: 'AI来源', timestamp: now };
     364        }
    372365
    373366        /* 检测广告 */
    374         for (var i = 0; i < adParams.length; i++) {
    375             if (params.has(adParams[i])) {
    376                 traffic = { type: 'paid', referrer: ref || null, detail: '付费访问', timestamp: now };
    377                 break;
     367        if (traffic.type === 'direct') {
     368            for (var i = 0; i < adParams.length; i++) {
     369                if (params.has(adParams[i])) {
     370                    traffic = { type: 'paid', referrer: ref || null, detail: '付费访问', timestamp: now };
     371                    break;
     372                }
     373            }
     374        }
     375
     376        /* 检测 AI 来源 */
     377        if (traffic.type === 'direct') {
     378            if (ref) {
     379                var aiRefLower = ref.toLowerCase();
     380                for (var aiIndex = 0; aiIndex < aiSources.length; aiIndex++) {
     381                    if (aiRefLower.indexOf(aiSources[aiIndex]) !== -1) {
     382                        traffic = { type: 'ai', referrer: ref, detail: 'AI来源', timestamp: now };
     383                        break;
     384                    }
     385                }
    378386            }
    379387        }
    … …  
    393401        if (traffic.type === 'direct' && ref && ref.indexOf(siteDomain) === -1) {
    394402            traffic = { type: 'referral', referrer: ref, detail: '外部链接', timestamp: now };
     403        }
     404
     405        if (traffic.type === 'direct') {
     406            var saved = localStorage.getItem(STORAGE_KEY);
     407            if (saved) {
     408                try {
     409                    var parsed = JSON.parse(saved);
     410                    if (parsed && parsed.type && parsed.timestamp) {
     411                        var ageDays = (now - parsed.timestamp) / (86400000);
     412                        if (ageDays < EXPIRY_DAYS) {
     413                            return parsed;
     414                        } else {
     415                            localStorage.removeItem(STORAGE_KEY);
     416                        }
     417                    }
     418                } catch (e) {
     419                    localStorage.removeItem(STORAGE_KEY);
     420                }
     421            }
    395422        }
    396423
    … …  
    409436            $safe_session = '';
    410437            if (isset($_SESSION['baiduseo_liulan'])) {
    411                 $safe_session = $_SESSION['baiduseo_liulan'];
     438                $safe_session = sanitize_text_field(wp_unslash($_SESSION['baiduseo_liulan']));
    412439            }
    413440            echo json_encode($safe_session);
    … …  
    939966            }
    940967        }
     968        $this->baiduseo_geo_output();
     969    }
     970    private function baiduseo_geo_current_config()
     971    {
     972        if (is_front_page() || is_home()) {
     973            return get_option('baiduseo_geo_home');
     974        }
     975        if (is_category()) {
     976            $term_id = get_queried_object_id();
     977            return $term_id ? get_option('baiduseo_geo_cate_' . $term_id) : [];
     978        }
     979        if (is_page()) {
     980            global $post;
     981            return isset($post->ID) ? get_post_meta($post->ID, 'baiduseo_geo_page', true) : [];
     982        }
     983        return [];
     984    }
     985    private function baiduseo_geo_source_form()
     986    {
     987        if (is_front_page() || is_home()) {
     988            $seo = get_option('seo_init');
     989            $front_page_id = (int)get_option('page_on_front');
     990            $title = get_bloginfo('name');
     991            $image = '';
     992            $author = '';
     993            $published = '';
     994            $modified = '';
     995            if ($front_page_id) {
     996                $front_page = get_post($front_page_id);
     997                if ($front_page && $front_page->post_type === 'page') {
     998                    $title = get_post_field('post_title', $front_page_id, 'raw');
     999                    $image = get_the_post_thumbnail_url($front_page_id, 'full');
     1000                    $author = get_the_author_meta('display_name', $front_page->post_author);
     1001                    $published = get_the_date('c', $front_page_id);
     1002                    $modified = get_the_modified_date('c', $front_page_id);
     1003                }
     1004            }
     1005            if (!$image) {
     1006                $custom_logo_id = get_theme_mod('custom_logo');
     1007                if ($custom_logo_id) $image = wp_get_attachment_image_url($custom_logo_id, 'full');
     1008            }
     1009            return [
     1010                'pageType' => 'website',
     1011                'pageUrl' => home_url('/'),
     1012                'seoTitle' => $title ? $title : '',
     1013                'seoKeywords' => is_array($seo) && isset($seo['keywords']) ? $seo['keywords'] : '',
     1014                'seoDesc' => is_array($seo) && isset($seo['description']) ? $seo['description'] : '',
     1015                'shareImage' => $image ? $image : '',
     1016                'author' => $author ? $author : '',
     1017                'published' => $published ? $published : '',
     1018                'modified' => $modified ? $modified : ''
     1019            ];
     1020        }
     1021        if (is_category()) {
     1022            $term_id = get_queried_object_id();
     1023            $term = $term_id ? get_term($term_id) : false;
     1024            if (!$term || is_wp_error($term)) return [];
     1025            $seo = get_option('baiduseo_cate_' . $term_id);
     1026            $description = is_array($seo) && isset($seo['description']) ? $seo['description'] : '';
     1027            if (!$description) {
     1028                $description = term_description($term_id);
     1029                $description = $description ? wp_strip_all_tags($description) : '';
     1030            }
     1031            return [
     1032                'pageType' => 'category',
     1033                'pageUrl' => get_term_link($term),
     1034                'seoTitle' => $term->name,
     1035                'seoKeywords' => is_array($seo) && isset($seo['keywords']) ? $seo['keywords'] : '',
     1036                'seoDesc' => $description ? $description : '',
     1037                'shareImage' => '',
     1038                'author' => '',
     1039                'published' => '',
     1040                'modified' => ''
     1041            ];
     1042        }
     1043        if (is_page()) {
     1044            global $post;
     1045            if (!isset($post->ID)) return [];
     1046            $seo = get_post_meta($post->ID, 'baiduseo_page', true);
     1047            $description = is_array($seo) && isset($seo['description']) ? $seo['description'] : '';
     1048            if (!$description && $post->post_excerpt) $description = wp_strip_all_tags($post->post_excerpt);
     1049            return [
     1050                'pageType' => 'page',
     1051                'pageUrl' => get_permalink($post->ID),
     1052                'seoTitle' => get_post_field('post_title', $post->ID, 'raw'),
     1053                'seoKeywords' => is_array($seo) && isset($seo['keywords']) ? $seo['keywords'] : '',
     1054                'seoDesc' => $description ? $description : '',
     1055                'shareImage' => get_the_post_thumbnail_url($post->ID, 'full'),
     1056                'author' => get_the_author_meta('display_name', $post->post_author),
     1057                'published' => get_the_date('c', $post->ID),
     1058                'modified' => get_the_modified_date('c', $post->ID)
     1059            ];
     1060        }
     1061        return [];
     1062    }
     1063    private function baiduseo_geo_form_value($form, $key)
     1064    {
     1065        return isset($form[$key]) && is_scalar($form[$key]) ? trim((string)$form[$key]) : '';
     1066    }
     1067    private function baiduseo_geo_switch_open($switch, $key)
     1068    {
     1069        return isset($switch[$key]) && $switch[$key];
     1070    }
     1071    private function baiduseo_geo_meta($name, $value, $type = 'name')
     1072    {
     1073        $value = trim((string)$value);
     1074        if ($value === '') return '';
     1075        $attr = $type === 'property' ? 'property' : 'name';
     1076        return '<meta ' . $attr . '="' . esc_attr($name) . '" content="' . esc_attr($value) . '">' . "\n";
     1077    }
     1078    private function baiduseo_geo_verify_content($value)
     1079    {
     1080        $value = trim((string)$value);
     1081        if ($value === '') return '';
     1082        $value = sanitize_text_field(html_entity_decode($value, ENT_QUOTES, get_bloginfo('charset')));
     1083        if ($value === '' || strlen($value) > 256) return '';
     1084        if (!preg_match('/^[A-Za-z0-9._:-]+$/', $value)) return '';
     1085        return $value;
     1086    }
     1087    private function baiduseo_geo_webmaster_verify_meta()
     1088    {
     1089        $baiduseo_zz = [];
     1090        if (class_exists('baiduseo_set') && class_exists('baiduseo_kp')) {
     1091            $baiduseo_zz = baiduseo_set::baiduseo_get_option(baiduseo_kp::baiduseo_wzt_log(), 'baiduseo_zz');
     1092        }
     1093        if (empty($baiduseo_zz)) $baiduseo_zz = get_option('baiduseo_zz');
     1094        if (!is_array($baiduseo_zz)) return '';
     1095        $html = '';
     1096        $zz_link = isset($baiduseo_zz['zz_link']) ? trim((string)$baiduseo_zz['zz_link']) : '';
     1097        if ($zz_link !== '') {
     1098            $query = wp_parse_url(str_replace('&#038;', '&', $zz_link), PHP_URL_QUERY);
     1099            if ($query) {
     1100                parse_str($query, $params);
     1101                $baidu_token = isset($params['token']) ? $this->baiduseo_geo_verify_content($params['token']) : '';
     1102                $html .= $this->baiduseo_geo_meta('baidu-site-verification', $baidu_token);
     1103            }
     1104        }
     1105        $bing_key = isset($baiduseo_zz['bing_key']) ? $this->baiduseo_geo_verify_content($baiduseo_zz['bing_key']) : '';
     1106        $html .= $this->baiduseo_geo_meta('msvalidate.01', $bing_key);
     1107        return $html;
     1108    }
     1109    private function baiduseo_geo_itemprop($name, $value)
     1110    {
     1111        $value = trim((string)$value);
     1112        if ($value === '') return '';
     1113        return '<meta itemprop="' . esc_attr($name) . '" content="' . esc_attr($value) . '">' . "\n";
     1114    }
     1115    private function baiduseo_geo_same_as($form)
     1116    {
     1117        $same_as = [];
     1118        foreach (['wechat', 'weibo', 'zhihu', 'bili', 'xurl', 'linkedin', 'facebook', 'youtube'] as $key) {
     1119            $value = $this->baiduseo_geo_form_value($form, $key);
     1120            if ($value !== '') $same_as[] = $value;
     1121        }
     1122        return $same_as;
     1123    }
     1124    private function baiduseo_geo_current_value($key, $form)
     1125    {
     1126        $field_map = [
     1127            'title' => 'seoTitle',
     1128            'desc' => 'seoDesc',
     1129            'url' => 'pageUrl',
     1130            'image' => 'shareImage',
     1131            'author' => 'author',
     1132            'published' => 'published',
     1133            'modified' => 'modified'
     1134        ];
     1135        if (!isset($field_map[$key])) return '';
     1136        return $this->baiduseo_geo_form_value($form, $field_map[$key]);
     1137    }
     1138    private function baiduseo_geo_output_value($key, $form, $schema_source, $source_form = [])
     1139    {
     1140        $source = isset($schema_source[$key]) ? $schema_source[$key] : 'auto';
     1141        if ($source === 'none') return '';
     1142        if ($source === 'auto') return $this->baiduseo_geo_current_value($key, $source_form);
     1143        return $this->baiduseo_geo_current_value($key, $form);
     1144    }
     1145    private function baiduseo_geo_schema_type($form)
     1146    {
     1147        $schema_type = $this->baiduseo_geo_form_value($form, 'schemaType');
     1148        $page_type = $this->baiduseo_geo_form_value($form, 'pageType');
     1149        if ($schema_type === 'CollectionPage' || $page_type === 'category') return 'CollectionPage';
     1150        if ($schema_type === 'FAQPage' && $this->baiduseo_geo_faq_items($this->baiduseo_geo_form_value($form, 'faqText'))) return 'FAQPage';
     1151        if ($schema_type === 'Article' || $page_type === 'article') return ['Article', 'BlogPosting'];
     1152        return 'WebPage';
     1153    }
     1154    private function baiduseo_geo_strip_empty($value)
     1155    {
     1156        if (is_array($value)) {
     1157            $out = [];
     1158            foreach ($value as $key => $item) {
     1159                $item = $this->baiduseo_geo_strip_empty($item);
     1160                if ($item !== '' && $item !== null && $item !== []) $out[$key] = $item;
     1161            }
     1162            return $out;
     1163        }
     1164        return $value;
     1165    }
     1166    private function baiduseo_geo_faq_items($faq_text)
     1167    {
     1168        $faq_text = trim((string)$faq_text);
     1169        if ($faq_text === '') return [];
     1170        $items = [];
     1171        $lines = preg_split('/\r\n|\r|\n/', $faq_text);
     1172        foreach ($lines as $line) {
     1173            $line = trim((string)$line);
     1174            if ($line === '' || strpos($line, '|') === false) continue;
     1175            $parts = explode('|', $line, 2);
     1176            $question = trim($parts[0]);
     1177            $answer = isset($parts[1]) ? trim($parts[1]) : '';
     1178            if ($question === '' || $answer === '') continue;
     1179            $items[] = [
     1180                '@type' => 'Question',
     1181                'name' => $question,
     1182                'acceptedAnswer' => [
     1183                    '@type' => 'Answer',
     1184                    'text' => $answer
     1185                ]
     1186            ];
     1187        }
     1188        return $items;
     1189    }
     1190    private function baiduseo_geo_is_article_schema($type)
     1191    {
     1192        return is_array($type) ? in_array('Article', $type, true) : $type === 'Article';
     1193    }
     1194    private function baiduseo_geo_schema($form, $schema_source, $active_schema_mode, $source_form = [])
     1195    {
     1196        if ($active_schema_mode === 'custom') {
     1197            $custom_schema_json = $this->baiduseo_geo_form_value($form, 'customSchemaJson');
     1198            $custom_schema = json_decode($custom_schema_json, true);
     1199            if (!is_array($custom_schema)) return '';
     1200            return '<script type="application/ld+json">' . "\n" . wp_json_encode($custom_schema, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT) . "\n" . '</script>' . "\n";
     1201        }
     1202        $url = $this->baiduseo_geo_output_value('url', $form, $schema_source, $source_form);
     1203        $title = $this->baiduseo_geo_output_value('title', $form, $schema_source, $source_form);
     1204        $desc = $this->baiduseo_geo_output_value('desc', $form, $schema_source, $source_form);
     1205        $image = $this->baiduseo_geo_output_value('image', $form, $schema_source, $source_form);
     1206        $author = $this->baiduseo_geo_output_value('author', $form, $schema_source, $source_form);
     1207        $published = $this->baiduseo_geo_output_value('published', $form, $schema_source, $source_form);
     1208        $modified = $this->baiduseo_geo_output_value('modified', $form, $schema_source, $source_form);
     1209        $schema_type = $this->baiduseo_geo_form_value($form, 'schemaType');
     1210        $company_address = $this->baiduseo_geo_form_value($form, 'companyAddress');
     1211        $org_type = $schema_type === 'LocalBusiness' ? ['Organization', 'LocalBusiness'] : 'Organization';
     1212        $org = [
     1213            '@type' => $org_type,
     1214            '@id' => $url ? $url . '#organization' : '',
     1215            'name' => $this->baiduseo_geo_form_value($form, 'companyName'),
     1216            'url' => $url,
     1217            'logo' => $image,
     1218            'description' => $desc,
     1219            'telephone' => $this->baiduseo_geo_form_value($form, 'phone'),
     1220            'email' => $this->baiduseo_geo_form_value($form, 'email'),
     1221            'address' => $company_address ? [
     1222                '@type' => 'PostalAddress',
     1223                'streetAddress' => $company_address,
     1224                'addressCountry' => 'CN'
     1225            ] : '',
     1226            'sameAs' => $this->baiduseo_geo_same_as($form)
     1227        ];
     1228        $website = [
     1229            '@type' => 'WebSite',
     1230            '@id' => $url ? $url . '#website' : '',
     1231            'url' => $url,
     1232            'name' => $title,
     1233            'publisher' => ['@id' => $url ? $url . '#organization' : '']
     1234        ];
     1235        $page = [
     1236            '@type' => $this->baiduseo_geo_schema_type($form),
     1237            '@id' => $url ? $url . '#webpage' : '',
     1238            'url' => $url,
     1239            'name' => $title,
     1240            'description' => $desc,
     1241            'image' => $image,
     1242            'isPartOf' => ['@id' => $url ? $url . '#website' : '']
     1243        ];
     1244        if ($this->baiduseo_geo_is_article_schema($page['@type'])) {
     1245            $page['headline'] = $title;
     1246            $page['author'] = $author ? ['@type' => 'Person', 'name' => $author] : '';
     1247            $page['datePublished'] = $published;
     1248            $page['dateModified'] = $modified;
     1249            $page['publisher'] = ['@id' => $url ? $url . '#organization' : ''];
     1250            $page['mainEntityOfPage'] = ['@type' => 'WebPage', '@id' => $url];
     1251            $page['keywords'] = $this->baiduseo_geo_form_value($form, 'seoKeywords');
     1252        }
     1253        if ($page['@type'] === 'FAQPage') {
     1254            $page['mainEntity'] = $this->baiduseo_geo_faq_items($this->baiduseo_geo_form_value($form, 'faqText'));
     1255        }
     1256        $graph = [$org, $website, $page];
     1257        if ($schema_type === 'Service') {
     1258            $graph[] = [
     1259                '@type' => 'Service',
     1260                '@id' => $url ? $url . '#service' : '',
     1261                'name' => $this->baiduseo_geo_form_value($form, 'serviceName') ?: $title,
     1262                'description' => $desc,
     1263                'provider' => ['@id' => $url ? $url . '#organization' : ''],
     1264                'areaServed' => $this->baiduseo_geo_form_value($form, 'areaServed'),
     1265                'url' => $url
     1266            ];
     1267        }
     1268        $schema = $this->baiduseo_geo_strip_empty([
     1269            '@context' => 'https://schema.org',
     1270            '@graph' => $graph
     1271        ]);
     1272        return '<script type="application/ld+json">' . "\n" . wp_json_encode($schema, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT) . "\n" . '</script>' . "\n";
     1273    }
     1274    private function baiduseo_geo_output()
     1275    {
     1276        $config = $this->baiduseo_geo_current_config();
     1277        if (empty($config) || !is_array($config)) return;
     1278        $form = isset($config['form']) && is_array($config['form']) ? $config['form'] : [];
     1279        $switch = isset($config['switchState']) && is_array($config['switchState']) ? $config['switchState'] : [];
     1280        $schema_source = isset($config['schemaSource']) && is_array($config['schemaSource']) ? $config['schemaSource'] : [];
     1281        $active_schema_mode = isset($config['activeSchemaMode']) ? $config['activeSchemaMode'] : 'builder';
     1282        $url = $this->baiduseo_geo_output_value('url', $form, $schema_source, $form);
     1283        $title = $this->baiduseo_geo_output_value('title', $form, $schema_source, $form);
     1284        $desc = $this->baiduseo_geo_output_value('desc', $form, $schema_source, $form);
     1285        $image = $this->baiduseo_geo_output_value('image', $form, $schema_source, $form);
     1286        $og_image = $this->baiduseo_geo_form_value($form, 'ogImage');
     1287        $twitter_image = $this->baiduseo_geo_form_value($form, 'twitterImage');
     1288        $html = '';
     1289        if ($this->baiduseo_geo_switch_open($switch, 'socialMaster')) {
     1290            if ($this->baiduseo_geo_switch_open($switch, 'og')) {
     1291                $html .= $this->baiduseo_geo_meta('og:type', $this->baiduseo_geo_form_value($form, 'pageType') === 'article' ? 'article' : 'website', 'property');
     1292                $html .= $this->baiduseo_geo_meta('og:url', $url, 'property');
     1293                $html .= $this->baiduseo_geo_meta('og:title', $title, 'property');
     1294                $html .= $this->baiduseo_geo_meta('og:description', $desc, 'property');
     1295                $html .= $this->baiduseo_geo_meta('og:image', $og_image, 'property');
     1296            }
     1297            if ($this->baiduseo_geo_switch_open($switch, 'twitter')) {
     1298                $html .= '<meta name="twitter:card" content="summary_large_image">' . "\n";
     1299                $html .= $this->baiduseo_geo_meta('twitter:title', $title);
     1300                $html .= $this->baiduseo_geo_meta('twitter:description', $desc);
     1301                $html .= $this->baiduseo_geo_meta('twitter:image', $twitter_image);
     1302            }
     1303            if ($this->baiduseo_geo_switch_open($switch, 'cn')) {
     1304                $html .= $this->baiduseo_geo_itemprop('name', $title);
     1305                $html .= $this->baiduseo_geo_itemprop('description', $desc);
     1306                $html .= $this->baiduseo_geo_itemprop('image', $image);
     1307            }
     1308            if ($this->baiduseo_geo_switch_open($switch, 'verify')) {
     1309                $html .= $this->baiduseo_geo_webmaster_verify_meta();
     1310                $html .= $this->baiduseo_geo_meta('360-site-verification', $this->baiduseo_geo_verify_content($this->baiduseo_geo_form_value($form, 'soVerify')));
     1311                $html .= $this->baiduseo_geo_meta('sogou_site_verification', $this->baiduseo_geo_verify_content($this->baiduseo_geo_form_value($form, 'sogouVerify')));
     1312                $html .= $this->baiduseo_geo_meta('shenma-site-verification', $this->baiduseo_geo_verify_content($this->baiduseo_geo_form_value($form, 'shenmaVerify')));
     1313            }
     1314        }
     1315        if ($this->baiduseo_geo_switch_open($switch, 'schema')) {
     1316            $html .= $this->baiduseo_geo_schema($form, $schema_source, $active_schema_mode, $form);
     1317        }
     1318        // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Meta tags and JSON-LD are assembled by helper methods that escape attributes or encode JSON.
     1319        if ($html) echo $html;
    9411320    }
    9421321    public function BaiduSEO_addlink($content)
    … …  
    14521831    public  function baiduseo_enqueue($hook)
    14531832    {
    1454         if ('toplevel_page_baiduseo' != $hook) return;
    1455         require plugin_dir_path(BAIDUSEO_FILE) . 'inc/admin/assets.php';
    1456         foreach ($baiduseo_assets as $key => $val) {
    1457             if ($val['type'] == 'css') {
    1458                 wp_enqueue_style($val['name'],  plugin_dir_url(BAIDUSEO_FILE) . 'inc/admin/' . $val['url'], array(), BAIDUSEO_VERSION, 'all');
    1459             } elseif ($val['type'] == 'js') {
    1460                 wp_enqueue_script($val['name'], plugin_dir_url(BAIDUSEO_FILE) . 'inc/admin/' . $val['url'], array(), BAIDUSEO_VERSION, true);
    1461             }
    1462         }
    1463         wp_register_script('baiduseo.js', false, array(), BAIDUSEO_VERSION, false);
    1464         wp_enqueue_script('baiduseo.js');
    1465         $url1 = baiduseo_common::baiduseo_url(0);
    1466         $baiduseo_yindao = get_option('baiduseo_yindao');
    1467         $baiduseo_yindao = $baiduseo_yindao ? $baiduseo_yindao : 0;
    1468         $baiduseo_version  = esc_url_raw(admin_url('plugin-install.php?tab=plugin-information&plugin=baiduseo'));
    1469         $baiduseo_indexnow = md5(esc_url(baiduseo_common::baiduseo_url(1)));
    1470         $baiduseo_aiarticle =  esc_url_raw(admin_url('plugin-install.php?tab=plugin-information&plugin=aiarticle'));
    1471         $baiduseo_google = baiduseo_common::baiduseo_url(1) . '/wp-sitemap.xml';
    1472         wp_add_inline_script('baiduseo.js', 'var baiduseo_wztkj_url="' . esc_js(plugins_url('', BAIDUSEO_FILE)) . '/inc/admin",baiduseo_nonce="' . esc_attr(wp_create_nonce('baiduseo')) . '",baiduseo_ajax="' . esc_url(admin_url('admin-ajax.php')) . '",baiduseo_tag ="' . esc_url(admin_url('edit-tags.php?taxonomy=post_tag')) . '",baiduseo_url="' . esc_url($url1) . '",baiduseo_yindao=' . (int)$baiduseo_yindao . ',baiduseo_v="' . esc_url($baiduseo_version) . '",baiduseo_indexnow="' . esc_attr($baiduseo_indexnow) . '",baiduseo_google="' . esc_url($baiduseo_google) . '",baiduseo_aiarticle="'.esc_url($baiduseo_aiarticle).'";', 'before');
     1833        if ('toplevel_page_baiduseo' == $hook) {
     1834            wp_enqueue_media();
     1835            require plugin_dir_path(BAIDUSEO_FILE) . 'inc/admin/assets.php';
     1836            foreach ($baiduseo_assets as $key => $val) {
     1837                if ($val['type'] == 'css') {
     1838                    wp_enqueue_style($val['name'],  plugin_dir_url(BAIDUSEO_FILE) . 'inc/admin/' . $val['url'], array(), BAIDUSEO_VERSION, 'all');
     1839                } elseif ($val['type'] == 'js') {
     1840                    wp_enqueue_script($val['name'], plugin_dir_url(BAIDUSEO_FILE) . 'inc/admin/' . $val['url'], array(), BAIDUSEO_VERSION, true);
     1841                }
     1842            }
     1843            wp_register_script('baiduseo.js', false, array(), BAIDUSEO_VERSION, false);
     1844            wp_enqueue_script('baiduseo.js');
     1845            $url1 = baiduseo_common::baiduseo_url(0);
     1846            $baiduseo_yindao = get_option('baiduseo_yindao');
     1847            $baiduseo_yindao = $baiduseo_yindao ? $baiduseo_yindao : 0;
     1848            $baiduseo_version  = esc_url_raw(admin_url('plugin-install.php?tab=plugin-information&plugin=baiduseo'));
     1849            $baiduseo_indexnow = md5(esc_url(baiduseo_common::baiduseo_url(1)));
     1850            $baiduseo_aiarticle =  esc_url_raw(admin_url('plugin-install.php?tab=plugin-information&plugin=aiarticle'));
     1851            $baiduseo_google = baiduseo_common::baiduseo_url(1) . '/wp-sitemap.xml';
     1852            wp_add_inline_script('baiduseo.js', 'var baiduseo_wztkj_url="' . esc_js(plugins_url('', BAIDUSEO_FILE)) . '/inc/admin",baiduseo_nonce="' . esc_attr(wp_create_nonce('baiduseo')) . '",baiduseo_ajax="' . esc_url(admin_url('admin-ajax.php')) . '",baiduseo_tag ="' . esc_url(admin_url('edit-tags.php?taxonomy=post_tag')) . '",baiduseo_url="' . esc_url($url1) . '",baiduseo_yindao=' . (int)$baiduseo_yindao . ',baiduseo_v="' . esc_url($baiduseo_version) . '",baiduseo_indexnow="' . esc_attr($baiduseo_indexnow) . '",baiduseo_google="' . esc_url($baiduseo_google) . '",baiduseo_aiarticle="'.esc_url($baiduseo_aiarticle).'",baiduseo_page_on_front=' . (int)get_option('page_on_front') . ';', 'before');
     1853            return;
     1854        }
     1855        if (!in_array($hook, ['post.php', 'post-new.php'], true)) return;
     1856        $baiduseo_tag = get_option('baiduseo_tag');
     1857        if (!isset($baiduseo_tag['auto']) || (int)$baiduseo_tag['auto'] !== 0) return;
     1858        $post_id = isset($_GET['post']) ? (int)$_GET['post'] : 0;
     1859        if (!$post_id) return;
     1860        $post = get_post($post_id);
     1861        if (!$post) return;
     1862        $taxonomy = self::baiduseo_get_post_tag_taxonomy($post->post_type);
     1863        if (!$taxonomy) return;
     1864        $taxonomy_object = get_taxonomy($taxonomy);
     1865        wp_enqueue_script('baiduseo-post-tag-recommend', plugin_dir_url(BAIDUSEO_FILE) . 'inc/admin/assets/baiduseo-post-tag-recommend.js', array(), BAIDUSEO_VERSION, true);
     1866        wp_add_inline_script('baiduseo-post-tag-recommend', 'window.baiduseoPostTagRecommend=' . wp_json_encode([
     1867            'ajaxUrl' => admin_url('admin-ajax.php'),
     1868            'nonce' => wp_create_nonce('baiduseo'),
     1869            'postId' => $post_id,
     1870            'taxonomy' => $taxonomy,
     1871            'restBase' => $taxonomy_object && !empty($taxonomy_object->rest_base) ? $taxonomy_object->rest_base : $taxonomy,
     1872            'label' => $taxonomy_object && !empty($taxonomy_object->labels->name) ? $taxonomy_object->labels->name : '标签',
     1873            'tagLimit' => self::baiduseo_get_tag_limit($baiduseo_tag),
     1874        ]) . ';', 'before');
    14731875    }
    14741876    public  function baiduseo_plugin_action_links($links)
    … …  
    15541956        }
    15551957    }
     1958    private static function baiduseo_get_tag_limit($tag_config)
     1959    {
     1960        if (!is_array($tag_config) || !isset($tag_config['num'])) {
     1961            return 0;
     1962        }
     1963        $limit = (int)$tag_config['num'];
     1964        return $limit > 0 && $limit != 11 ? $limit : 0;
     1965    }
     1966    public static function baiduseo_get_post_tag_taxonomy($post_type)
     1967    {
     1968        $taxonomies = get_object_taxonomies($post_type, 'names');
     1969        foreach ($taxonomies as $tax) {
     1970            if (!is_taxonomy_hierarchical($tax)) {
     1971                return $tax;
     1972            }
     1973        }
     1974        return '';
     1975    }
     1976    public static function baiduseo_get_post_tag_recommendations($post_ID, $ignored_ids = [], $content_override = [])
     1977    {
     1978        global $wpdb;
     1979        $post = get_post($post_ID);
     1980        if (!$post) {
     1981            return [];
     1982        }
     1983        $current_taxonomy = self::baiduseo_get_post_tag_taxonomy($post->post_type);
     1984        if (!$current_taxonomy) {
     1985            return [];
     1986        }
     1987        $tags = $wpdb->get_results($wpdb->prepare(
     1988            "SELECT a.term_id,a.name,b.term_taxonomy_id,b.taxonomy,b.count FROM {$wpdb->terms} as a LEFT JOIN {$wpdb->term_taxonomy} as b ON a.term_id=b.term_id WHERE b.taxonomy=%s",
     1989            $current_taxonomy
     1990        ), ARRAY_A);
     1991        if (!$tags) {
     1992            return [];
     1993        }
     1994        $existing_ids = wp_get_object_terms($post_ID, $current_taxonomy, ['fields' => 'ids']);
     1995        $existing_ids = is_array($existing_ids) ? array_map('intval', $existing_ids) : [];
     1996        $tag_limit = self::baiduseo_get_tag_limit(get_option('baiduseo_tag'));
     1997        $remaining_limit = $tag_limit ? $tag_limit - count($existing_ids) : 0;
     1998        if ($tag_limit && $remaining_limit <= 0) {
     1999            return [];
     2000        }
     2001        $ignored_ids = is_array($ignored_ids) ? array_map('intval', $ignored_ids) : [];
     2002        $title = isset($content_override['title']) ? sanitize_text_field(wp_unslash($content_override['title'])) : $post->post_title;
     2003        $content = isset($content_override['content']) ? wp_kses_post(wp_unslash($content_override['content'])) : $post->post_content;
     2004        $plain_content = wp_strip_all_tags(strip_shortcodes($content));
     2005        $plain_length = mb_strlen($plain_content, 'UTF-8');
     2006        $front_content = $plain_length ? mb_substr($plain_content, 0, (int)ceil($plain_length * 0.3), 'UTF-8') : '';
     2007        $headings = self::baiduseo_get_post_tag_heading_text($content);
     2008        $recent_terms = self::baiduseo_get_recent_post_tag_usage(array_column($tags, 'term_taxonomy_id'));
     2009        usort($tags, function ($a, $b) {
     2010            return (int)$b['count'] <=> (int)$a['count'];
     2011        });
     2012        $total_tags = count($tags);
     2013        $top_limit = (int)ceil($total_tags * 0.2);
     2014        $middle_limit = (int)ceil($total_tags * 0.5);
     2015        $normalized_seen = [];
     2016        $recommendations = [];
     2017        foreach ($tags as $index => $tag) {
     2018            $term_id = (int)$tag['term_id'];
     2019            if (in_array($term_id, $existing_ids, true) || in_array($term_id, $ignored_ids, true)) {
     2020                continue;
     2021            }
     2022            $name = isset($tag['name']) ? trim((string)$tag['name']) : '';
     2023            if (!self::baiduseo_is_valid_post_tag_candidate($name)) {
     2024                continue;
     2025            }
     2026            $normalized_name = self::baiduseo_normalize_post_tag_name($name);
     2027            if (!$normalized_name || isset($normalized_seen[$normalized_name])) {
     2028                continue;
     2029            }
     2030            $content_score = 0;
     2031            $reasons = [];
     2032            if (self::baiduseo_post_tag_text_contains($title, $name)) {
     2033                $content_score += 60;
     2034                $reasons[] = '标题命中';
     2035            }
     2036            if (self::baiduseo_post_tag_text_contains($front_content, $name)) {
     2037                $content_score += 30;
     2038                $reasons[] = '正文前段命中';
     2039            }
     2040            if (self::baiduseo_post_tag_text_contains($headings, $name)) {
     2041                $content_score += 20;
     2042                $reasons[] = '正文标题命中';
     2043            }
     2044            if (!$content_score && !self::baiduseo_post_tag_text_contains($plain_content, $name)) {
     2045                continue;
     2046            }
     2047            $length_score = self::baiduseo_get_post_tag_length_score($name);
     2048            $activity_score = 0;
     2049            if ($index < $top_limit) {
     2050                $activity_score = 10;
     2051                $reasons[] = '热门标签';
     2052            } elseif ($index < $middle_limit) {
     2053                $activity_score = 5;
     2054                $reasons[] = '活跃标签';
     2055            }
     2056            $count = (int)$tag['count'];
     2057            if ($count <= 1 && empty($recent_terms[(int)$tag['term_taxonomy_id']])) {
     2058                $activity_score -= 5;
     2059                $reasons[] = '低活跃降权';
     2060            }
     2061            $score = $content_score + $length_score + $activity_score;
     2062            $normalized_seen[$normalized_name] = true;
     2063            $recommendations[] = [
     2064                'term_id' => $term_id,
     2065                'term_taxonomy_id' => (int)$tag['term_taxonomy_id'],
     2066                'name' => $name,
     2067                'taxonomy' => $tag['taxonomy'],
     2068                'score' => $score,
     2069                'content_score' => $content_score,
     2070                'count' => $count,
     2071                'length' => mb_strlen($name, 'UTF-8'),
     2072                'reasons' => $reasons
     2073            ];
     2074        }
     2075        usort($recommendations, function ($a, $b) {
     2076            if ($a['score'] !== $b['score']) return $b['score'] <=> $a['score'];
     2077            if ($a['content_score'] !== $b['content_score']) return $b['content_score'] <=> $a['content_score'];
     2078            if ($a['count'] !== $b['count']) return $b['count'] <=> $a['count'];
     2079            if ($a['length'] !== $b['length']) return $b['length'] <=> $a['length'];
     2080            return $a['term_id'] <=> $b['term_id'];
     2081        });
     2082        $recommendations = array_map(function ($tag) {
     2083            unset($tag['content_score'], $tag['count'], $tag['length']);
     2084            return $tag;
     2085        }, $recommendations);
     2086        return array_slice($recommendations, 0, $tag_limit ? $remaining_limit : 15);
     2087    }
     2088    private static function baiduseo_normalize_post_tag_name($name)
     2089    {
     2090        $name = trim(wp_strip_all_tags((string)$name));
     2091        $name = preg_replace('/\s+/u', ' ', $name);
     2092        return function_exists('mb_strtolower') ? mb_strtolower($name, 'UTF-8') : strtolower($name);
     2093    }
     2094    private static function baiduseo_is_valid_post_tag_candidate($name)
     2095    {
     2096        $name = trim((string)$name);
     2097        if ($name === '' || mb_strlen($name, 'UTF-8') > 80) {
     2098            return false;
     2099        }
     2100        if ($name !== wp_strip_all_tags($name) || preg_match('/[\x00-\x1F\x7F]/u', $name)) {
     2101            return false;
     2102        }
     2103        $text = preg_replace('/[\p{Han}\p{L}\p{N}]/u', '', $name);
     2104        return mb_strlen($text, 'UTF-8') < mb_strlen($name, 'UTF-8');
     2105    }
     2106    private static function baiduseo_post_tag_text_contains($text, $term)
     2107    {
     2108        $text = self::baiduseo_normalize_post_tag_name($text);
     2109        $term = self::baiduseo_normalize_post_tag_name($term);
     2110        if ($text === '' || $term === '') {
     2111            return false;
     2112        }
     2113        return mb_strpos($text, $term, 0, 'UTF-8') !== false;
     2114    }
     2115    private static function baiduseo_get_post_tag_length_score($name)
     2116    {
     2117        $length = mb_strlen($name, 'UTF-8');
     2118        if ($length >= 9) return 15;
     2119        if ($length >= 5) return 10;
     2120        if ($length >= 3) return 5;
     2121        return 0;
     2122    }
     2123    private static function baiduseo_get_post_tag_heading_text($content)
     2124    {
     2125        if (!preg_match_all('/<h[23][^>]*>(.*?)<\/h[23]>/is', $content, $matches)) {
     2126            return '';
     2127        }
     2128        return wp_strip_all_tags(implode(' ', $matches[1]));
     2129    }
     2130    private static function baiduseo_get_recent_post_tag_usage($term_taxonomy_ids)
     2131    {
     2132        global $wpdb;
     2133        $term_taxonomy_ids = array_values(array_filter(array_map('intval', $term_taxonomy_ids)));
     2134        if (!$term_taxonomy_ids) {
     2135            return [];
     2136        }
     2137        $placeholders = implode(',', array_fill(0, count($term_taxonomy_ids), '%d'));
     2138        $since = gmdate('Y-m-d H:i:s', current_time('timestamp') - 90 * DAY_IN_SECONDS);
     2139        $rows = $wpdb->get_results($wpdb->prepare(
     2140            "SELECT tr.term_taxonomy_id,COUNT(*) as total FROM {$wpdb->term_relationships} tr INNER JOIN {$wpdb->posts} p ON tr.object_id=p.ID WHERE tr.term_taxonomy_id IN ($placeholders) AND p.post_status='publish' AND p.post_date >= %s GROUP BY tr.term_taxonomy_id",
     2141            array_merge($term_taxonomy_ids, [$since])
     2142        ), ARRAY_A);
     2143        $usage = [];
     2144        foreach ($rows as $row) {
     2145            $usage[(int)$row['term_taxonomy_id']] = (int)$row['total'];
     2146        }
     2147        return $usage;
     2148    }
    15562149    public function baiduseo_articlepublish($post_ID)
    15572150    {
    1558         global $wpdb;
    15592151        $baiduseo_wzt_log = baiduseo_kp::baiduseo_wzt_log();
    1560         // 1. 基础检查
    15612152        if (!$baiduseo_wzt_log) {
    15622153            return;
    15632154        }
    1564 
    1565         // 2. 获取文章对象和类型,为动态处理做准备
     2155        $baiduseo_tag = get_option('baiduseo_tag');
     2156        if (!isset($baiduseo_tag['auto']) || (int)$baiduseo_tag['auto'] !== 1) {
     2157            return;
     2158        }
    15662159        $post = get_post($post_ID);
    15672160        if (!$post) {
    1568             return; // 如果文章不存在,则退出
    1569         }
    1570         $post_type = $post->post_type;
    1571         $post_content = $post->post_content;
    1572 
    1573         // 3. 动态获取当前文章类型支持的“标签”分类法 (非层级分类法)
    1574         $taxonomies = get_object_taxonomies($post_type, 'names');
    1575         $tag_taxonomies = '';
    1576         foreach ($taxonomies as $tax) {
    1577             if (!is_taxonomy_hierarchical($tax)) {
    1578                 $tag_taxonomies = $tax;
    1579                 break;
    1580             }
    1581         }
    1582 
    1583         // 如果该文章类型没有任何标签分类法,则无需继续执行
    1584         if (!$tag_taxonomies) {
    15852161            return;
    15862162        }
    1587         $current_taxonomy = $tag_taxonomies;
    1588 
    1589         // 4. 读取插件设置
    1590         $baiduseo_tag = get_option('baiduseo_tag');
    1591 
    1592         // 准备标签列表(根据设置排序)
     2163        $current_taxonomy = self::baiduseo_get_post_tag_taxonomy($post->post_type);
     2164        if (!$current_taxonomy) {
     2165            return;
     2166        }
     2167        global $wpdb;
    15932168        if (isset($baiduseo_tag['pp']) && $baiduseo_tag['pp'] == 1) {
    1594             $tags = $wpdb->get_results('SELECT * FROM ' . $wpdb->prefix . 'terms ORDER BY LENGTH(name) DESC', ARRAY_A);
     2169            $tags = $wpdb->get_results(
     2170                $wpdb->prepare(
     2171                    'SELECT a.term_id,a.name,b.term_taxonomy_id,b.taxonomy FROM ' . $wpdb->prefix . 'terms as a left join ' . $wpdb->prefix . 'term_taxonomy as b on a.term_id=b.term_id where b.taxonomy=%s ORDER BY LENGTH(a.name) DESC',
     2172                    $current_taxonomy
     2173                ),
     2174                ARRAY_A
     2175            );
    15952176        } elseif (isset($baiduseo_tag['pp']) && $baiduseo_tag['pp'] == 2) {
    1596             $tags = $wpdb->get_results('SELECT * FROM ' . $wpdb->prefix . 'terms ORDER BY LENGTH(name) ASC', ARRAY_A);
     2177            $tags = $wpdb->get_results(
     2178                $wpdb->prepare(
     2179                    'SELECT a.term_id,a.name,b.term_taxonomy_id,b.taxonomy FROM ' . $wpdb->prefix . 'terms as a left join ' . $wpdb->prefix . 'term_taxonomy as b on a.term_id=b.term_id where b.taxonomy=%s ORDER BY LENGTH(a.name) ASC',
     2180                    $current_taxonomy
     2181                ),
     2182                ARRAY_A
     2183            );
    15972184        } else {
    1598             $tags = $wpdb->get_results('SELECT * FROM ' . $wpdb->prefix . 'terms', ARRAY_A);
    1599         }
    1600 
    1601         // 性能优化:提前准备好正则表达式的模式
     2185            $tags = $wpdb->get_results(
     2186                $wpdb->prepare(
     2187                    'SELECT a.term_id,a.name,b.term_taxonomy_id,b.taxonomy FROM ' . $wpdb->prefix . 'terms as a left join ' . $wpdb->prefix . 'term_taxonomy as b on a.term_id=b.term_id where b.taxonomy=%s',
     2188                    $current_taxonomy
     2189                ),
     2190                ARRAY_A
     2191            );
     2192        }
     2193        if (!$tags) {
     2194            return;
     2195        }
    16022196        $hremove_regex = isset($baiduseo_tag['hremove']) && $baiduseo_tag['hremove'] == 1;
    1603 
    1604         // 5. 执行标签添加逻辑
    1605         // 情况A:不限制标签数量,或数量设置为11
    1606         if (!isset($baiduseo_tag['num']) || !$baiduseo_tag['num'] || $baiduseo_tag['num'] == 11) {
    1607             foreach ($tags as $v) {
    1608                 // 检查该术语是否属于当前文章类型的标签分类法
    1609                 $term_in_tax = $wpdb->get_var($wpdb->prepare(
    1610                     "SELECT COUNT(*) FROM {$wpdb->prefix}term_taxonomy WHERE taxonomy = %s AND term_id = %d",
    1611                     $current_taxonomy,
    1612                     $v['term_id']
     2197        $max_tags = self::baiduseo_get_tag_limit($baiduseo_tag);
     2198        if ($max_tags) {
     2199            $current_count = (int)$wpdb->get_var($wpdb->prepare(
     2200                "SELECT COUNT(*) FROM {$wpdb->prefix}term_relationships tr INNER JOIN {$wpdb->prefix}term_taxonomy tt ON tr.term_taxonomy_id=tt.term_taxonomy_id WHERE tr.object_id=%d AND tt.taxonomy=%s",
     2201                $post_ID,
     2202                $current_taxonomy
     2203            ));
     2204            if ($current_count >= $max_tags) {
     2205                return;
     2206            }
     2207        }
     2208        foreach ($tags as $tag) {
     2209            $term_regex = baiduseo_tag::BaiduSEO_preg($tag['name']);
     2210            if ($hremove_regex) {
     2211                $pattern = '{(?!((<.*?)|(<a.*?)|(<h[1-6].*?>)))(' . $term_regex . ')(?!(([^<>]*?)>)|([^>]*?</a>)|([^>]*?</h[1-6]>))}i';
     2212            } else {
     2213                $pattern = '{(?!((<.*?)|(<a.*?)))(' . $term_regex . ')(?!(([^<>]*?)>)|([^>]*?</a>))}i';
     2214            }
     2215            if (preg_match($pattern, $post->post_content)) {
     2216                $is_attached = $wpdb->get_var($wpdb->prepare(
     2217                    "SELECT COUNT(*) FROM {$wpdb->prefix}term_relationships WHERE object_id = %d AND term_taxonomy_id = %d",
     2218                    $post_ID,
     2219                    $tag['term_taxonomy_id']
    16132220                ));
    1614                 if (!$term_in_tax) {
    1615                     continue;
    1616                 }
    1617 
    1618                 // 构建正则表达式
    1619                 $term_regex = baiduseo_tag::BaiduSEO_preg($v['name']);
    1620                 if ($hremove_regex) {
    1621                     $pattern = '{(?!((<.*?)|(<a.*?)|(<h[1-6].*?>)))(' . $term_regex . ')(?!(([^<>]*?)>)|([^>]*?<\/a>)|([^>]*?<\/h[1-6]>))}i';
    1622                 } else {
    1623                     $pattern = '{(?!((<.*?)|(<a.*?)))(' . $term_regex . ')(?!(([^<>]*?)>)|([^>]*?<\/a>))}i';
    1624                 }
    1625 
    1626                 if (preg_match($pattern, $post_content, $matches)) {
    1627                     // 获取 term_taxonomy_id
    1628                     $tt_id = $wpdb->get_var($wpdb->prepare(
    1629                         "SELECT term_taxonomy_id FROM {$wpdb->prefix}term_taxonomy WHERE taxonomy = %s AND term_id = %d",
    1630                         $current_taxonomy,
    1631                         $v['term_id']
     2221                if (!$is_attached) {
     2222                    $wpdb->insert($wpdb->prefix . "term_relationships", [
     2223                        'object_id' => $post_ID,
     2224                        'term_taxonomy_id' => $tag['term_taxonomy_id']
     2225                    ]);
     2226                    $wpdb->query($wpdb->prepare(
     2227                        "UPDATE {$wpdb->prefix}term_taxonomy SET count = count + 1 WHERE term_taxonomy_id = %d",
     2228                        $tag['term_taxonomy_id']
    16322229                    ));
    1633 
    1634                     if ($tt_id) {
    1635                         // 检查该标签是否已与文章关联
    1636                         $is_attached = $wpdb->get_var($wpdb->prepare(
    1637                             "SELECT COUNT(*) FROM {$wpdb->prefix}term_relationships WHERE object_id = %d AND term_taxonomy_id = %d",
    1638                             $post_ID,
    1639                             $tt_id
    1640                         ));
    1641                         if (!$is_attached) {
    1642                             // 添加关联
    1643                             $wpdb->insert($wpdb->prefix . "term_relationships", [
    1644                                 'object_id' => $post_ID,
    1645                                 'term_taxonomy_id' => $tt_id
    1646                             ]);
    1647                             // 更新计数
    1648                             $wpdb->query($wpdb->prepare(
    1649                                 "UPDATE {$wpdb->prefix}term_taxonomy SET count = count + 1 WHERE term_taxonomy_id = %d",
    1650                                 $tt_id
    1651                             ));
    1652                         }
    1653                     }
    1654                 }
    1655             }
    1656         }
    1657         // 情况B:限制标签数量
    1658         else {
    1659             $max_tags = (int)$baiduseo_tag['num'];
    1660             // 获取当前文章已有的标签数量
    1661             $existing_terms_count = wp_get_object_terms($post_ID, $current_taxonomy, array('fields' => 'ids'));
    1662             $existing_terms_count = is_array($existing_terms_count) ? count($existing_terms_count) : 0;
    1663 
    1664             if ($existing_terms_count < $max_tags) {
    1665                 foreach ($tags as $v) {
    1666                     // 如果已达到标签数量上限,则停止循环
    1667                     if ($existing_terms_count >= $max_tags) {
    1668                         break;
    1669                     }
    1670 
    1671                     // 检查该术语是否属于当前文章类型的标签分类法
    1672                     $term_in_tax = $wpdb->get_var($wpdb->prepare(
    1673                         "SELECT COUNT(*) FROM {$wpdb->prefix}term_taxonomy WHERE taxonomy = %s AND term_id = %d",
    1674                         $current_taxonomy,
    1675                         $v['term_id']
    1676                     ));
    1677                     if (!$term_in_tax) {
    1678                         continue;
    1679                     }
    1680 
    1681                     // 构建正则表达式
    1682                     $term_regex = baiduseo_tag::BaiduSEO_preg($v['name']);
    1683                     if ($hremove_regex) {
    1684                         $pattern = '{(?!((<.*?)|(<a.*?)|(<h[1-6].*?>)))(' . $term_regex . ')(?!(([^<>]*?)>)|([^>]*?<\/a>)|([^>]*?<\/h[1-6]>))}i';
    1685                     } else {
    1686                         $pattern = '{(?!((<.*?)|(<a.*?)))(' . $term_regex . ')(?!(([^<>]*?)>)|([^>]*?<\/a>))}i';
    1687                     }
    1688 
    1689                     if (preg_match($pattern, $post_content, $matches)) {
    1690                         // 获取 term_taxonomy_id
    1691                         $tt_id = $wpdb->get_var($wpdb->prepare(
    1692                             "SELECT term_taxonomy_id FROM {$wpdb->prefix}term_taxonomy WHERE taxonomy = %s AND term_id = %d",
    1693                             $current_taxonomy,
    1694                             $v['term_id']
    1695                         ));
    1696 
    1697                         if ($tt_id) {
    1698                             // 检查该标签是否已与文章关联
    1699                             $is_attached = $wpdb->get_var($wpdb->prepare(
    1700                                 "SELECT COUNT(*) FROM {$wpdb->prefix}term_relationships WHERE object_id = %d AND term_taxonomy_id = %d",
    1701                                 $post_ID,
    1702                                 $tt_id
    1703                             ));
    1704                             if (!$is_attached) {
    1705                                 // 添加关联
    1706                                 $wpdb->insert($wpdb->prefix . "term_relationships", [
    1707                                     'object_id' => $post_ID,
    1708                                     'term_taxonomy_id' => $tt_id
    1709                                 ]);
    1710                                 // 更新计数
    1711                                 $wpdb->query($wpdb->prepare(
    1712                                     "UPDATE {$wpdb->prefix}term_taxonomy SET count = count + 1 WHERE term_taxonomy_id = %d",
    1713                                     $tt_id
    1714                                 ));
    1715                                 // 更新已添加标签计数
    1716                                 $existing_terms_count++;
    1717                             }
     2230                    if ($max_tags) {
     2231                        $current_count++;
     2232                        if ($current_count >= $max_tags) {
     2233                            break;
    17182234                        }
    17192235                    }
  • baiduseo/trunk/readme.txt

    r3551508 r3625549  
    1 === SEO合集(支持百度/Google/Bing/头条推送) ===
     1=== SEO合集(支持百度/Google/Bing/geo优化) ===
    22Donate link: https://www.seoceo.cn
    3 Tags: seo, 百度, google, sitemap, 蜘蛛
     3Tags: seo, geo, google, sitemap, 蜘蛛
    44Requires at least: 5.3
    55Tested up to: 7.0
    6 Stable tag: 2.2.1
     6Stable tag: 2.2.2
    77Requires PHP: 7.4
    88License: GPLv3
    99License URI: https://www.gnu.org/licenses/gpl-3.0.html
    1010
    11 支持百度(baidu)、谷歌google、Bing、indexNow、sitemap地图、robots、TDK关键词部署、img图片alt/title属性、网站蜘蛛、网站SEO检测评分、WP优化、tag内链关键词自动关联、词库挖掘、热词库创建、友情链接户换、流量监控、访客明细、AI自动发文章等。
     11WordPress SEO插件和站长工具,支持百度推送、收录查询、Sitemap网站地图、robots优化、蜘蛛日志、IndexNow推送、内链优化、关键词监控和geo优化。
    1212
    1313== Description ==
    … …  
    1515SEO合集是一款面向中文网站、WordPress站长、企业官网、博客站、资讯站、内容站和SEO团队的WordPress SEO插件。插件适合用于seo优化、网站SEO优化、seo网站建设、WordPress SEO优化、中文SEO优化、站长SEO工具、网站SEO工具和SEO工具箱等场景,帮助站长在一个后台中完成搜索引擎推送、收录查询、网站地图、蜘蛛日志、站内内链、关键词排名监控和内容优化等工作。
    1616
    17 SEO合集围绕国内站长常用的搜索引擎优化需求开发,整合百度站长、百度推送、百度主动推送、百度自动推送、百度URL提交、百度收录查询、百度快速收录、Google站长、Google Indexing、Bing站长、Bing IndexNow、360站长、神马站长、今日头条站长、Sitemap网站地图、XML地图、HTML地图、robots.txt、网站蜘蛛、蜘蛛日志、站内内链、tag标签内链、关键词排名监控、流量词获取、图片Alt标签、301重定向、404监控、死链查询、原创率检测、AI文章助手和智能改写等功能。
     17SEO合集围绕国内站长常用的搜索引擎优化需求开发,整合百度站长、百度推送、百度主动推送、百度自动推送、百度URL提交、百度收录查询、百度快速收录、Google站长、Google Indexing、Bing站长、Bing IndexNow、360站长、神马站长、今日头条站长、Sitemap网站地图、XML地图、HTML地图、robots.txt、网站蜘蛛、蜘蛛日志、站内内链、tag标签内链、关键词排名监控、流量词获取、图片Alt标签、301重定向、404监控、死链查询、原创率检测、geo、AI文章助手和智能改写等功能。
    1818
    1919= SEO基础、SEO优化和WordPress SEO插件 =
    … …  
    8484
    8585SEO合集还包含友情链接、友情链接检测、SEO检测、WP优化、流量监控和用户漏洞修复等功能。友情链接检测适合需要维护交换链接的网站,SEO检测和WP优化适合站长日常检查网站基础优化情况,用户漏洞修复功能用于辅助处理WordPress站点常见安全风险。
     86= GEO生成式引擎优化、AI搜索优化和ChatGPT收录 =
     87
     88GEO合集主要面向国内中文网站、WordPress网站和WordPress站长,提供GEO优化、生成式引擎优化、生成引擎优化、AI搜索优化、AI SEO优化、AEO答案引擎优化、ChatGPT SEO、ChatGPT优化、ChatGPT收录、ChatGPT推荐、AI收录、AI引用、AI排名、AI推荐和AI品牌曝光等相关能力。
     89
     90相关功能适用于希望提升网站在ChatGPT、DeepSeek、豆包、腾讯元宝、Kimi、文心一言、通义千问、百度AI搜索、360AI搜索、Google AI Overview、Bing Copilot、Gemini、Claude和Perplexity等AI搜索引擎、AI问答平台、生成式搜索和大模型平台中曝光度的中文网站。
     91
     92插件可以帮助WordPress网站优化文章标题、正文结构、FAQ问答、内容摘要、语义信息、实体信息、结构化数据和llms.txt文件,让AI搜索引擎、生成式引擎和大语言模型更容易抓取、理解、收录、引用和推荐网站内容。通过WordPress GEO、AI SEO、AEO和传统SEO协同优化,提高网站页面在AI搜索结果、生成式回答和大模型答案中的可见性、引用率、推荐机会和品牌曝光。
    8693
    8794这些功能与百度站长、Google站长、Bing站长、Sitemap网站地图、网站蜘蛛、站内内链、关键词排名监控、原创率检测和AI文章助手共同组成SEO合集的综合工具面板。
    … …  
    209216插件部分功能需要第三方授权或接口服务支持。请在使用前阅读服务协议和隐私政策,并根据自身网站需求选择开启相关功能。
    210217
     218
     219
    211220== Changelog ==
    212 
    213 == Changelog ==
     221= 2.2.2 =(2026/07/28)
     222*新增geo功能
     223*修复已知BUG
     224*优化部分功能的实现方式
     225*全局安全更新
    214226= 2.2.1 =(2026/04/08)
    215227*修复流量监控不统计问题
  • baiduseo/trunk/seo_title_baidu.php

    r3501273 r3625549  
    33Plugin Name: SEO合集(支持百度/Google/Bing/头条推送)
    44Description: 含百度站长、360站长、Bing站长、今日头条站长、神马站长、IndexNow、tag标签内链、友情链接、百度地图sitemap、Google地图、 谷歌地图、关键词排名查询监控、360站长JS自动推送、 文章原创率检测、文章伪原创、 网站蜘蛛、robots、图片alt标签、天级推送、category隐藏、死链查询、百度自动推送、批量提交URL到站长、百度收录查询、批量推送未收录、301/404等功能、AI文章助手、流量监控。
    5 Version: 2.2.1
     5Version: 2.2.2
    66Author: 沃之涛科技
    77Author URI: https://www.seoceo.cn
    … …  
    1010*/
    1111if(!defined('ABSPATH'))exit;
    12 define('BAIDUSEO_VERSION','2.2.1');
     12define('BAIDUSEO_VERSION','2.2.2');
    1313define('BAIDUSEO_FILE',__FILE__);
    1414define('BAIDUSEO_NAME',plugin_basename(__FILE__));
Note: See TracChangeset for help on using the changeset viewer.