子比主题开发文档
使用指南Codestar Framework主题扩展在线部署AI 功能推荐插件赞助打赏

分享、海报与微信分享

扩展子比主题分享平台、移动端分享弹窗、海报生成数据、论坛/商城海报适配和微信 JS-SDK 分享。

模块定位

子比主题的分享能力主要由三部分组成:

能力入口
分享按钮、平台排序、复制链接inc/functions/zib-share.php
海报分享 Ajax 数据action/function.phpposter_share_data
微信内 JS-SDK 分享inc/functions/zib-share-wechat.phpjs/weixin-share.js

分享按钮不是单纯的 HTML 片段。它会读取后台 sorter 排序、根据移动端切换弹窗或 hover 面板、为海报按钮输出 poster-share 属性、为复制按钮输出 data-clipboard-text,并在开启返利时把 URL 改成当前用户的返利链接。

后台开关

后台「社交分享」配置集中在 inc/options/admin-options.php

配置 key作用
share_s普通文章页是否显示分享按钮,论坛和商城不受这个开关影响
share_itemsCSF sorter,控制分享平台启用状态和排序
wechat_share_s是否启用微信 JS-SDK 分享有图
wechat_share_option微信 AppID、AppSecret、是否强制使用站点图标
share_img是否启用海报分享
share_img_compatible_s是否把图片转 base64,提高跨域和缓存环境兼容性
share_img_byimg海报默认封面图
share_logo海报底部 Logo
share_desc海报底部文案

share_items 的默认启用项由主题函数提供:

function zib_get_share_items_sorter_default()
{
    $labels = zib_get_share_platform_labels();

    return array(
        'enabled'  => array(
            'qzone'  => $labels['qzone'],
            'weibo'  => $labels['weibo'],
            'qq'     => $labels['qq'],
            'poster' => $labels['poster'],
            'copy'   => $labels['copy'],
        ),
        'disabled' => array(
            'facebook' => $labels['facebook'],
            'twitter'  => $labels['twitter'],
            'telegram' => $labels['telegram'],
            'whatsapp' => $labels['whatsapp'],
            'linkedin' => $labels['linkedin'],
            'reddit'   => $labels['reddit'],
            'line'     => $labels['line'],
            'vk'       => $labels['vk'],
            'email'    => $labels['email'],
        ),
    );
}

如果 share_img 关闭,主题会从启用平台中移除 poster,海报按钮和平台注册表都会同步隐藏海报项。

分享入口

文章和分类的分享入口分别由两个函数输出:

zib_get_post_share_btn($post, $class, $modal);
zib_get_term_share_btn($term, $class, $modal);

它们内部会判断移动端:

  • 移动端或 $modal = true:输出 zib_get_refresh_modal_link(),Ajax action 为 share_modal
  • PC 端:输出 .hover-show.dropup,分享面板在 hover/dropup 容器里展示。

文章分享数据来自:

function zib_get_posts_share_btns($btns_opt, $post = null)
{
    $post = get_post($post);

    if (empty($post->ID)) {
        return;
    }

    $subtitle = trim(strip_tags(zib_get_post_meta($post->ID, 'subtitle', true)));
    $title    = trim(strip_tags(get_the_title($post))) . $subtitle;
    $desc     = zib_get_excerpt(160, '...', $post);
    $pic      = zib_post_thumbnail('full', '', true, $post);
    $url      = get_permalink($post);

    return zib_get_share_btns($btns_opt, $title, $desc, $pic, $url, $post->ID);
}

分类、标签、专题等 term 分享数据来自 term 名称、描述、封面图和 term 链接:

$title = trim(strip_tags($term->name));
$desc  = zib_str_cut($term->description, 0, 160, '...');
$pic   = zib_get_taxonomy_img_url($term->term_id, 'full');
$url   = get_term_link($term);

分享平台注册

主题支持的平台 key 在 zib_get_share_platform_labels() 中定义:

key平台
qzoneQQ 空间
weibo微博
qqQQ 好友
facebookFacebook
twitterX
telegramTelegram
whatsappWhatsApp
linkedinLinkedIn
redditReddit
lineLINE
vkVK
email邮件
poster海报分享
copy复制链接

启用平台会先读取 share_items,再经过 zib_active_share_items 过滤:

function zib_docs_share_items_filter($items)
{
    if (!in_array('copy', $items)) {
        $items[] = 'copy';
    }

    return $items;
}
add_filter('zib_active_share_items', 'zib_docs_share_items_filter');

平台注册表由 zib_get_share_platform_registry() 生成。每个平台包含 texticonhreftargetattr

