分页: 1 / 1

Typecho外链监控插件-LinkCare

发表于 : 2025年 9月 13日 15:46
寒士杰克

一、插件介绍

插件起源:关于出站链接的规范性通告 - 动态 - 南蛮子懋和 - 懋和道人 - 李懋和,俗名李栋梁。书法、国画爱好者,互联网安全与前端建设者。

Typecho外链监控插件: LinkCare 插件提供外链的全方位控制和监管,添加 rel属性、添加/移除UTM 参数、中转页、黑名单、添加CSS类名、添加标题属性、referer控制、添加svg后缀、取消链接显示纯文本、添加自定义参数以及可视化监控统计等功能。

目前使用的是图形规则引擎,很方便添加配置,可以提取JSON和导入配置文件。
图片

由于本人对外链相关知识薄弱,很多设置也都是通过ai进行,咨询需要实现的基础功能-> 界面样式和基础功能 -> 扩展功能咨询和实现。

比如目前通过IF/ELSE/ELSE IF/嵌套规则引擎,实现对目标域名、完整URL、Rel属性、URL路径、查询参数进行匹配值设置(包含/不包含、等于/不等于...)

可以修改Rel属性、添加UTM参数、新窗口打开/强制HTTPS、添加悬停提示、添加svg图标、添加黑名单、跳转转页、移除跟踪参数/自定义移除参数、移除UTM参数等等。

二、测试预览

1、插件设置

规则通过JSON配置,支持导入配置和导出

你可以:
图片

2、结果如下

图片

三、安装说明

  1. 下载插件并解压到 Typecho 插件目录 /usr/plugins/,确保文件夹名为 LinkCare

  2. 登录 Typecho 后台,进入「控制台」-「插件」页面

  3. 启用 LinkCare 插件

  4. 在主题模板适当位置添加调用代码

    代码: 全选

    <?php $this->content = LinkCare_Plugin::LinkCare($this->content); echo $this->content; ?>

或者在主题的 functions.php 中添加 (不建议)

代码: 全选

Typecho_Plugin::factory('Widget_Abstract_Contents')->contentEx = array('LinkCare_Plugin', 'LinkCare');

详细调用方法看:添加文章/页面渲染

1、插件启用自行设置参数

  1. 添加规则引擎:见 参数说明
  2. 添加跳转页链接、缓存时间、日志记录 (已弃用)
参数类型说明
跳转页地址string设置外链跳转中转页面,留空则不启用跳转功能
缓存过期时间select监控数据的缓存时间,可选1小时、6小时、12小时、24小时
启用日志记录checkbox记录外链处理日志,便于调试和统计

2、添加文章渲染

1、添加文章渲染

修改主题post.php中关于文章内容的输出:

代码: 全选

<?php 
// Step1: 获取文章内容
$content = $this->content;

// Step2: 交给 LinkCare 插件先处理外链 (重要)
$content = LinkCare_Plugin::LinkCare($content);

// Step3: LabelMark 处理 (其他插件,比如我的LabelMark)
$content = Typecho_Plugin::factory('Content')->LabelMark($content);

// Step4: 其他处理(表情、短代码)
echo reEmoPost(
    ShortCode(
        $content, 
        $this, 
        $this->user->hasLogin(), 
        $this->fields->ArticleType
    )
); 
?>

为了确保插件卸载的时候不会报错 (检查插件):

代码: 全选

<?php 
// Step1: 获取文章内容
$content = $this->content;

// Step2: 判断 LinkCare 插件是否启用并处理外链
$plugins = Typecho_Plugin::export();
if (isset($plugins['activated']['LinkCare'])) {
    $content = LinkCare_Plugin::LinkCare($content);
}
// Step3: LabelMark 处理
$content = Typecho_Plugin::factory('Content')->LabelMark($content);

// 然后应用你原有的其他处理函数
echo reEmoPost(ShortCode($content, $this, $this->user->hasLogin(), $this->fields->ArticleType)); 
?>

2、详细渲染教程

  1. 检查插件是否激活并处理内容 (同上)

在主题文件中,可以通过以下方式检查 LinkCare 插件是否激活,并对内容进行处理:

代码: 全选

// 检查 LinkCare 插件是否激活
$plugins = Typecho_Plugin::export();
if (isset($plugins['activated']['LinkCare'])) {
    // 使用 LinkCare 处理内容中的链接
    $content = LinkCare_Plugin::LinkCare($content);
}
  1. 处理单个链接

如果需要处理单个链接,可以使用以下方法:

代码: 全选

/**
 * 处理单个链接的函数
 * @param string $url 链接地址
 * @param string $text 链接文本
 * @return string 处理后的链接 HTML
 */
