Move all domain folders from src/AKSRT/{Domain} to src/{Domain}; remap
Composer PSR-4 "AKSRT\\": "src/"; update audit script + README + ADR
paths; remove dead tools/build-psr4-map.php (scanned deleted inc/classes).
274 lines
11 KiB
PHP
274 lines
11 KiB
PHP
<?php
|
||
/**
|
||
* Categories list (zibll widget categories depth).
|
||
*
|
||
* @package AKSRT
|
||
*/
|
||
|
||
namespace AKSRT\Widgets;
|
||
|
||
use AKSRT\Widgets\Traits\CommonWidgetFields;
|
||
|
||
defined('ABSPATH') || exit;
|
||
|
||
class CategoriesWidget extends \WP_Widget {
|
||
use CommonWidgetFields;
|
||
|
||
public function __construct() {
|
||
parent::__construct(
|
||
'aksrt_categories',
|
||
esc_html__('AKSRT 分类目录', 'aksrt'),
|
||
[
|
||
'description' => esc_html__('以列表形式展示文章分类', 'aksrt'),
|
||
'customize_selective_refresh' => true,
|
||
'classname' => 'widget_aksrt_categories',
|
||
]
|
||
);
|
||
}
|
||
|
||
/** @return array<int,int> */
|
||
public static function parse_ids_static(string $raw): array {
|
||
if ($raw === '') {
|
||
return [];
|
||
}
|
||
$ids = [];
|
||
foreach (preg_split('/[\s,]+/', $raw) ?: [] as $p) {
|
||
$id = (int) $p;
|
||
if ($id > 0) {
|
||
$ids[] = $id;
|
||
}
|
||
}
|
||
return array_values(array_unique($ids));
|
||
}
|
||
|
||
/**
|
||
* @param array<string,mixed> $instance
|
||
*/
|
||
public static function render_html(array $instance): string {
|
||
$count = max(1, min(50, (int) ($instance['count'] ?? 8)));
|
||
$orderby = sanitize_key((string) ($instance['orderby'] ?? 'count'));
|
||
if (!in_array($orderby, ['count', 'name', 'id', 'slug'], true)) {
|
||
$orderby = 'count';
|
||
}
|
||
$order = strtoupper((string) ($instance['order'] ?? 'DESC')) === 'ASC' ? 'ASC' : 'DESC';
|
||
$hide_empty = !isset($instance['hide_empty']) || !empty($instance['hide_empty']);
|
||
$hierarchical = !empty($instance['hierarchical']);
|
||
$parent = max(0, (int) ($instance['parent'] ?? 0));
|
||
$show_count = !isset($instance['show_count']) || !empty($instance['show_count']);
|
||
$show_desc = !empty($instance['show_desc']);
|
||
$show_thumb = !empty($instance['show_thumb']);
|
||
$exclude = self::parse_ids_static((string) ($instance['exclude'] ?? ''));
|
||
$include = self::parse_ids_static((string) ($instance['include'] ?? ''));
|
||
|
||
$term_args = [
|
||
'taxonomy' => 'category',
|
||
'number' => $hierarchical ? 0 : $count,
|
||
'orderby' => $orderby,
|
||
'order' => $order,
|
||
'hide_empty' => $hide_empty,
|
||
'hierarchical' => $hierarchical,
|
||
];
|
||
if ($parent > 0) {
|
||
$term_args['parent'] = $parent;
|
||
} elseif ($hierarchical) {
|
||
$term_args['parent'] = 0;
|
||
}
|
||
if ($include) {
|
||
$term_args['include'] = $include;
|
||
$term_args['orderby'] = 'include';
|
||
unset($term_args['number']);
|
||
}
|
||
if ($exclude) {
|
||
$term_args['exclude'] = $exclude;
|
||
}
|
||
|
||
$cats = get_categories($term_args);
|
||
if (!$cats) {
|
||
return function_exists('aksrt_get_ajax_null')
|
||
? aksrt_get_ajax_null(__('暂无分类', 'aksrt'))
|
||
: '<p class="widget-empty ajax-item">' . esc_html__('暂无分类', 'aksrt') . '</p>';
|
||
}
|
||
|
||
ob_start();
|
||
$cls = 'widget-list aksrt-cat-list ajax-item' . ($hierarchical ? ' is-tree' : '') . ($show_thumb ? ' has-thumb' : '');
|
||
echo '<ul class="' . esc_attr($cls) . '">';
|
||
$n = 0;
|
||
foreach ($cats as $cat) {
|
||
if (!$hierarchical && $n >= $count) {
|
||
break;
|
||
}
|
||
echo self::render_cat_item($cat, $show_count, $show_desc, $show_thumb, $hierarchical ? 0 : -1, $count, $n, $hide_empty, $orderby, $order);
|
||
$n++;
|
||
}
|
||
echo '</ul>';
|
||
return (string) ob_get_clean();
|
||
}
|
||
|
||
/**
|
||
* @param \WP_Term $cat
|
||
*/
|
||
private static function render_cat_item($cat, bool $show_count, bool $show_desc, bool $show_thumb, int $depth, int $limit, int &$n, bool $hide_empty, string $orderby, string $order): string {
|
||
if ($n > 0 && $limit > 0 && $n >= $limit && $depth >= 0) {
|
||
return '';
|
||
}
|
||
$html = '<li class="aksrt-cat-list__item depth-' . max(0, $depth) . '">';
|
||
$html .= '<a class="aksrt-cat-list__link" href="' . esc_url(get_category_link($cat->term_id)) . '">';
|
||
if ($show_thumb) {
|
||
$thumb = '';
|
||
$img_id = (int) get_term_meta($cat->term_id, 'thumbnail_id', true);
|
||
if (!$img_id) {
|
||
$img_id = (int) get_term_meta($cat->term_id, '_aksrt_cover', true);
|
||
}
|
||
if ($img_id) {
|
||
$thumb = (string) (wp_get_attachment_image_url($img_id, 'thumbnail') ?: '');
|
||
}
|
||
if ($thumb === '') {
|
||
$thumb = (string) get_term_meta($cat->term_id, '_aksrt_cover_url', true);
|
||
}
|
||
if ($thumb !== '') {
|
||
$ph = get_template_directory_uri() . '/assets/img/placeholder.svg';
|
||
$html .= '<span class="aksrt-cat-list__thumb"><img class="aksrt-lazy lazyload" data-src="' . esc_url($thumb) . '" src="' . esc_url($ph) . '" alt="" loading="lazy" width="32" height="32" /></span>';
|
||
} else {
|
||
$html .= '<span class="aksrt-cat-list__thumb is-empty"><i class="fa fa-folder-o" aria-hidden="true"></i></span>';
|
||
}
|
||
}
|
||
$html .= '<span class="aksrt-cat-list__text">';
|
||
$html .= '<span class="aksrt-cat-list__name">' . esc_html($cat->name) . '</span>';
|
||
if ($show_desc && $cat->description !== '') {
|
||
$desc = wp_trim_words(wp_strip_all_tags((string) $cat->description), 12, '…');
|
||
$html .= '<span class="aksrt-cat-list__desc muted-2-color em09">' . esc_html($desc) . '</span>';
|
||
}
|
||
$html .= '</span>';
|
||
if ($show_count) {
|
||
$html .= '<span class="count">' . (int) $cat->count . '</span>';
|
||
}
|
||
$html .= '</a>';
|
||
|
||
if ($depth >= 0) {
|
||
$children = get_categories([
|
||
'taxonomy' => 'category',
|
||
'parent' => (int) $cat->term_id,
|
||
'hide_empty' => $hide_empty,
|
||
'orderby' => $orderby,
|
||
'order' => $order,
|
||
]);
|
||
if ($children) {
|
||
$html .= '<ul class="aksrt-cat-list__children">';
|
||
foreach ($children as $child) {
|
||
$n++;
|
||
if ($limit > 0 && $n > $limit) {
|
||
break;
|
||
}
|
||
$html .= self::render_cat_item($child, $show_count, $show_desc, $show_thumb, $depth + 1, $limit, $n, $hide_empty, $orderby, $order);
|
||
}
|
||
$html .= '</ul>';
|
||
}
|
||
}
|
||
$html .= '</li>';
|
||
return $html;
|
||
}
|
||
|
||
/**
|
||
* @param array<string,mixed> $args
|
||
* @param array<string,mixed> $instance
|
||
*/
|
||
public function widget($args, $instance): void {
|
||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||
|
||
$title = apply_filters('widget_title', (string) ($instance['title'] ?? ''), $instance, $this->id_base);
|
||
if ($title !== '') {
|
||
echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||
}
|
||
$affix = function_exists('aksrt_widget_affix_attrs') ? aksrt_widget_affix_attrs($instance) : ['class' => '', 'attr' => ''];
|
||
if ($affix['class'] !== '') {
|
||
echo '<div class="is-affix"' . $affix['attr'] . '>';
|
||
}
|
||
echo self::render_html($instance); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||
if ($affix['class'] !== '') {
|
||
echo '</div>';
|
||
}
|
||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||
}
|
||
|
||
/**
|
||
* @param array<string,mixed> $instance
|
||
*/
|
||
public function form($instance): void {
|
||
?>
|
||
<p>
|
||
<label><?php esc_html_e('标题:', 'aksrt'); ?>
|
||
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('title')); ?>" value="<?php echo esc_attr((string) ($instance['title'] ?? __('分类目录', 'aksrt'))); ?>">
|
||
</label>
|
||
</p>
|
||
<p>
|
||
<label><?php esc_html_e('显示数量:', 'aksrt'); ?>
|
||
<input class="tiny-text" type="number" min="1" max="50" name="<?php echo esc_attr($this->get_field_name('count')); ?>" value="<?php echo esc_attr((string) ((int) ($instance['count'] ?? 8))); ?>">
|
||
</label>
|
||
</p>
|
||
<p>
|
||
<label><?php esc_html_e('排序', 'aksrt'); ?>
|
||
<select name="<?php echo esc_attr($this->get_field_name('orderby')); ?>">
|
||
<option value="count" <?php selected(($instance['orderby'] ?? 'count'), 'count'); ?>><?php esc_html_e('文章数', 'aksrt'); ?></option>
|
||
<option value="name" <?php selected(($instance['orderby'] ?? ''), 'name'); ?>><?php esc_html_e('名称', 'aksrt'); ?></option>
|
||
<option value="id" <?php selected(($instance['orderby'] ?? ''), 'id'); ?>><?php esc_html_e('ID', 'aksrt'); ?></option>
|
||
<option value="slug" <?php selected(($instance['orderby'] ?? ''), 'slug'); ?>><?php esc_html_e('别名', 'aksrt'); ?></option>
|
||
</select>
|
||
</label>
|
||
<select name="<?php echo esc_attr($this->get_field_name('order')); ?>">
|
||
<option value="DESC" <?php selected(($instance['order'] ?? 'DESC'), 'DESC'); ?>><?php esc_html_e('降序', 'aksrt'); ?></option>
|
||
<option value="ASC" <?php selected(($instance['order'] ?? ''), 'ASC'); ?>><?php esc_html_e('升序', 'aksrt'); ?></option>
|
||
</select>
|
||
</p>
|
||
<p>
|
||
<label><?php esc_html_e('父分类 ID(0=全部)', 'aksrt'); ?>
|
||
<input class="tiny-text" type="number" min="0" name="<?php echo esc_attr($this->get_field_name('parent')); ?>" value="<?php echo esc_attr((string) ((int) ($instance['parent'] ?? 0))); ?>">
|
||
</label>
|
||
</p>
|
||
<p>
|
||
<label><?php esc_html_e('包含 ID', 'aksrt'); ?>
|
||
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('include')); ?>" value="<?php echo esc_attr((string) ($instance['include'] ?? '')); ?>">
|
||
</label>
|
||
</p>
|
||
<p>
|
||
<label><?php esc_html_e('排除 ID', 'aksrt'); ?>
|
||
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('exclude')); ?>" value="<?php echo esc_attr((string) ($instance['exclude'] ?? '')); ?>">
|
||
</label>
|
||
</p>
|
||
<p>
|
||
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('show_count')); ?>" value="1" <?php checked(!isset($instance['show_count']) || !empty($instance['show_count'])); ?>> <?php esc_html_e('显示数量', 'aksrt'); ?></label><br>
|
||
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('show_desc')); ?>" value="1" <?php checked(!empty($instance['show_desc'])); ?>> <?php esc_html_e('显示描述', 'aksrt'); ?></label><br>
|
||
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('show_thumb')); ?>" value="1" <?php checked(!empty($instance['show_thumb'])); ?>> <?php esc_html_e('显示封面', 'aksrt'); ?></label><br>
|
||
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('hide_empty')); ?>" value="1" <?php checked(!isset($instance['hide_empty']) || !empty($instance['hide_empty'])); ?>> <?php esc_html_e('隐藏空分类', 'aksrt'); ?></label><br>
|
||
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('hierarchical')); ?>" value="1" <?php checked(!empty($instance['hierarchical'])); ?>> <?php esc_html_e('层级树', 'aksrt'); ?></label>
|
||
</p>
|
||
<?php $this->render_common_form_section($instance); ?>
|
||
<?php
|
||
}
|
||
|
||
/**
|
||
* @param array<string,mixed> $new_instance
|
||
* @param array<string,mixed> $old_instance
|
||
* @return array<string,mixed>
|
||
*/
|
||
public function update($new_instance, $old_instance): array {
|
||
$orderby = sanitize_key((string) ($new_instance['orderby'] ?? 'count'));
|
||
if (!in_array($orderby, ['count', 'name', 'id', 'slug'], true)) {
|
||
$orderby = 'count';
|
||
}
|
||
return $this->update_common_fields($new_instance, $old_instance, [
|
||
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
|
||
'count' => max(1, min(50, (int) ($new_instance['count'] ?? 8))),
|
||
'orderby' => $orderby,
|
||
'order' => strtoupper((string) ($new_instance['order'] ?? 'DESC')) === 'ASC' ? 'ASC' : 'DESC',
|
||
'parent' => max(0, (int) ($new_instance['parent'] ?? 0)),
|
||
'include' => sanitize_text_field((string) ($new_instance['include'] ?? '')),
|
||
'exclude' => sanitize_text_field((string) ($new_instance['exclude'] ?? '')),
|
||
'show_count' => !empty($new_instance['show_count']) ? 1 : 0,
|
||
'show_desc' => !empty($new_instance['show_desc']) ? 1 : 0,
|
||
'show_thumb' => !empty($new_instance['show_thumb']) ? 1 : 0,
|
||
'hide_empty' => !empty($new_instance['hide_empty']) ? 1 : 0,
|
||
'hierarchical' => !empty($new_instance['hierarchical']) ? 1 : 0,
|
||
]);
|
||
}
|
||
}
|