$registry[$key] = array(
    'text'   => $text,
    'icon'   => zib_get_svg($key . '-color'),
    'href'   => zib_build_share_platform_href($key, $ctx),
    'target' => '_blank',
    'attr'   => '',
);

postercopy 是特殊项:

if ('poster' === $key) {
    $item['target'] = '';
    $item['attr']   = 'poster-share="' . esc_attr($poster_id) . '"';
} elseif ('copy' === $key) {
    $item['target'] = '';
    $item['attr']   = 'data-clipboard-text="' . esc_url($ctx['url']) . '" data-clipboard-tag="' . esc_attr(__('链接', 'zib_language')) . '"';
}

新增分享平台时,不要改主题核心函数,使用 zib_share_platform_registry

function zib_docs_add_share_platform($registry, $ctx, $poster_id)
{
    $registry['custom_link'] = array(
        'text'   => __('自定义分享', 'zib_language'),
        'icon'   => zib_get_svg('share'),
        'href'   => zib_build_share_query_url('https://example.com/share', array(
            'url'   => $ctx['url'],
            'title' => $ctx['link_title'],
        )),
        'target' => '_blank',
        'attr'   => '',
    );

    return $registry;
}
add_filter('zib_share_platform_registry', 'zib_docs_add_share_platform', 10, 3);

同时把平台 key 放入启用项:

function zib_docs_enable_share_platform($items)
{
    $items[] = 'custom_link';
    return array_values(array_unique($items));
}
add_filter('zib_active_share_items', 'zib_docs_enable_share_platform');

分享链接与返利

主题不会直接把平台 query 手写到字符串里,而是先通过 zib_build_share_query_url() 编码:

function zib_build_share_query_url($base, $params = array())
{
    if (!$params) {
        return $base;
    }

    $query = array();
    foreach ($params as $key => $value) {
        if ($value === '' || $value === null) {
            continue;
        }
        $query[] = rawurlencode($key) . '=' . rawurlencode($value);
    }

    $separator = (false !== strpos($base, '?')) ? '&' : '?';

    return $base . $separator . implode('&', $query);
}

这样标题、描述、图片和 URL 里包含中文、空格、&# 时不会破坏平台链接。扩展平台时优先复用这个函数,不要自己拼 ?url=

分享链接还会联动返利系统:

$user_id = get_current_user_id();
if (_pz('pay_rebate_s') && $user_id) {
    $url = zibpay_get_rebate_link($user_id, $url);
}

这段在 zib_get_share_btns() 中执行,所以普通分享平台、复制链接和海报入口拿到的都是最终分享 URL。海报 Ajax 里还会再做一次同样判断,保证二维码也带返利链接。自定义分享入口如果绕过 zib_get_share_btns(),就要自己补上这段逻辑,否则会出现按钮链接带返利、海报二维码不带返利,或反过来的不一致。

分享弹窗

移动端分享弹窗通过 Ajax 动作 share_modal 加载:

add_action('wp_ajax_share_modal', 'zib_ajax_share_modal');
add_action('wp_ajax_nopriv_share_modal', 'zib_ajax_share_modal');

请求参数:

参数说明
id文章 ID 或 term ID
typepostterm

弹窗内容会调用 zib_get_share('horizontal', $post)zib_get_term_share('horizontal', $term),标题文案可以通过 share_modal_header_text 过滤:

function zib_docs_share_modal_header($text)
{
    return __('分享给朋友', 'zib_language');
}
add_filter('share_modal_header_text', 'zib_docs_share_modal_header');

分享弹窗高度由启用项数量估算:

function zib_get_share_modal_height()
{
    $count = max(1, count(zib_get_active_share_items()));
    return min(520, 180 + (int) ceil($count / 5) * 72);
}

所以启用平台过多时,移动端弹窗会变高。后台提示中也建议同时启用的平台不要过多。

弹窗动作同时注册了 nopriv。它只适合输出公开分享按钮,不应该在 share_modal_header_text 或后续按钮过滤里读取当前登录用户的隐私数据。如果某个分享入口只允许登录用户使用,应在生成按钮前判断权限,而不是等 Ajax 弹窗打开后再拦截。

海报分享数据

海报按钮只负责输出属性:

<a class="share-btn poster" poster-share="123" href="javascript:;">...</a>

前端 js/poster-share.js 监听 [poster-share] 点击,创建 Bootstrap modal,然后请求:

var ajax_data = {
    action: 'poster_share_data',
    id: _id,
};

后端 Ajax:

add_action('wp_ajax_poster_share_data', 'zib_ajax_poster_share_data');
add_action('wp_ajax_nopriv_poster_share_data', 'zib_ajax_poster_share_data');

普通文章、term、返利用户会生成不同的数据:

poster-share数据来源典型场景
123get_post(123)文章、商品、帖子、版块等 post 对象
term_12get_term(12)分类、标签、专题、论坛话题
rebate_8get_userdata(8) + 返利配置用户推荐返利海报
user_8get_userdata(8) + 返利配置作者或用户推荐海报

后端按这些分支组装基础数据,再统一返回:

字段说明
url最终分享链接,可能已经加入返利参数
qrcode二维码 base64
banner主封面图
banner_default默认封面图
banner_spare主题兜底图
logo海报底部 Logo
title海报标题
content海报描述
tags作者、分类、统计等标签
description底部提示文案

普通文章分支会把标题截到 32 字,描述取 zib_get_excerpt(70, '...', $post),文章类型为 post 时补作者和分类:

$subtitle = trim(strip_tags(zib_get_post_meta($post->ID, 'subtitle', true)));
$title    = trim(strip_tags($post->post_title)) . $subtitle;
$title    = zib_str_cut($title, 0, 32);

$desc       = zib_get_excerpt(70, '...', $post);
$url        = get_permalink($post);
$banner_url = zib_post_thumbnail('full', '', true, $post);

term 分支会读取 term 名称、描述、封面和文章数:

$title      = trim(strip_tags($term->name));
$desc       = zib_str_cut($term->description, 0, 160, '...');
$banner_url = zib_get_taxonomy_img_url($term->term_id, 'full', '');
$url        = get_term_link($term);
$tags       = $term->count ? sprintf(__('%s篇文章', 'zib_language'), $term->count) : '';

返利分支不读取文章,而是使用返利海报配置:

$url        = zibpay_get_rebate_link($user_id);
$banner_url = _pz('pay_rebate_poster_img');
$title      = _pz('pay_rebate_poster_title');
$desc       = _pz('pay_rebate_poster_desc');

主题在返回前提供 poster_share_data 过滤器:

$data = apply_filters('poster_share_data', $data, $obj, $id);

扩展海报时优先改这份数据,不要改 poster-share.js 的绘图流程:

function zib_docs_poster_share_data_filter($data, $obj, $id)
{
    if (isset($obj->post_type) && 'post' === $obj->post_type) {
        $subtitle = zib_get_post_meta($obj->ID, 'subtitle', true);

        if ($subtitle) {
            $data['title'] = zib_str_cut(strip_tags($obj->post_title . ' ' . $subtitle), 0, 32);
        }
    }

    return $data;
}
add_filter('poster_share_data', 'zib_docs_poster_share_data_filter', 10, 3);

如果站点图片使用 OSS、CDN 或防盗链,海报可能因为 canvas 跨域污染而无法导出。主题提供 share_img_compatible_s,开启后会把 bannerbanner_defaultlogo 转为 base64:

if (_pz('share_img_compatible_s')) {
    $data['banner']         = zib_get_img_auto_base64($data['banner']);
    $data['banner_default'] = zib_get_img_auto_base64($data['banner_default']);
    $data['logo']           = zib_get_img_auto_base64($data['logo']);
}

前端 poster-share.js 使用三层主图兜底:

  1. banner:当前内容主图。
  2. banner_default:后台海报默认封面图。
  3. banner_spare:主题内置 img/share_img.jpg
if (loadCount == 0) {
    config.banner ? (image.src = config.banner) : (loadCount = 1);
}
if (loadCount == 1) {
    config.banner_default ? (image.src = config.banner_default) : (loadCount = 2);
}
if (loadCount == 2) {
    image.src = config.banner_spare;
}

前端还会根据 type = product 切换商品海报布局,普通文章海报和商品海报不是两个 Ajax。扩展新海报类型时,可以继续增加 type,但要同步确认前端绘制逻辑是否识别这个类型;如果只是改标题、标签、封面,直接过滤 poster_share_data 更稳。

论坛和商城适配

海报数据是跨模块协议。论坛和商城没有复制一套海报生成器,而是通过 poster_share_data 改写字段。

商城商品会把海报类型改成 product,并补充价格、原价和积分支付状态:

function zib_shop_poster_share_data_filter($data, $obj, $id)
{
    if (isset($obj->post_type) && 'shop_product' == $obj->post_type) {
        $data['type']           = 'product';
        $data['is_points']      = $pay_modo === 'points';
        $data['price']          = (string) $price;
        $data['original_price'] = $start_price > $price ? (string) $start_price : '';
        $data['banner']         = zib_shop_get_product_thumbnail_url($obj);
    }

    return $data;
}
add_filter('poster_share_data', 'zib_shop_poster_share_data_filter', 10, 3);

