519 lines
16 KiB
PHP
519 lines
16 KiB
PHP
<?php
|
||
/**
|
||
* AKSRT 自定义小工具
|
||
*
|
||
* @package AKSRT
|
||
*/
|
||
|
||
defined('ABSPATH') || exit;
|
||
|
||
// ============================================================
|
||
// 1. 用户中心小工具 — 替换旧作者信息小工具
|
||
// ============================================================
|
||
|
||
class AKSRT_About_Widget extends WP_Widget {
|
||
|
||
public function __construct() {
|
||
parent::__construct(
|
||
'aksrt_about',
|
||
esc_html__('AKSRT 用户中心', 'aksrt'),
|
||
[
|
||
'description' => esc_html__('登录状态面板:未登录显示登录/注册按钮,已登录显示用户信息与快捷入口', 'aksrt'),
|
||
'customize_selective_refresh' => true,
|
||
]
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 前端渲染
|
||
*/
|
||
public function widget($args, $instance): void {
|
||
echo $args['before_widget'];
|
||
|
||
$title = apply_filters('widget_title', $instance['title'] ?? '', $instance, $this->id_base);
|
||
if ($title) {
|
||
echo $args['before_title'] . esc_html($title) . $args['after_title'];
|
||
}
|
||
|
||
// 判断登录状态
|
||
if (is_user_logged_in()) {
|
||
$this->render_logged_in();
|
||
} else {
|
||
$this->render_logged_out($instance);
|
||
}
|
||
|
||
// 输出内联样式 & JS(仅在本 widget 实例加载时注入一次)
|
||
$this->inline_assets();
|
||
|
||
echo $args['after_widget'];
|
||
}
|
||
|
||
// ==========================================
|
||
// 已登录状态渲染(Zibll 风格用户卡片)
|
||
// ==========================================
|
||
private function render_logged_in(): void {
|
||
$u = wp_get_current_user();
|
||
$avatar = get_avatar_url($u->ID, ['size' => 160]);
|
||
$profile = aksrt_get_user_center_url();
|
||
$logout = aksrt_get_logout_url(home_url('/'));
|
||
|
||
// 封面图:用户在用户中心上传的背景图 > 默认背景图
|
||
$cover = get_user_meta($u->ID, 'aksrt_background_image', true);
|
||
if (! $cover) {
|
||
$cover = get_theme_file_uri('assets/img/person_background.png');
|
||
}
|
||
?>
|
||
<div class="aksrt-user-card">
|
||
<div class="aksrt-user-card__cover" style="padding-bottom: 50%;">
|
||
<img class="aksrt-user-card__cover-img" src="<?php echo esc_url($cover); ?>" alt=""<?php echo function_exists('aksrt_lazy_loading') ? aksrt_lazy_loading('widget') : ' loading="lazy"'; ?>>
|
||
</div>
|
||
|
||
<div class="aksrt-user-card__content">
|
||
<div class="aksrt-user-card__avatar">
|
||
<img src="<?php echo esc_url($avatar); ?>" alt="" width="80" height="80"<?php echo function_exists('aksrt_lazy_loading') ? aksrt_lazy_loading('avatar') : ' loading="lazy"'; ?>>
|
||
</div>
|
||
|
||
<div class="aksrt-user-card__info">
|
||
<div class="aksrt-user-card__name"><?php echo esc_html($u->display_name); ?></div>
|
||
<div class="aksrt-user-card__desc"><?php echo esc_html($u->user_email); ?></div>
|
||
</div>
|
||
|
||
<div class="aksrt-user-card__btns">
|
||
<a class="aksrt-user-card__btn aksrt-user-card__btn--primary" href="<?php echo esc_url($profile); ?>">
|
||
<i class="fas fa-user" aria-hidden="true"></i> <?php esc_html_e('个人主页', 'aksrt'); ?>
|
||
</a>
|
||
<a class="aksrt-user-card__btn aksrt-user-card__btn--outline" href="<?php echo esc_url($logout); ?>">
|
||
<i class="fas fa-sign-out-alt" aria-hidden="true"></i> <?php esc_html_e('退出', 'aksrt'); ?>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<?php
|
||
}
|
||
|
||
// ==========================================
|
||
// 未登录状态渲染(Zibll 风格用户卡片)
|
||
// ==========================================
|
||
private function render_logged_out(array $instance = []): void {
|
||
$login_url = aksrt_get_login_url();
|
||
$register_url = aksrt_get_register_url();
|
||
|
||
// 封面图:后台配置 > 默认
|
||
$cover = ! empty($instance['cover']) ? $instance['cover'] : get_theme_file_uri('assets/img/widgets-original.png');
|
||
$avatar = get_avatar_url(0, ['size' => 160, 'default' => 'mystery']);
|
||
|
||
$oauth_html = '';
|
||
if (function_exists('AKSRT_OAuth') && method_exists(AKSRT_OAuth::get_instance(), 'get_login_buttons')) {
|
||
$oauth_html = AKSRT_OAuth::get_instance()->get_login_buttons();
|
||
}
|
||
?>
|
||
<div class="aksrt-user-card">
|
||
<div class="aksrt-user-card__cover" style="padding-bottom: 50%;">
|
||
<?php if ($cover) : ?>
|
||
<img class="aksrt-user-card__cover-img" src="<?php echo esc_url($cover); ?>" alt=""<?php echo function_exists('aksrt_lazy_loading') ? aksrt_lazy_loading('widget') : ' loading="lazy"'; ?>>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
<div class="aksrt-user-card__content">
|
||
<div class="aksrt-user-card__avatar">
|
||
<img src="<?php echo esc_url($avatar); ?>" alt="" width="80" height="80"<?php echo function_exists('aksrt_lazy_loading') ? aksrt_lazy_loading('avatar') : ' loading="lazy"'; ?>>
|
||
</div>
|
||
|
||
<div class="aksrt-user-card__info">
|
||
<div class="aksrt-user-card__desc"><?php esc_html_e('Hi! 请登录', 'aksrt'); ?></div>
|
||
</div>
|
||
|
||
<div class="aksrt-user-card__btns">
|
||
<a class="aksrt-user-card__btn aksrt-user-card__btn--primary" href="<?php echo esc_url($login_url); ?>"><?php esc_html_e('登 录', 'aksrt'); ?></a>
|
||
<?php if (get_option('users_can_register')) : ?>
|
||
<a class="aksrt-user-card__btn aksrt-user-card__btn--outline" href="<?php echo esc_url($register_url); ?>"><?php esc_html_e('注 册', 'aksrt'); ?></a>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
<?php if ($oauth_html) : ?>
|
||
<div class="aksrt-user-card__oauth">
|
||
<div class="aksrt-user-card__divider"><?php esc_html_e('社交账号登录', 'aksrt'); ?></div>
|
||
<div class="aksrt-user-card__social-icons">
|
||
<?php echo $oauth_html; ?>
|
||
</div>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
<?php
|
||
}
|
||
|
||
// ==========================================
|
||
// 内联样式(Zibll 风格用户卡片,只输出一次)
|
||
// ==========================================
|
||
private function inline_assets(): void {
|
||
if (did_action('aksrt_user_card_assets')) {
|
||
return;
|
||
}
|
||
do_action('aksrt_user_card_assets');
|
||
?>
|
||
<style>
|
||
/* ========== AKSRT 用户卡片(Zibll 风格) ========== */
|
||
|
||
/*
|
||
* 消除"卡片套卡片":
|
||
* 外层 .widget-card 提供 background/shadow/border-radius/padding:15px
|
||
* 内层 .aksrt-user-card 用负 margin 把封面图扩展到外层边缘,
|
||
* 使封面图与外层卡片融为一体,不再有 15px 间距。
|
||
*/
|
||
.widget-card > .aksrt-user-card {
|
||
margin: -15px -15px 0;
|
||
overflow: hidden;
|
||
border-radius: var(--radius-card, 8px) var(--radius-card, 8px) 0 0;
|
||
}
|
||
|
||
/* 有标题时:取消标题下方的 margin-bottom,因为卡片已无额外间距 */
|
||
.widget.aksrt_about.widget-card > .widget-title + .aksrt-user-card {
|
||
margin-top: -5px;
|
||
}
|
||
|
||
/* ---- 封面图 ---- */
|
||
.aksrt-user-card__cover {
|
||
position: relative;
|
||
overflow: hidden;
|
||
background: linear-gradient(135deg, var(--color-primary), var(--color-primary-dark, #4f46e5));
|
||
}
|
||
.aksrt-user-card__cover-img {
|
||
position: absolute;
|
||
inset: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
}
|
||
|
||
/* ---- 内容区 — 悬浮在封面上方 ---- */
|
||
.aksrt-user-card__content {
|
||
padding: 15px;
|
||
margin-top: -55px;
|
||
position: relative;
|
||
text-align: center;
|
||
}
|
||
|
||
/* ---- 头像(绝对居中) ---- */
|
||
.aksrt-user-card__avatar {
|
||
text-align: center;
|
||
line-height: 0;
|
||
}
|
||
.aksrt-user-card__avatar img {
|
||
display: block;
|
||
width: 80px;
|
||
height: 80px;
|
||
margin: 0 auto;
|
||
border-radius: 50%;
|
||
border: 3px solid var(--color-primary);
|
||
object-fit: cover;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||
}
|
||
|
||
/* ---- 用户信息 ---- */
|
||
.aksrt-user-card__info {
|
||
margin-top: 12px;
|
||
text-align: center;
|
||
}
|
||
.aksrt-user-card__name {
|
||
font-size: 1.1em;
|
||
font-weight: bold;
|
||
color: var(--color-ink, var(--color-text));
|
||
}
|
||
.aksrt-user-card__desc {
|
||
font-size: 13px;
|
||
color: var(--color-text-muted, #9ca3af);
|
||
margin-top: 4px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
/* ---- 按钮组 ---- */
|
||
.aksrt-user-card__btns {
|
||
display: flex;
|
||
gap: 10px;
|
||
justify-content: center;
|
||
margin-top: 16px;
|
||
}
|
||
.aksrt-user-card__btn {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 6px;
|
||
padding: 8px 20px;
|
||
border-radius: 20px;
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
text-decoration: none;
|
||
transition: opacity 0.2s ease, transform 0.15s ease;
|
||
flex: 1;
|
||
max-width: 140px;
|
||
}
|
||
.aksrt-user-card__btn:hover {
|
||
opacity: 0.88;
|
||
transform: translateY(-1px);
|
||
}
|
||
.aksrt-user-card__btn:active {
|
||
transform: translateY(0);
|
||
}
|
||
.aksrt-user-card__btn--primary {
|
||
background: var(--color-primary);
|
||
color: #fff;
|
||
}
|
||
.aksrt-user-card__btn--outline {
|
||
background: var(--color-primary-softer, rgba(136,171,218,0.05));
|
||
color: var(--color-primary);
|
||
border: 1px solid var(--color-primary-50, rgba(136,171,218,0.12));
|
||
}
|
||
|
||
/* ---- OAuth 社交登录 ---- */
|
||
.aksrt-user-card__oauth {
|
||
margin-top: 12px;
|
||
}
|
||
.aksrt-user-card__divider {
|
||
font-size: 12px;
|
||
color: var(--color-text-muted, #9ca3af);
|
||
margin-bottom: 10px;
|
||
position: relative;
|
||
}
|
||
.aksrt-user-card__divider::before,
|
||
.aksrt-user-card__divider::after {
|
||
content: '';
|
||
position: absolute;
|
||
top: 50%;
|
||
width: 28%;
|
||
height: 1px;
|
||
background: var(--color-border, #e5e7eb);
|
||
}
|
||
.aksrt-user-card__divider::before { left: 0; }
|
||
.aksrt-user-card__divider::after { right: 0; }
|
||
|
||
.aksrt-user-card__social-icons {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 8px;
|
||
justify-content: center;
|
||
}
|
||
.aksrt-user-card__social-icons a {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 36px;
|
||
height: 36px;
|
||
border-radius: 50%;
|
||
font-size: 15px;
|
||
color: var(--color-text-muted, #6b7280);
|
||
background: var(--color-bg-tertiary, #f3f4f6);
|
||
text-decoration: none;
|
||
transition: background 0.18s ease, color 0.18s ease;
|
||
}
|
||
.aksrt-user-card__social-icons a:hover {
|
||
background: var(--color-primary);
|
||
color: #fff;
|
||
}
|
||
|
||
/* ---- 暗色适配 ---- */
|
||
html.dark .aksrt-user-card__avatar img,
|
||
body.theme-dark .aksrt-user-card__avatar img {
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
||
}
|
||
html.dark .aksrt-user-card__social-icons a,
|
||
body.theme-dark .aksrt-user-card__social-icons a {
|
||
background: rgba(255, 255, 255, 0.08);
|
||
}
|
||
html.dark .aksrt-user-card__social-icons a:hover,
|
||
body.theme-dark .aksrt-user-card__social-icons a:hover {
|
||
background: var(--color-primary);
|
||
}
|
||
|
||
/* ---- 移动端 ---- */
|
||
@media screen and (max-width: 480px) {
|
||
.aksrt-user-card__content {
|
||
margin-top: -45px;
|
||
}
|
||
.aksrt-user-card__avatar img {
|
||
width: 68px;
|
||
height: 68px;
|
||
}
|
||
}
|
||
</style>
|
||
<?php
|
||
}
|
||
|
||
// ==========================================
|
||
// 后台表单
|
||
// ==========================================
|
||
public function form($instance): void {
|
||
$title = $instance['title'] ?? '';
|
||
$cover = $instance['cover'] ?? '';
|
||
$default_cover = get_theme_file_uri('assets/img/widgets-original.png');
|
||
?>
|
||
<p>
|
||
<label for="<?php echo $this->get_field_id('title'); ?>"><?php esc_html_e('标题(可选):', 'aksrt'); ?></label>
|
||
<input class="widefat" id="<?php echo $this->get_field_id('title'); ?>"
|
||
name="<?php echo $this->get_field_name('title'); ?>"
|
||
type="text" value="<?php echo esc_attr($title); ?>">
|
||
</p>
|
||
<p>
|
||
<label for="<?php echo $this->get_field_id('cover'); ?>"><?php esc_html_e('未登录背景图 URL:', 'aksrt'); ?></label>
|
||
<input class="widefat" id="<?php echo $this->get_field_id('cover'); ?>"
|
||
name="<?php echo $this->get_field_name('cover'); ?>"
|
||
type="url" value="<?php echo esc_attr($cover); ?>"
|
||
placeholder="<?php echo esc_attr($default_cover); ?>">
|
||
</p>
|
||
<p style="font-size:12px;color:#666;">
|
||
<?php esc_html_e('留空则使用默认背景图。已登录用户可使用个人封面图。', 'aksrt'); ?>
|
||
</p>
|
||
<?php
|
||
}
|
||
|
||
public function update($new_instance, $old_instance): array {
|
||
return [
|
||
'title' => sanitize_text_field($new_instance['title'] ?? ''),
|
||
'cover' => esc_url_raw($new_instance['cover'] ?? ''),
|
||
];
|
||
}
|
||
}
|
||
|
||
|
||
// ============================================================
|
||
// 2. 分类目录小工具
|
||
// ============================================================
|
||
|
||
class AKSRT_Categories_Widget extends WP_Widget {
|
||
|
||
public function __construct() {
|
||
parent::__construct(
|
||
'aksrt_categories',
|
||
esc_html__('AKSRT 分类目录', 'aksrt'),
|
||
array(
|
||
'description' => esc_html__('以列表形式展示文章分类', 'aksrt'),
|
||
'customize_selective_refresh' => true,
|
||
)
|
||
);
|
||
}
|
||
|
||
public function widget($args, $instance) {
|
||
echo $args['before_widget'];
|
||
|
||
$title = apply_filters('widget_title', $instance['title'], $instance, $this->id_base);
|
||
|
||
if ($title): ?>
|
||
<h3 class="widget-title"><?php echo $title; ?></h3>
|
||
<?php endif;
|
||
|
||
$count = absint($instance['count'] ?: 8);
|
||
$cats = get_categories(array('number' => $count, 'orderby' => 'count', 'order' => 'DESC'));
|
||
?>
|
||
<ul class="widget-list">
|
||
<?php foreach ($cats as $cat): ?>
|
||
<li>
|
||
<a href="<?php echo esc_url(get_category_link($cat->term_id)); ?>"><?php echo esc_html($cat->name); ?></a>
|
||
<span><?php echo (int)$cat->count; ?></span>
|
||
</li>
|
||
<?php endforeach; ?>
|
||
</ul>
|
||
<?php
|
||
echo $args['after_widget'];
|
||
}
|
||
|
||
public function form($instance) {
|
||
$title = $instance['title'] ?? __('分类目录', 'aksrt');
|
||
$count = isset($instance['count']) ? (int)$instance['count'] : 8;
|
||
?>
|
||
<p>
|
||
<label for="<?php echo $this->get_field_id('title'); ?>"><?php esc_html_e('标题:', 'aksrt'); ?></label>
|
||
<input class="widefat" id="<?php echo $this->get_field_id('title'); ?>"
|
||
name="<?php echo $this->get_field_name('title'); ?>"
|
||
type="text" value="<?php echo esc_attr($title); ?>">
|
||
</p>
|
||
<p>
|
||
<label for="<?php echo $this->get_field_id('count'); ?>"><?php esc_html_e('显示数量:', 'aksrt'); ?></label>
|
||
<input class="tiny-text" id="<?php echo $this->get_field_id('count'); ?>"
|
||
name="<?php echo $this->get_field_name('count'); ?>"
|
||
type="number" min="1" max="50" value="<?php echo $count; ?>">
|
||
</p>
|
||
<?php
|
||
}
|
||
|
||
public function update($new, $old) {
|
||
return [
|
||
'title' => sanitize_text_field($new['title']),
|
||
'count' => absint($new['count']),
|
||
];
|
||
}
|
||
}
|
||
|
||
|
||
// ============================================================
|
||
// 3. 标签云小工具
|
||
// ============================================================
|
||
|
||
class AKSRT_Tag_Cloud_Widget extends WP_Widget {
|
||
|
||
public function __construct() {
|
||
parent::__construct(
|
||
'aksrt_tag_cloud',
|
||
esc_html__('AKSRT 标签云', 'aksrt'),
|
||
array(
|
||
'description' => esc_html__('展示文章标签云', 'aksrt'),
|
||
'customize_selective_refresh' => true,
|
||
)
|
||
);
|
||
}
|
||
|
||
public function widget($args, $instance) {
|
||
echo $args['before_widget'];
|
||
|
||
$title = apply_filters('widget_title', $instance['title'], $instance, $this->id_base);
|
||
$count = absint($instance['count'] ?: 15);
|
||
|
||
if ($title): ?>
|
||
<h3 class="widget-title"><?php echo $title; ?></h3>
|
||
<?php endif;
|
||
?>
|
||
|
||
<div class="tag-cloud">
|
||
<?php
|
||
$tags = get_tags(['number' => $count, 'orderby' => 'count', 'order' => 'DESC']);
|
||
foreach ($tags as $tag):
|
||
$size = 12 + min(4, (int)($tag->count / 5));
|
||
?>
|
||
<a href="<?php echo esc_url(get_tag_link($tag->term_id)); ?>" style="font-size:<?php echo $size ?>px;">#<?php echo esc_html($tag->name); ?></a>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
<?php
|
||
echo $args['after_widget'];
|
||
}
|
||
|
||
public function form($instance) {
|
||
$title = $instance['title'] ?? __('标签云', 'aksrt');
|
||
$count = isset($instance['count']) ? (int)$instance['count'] : 15;
|
||
?>
|
||
<p>
|
||
<label for="<?php echo $this->get_field_id('title'); ?>"><?php esc_html_e('标题:', 'aksrt'); ?></label>
|
||
<input class="widefat" id="<?php echo $this->get_field_id('title'); ?>"
|
||
name="<?php echo $this->get_field_name('title'); ?>"
|
||
type="text" value="<?php echo esc_attr($title); ?>">
|
||
</p>
|
||
<p>
|
||
<label for="<?php echo $this->get_field_id('count'); ?>"><?php esc_html_e('标签数量:', 'aksrt'); ?></label>
|
||
<input class="tiny-text" id="<?php echo $this->get_field_id('count'); ?>"
|
||
name="<?php echo $this->get_field_name('count'); ?>"
|
||
type="number" min="1" max="50" value="<?php echo $count; ?>">
|
||
</p>
|
||
<?php
|
||
}
|
||
|
||
public function update($new, $old) {
|
||
return [
|
||
'title' => sanitize_text_field($new['title']),
|
||
'count' => absint($new['count']),
|
||
];
|
||
}
|
||
}
|
||
|
||
|
||
// 注册函数已迁移至 class-aksrt-widgets.php(注册中心)
|