Files
wordpress-theme-aksrt/src/WidgetSystem/FrontendRenderer.php
T
kate522andClaude Opus 5 35bb96e82f refactor: rename the theme brand and every identifier from AKSRT to Jio
Full-project rename. `git grep -i aksrt` is now empty apart from the
DBMigrator 3.3.0 changelog note that documents the rename itself.

Code identifiers
- PHP namespace AKSRT\ -> Jio\ (composer PSR-4 + 378 declarations)
- global constants AKSRT_* -> JIO_* (VERSION/DIR/URI, PAY_TYPE_*, CAPTCHA_*)
- procedural functions, hooks, options, meta keys, table names,
  cookies, query vars, ajax actions and nonces: aksrt_ -> jio_
- text domain aksrt -> jio; Theme Name -> Jio
- JS globals: aksrtData/aksrtAdminData/... -> jioData/jioAdminData/...,
  window.AKSRTCaptcha & friends -> window.JioCaptcha, Aksrt* -> Jio*
- Vue components Aks*.vue -> Jio*.vue (15 files renamed + imports)
- CSS classes/data-attrs/custom properties: aksrt- -> jio-
- REST namespace aksrt/v1 -> jio/v1 (incl. payment gateway callbacks)

Storage layer
- DB tables {prefix}aksrt_* -> {prefix}jio_*; SCHEMA_VERSION 3.2.0 -> 3.3.0
  so dbDelta creates the new names on install
- widget id_base aksrt_widget_* -> jio_widget_*
- uninstall.php drop list rebuilt from the actual migrator tables
  (dropped 3 stale entries, added the 17 missing ones)

Removed
- hardcoded https://api.aksrt.com/version.json default update endpoint;
  get_endpoint() now reads the 「更新端点」 setting and do_check() no-ops
  when it is empty

Files renamed: inc/helpers/aksrt-*.php -> jio-*.php (54),
assets/admin/src/components/form/Aks*.vue -> Jio*.vue (15).
Build artifacts regenerated: assets/css/build, assets/js/runtime,
assets/admin/build.

Verified: audit-psr4-paths errors=0, audit-admin-contracts errors=0
(warnings=56, unchanged baseline), 813/813 widget-system tests,
44/44 phpunit, php -l clean across all PHP.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-11-01 20:05:04 +08:00

188 lines
9.3 KiB
PHP