论坛会针对帖子、版块、话题、标签和版块分类补充作者、版块、帖子数、回复数和热度:

function zib_docs_bbs_poster_tags($data, $obj, $id)
{
    if (isset($obj->post_type) && 'forum_post' === $obj->post_type) {
        $author = get_userdata($obj->post_author);

        if (!empty($author->display_name)) {
            $data['tags'] = sprintf(__('作者: %s', 'zib_language'), esc_attr($author->display_name));
        }
    }

    return $data;
}
add_filter('poster_share_data', 'zib_docs_bbs_poster_tags', 20, 3);

扩展自己的业务类型时,建议也走同一个过滤器。这样前端 modal、下载按钮、二维码、跨域兼容和错误提示都能沿用主题已有逻辑。

分享按钮本体也被多个业务模块复用:

模块入口函数
普通文章底部zib_get_post_share_btn(null, 'action action-share')
移动端文章底栏zib_get_post_share_btn($post, 'tabbar-item single-action-tabbar')
论坛帖子zib_get_post_share_btn($post, 'btn-share ' . $class, $modal)
论坛版块zib_get_post_share_btn($plate_id, 'btn-share ' . $class, true)
论坛话题/标签/分类zib_get_term_share_btn($term, $class . ' btn-share', true)
商城商品zib_get_post_share_btn($post_id, 'btn-share ' . $class, true)

因此新增平台、排序和复制协议应在 zib_get_share_platform_registry() 这一层扩展。不要只改文章页模板,否则论坛、商城和移动端入口不会同步。

微信 JS-SDK 分享

微信分享由 wechat_share_s 控制。开启后,主题实例化:

if (_pz('wechat_share_s')) {
    new ZibWeChatShare(_pz('wechat_share_option'));
}

构造函数要求 appidapp_secret 存在,然后在 wp_footer 输出 window.WeChatShareDate

window.WeChatShareDate = {
    appId: '...',
    timestamp: '...',
    nonceStr: '...',
    signature: '...',
    url: '...',
    title: '',
    img: '...',
    desc: '',
}

前端 js/weixin-share.js 会读取这份数据,并调用:

wx.updateTimelineShareData({
    title: _title,
    link: _link,
    imgUrl: _img,
});
wx.updateAppMessageShareData({
    title: _title,
    desc: _desc,
    link: _link,
    imgUrl: _img,
});

微信分享图来源顺序:

页面图片来源
taxonomyzib_get_taxonomy_img_url($obj_id, 'full')
singlezib_post_thumbnail('full', '', true, $obj_id)
author用户 custom_avatar
兜底_pz('iconpng')_pz('favicon')

如果开启 only_logo,所有页面都会使用站点图标,适合开启图片防盗链、封面图不稳定或希望统一品牌图的站点。

签名流程会缓存本地 token/ticket,并在过期前约 300 秒刷新。扩展时不要在每次页面加载都请求微信接口,否则容易触发频率限制。

安全与兼容边界

  • 分享 URL、标题、描述、图片都要在输出前转义,URL 使用 esc_url(),属性使用 esc_attr()
  • 新增外部分享平台时,跳转链接用 zib_build_share_query_url()add_query_arg() 拼装,不手写未编码 query。
  • 不要把未公开内容、付费内容正文、用户隐私字段写进海报数据。
  • 海报封面要准备默认图和兜底图,避免无图文章生成失败。
  • 使用 CDN、OSS、防盗链时优先配置跨域规则;仍有兼容问题再考虑 share_img_compatible_s
  • poster_share_data 同时允许未登录请求,不要在过滤器里输出仅登录用户可见的敏感数据。
  • 微信 JS-SDK 的安全域名必须和当前访问域名匹配,移动端微信内分享才会生效。

落地清单

目标检查点
新增分享平台zib_share_platform_registryzib_active_share_items、图标、URL 编码、target
改分享排序后台 share_items sorter,不改模板
改移动端弹窗文案share_modal_header_text
改文章海报字段poster_share_data
改返利分享zibpay_get_rebate_link() 链路是否保持一致
适配商品或论坛内容复用 poster_share_data,必要时设置 type = product
处理跨域图片OSS/CDN CORS、默认图、兜底图、share_img_compatible_s
微信分享有图wechat_share_s、AppID、AppSecret、JS 接口安全域名、分享图来源

本页根据 inc/functions/zib-share.phpaction/function.phpjs/poster-share.jsinc/functions/shop/inc/functions.phpinc/functions/bbs/inc/functions.phpinc/functions/zib-share-wechat.phpjs/weixin-share.jsinc/functions/zib-single.phpinc/functions/zib-footer.phpinc/options/admin-options.php 蒸馏整理。

On this page