Files
wordpress-theme-aksrt/inc/helpers/aksrt-links.php
T
kate522 c472045ce4 fix(auth): align the social-login surfaces with the reference theme
The "other login methods" block drifted from zibll on three axes. Gitee was
mapped to fa-code-branch because FA5 Free ships no Gitee brand glyph, so the
button showed a generic branch icon; Google showed the monochrome FA glyph
instead of the reference's multi-colour mark. The modal also printed an
implicit agreement line under the social buttons, which the reference guest
card does not have. And the top-nav user popover built its own
.social-login-divider / .oauth-row / .oauth-btn markup and then wrapped that
in a second separator and .social_loginbar, so the block rendered nested and
unlike the reference.

aksrt_brand_icon() now routes zibsvg-/customsvg- values through the shared
icon renderer, and the brand map resolves gitee to zibsvg-gitee and google to
zibsvg-google-color. Both sprite keys already exist in the runtime symbol
sheet, which the loader pulls in on every frontend page. Because
get_auth_providers_data() and binding_row() both go through
aksrt_brand_icon(), one map change fixes the icon on the modal, the user card,
the top-nav popover, the user-center binding list and the profile social
links.

render_social_buttons() is rewritten to the reference shape: an optional
.social-separator, a .social_loginbar, and one .social-login-item <type>
toggle-radius anchor per enabled provider with rel="nofollow" and a
"<label>登录" title. oauth_button_lg switches to the button-lg variant with a
label, and the WeChat entry carries qrcode-signin so auth.js opens the QR
panel instead of navigating, matching the reference. The navbar and the link
apply modal now append the block verbatim instead of re-wrapping it, and the
user-card loop adopts the same item attributes. The retired
.social-login-divider / .oauth-row / .oauth-btn rules are dropped from
base-1.css, and the binding-list icon rule gains an svg selector so the
sprite icons keep the 22px the FA glyphs had.

Verification: node --test across widget-system, navigation, admin,
asset-ledger, dev, front-submit and visual (613 tests), plus php -l on every
changed PHP file. Extended the auth source contract with the agreement-line,
brand-icon and popover-structure cases, and pinned the user-card guest
markup.
2026-10-17 14:42:42 +08:00