function processLinkWithLinkCare($url, $text = '') {
    // 检查 LinkCare 插件是否激活
   $plugins = Typecho_Plugin::export();
    if (isset($plugins['activated']['LinkCare'])) {
        // 创建模拟的 HTML 链接
        $linkHtml = '<a href="' . $url . '">' . $text . '</a>';  
        // 使用 LinkCare 处理链接
        $processedHtml = LinkCare_Plugin::LinkCare($linkHtml);
        // 返回完整处理后的 HTML
        return $processedHtml;
    }
    // 如果没有激活 LinkCare 插件,直接返回原链接 HTML
    return '<a href="' . $url . '">' . $text . '</a>';
}

代码: 全选

// 使用示例
$link = processLinkWithLinkCare('https://example.com', '示例链接');
echo $link;
  1. 在模板中的完整应用示例

代码: 全选

<?php
// 在页面顶部定义处理函数
function processLinkWithLinkCare($url, $text = '') {
    $plugins = Typecho_Plugin::export();
    if (isset($plugins['activated']['LinkCare'])) {
        $linkHtml = '<a href="' . $url . '">' . $text . '</a>';
        return LinkCare_Plugin::LinkCare($linkHtml);
    }
    return '<a href="' . $url . '">' . $text . '</a>';
}
?>

代码: 全选

<!-- 在需要显示链接的地方 -->
<?php 
$processedLink = processLinkWithLinkCare($link['url'], $link['name']);
// 提取链接属性
preg_match('/<a\s+([^>]+)>(.*?)<\/a>/i', $processedLink, $linkMatches);
$linkAttrs = isset($linkMatches[1]) ? $linkMatches[1] : 'href="' . $link['url'] . '"';
$linkText = isset($linkMatches[2]) ? $linkMatches[2] : $link['name'];
?>
<a <?php echo $linkAttrs; ?>><?php echo $linkText; ?></a>
  1. 处理文章内容中的链接

对于文章内容中的链接处理,可以使用以下方式:

代码: 全选

<?php 
// 获取原始内容
$content = $this->content;

// 检查并处理 LinkCare 插件
$plugins = Typecho_Plugin::export();
if (isset($plugins['activated']['LinkCare'])) {
    $content = LinkCare_Plugin::LinkCare($content);
}

// 同时处理其他可能的内容插件(如 LabelMark)
if (array_key_exists('LabelMark', $plugins['activated'])) {
    $content = Typecho_Plugin::factory('Content')->LabelMark($content);
}

// 输出处理后的内容
echo $content;
?>

3、具体示例

图片

图片

备注:使用BearSimple主题的直接替换我下面的代码即可:

代码: 全选

<?php
    /**
    * 友链[新]
    *
    * @package custom
    */
?>
<?php if (!defined('__TYPECHO_ROOT_DIR__')) exit;
$loader = Bsoptions('Lazyload_style');

// 定义处理链接的函数
function processLinkWithLinkCare($url, $text = '') {
    // 检查LinkCare插件是否激活
    $plugins = Typecho_Plugin::export();
    if (isset($plugins['activated']['LinkCare'])) {
        // 创建模拟的HTML链接
        $linkHtml = '<a href="' . $url . '">' . $text . '</a>';
        
        // 使用LinkCare处理链接
        $processedHtml = LinkCare_Plugin::LinkCare($linkHtml);
        
        // 返回完整处理后的HTML
        return $processedHtml;
    }
    
    // 如果没有激活LinkCare插件,直接返回原链接HTML
    return '<a href="' . $url . '">' . $text . '</a>';
}

// 获取原始链接数据
function getOriginalFriendLinks($type = NULL) {
    return get_friendlink($type);
}
?>
<?php $this->need('compoment/head.php');?>
<?php if(Bsoptions('FriendLinkSkin') == '1' || Bsoptions('FriendLinkSkin') == '3'): ?>
<link rel="stylesheet" type="text/css" href="<?php AssetsDir();?>assets/css/modules/friendlink_custom.min.css?v=<?php echo themeVersion(); ?>">
<?php endif; ?>

    
<div class="pure-g" id="layout">
            <div class="pure-u-1 pure-u-md-<?php if(Bsoptions('site_style') == '1' || Bsoptions('site_style') == ''):?>3<?php endif;?><?php if(Bsoptions('site_style') == '2'):?>4<?php endif;?>-4">
                <div class="content_container">
                   <div class="page-card">
                <h2><i class="jsfiddle icon"></i> <?php $this->title() ?></h2>
