Files
kate522andClaude Opus 5 427968a721 chore(branding): remove leftover upstream theme identifiers
The theme was renamed Zibll/子比 → AKSRT → Jio, but upstream branding
survived the sweeps in CSS classes, the SVG sprite prefix, a persisted
widget field ID, JS events, admin Vue class names, test infrastructure
and docblocks.

Renames:
- zib-widget / zib-widget-wrap / zib-slider → jio-widget-box /
  jio-widget-box-wrap / jio-slider-box (jio-widget is taken by the modern
  widget shell, jio-box-* by the card primitives)
- zib-partners-scroll + zps-* → jio-partners-scroll + jps-*
- zib-combo-info-card + zci-* → jio-combo-info-card + jci-*
- zibsvg- → jiosvg-, including the exported picker helpers and the
  fa-zibsvg- family-prefix guard
- zibpay_type → pay_type; the dual-read fallback in PostQuery collapses
  to a single key
- [zibajax="submit"] dropped (no PHP emitter); zib_ajax.success →
  jio_ajax.success
- aks-* admin Vue classes → jio-*
- zib_palette() → palette()

User-facing vendor traces are gone: the floating "more" button default no
longer ships a zibll.com link or 子比主题 marketing copy, the notice
widget ships no preset promo entries, and the four 官网教程 links are
removed from the post-list admin schema.

Test infrastructure renamed to neutral names (docker-compose.reference.yml,
Caddyfile, article-baseline.json, modern-widgets-contract.test.cjs) with
their DB/volume/env/slug identifiers following.

Kept intentionally, documented in tests/fixtures/README.md: the golden
fixture reference fingerprint (a SHA-256 record of an external directory,
read by validate-widget-golden / generate-widget-golden-fixture /
audit-widget-system), the doesNotMatch guards that prove the old tokens are
gone, and the reference theme's own meta key in the visual fixtures.

No backward-compat or data migration: saved widget configs holding old
icon keys or the old field ID silently lose them, per the project's
rewrite-over-compatibility rule.

Verified: 132/132 node contract tests, php tests/widget-system/run.php
(146 assertions), audit-admin-contracts, audit-psr4-paths and
validate-widget-golden all green; tracked bundles regenerated and in sync.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-11-02 00:06:54 +08:00