<?php
namespace Jio\WidgetSystem;
defined('ABSPATH') || exit;
/**
* Applies the common widget contract before dispatching business markup.
*/
final class FrontendRenderer {
public static function render(WidgetDefinition $definition, array $args, array $instance, SchemaWidget $widget): void {
if (!WidgetVisibility::should_render($instance)) {
return;
}
$context = [
'widget_id' => (string) $widget->id,
'id_base' => $definition->id_base(),
];
$content = RendererRegistry::render($definition->renderer(), $instance, $context);
if ($content === '' && $definition->slug() !== 'layout_gap') {
return;
}
AssetManager::enqueue_for($definition);
if ($definition->slug() === 'posts_toc') {
// The TOC renderer owns the reference widget-container wrapper and
// displays its title inside the generated directory, not above it.
echo $content; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
return;
}
if (in_array($definition->slug(), ['user_card', 'author_card', 'tab_posts', 'posts'], true)) {
self::render_reference_widget($definition, $instance, $widget, $content);
return;
}
if (in_array($definition->slug(), [
'post_pay', 'oneline_posts', 'hot_posts',
'slider', 'slider_media_combo', 'icon_cards', 'icon_cover_cards',
'buttons', 'notice', 'term_cards', 'term_aggregation', 'media_cards',
'combo_info', 'partners', 'recent_comments',
], true)) {
echo (string) ($args['before_widget'] ?? '');
if ($definition->slug() !== 'post_pay') echo self::reference_title($instance);
$content_classes = ['widget-content'];
$device_class = WidgetVisibility::device_class($instance);
if ($device_class !== '') {
$content_classes[] = $device_class;
}
if (!empty($instance['animation_in'])) {
$content_classes[] = 'obs-animate ani-' . sanitize_html_class((string) $instance['animation_in']);
if (!empty($instance['animation_repeat'])) {
$content_classes[] = 'obs-animate-repeat';
}
}
echo '<div class="' . esc_attr(implode(' ', $content_classes)) . '">' . $content . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo (string) ($args['after_widget'] ?? '');
return;
}
$classes = ['jio-widget', 'jio-panel-wrap', 'jio-widget--' . $definition->slug(), 'jio-widget--' . $definition->size()];
if ($definition->size() === 'sidebar') {
$classes[] = 'jio-panel';
}
$device_class = WidgetVisibility::device_class($instance);
if ($device_class !== '') {
$classes[] = $device_class;
}
if (!empty($instance['sidebar_affix'])) {
$classes[] = 'jio-widget--affix';
}
if (!empty($instance['animation_in'])) {
$classes[] = 'jio-widget--animate';
$classes[] = 'jio-widget--animate-' . sanitize_html_class((string) $instance['animation_in']);
}
if (!empty($instance['animation_repeat'])) {
$classes[] = 'jio-widget--animate-repeat';
}
$style = self::style($instance);
echo (string) ($args['before_widget'] ?? '<section>'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo '<div class="' . esc_attr(implode(' ', $classes)) . '" data-jio-widget="' . esc_attr($definition->slug()) . '" data-widget-id="' . esc_attr((string) $widget->id) . '"' . ($style !== '' ? ' style="' . esc_attr($style) . '"' : '') . '>';
if ($definition->size() !== 'sidebar') {
echo '<div class="jio-widget__inner container">';
}
echo self::title($instance); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo '<div class="jio-widget__content">' . $content . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
if ($definition->size() !== 'sidebar') {
echo '</div>';
}
echo '</div>';
echo (string) ($args['after_widget'] ?? '</section>'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
private static function render_reference_widget(WidgetDefinition $definition, array $instance, SchemaWidget $widget, string $content): void {
$classes = ['zib-widget-wrap', 'jio-' . str_replace('_', '-', $definition->slug()) . '-wrap'];
if (in_array($definition->slug(), ['user_card', 'author_card'], true)) {
$classes[] = 'jio-user-widget-wrap';
}
$device_class = WidgetVisibility::device_class($instance);
if ($device_class !== '') {
$classes[] = $device_class;
}
if (!empty($instance['sidebar_affix'])) {
$classes[] = 'jio-widget--affix';
}
$content_classes = ['widget-content'];
if (!empty($instance['animation_in'])) {
$content_classes[] = 'obs-animate ani-' . sanitize_html_class((string) $instance['animation_in']);
}
if (!empty($instance['animation_repeat'])) {
$content_classes[] = 'obs-animate-repeat';
}
$style = self::style($instance);
$affix_attr = !empty($instance['sidebar_affix']) ? ' data-affix="true"' : '';
echo '<div class="' . esc_attr(implode(' ', $classes)) . '" data-jio-widget="' . esc_attr($definition->slug()) . '" data-widget-id="' . esc_attr((string) $widget->id) . '"' . $affix_attr . ($style !== '' ? ' style="' . esc_attr($style) . '"' : '') . '>';
echo '<div class="widget-container"><div>';
echo self::reference_title($instance); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo '<div class="' . esc_attr(implode(' ', $content_classes)) . '">' . $content . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo '</div></div></div>';
}
private static function title(array $instance): string {
$title = trim((string) ($instance['title'] ?? ''));
if ($title === '') {
return '';
}
$safe_title = wp_kses_post($title);
$highlight = trim((string) ($instance['title_highlight'] ?? ''));
if ($highlight !== '') {
$safe_highlight = esc_html($highlight);
$safe_title = str_replace($safe_highlight, '<mark class="' . esc_attr((string) ($instance['title_highlight_color'] ?? 'focus-color')) . '">' . $safe_highlight . '</mark>', $safe_title);
}
$html = '<header class="jio-widget__header' . (($instance['title_style'] ?? '') === 'big' ? ' jio-widget__header--big' : '') . '"><div><h2 class="jio-widget__title">' . nl2br($safe_title) . '</h2>';
if (!empty($instance['subtitle'])) {
$html .= '<p class="jio-widget__subtitle">' . nl2br(wp_kses_post((string) $instance['subtitle'])) . '</p>';
}
$html .= '</div><nav class="jio-widget__title-links" aria-label="' . esc_attr__('模块链接', 'jio') . '">';
foreach (['title_link', 'title_link2', 'title_link3'] as $index => $key) {
$link = RendererHelpers::link((array) ($instance[$key] ?? []));
if ($link['url'] !== '' && $link['text'] !== '') {
$color = (string) ($instance['title_link_style']['color_' . ($index + 1)] ?? '');
$html .= '<a class="' . esc_attr($color) . '" href="' . $link['url'] . '"' . RendererHelpers::link_attrs($link) . '>' . esc_html($link['text']) . '</a>';
}
}
return $html . '</nav></header>';
}
private static function reference_title(array $instance): string {
$title = trim((string) ($instance['title'] ?? ''));
if ($title === '') return '';
$title = wp_kses_post($title);
$highlight = trim((string) ($instance['title_highlight'] ?? ''));
if ($highlight !== '') $title = str_replace(esc_html($highlight), '<span class="title-highlight ' . esc_attr((string) ($instance['title_highlight_color'] ?? 'focus-color')) . '">' . esc_html($highlight) . '</span>', $title);
$subtitle = !empty($instance['subtitle']) ? wp_kses_post((string) $instance['subtitle']) : '';
if (($instance['title_style'] ?? '') === 'big') {
$links = '';
foreach (['title_link', 'title_link2', 'title_link3'] as $index => $key) { $link = RendererHelpers::link((array) ($instance[$key] ?? [])); if ($link['url'] && $link['text']) $links .= '<a class="but ' . esc_attr((string) ($instance['title_link_style']['color_' . ($index + 1)] ?? '')) . '" href="' . $link['url'] . '"' . RendererHelpers::link_attrs($link) . '>' . esc_html($link['text']) . '</a>'; }
return '<div class="big-title-wrap"><h2 class="big-title">' . $title . '</h2>' . ($subtitle ? '<p class="big-title-sub muted-2-color">' . $subtitle . '</p>' : '') . ($links ? '<div class="flex jc big-title-buts">' . $links . '</div>' : '') . '</div>';
}
$link = RendererHelpers::link((array) ($instance['title_link'] ?? []));
$more = $link['url'] ? '<div class="pull-right em09 mt3"><a class="muted-2-color" href="' . $link['url'] . '"' . RendererHelpers::link_attrs($link) . '>' . esc_html($link['text'] ?: __('更多', 'jio')) . '</a></div>' : '';
return '<div class="box-body notop"><div class="title-theme">' . $title . ($subtitle ? '<small class="ml10">' . $subtitle . '</small>' : '') . $more . '</div></div>';
}
private static function style(array $instance): string {
$properties = [];
foreach (['layout_pin_pc' => 'space', 'layout_pin_m' => 'space-m'] as $key => $prefix) {
foreach (['top', 'right', 'bottom', 'left'] as $edge) {
$value = $instance[$key][$edge] ?? '';
if ($value !== '' && is_numeric($value)) {
$properties[] = '--jio-widget-' . $prefix . '-' . $edge . ':' . (float) $value . 'px';
}
}
}
foreach (['light', 'dark'] as $mode) {
$background = (array) ($instance['layout_bg'][$mode] ?? []);
if (!empty($background['color'])) {
$properties[] = '--jio-widget-bg-' . $mode . ':' . (string) $background['color'];
}
if (!empty($background['image'])) {
$properties[] = '--jio-widget-bg-image-' . $mode . ':url(' . esc_url_raw((string) $background['image']) . ')';
}
}
return implode(';', $properties);
}
}