<div class="ui secondary pointing short scrolling menu"  id="uitab">
    <a class="active item" data-tab="first"><i class="list icon"></i>友链列表</a>
    <?php if(Bsoptions('FriendLinkRejectShow') == true):?>
    <a class="item" data-tab="fourth"><i class="exclamation circle icon"></i>失效友链列表</a>
    <?php endif;?>
  <a class="item" data-tab="second"><i class="bullhorn icon"></i>友链说明</a>
                      <?php if(Bsoptions('FriendLinkSubmit') == true || Bsoptions('FriendLinkSubmit') == ''): ?><a class="item" data-tab="third"><i class="hand holding heart icon"></i>提交友链</a><?php endif; ?>

</div>
<div class="ui bottom attached active tab " data-tab="first">
    <?php if(Bsoptions('FriendLinkSkin') == '0' || Bsoptions('FriendLinkSkin') == ''): ?>
    <div class="flcard-columns">
      <?php foreach(getOriginalFriendLinks() as $link): 
          $processedLink = processLinkWithLinkCare($link['friendurl'], $link['friendname']);
          // 提取链接属性
          preg_match('/<a\s+([^>]+)>(.*?)<\/a>/i', $processedLink, $linkMatches);
          $linkAttrs = isset($linkMatches[1]) ? $linkMatches[1] : 'href="' . $link['friendurl'] . '"';
          $linkText = isset($linkMatches[2]) ? $linkMatches[2] : $link['friendname'];
      ?>
    <a class="flcard" <?php echo $linkAttrs; ?> style="text-align:center">
      <div class="flcard-body">
        <div class="mt-1 mb-1">
          <img class="flcard-avatar<?php if(Bsoptions('Lazyload') == true): ?> lazyload lazyload-style-<?php echo $loader;?><?php endif;?>" <?php if(Bsoptions('Lazyload') == true): ?>src="data:image/svg+xml;base64,PCEtLUFyZ29uTG9hZGluZy0tPgo8c3ZnIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgc3Ryb2tlPSIjZmZmZmZmMDAiPjxnPjwvZz4KPC9zdmc+" data-<?php endif; ?>src="<?php echo $link['friendpic']; ?>" <?php if(Bsoptions('Lazyload') == '' || Bsoptions('Lazyload') == false): ?>loading="lazy"<?php endif;?>>
        </div>
        <div style="margin-top:-3px">
        <h4 class="flcard-title mb-1"><?php echo $linkText; ?></h4>
        <p class="mt-1 mb-1"><?php echo $link['frienddec']; ?></p>
        </div>
      </div>
    </a>
      <?php endforeach; ?>
      </div>
     <?php endif;?>
    
   
      
      
<?php if(Bsoptions('FriendLinkSkin') == '1'): ?>
<div class="page friend">
				<ul class="friend-ul grid w-full grid-cols-3 gap-4 sm:grid-cols-4 xl:grid-cols-5">
				     <?php foreach(getOriginalFriendLinks() as $link): 
				         $processedLink = processLinkWithLinkCare($link['friendurl'], $link['friendname']);
				         // 提取链接属性
				         preg_match('/<a\s+([^>]+)>(.*?)<\/a>/i', $processedLink, $linkMatches);
				         $linkAttrs = isset($linkMatches[1]) ? $linkMatches[1] : 'href="' . $link['friendurl'] . '"';
				         $linkText = isset($linkMatches[2]) ? $linkMatches[2] : $link['friendname'];
				     ?>
					<li class="relative inline-block transform overflow-hidden text-center duration-300 hover:-translate-y-1">
						<a <?php echo $linkAttrs; ?> class="block h-full w-full py-4"><?php echo $linkText; ?></a>
						<img class="friend-img absolute right-0 top-0 h-full transform rounded-full duration-300<?php if(Bsoptions('Lazyload') == true): ?> lazyload lazyload-style-<?php echo $loader;?><?php endif;?>" <?php if(Bsoptions('Lazyload') == true): ?>src="data:image/svg+xml;base64,PCEtLUFyZ29uTG9hZGluZy0tPgo8c3ZnIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgc3Ryb2tlPSIjZmZmZmZmMDAiPjxnPjwvZz4KPC9zdmc+" <?php endif; ?>
							 <?php if(Bsoptions('Lazyload') == true): ?> data-<?php endif; ?>src="<?php echo $link['friendpic']; ?>" alt="<?php echo $link['friendname']; ?>" <?php if(Bsoptions('Lazyload') == '' || Bsoptions('Lazyload') == false): ?>loading="lazy"<?php endif;?>>
					</li>
					<?php endforeach; ?>
				
				
				</ul>