243 lines
11 KiB
PHP
Raw Permalink 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 9.0 article-list DOM contract.
*
* @package Jio
*/
defined('ABSPATH') || exit;
if (!function_exists('jio_renderer_list')) {
function jio_renderer_list(array $data, array $cfg): string {
$classes = ['posts-item', 'list', 'ajax-item'];
if ((string) ($cfg['style'] ?? '') !== '') {
$classes[] = (string) $cfg['style'];
}
if (!empty($data['is_mult_thumb'])) {
$classes[] = 'mult-thumb';
}
if (!empty($data['is_no_thumb'])) {
$classes[] = 'no-thumb';
}
if (!empty($cfg['no_margin'])) {
$classes[] = 'no_margin';
}
$classes[] = ($cfg['style'] ?? '') === 'style2' ? 'flex xx' : 'flex';
$title = '<h2 class="item-heading' . (!empty($cfg['heading_bold']) ? ' font-bold' : '') . '"><a' . (!empty($cfg['target_blank']) ? ' target="_blank"' : '') . ' href="' . esc_url((string) $data['permalink']) . '">' . jio_renderer_title_markup($data, $cfg) . '</a></h2>';
$excerpt_class = ($cfg['style'] ?? '') === 'style2' ? 'text-ellipsis-2' : 'text-ellipsis';
$excerpt = '<div class="item-excerpt muted-color ' . $excerpt_class . ' mb6">' . jio_renderer_excerpt_markup($data, $cfg) . '</div>';
$thumb = jio_renderer_thumb($data, $cfg);
$mult = jio_renderer_mult_thumb($data, $cfg);
$graphic = '';
if ($thumb !== '' && empty($data['is_mult_thumb'])) {
$graphic = '<div class="post-graphic' . (($cfg['img_position'] ?? 'left') === 'right' ? ' order1' : '') . '">' . $thumb . '</div>';
}
$badges = jio_renderer_badges($data, $cfg);
$meta = jio_renderer_meta($data, $cfg, false);
$html = '<posts class="' . esc_attr(implode(' ', $classes)) . '">';
if (($cfg['style'] ?? '') === 'style2') {
$html .= $title . '<div class="flex">' . $graphic . '<div class="item-body flex xx flex1 jsb">';
$html .= $mult !== '' ? $mult : $excerpt;
$html .= $badges . $meta . '</div></div>';
} else {
$html .= $graphic . '<div class="item-body flex xx flex1 jsb">' . $title;
$html .= $mult !== '' ? $mult : $excerpt;
$html .= '<div>' . $badges . $meta . '</div></div>';
}
return $html . '</posts>';
}
}
if (!function_exists('jio_renderer_card')) {
function jio_renderer_card(array $data, array $cfg): string {
$classes = ['posts-item', 'card', 'ajax-item'];
if ((string) ($cfg['style'] ?? '') !== '') {
$classes[] = (string) $cfg['style'];
}
$title = '<h2 class="item-heading' . (!empty($cfg['heading_bold']) ? ' font-bold' : '') . '"><a' . (!empty($cfg['target_blank']) ? ' target="_blank"' : '') . ' href="' . esc_url((string) $data['permalink']) . '">' . jio_renderer_title_markup($data, $cfg) . '</a></h2>';
$html = '<posts class="' . esc_attr(implode(' ', $classes)) . '">';
$html .= jio_renderer_thumb($data, $cfg);
$html .= '<div class="item-body">' . $title;
$html .= jio_renderer_badges($data, $cfg);
$html .= jio_renderer_meta($data, $cfg, true);
return $html . '</div></posts>';
}
}
if (!function_exists('jio_renderer_mini')) {
function jio_renderer_mini(array $data, array $cfg = []): string {
$target = !empty($cfg['target_blank']) ? ' target="_blank"' : '';
$thumb = '';
$show_thumb = !array_key_exists('show_thumb', $cfg) || !empty($cfg['show_thumb']);
$show_meta = !array_key_exists('show_meta', $cfg) || !empty($cfg['show_meta']);
if ($show_thumb && (string) $data['primary'] !== '') {
$default_attr = empty($data['has_real']) ? ' data-thumb="default"' : '';
$thumb = '<div class="mr10"><div class="item-thumbnail"><a' . $target . ' href="' . esc_url((string) $data['permalink']) . '"><img' . $default_attr . jio_get_lazy_attr('lazy_posts_thumb', (string) $data['primary'], 'fit-cover radius8') . ' alt="' . esc_attr((string) $data['title']) . '"></a></div></div>';
}
$number = '';
if (!empty($cfg['show_number'])) {
$number_value = max(1, (int) ($cfg['number_offset'] ?? 1));
$colors = ['c-red', 'c-yellow', 'c-purple', 'c-blue', 'c-green'];
$number_class = $colors[$number_value - 1] ?? '';
$number = '<span class="badg badg-sm mr3 ' . esc_attr($number_class) . '">' . $number_value . '</span>';
}
$title_class = $show_thumb ? ' text-ellipsis-2' : ' text-ellipsis' . (!$show_meta && empty($cfg['show_number']) ? ' icon-circle' : '');
$title = esc_html((string) $data['title']);
if ((string) ($data['subtitle'] ?? '') !== '') {
$title .= '<span class="focus-color">' . esc_html((string) $data['subtitle']) . '</span>';
}
$meta = '';
if ($show_meta) {
$show_time = !empty($cfg['show_time']);
if (!empty($cfg['show_author'])) {
$author_value = $show_time ? (string) $data['time_ago'] : (string) $data['author_name'];
$meta_left = '<item class="meta-author flex ac">' . jio_get_avatar_box((int) $data['author_id'], 'avatar-mini') . '<span class="ml6">' . esc_html($author_value) . '</span></item>';
} else {
$meta_left = $show_time ? '<item title="' . esc_attr((string) $data['time_title']) . '" class="icon-circle mln3">' . esc_html((string) $data['time_ago']) . '</item>' : '';
}
$pay = jio_renderer_pay_badge($data, true);
if ($meta_left === '') {
$meta_left = $pay;
$pay = '';
}
if ($meta_left === '') {
$meta_left = '<item></item>';
}
$meta_right = $pay . '<item class="meta-view">' . jio_renderer_svg_icon('view') . esc_html(jio_renderer_meta_number((int) $data['views'])) . '</item>';
$meta = '<div class="item-meta muted-2-color flex jsb ac' . (!$show_thumb ? ' mt6' : '') . '">' . $meta_left . '<div class="meta-right">' . $meta_right . '</div></div>';
}
$class = trim('posts-mini ' . (string) ($cfg['class'] ?? ''));
return '<div class="' . esc_attr($class) . '">' . $thumb . '<div class="posts-mini-con flex xx flex1 jsb"><h2 class="item-heading' . esc_attr($title_class) . '">' . $number . '<a' . $target . ' href="' . esc_url((string) $data['permalink']) . '">' . $title . '</a></h2>' . $meta . '</div></div>';
}
}
if (!function_exists('jio_render_post')) {
function jio_render_post(array $args = []): string {
$cfg = jio_renderer_config($args);
$data = jio_renderer_collect_post_data($cfg);
if (!$data) {
return '';
}
return ($cfg['mode'] ?? 'list') === 'card'
? jio_renderer_card($data, $cfg)
: jio_renderer_list($data, $cfg);
}
}
if (!function_exists('jio_render_post_list')) {
/**
* Render the canonical article stream.
*
* @param array<string,mixed> $args legacy-shaped list arguments.
* @param WP_Query|null $query Optional custom query.
*/
function jio_render_post_list(array $args = [], ?WP_Query $query = null, bool $echo = true): string {
$cfg = jio_renderer_config($args);
$has_posts = $query instanceof WP_Query ? $query->have_posts() : have_posts();
if (!$has_posts) {
$html = jio_get_null(__('暂无内容', 'jio'), 100, 'null-post.svg');
if ($echo) {
echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
return '';
}
return $html;
}
$classes = ['posts-row', 'ajaxpager'];
foreach (preg_split('/\s+/', trim((string) ($cfg['wrapper_class'] ?? ''))) ?: [] as $class) {
$class = sanitize_html_class($class);
if ($class !== '') {
$classes[] = $class;
}
}
// legacy 契约:分页等控件与列表同处一个 .ajaxpager 内,供 runtime post_ajax 提取。
$append_inner_html = (string) ($cfg['append_inner_html'] ?? '');
$html = empty($cfg['skip_wrapper'])
? '<div class="' . esc_attr(implode(' ', array_unique($classes))) . '" data-view="' . esc_attr((string) $cfg['mode']) . '">'
: '';
if ($query instanceof WP_Query) {
while ($query->have_posts()) {
$query->the_post();
$html .= jio_render_post($cfg);
}
wp_reset_postdata();
} else {
while (have_posts()) {
the_post();
$html .= jio_render_post($cfg);
}
}
$html .= $append_inner_html;
if (empty($cfg['skip_wrapper'])) {
$html .= '</div>';
}
if ($echo) {
echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
return '';
}
return $html;
}
}
if (!function_exists('jio_render_mini_list')) {
function jio_render_mini_list(WP_Query $query, array $args = [], bool $echo = true): string {
$cfg = jio_renderer_config($args);
// The reference mini rows follow the site's list ordering time source.
$cfg['time_source'] = $cfg['list_orderby'] === 'modified' ? 'modified' : 'date';
$html = '';
$number = max(0, (int) ($args['number_offset'] ?? 0));
while ($query->have_posts()) {
$query->the_post();
$data = jio_renderer_collect_post_data($cfg);
if (!$data) {
continue;
}
$number++;
$html .= jio_renderer_mini($data, array_merge($cfg, ['number_offset' => $number]));
}
wp_reset_postdata();
if ($echo) {
echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
return '';
}
return $html;
}
}
if (!function_exists('jio_render_placeholder')) {
/**
* 骨架屏占位。
*
* 骨架块复用真实卡片的 posts-item / post-graphic / item-body 结构与尺寸,
* 只是把内容换成 .placeholder。列表模式四行(标题 t1、摘要 k1、k2、两枚 s1),
* 卡片模式三行(标题 t1、摘要 k2、两枚 s1)。
*
* 刻意与真实条目不同的两点:
* - **不写 `ajax-item`**:否则 `find('.ajax-item')` / ajax-list 的 `inject()`
* 会把骨架当成真实条目删除或定位。
* - **元素是 `<div>` 而非 `<posts>`**,且 card 分支的缩略图是
* `.item-thumbnail` 直接子元素(不经 `.post-graphic`,那个带 20px 右边距,
* 是列表变体的 wrapper)。
*
* @param string $mode list|card|mini|mini_thumb。
* @param int $count 重复数量,1–12。
*/
function jio_render_placeholder(string $mode = 'list', int $count = 3): string {
$count = max(1, min(12, $count));
if ($mode === 'mini') {
$item = '<div class="posts-mini"><div class="placeholder k1"></div></div>';
} elseif ($mode === 'mini_thumb') {
$item = '<div class="posts-mini "><div class="mr10"><div class="item-thumbnail placeholder"></div></div><div class="posts-mini-con flex xx flex1 jsb"><div class="placeholder t1"></div><div class="placeholder s1"></div></div></div>';
} elseif ($mode === 'card') {
$item = '<div class="posts-item card"><div class="item-thumbnail"><div class="radius8 item-thumbnail placeholder"></div> </div><div class="item-body "> <h2 class="item-excerpt placeholder t1 item-heading"></h2> <p class="mt10 placeholder k2"></p><i class="flex jsb"><i class="placeholder s1"></i><i class="placeholder s1 ml10"></i></i></div></div>';
} else {
$item = '<div class="posts-item list flex"><div class="post-graphic"><div class="radius8 item-thumbnail placeholder"></div> </div><div class="item-body flex xx flex1 jsb"> <p class="placeholder t1"></p> <h4 class="item-excerpt placeholder k1"></h4><p class="placeholder k2"></p><i><i class="placeholder s1"></i><i class="placeholder s1 ml10"></i></i></div></div>';
}
return str_repeat($item, $count);
}
}