485 lines
21 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* 网址导航 / 书签链接渲染(legacy pages/links.php + legacy parity).
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
if (!function_exists('aksrt_go_link')) :
/**
* Wrap external URL with /go/ intermediate page when enabled.
*/
function aksrt_go_link(string $url, bool $force = false): string {
$url = esc_url_raw($url);
if ($url === '') {
return '';
}
if (!$force && class_exists('\AKSRT\Settings\Settings') && empty(\AKSRT\Settings\Settings::get_value('seo_go_link', true))) {
return $url;
}
if (class_exists('\AKSRT\Core\GoRedirect')) {
return \AKSRT\Core\GoRedirect::get_instance()->create_go_link($url);
}
return $url;
}
endif;
if (!function_exists('aksrt_get_page_links_settings')) :
/**
* Read page_links_* meta for a page.
*
* @return array<string,mixed>
*/
function aksrt_get_page_links_settings(int $post_id = 0): array {
if ($post_id <= 0) {
$post_id = (int) get_queried_object_id();
}
$cats = get_post_meta($post_id, 'page_links_category', true);
if (!is_array($cats)) {
$cats = $cats !== '' && $cats !== null ? array_filter(array_map('intval', (array) $cats)) : [];
} else {
$cats = array_values(array_filter(array_map('intval', $cats)));
}
$search_types = get_post_meta($post_id, 'page_links_search_types', true);
if (!is_array($search_types)) {
$search_types = $search_types ? array_values(array_filter(array_map('sanitize_key', (array) $search_types))) : [];
} else {
$search_types = array_values(array_filter(array_map('sanitize_key', $search_types)));
}
$submit_cats = get_post_meta($post_id, 'page_links_submit_cats', true);
if (!is_array($submit_cats)) {
$submit_cats = $submit_cats !== '' && $submit_cats !== null ? array_filter(array_map('intval', (array) $submit_cats)) : [];
} else {
$submit_cats = array_values(array_filter(array_map('intval', $submit_cats)));
}
$style = sanitize_key((string) get_post_meta($post_id, 'page_links_style', true));
if (!in_array($style, ['card', 'bigcard', 'image', 'simple'], true)) {
$style = 'card';
}
$orderby = sanitize_key((string) get_post_meta($post_id, 'page_links_orderby', true));
if (!in_array($orderby, ['name', 'updated', 'rating', 'rand', 'id', 'length'], true)) {
$orderby = 'name';
}
$order = strtoupper((string) get_post_meta($post_id, 'page_links_order', true));
if (!in_array($order, ['ASC', 'DESC'], true)) {
$order = 'ASC';
}
$position = sanitize_key((string) get_post_meta($post_id, 'page_links_content_position', true));
if (!in_array($position, ['top', 'bottom'], true)) {
$position = 'top';
}
return [
'content_s' => (bool) get_post_meta($post_id, 'page_links_content_s', true),
'content_position' => $position,
'search_s' => (bool) get_post_meta($post_id, 'page_links_search_s', true),
'search_types' => $search_types,
'category' => $cats,
'style' => $style,
'limit' => max(0, (int) get_post_meta($post_id, 'page_links_limit', true)),
'orderby' => $orderby,
'order' => $order,
'go_s' => (bool) get_post_meta($post_id, 'page_links_go_s', true),
'blank_s' => (bool) get_post_meta($post_id, 'page_links_blank_s', true),
// Default nofollow ON (legacy default true).
'nofollow_s' => get_post_meta($post_id, 'page_links_nofollow_s', true) === ''
? true
: (bool) get_post_meta($post_id, 'page_links_nofollow_s', true),
'submit_s' => (bool) get_post_meta($post_id, 'page_links_submit_s', true),
'submit_sign_s' => get_post_meta($post_id, 'page_links_submit_sign_s', true) === ''
? true
: (bool) get_post_meta($post_id, 'page_links_submit_sign_s', true),
'submit_cats' => $submit_cats,
'submit_title' => (string) (get_post_meta($post_id, 'page_links_submit_title', true) ?: __('申请入驻', 'aksrt')),
'submit_dec' => (string) get_post_meta($post_id, 'page_links_submit_dec', true),
];
}
endif;
if (!function_exists('aksrt_links_box')) :
/**
* Render bookmark cards (legacy).
*
* @param array<int,object|array> $links Bookmark rows from get_bookmarks().
*/
function aksrt_links_box($links = [], string $type = 'card', bool $nofollow = true, bool $go_link = false, bool $blank_s = false): string {
if (empty($links) || !is_array($links)) {
return '';
}
$type = in_array($type, ['card', 'bigcard', 'image', 'simple'], true) ? $type : 'card';
$placeholder = get_template_directory_uri() . '/assets/img/placeholder.svg';
$html = '';
$i = 0;
foreach ($links as $raw) {
$link = (array) $raw;
if (empty($link['href']) && !empty($link['link_url'])) {
$link['href'] = $link['link_url'];
}
if (empty($link['title']) && !empty($link['link_name'])) {
$link['title'] = $link['link_name'];
}
if (empty($link['src']) && !empty($link['link_image'])) {
$link['src'] = $link['link_image'];
}
if (empty($link['desc']) && !empty($link['link_description'])) {
$link['desc'] = $link['link_description'];
}
if ($blank_s) {
$link['blank'] = true;
}
if (empty($link['blank']) && !empty($link['link_target'])) {
$link['blank'] = true;
}
$href = !empty($link['href']) ? (string) $link['href'] : '';
$title = !empty($link['title']) ? (string) $link['title'] : '';
if ($href === '' || $title === '') {
continue;
}
if ($go_link || !empty($link['go_link'])) {
$href = aksrt_go_link($href, true);
} else {
$href = esc_url($href);
}
$src = !empty($link['src']) ? esc_url((string) $link['src']) : '';
$blank = !empty($link['blank']) ? ' target="_blank"' : '';
$dec = !empty($link['desc']) ? (string) $link['desc'] : '';
$rel = $nofollow ? ' rel="nofollow noopener noreferrer"' : ' rel="noopener noreferrer"';
$img_src = $src !== '' ? $src : $placeholder;
$img = '<img' . aksrt_get_lazy_attr('lazy_other', $img_src, 'avatar') . ' alt="">';
$data_dec = $dec !== ''
? ' title="' . esc_attr($title) . '" data-content="' . esc_attr($dec) . '"'
: ' data-content="' . esc_attr($title) . '"';
if ($type === 'bigcard') {
$html .= '<div class="author-minicard links-card radius8 big-card">';
$html .= '<a' . $blank . ' href="' . esc_url($href) . '" title="' . esc_attr($dec) . '"' . $rel . '>';
$html .= '<div class="avatar-img link-img">' . $img . '</div>';
$html .= '<div class="text-ellipsis mt20 font-bold">' . esc_html($title) . '</div>';
if ($dec !== '') {
$html .= '<dd class="mt6 avatar-dest em09 muted-3-color text-ellipsis">' . esc_html($dec) . '</dd>';
}
$html .= '</a></div>';
} elseif ($type === 'image') {
$html .= '<a' . $blank . ' class="avatar-img link-img link-only-img" data-trigger="hover" data-toggle="popover" data-placement="top"'
. $data_dec . ' href="' . esc_url($href) . '"' . $rel . '>' . $img . '</a>';
} elseif ($type === 'simple') {
$sc = $i === 0 ? '' : 'icon-spot';
$html .= '<a' . $blank . ' class="' . esc_attr($sc) . '" data-trigger="hover" data-toggle="popover" data-placement="top"'
. $data_dec . ' href="' . esc_url($href) . '"' . $rel . '>' . esc_html($title) . '</a>';
} else {
$html .= '<div class="author-minicard links-card radius8"><a' . $blank . ' href="' . esc_url($href) . '" title="' . esc_attr($dec) . '"' . $rel . '>';
$html .= '<ul class="list-inline">';
$html .= '<li><div class="avatar-img link-img">' . $img . '</div></li>';
$html .= '<li><dl><dt class="text-ellipsis">' . esc_html($title) . '</dt>';
if ($dec !== '') {
$html .= '<dd class="mt3 avatar-dest em09 muted-3-color text-ellipsis">' . esc_html($dec) . '</dd>';
}
$html .= '</dl></li></ul></a></div>';
}
$i++;
}
return $html;
}
endif;
if (!function_exists('aksrt_page_links_html')) :
/**
* Build the multi/single category links block for a page.
*/
function aksrt_page_links_html(int $post_id = 0): string {
$post_id = $post_id > 0 ? $post_id : (int) get_queried_object_id();
$s = aksrt_get_page_links_settings($post_id);
$cats = $s['category'];
if (empty($cats)) {
if (current_user_can('manage_links')) {
return '<div class="mb20"><a class="author-minicard links-card radius8" href="' . esc_url(admin_url('link-manager.php')) . '" target="_blank" rel="noopener">'
. esc_html__('添加链接', 'aksrt') . '</a></div>';
}
return '<div class="author-minicard links-card radius8 mb20">' . esc_html__('暂无链接', 'aksrt') . '</div>';
}
$limit = (int) $s['limit'];
$orderby = (string) $s['orderby'];
$order = (string) $s['order'];
$style = (string) $s['style'];
$nofollow = (bool) $s['nofollow_s'];
$go_s = (bool) $s['go_s'];
$blank_s = (bool) $s['blank_s'];
$submit_s = (bool) $s['submit_s'];
$submit_title = (string) $s['submit_title'];
$is_mobile = wp_is_mobile();
$links_con = '';
if (count($cats) > 1) {
$link_cat_con = '';
$link_cat_nav = '';
foreach ($cats as $cat_id) {
$args = [
'orderby' => $orderby,
'order' => $order,
'limit' => $limit > 0 ? $limit : -1,
'category' => (int) $cat_id,
];
$_link = aksrt_links_box(get_bookmarks($args), $style, $nofollow, $go_s, $blank_s);
if (!$_link) {
continue;
}
$term = get_term((int) $cat_id, 'link_category');
if (!$term || is_wp_error($term)) {
continue;
}
$_id = 'links-' . (int) $cat_id;
$link_cat_con .= '<div class="mb20 links-cat-box"><div class="links-cat-header"><div class="title-theme"><div id="' . esc_attr($_id) . '">'
. esc_html($term->name) . '</div></div></div><div class="links-box links-style-' . esc_attr($style) . '">' . $_link . '</div></div>';
$link_cat_nav .= '<li class="n-H2"><a class="text-ellipsis" href="#' . esc_attr($_id) . '">' . esc_html($term->name) . '</a></li>';
}
if ($link_cat_con === '') {
$links_con = '';
} else {
$submit_nav = $submit_s
? '<div class="link-nav-submit"><a class="padding-h10 hollow but c-theme btn-block text-ellipsis" href="#submit-links-modal" data-toggle="modal">'
. esc_html($submit_title) . '</a></div>'
: '';
$submit_con = $submit_s
? '<div class="link-cat-submit"><a class="padding-h10 hollow but c-theme btn-block text-ellipsis" href="#submit-links-modal" data-toggle="modal">'
. esc_html($submit_title) . '</a></div>'
: '';
$offset_top = $is_mobile ? '-8' : '';
$offset_bottom = $is_mobile ? '20' : '-30';
$links_con = '<div class="fixed-wrap">'
. '<div class="fixed-wrap-nav affix-header-sm affix link-nav-box tab-auto-center-sm" data-offset-top="' . esc_attr((string) $offset_top) . '" data-offset-bottom="' . esc_attr((string) $offset_bottom) . '">'
. '<div class="aksrt-panel"><ul class="nav list-inline scroll-x mini-scrollbar tab-nav-theme">' . $link_cat_nav . '</ul></div>'
. $submit_nav
. '</div><div class="fixed-wrap-content">' . $link_cat_con . $submit_con . '</div></div>';
}
} else {
$cat_id = (int) $cats[0];
$args = [
'orderby' => $orderby,
'order' => $order,
'limit' => $limit > 0 ? $limit : -1,
'category' => $cat_id,
];
$submit_con = $submit_s
? '<div class="mt20"><a class="padding-h10 hollow but c-theme btn-block text-ellipsis" href="#submit-links-modal" data-toggle="modal">'
. esc_html($submit_title) . '</a></div>'
: '';
$links_con = '<div class="links-box links-style-' . esc_attr($style) . '">'
. aksrt_links_box(get_bookmarks($args), $style, $nofollow, $go_s, $blank_s)
. '</div>' . $submit_con;
}
if ($links_con === '') {
if (current_user_can('manage_links')) {
return '<div class="mb20"><a class="author-minicard links-card radius8" href="' . esc_url(admin_url('link-manager.php')) . '" target="_blank" rel="noopener">'
. esc_html__('添加链接', 'aksrt') . '</a></div>';
}
return '<div class="author-minicard links-card radius8 mb20">' . esc_html__('暂无链接', 'aksrt') . '</div>';
}
return '<div class="links-page-container mb20">' . $links_con . '</div>';
}
endif;
if (!function_exists('aksrt_link_get_search_cover')) :
/**
* Search cover with multi-engine tabs (legacy).
*/
function aksrt_link_get_search_cover(int $post_id = 0): string {
$post_id = $post_id > 0 ? $post_id : (int) get_queried_object_id();
$s = aksrt_get_page_links_settings($post_id);
if (empty($s['search_s']) || empty($s['search_types'])) {
return '';
}
$search_types_args = [
'self' => [
'name' => __('站内', 'aksrt'),
'input_name' => 's',
'url' => home_url('/'),
],
'baidu' => [
'name' => __('百度', 'aksrt'),
'input_name' => 'wd',
'url' => 'https://www.baidu.com/s',
],
'google' => [
'name' => __('谷歌', 'aksrt'),
'input_name' => 'q',
'url' => 'https://www.google.com/search',
],
'bing' => [
'name' => __('必应', 'aksrt'),
'input_name' => 'q',
'url' => 'https://cn.bing.com/search',
],
'sogou' => [
'name' => __('搜狗', 'aksrt'),
'input_name' => 'query',
'url' => 'https://www.sogou.com/web',
],
'360' => [
'name' => '360',
'input_name' => 'q',
'url' => 'https://www.so.com/s',
],
];
$type_nav = '';
$type_con = '';
$i = 0;
foreach ($s['search_types'] as $k) {
if (!isset($search_types_args[$k])) {
continue;
}
$cfg = $search_types_args[$k];
$active = $i === 0 ? ' active' : '';
$pane_id = 'link-page-' . $post_id . '-' . $k;
$type_nav .= '<li class="' . esc_attr(trim($active)) . '"><a href="javascript:;" data-aksrt-tab-target="#' . esc_attr($pane_id) . '">'
. esc_html($cfg['name']) . '</a></li>';
$type_con .= '<div class="tab-pane' . ($i === 0 ? ' in active' : '') . '" id="' . esc_attr($pane_id) . '">'
. '<form method="get" target="_blank" class="padding-10 search-form" action="' . esc_url($cfg['url']) . '">'
. '<div class="line-form blur-bg"><div class="search-input-text">'
. '<input type="text" name="' . esc_attr($cfg['input_name']) . '" class="line-form-input" tabindex="1" value="" autocomplete="off">'
. '<i class="line-form-line"></i>'
. '<div class="scale-placeholder">' . esc_html(sprintf(__('%s搜索', 'aksrt'), $cfg['name'])) . '</div>'
. '<div class="abs-right muted-color"><button type="submit" tabindex="2" class="null" aria-label="' . esc_attr__('搜索', 'aksrt') . '">'
. '<i class="fa fas fa-search" aria-hidden="true"></i></button></div>'
. '</div></div></form></div>';
$i++;
}
if ($type_nav === '') {
return '';
}
$title = get_the_title($post_id);
$img = '';
$thumb_id = get_post_thumbnail_id($post_id);
if ($thumb_id) {
$image = wp_get_attachment_image_src($thumb_id, 'full');
$img = !empty($image[0]) ? (string) $image[0] : '';
}
if ($img === '') {
$img = (string) get_post_meta($post_id, 'page_header_cover_img', true);
}
if ($img === '' && class_exists('\AKSRT\Settings\Settings')) {
$other = \AKSRT\Settings\Settings::get_other_page_settings();
$img = (string) ($other['header_cover_img'] ?? '');
}
if ($img === '') {
$img = get_template_directory_uri() . '/assets/img/placeholder.svg';
}
$src = get_template_directory_uri() . '/assets/img/placeholder.svg';
$search = '<div class="header-slider-search abs-center"><div class="header-slider-search-more text-center before">'
. '<div class="em14 font-bold mb10">' . esc_html($title) . '</div></div>'
. '<div class="search-input"><div class="flex jc"><ul class="list-inline scroll-x mini-scrollbar tab-nav-theme">' . $type_nav . '</ul></div>'
. '<div class="tab-content">' . $type_con . '</div></div></div>';
return '<div class="page-cover theme-box radius8 main-shadow link-page-search-cover">'
. '<img' . aksrt_get_lazy_attr('lazy_cover', (string) $img, 'fit-cover no-scale', (string) $src) . ' alt="">'
. $search . '</div>';
}
endif;
if (!function_exists('aksrt_submit_links_modal')) :
/**
* Frontend submit modal markup (legacy).
*
* @param array{title?:string,dec?:string,sign?:bool,cats?:int[]} $args
*/
function aksrt_submit_links_modal(array $args = []): string {
$defaults = [
'title' => __('申请入驻', 'aksrt'),
'dec' => '',
'sign' => true,
'cats' => [],
];
$args = array_merge($defaults, $args);
$title = (string) $args['title'];
$header = $title !== ''
? '<div class="mb20"><button type="button" class="close" data-dismiss="modal" aria-label="' . esc_attr__('关闭', 'aksrt') . '">'
. '<i class="fa fas fa-times" aria-hidden="true"></i></button>'
. '<b class="modal-title flex ac"><span class="toggle-radius mr10 b-theme"><i class="fa fas fa-edit" aria-hidden="true"></i></span>'
. esc_html($title) . '</b></div>'
: '';
$body = '';
if (!empty($args['dec'])) {
$body .= '<div class="muted-box em09">' . wp_kses_post((string) $args['dec']) . '</div>';
}
$need_login = !empty($args['sign']) && !is_user_logged_in();
if ($need_login) {
$sign_url = function_exists('aksrt_get_login_url')
? aksrt_get_login_url()
: (function_exists('aksrt_get_sign_page_url') ? aksrt_get_sign_page_url('login') : wp_login_url());
$body .= '<div class="muted-box text-center">';
$body .= '<div class="mb20 muted-3-color">' . esc_html__('请先登录', 'aksrt') . '</div>';
$body .= '<p><a href="' . esc_url($sign_url) . '" class="signin-loader but c-blue padding-lg">'
. '<i class="fa fas fa-fw fa-sign-in-alt mr10" aria-hidden="true"></i>' . esc_html__('登录', 'aksrt') . '</a></p>';
if (class_exists('\AKSRT\Auth\OAuth') && method_exists(\AKSRT\Auth\OAuth::get_instance(), 'render_social_buttons')) {
$body .= (string) \AKSRT\Auth\OAuth::get_instance()->render_social_buttons(__('使用以下方式快速登录', 'aksrt'));
}
$body .= '</div>';
} else {
$term_args = [
'taxonomy' => 'link_category',
'hide_empty' => false,
];
if (!empty($args['cats']) && is_array($args['cats'])) {
$term_args['include'] = array_map('intval', $args['cats']);
$term_args['orderby'] = 'include';
}
$terms = get_terms($term_args);
$cats_options = '';
if (!is_wp_error($terms) && !empty($terms)) {
foreach ($terms as $item) {
$cats_options .= '<option value="' . (int) $item->term_id . '">' . esc_html($item->name) . '</option>';
}
}
$cats_field = $cats_options !== ''
? '<div class="col-sm-12 mb10"><div class="em09 muted-2-color mb6">' . esc_html__('网站类别', 'aksrt') . '</div>'
. '<div class="form-select"><select name="link_category" class="form-control">' . $cats_options . '</select></div></div>'
: '';
$placeholder_upload = get_template_directory_uri() . '/assets/img/placeholder.svg';
$body .= '<form class="form-horizontal mt10 form-upload aksrt-links-submit-form" enctype="multipart/form-data">';
$body .= '<div class="row gutters-5">';
$body .= '<div class="col-sm-6 mb10"><div class="em09 muted-2-color mb6">' . esc_html__('网站名称(必填)', 'aksrt') . '</div>'
. '<input type="text" class="form-control" name="link_name" required placeholder="' . esc_attr__('请输入网站名称', 'aksrt') . '"></div>';
$body .= '<div class="col-sm-6 mb10"><div class="em09 muted-2-color mb6">' . esc_html__('网站地址(必填)', 'aksrt') . '</div>'
. '<input type="url" class="form-control" name="link_url" required placeholder="https://..."></div>';
$body .= '<div class="col-sm-12 mb10"><div class="em09 muted-2-color mb6">' . esc_html__('网站简介', 'aksrt') . '</div>'
. '<input type="text" class="form-control" name="link_description" placeholder="' . esc_attr__('一句话介绍网站', 'aksrt') . '"></div>';
$body .= $cats_field;
$body .= '<div class="col-sm-12 mb10"><div class="em09 muted-2-color mb6">' . esc_html__('LOGO图像', 'aksrt') . '</div>'
. '<label class="pointer"><div class="preview preview-square"><img class="fit-cover aksrt-links-logo-preview" src="'
. esc_url($placeholder_upload) . '" alt=""></div>'
. '<input class="hide aksrt-links-logo-input" type="file" accept="image/gif,image/jpeg,image/jpg,image/png" name="image"></label>'
. '<div class="px12 muted-2-color mb6">' . esc_html__('请选择正方形 LOGO,支持 jpg/png/gif', 'aksrt') . '</div></div>';
$body .= '</div>';
$body .= '<div class="text-right edit-footer">'
. '<button type="submit" class="but c-blue padding-lg aksrt-links-submit-btn"><i class="fa fas fa-check" aria-hidden="true"></i>'
. esc_html__('确认提交', 'aksrt') . '</button></div>';
$body .= wp_nonce_field('aksrt_frontend_links_submit', '_wpnonce', false, false);
$body .= '<input type="hidden" name="action" value="aksrt_frontend_links_submit">';
$body .= '</form>';
}
return '<div class="modal fade" id="submit-links-modal" tabindex="-1" role="dialog" aria-hidden="true">'
. '<div class="modal-dialog" role="document"><div class="modal-content"><div class="modal-body">'
. $header . $body
. '</div></div></div></div>';
}
endif;