</div>
<?php endif;?>
<?php if(Bsoptions('FriendLinkSkin') == '2'): ?>
  <div class="ui three column doubling stackable masonry grid" style="word-break:break-all;">
      <?php foreach(getOriginalFriendLinks() as $link): 
          $processedLink = processLinkWithLinkCare($link['friendurl'], $link['friendname']);
          // 提取链接属性
          preg_match('/<a\s+([^>]+)>(.*?)<\/a>/i', $processedLink, $linkMatches);
          $linkAttrs = isset($linkMatches[1]) ? $linkMatches[1] : 'href="' . $link['friendurl'] . '"';
          $linkText = isset($linkMatches[2]) ? $linkMatches[2] : $link['friendname'];
      ?>
      <div class="column">
      <div class="ui fluid card" <?php echo $linkAttrs; ?>><div class="content"><div class="center aligned header"><?php echo $linkText; ?></div><div class="center aligned description"><p><?php echo $link['frienddec']; ?></p></div></div><div class="extra content"><div class="center aligned author"><img class="ui avatar image<?php if(Bsoptions('Lazyload') == true): ?> lazyload lazyload-style-<?php echo $loader;?><?php endif;?>" <?php if(Bsoptions('Lazyload') == true): ?>src="data:image/svg+xml;base64,PCEtLUFyZ29uTG9hZGluZy0tPgo8c3ZnIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgc3Ryb2tlPSIjZmZmZmZmMDAiPjxnPjwvZz4KPC9zdmc+" <?php endif; ?> <?php if(Bsoptions('Lazyload') == true): ?> data-<?php endif; ?>src="<?php echo $link['friendpic']; ?>" <?php if(Bsoptions('Lazyload') == '' || Bsoptions('Lazyload') == false): ?>loading="lazy"<?php endif;?>><?php echo $linkText; ?></div></div></div></div>
      <?php endforeach; ?>
     </div>
     <?php endif;?>
 <?php if(Bsoptions('FriendLinkSkin') == '3'): ?>
 <div class="bsui_link_container">
        <div class="bsui_link_masonry">
      <?php foreach(getOriginalFriendLinks() as $link): 
          $processedLink = processLinkWithLinkCare($link['friendurl'], $link['friendname']);
          // 提取链接属性
          preg_match('/<a\s+([^>]+)>(.*?)<\/a>/i', $processedLink, $linkMatches);
          $linkAttrs = isset($linkMatches[1]) ? $linkMatches[1] : 'href="' . $link['friendurl'] . '"';
          $linkText = isset($linkMatches[2]) ? $linkMatches[2] : $link['friendname'];
      ?>
      <div class="bsui_link_card">
                <img <?php if(Bsoptions('Lazyload') == true): ?>src="data:image/svg+xml;base64,PCEtLUFyZ29uTG9hZGluZy0tPgo8c3ZnIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgc3Ryb2tlPSIjZmZmZmZmMDAiPjxnPjwvZz4KPC9zdmc+" <?php endif; ?> <?php if(Bsoptions('Lazyload') == true): ?> data-<?php endif; ?>src="<?php echo $link['friendpic']; ?>" class="bsui_link_icon<?php if(Bsoptions('Lazyload') == true): ?> lazyload lazyload-style-<?php echo $loader;?><?php endif;?>" <?php if(Bsoptions('Lazyload') == '' || Bsoptions('Lazyload') == false): ?>loading="lazy"<?php endif;?>>
                <div class="bsui_link_content">
                    <a <?php echo $linkAttrs; ?> class="bsui_link_name"><?php echo $linkText; ?></a>
                    <p class="bsui_link_desc"><?php echo $link['frienddec']; ?></p>
                </div>
            </div>
     
      <?php endforeach; ?>
     </div></div>
     <?php endif;?>  
   
   
 
    
    
</div>
<?php if(Bsoptions('FriendLinkRejectShow') == true):?>
<div class="ui bottom attached tab " data-tab="fourth">
    <?php if(Bsoptions('FriendLinkRejectShow') == true && Bsoptions('FriendLinkRejectNote') !== ''):?>
    <div class="ui warning message">
  <i class="exclamation circle icon"></i>
  <?php echo Bsoptions('FriendLinkRejectNote');?>
</div>
<?php endif;?>
 <table class="ui black unstackable celled long scrolling table" style="word-break:break-all">
  <thead>
    <tr><th>友链名称</th>
    <th>失效原因</th>
  </tr></thead><tbody>
      <?php foreach(getOriginalFriendLinks('reject') as $link): ?>
    <tr>
      <td><?php echo $link['friendname'];?></td>
      <td><?php if($link['rejectreason'] == NULL):?>无<?php else:?><?php echo $link['rejectreason'];?><?php endif;?></td>
    </tr>
    <?php endforeach;?>
 
  </tbody>
</table>
    </div>
    <?php endif;?>
<div class="ui bottom attached tab bdesc" data-tab="second">
  <?php 
  // Step1: 获取页面内容
  $content = $this->content;

  // Step2: 判断 LinkCare 插件是否启用并处理外链
  $plugins = Typecho_Plugin::export();
  if (isset($plugins['activated']['LinkCare'])) {
      $content = LinkCare_Plugin::LinkCare($content);
  }
  
  // Step3: LabelMark 处理(如果存在)
  if (array_key_exists('LabelMark', $plugins['activated'])) {
      $content = Typecho_Plugin::factory('Content')->LabelMark($content);
  }

  // 输出处理后的内容
  echo reEmoPost(ShortCode($content, $this, $this->user->hasLogin(), $this->fields->ArticleType)); 
  ?>
</div>


<div class="ui bottom attached tab " data-tab="third">



<form class="ui form" id="friendform">
  <div class="field">
    <label class="required">友链名称</label>
    <input type="text" id="friendname" name="friendname" placeholder="填写友链名称" required>
  </div>
  <div class="field">
    <label class="required">友链网址</label>
    <input type="text" id="friendurl" name="friendurl" placeholder="填写友链网址,包含http(s)://" required>
  </div>
  <div class="field">
    <label <?php if(Bsoptions('FriendLinkLogomust') == true):?>class="required"<?php endif;?>>友链图标</label>
    <input type="text" id="friendpic" name="friendpic" placeholder="填写友链图标地址,包含http(s)://">
  </div>
 
  <div class="field">
    <label class="required">友链描述</label>
    <input type="text" id="frienddec" name="frienddec" placeholder="填写友链描述" required>
  </div>
  <div class="field">
    <label id="contm"<?php if(Bsoptions('FriendLinkEmailmust') == true):?> class="required"<?php endif;?>>联系邮箱</label>
    <input type="text" id="contactmail" name="contactmail" placeholder="填写联系邮箱" <?php if(empty(Bsoptions('FriendLinkLogoDefault'))):?>required<?php endif;?>>
  </div>
 <div class="field">
    <label class="required">您站点的友链放置页面网址</label>
    <input type="text" id="friendcheckurl" name="checkurl" placeholder="填写您站点的友链放置页面网址" required>
  </div>


<?php switch(Bsoptions('VerifyChoose')) :?>
<?php case '1' :?>
             <?php \BsCore\Plugin::spam_protection_mathjia();?>
            <div class="field">
      <label class="required">若提交,则表示您已阅读本站友链相关说明并严格遵守本站关于友链的相关要求</label>
</div>
   <div class="ui button" id="friendbtn" >提交</div>
             <?php break; ?>
             <?php case '11' :?>
             <?php \BsCore\Plugin::spam_protection_mathjian();?>
             <div class="field">
      <label class="required">若提交,则表示您已阅读本站友链相关说明并严格遵守本站关于友链的相关要求</label>
</div>
<div class="ui button" id="friendbtn" >提交</div>
             <?php break; ?>
              <?php case '22-2' :?>
             <?php \BsCore\Plugin::spam_protection_question();?>
            <div class="field">
      <label class="required">若提交,则表示您已阅读本站友链相关说明并严格遵守本站关于友链的相关要求</label>
</div>
<div class="ui button" id="friendbtn">提交</div>
             <?php break; ?>
             <?php case '2' :?>
          
             
             <?php $this->need('modules/Verify/BearCaptcha/Vaptcha/Vaptcha_Link.php'); ?>
    <div class="field">
      <label class="required">若提交,则表示您已阅读本站友链相关说明并严格遵守本站关于友链的相关要求</label>
</div>
<div class="ui button" id="friendbtn"  style="pointer-events:none">提交</div>
             <?php break; ?>
             <?php case '2-1' :?>
       
             <?php $this->need('modules/Verify/BearCaptcha/Dingxiang/DX_Captcha_Link.php'); ?>
             <div class="field">
      <label class="required">若提交,则表示您已阅读本站友链相关说明并严格遵守本站关于友链的相关要求</label>
</div>
<div class="ui button" id="friendbtn"  style="pointer-events:none">提交</div>
             <?php break; ?>
             <?php case '2-2' :?>
            
             <?php $this->need('modules/Verify/BearCaptcha/Turnstile/Turnstile_Link.php'); ?>
             <div class="field">
      <label class="required">若提交,则表示您已阅读本站友链相关说明并严格遵守本站关于友链的相关要求</label>
</div>
<div class="ui button" id="friendbtn"  style="pointer-events:none">提交</div>
             <?php break; ?>
              <?php case '2-3' :?>
          
             <?php $this->need('modules/Verify/BearCaptcha/Geetest/Geetest_Link.php'); ?>
             <div class="field">
      <label class="required">若提交,则表示您已阅读本站友链相关说明并严格遵守本站关于友链的相关要求</label>
</div>
	<div class="ui button" id="friendbtn"  style="pointer-events:none">提交</div>
             <?php break; ?>
             <?php case '22' :?>
          
             <p>
             <span class="ui button" id="bsverify" onclick="verify();" style="float:left;" disabled><i class="cloudversify icon"></i><?php if(empty(Bsoptions('Verify22_Buttontext'))){echo '人机验证';}else{
			echo Bsoptions('Verify22_Buttontext');}
			  ?></span>
			  <div class="field"><div style="height:5px"></div>
      <label class="required">若提交,则表示您已阅读本站友链相关说明并严格遵守本站关于友链的相关要求</label>
</div>
			  <div class="ui button" id="friendbtn"  style="pointer-events:none">提交</div>

</p>
<?php $this->need('modules/Verify/BearCaptcha/Captcha/Captcha_Link.php'); ?>
             <?php break; ?>
     <?php case '3' || '' :?>
      <div class="field">
  <label class="required">验证码</label>
   <div id="captcha">
    </div>
        <div class="field">
    <input style="width:200px" type="text" id="captcha_verify" name="captcha_verify" placeholder="输入验证码,区分大小写">
  </div>
  </div>
         <div class="field">
      <label class="required">若提交,则表示您已阅读本站友链相关说明并严格遵守本站关于友链的相关要求</label>
</div>
         <div class="ui button" id="friendbtn">提交</div>
             <?php break; ?>
 <?php endswitch; ?>
  
</form>


</div>

</div>
<script>
$(function(){
    <?php if(Bsoptions('VerifyChoose') == '3'||Bsoptions('VerifyChoose') == '') :?>
    createCaptcha();
    <?php endif; ?>
    getTokenInfo();
    $('#friendbtn').on('click',function(){
        let friendName = document.getElementById('friendname');
    let friendUrl = document.getElementById('friendurl');
    let friendPic = document.getElementById('friendpic');
    let friendDec = document.getElementById('frienddec');
    let contactMail = document.getElementById('contactmail');
    let checkurl = document.getElementById('friendcheckurl');
    <?php if(Bsoptions('VerifyChoose') == '3'||Bsoptions('VerifyChoose') == '') :?>
    let captcha = document.getElementById('captcha_verify');
    <?php endif; ?>
    let rejectWord = new RegExp("[`~!#$^*|{}';',\\[\\]<>《》~!#¥……*()——|{}【】‘;:”“'。,、? ]");
    let checkmail = /^[a-zA-Z0-9]+([-_.][A-Za-zd]+)*@([a-zA-Z0-9]+[-.])+[A-Za-zd]{2,5}$/;
    if($("#contm").hasClass('required')){
    if(contactMail.value == ''){
        $('body').toast({
							    title:'温馨提示~',
							    class: 'warning',
							    message: '您的联系邮箱为空,请务必填写哦', 
							    showIcon: 'flushed outline',
							    showProgress: 'top',
										});
        return false;
    }    
    
    if(rejectWord.test(contactMail.value) ||checkmail.test(contactMail.value) == false){
        $('body').toast({
							    title:'温馨提示~',
							    class: 'warning',
							    message: '您的联系邮箱包含违规字符,也有可能是非邮箱正确格式哦', 
							    showIcon: 'flushed outline',
							    showProgress: 'top',
										});
        return false;
    }
    };
    if(friendName.value == '' || rejectWord.test(friendName.value)){
        $('body').toast({
							    title:'温馨提示~',
							    class: 'warning',
							    message: '您的友链名称为空或者包含违规字符哦', 
							    showIcon: 'flushed outline',
							    showProgress: 'top',
										});
        return false;
    }
    else if(friendUrl.value == '' || rejectWord.test(friendUrl.value)){
        $('body').toast({
							    title:'温馨提示~',
							    class: 'warning',
							    message: '您的友链网址为空或者包含违规字符哦', 
							    showIcon: 'flushed outline',
							    showProgress: 'top',
										});
        return false;
    }
    else if(checkurl.value == '' || rejectWord.test(checkurl.value)){
        $('body').toast({
							    title:'温馨提示~',
							    class: 'warning',
							    message: '您站点的友链放置页面网址为空或者包含违规字符哦', 
							    showIcon: 'flushed outline',
							    showProgress: 'top',
										});
        return false;
    }
   
    else if(friendDec.value == ''){
        $('body').toast({
							    title:'温馨提示~',
							    class: 'warning',
							    message: '您的友链描述为空或者包含违规字符哦', 
							    showIcon: 'flushed outline',
							    showProgress: 'top',
										});
        return false;
    }
    <?php if(Bsoptions('VerifyChoose') == '3'||Bsoptions('VerifyChoose') == '') :?>
    else if(captcha.value == ''){
        $('body').toast({
							    title:'温馨提示~',
							    class: 'warning',
							    message: '验证码为空哦', 
							    showIcon: 'flushed outline',
							    showProgress: 'top',
										});
        return false;
    }
    else if(captcha.value !== code){
        $('body').toast({
							    title:'温馨提示~',
							    class: 'warning',
							    message: '验证码错误,请重新输入', 
							    showIcon: 'flushed outline',
							    showProgress: 'top',
										});
										createCaptcha();
        return false;
    };
    <?php endif; ?>
    $("#friendbtn").css("pointer-events","none").addClass('loading');
                    $.ajax({
                        type: "POST",
                        url: "<?php getFriendFile(); ?>",
                        data: $("#friendform").serialize(),
                        dateType: "json",
                        success: function(json) {
                            jsonx = json.trim();
                            result = JSON.parse(jsonx);
                       switch(result.code){  
                           case 1:
                    $('body').toast({
							    title:'提交成功',
							    class: 'green',
							    message: '您的友链申请已提交成功,请等待博主审核~', 
							    showIcon: 'grin beam outline',
							    showProgress: 'top',
										});
										window.location.reload();
										break;
						case 0:
						$('body').toast({
							    title:'提交失败',
							    class: 'warning',
							    message: result.msg, 
							    showIcon: 'flushed outline',
							    showProgress: 'top',
										});
						$("#friendbtn").css("pointer-events","auto").removeClass('loading');
						    break;
                       }
                        },
                        error: function() {
                            alert("提交过程中出现错误,请稍后再试~~");
                        }
                    });
});
 });
function getTokenInfo(){
     $.post('<?php getCommentAction(); ?>',{action:'getCommentToken'},function (res) {
         res = JSON.parse(res);
         if(res.code == 1){
             $("#SecurityToken").remove();
        var tokenInput=$("<input type=\"hidden\" name=\"SecurityToken\"  id=\"SecurityToken\">");
tokenInput.attr("value", res.token);
$("#friendform").append(tokenInput);
         }
    })
}
var code;
function createCaptcha() {
  document.getElementById('captcha').innerHTML = "";
  var charsArray =
  "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@!#$%^&*";
  var lengthOtp = 6;
  var captcha = [];
  for (var i = 0; i < lengthOtp; i++) {
    var index = Math.floor(Math.random() * charsArray.length + 1);
    if (captcha.indexOf(charsArray[index]) == -1)
    captcha.push(charsArray[index]);else
    i--;
  }
  var canv = document.createElement("canvas");
  canv.id = "captcha";
  canv.width = 100;
  canv.height = 50;
  var ctx = canv.getContext("2d");
  ctx.font = "25px Georgia";
  ctx.strokeText(captcha.join(""), 0, 30);
  code = captcha.join("");
  document.getElementById("captcha").appendChild(canv);
}
 </script> 

</div>

</div>
<?php $this->need('compoment/sidebar.php'); ?>
<?php $this->need('compoment/foot.php'); ?>

3、参数说明

LinkCare 的核心功能是其高级规则引擎,允许用户创建复杂的外链处理规则。

每个规则由以下部分组成:

  • 规则名称:规则的标识名称
  • 启用状态:是否启用该规则
  • 条件组:一个或多个条件判断
  • 动作:满足条件时执行的操作

1. 规则名称

例如:全局外链+跳转中间页

2. 条件类型

规则可以设置多个条件组,每个条件组包含:

  1. 条件类型(if/else if/else)
  2. 逻辑关系(AND/OR)
  3. 多个具体条件

| 字段类型 | 说明 | 示例 |
| ----- | ---------- | ---------------- |
| 域名 | 匹配链接的域名部分 | null |
| URL | 匹配完整URL | null |
| Rel属性 | 匹配链接的rel属性 | nofollow |
| URL路径 | 匹配URL路径部分 | /path/to/page |
| 查询参数 | 匹配URL查询参数 | utm_source=xxx |
| 页面路径 | 匹配当前页面路径 | /category/tech |
| 页面类型 | 匹配当前页面类型 | post, page, home |
| 页面名称 | 匹配当前页面名称 | about |
| 特殊页面 | 判断是否为特殊页面 | true/false |

3. 操作符类型

|操作符|说明|
|---|---|
|匹配|通配符匹配(支持*和?)|
|包含|字符串包含匹配|
|不包含|字符串不包含匹配|
|等于|完全相等匹配|
|不等于|不完全相等匹配|
|开始于|字符串开始部分匹配|
|结束于|字符串结束部分匹配|
|正则匹配|正则表达式匹配|

4. 动作配置

规则可以配置多种动作,包括:

链接属性类动作

|动作类型|功能说明|配置参数|
|---|---|---|
|添加Rel属性|为链接添加rel属性|选择要添加的属性值(nofollow、noopener等)|
|设置新窗口打开|设置target="_blank"|开关控制|
|添加CSS类|为链接添加CSS类名|输入类名|
|添加Title属性|为链接添加title属性|输入title内容|
|添加Referer|为链接添加自定义Referer|输入Referer值|

安全控制类动作

|动作类型|功能说明|配置参数|
|---|---|---|
|启用重定向|启用跳转功能|开关控制|
|添加到黑名单|将链接加入黑名单|选择处理方式(禁用/警告)|
|强制HTTPS|将HTTP链接转换为HTTPS|开关控制|

参数处理类动作

|动作类型|功能说明|配置参数|
|---|---|---|
|添加UTM参数|为链接添加UTM追踪参数|配置各UTM参数值|
|添加自定义参数|添加自定义查询参数|配置参数名和值|
|移除追踪参数|移除常见追踪参数|开关控制|
|移除自定义参数|移除指定的查询参数|输入参数名列表|
|移除UTM参数|移除所有UTM参数|开关控制|

特殊标识类动作

|动作类型|功能说明|配置参数|
|---|---|---|
|SVG后缀标识|为SVG链接添加特殊标识|配置后缀文本和Base64代码|

5. 示例

参考 操作示例

4. 监控页面

代码: 全选

graph TD
    A[监控页面] --> B[统计概览]
    A --> C[筛选区域]
    A --> D[链接列表]
    A --> E[分页控件]
    C --> F[搜索框]
    C --> G[显示数量选择]
    C --> H[刷新缓存按钮]
    D --> I[链接信息]
    D --> J[域名信息]
    D --> K[状态标签]
    D --> L[文章信息]

状态标签说明

标签说明
nofollow链接包含rel="nofollow"属性
noopener链接包含rel="noopener"属性
noreferrer链接包含rel="noreferrer"属性
target_blank链接在新窗口打开
blacklisted链接被加入黑名单
sponsored链接为赞助链接
svg-link链接指向SVG文件

四、操作示例

示例1:为所有外链添加 nofollow

  1. 进入插件设置页面
  2. 点击「新建规则」
  3. 设置规则名称为"添加nofollow"
  4. 添加条件:
    • 字段:domain
    • 操作符:not_equals
    • 值:[你的网站域名]
  5. 添加动作:
    • 类型:add_rel
    • 值:nofollow
  6. 保存规则并启用

示例2:为指定域名添加特殊样式

  1. 进入插件设置页面
  2. 点击「新建规则」
  3. 设置规则名称为"合作伙伴链接"
  4. 添加条件:
    • 字段:domain
    • 操作符:equals
    • 值:partner.com
  5. 添加动作:
    • 类型:add_class
    • 值:partner-link
    • 类型:add_rel
    • 值:sponsored
  6. 保存规则并启用

示例3:屏蔽特定网站链接

  1. 进入插件设置页面
  2. 点击「新建规则」
  3. 设置规则名称为"屏蔽恶意网站"
  4. 添加条件:
    • 字段:domain
    • 操作符:contains
    • 值:malware-site.com
  5. 添加动作:
    • 类型:add_to_blacklist
    • 模式:disable
    • 文本:[该链接已被屏蔽]
  6. 保存规则并启用

示例4:为社交媒体链接设置新窗口打开

  1. 进入插件设置页面
  2. 点击「新建规则」
  3. 设置规则名称为"社交媒体链接"
  4. 添加条件(使用 OR 逻辑):
    • 条件1:
      • 字段:domain
      • 操作符:contains
      • 值:facebook.com
    • 条件2:
      • 字段:domain
      • 操作符:contains
      • 值:twitter.com
  5. 添加动作:
    • 类型:target_blank
  6. 保存规则并启用

五、下载方式

[bsgit user="TGU-HansJack"]typecho-linkcare-plugin[/bsgit]

点点⭐

六、参考文章

  1. HTML attribute: rel
  2. Google Analytics 4 的UTM参数使用指南(2025) | GA小站
  3. 中央网信办部署开展2024年“清朗”系列专项行动_中央网络安全和信息化委员会办公室
  4. 关于本站外链处理的思考与行动 - 技术 - 南蛮子懋和 - 懋和道人-李懋和,俗名李栋梁。书法、国画爱好者,互联网安全与前端建设者。