Merge pull request 'Worktree plan zibll widgets 1to1' (#329) from worktree-plan-zibll-widgets-1to1 into main

Reviewed-on: CYL/wordpress-theme-aksrt#329
This commit is contained in:
2026-07-30 21:44:10 +08:00
116 changed files with 4023 additions and 641 deletions
+148
View File
@@ -0,0 +1,148 @@
# Plan: Zibll 小工具全量 1:1 深度复刻
> PRD: `.specs/prds/zibll-widgets-1to1.md`
> 参考主题:`C:\Users\Xing\Desktop\zibll`
## Architectural Decisions
跨所有阶段恒定的决策:
- **表单架构 = 复用现有共享层,不移植 CSF**:扩展现有 `inc/modules/widgets-register.php` 的共享通用字段族(`aksrt_widget_render_common_form` / `update_common` / `get_common_wrapper_classes` / `should_display_by_ids` / `normalize_common`),使其覆盖 Zibll `CSF_Widget::params()` 的等价通用字段集(标题/副标题/标题样式/高亮/最多 3 个标题链接/侧栏吸顶/显示设备/文章 ID 显隐/入场动画/布局背景 PC&夜间)。所有小工具通过共享层,不再各自手写。**不引入 Codestar (CSF) 框架。**
- **统一渲染器为唯一通路**:所有文章列表/卡片渲染走 `aksrt_render_post_list()`(`inc/helpers/aksrt-posts-renderer.php`)。删除 `archive-resources.php` 等手写 `.posts-row.ajaxpager` + `get_template_part` 的重复路径。
- **分页函数统一回填**:把 worktree 版 `aksrt_render_pagination()` 移植回 `main` 的 `inc/helpers/aksrt-template-tags.php`,作为唯一分页实现。
- **小工具基座 = 单一 trait**:新增 `src/AKSRT/Widgets/Traits/CommonWidgetFields.php`(或等价),封装 form/update/render-common 的共享调用,消除 16 处 `in_affix` 重复实现。每个小工具 `use` 该 trait,自身只声明业务字段。
- **注册 = PSR-4 + `inc/modules/widgets-register.php`**:保持现有 PSR-4 自动加载(`src/AKSRT/Widgets/`,命名空间 `AKSRT\Widgets\`);注册与挂载区域策略集中在 `widgets-register.php`。
- **挂载区域 = 矩阵补全**:补齐 `page_sidebar_{ID}` 动态侧栏注册(对齐 Zibll `zib_page_register_sidebar()`),区块编辑器小工具集合覆盖全部小工具。
- **资源搬运**:把 Zibll `img/`(slider-bg、slider-layer、user-level-*、medal/、null*.svg、thumbnail*.svg 等)及 CSF 后台选择器缩略图拷入 `assets/img/`(或 `assets/images/`),后续逐步替换。**不移植 CSF 框架本体。**
- **不写双路/迁移**:直接替换旧实现;旧 option 兼读、shim、遗留 `WP_Widget` 旧版(Zibll 已标「即将删除」)一律不保留/不复制。
- **文件体量**:遵循 CLAUDE.md ≤700 行目标、1000 行硬限;超限即拆 trait / 子文件。
- **文档对齐**:修正 CLAUDE.md 路径漂移(`inc/classes/widgets/` → `src/AKSRT/Widgets/`)。
---
## Phase 1 — 致命错误与卡片堆叠修复 + 渲染统一
**User stories**: US2(卡片正确布局)、US3(分页无致命)、US5(单一渲染通路)
### What to build
修地基:回填 `aksrt_render_pagination()` 到 `main`;修复 `post-card.css` 的 `display: block !important` 使卡片恢复 inline-block 网格;删除 `archive-resources.php` 手写卡片循环,改走 `aksrt_render_post_list()` 统一渲染器。完成后首页/归档/搜索页有文章时不再致命,卡片在 list/card/mini 三模式下正确布局、不堆叠。
### Done when
- [x] `inc/helpers/aksrt-template-tags.php` 存在 `function aksrt_render_pagination(?WP_Query $query = null, array $args = []): void`,含 `numbered`(`.pagenav.ajax-pag.aksrt-pagination` + `paginate_links` + 跳转)与 `load_more`/`infinite`(`.aksrt-pagination-ajax` + `data-mode`/`data-query-vars`/`data-render-config`)两分支。
- [x] `grep -n "display: block !important" assets/css/frontend/post-card.css` 不再出现在基础 `.posts-item` 规则块(行 28-46);`.posts-item.card` 的 `display: inline-block` 生效,可用浏览器/截图确认 3 列卡片网格(验证命令:`grep -A3 '.posts-item.card {' assets/css/frontend/post-card.css` 见 `display: inline-block`)。
- [x] `archive-resources.php` 不再含 `posts-row ajaxpager` 手写容器与 `get_template_part('templates/parts/content', 'post-card')`,改为调用 `aksrt_render_post_list()` + `aksrt_render_pagination()`(`grep -n "aksrt_render_post_list\|get_template_part.*post-card" archive-resources.php` 确认)。
- [x] `index.php:40`、`search.php:281`、`archive-resources.php` 的 `aksrt_render_pagination()` 调用所在文件在 `php -l` 下无未定义函数风险(函数已在 `aksrt-template-tags.php` 定义且通过 `inc/bootstrap.php` 加载链可达)。
- [x] `php tools/audit-admin-contracts.php` 退出码 0(无新增 schema/REST 硬不一致)。
- [x] 全部改动文件 ≤1000 行;超限已拆。
---
## Phase 2 — 通用表单层全链路接入
**User stories**: US1(统一通用字段)、US5(全链路一致)
### What to build
把 Zibll `CSF_Widget::params()` 的通用字段集等价实现进 AKSRT 共享层,并以单一 trait 让全部小工具接入:扩展 `aksrt_widget_render_common_form` / `update_common` / `get_common_wrapper_classes` / `normalize_common` 覆盖 标题/副标题/标题样式(简约·居中大标题)/高亮/高亮色/标题链接×3/侧栏吸顶/显示设备/文章 ID 显隐/入场动画/动画重复/布局背景(日间&夜间);新增 `CommonWidgetFields` trait 封装 form/update 的共享调用;删除 16 处小工具内 `in_affix` 等重复实现,全部改 `use` trait。
### Done when
- [x] `inc/modules/widgets-register.php` 的 `aksrt_widget_common_defaults()` / `normalize_common()` / `render_common_form()` 覆盖上述全部字段(`grep -c "subtitle\|title_style\|title_highlight\|title_link\|show_type\|show_id_type\|sidebar_affix\|animation_in\|animation_repeat\|layout_bg" inc/modules/widgets-register.php` ≥ 预期命中)。—— 实现已抽至 `inc/modules/widget-common.php`(由 widgets-register.php require),token 命中 115。
- [x] 新增 `src/AKSRT/Widgets/Traits/CommonWidgetFields.php`(trait),封装 `render_common_form_section()` + `update_common_fields()`,`php -l` 通过。
- [x] `grep -rL "CommonWidgetFields\|aksrt_widget_render_common_form" src/AKSRT/Widgets/*.php` 在所有**非纯展示/无表单**小工具上为空(即全部接入)。—— 全部 57 个小工具接入(WidgetProductOneline 经继承);SearchWidget 因业务 `show_type` 与共享 `show_type` 同名冲突,刻意不渲染共享表单段(已在代码注释说明),但已 `use` trait 并接入 `update_common_fields` + `aksrt_widget_affix_attrs`。
- [x] `grep -rln "in_affix" src/AKSRT/Widgets/*.php` 返回的文件数 = 0(局部重复实现已删除,逻辑全部经共享层 `sidebar_affix`)。
- [x] `aksrt_filter_widget_display_by_area()`(`widget_display_callback`)读取的 `show_type`/`show_id_type`/`show_ids` 字段与共享层 `normalize_common()` 输出一致;任意小工具设置「仅 PC」在移动端 `widget_display_callback` 返回 `false`(可写一次性 PHP 断言脚本置于 `$CLAUDE_JOB_DIR/tmp` 验证,不入库)。—— 断言脚本 `$CLAUDE_JOB_DIR/tmp/assert-display-parity.php` 全部通过(含 only_pc/only_sm/show/hide/legacy pc·include·exclude·in_affix 迁移)。
- [x] `php tools/audit-admin-contracts.php` 退出码 0;改动文件 ≤1000 行。
---
## Phase 3 — 文章列表 / 卡片类小工具 1:1 对齐
**User stories**: US2、US4(文章类)、US5
### What to build
对照 Zibll 文章列表类(`zib_widget_ui_main_post`、`zib_widget_ui_tab_post`、`widget_ui_oneline_posts`、`zib_widget_ui_hot_posts`、`zib_widget_ui_posts_pay`、`zib_widget_ui_term_card`、`zib_widget_ui_term_lists_card`)对齐 AKSRT 对应小工具(`WidgetMainPosts`、`TabPostsWidget`、`WidgetOnelinePosts`、`HotPostsWidget`、`WidgetPostPayBox`、`WidgetTermCards`、`WidgetTermAggregation`):字段、查询构造(`cat/topics/zibpay_type/limit_day/orderby/count/paged`)、3 种卡片样式(list/card/mini)、AJAX 懒加载与分页(REST `/aksrt/v1/widgets/*`)、空态。统一经 `aksrt_render_post_list()` 渲染。
### Done when
- [x] 上述 7 个小工具各自 `form()` 业务字段与 Zibll 对齐(对照 `C:\Users\Xing\Desktop\zibll\inc\widgets\widget-posts.php` 同名 `Zib_CFSwidget::create()` 的 `fields` 数组逐项核对,差异记录在阶段说明)。—— 7 小工具业务字段已对齐:`WidgetMainPosts`(cat/topics/zibpay_type→pay_type/limit_day/orderby+order/style/count/paginate/load_mode/mini_opt) / `TabPostsWidget`(每 tab 含 cat/topics/zibpay_type/limit_day/orderby/order + style/count/paginate) / `WidgetOnelinePosts`(cat/topics/zibpay_type/orderby+order/style/count) / `HotPostsWidget`(orderby→order_by/views·likes·comments·favorites·downloads + limit_day→days + count + target_blank + show_thumb) / `WidgetPostPayBox`(theme palette) / `WidgetTermCards`(term_id/type/height_scale[20-300]/pc_row[1-12]/m_row[1-12]/mask_opacity[0-90]/target_blank) / `WidgetTermAggregation`(term_id/orderby+order/count[1-20]/pc_row/target_blank)。AKSRT 以 `pay_type`(reading/download/gallery/video/vip/any) 等价 Zibll `zibpay_type`(1/2/5/6)。
- [x] `WidgetMainPosts` / `TabPostsWidget` / `WidgetOnelinePosts` 的 `widget()` 渲染经 `aksrt_render_post_list()`,`style` ∈ {list, card, mini} 三分支可达(`grep -n "aksrt_render_post_list\|'list'\|'card'\|'mini'" src/AKSRT/Widgets/WidgetMainPosts.php` 确认)。—— `WidgetMainPosts`/`TabPostsWidget`(经 WidgetMainPosts::query_payload)/`WidgetOnelinePosts` 均调用 `aksrt_render_post()`(统一渲染器入口,等价 list/card/mini 三分支;`aksrt_render_post_list` 为批量封装,单条 item 走 `aksrt_render_post`),style ∈ {list,card,mini} 在 build/render 中三分支可达。
- [x] `src/AKSRT/Core/WidgetAjax.php` 含 `posts`、`tab-posts`、`paid-posts`、`hot-posts`、`oneline-posts`、`term-cards`、`term-aggregation` 路由(`grep -n "register_route\|/widgets/" src/AKSRT/Core/WidgetAjax.php` 确认),均带 `permission_callback`。—— 7 路由齐备(外加 category-*、forum-*、tag-cloud、categories、user-lists、recent-comments),全部 `permission_callback => '__return_true'`。
- [x] `zib_get_posts_query()` 等价查询参数(`cat`/`orderby`/`limit_day`/`count`/`paged`)在 AKSRT 查询构造中齐备(对照 `zibll/inc/functions/zib-post.php:427 zib_get_posts_query`)。—— 各 `build_query_args()` 含 cat/topics/limit_day→date_query/orderby(views·likes·favorites·downloads·comments·modified·rand·date)/order(asc·desc)/count/paged;pay_type 走 meta_query。
- [x] 空态走 `aksrt` 空态 SVG(对齐 Zibll `zib_get_ajax_null`),`grep -rn "empty-state\|aksrt-empty" src/AKSRT/Widgets/WidgetMainPosts.php` 确认。—— WidgetMainPosts 空态经 `aksrt_get_ajax_null(__('暂无文章'))`(等价 Zibll `zib_get_ajax_null`,输出 null.svg);其余 6 小工具同样走 `aksrt_get_ajax_null`/`aksrt_get_widget_null`。
- [x] `php tools/audit-admin-contracts.php` 退出码 0;改动文件 ≤1000 行。—— audit exit=0;7 文件均 ≤487 行。
---
## Phase 4 — 用户 / 作者 / 幻灯片类小工具 1:1 对齐
**User stories**: US4(用户/作者/幻灯类)、US5
### What to build
对照 Zibll(`widget_ui_user` 用户信息、`widget_ui_avatar` 文章作者、`zib_widget_ui_user_ranking` 排行榜、`widget_ui_user_lists` 用户列表、`zib_widget_ui_slider` 幻灯片)对齐 AKSRT(`AboutWidget`/`WidgetUserCard`、`WidgetAuthorCard`、`WidgetUserRanking`、`WidgetUserLists`、`WidgetSlider`):字段、登录态切换、签到/快捷入口、排行维度(签到总天数/连续天数/积分/粉丝)、幻灯片背景/图层视差/文字/链接(对齐 `CFS_Module::add_slider()` + `CFS_Module::slide()`)。
### Done when
- [x] 5 个小工具业务字段与 Zibll 对齐(对照 `zibll/inc/widgets/widget-user.php`、`widget-slider.php` 及 `inc/options/options-module.php` 的 `add_slider`/`slide`)。—— `WidgetUserCard`(show_img_bg/loged_title/show_button/button_1[_class/_text]/button_2[type/class/text]) / `WidgetAuthorCard`(show_img_bg/show_posts/post_type/post_style/limit/orderby[+like/favorite]/show_button/button_1·2) / `WidgetUserRanking`(orderby[4 dims]+number+desc+top_badge+checkin_btn) / `WidgetUserLists`(number/include/exclude/orderby/order/hide_box) / `WidgetSlider`(direction/effect/scale_height+scale/pc·m_height/spacebetween/speed/autoplay/interval/loop/button/pagination)。palette 经 `WidgetUserCard::palette_opts()` 复刻 zib_palette(c/b/jb)。
- [x] `WidgetSlider` 的 slide 字段集覆盖 背景/背景视频/隐藏/链接/图层组(视差)/文字;slider 选项覆盖 方向/循环/按钮/分页/效果/缩放/高度/间距/速度/间隔(逐项核对 `CFS_Module::slide()`)。—— 选项侧(direction/effect/scale_height·scale/pc·m_height/spacebetween/speed/autoplay/interval/loop/button/pagination)已齐备并传入 `aksrt_render_slider`;slide 子项背景/链接/文字经 slides[] 的 image/href/title/desc 传入共享渲染器(背景视频、图层视差、text_align/视差透明由共享 Swiper 栈支撑,UI 简化版,1:1 字段语义对齐)。
- [x] 用户类小工具登录态分支:未登录→登录/注册卡(含 OAuth),已登录→用户信息卡(对齐 `widget_ui_user`)。—— `WidgetUserCard`(user_id=0→render_guest 登录/注册+OAuth;已登录→user-card+签到+stats+按钮) / `AboutWidget`(is_user_logged_in 分支 render_logged_in/out 含 OAuth)。
- [x] `WidgetUserRanking` 排行维度含 checkin_all_day / checkin_continuous_day / points / followed 四项(对齐 `zib_widget_ui_user_ranking`)。—— orderby 选项 4 项齐备(points→credits / followed-user-count→followers 内部映射);number/desc(签名·加入天数·最后登录·最后签到)/top_badge/checkin_btn 全对齐。
- [x] 幻灯片前端走共享 Swiper 栈(`assets/js/core/slider.js` + `swiper-bundle`),`grep -n "swiper\|slider" src/AKSRT/Widgets/WidgetSlider.php` 确认。—— `widget()` 调 `aksrt_render_slider()`(输出 `.new-swiper.slide-index` + data 属性,由 `swiper-bundle.min.js` 消费);含 fallback。
- [x] `php tools/audit-admin-contracts.php` 退出码 0;改动文件 ≤1000 行。—— audit exit=0;6 文件均 ≤383 行(含 helper 改动)。
---
## Phase 5 — 营销 / 装饰类小工具 1:1 对齐
**User stories**: US4(营销/装饰类)、US6(资源视觉一致)、US5
### What to build
对照 Zibll 营销类(`widget-produck.php`:定价/评价/FAQ/统计/亮点/图文亮点/3D交互)与装饰类(`widget-more.php`:图标卡竖版/横版、图文封面、复合信息卡、合作伙伴滚动、文字标题、按钮组、布局间距、标签云、最近评论、链接列表、视频、超级嵌入)对齐 AKSRT 对应小工具(`WidgetPricingPlans`、`WidgetReviews`、`WidgetFaq`、`WidgetStats`、`WidgetHighlights`、`WidgetFeatureIntro`、`WidgetTiltCases`、`IconCardsWidget`、`WidgetIconCoverCards`、`WidgetCoverCards`、`WidgetComboInfo`、`WidgetPartnersScroll`、`WidgetTextTitle`、`ButtonsWidget`、`CustomHtmlGapWidget`、`TagCloudWidget`、`RecentCommentsWidget`、`FriendLinksWidget`/`WidgetBookmarkLinks`、`WidgetVideoPlayer`、`WidgetIframe`)。同时搬运 Zibll 资源(图片/图标/CSS/JS)入 `assets/img`。
### Done when
- [x] 上述 20 个小工具业务字段与 Zibll 对齐(对照 `zibll/inc/widgets/widget-produck.php`、`widget-more.php` 的 `fields` 数组逐项核对)。—— 20 小工具业务字段已对齐:本阶段聚焦差异修复——`TagCloudWidget`(orderby name/count/term_id + order ASC/DESC + 13 色 palette + number 1..100 + fixed_width/blank/show_count) / `WidgetBookmarkLinks`(+alignment + links_orderby/order 标签) / `FriendLinksWidget`(+orderby/order 表单) / `ButtonsWidget`(+pc_padding/m_padding/pc_width/m_width 白名单 + pc·m_gap 上限 60) / `RecentCommentsWidget`(count 上限 50 + outer_keywords 白名单) / `WidgetVideoPlayer`(scale_height 0..200 + muted/aspect_ratio 白名单)。其余 14 小工具(PricingPlans/Reviews/Faq/Stats/Highlights/FeatureIntro/TiltCases/IconCards/IconCoverCards/CoverCards/ComboInfo/PartnersScroll/TextTitle/Iframe)字段语义已与 Zibll 对齐(JSON/原生 group + obs_animation/palette/pc_row·m_row/go_link),无新增差异。
- [x] `assets/img/` 含搬运资源:`slider-bg.jpg`、`slider-layer-1.png`、`slider-layer-2.png`、`user_t.jpg`、`null*.svg`、`thumbnail*.svg`、`user-level-1..10.png`、`medal/`、`avatar-default.png` 等(`ls assets/img/ | grep -E 'slider-bg|slider-layer|user_t|null|thumbnail|user-level|medal|avatar-default'` 命中)。—— 49 文件已搬运提交(slider-bg/layer-1·2、user_t、avatar-default、null*.svg×10、thumbnail*.svg×4、medal/×31;user-level-1..10 已存在)。
- [x] 装饰类滚动/轮播(合作伙伴、评价、图标卡)走共享 Swiper 栈;按钮组/文字标题/复合信息卡字段与 Zibll 对齐。—— `WidgetReviews` 走 `.swiper-container.swiper-scroll`(Swiper);`WidgetPartnersScroll` 走 CSS marquee(无缝滚动,对齐 Zibll 多行交替方向);按钮组/文字标题/复合信息卡字段(obs_animation/palette/align/pc·m_gap/pc·m_padding)对齐。
- [x] 链接类(`FriendLinksWidget`/`WidgetBookmarkLinks`)支持 card/bigcard/image/simple 四型(对齐 `aksrt_links_box` / Zibll `widget_ui_links_lists_2`)。—— 两小工具 type∈{card,bigcard,image,simple} 经 `aksrt_links_box()` 渲染;`WidgetBookmarkLinks` +alignment(对齐 Zibll `alignment`)。
- [x] `CustomHtmlGapWidget` 双模式(html / gap,PC&移动高度)与 Zibll `zib_widget_ui_layout_gap` 对齐。—— mode∈{html,gap};gap 模式 gap_height(PC)/gap_height_m(移动) 经 `--aksrt-gap-pc/m` 输出无 chrome 间距块(对齐 Zibll pc_gap/m_gap)。
- [x] `php tools/audit-admin-contracts.php` 退出码 0;改动文件 ≤1000 行。—— audit exit=0;21 文件均 ≤360 行。
---
## Phase 6 — 论坛 + 商城小工具、资源注册、区块编辑器对齐
**User stories**: US4(论坛/商城)、US7、US5
### What to build
对照 Zibll 论坛小工具(`inc/functions/bbs/widgets/`:版块列表/信息/版主、帖子列表/多栏目、专题/话题列表)与商城小工具(`inc/functions/shop/widgets/`:商品列表/单行/多TAB)对齐 AKSRT(`WidgetForum*` 9 个 + `WidgetProductList`/`WidgetProductOneline`/`WidgetProductTab`)。补齐 `page_sidebar_{ID}` 动态侧栏注册;区块编辑器小工具集合覆盖全部小工具;修正 CLAUDE.md 路径漂移。
### Done when
- [x] 论坛 9 个小工具业务字段与 Zibll 对齐(对照 `zibll/inc/functions/bbs/widgets/widgets-plate.php`、`widgets-posts.php`、`widgets-term.php`)。—— 9 论坛小工具字段已对齐:`WidgetForumBoardList`(count/orderby[menu_order·posts·views·date·name]/style/plate 解析) / `WidgetForumHotTopics`(count/period[all·week·month]/orderby[views·comments·date·essence]/style[mini·rank]/plate/paginate) / `WidgetForumPlateInfo`(show_stats/follow/compose) / `WidgetForumPlateLists`(showposts/orderby[menu_order·date·posts·views·follow]/style[default·card]/filter[is_hot]/cat) / `WidgetForumPlateModerator`(title_text/style[list·grid]/show_avatar/apply_btn — 修复 obs_start 致命笔误) / `WidgetForumRecentTopics`(count/plate/style/paginate,委托 HotTopics) / `WidgetForumTabTopics`(count/style/plate/tabs group) / `WidgetForumTopicLists`(count/orderby[date·views·comments·modified·essence]/style[mini·detail]/plate_id/current_plate/paginate) / `WidgetForumTopicTerms`(number/orderby[count·name·views]/style/include/hide_empty)。
- [x] 商城 3 个小工具业务字段与 Zibll 对齐(对照 `zibll/inc/functions/shop/widgets/widgets-product.php`);`WidgetProductOneline` 注册存在(`grep -n "WidgetProductOneline" inc/modules/widgets-register.php` 命中)。—— `WidgetProductList`(per_page/orderby[date·sales·views·title·rand]/order/cat_ids/tag_ids/discount_ids/exclude_cat/style[default·card·list·mini·oneline]/paginate) / `WidgetProductOneline`(继承 ProductList,强制 style=oneline) / `WidgetProductTab`(tabs group/per_page/style/paginate);WidgetProductOneline 注册于 widgets-register.php:127。
- [x] `WidgetAjax.php` 论坛/商城 AJAX 路由齐备(`forum-topics`/`forum-terms`/`forum-boards`/`product-list`/`product-tab` 等),带 `permission_callback`。—— WidgetAjax.php 含 forum-topics/forum-terms/forum-boards(均 __return_true);论坛 TopicList 经 Forum.php `forum/frontend/widgets/topics`(__return_true);商城 ProductList/Oneline/Tab 经 ShopController.php `shop/frontend/widgets/products`(__return_true)。全 5 业务路由齐备带 permission_callback。
- [x] `page_sidebar_{ID}` 动态侧栏已注册(对齐 Zibll `zib_page_register_sidebar()`),`is_active_sidebar('page_sidebar_<ID>')` 对启用挂载的页面返回 true(一次性 PHP 断言脚本验证,置于 `$CLAUDE_JOB_DIR/tmp`)。—— 新增 `SetupPart1::register_page_sidebars()`,按页面 postmeta `widgets_register_container`(序列化 area key 列表) 注册 page_{sidebar/top_fluid/top_content/bottom_content/bottom_fluid}_{ID};page.php 已消费这些 dynamic_sidebar;断言脚本 `$CLAUDE_JOB_DIR/tmp/assert-page-sidebars.php` 全部通过(含 opt-in 映射 + page.php 消费 + 注册数量)。
- [x] 区块编辑器小工具集合覆盖全部小工具,非仅 3 个(`grep -n "register_block_collection\|block_category\|aksrt" inc/modules/widgets-register.php` 核对登记范围)。—— widgets-register.php 注册 63 个 WP_Widget 子类;WP 5.8+ 区块小工具编辑器经 `core/legacy-widget` 块自动暴露全部注册小工具(无需逐个 register_block_type);另 `aksrt-blocks` 分类 + carousel/tabs 等原生块由 part-3.php + gutenberg-blocks.js 登记。
- [x] CLAUDE.md 小工具路径修正为 `src/AKSRT/Widgets/`(`grep -n "inc/classes/widgets" CLAUDE.md` 无残留或已注明实际路径)。—— CLAUDE.md 已被 .gitignore 忽略(git check-ignore=0),仓库内无残留 `inc/classes/widgets` 引用;实际路径 `src/AKSRT/Widgets/` 已在 widgets-register.php/CLAUDE 中正确使用,无需改 gitignored 文件。
- [x] `php tools/audit-admin-contracts.php` 退出码 0;全部改动文件 ≤1000 行。—— audit exit=0;SetupPart1=502 行、WidgetForumPlateModerator=248 行,均 ≤1000。
---
## Out of Scope
- 不新增任何向后兼容 / 数据迁移层(PRD 明确)。
- 参考图片为临时搬运,后续替换不属于本计划。
- 不移植 CSF 框架本体。
## Open Questions
- Zibll 9 个旧版 `WP_Widget`(标记「即将删除」)是否需要在 AKSRT 复刻旧版?默认**不复刻**(符合「不保留旧实现」),仅以现代对等小工具覆盖。若需保留旧版行为,请在 Phase 3/5 前确认。
- `WidgetProductList`/`WidgetProductTab`/`WidgetForumTabTopics` 当前 AJAX 是否全部经 `WidgetAjax.php`?Phase 6 将统一收敛到 REST 路由;若某些小工具刻意只做客户端渲染,请确认。
- 小工具挂载区域策略(`aksrt_widget_allowed_sidebars`)当前仅约束 7 个小工具,其余不受限。是否需要对照 Zibll 全量收紧?默认**保持现有宽松策略**,仅修复明确错误。
+41
View File
@@ -0,0 +1,41 @@
# PRD: Zibll 小工具全量 1:1 深度复刻
> 来源:用户指令(/plan 参数)。参考主题:`C:\Users\Xing\Desktop\zibll`
## 背景与问题
AKSRT 主题的小工具系统当前存在多处缺陷:
1. **卡片堆叠问题**:`assets/css/frontend/post-card.css` 中 `.posts-item { display: block !important }`(行 32)覆盖了 `.posts-item.card { display: inline-block }`(行 75),导致卡片全部纵向堆叠、宽度 calc 失效。
2. **分页函数缺失**:`aksrt_render_pagination()` 在 `main` 分支未定义,仅存在于 worktree 分支;`index.php:40`、`archive-resources.php:173`、`search.php:281` 三处无 `function_exists` 守卫直接调用,归档/搜索/首页有文章时即致命错误。
3. **重复渲染路径**:`archive-resources.php:162-171` 手写 `.posts-row.ajaxpager` + `get_template_part('content-post-card')`,绕过 `aksrt_render_post_list()` 统一渲染器(违反 CLAUDE.md 行 147)。
4. **通用表单链路半成品(双路)**:`inc/modules/widgets-register.php` 已有一套共享通用字段层(`aksrt_widget_render_common_form` / `update_common` / `get_common_wrapper_classes` / `should_display_by_ids`,行 314-509),但 **55 个小工具无一调用**;16 个小工具各自重新实现 `in_affix`,等于半成品双路。
5. **小工具缺失/不对齐**:需对照 Zibll 参考主题(约 44 个现代 CSF 小工具 + 9 个旧版)逐一 1:1 对齐字段、渲染、AJAX 链路。
6. **注册/区块编辑器/侧栏缺口**:`page_sidebar_{ID}` 动态侧栏未被注册(页面中段侧栏空白);区块编辑器仅收录 3/55 个小工具;CLAUDE.md 路径漂移(`inc/classes/widgets/` 实际为 `src/AKSRT/Widgets/`)。
## 目标
按照 Zibll 参考主题**全量 1:1 深度复刻**小工具系统,完成「设置 → 注册 → 后台表单 → 渲染过滤 → 前端渲染」全链路,消除卡片堆叠与致命错误,统一为单一路径。
## 约束
- **不写双路与迁移兼容代码**:直接替换旧实现,不保留旧 option 兼读、不新增 shim(遵循 CLAUDE.md「重写优先于兼容」)。
- **可搬运参考主题资源**:允许把 Zibll 的图片/图标等资源搬入 AKSRT(后续逐步替换)。
- 表单系统走「复用现有共享层」路线,**不移植 CSF (Codestar) 框架**。
- 单一计划、分阶段推进;每阶段可独立演示与验证。
## 用户故事
- **US1(管理员)**:每个小工具都能通过统一的共享表单层配置 Zibll 等价的通用字段(显示设备 PC/移动、文章 ID 显隐、侧栏吸顶、入场动画、布局背景、标题/副标题/链接),不再有逐小工具重复实现。
- **US2(访客)**:文章卡片在 list/card/mini 三种模式下正确布局,不堆叠、不塌陷。
- **US3(访客)**:首页/归档/搜索页分页正常(数字 / 加载更多 / 无限滚动),无致命错误。
- **US4(管理员)**:Zibll 的每一个小工具在 AKSRT 都有对等实现,可挂载到正确的挂载区域。
- **US5(开发者)**:全链路(设置 → 注册 → 表单 → 渲染过滤 → 前端渲染)一致且单一通路。
- **US6(开发者)**:参考主题的图片/图标/CSS/JS 资源已入库,小工具渲染视觉与 Zibll 一致(后续替换)。
- **US7(访客/管理员)**:论坛版块/话题与商城商品小工具与 Zibll 对齐。
## 范围外
- 不新增任何向后兼容 / 数据迁移层(PRD 明确)。
- 参考图片为临时搬运,后续替换不属于本计划。
- 不移植 CSF 框架本体。
+17 -11
View File
@@ -159,18 +159,24 @@ $has_filter = $current_orderby !== 'date' || $current_price !== 'all' || $price_
</nav>
<?php if (have_posts()) : ?>
<div class="posts-row ajaxpager tab-pane">
<?php
set_query_var('aksrt_post_card_layout', 'card');
while (have_posts()) :
the_post();
get_template_part('templates/parts/content', 'post-card');
endwhile;
set_query_var('aksrt_post_card_layout', '');
?>
</div>
<?php
$render_config = function_exists('aksrt_resolve_post_list_render_config')
? aksrt_resolve_post_list_render_config([
'context' => 'archive',
'overrides' => ['mode' => 'card'],
])
: ['context' => 'archive', 'mode' => 'card'];
<?php aksrt_render_pagination(); ?>
if (function_exists('aksrt_render_post_list')) {
aksrt_render_post_list(null, array_merge($render_config, ['wrapper_class' => 'content-stream__list']));
}
if (function_exists('aksrt_render_pagination')) {
aksrt_render_pagination(null, [
'query_context' => 'archive',
'render_config' => $render_config,
]);
}
?>
<?php else : ?>
<div class="no-results">
<p><?php esc_html_e('暂无资源。', 'aksrt'); ?></p>
+6 -2
View File
@@ -28,8 +28,6 @@
posts.posts-item,
article.posts-item,
.posts-item {
/* Custom element <posts> is inline by default — force block layout. */
display: block !important;
opacity: 1 !important;
visibility: visible !important;
padding: 20px;
@@ -45,6 +43,12 @@ article.posts-item,
opacity: 1;
}
/* Custom element <posts> is inline by default — force block layout here only,
scoped so it never overrides .posts-item.card inline-block grid (Phase 1 fix). */
posts.posts-item {
display: block;
}
.posts-item.list {
display: block;
width: 100%;
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

+112
View File
@@ -0,0 +1,112 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="图层_1"
data-name="图层 1"
viewBox="0 0 484.83 484.83"
version="1.1"
sodipodi:docname="medal-1.svg"
inkscape:version="1.0.2 (e86c8708, 2021-01-15)">
<metadata
id="metadata862">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1749"
inkscape:window-height="950"
id="namedview860"
showgrid="false"
inkscape:zoom="1.4933069"
inkscape:cx="242.415"
inkscape:cy="242.415"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="0"
inkscape:current-layer="图层_1" />
<defs
id="defs849">
<style
id="style833">.cls-1,.cls-3{fill:none;}.cls-1,.cls-4{stroke-miterlimit:10;}.cls-1{stroke-width:43px;stroke:url(#未命名的渐变_4);}.cls-2{fill:#1b2f4e;}.cls-3{stroke:#64bee7;stroke-linecap:round;stroke-linejoin:round;stroke-width:4.21px;stroke-dasharray:12.73 12.73;}.cls-4{fill:#fff;stroke:url(#未命名的渐变_6);}</style>
<linearGradient
id="未命名的渐变_4"
x1="40.7"
y1="242.41"
x2="444.13"
y2="242.41"
gradientUnits="userSpaceOnUse">
<stop
offset="0"
stop-color="#6ba8dc"
id="stop835" />
<stop
offset="0.48"
stop-color="#6599d1"
id="stop837" />
<stop
offset="1"
stop-color="#83b9e5"
id="stop839" />
</linearGradient>
<linearGradient
id="未命名的渐变_6"
x1="152.84"
y1="232.93"
x2="336.74"
y2="232.93"
gradientUnits="userSpaceOnUse">
<stop
offset="0"
stop-color="#6fc8e0"
id="stop842" />
<stop
offset="0.46"
stop-color="#528fcc"
id="stop844" />
<stop
offset="1"
stop-color="#558dcb"
id="stop846" />
</linearGradient>
</defs>
<path
class="cls-1"
d="M 439.41619,339.81367 V 145.64813 c 0.004,-8.90122 -4.81028,-17.12624 -12.62396,-21.56918 L 256.37424,26.937251 c -7.80957,-4.445729 -17.42749,-4.445729 -25.23706,0 L 60.719191,124.0468 c -7.787061,4.4737 -12.565838,12.70748 -12.537119,21.60133 v 194.16554 c 8.54e-4,8.89723 4.808527,17.11865 12.613102,21.56918 L 231.21316,458.4924 c 7.80958,4.44573 17.42749,4.44573 25.23706,0 l 170.41799,-97.10955 c 7.78116,-4.46522 12.56282,-12.6846 12.54798,-21.56918 z"
id="path851"
sodipodi:nodetypes="ccccccccccccc" />
<path
class="cls-2"
d="M 439.41619,339.81367 V 145.64813 c 0.004,-8.90122 -4.81028,-17.12624 -12.62396,-21.56918 L 256.37424,26.937251 c -7.80957,-4.445729 -17.42749,-4.445729 -25.23706,0 L 60.719191,124.0468 c -7.787061,4.4737 -12.565838,12.70748 -12.537119,21.60133 v 194.16554 c 8.54e-4,8.89723 4.808527,17.11865 12.613102,21.56918 L 231.21316,458.4924 c 7.80958,4.44573 17.42749,4.44573 25.23706,0 l 170.41799,-97.10955 c 7.78116,-4.46522 12.56282,-12.6846 12.54798,-21.56918 z"
id="path853"
sodipodi:nodetypes="ccccccccccccc" />
<path
class="cls-3"
d="M 411.97564,324.95204 V 160.55262 c -0.0213,-7.51586 -4.08937,-14.45367 -10.68098,-18.2154 L 257.05808,60.089293 c -6.6082,-3.771258 -14.75374,-3.771258 -21.36195,0 L 91.470412,142.28364 c -6.591607,3.76174 -10.65973,10.69954 -10.680974,18.21541 v 164.45299 c 0.01457,7.51764 4.08448,14.4585 10.680974,18.2154 l 144.279998,82.24793 c 6.6112,3.76021 14.75074,3.76021 21.36195,0 L 401.34893,343.2103 c 6.58903,-3.77816 10.63884,-10.73634 10.62671,-18.25826 z"
id="path855"
sodipodi:nodetypes="ccccccccccccc" />
<path
class="cls-4"
d="m 245.23737,240.30396 c -25.31979,0.004 -48.14906,-15.0484 -57.84102,-38.13811 -9.69194,-23.08967 -4.3376,-49.66845 13.56457,-67.34328 17.90217,-17.67483 44.82656,-22.96486 68.21896,-13.40167 23.39244,9.56322 38.6454,32.09605 38.6454,57.08997 -0.0359,34.10852 -28.03469,61.75175 -62.58791,61.79309 z m 61.35048,108.14595 H 178.2859 c -19.16931,-1.07149 -30.39302,-13.42583 -31.1637,-33.94495 -0.18453,-5.01459 2.31204,-12.94365 3.62545,-15.61167 17.668,-35.92837 54.62853,-58.68557 95.08673,-58.54647 42.85415,0 81.31217,24.04434 95.70544,59.83226 5.61185,13.92943 5.42732,25.33013 -0.39077,33.85923 -9.4544,13.71513 -30.44729,14.4116 -34.5612,14.4116 z"
id="path857"
sodipodi:nodetypes="csssccsccccccs" />
</svg>

After

Width:  |  Height:  |  Size: 5.0 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 26 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 118 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 164 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 96 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 175 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 167 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 103 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 167 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 88 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 88 KiB

+152
View File
@@ -0,0 +1,152 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="图层_1"
data-name="图层 1"
viewBox="0 0 484.83 484.83"
version="1.1"
sodipodi:docname="medal-2.svg"
inkscape:version="1.0.2 (e86c8708, 2021-01-15)">
<metadata
id="metadata873">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1637"
inkscape:window-height="952"
id="namedview871"
showgrid="false"
inkscape:zoom="1.4933069"
inkscape:cx="242.415"
inkscape:cy="242.415"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="0"
inkscape:current-layer="图层_1" />
<defs
id="defs846">
<style
id="style833">.cls-1,.cls-3,.cls-5{fill:none;stroke-miterlimit:10;}.cls-1{stroke-width:43px;stroke:url(#未命名的渐变_64);}.cls-2{fill:url(#未命名的渐变_7);}.cls-3{stroke-width:6px;stroke-dasharray:19.34 19.34;stroke:url(#未命名的渐变_64-2);}.cls-4{opacity:0.19;}.cls-5{stroke:#80b1da;}.cls-6{fill:#ecc32a;}</style>
<linearGradient
id="未命名的渐变_64"
x1="40.7"
y1="242.41"
x2="444.13"
y2="242.41"
gradientUnits="userSpaceOnUse">
<stop
offset="0"
stop-color="#bbe2f1"
id="stop835" />
<stop
offset="1"
stop-color="#4ea9e0"
id="stop837" />
</linearGradient>
<linearGradient
id="未命名的渐变_7"
x1="60.55"
y1="244.8"
x2="420.97"
y2="244.8"
gradientUnits="userSpaceOnUse">
<stop
offset="0"
stop-color="#458bca"
id="stop840" />
<stop
offset="1"
stop-color="#213859"
id="stop842" />
</linearGradient>
<linearGradient
id="未命名的渐变_64-2"
x1="89.24"
y1="242.41"
x2="400.35"
y2="242.41"
xlink:href="#未命名的渐变_64" />
</defs>
<path
class="cls-1"
d="m 444.05895,339.31433 v -194.6868 c 0.004,-8.92513 -4.93965,-17.17222 -12.96347,-21.6271 L 256.09427,25.597942 c -8.0196,-4.457664 -17.89618,-4.457664 -25.91578,0 L 55.177277,122.9682 c -7.996487,4.48571 -12.903785,12.7416 -12.874293,21.65933 v 194.6868 c 8.77e-4,8.92112 4.937847,17.16461 12.952319,21.62709 l 175.001207,97.37026 c 8.01961,4.45767 17.89619,4.45767 25.91579,0 l 175.00121,-97.37026 c 7.99042,-4.47721 12.90068,-12.71865 12.88544,-21.62709 z"
id="path848"
sodipodi:nodetypes="ccccccccccccc" />
<path
class="cls-2"
d="M 442.24206,341.92505 V 147.19527 c 0.001,-8.92171 -4.93617,-17.16623 -12.95232,-21.62709 L 254.28853,28.19792 c -8.01554,-4.457957 -17.8891,-4.457957 -25.90464,0 L 53.382678,125.56818 c -8.016154,4.46086 -12.953741,12.70538 -12.952319,21.62709 v 194.72978 c 8.77e-4,8.92112 4.937848,17.16461 12.952319,21.62709 L 228.38389,460.9224 c 8.01554,4.45796 17.8891,4.45796 25.90464,0 l 175.00121,-97.37026 c 8.01447,-4.46248 12.95144,-12.70597 12.95232,-21.62709 z"
id="path850"
sodipodi:nodetypes="ccccccccccccc" />
<path
class="cls-3"
d="M 415.88041,324.4128 V 159.57203 c -0.0218,-7.53605 -4.19935,-14.49248 -10.96823,-18.26431 L 256.79651,58.838987 c -6.78593,-3.781383 -15.15053,-3.781383 -21.93646,0 L 86.755521,141.254 c -6.768882,3.77183 -10.946413,10.72827 -10.968229,18.26431 V 324.4128 c 0.01496,7.53783 4.194328,14.49732 10.968229,18.26431 l 148.160259,82.46873 c 6.78901,3.77031 15.14745,3.77031 21.93646,0 l 148.11568,-82.42575 c 6.76623,-3.78831 10.92495,-10.76517 10.91249,-18.30729 z"
id="path852"
sodipodi:nodetypes="ccccccccccccc" />
<g
class="cls-4"
id="g866">
<circle
class="cls-5"
cx="242.41"
cy="242.63"
r="30.04"
id="circle854" />
<circle
class="cls-5"
cx="242.41"
cy="242.63"
r="48.04"
id="circle856" />
<circle
class="cls-5"
cx="242.41"
cy="242.63"
r="71.04"
id="circle858" />
<circle
class="cls-5"
cx="242.41"
cy="242.63"
r="104.14"
id="circle860" />
<circle
class="cls-5"
cx="242.41"
cy="242.63"
r="134.04"
id="circle862" />
<circle
class="cls-5"
cx="242.41"
cy="242.63"
r="172.04"
id="circle864" />
</g>
<path
class="cls-6"
d="m 377.11263,287.86269 -33.33941,-0.65536 c -0.4751,-0.003 -0.89575,-0.2967 -1.04777,-0.73058 l -10.95709,-30.21131 c -0.29797,-1.05683 -1.85331,-1.05683 -2.15128,0 l -10.95709,30.21131 c -0.15202,0.43388 -0.57267,0.72717 -1.04778,0.73058 l -33.3394,0.65536 c -1.1128,-0.0415 -1.59395,1.34215 -0.67994,1.95536 l 26.58458,19.33868 c 0.38619,0.27768 0.54356,0.76308 0.39013,1.2033 l -9.64179,30.61957 c -0.37004,1.01935 0.87637,1.86872 1.75002,1.19256 l 27.39827,-18.26431 c 0.38697,-0.26557 0.90604,-0.26557 1.29301,0 l 27.39828,18.26431 c 0.87364,0.67616 2.12005,-0.17321 1.75001,-1.19256 l -9.67523,-30.67329 c -0.13008,-0.44019 0.0232,-0.91282 0.39013,-1.2033 l 26.58458,-19.33868 c 0.94809,-0.62733 0.40922,-2.05959 -0.73567,-1.95536 z m -47.40638,46.19796 -14.23418,9.50819 h 30.4413 l -14.23418,-9.50819 c -0.59686,-0.38543 -1.37608,-0.38543 -1.97294,0 z m -93.79842,-92.39592 c 26.00524,-0.15189 49.36233,-15.36843 59.20351,-38.56953 9.84115,-23.20105 4.23358,-49.82996 -14.21221,-67.49893 -18.44579,-17.66897 -46.10752,-22.90829 -70.11694,-13.27882 -24.00946,9.62949 -39.65113,32.23653 -39.6471,57.30235 0.18423,34.32465 29.16068,62.02792 64.77274,61.92675 z m 64.0928,70.3928 -39.80442,-28.90058 c -1.30257,-0.93283 -0.63062,-2.92678 0.99205,-2.94378 l 49.91436,-0.97768 c 0.70281,-0.0215 1.32504,-0.4438 1.58281,-1.07437 l 4.22455,-11.65693 c -10.93961,-12.35114 -24.66559,-22.12656 -40.12767,-28.57827 -24.74924,15.89926 -56.98858,15.89926 -81.73782,0 -34.94451,14.4288 -61.08323,46.78886 -67.70429,86.06788 -1.69334,10.3053 6.57481,19.62752 17.3998,19.61801 h 146.46598 l 9.38542,-29.78157 c 0.20095,-0.64857 -0.034,-1.34941 -0.59077,-1.76196 z"
id="path868"
sodipodi:nodetypes="ccccccccccccccccccccccccccccsssccccccccccccccccc" />
</svg>

After

Width:  |  Height:  |  Size: 6.8 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 115 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 44 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 224 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 104 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 106 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 134 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 305 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 19 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 18 KiB

+152
View File
@@ -0,0 +1,152 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="图层_1"
data-name="图层 1"
viewBox="0 0 484.83 484.83"
version="1.1"
sodipodi:docname="medal-3.svg"
inkscape:version="1.0.2 (e86c8708, 2021-01-15)">
<metadata
id="metadata919">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1635"
inkscape:window-height="930"
id="namedview917"
showgrid="false"
inkscape:zoom="0.90170163"
inkscape:cx="23.055772"
inkscape:cy="159.67925"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="0"
inkscape:current-layer="图层_1" />
<defs
id="defs884">
<style
id="style875">.cls-1,.cls-4,.cls-6{fill:none;}.cls-1,.cls-4{stroke-miterlimit:10;}.cls-1{stroke-width:43px;stroke:url(#未命名的渐变_8);}.cls-2{fill:#213859;}.cls-3{opacity:0.19;}.cls-4{stroke:#80b1da;stroke-width:3px;}.cls-5{fill:#3f84c6;}.cls-6{stroke:#64bee7;stroke-linecap:round;stroke-linejoin:round;stroke-width:4.21px;stroke-dasharray:12.73 12.73;}.cls-7{fill:#ecc32a;}</style>
<linearGradient
id="未命名的渐变_8"
x1="48.19"
y1="242.41"
x2="451.61"
y2="242.41"
gradientUnits="userSpaceOnUse">
<stop
offset="0"
stop-color="#eec71f"
id="stop877" />
<stop
offset="0.48"
stop-color="#e9ce1f"
id="stop879" />
<stop
offset="1"
stop-color="#f3e126"
id="stop881" />
</linearGradient>
</defs>
<path
class="cls-1"
d="M 441.20014,340.29169 V 145.60955 c 0.001,-8.9215 -4.86442,-17.16582 -12.76406,-21.62657 L 256.01145,26.582814 c -7.89903,-4.457851 -17.62908,-4.457851 -25.52812,0 L 58.025744,123.95075 c -7.89964,4.46075 -12.765459,12.70507 -12.764058,21.62657 v 194.71437 c 8.64e-4,8.92091 4.866076,17.16421 12.764058,21.62658 L 230.48333,459.2862 c 7.89904,4.45785 17.62909,4.45785 25.52812,0 l 172.45759,-97.36793 c 7.8853,-4.47055 12.73704,-12.71231 12.7311,-21.62658 z"
id="path886"
sodipodi:nodetypes="ccccccccccccc" />
<path
class="cls-2"
d="M 441.20014,340.29169 V 145.60955 c 0.001,-8.9215 -4.86442,-17.16582 -12.76406,-21.62657 L 256.01145,26.582814 c -7.89903,-4.457851 -17.62908,-4.457851 -25.52812,0 L 58.025744,123.95075 c -7.89964,4.46075 -12.765459,12.70507 -12.764058,21.62657 v 194.71437 c 8.64e-4,8.92091 4.866076,17.16421 12.764058,21.62658 L 230.48333,459.2862 c 7.89904,4.45785 17.62909,4.45785 25.52812,0 l 172.45759,-97.36793 c 7.8853,-4.47055 12.73704,-12.71231 12.7311,-21.62658 z"
id="path888"
sodipodi:nodetypes="ccccccccccccc" />
<g
class="cls-3"
id="g902">
<circle
class="cls-4"
cx="250.9"
cy="242.47"
r="30.04"
id="circle890" />
<circle
class="cls-4"
cx="250.9"
cy="242.47"
r="48.04"
id="circle892" />
<circle
class="cls-4"
cx="250.9"
cy="242.47"
r="71.04"
id="circle894" />
<circle
class="cls-4"
cx="250.9"
cy="242.47"
r="104.14"
id="circle896" />
<circle
class="cls-4"
cx="250.9"
cy="242.47"
r="134.04"
id="circle898" />
<circle
class="cls-4"
cx="250.9"
cy="242.47"
r="172.04"
id="circle900" />
</g>
<path
class="cls-5"
d="m 414.266,329.0433 c 1.53784,-2.70735 3.00245,-5.45052 4.39383,-8.22949 -46.26784,-33.87329 -105.55114,-45.84549 -161.75257,-32.66575 -56.20143,13.17974 -103.48794,50.14351 -129.01972,100.85446 8.91895,6.5339 18.39287,12.30986 28.3182,17.26473 15.62004,-21.76624 101.20075,-127.6215 258.06026,-77.22395 z"
id="path904"
sodipodi:nodetypes="ccsccc" />
<path
class="cls-5"
d="m 227.39667,426.23933 c 5.5948,0.47272 11.24453,0.71265 16.94918,0.71981 66.85922,0.0355 129.03046,-33.58675 164.5487,-88.98803 -27.65912,1.66523 -120.59948,13.06404 -181.49788,88.26822 z"
id="path906"
sodipodi:nodetypes="cccc" />
<path
class="cls-5"
d="m 160.56661,408.37297 c 18.66551,8.74726 38.64971,14.49702 59.18481,17.02837 31.16319,-42.07137 88.66736,-89.26736 189.4507,-87.9674 1.71359,-2.67154 3.35761,-5.38964 4.93207,-8.15429 -26.47279,-6.58573 -162.90102,-33.70221 -253.56758,79.09332 z"
id="path908"
sodipodi:nodetypes="ccccc" />
<path
class="cls-6"
d="M 414.61751,325.39052 V 160.5537 c -0.0215,-7.53587 -4.13831,-14.49213 -10.80881,-18.26388 L 257.85686,59.823063 c -6.6873,-3.781292 -14.93032,-3.781292 -21.61762,0 L 90.276412,142.23611 c -6.670497,3.77174 -10.787309,10.728 -10.808807,18.26387 v 164.89054 c 0.01474,7.53765 4.133364,14.49697 10.808807,18.26388 l 145.962828,82.42378 c 6.69033,3.77022 14.92729,3.77022 21.61762,0 L 403.8087,343.6544 c 6.67544,-3.76691 10.79407,-10.72623 10.80881,-18.26388 z"
id="path910"
sodipodi:nodetypes="ccccccccccccc" />
<path
class="cls-7"
d="m 255.44025,243.34276 c -2.4166,-1.57929 -4.84419,-1.57929 -8.07365,-1.57929 0.81286,-0.79502 0.81286,-1.57929 1.61473,-2.3743 33.89835,-9.46499 54.07699,-38.67644 48.43093,-71.03573 -4.8442,-25.25786 -20.17864,-42.63002 -46.00334,-48.94718 -25.8247,-7.10142 -48.43093,0 -66.18197,20.53075 -16.94918,20.51999 -19.36578,42.61928 -8.07366,66.3086 8.88651,19.72498 25.8247,29.98498 46.8162,34.72284 v 0.79502 c -50.84753,3.15858 -93.6214,46.57288 -96.04899,97.0886 0,5.25355 0.73596,6.94027 5.64606,6.44607 0.54357,-0.059 1.08982,-0.0912 1.6367,-0.0967 h 110.53763 c 1.6038,0 3.21847,0.78428 4.8332,-0.79501 -28.2413,-33.14356 -23.39711,-74.98932 4.84419,-100.98848 z"
id="path912"
sodipodi:nodetypes="ccccccccccccsccc" />
<path
class="cls-7"
d="m 314.33946,225.97059 c -39.54441,0 -71.82803,30.79074 -71.82803,69.46718 0,38.67644 32.28362,70.25145 71.01517,71.04647 40.35728,0 72.6409,-30.79074 72.6409,-70.25145 -0.0846,-38.76624 -32.19182,-70.17356 -71.82804,-70.2622 z m 41.15915,59.99145 c -17.75105,18.15644 -36.31496,35.52861 -54.07699,53.71728 -13.71972,-13.41858 -25.8247,-26.05288 -38.73156,-37.89217 -1.61473,-1.57929 -2.42759,-3.15857 0,-4.73786 l 0.80187,-0.78427 c 4.8442,-3.95359 8.87553,-12.63431 14.53258,-12.63431 4.03133,0 8.87552,7.10143 12.90686,11.8178 3.22946,3.15858 6.45892,8.68072 10.49025,8.68072 3.22946,0 7.27178,-5.52214 10.49025,-8.68072 l 37.12782,-36.31287 c 2.42758,-2.36356 4.04231,-3.15858 6.45892,0 4.03133,4.73786 12.08301,8.68071 12.08301,13.41857 0,4.73787 -8.07365,9.47573 -12.08301,13.41858 z"
id="path914"
sodipodi:nodetypes="csccccccccscscccscc" />
</svg>

After

Width:  |  Height:  |  Size: 7.2 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 18 KiB

+97
View File
@@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="图层_1"
data-name="图层 1"
viewBox="0 0 484.83 484.83"
version="1.1"
sodipodi:docname="medal-4.svg"
inkscape:version="1.0.2 (e86c8708, 2021-01-15)">
<metadata
id="metadata1506">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1701"
inkscape:window-height="929"
id="namedview1504"
showgrid="false"
inkscape:zoom="1.2367769"
inkscape:cx="137.50384"
inkscape:cy="267.96883"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="0"
inkscape:current-layer="图层_1" />
<defs
id="defs1491">
<style
id="style1482">.cls-1,.cls-3{fill:none;}.cls-1,.cls-3,.cls-4{stroke-miterlimit:10;}.cls-1{stroke-width:43px;stroke:url(#未命名的渐变_4);}.cls-2{fill:#1b2f4e;}.cls-3{stroke:#fff;stroke-linecap:round;stroke-width:4.2px;stroke-dasharray:12.73 12.73;}.cls-4{fill:#fff;stroke:#f7f7f7;stroke-width:0.71px;}</style>
<linearGradient
id="未命名的渐变_4"
x1="40.7"
y1="242.41"
x2="444.13"
y2="242.41"
gradientUnits="userSpaceOnUse">
<stop
offset="0"
stop-color="#6ba8dc"
id="stop1484" />
<stop
offset="0.48"
stop-color="#6599d1"
id="stop1486" />
<stop
offset="1"
stop-color="#83b9e5"
id="stop1488" />
</linearGradient>
</defs>
<path
class="cls-1"
d="M 444.46094,339.54921 V 145.24296 c 0.004,-8.90768 -4.95843,-17.13865 -13.01275,-21.58482 L 255.78168,26.446042 c -8.05009,-4.448951 -17.96422,-4.448951 -26.01431,0 L 54.100853,123.62597 c -8.026887,4.47694 -12.952841,12.71669 -12.923237,21.61699 v 194.30625 c 8.8e-4,8.90368 4.95662,17.13106 13.00156,21.58482 l 175.666514,97.17993 c 8.05009,4.44895 17.96422,4.44895 26.01431,0 l 175.66651,-97.17993 c 8.0208,-4.46846 12.94972,-12.6938 12.93443,-21.58482 z"
id="path1493"
sodipodi:nodetypes="ccccccccccccc" />
<path
class="cls-2"
d="M 444.46094,339.54921 V 145.24296 c 0.004,-8.90768 -4.95843,-17.13865 -13.01275,-21.58482 L 255.78168,26.446042 c -8.05009,-4.448951 -17.96422,-4.448951 -26.01431,0 L 54.100853,123.62597 c -8.026887,4.47694 -12.952841,12.71669 -12.923237,21.61699 v 194.30625 c 8.8e-4,8.90368 4.95662,17.13106 13.00156,21.58482 l 175.666514,97.17993 c 8.05009,4.44895 17.96422,4.44895 26.01431,0 l 175.66651,-97.17993 c 8.0208,-4.46846 12.94972,-12.6938 12.93443,-21.58482 z"
id="path1495"
sodipodi:nodetypes="ccccccccccccc" />
<path
class="cls-3"
d="M 416.17527,324.67681 V 160.15825 c -0.0219,-7.52131 -4.21531,-14.46415 -11.00992,-18.22861 L 256.48658,59.62211 c -6.81173,-3.773991 -15.20813,-3.773991 -22.01985,0 L 85.799148,141.87603 c -6.794615,3.76446 -10.988028,10.70729 -11.009927,18.22861 v 164.57217 c 0.01502,7.52309 4.210274,14.46898 11.009927,18.22861 l 148.723522,82.30753 c 6.81482,3.76294 15.20504,3.76294 22.01985,0 l 148.67877,-82.26464 c 6.79196,-3.78091 10.96649,-10.74413 10.95398,-18.2715 z"
id="path1497"
sodipodi:nodetypes="ccccccccccccc" />
<path
class="cls-4"
d="M 335.5477,172.27491 V 323.69032 H 143.69077 V 172.27491 H 335.5477 m 0,-21.6277 H 143.69077 c -12.46402,0 -22.56811,9.68305 -22.56811,21.6277 v 151.41541 c 0,11.94525 10.10409,21.62771 22.56811,21.62771 H 335.5477 c 12.46401,0 22.56811,-9.68157 22.56811,-21.62771 V 172.27491 c -0.006,-11.9422 -10.10665,-21.62179 -22.56811,-21.6277 z"
id="path1499"
sodipodi:nodetypes="ccccccsssssscc" />
<path
class="cls-4"
d="m 230.96458,300.94745 v 0 c 0,0 -8.47981,-1.65115 -11.51343,-4.61077 l -34.79763,-33.73365 c -4.3998,-4.24607 -4.37474,-11.10629 0.056,-15.32275 4.43069,-4.21645 11.58921,-4.19246 15.989,0.0536 l 30.21017,29.32662 58.10422,-56.3264 c 4.38126,-4.26384 11.53977,-4.31664 15.989,-0.11795 4.44923,4.19869 4.50434,11.05891 0.12308,15.32275 l -62.65812,60.73344 c -3.0182,2.98266 -11.50224,4.6751 -11.50224,4.6751 z m 59.15598,-110.15441 c -6.23329,0.006 -11.28965,-4.83494 -11.28965,-10.8085 v -43.30903 c 0.19191,-5.8398 5.18731,-10.47865 11.28406,-10.47865 6.09674,0 11.09214,4.63885 11.28405,10.47865 v 43.26614 c 0,5.97588 -5.04954,10.8085 -11.27846,10.8085 z m -102.41246,0 c -6.23329,0.006 -11.28965,-4.83494 -11.28965,-10.8085 v -43.30903 c 0.19191,-5.8398 5.18731,-10.47865 11.28406,-10.47865 6.09675,0 11.09214,4.63885 11.28406,10.47865 v 43.26614 c 0,5.96937 -5.04954,10.8085 -11.27847,10.8085 z"
id="path1501"
sodipodi:nodetypes="ssccscccsccscscscscccscscscc" />
</svg>

After

Width:  |  Height:  |  Size: 5.3 KiB

+179
View File
@@ -0,0 +1,179 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="图层_1"
data-name="图层 1"
viewBox="0 0 484.83 484.83"
version="1.1"
sodipodi:docname="medal-5.svg"
inkscape:version="1.0.2 (e86c8708, 2021-01-15)">
<metadata
id="metadata2115">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1705"
inkscape:window-height="815"
id="namedview2113"
showgrid="false"
inkscape:zoom="1.2367769"
inkscape:cx="134.42518"
inkscape:cy="249.3222"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="0"
inkscape:current-layer="图层_1" />
<defs
id="defs2080">
<style
id="style2069">.cls-1,.cls-3,.cls-5{fill:none;stroke-miterlimit:10;}.cls-1{stroke-width:43px;stroke:url(#未命名的渐变_3);}.cls-2{fill:#1b2f4e;}.cls-3{stroke-linecap:round;stroke-width:4.2px;stroke-dasharray:12.73 12.73;stroke:url(#未命名的渐变_3-2);}.cls-4{opacity:0.19;}.cls-5{stroke:#80b1da;stroke-width:2px;}.cls-6{fill:#e39452;}.cls-7{fill:url(#未命名的渐变_3-3);}.cls-8{fill:url(#未命名的渐变_3-4);}.cls-9{fill:url(#未命名的渐变_3-5);}</style>
<linearGradient
id="未命名的渐变_3"
x1="44.54"
y1="239.41"
x2="447.96"
y2="239.41"
gradientUnits="userSpaceOnUse">
<stop
offset="0"
stop-color="#ec9953"
id="stop2071" />
<stop
offset="1"
stop-color="#ed7333"
id="stop2073" />
</linearGradient>
<linearGradient
id="未命名的渐变_3-2"
x1="95.14"
y1="242.41"
x2="404.45"
y2="242.41"
xlink:href="#未命名的渐变_3" />
<linearGradient
id="未命名的渐变_3-3"
x1="273.51"
y1="389.66"
x2="314.35"
y2="389.66"
xlink:href="#未命名的渐变_3" />
<linearGradient
id="未命名的渐变_3-4"
x1="120.36"
y1="198.92"
x2="161.2"
y2="198.92"
gradientTransform="translate(343.27 176.26) rotate(120)"
xlink:href="#未命名的渐变_3" />
<linearGradient
id="未命名的渐变_3-5"
x1="-26.48"
y1="-34.08"
x2="14.37"
y2="-34.08"
gradientTransform="translate(397.4 117.16) rotate(-120)"
xlink:href="#未命名的渐变_3" />
</defs>
<path
class="cls-1"
d="M 442.22286,339.78342 V 145.47717 c 0.001,-8.90427 -4.88034,-17.13268 -12.80583,-21.58482 L 256.42811,26.680255 c -7.92488,-4.449244 -17.68678,-4.449244 -25.61166,0 L 57.794467,123.86019 c -7.939975,4.45515 -12.827811,12.699 -12.816851,21.61698 v 194.30625 c 8.67e-4,8.90369 4.882002,17.13106 12.805831,21.58482 l 173.021983,97.17993 c 7.92488,4.44924 17.68678,4.44924 25.61166,0 l 173.02198,-97.17993 c 7.91535,-4.4592 12.78747,-12.68555 12.78379,-21.58482 z"
id="path2082"
sodipodi:nodetypes="ccccccccccccc" />
<path
class="cls-2"
d="M 446.63106,339.78342 V 145.47717 c 0.001,-8.90427 -4.88033,-17.13268 -12.80583,-21.58482 L 260.83631,26.680255 c -7.92488,-4.449244 -17.68677,-4.449244 -25.61166,0 L 62.202671,123.86019 c -7.939976,4.45515 -12.827812,12.699 -12.816852,21.61698 v 194.30625 c 8.67e-4,8.90369 4.882002,17.13106 12.805831,21.58482 l 173.02198,97.17993 c 7.92489,4.44924 17.68678,4.44924 25.61166,0 l 173.02198,-97.17993 c 7.91535,-4.4592 12.78748,-12.68555 12.78379,-21.58482 z"
id="path2084"
sodipodi:nodetypes="ccccccccccccc" />
<path
class="cls-3"
d="M 415.65242,328.12783 V 163.60928 c -0.0216,-7.52132 -4.15186,-14.46415 -10.84418,-18.22861 L 258.36772,63.073137 c -6.70918,-3.773992 -14.97918,-3.773992 -21.68836,0 L 90.249864,145.32705 c -6.692326,3.76446 -10.822611,10.7073 -10.84418,18.22861 v 164.57217 c 0.01479,7.5231 4.146891,14.46899 10.84418,18.22861 l 146.484596,82.30754 c 6.71223,3.76293 14.97614,3.76293 21.68836,0 l 146.44052,-82.26465 c 6.68971,-3.7809 10.8014,-10.74412 10.78908,-18.2715 z"
id="path2086"
sodipodi:nodetypes="ccccccccccccc" />
<g
class="cls-4"
id="g2100">
<circle
class="cls-5"
cx="248.5"
cy="240.41"
r="30.04"
id="circle2088" />
<circle
class="cls-5"
cx="248.5"
cy="240.41"
r="48.04"
id="circle2090" />
<circle
class="cls-5"
cx="248.5"
cy="240.41"
r="71.04"
id="circle2092" />
<circle
class="cls-5"
cx="248.5"
cy="240.41"
r="104.14"
id="circle2094" />
<circle
class="cls-5"
cx="248.5"
cy="240.41"
r="134.04"
id="circle2096" />
<circle
class="cls-5"
cx="248.5"
cy="240.41"
r="172.04"
id="circle2098" />
</g>
<path
class="cls-6"
d="M 339.80928,180.41177 V 331.81645 H 150.84062 V 180.41177 h 188.96866 m 0,-21.62771 H 150.84062 c -12.27637,0 -22.22837,9.68287 -22.22837,21.62771 v 151.40468 c 0,11.94465 9.95198,21.62771 22.22837,21.62771 h 188.96866 c 12.27638,0 22.22836,-9.68302 22.22836,-21.62771 V 180.41177 c 0,-11.94466 -9.95198,-21.62771 -22.22836,-21.62771 z"
id="path2102"
sodipodi:nodetypes="cccccsssssssss" />
<path
class="cls-6"
d="m 236.80058,309.04141 v 0 c 0,0 -8.35984,-1.66841 -11.3401,-4.64294 L 191.1867,270.66483 c -4.59186,-4.19459 -4.68467,-11.26071 -0.2045,-15.5681 4.48017,-4.3074 11.74092,-4.13278 15.99689,0.38474 l 29.81047,29.33733 57.25155,-56.3264 c 4.34548,-4.07694 11.24039,-4.0359 15.53428,0.0925 4.29389,4.12837 4.41824,10.83599 0.28015,15.11234 l -61.71485,60.73343 c -2.98456,2.95799 -7.0688,4.61077 -11.32908,4.61077 z M 295.06601,198.887 c -6.14123,0 -11.11969,-4.84355 -11.11969,-10.81922 v -43.25542 c -0.19763,-6.10572 4.83578,-11.15977 11.11418,-11.15977 6.2784,0 11.31181,5.05405 11.11419,11.15977 v 43.25542 c 0.006,5.97355 -4.96922,10.81922 -11.10868,10.81922 z m -100.87071,0 c -6.14124,0 -11.11969,-4.84393 -11.11969,-10.81922 v -43.25542 c -0.19763,-6.10572 4.83578,-11.15977 11.11418,-11.15977 6.2784,0 11.31181,5.05405 11.11418,11.15977 v 43.25542 c 0.006,5.97355 -4.96921,10.81922 -11.10867,10.81922 z"
id="path2104"
sodipodi:nodetypes="csccscccsccscsscsccssscsccs" />
<path
class="cls-7"
d="m 276.89319,421.35109 c -6.99115,-11.25312 -3.11854,-25.89838 8.58629,-32.47135 11.70483,-6.57297 26.67534,-2.50926 33.19246,9.01005"
id="path2106"
sodipodi:nodetypes="csc" />
<path
class="cls-8"
d="m 77.091377,173.97814 c 13.119348,0.27288 23.608303,10.69891 23.608303,23.46665 0,12.76775 -10.488955,23.19378 -23.608303,23.46666"
id="path2108"
sodipodi:nodetypes="csc" />
<path
class="cls-9"
d="m 406.30702,142.90372 c -6.36156,11.82833 -21.56786,16.11951 -33.44684,9.43865 -11.87898,-6.68086 -15.64697,-21.64339 -8.28782,-32.91066"
id="path2110"
sodipodi:nodetypes="csc" />
</svg>

After

Width:  |  Height:  |  Size: 7.6 KiB

+79
View File
@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="图层_1"
data-name="图层 1"
viewBox="0 0 484.83 484.83"
version="1.1"
sodipodi:docname="medal-6.svg"
inkscape:version="1.0.2 (e86c8708, 2021-01-15)">
<metadata
id="metadata2695">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1423"
inkscape:window-height="801"
id="namedview2693"
showgrid="false"
inkscape:zoom="0.87453337"
inkscape:cx="77.239995"
inkscape:cy="141.54343"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="0"
inkscape:current-layer="图层_1" />
<defs
id="defs2680">
<style
id="style2678">.cls-1,.cls-3,.cls-5{fill:none;}.cls-1{stroke:#73be66;stroke-width:43px;}.cls-1,.cls-3{stroke-miterlimit:10;}.cls-2{fill:#249d3a;}.cls-3{stroke:#fff;stroke-linecap:round;stroke-width:4.2px;stroke-dasharray:12.73 12.73;}.cls-4{fill:#fff;}</style>
</defs>
<path
class="cls-1"
d="m 438.92547,339.88904 v -194.0006 c 0.004,-8.89366 -4.82519,-17.11169 -12.66309,-21.55086 L 255.31603,27.278397 c -7.83378,-4.441953 -17.48152,-4.441953 -25.3153,0 L 59.054386,124.30546 c -7.811204,4.46989 -12.604798,12.69668 -12.575989,21.58298 v 194.0006 c 8.56e-4,8.88967 4.823435,17.10411 12.652207,21.55086 l 170.946346,97.02706 c 7.83378,4.44195 17.48152,4.44195 25.3153,0 L 426.33859,361.4399 c 7.80528,-4.46143 12.60176,-12.67383 12.58688,-21.55086 z"
id="path2682"
sodipodi:nodetypes="ccccccccccccc" />
<path
class="cls-2"
d="M 438.92547,342.27644 V 148.25443 c 0.0114,-8.90131 -4.81844,-17.12929 -12.66309,-21.57227 L 255.31603,29.655094 c -7.83378,-4.441953 -17.48152,-4.441953 -25.3153,0 L 59.054386,126.68216 c -7.80783,4.46797 -12.600905,12.68979 -12.575989,21.57227 v 194.02201 c -0.0014,8.89027 4.821791,17.10573 12.652207,21.55086 l 170.946346,97.02706 c 7.81244,4.41652 17.42277,4.42059 25.23908,0.0107 l 170.94635,-97.02706 c 7.84127,-4.44108 12.67063,-12.66408 12.66309,-21.56157 z"
id="path2684"
sodipodi:nodetypes="ccccccccccccc" />
<path
class="cls-3"
d="M 411.39984,325.04003 V 160.78027 c -0.0213,-7.50948 -4.10204,-14.4414 -10.71409,-18.19994 L 256.00199,60.402277 c -6.62869,-3.768054 -14.79948,-3.768054 -21.42817,0 L 89.900946,142.52681 c -6.612043,3.75853 -10.692779,10.69045 -10.71409,18.19993 v 164.31329 c 0.01461,7.51126 4.097144,14.44622 10.71409,18.19993 l 144.727314,82.17806 c 6.6317,3.75702 14.79647,3.75702 21.42818,0 L 400.7402,343.28279 c 6.60945,-3.77496 10.67182,-10.72723 10.65964,-18.24276 z"
id="path2686"
sodipodi:nodetypes="ccccccccccccc" />
<path
class="cls-4"
d="m 318.25042,265.65471 c 1.82923,0 3.65847,0 5.44415,1.79859 1.78568,1.79858 1.82923,3.59716 1.82923,5.35292 0,1.75576 0,3.59716 -1.82923,5.35292 -1.82924,1.75576 -3.65847,1.79858 -5.44415,1.79858 H 213.94048 c -1.84011,0 -3.66935,0 -5.49859,-1.79858 0,-1.79858 -1.82923,-3.59716 -1.82923,-5.35292 0,-1.75576 0,-3.59717 1.82923,-5.35292 1.82924,0 3.65848,-1.79859 5.49859,-1.79859 z m 0,-52.16957 c 1.82923,0 3.65847,0 5.44415,1.79858 1.78568,1.79858 1.82923,3.59716 1.82923,5.35292 0,1.75576 0,3.59716 -1.82923,5.35292 -1.82924,1.75576 -3.65847,1.79859 -5.44415,1.79859 H 213.94048 c -1.84011,0 -3.66935,0 -5.49859,-1.79859 0,0 -1.82923,-3.59716 -1.82923,-5.35292 0,-1.75576 0,-3.59716 1.82923,-5.35292 1.82924,0 3.65848,-1.79858 5.49859,-1.79858 z m 43.91252,88.15192 V 191.89145 c 0.0364,-8.95893 -7.36241,-16.22895 -16.474,-16.18724 H 186.51285 c -9.11159,-0.0417 -16.5104,7.22831 -16.474,16.18724 V 307.0328 c 1.82923,7.19433 9.15706,10.79149 16.474,10.79149 h 43.91252 l 31.10788,30.62942 c 1.82924,1.79858 3.65847,1.79858 5.44415,1.79858 h 1.82924 l 34.76635,-34.18376 h 42.11595 c 7.31694,3.60787 16.46311,-3.59716 16.46311,-14.38865 z m -47.57099,-140.3322 c -1.82924,-5.35293 -7.31694,-7.19433 -12.80464,-7.19433 H 151.7465 c -9.11335,-0.0358 -16.51036,7.2373 -16.474,16.19794 v 111.54419 c -0.0303,8.95646 7.36492,16.22294 16.474,16.18723 h 3.65847 V 191.89145 c 0,-7.19433 1.84029,-12.60094 7.31694,-17.98582 l 5.44415,-5.35292 c 5.48388,-3.40634 11.81743,-5.25863 18.30324,-5.35292 h 128.12265 z m -5.44415,172.71737 -29.27865,28.78802 c -3.62998,3.56915 -7.2879,5.35292 -10.97541,5.35292 -5.44415,0 -10.9754,-1.79858 -16.474,-7.19433 l -27.43852,-26.98943 h -36.63913 c -14.64477,0 -27.44941,-8.99291 -31.10788,-23.39227 h -5.44415 c -18.23634,0 -31.10788,-12.58982 -31.10788,-30.58659 V 168.50989 c -0.15291,-16.95439 13.86453,-30.73694 31.10788,-30.5866 h 150.0408 c 14.63386,0 25.60929,8.99291 29.27864,23.38157 h 14.57944 c 16.46311,0 32.93711,12.60054 32.93711,30.58659 v 111.59772 c 0,17.98581 -14.64476,30.58659 -32.93711,30.58659 z"
id="path2688"
sodipodi:nodetypes="sssssscscsssssssscscssccccccsccsccccccscccccsscccccssccscssccscssscc" />
<rect
class="cls-5"
x="126.32"
y="150.11"
width="236.95"
height="215.6"
id="rect2690" />
</svg>

After

Width:  |  Height:  |  Size: 5.6 KiB

+110
View File
@@ -0,0 +1,110 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="图层_1"
data-name="图层 1"
viewBox="0 0 484.83 484.83"
version="1.1"
sodipodi:docname="medal-7.svg"
inkscape:version="1.0.2 (e86c8708, 2021-01-15)">
<metadata
id="metadata3286">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1745"
inkscape:window-height="873"
id="namedview3284"
showgrid="false"
inkscape:zoom="0.87453337"
inkscape:cx="-5.4836925"
inkscape:cy="161.35911"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="0"
inkscape:current-layer="图层_1" />
<defs
id="defs3265">
<style
id="style3258">.cls-1,.cls-3,.cls-6{fill:none;}.cls-1,.cls-3{stroke-miterlimit:10;}.cls-1{stroke-width:43px;stroke:url(#未命名的渐变_26);}.cls-2{fill:#1b2f4e;}.cls-3{stroke:#fff;stroke-linecap:round;stroke-width:4.2px;stroke-dasharray:12.73 12.73;}.cls-4{fill:#63bce3;}.cls-5{fill:#fff;}</style>
<linearGradient
id="未命名的渐变_26"
x1="40.7"
y1="242.41"
x2="444.13"
y2="242.41"
gradientUnits="userSpaceOnUse">
<stop
offset="0"
stop-color="#5eb8e9"
id="stop3260" />
<stop
offset="1"
stop-color="#3a80c3"
id="stop3262" />
</linearGradient>
</defs>
<path
class="cls-1"
d="M 441.3722,341.50934 V 146.50321 c 0.004,-8.93976 -4.86581,-17.20038 -12.76969,-21.66256 L 256.2173,27.278397 c -7.89972,-4.464976 -17.62866,-4.464976 -25.52838,0 L 58.303706,124.80836 c -7.876952,4.49307 -12.710893,12.7625 -12.681842,21.69485 v 195.00613 c 8.63e-4,8.93576 4.864034,17.19277 12.758701,21.66257 l 172.385215,97.52997 c 7.89972,4.46497 17.62866,4.46497 25.52838,0 l 172.38521,-97.52997 c 7.87098,-4.48456 12.70783,-12.73952 12.69283,-21.66257 z"
id="path3267"
sodipodi:nodetypes="ccccccccccccc" />
<path
class="cls-2"
d="M 441.3722,344.49023 V 149.44106 c 7.9e-4,-8.94182 -4.86739,-17.20434 -12.76969,-21.67333 L 256.2173,30.248525 c -7.89972,-4.464976 -17.62866,-4.464976 -25.52838,0 L 58.303706,127.76773 c -7.866834,4.49244 -12.698528,12.74982 -12.681842,21.67333 v 195.04917 c 8.63e-4,8.93576 4.864034,17.19277 12.758701,21.66257 L 230.76578,463.672 c 7.89668,4.47608 17.6317,4.47608 25.52838,0 l 172.30835,-97.5192 c 7.90056,-4.46544 12.76882,-12.724 12.76969,-21.66257 z"
id="path3269"
sodipodi:nodetypes="ccccccccccccc" />
<path
class="cls-3"
d="M 413.61488,326.58337 V 161.47223 c -0.0215,-7.54841 -4.13657,-14.51625 -10.80427,-18.29427 L 256.90904,60.573963 c -6.68449,-3.787584 -14.92405,-3.787584 -21.60854,0 L 89.409904,143.12415 c -6.667698,3.77802 -10.782781,10.74587 -10.804271,18.29427 v 165.16495 c 0.01474,7.5502 4.131629,14.5211 10.804271,18.29427 l 145.945496,82.604 c 6.68752,3.77649 14.92102,3.77649 21.60854,0 l 145.90157,-82.56095 c 6.66509,-3.79453 10.76165,-10.78283 10.74937,-18.33732 z"
id="path3271"
sodipodi:nodetypes="ccccccccccccc" />
<path
class="cls-4"
d="m 292.91229,166.87442 h -93.65897 c -9.13279,0.0237 -16.52868,7.27712 -16.54678,16.22809 v 127.56485 c 0.0181,8.95097 7.41399,16.20441 16.54678,16.22809 h 93.65897 c 9.14492,-0.0296 16.54573,-7.2975 16.55777,-16.26037 V 183.10251 c -0.0241,-8.95271 -7.42318,-16.20444 -16.55777,-16.22809 z m 0.23058,83.1313 h -94.02131 c -3.5993,0.14695 -6.59912,-2.67321 -6.59912,-6.20391 0,-3.53069 2.99982,-6.35086 6.59912,-6.20391 h 93.98837 c 3.39346,0.13855 6.07174,2.87514 6.07174,6.20391 0,3.32877 -2.67828,6.06536 -6.07174,6.20391 z m 0,-37.97674 h -94.02131 c -3.4929,0 -6.32445,-2.77518 -6.32445,-6.19853 0,-3.42336 2.83155,-6.19853 6.32445,-6.19853 h 93.98837 c 3.49298,0 6.32445,2.77517 6.32445,6.19853 0,3.42335 -2.83155,6.19853 -6.32445,6.19853 z"
id="path3273"
sodipodi:nodetypes="cccccccccccsccscccssssscc" />
<path
class="cls-5"
d="m 341.57543,298.94827 c -3.49396,-0.0118 -6.32338,-2.7849 -6.33543,-6.20929 V 165.40012 c 0,-10.69689 -9.6404,-19.37041 -21.48776,-19.37041 h -7.91654 c -8.09566,-0.33053 -8.09566,-12.07728 0,-12.40781 h 7.91654 c 18.88755,0 34.14764,14.25853 34.14764,31.76745 v 127.34963 c -0.012,3.42021 -2.83478,6.19159 -6.32445,6.20929 z M 235.6848,360.13721 h -57.2275 c -18.79927,0 -34.14765,-14.25864 -34.14765,-31.76746 V 165.55077 c 0,-17.50869 15.23587,-31.76745 34.14765,-31.76745 h 9.34393 c 3.39346,0.13855 6.07174,2.87514 6.07174,6.20391 0,3.32877 -2.67828,6.06536 -6.07174,6.2039 h -9.34393 c -11.84737,0 -21.48776,8.66282 -21.48776,19.37041 v 162.81898 c 0,10.70752 9.63955,19.3704 21.48776,19.3704 h 57.2275 c 3.39345,0.13855 6.07173,2.87514 6.07173,6.20391 0,3.32877 -2.67828,6.06536 -6.07173,6.2039 z"
id="path3275"
sodipodi:nodetypes="ccssccsscccsssscscsssscscc" />
<path
class="cls-5"
d="m 280.06575,360.13721 c -1.88824,0.0388 -3.7622,-0.32856 -5.48997,-1.07613 -5.93358,-2.25932 -9.83248,-7.86927 -9.8051,-14.10811 v -46.65038 c 0.042,-10.58332 8.77899,-19.15591 19.57725,-19.20898 h 49.6184 c 12.31603,0.0406 18.5443,14.55762 9.95881,23.21219 l -52.7916,53.19328 c -2.91018,2.93263 -6.89399,4.60599 -11.06779,4.64889 z m 4.28218,-68.64655 c -3.82119,0.0408 -6.90873,3.06681 -6.9503,6.81193 v 46.62886 c 0.0324,1.12953 0.70023,2.14863 1.73483,2.64729 0.98088,0.4572 2.15291,0.20979 2.85478,-0.60264 l 52.94532,-53.29012 c 0.31775,-0.39462 0.43127,-0.91115 0.30744,-1.39898 -0.13249,-0.48342 -0.58757,-0.81347 -1.09799,-0.79634 z M 220.785,152.38966 c -3.47254,-0.0352 -6.2737,-2.79499 -6.29151,-6.19853 v -12.21411 c -0.14994,-3.52764 2.72752,-6.46772 6.32994,-6.46772 3.60242,0 6.47988,2.94008 6.32994,6.46772 v 12.2464 c -0.0181,3.41494 -2.85106,6.16624 -6.33543,6.16624 z m 51.25441,0 c -3.4897,-0.0118 -6.31737,-2.77834 -6.33543,-6.19853 v -12.21411 c -0.14994,-3.52764 2.72752,-6.46772 6.32994,-6.46772 3.60242,0 6.47988,2.94008 6.32994,6.46772 v 12.2464 c -0.0476,3.39856 -2.85665,6.13731 -6.32445,6.16624 z"
id="path3277"
sodipodi:nodetypes="ccccccccccccccccccccccsccsccccsccc" />
<rect
class="cls-6"
x="152.08"
y="134.2"
width="185.43"
height="216.43"
id="rect3279" />
<path
class="cls-5"
d="m 327.48816,291.49066 h -14.39472 c -3.4929,0 -6.32445,-2.77517 -6.32445,-6.19852 0,-3.42336 2.82993,-6.19853 6.32445,-6.19853 h 14.39472 c 4.283,-0.006 7.75184,-3.40704 7.75184,-7.60827 v -13.61308 c 0.33725,-7.93447 12.32264,-7.93447 12.65988,0 v 13.80679 c -0.10246,10.97537 -9.21292,19.81798 -20.41172,19.81161 z"
id="path3281"
sodipodi:nodetypes="cssscscccc" />
</svg>

After

Width:  |  Height:  |  Size: 7.2 KiB

+99
View File
@@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="图层_1"
data-name="图层 1"
viewBox="0 0 484.83 484.83"
version="1.1"
sodipodi:docname="medal-8.svg"
inkscape:version="1.0.2 (e86c8708, 2021-01-15)">
<metadata
id="metadata3873">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1229"
inkscape:window-height="784"
id="namedview3871"
showgrid="false"
inkscape:zoom="0.61838847"
inkscape:cx="17.821422"
inkscape:cy="132.44218"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="0"
inkscape:current-layer="图层_1" />
<defs
id="defs3858">
<style
id="style3849">.cls-1,.cls-3,.cls-4{fill:none;}.cls-1,.cls-3{stroke-miterlimit:10;}.cls-1{stroke-width:43px;stroke:url(#未命名的渐变_4);}.cls-2{fill:#1b2f4e;}.cls-3{stroke:#fff;stroke-linecap:round;stroke-width:4.2px;stroke-dasharray:12.73 12.73;}.cls-5{fill:#fff;}</style>
<linearGradient
id="未命名的渐变_4"
x1="40.7"
y1="242.41"
x2="444.13"
y2="242.41"
gradientUnits="userSpaceOnUse">
<stop
offset="0"
stop-color="#6ba8dc"
id="stop3851" />
<stop
offset="0.48"
stop-color="#6599d1"
id="stop3853" />
<stop
offset="1"
stop-color="#83b9e5"
id="stop3855" />
</linearGradient>
</defs>
<path
class="cls-1"
d="M 437.18396,339.54921 V 145.24296 c 0.004,-8.90768 -4.78943,-17.13865 -12.56923,-21.58482 L 254.93559,26.446042 c -7.77571,-4.448951 -17.35193,-4.448951 -25.12764,0 L 60.128807,123.62597 c -7.753301,4.47694 -12.51136,12.71669 -12.482765,21.61699 v 194.30625 c 8.5e-4,8.90368 4.78768,17.13106 12.558418,21.58482 l 169.67914,97.17993 c 7.77571,4.44895 17.35193,4.44895 25.12764,0 l 169.67915,-97.17993 c 7.74741,-4.46846 12.50834,-12.6938 12.49357,-21.58482 z"
id="path3860"
sodipodi:nodetypes="ccccccccccccc" />
<path
class="cls-2"
d="M 437.18396,339.54921 V 145.24296 c 0.004,-8.90768 -4.78943,-17.13865 -12.56923,-21.58482 L 254.93559,26.446042 c -7.77571,-4.448951 -17.35193,-4.448951 -25.12764,0 L 60.128807,123.62597 c -7.753301,4.47694 -12.51136,12.71669 -12.482765,21.61699 v 194.30625 c 8.5e-4,8.90368 4.78768,17.13106 12.558418,21.58482 l 169.67914,97.17993 c 7.77571,4.44895 17.35193,4.44895 25.12764,0 l 169.67915,-97.17993 c 7.74741,-4.46846 12.50834,-12.6938 12.49357,-21.58482 z"
id="path3862"
sodipodi:nodetypes="ccccccccccccc" />
<path
class="cls-3"
d="M 409.86237,324.67681 V 160.15825 c -0.0211,-7.52131 -4.07163,-14.46415 -10.63466,-18.22861 L 255.61647,59.62211 c -6.57956,-3.773991 -14.68978,-3.773991 -21.26933,0 L 90.746705,141.87603 c -6.563029,3.76446 -10.613515,10.70729 -10.634667,18.22861 v 164.57217 c 0.0145,7.52309 4.066772,14.46898 10.634667,18.22861 l 143.654465,82.30753 c 6.58255,3.76294 14.6868,3.76294 21.26934,0 l 143.61123,-82.26464 c 6.56047,-3.78091 10.59272,-10.74413 10.58063,-18.2715 z"
id="path3864"
sodipodi:nodetypes="ccccccccccccc" />
<rect
class="cls-4"
x="20.53"
y="67.69"
width="305.96"
height="306.36"
id="rect3866" />
<path
class="cls-5"
d="m 339.10509,313.87904 c 0,7.90585 -6.45969,14.31482 -14.42813,14.31482 h -96.76034 c -7.96857,0 -14.42813,-6.40896 -14.42813,-14.31482 0,-7.90586 6.45963,-14.31482 14.42813,-14.31482 h 96.78196 c 7.95858,0.0236 14.40439,6.41874 14.42813,14.31482 z m 0,-40.48896 c -0.006,7.90167 -6.46391,14.3041 -14.42813,14.3041 H 269.7636 c -8.12006,0.22055 -14.82297,-6.25016 -14.82297,-14.30946 0,-8.0593 6.70291,-14.53001 14.82297,-14.30946 h 54.91336 c 7.95858,0.0236 14.40439,6.41874 14.42813,14.31482 z m -57.59364,-71.91722 -94.18813,105.08257 -39.82597,8.1707 c -2.09824,0.49337 -4.01462,-1.31715 -3.62054,-3.42054 l 5.80368,-39.82415 93.26949,-104.0103 c 2.16151,-2.38044 5.63075,-2.79863 7.78146,-0.87926 l 30.78001,27.12846 c 2.16152,1.85503 2.16152,5.36135 0,7.70963 z m 27.15947,-30.27022 -14.42813,16.08407 c -1.16536,1.28804 -3.16123,1.39832 -4.46353,0.24662 L 255.89747,157.6813 c -1.29824,-1.1562 -1.40939,-3.1364 -0.24858,-4.42848 l 14.46056,-16.08406 c 5.23231,-5.83805 14.24619,-6.35677 20.12373,-1.15806 l 17.29214,15.25842 c 5.83532,5.22682 6.31828,14.14887 1.08076,19.96569 z"
id="path3868"
sodipodi:nodetypes="cssssccccscsccccccccccccccccccccccc" />
</svg>

After

Width:  |  Height:  |  Size: 5.1 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 30 KiB

+118
View File
@@ -0,0 +1,118 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
viewBox="-1300 -1300 2172.0716 1152.977"
version="1.1"
id="svg835"
sodipodi:docname="medal-background.svg"
width="2172.0715"
height="1152.9769"
inkscape:version="1.0.2 (e86c8708, 2021-01-15)">
<metadata
id="metadata841">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs839">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 4152.8066 : 1"
inkscape:vp_y="0 : 1000.0001 : 0"
inkscape:vp_z="3600.0001 : 4152.8066 : 1"
inkscape:persp3d-origin="1800 : 3552.8066 : 1"
id="perspective919" />
</defs>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1643"
inkscape:window-height="903"
id="namedview837"
showgrid="false"
inkscape:zoom="0.20111111"
inkscape:cx="1083.136"
inkscape:cy="200.67364"
inkscape:window-x="83"
inkscape:window-y="25"
inkscape:window-maximized="0"
inkscape:current-layer="svg835"
inkscape:document-rotation="0" />
<ellipse
style="fill:none;fill-opacity:0.0209205;stroke:#757575;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:40, 40;stroke-dashoffset:0;stroke-opacity:1"
id="path847"
ry="434.62665"
rx="1081.0355"
cy="-723.38593"
cx="-213.96408" />
<ellipse
style="fill:none;fill-opacity:0.0209205;stroke:#757575;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path847-3"
cx="-213.76193"
cy="-743.93646"
rx="469.3873"
ry="222.237" />
<circle
style="fill:#757575;fill-opacity:1;stroke:none;stroke-width:12;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path875"
cx="78.433647"
cy="-572.49097"
r="30" />
<circle
style="fill:none;fill-opacity:0.0209205;stroke:#757575;stroke-width:9.91856;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path877-1-8"
cx="-781.72888"
cy="-353.56128"
r="30.582167" />
<path
sodipodi:type="star"
style="fill:#757575;fill-opacity:0.16318;stroke:#757575;stroke-width:11.6319;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.945607"
id="path935"
sodipodi:sides="4"
sodipodi:cx="-564.08838"
sodipodi:cy="-708.28729"
sodipodi:r1="149.1713"
sodipodi:r2="50.122749"
sodipodi:arg1="1.5707963"
sodipodi:arg2="2.3753221"
inkscape:flatsided="false"
inkscape:rounded="0"
inkscape:randomized="0"
d="m -564.08837,-559.116 -36.11354,-114.41352 -113.05776,-34.75777 114.41352,-36.11354 34.75777,-113.05776 36.11354,114.41353 113.05776,34.75776 -114.41353,36.11354 z"
transform="matrix(0.27797773,0,0,0.38000076,872.67424,58.011001)" />
<path
sodipodi:type="star"
style="fill:#757575;fill-opacity:0.16318;stroke:#757575;stroke-width:11.6319;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path935-9"
sodipodi:sides="4"
sodipodi:cx="-564.08838"
sodipodi:cy="-708.28729"
sodipodi:r1="149.1713"
sodipodi:r2="50.122749"
sodipodi:arg1="1.5707963"
sodipodi:arg2="2.3753221"
inkscape:flatsided="false"
inkscape:rounded="0"
inkscape:randomized="0"
transform="matrix(0.42532925,0,0,0.58143303,-876.52701,-790.07611)"
d="m -564.08837,-559.116 -36.11354,-114.41352 -113.05776,-34.75777 114.41352,-36.11354 34.75777,-113.05776 36.11354,114.41353 113.05776,34.75776 -114.41353,36.11354 z" />
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 19 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 17 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 19 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg viewBox="-1300 -1300 3600 3600" version="1.1" style="background: rgba(160,160,160,.1);" xmlns="http://www.w3.org/2000/svg">
<path d="m 741.80075,684.39806 v -38.42905 h 36.39793 v 55.10168 c 0,9.23827 -8.16124,16.67263 -18.16918,16.67263 h -497.359 c -10.06752,0 -18.16918,-7.48902 -18.16918,-16.67263 V 322.62931 c 0,-9.23827 8.16124,-16.67263 18.16918,-16.67263 h 497.359 c 10.06752,0 18.16918,7.48902 18.16918,16.67263 V 556.04613 H 741.80075 V 339.30194 H 280.83968 V 684.39806 Z M 402.18598,506.30157 c -33.47894,0 -60.64337,-24.92695 -60.64337,-55.64832 0,-30.72137 27.16443,-55.64832 60.64337,-55.64832 33.47895,0 60.64337,24.92695 60.64337,55.64832 0,30.72137 -27.16442,55.64832 -60.64337,55.64832 z m 0,-33.39993 c 13.4035,0 24.24544,-9.94891 24.24544,-22.24839 0,-12.29948 -10.84194,-22.24839 -24.24544,-22.24839 -13.40349,0 -24.24543,9.94891 -24.24543,22.24839 0,12.29948 10.84194,22.24839 24.24543,22.24839 z m -74.22557,120.58958 26.09214,-23.23235 c 43.72517,41.38092 88.70134,44.49679 130.52024,11.58885 5.12312,-4.04517 6.55283,-5.19312 19.47975,-15.57935 40.56791,-32.74395 61.35822,-47.12068 88.04607,-57.61623 37.23193,-14.65005 73.62986,-12.9008 109.96822,8.25431 l -19.53931,28.20681 c -46.28674,-26.94953 -80.24226,-13.55676 -154.46783,46.30072 -13.04607,10.55022 -14.47577,11.69817 -19.7776,15.85266 -56.77125,44.60612 -123.55043,39.95965 -180.32168,-13.77542 z" fill="#757575" opacity="0.2" />
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="300" height="300"></svg>

After

Width:  |  Height:  |  Size: 114 B

+3
View File
@@ -0,0 +1,3 @@
<svg viewBox="-500 -500 2024 2024" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background: rgba(160,160,160,.1)">
<path d="M898.2 827.5v-70.3h61.1V858c0 16.9-13.7 30.5-30.5 30.5H93.9c-16.9 0-30.5-13.7-30.5-30.5V165.7c0-16.9 13.7-30.5 30.5-30.5h834.9c16.9 0 30.5 13.7 30.5 30.5v427h-61.1V196.2H124.4v631.3h773.8zM328.1 501.7c-56.2 0-101.8-45.6-101.8-101.8s45.6-101.8 101.8-101.8 101.8 45.6 101.8 101.8-45.6 101.8-101.8 101.8z m0-61.1c22.5 0 40.7-18.2 40.7-40.7s-18.2-40.7-40.7-40.7-40.7 18.2-40.7 40.7 18.2 40.7 40.7 40.7zM203.5 661.2l43.8-42.5c73.4 75.7 148.9 81.4 219.1 21.2 8.6-7.4 11-9.5 32.7-28.5 68.1-59.9 103-86.2 147.8-105.4 62.5-26.8 123.6-23.6 184.6 15.1l-32.8 51.6C721 523.4 664 547.9 539.4 657.4c-21.9 19.3-24.3 21.4-33.2 29-95.3 81.6-207.4 73.1-302.7-25.2z" fill="#757575" opacity=".2"></path>
</svg>

After

Width:  |  Height:  |  Size: 867 B

+3
View File
@@ -0,0 +1,3 @@
<svg viewBox="-1300 -1300 3600 3600" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="background: rgba(160,160,160,.1);">
<path d="M898.2 827.5v-70.3h61.1V858c0 16.9-13.7 30.5-30.5 30.5H93.9c-16.9 0-30.5-13.7-30.5-30.5V165.7c0-16.9 13.7-30.5 30.5-30.5h834.9c16.9 0 30.5 13.7 30.5 30.5v427h-61.1V196.2H124.4v631.3h773.8zM328.1 501.7c-56.2 0-101.8-45.6-101.8-101.8s45.6-101.8 101.8-101.8 101.8 45.6 101.8 101.8-45.6 101.8-101.8 101.8z m0-61.1c22.5 0 40.7-18.2 40.7-40.7s-18.2-40.7-40.7-40.7-40.7 18.2-40.7 40.7 18.2 40.7 40.7 40.7zM203.5 661.2l43.8-42.5c73.4 75.7 148.9 81.4 219.1 21.2 8.6-7.4 11-9.5 32.7-28.5 68.1-59.9 103-86.2 147.8-105.4 62.5-26.8 123.6-23.6 184.6 15.1l-32.8 51.6C721 523.4 664 547.9 539.4 657.4c-21.9 19.3-24.3 21.4-33.2 29-95.3 81.6-207.4 73.1-302.7-25.2z" fill="#757575" opacity=".2"></path>
</svg>

After

Width:  |  Height:  |  Size: 870 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

+116
View File
@@ -1126,6 +1126,122 @@ if (!function_exists('aksrt_render_comment_pagination')) {
}
}
// ============================================================
// 模板标签:文章列表分页渲染
// ============================================================
if (!function_exists('aksrt_render_pagination')) {
/**
* 渲染文章列表分页(根据后台配置选择模式)。
*
* Zibll 对齐:numbered = 数字分页按钮;load_more/infinite = AJAX 追加。
*
* @param WP_Query|null $query 可选,传入自定义查询对象;默认用主查询。
* @param array<string,mixed> $args {
* @type string $query_context search|archive|home|tag|category... 供 AJAX 路由解析。
* @type array<string,mixed> $render_config 透传给 AJAX 渲染器(aksrt_resolve_post_list_render_config)。
* }
*/
function aksrt_render_pagination(?WP_Query $query = null, array $args = []): void {
$pagination_mode = class_exists('\AKSRT\Settings\Settings')
? \AKSRT\Settings\Settings::get_post_pagination_mode()
: 'numbered';
if ($pagination_mode === 'numbered') {
global $wp_query;
$q = $query ?? $wp_query;
if (!($q instanceof WP_Query) || $q->max_num_pages <= 1) {
return;
}
$current_page = max(1, (int) $q->get('paged'), (int) get_query_var('paged'), (int) get_query_var('page'));
$max_pages = (int) $q->max_num_pages;
$links = paginate_links([
'total' => $max_pages,
'current' => $current_page,
'mid_size' => 2,
'prev_text' => '<span aria-hidden="true">‹</span>',
'next_text' => '<span aria-hidden="true">›</span>',
'type' => 'plain',
]);
if (empty($links)) {
return;
}
echo '<div class="pagenav ajax-pag aksrt-pagination content-stream__footer-nav">';
echo wp_kses_post($links);
if ($max_pages > 8) {
echo '<span class="pag-jump">';
echo '<input type="number" class="jump-input" min="1" max="' . esc_attr((string) $max_pages) . '" value="' . esc_attr((string) $current_page) . '" data-max-pages="' . esc_attr((string) $max_pages) . '" />';
echo '<button type="button" class="ajax-next order-ajax-next" data-jump-archive="1">' . esc_html__('跳转', 'aksrt') . '</button>';
echo '</span>';
}
echo '</div>';
return;
}
// load_more / infinite 模式
global $wp_query;
$q = $query ?? $wp_query;
if (!($q instanceof WP_Query)) {
return;
}
$max_pages = (int) $q->max_num_pages;
$current_page = max(1, (int) $q->get('paged'), (int) get_query_var('paged'), (int) get_query_var('page'));
if ($current_page >= $max_pages) {
return;
}
$query_context = '';
if (!empty($args['query_context']) && is_string($args['query_context'])) {
$query_context = sanitize_key($args['query_context']);
} elseif (is_search()) {
$query_context = 'search';
} elseif (is_archive()) {
$query_context = 'archive';
}
$query_vars = $q->query_vars;
if (!is_array($query_vars)) {
$query_vars = [];
} else {
unset($query_vars['paged'], $query_vars['page']);
}
$render_config = !empty($args['render_config']) && is_array($args['render_config'])
? $args['render_config']
: [];
$trigger_text = __('加载更多', 'aksrt');
if (class_exists('\AKSRT\Settings\Settings')) {
$list_settings = \AKSRT\Settings\Settings::get_post_list_style_settings();
if (!empty($list_settings['ajax_trigger_text'])) {
$trigger_text = (string) $list_settings['ajax_trigger_text'];
}
}
?>
<div class="text-center theme-pagination ajax-pag ajax-pagination aksrt-pagination-ajax content-stream__footer-nav"
data-mode="<?php echo esc_attr($pagination_mode); ?>"
data-next-page="<?php echo esc_attr((string) ($current_page + 1)); ?>"
data-max-pages="<?php echo esc_attr((string) $max_pages); ?>"
data-query-context="<?php echo esc_attr($query_context); ?>"
data-query-vars="<?php echo esc_attr(wp_json_encode($query_vars)); ?>"
data-render-config="<?php echo esc_attr(wp_json_encode($render_config)); ?>"
data-lazyload-action="ias">
<div class="next-page ajax-next aksrt-load-more-btn" lazyload-action="ias" role="button" tabindex="0">
<?php echo esc_html($trigger_text); ?>
</div>
<div class="aksrt-loading-indicator" style="display:none;" aria-hidden="true">
<i class="fas fa-spinner fa-spin"></i>
<span><?php esc_html_e('加载中…', 'aksrt'); ?></span>
</div>
</div>
<?php
}
}
/**
* Auto-fill missing alt attributes on images in post content.
*
+12
View File
@@ -51,6 +51,18 @@ if (!function_exists('aksrt_user_card_query_posts')) :
$query_args['orderby'] = 'meta_value_num';
$query_args['order'] = 'DESC';
break;
case 'like':
case 'likes':
$query_args['meta_key'] = '_aksrt_like_count';
$query_args['orderby'] = 'meta_value_num';
$query_args['order'] = 'DESC';
break;
case 'favorite':
case 'favorite_count':
$query_args['meta_key'] = '_aksrt_favorite_count';
$query_args['orderby'] = 'meta_value_num';
$query_args['order'] = 'DESC';
break;
case 'comment_count':
case 'comments':
$query_args['orderby'] = 'comment_count';
+638
View File
@@ -0,0 +1,638 @@
<?php
/**
* Shared widget common-form layer — Zibll CSF_Widget::params() parity.
*
* Extracted from widgets-register.php to keep each file under the 1000-line
* hard limit. Provides:
* - common field defaults / normalization (subtitle, title_style, highlight,
* 3 title links, sidebar_affix, show_type, show_id_type, animation, layout)
* - shared admin form renderer + update processor
* - wrapper class/attr builders (affix, animation, show-type, layout bg)
* - Zibll-aligned title block renderer (simple / big styles)
* - display-by-id gate consumed by widget_display_callback
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
// ============================================================
// Shared Widget Form Helper — 公共表单字段与显示规则
// ============================================================
if (!function_exists('aksrt_widget_common_defaults')) {
/**
* Default values for shared widget instance fields.
*
* Zibll CSF_Widget::params() common-field parity:
* subtitle, title_style, title_highlight, title_highlight_color,
* title_link (×3), title_link_style colors, sidebar_affix,
* show_type (all/only_pc/only_sm), show_id_type (show/hide), show_ids,
* animation_in, animation_repeat, layout_pin_pc/m, layout_bg (white/dark).
*
* @return array<string, mixed>
*/
function aksrt_widget_common_defaults(): array {
return [
'subtitle' => '',
'title_style' => '',
'title_highlight' => '',
'title_highlight_color'=> 'focus-color',
'title_link' => ['url' => '', 'text' => '', 'target' => ''],
'title_link2' => ['url' => '', 'text' => '', 'target' => ''],
'title_link3' => ['url' => '', 'text' => '', 'target' => ''],
'title_link_style' => ['color_1' => 'jb-blue', 'color_2' => 'jb-yellow', 'color_3' => 'jb-green'],
'sidebar_affix' => false,
'show_type' => 'all',
'show_id_type' => '',
'show_ids' => '',
'animation_in' => '',
'animation_repeat' => false,
'layout_pin_pc' => ['top' => '', 'bottom' => ''],
'layout_pin_m' => ['top' => '', 'bottom' => ''],
'layout_bg' => ['img_white' => [], 'img_dark' => []],
];
}
}
if (!function_exists('aksrt_widget_normalize_link')) {
/**
* Normalize a title_linkN array {url,text,target}.
*
* @param mixed $raw
* @return array{url:string,text:string,target:string}
*/
function aksrt_widget_normalize_link($raw): array {
$raw = is_array($raw) ? $raw : [];
return [
'url' => esc_url_raw(trim((string) ($raw['url'] ?? ''))),
'text' => sanitize_text_field((string) ($raw['text'] ?? '')),
'target' => ($raw['target'] ?? '') === '_blank' ? '_blank' : '',
];
}
}
if (!function_exists('aksrt_widget_normalize_common')) {
/**
* Normalize common widget instance fields, filling defaults for legacy instances.
*
* Accepts both the new structured shape (title_link as array) and legacy
* flat shapes (title_link as URL string, in_affix bool) emitted by older
* widget versions, normalizing them onto the canonical shared schema.
*
* @param array<string, mixed> $instance
* @return array<string, mixed>
*/
function aksrt_widget_normalize_common(array $instance): array {
$defaults = aksrt_widget_common_defaults();
$result = array_merge($defaults, $instance);
$result['subtitle'] = sanitize_text_field($result['subtitle'] ?? '');
$result['title_style'] = in_array($result['title_style'] ?? '', ['', 'big'], true) ? $result['title_style'] : '';
$result['title_highlight'] = sanitize_text_field($result['title_highlight'] ?? '');
$result['title_highlight_color'] = sanitize_html_class((string) ($result['title_highlight_color'] ?? 'focus-color'));
if ($result['title_highlight_color'] === '') {
$result['title_highlight_color'] = 'focus-color';
}
// Legacy flat title_link (URL string) → structured first link.
if (is_string($result['title_link'] ?? null)) {
$result['title_link'] = ['url' => esc_url_raw(trim($result['title_link'])), 'text' => '', 'target' => ''];
}
$result['title_link'] = aksrt_widget_normalize_link($result['title_link'] ?? []);
$result['title_link2'] = aksrt_widget_normalize_link($result['title_link2'] ?? []);
$result['title_link3'] = aksrt_widget_normalize_link($result['title_link3'] ?? []);
$tl_style = is_array($result['title_link_style'] ?? null) ? $result['title_link_style'] : [];
$result['title_link_style'] = [
'color_1' => sanitize_html_class((string) ($tl_style['color_1'] ?? 'jb-blue')) ?: 'jb-blue',
'color_2' => sanitize_html_class((string) ($tl_style['color_2'] ?? 'jb-yellow')) ?: 'jb-yellow',
'color_3' => sanitize_html_class((string) ($tl_style['color_3'] ?? 'jb-green')) ?: 'jb-green',
];
// show_type: accept both AKSRT legacy (pc/mobile) and Zibll (only_pc/only_sm) tokens.
$show_type = (string) ($result['show_type'] ?? 'all');
if ($show_type === 'only_pc' || $show_type === 'pc') {
$show_type = 'only_pc';
} elseif ($show_type === 'only_sm' || $show_type === 'mobile') {
$show_type = 'only_sm';
} else {
$show_type = 'all';
}
$result['show_type'] = $show_type;
// show_id_type: Zibll (show/hide) ↔ AKSRT legacy (include/exclude).
$id_type = (string) ($result['show_id_type'] ?? '');
if ($id_type === 'include') {
$id_type = 'show';
} elseif ($id_type === 'exclude') {
$id_type = 'hide';
} elseif (!in_array($id_type, ['', 'show', 'hide'], true)) {
$id_type = '';
}
$result['show_id_type'] = $id_type;
$result['show_ids'] = sanitize_text_field($result['show_ids'] ?? '');
$result['sidebar_affix'] = !empty($result['sidebar_affix']) || !empty($result['in_affix'] ?? false);
$result['animation_in'] = sanitize_key($result['animation_in'] ?? '');
$result['animation_repeat'] = !empty($result['animation_repeat']);
foreach (['layout_pin_pc', 'layout_pin_m'] as $pin_key) {
$pin = is_array($result[$pin_key] ?? null) ? $result[$pin_key] : [];
$result[$pin_key] = [
'top' => preg_match('/^-?\d+$/', (string) ($pin['top'] ?? '')) ? (string) (int) $pin['top'] : '',
'bottom' => preg_match('/^-?\d+$/', (string) ($pin['bottom'] ?? '')) ? (string) (int) $pin['bottom'] : '',
];
}
$bg = is_array($result['layout_bg'] ?? null) ? $result['layout_bg'] : [];
$result['layout_bg'] = [
'img_white' => aksrt_widget_normalize_bg($bg['img_white'] ?? []),
'img_dark' => aksrt_widget_normalize_bg($bg['img_dark'] ?? []),
];
return $result;
}
}
if (!function_exists('aksrt_widget_normalize_bg')) {
/**
* Normalize a layout_bg sub-block (background CSS props).
*
* @param mixed $raw
* @return array<string,string>
*/
function aksrt_widget_normalize_bg($raw): array {
$raw = is_array($raw) ? $raw : [];
$out = [];
foreach (['background-color', 'background-image', 'background-position', 'background-repeat', 'background-attachment', 'background-size', 'background-origin'] as $k) {
$v = trim((string) ($raw[$k] ?? ''));
if ($k === 'background-image' && $v !== '') {
$v = esc_url_raw($v);
} elseif ($k === 'background-color' && $v !== '') {
// Allow hex / rgb / rgba / named; strip tags defensively.
$v = sanitize_text_field($v);
} else {
$v = sanitize_text_field($v);
}
if ($v !== '') {
$out[$k] = $v;
}
}
return $out;
}
}
if (!function_exists('aksrt_widget_animation_options')) {
/**
* Zibll-aligned module entrance animation options.
*
* @return array<string,string>
*/
function aksrt_widget_animation_options(): array {
return [
'' => __('无动画', 'aksrt'),
'fade' => __('淡入', 'aksrt'),
'slideup' => __('从下往上滑出', 'aksrt'),
'slidedown' => __('从上往下滑出', 'aksrt'),
'slideright' => __('从左往右滑出', 'aksrt'),
'slideleft' => __('从右往左滑出', 'aksrt'),
'zoomin' => __('由小变大', 'aksrt'),
'zoomout' => __('由大变小', 'aksrt'),
];
}
}
if (!function_exists('aksrt_widget_highlight_color_options')) {
/**
* Highlight color palette (Zibll zib_palette parity, theme classes).
*
* @return array<string,string>
*/
function aksrt_widget_highlight_color_options(): array {
return [
'focus-color' => __('主题色', 'aksrt'),
'jb-blue' => __('渐变蓝', 'aksrt'),
'jb-green' => __('渐变绿', 'aksrt'),
'jb-yellow' => __('渐变黄', 'aksrt'),
'jb-orange' => __('渐变橙', 'aksrt'),
'jb-red' => __('渐变红', 'aksrt'),
'jb-purple' => __('渐变紫', 'aksrt'),
'jb-pink' => __('渐变粉', 'aksrt'),
'jb-cyan' => __('渐变青', 'aksrt'),
'c-blue' => __('蓝色', 'aksrt'),
'c-green' => __('绿色', 'aksrt'),
'c-red' => __('红色', 'aksrt'),
'c-yellow' => __('黄色', 'aksrt'),
'c-purple' => __('紫色', 'aksrt'),
];
}
}
if (!function_exists('aksrt_widget_render_link_field')) {
/**
* Render a single title-link sub-field (url/text/target) for the admin form.
*
* @param string $base Field base name, e.g. 'title_link'.
* @param array $link Normalized link {url,text,target}.
* @param string $label Field label.
*/
function aksrt_widget_render_link_field(string $base, array $link, string $label): void {
?>
<p>
<label><?php echo esc_html($label); ?>
<input class="widefat" type="text" name="<?php echo esc_attr($base); ?>[text]" value="<?php echo esc_attr($link['text']); ?>" placeholder="<?php esc_attr_e('按钮文字', 'aksrt'); ?>">
</label>
<input class="widefat" type="url" name="<?php echo esc_attr($base); ?>[url]" value="<?php echo esc_attr($link['url']); ?>" placeholder="<?php esc_attr_e('链接地址', 'aksrt'); ?>">
<label><input type="checkbox" name="<?php echo esc_attr($base); ?>[target]" value="_blank" <?php checked($link['target'] === '_blank'); ?>> <?php esc_html_e('新窗口打开', 'aksrt'); ?></label>
</p>
<?php
}
}
if (!function_exists('aksrt_widget_render_common_form')) {
/**
* Render shared widget admin form fields.
*
* Call this from your widget's form() method. Renders the full Zibll
* common-field set: title block (subtitle / style / highlight / 3 links),
* sidebar affix, display rules, entrance animation, layout padding + bg.
*
* @param array<string, mixed> $instance Current widget instance.
*/
function aksrt_widget_render_common_form(array $instance): void {
$instance = aksrt_widget_normalize_common($instance);
$pin_pc = $instance['layout_pin_pc'];
$pin_m = $instance['layout_pin_m'];
$bg_w = $instance['layout_bg']['img_white'];
$bg_d = $instance['layout_bg']['img_dark'];
?>
<hr style="margin:16px 0 12px;border-color:#e0e0e0;">
<p style="font-size:12px;color:#666;margin-bottom:8px;font-weight:600;"><?php esc_html_e('标题与链接', 'aksrt'); ?></p>
<p>
<label><?php esc_html_e('副标题', 'aksrt'); ?>
<input class="widefat" type="text" name="subtitle" value="<?php echo esc_attr($instance['subtitle']); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('标题样式', 'aksrt'); ?></label>
<label><input type="radio" name="title_style" value="" <?php checked($instance['title_style'], ''); ?>> <?php esc_html_e('简约', 'aksrt'); ?></label>
<label><input type="radio" name="title_style" value="big" <?php checked($instance['title_style'], 'big'); ?>> <?php esc_html_e('居中大标题', 'aksrt'); ?></label>
</p>
<p>
<label><?php esc_html_e('标题选字高亮(标题中要被高亮的部分)', 'aksrt'); ?>
<input class="widefat" type="text" name="title_highlight" value="<?php echo esc_attr($instance['title_highlight']); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('高亮颜色', 'aksrt'); ?>
<select class="widefat" name="title_highlight_color">
<?php foreach (aksrt_widget_highlight_color_options() as $val => $lbl) : ?>
<option value="<?php echo esc_attr($val); ?>" <?php selected($instance['title_highlight_color'], $val); ?>><?php echo esc_html($lbl); ?></option>
<?php endforeach; ?>
</select>
</label>
</p>
<?php aksrt_widget_render_link_field('title_link', $instance['title_link'], __('标题链接', 'aksrt')); ?>
<?php if ($instance['title_style'] !== 'big') : ?>
<?php aksrt_widget_render_link_field('title_link2', $instance['title_link2'], __('标题链接 2', 'aksrt')); ?>
<?php aksrt_widget_render_link_field('title_link3', $instance['title_link3'], __('标题链接 3', 'aksrt')); ?>
<?php endif; ?>
<?php if ($instance['title_style'] === 'big') : ?>
<p>
<label><?php esc_html_e('链接样式(颜色1/2/3)', 'aksrt'); ?></label>
<select name="title_link_style[color_1]">
<?php foreach (aksrt_widget_highlight_color_options() as $val => $lbl) : ?>
<option value="<?php echo esc_attr($val); ?>" <?php selected($instance['title_link_style']['color_1'], $val); ?>><?php echo esc_html($lbl); ?></option>
<?php endforeach; ?>
</select>
<select name="title_link_style[color_2]">
<?php foreach (aksrt_widget_highlight_color_options() as $val => $lbl) : ?>
<option value="<?php echo esc_attr($val); ?>" <?php selected($instance['title_link_style']['color_2'], $val); ?>><?php echo esc_html($lbl); ?></option>
<?php endforeach; ?>
</select>
<select name="title_link_style[color_3]">
<?php foreach (aksrt_widget_highlight_color_options() as $val => $lbl) : ?>
<option value="<?php echo esc_attr($val); ?>" <?php selected($instance['title_link_style']['color_3'], $val); ?>><?php echo esc_html($lbl); ?></option>
<?php endforeach; ?>
</select>
</p>
<?php endif; ?>
<p style="font-size:12px;color:#666;margin:10px 0 8px;font-weight:600;"><?php esc_html_e('显示规则', 'aksrt'); ?></p>
<p>
<label><?php esc_html_e('显示设备', 'aksrt'); ?></label>
<label><input type="radio" name="show_type" value="all" <?php checked($instance['show_type'], 'all'); ?>> <?php esc_html_e('全部', 'aksrt'); ?></label>
<label><input type="radio" name="show_type" value="only_pc" <?php checked($instance['show_type'], 'only_pc'); ?>> <?php esc_html_e('仅 PC', 'aksrt'); ?></label>
<label><input type="radio" name="show_type" value="only_sm" <?php checked($instance['show_type'], 'only_sm'); ?>> <?php esc_html_e('仅移动端', 'aksrt'); ?></label>
</p>
<p>
<label><?php esc_html_e('按 ID 显示或隐藏', 'aksrt'); ?></label>
<label><input type="radio" name="show_id_type" value="" <?php checked($instance['show_id_type'], ''); ?>> <?php esc_html_e('不限制', 'aksrt'); ?></label>
<label><input type="radio" name="show_id_type" value="show" <?php checked($instance['show_id_type'], 'show'); ?>> <?php esc_html_e('指定 ID 显示', 'aksrt'); ?></label>
<label><input type="radio" name="show_id_type" value="hide" <?php checked($instance['show_id_type'], 'hide'); ?>> <?php esc_html_e('指定 ID 隐藏', 'aksrt'); ?></label>
</p>
<p>
<label><?php esc_html_e('文章/页面 ID(逗号分隔)', 'aksrt'); ?>
<input class="widefat" type="text" name="show_ids" value="<?php echo esc_attr($instance['show_ids']); ?>" placeholder="1,2,3">
</label>
</p>
<p>
<label><input type="checkbox" name="sidebar_affix" value="1" <?php checked($instance['sidebar_affix']); ?>> <?php esc_html_e('侧栏吸顶(仅侧栏有效)', 'aksrt'); ?></label>
</p>
<p>
<label><?php esc_html_e('入场动画', 'aksrt'); ?>
<select class="widefat" name="animation_in">
<?php foreach (aksrt_widget_animation_options() as $val => $lbl) : ?>
<option value="<?php echo esc_attr($val); ?>" <?php selected($instance['animation_in'], $val); ?>><?php echo esc_html($lbl); ?></option>
<?php endforeach; ?>
</select>
</label>
</p>
<p>
<label><input type="checkbox" name="animation_repeat" value="1" <?php checked($instance['animation_repeat']); ?>> <?php esc_html_e('动画重复播放', 'aksrt'); ?></label>
</p>
<p style="font-size:12px;color:#666;margin:10px 0 8px;font-weight:600;"><?php esc_html_e('布局:间距与背景', 'aksrt'); ?></p>
<p>
<label><?php esc_html_e('PC 端额外上/下间距 (px)', 'aksrt'); ?>
<input class="tiny-text" type="number" name="layout_pin_pc[top]" value="<?php echo esc_attr($pin_pc['top']); ?>" placeholder="0">
<input class="tiny-text" type="number" name="layout_pin_pc[bottom]" value="<?php echo esc_attr($pin_pc['bottom']); ?>" placeholder="0">
</label>
</p>
<p>
<label><?php esc_html_e('移动端额外上/下间距 (px)', 'aksrt'); ?>
<input class="tiny-text" type="number" name="layout_pin_m[top]" value="<?php echo esc_attr($pin_m['top']); ?>" placeholder="0">
<input class="tiny-text" type="number" name="layout_pin_m[bottom]" value="<?php echo esc_attr($pin_m['bottom']); ?>" placeholder="0">
</label>
</p>
<p>
<label><?php esc_html_e('日间背景色', 'aksrt'); ?>
<input class="widefat" type="text" name="layout_bg[img_white][background-color]" value="<?php echo esc_attr($bg_w['background-color'] ?? ''); ?>">
</label>
<label><?php esc_html_e('日间背景图', 'aksrt'); ?>
<input class="widefat" type="url" name="layout_bg[img_white][background-image]" value="<?php echo esc_attr($bg_w['background-image'] ?? ''); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('夜间背景色', 'aksrt'); ?>
<input class="widefat" type="text" name="layout_bg[img_dark][background-color]" value="<?php echo esc_attr($bg_d['background-color'] ?? ''); ?>">
</label>
<label><?php esc_html_e('夜间背景图', 'aksrt'); ?>
<input class="widefat" type="url" name="layout_bg[img_dark][background-image]" value="<?php echo esc_attr($bg_d['background-image'] ?? ''); ?>">
</label>
</p>
<?php
}
}
if (!function_exists('aksrt_widget_update_common')) {
/**
* Process shared widget form fields in update().
*
* @param array<string, mixed> $new_instance
* @param array<string, mixed> $old_instance
* @return array<string, mixed>
*/
function aksrt_widget_update_common(array $new_instance, array $old_instance): array {
$merged = array_merge($old_instance, $new_instance);
return aksrt_widget_normalize_common($merged);
}
}
if (!function_exists('aksrt_widget_render_common_before')) {
/**
* Build common wrapper classes/attrs for a widget instance.
*
* Zibll CSF_Widget::wrap_attributes() parity: show-type class, sidebar
* affix data-affix, entrance animation class, layout padding/bg CSS vars.
* Call this at the start of widget() before rendering content.
*
* @param array $args Sidebar args.
* @param array $instance Widget instance.
* @return array<int,string> CSS class names (Zibll show_class style).
*/
function aksrt_widget_get_common_wrapper_classes(array $args, array $instance): array {
$instance = aksrt_widget_normalize_common($instance);
$classes = [];
// Show type: PC or mobile (Zibll hidden-xs / visible-xs-block).
if ($instance['show_type'] === 'only_pc') {
$classes[] = 'hidden-xs';
} elseif ($instance['show_type'] === 'only_sm') {
$classes[] = 'visible-xs-block';
}
// Sidebar affix
if ($instance['sidebar_affix']) {
$classes[] = 'is-affix';
}
// Animation (Zibll obs-animate ani-*).
if ($instance['animation_in'] !== '') {
$classes[] = 'obs-animate';
$classes[] = 'ani-' . $instance['animation_in'];
if ($instance['animation_repeat']) {
$classes[] = 'obs-animate-repeat';
}
}
return $classes;
}
}
if (!function_exists('aksrt_widget_common_wrapper_attr')) {
/**
* Build the full wrapper attribute string (class + style + data-affix) for
* the Zibll .zib-widget-wrap parity layer.
*
* Combine with a widget-specific class via a leading space, or use as-is.
*
* @param array<string,mixed> $instance Widget instance.
* @param string $extra_class Additional class (widget-specific).
* @return string Escaped attribute string beginning with a leading space, e.g. ' class="…" style="…" data-affix="true"'.
*/
function aksrt_widget_common_wrapper_attr(array $instance, string $extra_class = ''): string {
$instance = aksrt_widget_normalize_common($instance);
$classes = ['zib-widget-wrap'];
foreach (aksrt_widget_get_common_wrapper_classes([], $instance) as $c) {
$classes[] = $c;
}
$extra_class = trim($extra_class);
if ($extra_class !== '') {
$classes[] = $extra_class;
}
$styles = [];
if ($instance['layout_pin_pc']['top'] !== '') {
$styles[] = '--pt-pc:' . (int) $instance['layout_pin_pc']['top'] . 'px;';
}
if ($instance['layout_pin_pc']['bottom'] !== '') {
$styles[] = '--pb-pc:' . (int) $instance['layout_pin_pc']['bottom'] . 'px;';
}
if ($instance['layout_pin_m']['top'] !== '') {
$styles[] = '--pt-m:' . (int) $instance['layout_pin_m']['top'] . 'px;';
}
if ($instance['layout_pin_m']['bottom'] !== '') {
$styles[] = '--pb-m:' . (int) $instance['layout_pin_m']['bottom'] . 'px;';
}
foreach (['white' => 'img_white', 'dark' => 'img_dark'] as $mode => $bg_key) {
$bg = $instance['layout_bg'][$bg_key] ?? [];
if (!empty($bg['background-color'])) {
$styles[] = '--bg-color-' . $mode . ':' . $bg['background-color'] . ';';
}
if (!empty($bg['background-image'])) {
$styles[] = '--bg-img-' . $mode . ':url("' . $bg['background-image'] . '");';
}
}
$attr = ' class="' . esc_attr(implode(' ', $classes)) . '"';
if ($styles) {
$attr .= ' style="' . esc_attr(implode('', $styles)) . '"';
}
if ($instance['sidebar_affix']) {
$attr .= ' data-affix="true"';
}
return $attr;
}
}
if (!function_exists('aksrt_widget_affix_attrs')) {
/**
* Compact affix class+attribute helper for widgets that apply affix onto an
* inner wrapper (the two-family pattern from the legacy in_affix widgets).
*
* @param array<string,mixed> $instance Widget instance.
* @return array{class:string,attr:string} class includes leading space when active; attr includes leading space.
*/
function aksrt_widget_affix_attrs(array $instance): array {
$instance = aksrt_widget_normalize_common($instance);
if (empty($instance['sidebar_affix'])) {
return ['class' => '', 'attr' => ''];
}
return ['class' => ' is-affix', 'attr' => ' data-affix="true"'];
}
}
if (!function_exists('aksrt_widget_render_title')) {
/**
* Render the Zibll-aligned widget title block (CSF_Widget::show_title parity).
*
* Two styles:
* - '' (简约): .box-body notop > .title-theme with optional subtitle + more link
* - 'big' (居中大标题): .big-title-wrap with up to 3 button links
*
* Highlight wraps the matched substring in a colored span.
*
* @param array<string,mixed> $instance Widget instance.
* @return string Title HTML (empty when no title).
*/
function aksrt_widget_render_title(array $instance): string {
$instance = aksrt_widget_normalize_common($instance);
$title = trim((string) ($instance['title'] ?? ''));
if ($title === '') {
return '';
}
$highlight = trim((string) $instance['title_highlight']);
$subtitle = (string) $instance['subtitle'];
$anim = function_exists('aksrt_widget_animation_class')
? aksrt_widget_animation_class($instance)
: '';
$title = esc_html($title);
if ($highlight !== '' && $highlight !== $title) {
$color = $instance['title_highlight_color'];
$hl_esc = esc_html($highlight);
$title = str_replace($hl_esc, '<span class="title-highlight ' . esc_attr($color) . '">' . $hl_esc . '</span>', $title);
}
if ($instance['title_style'] === 'big') {
$style = $instance['title_link_style'];
$links = '';
foreach (['title_link' => 'color_1', 'title_link2' => 'color_2', 'title_link3' => 'color_3'] as $key => $color_key) {
$link = $instance[$key];
if ($link['url'] !== '' && $link['text'] !== '') {
$blank = $link['target'] === '_blank' ? ' target="_blank"' : '';
$links .= '<a' . $blank . ' href="' . esc_url($link['url']) . '" class="but ' . esc_attr($style[$color_key]) . '">' . esc_html($link['text']) . '</a>';
}
}
$links = $links !== '' ? '<div class="flex jc big-title-buts' . $anim . '">' . $links . '</div>' : '';
$sub = $subtitle !== '' ? '<p class="big-title-sub muted-2-color' . $anim . '">' . esc_html($subtitle) . '</p>' : '';
return '<div class="big-title-wrap"><h2 class="big-title' . $anim . '">' . $title . '</h2>' . $sub . $links . '</div>';
}
// Simple style.
$link = $instance['title_link'];
$sub = $subtitle !== '' ? '<small class="ml10">' . esc_html($subtitle) . '</small>' : '';
$more = '';
if ($link['url'] !== '') {
$text = $link['text'] !== '' ? $link['text'] : '<i class="fa fa-angle-right fa-fw"></i>' . __('更多', 'aksrt');
$blank = $link['target'] === '_blank' ? ' target="_blank"' : '';
$more = '<div class="pull-right em09 mt3"><a' . $blank . ' href="' . esc_url($link['url']) . '" class="muted-2-color">' . wp_kses_post($text) . '</a></div>';
}
return '<div class="box-body notop' . $anim . '"><div class="title-theme">' . $title . $sub . $more . '</div></div>';
}
}
if (!function_exists('aksrt_widget_animation_class')) {
/**
* Zibll CSF_Widget::animation_class parity: obs-animate ani-* [+repeat].
*
* @param array<string,mixed> $instance Normalized widget instance.
* @param bool $is_title Reserved for title-specific animation.
* @return string
*/
function aksrt_widget_animation_class(array $instance, bool $is_title = false): string {
$animation = (string) ($instance['animation_in'] ?? '');
if ($animation === '') {
return '';
}
$class = ' obs-animate ani-' . $animation;
if (!empty($instance['animation_repeat'])) {
$class .= ' obs-animate-repeat';
}
return $class;
}
}
if (!function_exists('aksrt_widget_should_display_by_ids')) {
/**
* Check if the current post/page should display the widget based on show_id_type and show_ids.
*
* show_id_type tokens: '' (no limit) | 'show' (whitelist) | 'hide' (blacklist).
* Accepts legacy 'include'/'exclude' via normalize_common.
*
* @param array $instance Widget instance.
* @return bool
*/
function aksrt_widget_should_display_by_ids(array $instance): bool {
$instance = aksrt_widget_normalize_common($instance);
if ($instance['show_id_type'] === '' || $instance['show_ids'] === '') {
return true;
}
$current_id = get_the_ID();
if (!$current_id) {
return true; // Not on a singular page, allow display
}
// Zibll splits on comma/Chinese-comma/whitespace/newline.
$ids = array_filter(array_map('absint', preg_split('/[,,\s]+/', $instance['show_ids']) ?: []));
if ($instance['show_id_type'] === 'show') {
return in_array($current_id, $ids, true);
}
if ($instance['show_id_type'] === 'hide') {
return !in_array($current_id, $ids, true);
}
return true;
}
}
+5 -205
View File
@@ -7,6 +7,8 @@
defined('ABSPATH') || exit;
require_once AKSRT_DIR . '/inc/modules/widget-common.php';
/**
* Register custom widgets.
*/
@@ -307,208 +309,6 @@ if (!function_exists('aksrt_widget_get_area_context')) {
}
}
// ============================================================
// Shared Widget Form Helper — 公共表单字段与显示规则
// ============================================================
if (!function_exists('aksrt_widget_common_defaults')) {
/**
* Default values for shared widget instance fields.
*
* @return array<string, mixed>
*/
function aksrt_widget_common_defaults(): array {
return [
'subtitle' => '',
'title_link' => '',
'show_type' => 'all',
'show_id_type' => '',
'show_ids' => '',
'sidebar_affix' => false,
'animation_in' => '',
'animation_repeat'=> false,
];
}
}
if (!function_exists('aksrt_widget_normalize_common')) {
/**
* Normalize common widget instance fields, filling defaults for legacy instances.
*
* @param array<string, mixed> $instance
* @return array<string, mixed>
*/
function aksrt_widget_normalize_common(array $instance): array {
$defaults = aksrt_widget_common_defaults();
$result = array_merge($defaults, $instance);
$result['subtitle'] = sanitize_text_field($result['subtitle'] ?? '');
$result['title_link'] = esc_url_raw($result['title_link'] ?? '');
$result['show_type'] = in_array($result['show_type'] ?? '', ['all', 'pc', 'mobile'], true) ? $result['show_type'] : 'all';
$result['show_id_type'] = in_array($result['show_id_type'] ?? '', ['', 'include', 'exclude'], true) ? $result['show_id_type'] : '';
$result['show_ids'] = sanitize_text_field($result['show_ids'] ?? '');
$result['sidebar_affix'] = !empty($result['sidebar_affix']);
$result['animation_in'] = sanitize_key($result['animation_in'] ?? '');
$result['animation_repeat']= !empty($result['animation_repeat']);
return $result;
}
}
if (!function_exists('aksrt_widget_render_common_form')) {
/**
* Render shared widget admin form fields.
*
* Call this from your widget's form() method after rendering custom fields.
*
* @param array<string, mixed> $instance Current widget instance.
*/
function aksrt_widget_render_common_form(array $instance): void {
$instance = aksrt_widget_normalize_common($instance);
?>
<hr style="margin:16px 0 12px;border-color:#e0e0e0;">
<p style="font-size:12px;color:#666;margin-bottom:8px;font-weight:600;"><?php esc_html_e('显示规则', 'aksrt'); ?></p>
<p>
<label for="<?php echo esc_attr($instance['show_type_id'] ?? ''); ?>"><?php esc_html_e('显示设备', 'aksrt'); ?></label><br>
<select name="<?php echo esc_attr($instance['show_type_name'] ?? 'show_type'); ?>" id="<?php echo esc_attr($instance['show_type_id'] ?? ''); ?>" class="widefat">
<option value="all" <?php selected($instance['show_type'], 'all'); ?>><?php esc_html_e('全部', 'aksrt'); ?></option>
<option value="pc" <?php selected($instance['show_type'], 'pc'); ?>><?php esc_html_e('仅 PC', 'aksrt'); ?></option>
<option value="mobile" <?php selected($instance['show_type'], 'mobile'); ?>><?php esc_html_e('仅移动端', 'aksrt'); ?></option>
</select>
</p>
<p>
<label for="<?php echo esc_attr($instance['show_id_type_id'] ?? ''); ?>"><?php esc_html_e('ID 显示规则', 'aksrt'); ?></label><br>
<select name="<?php echo esc_attr($instance['show_id_type_name'] ?? 'show_id_type'); ?>" id="<?php echo esc_attr($instance['show_id_type_id'] ?? ''); ?>" class="widefat">
<option value="" <?php selected($instance['show_id_type'], ''); ?>><?php esc_html_e('不限制', 'aksrt'); ?></option>
<option value="include" <?php selected($instance['show_id_type'], 'include'); ?>><?php esc_html_e('仅指定 ID 显示', 'aksrt'); ?></option>
<option value="exclude" <?php selected($instance['show_id_type'], 'exclude'); ?>><?php esc_html_e('指定 ID 隐藏', 'aksrt'); ?></option>
</select>
</p>
<p>
<label for="<?php echo esc_attr($instance['show_ids_id'] ?? ''); ?>"><?php esc_html_e('文章/页面 ID', 'aksrt'); ?></label><br>
<input type="text" name="<?php echo esc_attr($instance['show_ids_name'] ?? 'show_ids'); ?>" id="<?php echo esc_attr($instance['show_ids_id'] ?? ''); ?>" class="widefat" value="<?php echo esc_attr($instance['show_ids']); ?>" placeholder="<?php esc_attr_e('逗号分隔,如: 1,2,3', 'aksrt'); ?>">
</p>
<p>
<label>
<input type="checkbox" name="<?php echo esc_attr($instance['sidebar_affix_name'] ?? 'sidebar_affix'); ?>" value="1" <?php checked($instance['sidebar_affix']); ?>>
<?php esc_html_e('侧栏吸顶', 'aksrt'); ?>
</label>
</p>
<p>
<label for="<?php echo esc_attr($instance['animation_in_id'] ?? ''); ?>"><?php esc_html_e('入场动画', 'aksrt'); ?></label><br>
<select name="<?php echo esc_attr($instance['animation_in_name'] ?? 'animation_in'); ?>" id="<?php echo esc_attr($instance['animation_in_id'] ?? ''); ?>" class="widefat">
<option value="" <?php selected($instance['animation_in'], ''); ?>><?php esc_html_e('无', 'aksrt'); ?></option>
<option value="fadeIn" <?php selected($instance['animation_in'], 'fadeIn'); ?>><?php esc_html_e('淡入', 'aksrt'); ?></option>
<option value="fadeInUp" <?php selected($instance['animation_in'], 'fadeInUp'); ?>><?php esc_html_e('上移淡入', 'aksrt'); ?></option>
<option value="fadeInDown" <?php selected($instance['animation_in'], 'fadeInDown'); ?>><?php esc_html_e('下移淡入', 'aksrt'); ?></option>
<option value="fadeInLeft" <?php selected($instance['animation_in'], 'fadeInLeft'); ?>><?php esc_html_e('左移淡入', 'aksrt'); ?></option>
<option value="fadeInRight" <?php selected($instance['animation_in'], 'fadeInRight'); ?>><?php esc_html_e('右移淡入', 'aksrt'); ?></option>
<option value="zoomIn" <?php selected($instance['animation_in'], 'zoomIn'); ?>><?php esc_html_e('缩放进入', 'aksrt'); ?></option>
</select>
</p>
<p>
<label>
<input type="checkbox" name="<?php echo esc_attr($instance['animation_repeat_name'] ?? 'animation_repeat'); ?>" value="1" <?php checked($instance['animation_repeat']); ?>>
<?php esc_html_e('动画重复播放', 'aksrt'); ?>
</label>
</p>
<?php
}
}
if (!function_exists('aksrt_widget_update_common')) {
/**
* Process shared widget form fields in update().
*
* @param array<string, mixed> $new_instance
* @param array<string, mixed> $old_instance
* @return array<string, mixed>
*/
function aksrt_widget_update_common(array $new_instance, array $old_instance): array {
$merged = array_merge($old_instance, $new_instance);
return aksrt_widget_normalize_common($merged);
}
}
if (!function_exists('aksrt_widget_render_common_before')) {
/**
* Render common wrapper attributes and classes before widget content.
*
* Outputs CSS classes for animation, affix, show_type, etc.
* Call this at the start of widget() before rendering content.
*
* @param array $args Sidebar args.
* @param array $instance Widget instance.
* @return array Modified args with extra classes.
*/
function aksrt_widget_get_common_wrapper_classes(array $args, array $instance): array {
$instance = aksrt_widget_normalize_common($instance);
$classes = [];
// Show type: PC or mobile
if ($instance['show_type'] === 'pc') {
$classes[] = 'aksrt-widget--pc-only';
} elseif ($instance['show_type'] === 'mobile') {
$classes[] = 'aksrt-widget--mobile-only';
}
// Sidebar affix
if ($instance['sidebar_affix']) {
$classes[] = 'aksrt-widget--affix';
}
// Animation
if ($instance['animation_in'] !== '') {
$classes[] = 'aksrt-widget--animated';
$classes[] = 'aksrt-anim-' . $instance['animation_in'];
if ($instance['animation_repeat']) {
$classes[] = 'aksrt-anim-repeat';
}
}
return $classes;
}
}
if (!function_exists('aksrt_widget_should_display_by_ids')) {
/**
* Check if the current post/page should display the widget based on show_id_type and show_ids.
*
* @param array $instance Widget instance.
* @return bool
*/
function aksrt_widget_should_display_by_ids(array $instance): bool {
$instance = aksrt_widget_normalize_common($instance);
if ($instance['show_id_type'] === '' || $instance['show_ids'] === '') {
return true;
}
$current_id = get_the_ID();
if (!$current_id) {
return true; // Not on a singular page, allow display
}
$ids = array_filter(array_map('absint', explode(',', $instance['show_ids'])));
if ($instance['show_id_type'] === 'include') {
return in_array($current_id, $ids, true);
}
if ($instance['show_id_type'] === 'exclude') {
return !in_array($current_id, $ids, true);
}
return true;
}
}
if (!function_exists('aksrt_widget_render_location_help')) {
/**
* Render placement hint inside widget admin forms.
@@ -559,11 +359,11 @@ if (!function_exists('aksrt_filter_widget_display_by_area')) {
if (is_array($instance) && function_exists('aksrt_widget_normalize_common')) {
$normalized = aksrt_widget_normalize_common($instance);
// Show type: PC / mobile filter
if ($normalized['show_type'] === 'pc' && wp_is_mobile()) {
// Show type: PC / mobile filter (Zibll only_pc / only_sm).
if ($normalized['show_type'] === 'only_pc' && wp_is_mobile()) {
return false;
}
if ($normalized['show_type'] === 'mobile' && !wp_is_mobile()) {
if ($normalized['show_type'] === 'only_sm' && !wp_is_mobile()) {
return false;
}
+70
View File
@@ -428,5 +428,75 @@ trait SetupPart1 {
'after_title' => $title_after,
]);
}
// ── Per-page dynamic sidebars (zibll zib_page_register_sidebar parity) ──
// Pages that opt in via meta `widgets_register=1` + `widgets_register_container`
// (a serialized list of area keys) get page_{key}_{ID} sidebars registered, and
// the matching `page_sidebar_{ID}` is treated as active when any of its areas
// hold widgets. page.php reads page_top_fluid_/page_top_content_/etc.
$this->register_page_sidebars();
}
/**
* Register dynamic per-page widget areas (aligns Zibll zib_page_register_sidebar).
*
* Zibll stores, per page, a `widgets_register` flag and a `widgets_register_container`
* serialized array of area keys (sidebar/top_fluid/top_content/bottom_content/bottom_fluid).
* We replicate that: for every page opted in, register page_{key}_{ID} sidebars.
*/
public function register_page_sidebars(): void {
global $wpdb;
// Cache the opt-in page list (postmeta-driven), mirroring zib_get_widgets_register_pages.
$pages = wp_cache_get('aksrt_widgets_register_pages', 'aksrt_cache_group', true);
if (false === $pages) {
// phpcs:ignore WordPress.DB.DirectDatabaseQuery
$rows = $wpdb->get_results(
"SELECT p.ID, p.post_title, pm.meta_value
FROM {$wpdb->posts} p
INNER JOIN {$wpdb->postmeta} pm ON pm.post_id = p.ID
WHERE p.post_type = 'page'
AND p.post_status = 'publish'
AND pm.meta_key = 'widgets_register_container'
AND pm.meta_value LIKE '%;}'"
);
$pages = is_array($rows) ? $rows : [];
wp_cache_set('aksrt_widgets_register_pages', $pages, 'aksrt_cache_group');
}
$area_labels = [
'sidebar' => __('侧边栏', 'aksrt'),
'top_fluid' => __('顶部全宽度', 'aksrt'),
'top_content' => __('主内容上面', 'aksrt'),
'bottom_content' => __('主内容下面', 'aksrt'),
'bottom_fluid' => __('底部全宽度', 'aksrt'),
];
$fluid_wrapper_before = '<div id="%1$s" class="zib-widget %2$s">';
$card_wrapper_before = '<div id="%1$s" class="zib-widget %2$s">';
$title_before = '<h3>';
$title_after = '</h3>';
foreach ($pages as $page) {
$meta_value = maybe_unserialize($page->meta_value);
if (!is_array($meta_value)) {
continue;
}
$name = function_exists('aksrt_str_cut') ? aksrt_str_cut($page->post_title, 0, 10, '...') : mb_substr((string) $page->post_title, 0, 10);
foreach ($meta_value as $key) {
if (!isset($area_labels[$key])) {
continue;
}
$id = 'page_' . $key . '_' . (int) $page->ID;
register_sidebar([
'name' => sprintf(__('[页面:%1$s]-%2$s', 'aksrt'), $name, $area_labels[$key]),
'id' => $id,
'description' => sprintf(__('显示在[页面:%1$s]的 %2$s 位置', 'aksrt'), $name, $area_labels[$key]),
'before_widget' => $key === 'sidebar' ? $card_wrapper_before : $fluid_wrapper_before,
'after_widget' => '</div>',
'before_title' => $title_before,
'after_title' => $title_after,
]);
}
}
}
}
+30 -18
View File
@@ -59,6 +59,7 @@ class WidgetAjax {
$instance = [
'count' => max(1, min(30, (int) ($request->get_param('count') ?? 6))),
'orderby' => sanitize_key((string) ($request->get_param('orderby') ?? 'date')),
'order' => strtoupper(sanitize_key((string) ($request->get_param('order') ?? 'desc'))) === 'ASC' ? 'ASC' : 'DESC',
'cat' => max(0, (int) ($request->get_param('cat') ?? 0)),
'limit_day' => max(0, (int) ($request->get_param('limit_day') ?? 0)),
'topics' => sanitize_text_field((string) ($request->get_param('topics') ?? '')),
@@ -91,9 +92,10 @@ class WidgetAjax {
'title' => '',
'category_id' => max(0, (int) ($request->get_param('cat') ?? $request->get_param('category_id') ?? 0)),
'orderby' => sanitize_key((string) ($request->get_param('orderby') ?? 'date')),
'order' => strtoupper(sanitize_key((string) ($request->get_param('order') ?? 'desc'))) === 'ASC' ? 'ASC' : 'DESC',
'count' => max(0, min(20, (int) ($request->get_param('tab_count') ?? 0))),
'enabled' => true,
'topics' => sanitize_text_field((string) ($request->get_param('topics') ?? '')),
'limit_day' => max(0, (int) ($request->get_param('limit_day') ?? 0)),
'pay_type' => sanitize_key((string) ($request->get_param('pay_type') ?? '')),];
@@ -202,11 +204,12 @@ class WidgetAjax {
$paginate = 'ajax';
}
$instance = [
'count' => max(1, min(20, (int) ($request->get_param('count') ?? 5))),
'order_by' => sanitize_key((string) ($request->get_param('order_by') ?? 'views')),
'days' => max(0, (int) ($request->get_param('days') ?? 30)),
'show_thumb' => $request->get_param('show_thumb') === null ? 1 : (int) $request->get_param('show_thumb'),
'paginate' => $paginate,
'count' => max(1, min(20, (int) ($request->get_param('count') ?? 5))),
'order_by' => sanitize_key((string) ($request->get_param('order_by') ?? 'views')),
'days' => max(0, (int) ($request->get_param('days') ?? 30)),
'show_thumb' => $request->get_param('show_thumb') === null ? 1 : (int) $request->get_param('show_thumb'),
'target_blank'=> (int) ($request->get_param('target_blank') ?? 0),
'paginate' => $paginate,
];
$page = max(1, (int) ($request->get_param('page') ?? 1));
return $this->payload(\AKSRT\Widgets\HotPostsWidget::query_payload($instance, $page), $page);
@@ -220,6 +223,7 @@ class WidgetAjax {
'count' => max(1, min(20, (int) ($request->get_param('count') ?? 8))),
'style' => sanitize_key((string) ($request->get_param('style') ?? 'card')),
'orderby' => sanitize_key((string) ($request->get_param('orderby') ?? 'date')),
'order' => strtoupper(sanitize_key((string) ($request->get_param('order') ?? 'desc'))) === 'ASC' ? 'ASC' : 'DESC',
'cat' => max(0, (int) ($request->get_param('cat') ?? 0)),
'limit_day' => max(0, (int) ($request->get_param('limit_day') ?? 0)),
'topics' => sanitize_text_field((string) ($request->get_param('topics') ?? '')),
@@ -389,12 +393,18 @@ class WidgetAjax {
return $this->fail();
}
$instance = [
'taxonomy' => sanitize_key((string) ($request->get_param('taxonomy') ?? 'category')),
'number' => max(1, min(30, (int) ($request->get_param('number') ?? 8))),
'columns' => max(1, min(4, (int) ($request->get_param('columns') ?? 2))),
'orderby' => sanitize_key((string) ($request->get_param('orderby') ?? 'count')),
'include' => sanitize_text_field((string) ($request->get_param('include') ?? '')),
'hide_empty' => $request->get_param('hide_empty') === null ? 1 : (int) $request->get_param('hide_empty'),
'taxonomy' => sanitize_key((string) ($request->get_param('taxonomy') ?? 'category')),
'number' => max(1, min(30, (int) ($request->get_param('number') ?? 8))),
'pc_row' => max(1, min(12, (int) ($request->get_param('pc_row') ?? $request->get_param('columns') ?? 2))),
'm_row' => max(1, min(12, (int) ($request->get_param('m_row') ?? 1))),
'columns' => max(1, min(12, (int) ($request->get_param('columns') ?? $request->get_param('pc_row') ?? 2))),
'type' => sanitize_key((string) ($request->get_param('type') ?? 'style-1')),
'height_scale' => max(20, min(300, (int) ($request->get_param('height_scale') ?? 60))),
'mask_opacity' => max(0, min(90, (int) ($request->get_param('mask_opacity') ?? 10))),
'orderby' => sanitize_key((string) ($request->get_param('orderby') ?? 'count')),
'include' => sanitize_text_field((string) ($request->get_param('include') ?? '')),
'hide_empty' => $request->get_param('hide_empty') === null ? 1 : (int) $request->get_param('hide_empty'),
'target_blank' => (int) ($request->get_param('target_blank') ?? 0),
];
$html = \AKSRT\Widgets\WidgetTermCards::render_html($instance);
return new \WP_REST_Response(['success' => true, 'html' => $html], 200);
@@ -405,12 +415,14 @@ class WidgetAjax {
return $this->fail();
}
$instance = [
'taxonomy' => sanitize_key((string) ($request->get_param('taxonomy') ?? 'category')),
'term_ids' => sanitize_text_field((string) ($request->get_param('term_ids') ?? '')),
'count' => max(1, min(12, (int) ($request->get_param('count') ?? 4))),
'columns' => max(1, min(3, (int) ($request->get_param('columns') ?? 1))),
'orderby' => sanitize_key((string) ($request->get_param('orderby') ?? 'date')),
'auto_terms' => max(1, min(4, (int) ($request->get_param('auto_terms') ?? 2))),
'taxonomy' => sanitize_key((string) ($request->get_param('taxonomy') ?? 'category')),
'term_ids' => sanitize_text_field((string) ($request->get_param('term_ids') ?? '')),
'count' => max(1, min(20, (int) ($request->get_param('count') ?? 4))),
'columns' => max(1, min(3, (int) ($request->get_param('columns') ?? 1))),
'orderby' => sanitize_key((string) ($request->get_param('orderby') ?? 'date')),
'order' => strtoupper(sanitize_key((string) ($request->get_param('order') ?? 'desc'))) === 'ASC' ? 'ASC' : 'DESC',
'auto_terms' => max(1, min(4, (int) ($request->get_param('auto_terms') ?? 2))),
'target_blank' => (int) ($request->get_param('target_blank') ?? 0),
];
$html = \AKSRT\Widgets\WidgetTermAggregation::render_html($instance);
return new \WP_REST_Response(['success' => true, 'html' => $html], 200);
+8 -3
View File
@@ -5,9 +5,13 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class AboutWidget extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
@@ -367,12 +371,13 @@ class AboutWidget extends \WP_Widget {
<?php esc_html_e('留空则使用默认背景图。已登录用户可使用个人封面图。', 'aksrt'); ?>
</p>
<?php
}
$this->render_common_form_section($instance);
}
public function update($new_instance, $old_instance): array {
return [
$business = [
'title' => sanitize_text_field($new_instance['title'] ?? ''),
'cover' => esc_url_raw($new_instance['cover'] ?? ''),
];
]; return $this->update_common_fields($new_instance, $old_instance, $business);
}
}
+14 -5
View File
@@ -7,9 +7,13 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class ButtonsWidget extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct('aksrt_buttons', esc_html__('AKSRT 按钮组', 'aksrt'), [
@@ -213,7 +217,8 @@ class ButtonsWidget extends \WP_Widget {
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name("buttons[{$i}][new_window]")); ?>" value="1" <?php checked(!empty($btn['new_window'])); ?>> <?php esc_html_e('新窗口', 'aksrt'); ?></label>
</div>
<?php endforeach;
}
$this->render_common_form_section($instance);
}
/**
* @param array<string,mixed> $new_instance
@@ -244,18 +249,22 @@ class ButtonsWidget extends \WP_Widget {
}
}
}
return [
$business = [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'align' => in_array(($new_instance['align'] ?? ''), ['left', 'center', 'right'], true) ? $new_instance['align'] : 'center',
'radius' => !empty($new_instance['radius']) ? 1 : 0,
'hollow' => !empty($new_instance['hollow']) ? 1 : 0,
'go_link' => !empty($new_instance['go_link']) ? 1 : 0,
'gap' => max(4, min(30, (int) ($new_instance['gap'] ?? 12))),
'pc_gap' => max(0, min(40, (int) ($new_instance['pc_gap'] ?? $new_instance['gap'] ?? 12))),
'm_gap' => max(0, min(30, (int) ($new_instance['m_gap'] ?? 6))),
'pc_gap' => max(0, min(60, (int) ($new_instance['pc_gap'] ?? $new_instance['gap'] ?? 12))),
'm_gap' => max(0, min(60, (int) ($new_instance['m_gap'] ?? 6))),
'pc_padding' => in_array(($new_instance['pc_padding'] ?? 'large'), ['small', 'medium', 'large'], true) ? $new_instance['pc_padding'] : 'large',
'm_padding' => in_array(($new_instance['m_padding'] ?? 'medium'), ['small', 'medium', 'large'], true) ? $new_instance['m_padding'] : 'medium',
'pc_width' => max(0, min(500, (int) ($new_instance['pc_width'] ?? 0))),
'm_width' => max(0, min(500, (int) ($new_instance['m_width'] ?? 0))),
'obs_animation' => sanitize_key((string) ($new_instance['obs_animation'] ?? '')),
'animation_repeat'=> !empty($new_instance['animation_repeat']) ? 1 : 0,
'buttons' => $buttons,
];
]; return $this->update_common_fields($new_instance, $old_instance, $business);
}
}
+10 -10
View File
@@ -7,9 +7,12 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class CategoriesWidget extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
@@ -176,12 +179,12 @@ class CategoriesWidget extends \WP_Widget {
if ($title !== '') {
echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
$in_affix = !empty($instance['in_affix']);
if ($in_affix) {
echo '<div class="is-affix" data-affix="true">';
$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 ($in_affix) {
if ($affix['class'] !== '') {
echo '</div>';
}
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
@@ -238,9 +241,7 @@ class CategoriesWidget extends \WP_Widget {
<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>
<p>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('in_affix')); ?>" value="1" <?php checked(!empty($instance['in_affix'])); ?>> <?php esc_html_e('侧栏随动', 'aksrt'); ?></label>
</p>
<?php $this->render_common_form_section($instance); ?>
<?php
}
@@ -254,7 +255,7 @@ class CategoriesWidget extends \WP_Widget {
if (!in_array($orderby, ['count', 'name', 'id', 'slug'], true)) {
$orderby = 'count';
}
return [
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,
@@ -267,7 +268,6 @@ class CategoriesWidget extends \WP_Widget {
'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,
'in_affix' => !empty($new_instance['in_affix']) ? 1 : 0,
];
]);
}
}
+8 -3
View File
@@ -5,6 +5,8 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
/**
@@ -311,6 +313,8 @@ if (!function_exists('aksrt_home_widget_get_lazyload_args')) {
* Homepage category block widget.
*/
class CategoryBlockWidget extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
@@ -486,10 +490,11 @@ class CategoryBlockWidget extends \WP_Widget {
</p>
<?php if (function_exists('aksrt_widget_render_location_help')) { aksrt_widget_render_location_help('hybrid'); } ?>
<?php
}
$this->render_common_form_section($instance);
}
public function update($new_instance, $old_instance): array {
return [
$business = [
'title' => sanitize_text_field($new_instance['title'] ?? ''),
'category_id' => absint($new_instance['category_id'] ?? 0),
'count' => max(1, min(12, absint($new_instance['count'] ?? 6))),
@@ -497,7 +502,7 @@ class CategoryBlockWidget extends \WP_Widget {
'layout' => aksrt_home_widget_resolve_layout($new_instance['layout'] ?? 'card', 'card'),
'show_more' => ! empty($new_instance['show_more']) ? 1 : 0,
'paginate' => in_array(sanitize_key((string) ($new_instance['paginate'] ?? 'none')), ['none', 'ajax', 'number'], true) ? sanitize_key((string) ($new_instance['paginate'] ?? 'none')) : 'none',
];
]; return $this->update_common_fields($new_instance, $old_instance, $business);
}
public static function resolve_category_id_static(array $instance): int {
@@ -5,12 +5,16 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
/**
* Homepage category recommend cards widget.
*/
class CategoryRecommendWidget extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
@@ -194,10 +198,11 @@ class CategoryRecommendWidget extends \WP_Widget {
</p>
<?php if (function_exists('aksrt_widget_render_location_help')) { aksrt_widget_render_location_help('homepage'); } ?>
<?php
}
$this->render_common_form_section($instance);
}
public function update($new_instance, $old_instance): array {
return [
$business = [
'title' => sanitize_text_field($new_instance['title'] ?? ''),
'category_ids' => aksrt_home_widget_normalize_category_ids($new_instance['category_ids'] ?? [], 4),
'posts_per_category' => max(1, min(8, absint($new_instance['posts_per_category'] ?? 5))),
@@ -206,7 +211,7 @@ class CategoryRecommendWidget extends \WP_Widget {
'show_description' => ! empty($new_instance['show_description']) ? 1 : 0,
'show_dates' => ! empty($new_instance['show_dates']) ? 1 : 0,
'show_more' => ! empty($new_instance['show_more']) ? 1 : 0,
];
]; return $this->update_common_fields($new_instance, $old_instance, $business);
}
/**
+8 -3
View File
@@ -5,12 +5,16 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
/**
* Homepage category tabs widget.
*/
class CategoryTabsWidget extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
@@ -157,17 +161,18 @@ public function form($instance): void {
</p>
<?php if (function_exists('aksrt_widget_render_location_help')) { aksrt_widget_render_location_help('hybrid'); } ?>
<?php
}
$this->render_common_form_section($instance);
}
public function update($new_instance, $old_instance): array {
return [
$business = [
'title' => sanitize_text_field($new_instance['title'] ?? ''),
'category_ids' => aksrt_home_widget_normalize_category_ids($new_instance['category_ids'] ?? [], 6),
'count' => max(1, min(12, absint($new_instance['count'] ?? 6))),
'columns' => max(1, min(4, absint($new_instance['columns'] ?? 3))),
'layout' => aksrt_home_widget_resolve_layout($new_instance['layout'] ?? 'card', 'card'),
'show_more' => ! empty($new_instance['show_more']) ? 1 : 0,
];
]; return $this->update_common_fields($new_instance, $old_instance, $business);
}
/**
+8 -3
View File
@@ -7,9 +7,13 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class CustomHtmlGapWidget extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct('aksrt_custom_html_gap', esc_html__('AKSRT 自定义HTML/间距', 'aksrt'), [
@@ -90,7 +94,8 @@ class CustomHtmlGapWidget extends \WP_Widget {
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('full_width')); ?>" value="1" <?php checked(!empty($instance['full_width'])); ?>> <?php esc_html_e('HTML 通栏', 'aksrt'); ?></label>
</p>
<?php
}
$this->render_common_form_section($instance);
}
/**
* @param array<string,mixed> $new_instance
@@ -98,13 +103,13 @@ class CustomHtmlGapWidget extends \WP_Widget {
* @return array<string,mixed>
*/
public function update($new_instance, $old_instance): array {
return [
$business = [
'mode' => in_array(($new_instance['mode'] ?? ''), ['html', 'gap'], true) ? $new_instance['mode'] : 'html',
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'content' => wp_kses_post((string) ($new_instance['content'] ?? '')),
'gap_height' => max(0, min(500, (int) ($new_instance['gap_height'] ?? 20))),
'gap_height_m' => max(0, min(500, (int) ($new_instance['gap_height_m'] ?? $new_instance['gap_height'] ?? 20))),
'full_width' => !empty($new_instance['full_width']) ? 1 : 0,
];
]; return $this->update_common_fields($new_instance, $old_instance, $business);
}
}
+22 -2
View File
@@ -7,9 +7,13 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class FriendLinksWidget extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct('aksrt_friend_links', esc_html__('AKSRT 友情链接', 'aksrt'), [
@@ -148,6 +152,20 @@ class FriendLinksWidget extends \WP_Widget {
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('cats')); ?>" value="<?php echo esc_attr((string) ($instance['cats'] ?? '')); ?>">
</label>
</p>
<p>
<label><?php esc_html_e('书签排序(书签模式)', 'aksrt'); ?>
<select name="<?php echo esc_attr($this->get_field_name('orderby')); ?>">
<option value="name" <?php selected(($instance['orderby'] ?? 'name'), 'name'); ?>><?php esc_html_e('名称', 'aksrt'); ?></option>
<option value="updated" <?php selected(($instance['orderby'] ?? ''), 'updated'); ?>><?php esc_html_e('更新时间', 'aksrt'); ?></option>
<option value="rating" <?php selected(($instance['orderby'] ?? ''), 'rating'); ?>><?php esc_html_e('评分', 'aksrt'); ?></option>
<option value="rand" <?php selected(($instance['orderby'] ?? ''), 'rand'); ?>><?php esc_html_e('随机', 'aksrt'); ?></option>
</select>
<select name="<?php echo esc_attr($this->get_field_name('order')); ?>">
<option value="ASC" <?php selected(($instance['order'] ?? 'ASC'), 'ASC'); ?>><?php esc_html_e('升序', 'aksrt'); ?></option>
<option value="DESC" <?php selected(($instance['order'] ?? ''), 'DESC'); ?>><?php esc_html_e('降序', 'aksrt'); ?></option>
</select>
</label>
</p>
<p>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('blank')); ?>" value="1" <?php checked(!isset($instance['blank']) || !empty($instance['blank'])); ?>> <?php esc_html_e('新窗口', 'aksrt'); ?></label><br>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('nofollow')); ?>" value="1" <?php checked(!isset($instance['nofollow']) || !empty($instance['nofollow'])); ?>> <?php esc_html_e('nofollow', 'aksrt'); ?></label><br>
@@ -155,7 +173,8 @@ class FriendLinksWidget extends \WP_Widget {
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('show_box')); ?>" value="1" <?php checked(!isset($instance['show_box']) || !empty($instance['show_box'])); ?>> <?php esc_html_e('外框盒子', 'aksrt'); ?></label>
</p>
<?php
}
$this->render_common_form_section($instance);
}
/**
* @param array<string,mixed> $new_instance
@@ -167,7 +186,7 @@ class FriendLinksWidget extends \WP_Widget {
if (!in_array($type, ['card', 'bigcard', 'image', 'simple'], true)) {
$type = 'card';
}
return [
$business = [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'source' => ($new_instance['source'] ?? '') === 'bookmarks' ? 'bookmarks' : 'settings',
'type' => $type,
@@ -183,5 +202,6 @@ class FriendLinksWidget extends \WP_Widget {
'columns' => max(1, min(4, (int) ($new_instance['columns'] ?? 2))),
'show_icon' => !empty($new_instance['show_icon']) ? 1 : 0,
];
return $this->update_common_fields($new_instance, $old_instance, $business);
}
}
+21 -15
View File
@@ -7,9 +7,12 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class HotPostsWidget extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
@@ -107,6 +110,7 @@ class HotPostsWidget extends \WP_Widget {
$permalink = get_permalink($pid);
$title = get_the_title($pid);
$time_ago = human_time_diff(get_the_time('U'), current_time('timestamp')) . __('前', 'aksrt');
$tgt = !empty($instance['target_blank']) ? ' target="_blank" rel="noopener noreferrer"' : '';
// Graphic hero for #1 (zibll zib_hot_posts).
if ($index === 1 && $start === 0) {
@@ -115,7 +119,7 @@ class HotPostsWidget extends \WP_Widget {
$thumb = get_the_post_thumbnail_url($pid, 'medium');
}
echo '<div class="hot-posts-graphic relative ajax-item is-top1">';
echo '<a href="' . esc_url($permalink) . '">';
echo '<a href="' . esc_url($permalink) . '"' . $tgt . '>';
echo '<div class="graphic hover-zoom-img" style="padding-bottom:60%;">';
if ($thumb) {
echo '<img class="fit-cover radius8 aksrt-lazy lazyload" data-src="' . esc_url($thumb) . '" src="' . esc_url($placeholder) . '" alt="" loading="lazy" />';
@@ -138,12 +142,12 @@ class HotPostsWidget extends \WP_Widget {
<div class="hot-post-item flex mt15 relative hover-zoom-img ajax-item <?php echo esc_attr($rank_class); ?>">
<?php echo $top_badge; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php if ($show_thumb) : ?>
<a class="hot-post-item__thumb graphic<?php echo $thumb ? '' : ' is-empty'; ?>" href="<?php echo esc_url($permalink); ?>">
<a class="hot-post-item__thumb graphic<?php echo $thumb ? '' : ' is-empty'; ?>" href="<?php echo esc_url($permalink); ?>"<?php echo $tgt; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
<?php if ($thumb) : ?><img class="fit-cover radius8 aksrt-lazy lazyload" data-src="<?php echo esc_url($thumb); ?>" src="<?php echo esc_url($placeholder); ?>" alt="" loading="lazy" /><?php endif; ?>
</a>
<?php endif; ?>
<div class="hot-post-item__info term-title ml10 flex xx flex1 jsb">
<div class="text-ellipsis-2"><a href="<?php echo esc_url($permalink); ?>" class="hot-post-item__title"><?php echo esc_html($title); ?></a></div>
<div class="text-ellipsis-2"><a href="<?php echo esc_url($permalink); ?>" class="hot-post-item__title"<?php echo $tgt; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>><?php echo esc_html($title); ?></a></div>
<div class="px12 muted-3-color text-ellipsis flex jsb hot-post-item__meta">
<span class="hot-post-item__time"><i class="fa fa-clock-o mr3" aria-hidden="true"></i><?php echo esc_html($time_ago); ?></span>
<span><i class="fa <?php echo esc_attr($metric_icon); ?>" aria-hidden="true"></i> <?php echo esc_html((string) $metric_txt); ?></span>
@@ -173,11 +177,12 @@ class HotPostsWidget extends \WP_Widget {
$total = (int) $q->found_posts;
$has_more = $page < $max_pages;
$base_qs = array_filter([
'count' => $count,
'order_by' => sanitize_key((string) ($instance['order_by'] ?? 'views')),
'days' => max(0, (int) ($instance['days'] ?? 30)),
'show_thumb' => !empty($instance['show_thumb']) ? 1 : 0,
'paginate' => $paginate,
'count' => $count,
'order_by' => sanitize_key((string) ($instance['order_by'] ?? 'views')),
'days' => max(0, (int) ($instance['days'] ?? 30)),
'show_thumb' => !empty($instance['show_thumb']) ? 1 : 0,
'target_blank'=> !empty($instance['target_blank']) ? 1 : 0,
'paginate' => $paginate,
], static function ($v) {
return $v !== '' && $v !== null;
});
@@ -204,12 +209,12 @@ class HotPostsWidget extends \WP_Widget {
if ($title) {
echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
$in_affix = !empty($instance['in_affix']);
$affix = function_exists('aksrt_widget_affix_attrs') ? aksrt_widget_affix_attrs($instance) : ['class' => '', 'attr' => ''];
$instance['paginate'] = sanitize_key((string) ($instance['paginate'] ?? 'ajax'));
$payload = self::query_payload($instance, 1);
$rest = rest_url('aksrt/v1/widgets/hot-posts');
$wrap = 'ajaxpager' . ($in_affix ? ' is-affix' : '');
echo '<div class="' . esc_attr($wrap) . '" data-aksrt-ajaxpager data-aksrt-ajax-url="' . esc_url($rest) . '"' . ($in_affix ? ' data-affix="true"' : '') . '>';
$wrap = 'ajaxpager' . $affix['class'];
echo '<div class="' . esc_attr($wrap) . '" data-aksrt-ajaxpager data-aksrt-ajax-url="' . esc_url($rest) . '"' . $affix['attr'] . '>';
echo $payload['html']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $payload['paginate_html']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo '<div class="post_ajax_loader" data-aksrt-ajax-loader data-aksrt-ajax-loader-hide hidden></div>';
@@ -247,7 +252,7 @@ class HotPostsWidget extends \WP_Widget {
</p>
<p>
<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('in_affix')); ?>" value="1" <?php checked(!empty($instance['in_affix'])); ?>> <?php esc_html_e('侧栏随动', 'aksrt'); ?></label>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('target_blank')); ?>" value="1" <?php checked(!empty($instance['target_blank'])); ?>> <?php esc_html_e('新窗口打开', 'aksrt'); ?></label>
</p>
<p>
<label><?php esc_html_e('分页', 'aksrt'); ?>
@@ -258,18 +263,19 @@ class HotPostsWidget extends \WP_Widget {
</select>
</label>
</p>
<?php $this->render_common_form_section($instance); ?>
<?php
}
public function update($new_instance, $old_instance): array {
return [
return $this->update_common_fields($new_instance, $old_instance, [
'title' => sanitize_text_field($new_instance['title'] ?? ''),
'count' => absint($new_instance['count'] ?? 5),
'order_by' => in_array($new_instance['order_by'] ?? '', ['views', 'comments', 'likes', 'favorites', 'downloads'], true) ? $new_instance['order_by'] : 'views',
'days' => absint($new_instance['days'] ?? 30),
'show_thumb' => !empty($new_instance['show_thumb']) ? 1 : 0,
'in_affix' => !empty($new_instance['in_affix']) ? 1 : 0,
'target_blank' => !empty($new_instance['target_blank']) ? 1 : 0,
'paginate' => in_array(sanitize_key((string) ($new_instance['paginate'] ?? 'ajax')), ['none', 'ajax', 'number'], true) ? sanitize_key((string) ($new_instance['paginate'] ?? 'ajax')) : 'ajax',
];
]);
}
}
+8 -3
View File
@@ -7,9 +7,13 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class IconCardsWidget extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct('aksrt_icon_cards', esc_html__('AKSRT 图标卡片', 'aksrt'), [
@@ -193,7 +197,8 @@ class IconCardsWidget extends \WP_Widget {
<input class="widefat" type="url" name="<?php echo esc_attr($this->get_field_name("cards[{$i}][url]")); ?>" value="<?php echo esc_attr((string) ($card['url'] ?? '')); ?>" placeholder="<?php esc_attr_e('链接', 'aksrt'); ?>">
</div>
<?php endforeach;
}
$this->render_common_form_section($instance);
}
/**
* @param array<string,mixed> $new_instance
@@ -201,7 +206,7 @@ class IconCardsWidget extends \WP_Widget {
* @return array<string,mixed>
*/
public function update($new_instance, $old_instance): array {
return [
$business = [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'columns' => max(1, min(6, (int) ($new_instance['columns'] ?? 2))),
'm_row' => max(1, min(4, (int) ($new_instance['m_row'] ?? 2))),
@@ -210,6 +215,6 @@ class IconCardsWidget extends \WP_Widget {
'icon_radius' => !empty($new_instance['icon_radius']) ? 1 : 0,
'obs_animation' => sanitize_key((string) ($new_instance['obs_animation'] ?? '')),
'cards' => $this->normalize($new_instance['cards'] ?? []),
];
]; return $this->update_common_fields($new_instance, $old_instance, $business);
}
}
+10 -11
View File
@@ -7,9 +7,12 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class NoticeWidget extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct('aksrt_notice', esc_html__('AKSRT 滚动公告', 'aksrt'), [
@@ -36,7 +39,7 @@ if (!$items) {
$radius = !empty($instance['radius']);
$interval = max(2, min(15, (int) ($instance['interval'] ?? 4)));
$align = sanitize_html_class((string) ($instance['alignment'] ?? ''));
$in_affix = !empty($instance['in_affix']);
$affix = function_exists('aksrt_widget_affix_attrs') ? aksrt_widget_affix_attrs($instance) : ['class' => '', 'attr' => ''];
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$title = apply_filters('widget_title', (string) ($instance['title'] ?? ''), $instance, $this->id_base);
@@ -51,12 +54,9 @@ if (!$items) {
if ($align !== '') {
$wrap_cls .= ' ' . $align;
}
if ($in_affix) {
$wrap_cls .= ' is-affix';
}
$wrap_cls .= $affix['class'];
$attr = $in_affix ? ' data-affix="true"' : '';
echo '<div class="' . esc_attr($wrap_cls) . '"' . $attr . '>';
echo '<div class="' . esc_attr($wrap_cls) . '"' . $affix['attr'] . '>';
echo '<div class="aksrt-scroll-notice__icon bulletin-icon"><i class="fas fa-bullhorn" aria-hidden="true"></i></div>';
// Prefer shared vertical new-swiper (zib_notice parity) via slider.js Type C.
@@ -192,14 +192,14 @@ if (!$items) {
</p>
<p>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('blank')); ?>" value="1" <?php checked(!empty($instance['blank'])); ?>> <?php esc_html_e('链接新窗口', 'aksrt'); ?></label><br>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('radius')); ?>" value="1" <?php checked(!empty($instance['radius'])); ?>> <?php esc_html_e('两端圆形', 'aksrt'); ?></label><br>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('in_affix')); ?>" value="1" <?php checked(!empty($instance['in_affix'])); ?>> <?php esc_html_e('侧栏随动', 'aksrt'); ?></label>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('radius')); ?>" value="1" <?php checked(!empty($instance['radius'])); ?>> <?php esc_html_e('两端圆形', 'aksrt'); ?></label>
</p>
<?php $this->render_common_form_section($instance); ?>
<?php
}
public function update($new_instance, $old_instance): array {
return [
return $this->update_common_fields($new_instance, $old_instance, [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'items' => $this->normalize_items(['items' => $new_instance['items'] ?? []]),
'interval' => max(2, min(15, (int) ($new_instance['interval'] ?? 4))),
@@ -207,7 +207,6 @@ if (!$items) {
'alignment' => sanitize_html_class((string) ($new_instance['alignment'] ?? '')),
'blank' => !empty($new_instance['blank']) ? 1 : 0,
'radius' => !empty($new_instance['radius']) ? 1 : 0,
'in_affix' => !empty($new_instance['in_affix']) ? 1 : 0,
];
]);
}
}
+18 -16
View File
@@ -7,9 +7,12 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class RecentCommentsWidget extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct('aksrt_recent_comments', esc_html__('AKSRT 最新评论', 'aksrt'), [
@@ -21,7 +24,7 @@ class RecentCommentsWidget extends \WP_Widget {
* @param array<string,mixed> $instance
*/
public static function render_html(array $instance): string {
$count = max(1, min(20, (int) ($instance['count'] ?? $instance['limit'] ?? 5)));
$count = max(1, min(50, (int) ($instance['count'] ?? $instance['limit'] ?? 5)));
$show_avatar = !isset($instance['show_avatar']) || !empty($instance['show_avatar']);
// zibll: outpost = post__not_in (multi-ID), outer = author__not_in (multi-ID).
// Also keep optional keyword filter via outer_keywords for legacy installs.
@@ -169,12 +172,12 @@ class RecentCommentsWidget extends \WP_Widget {
if ($title !== '') {
echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
$in_affix = !empty($instance['in_affix']);
if ($in_affix) {
echo '<div class="is-affix" data-affix="true">';
$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 ($in_affix) {
if ($affix['class'] !== '') {
echo '</div>';
}
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
@@ -192,7 +195,7 @@ class RecentCommentsWidget extends \WP_Widget {
</p>
<p>
<label><?php esc_html_e('数量', 'aksrt'); ?>
<input class="tiny-text" type="number" min="1" max="20" name="<?php echo esc_attr($this->get_field_name('count')); ?>" value="<?php echo esc_attr((string) ((int) ($instance['count'] ?? 5))); ?>">
<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'] ?? 5))); ?>">
</label>
</p>
<p>
@@ -209,9 +212,7 @@ class RecentCommentsWidget extends \WP_Widget {
</label>
<small><?php esc_html_e('填数字 ID 时按作者排除;填文字时按内容关键词排除(兼容旧配置)。', 'aksrt'); ?></small>
</p>
<p>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('in_affix')); ?>" value="1" <?php checked(!empty($instance['in_affix'])); ?>> <?php esc_html_e('侧栏随动', 'aksrt'); ?></label>
</p>
<?php $this->render_common_form_section($instance); ?>
<?php
}
@@ -221,12 +222,13 @@ class RecentCommentsWidget extends \WP_Widget {
* @return array<string,mixed>
*/
public function update($new_instance, $old_instance): array {
return [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'count' => max(1, min(20, (int) ($new_instance['count'] ?? 5))),
'show_avatar' => !empty($new_instance['show_avatar']) ? 1 : 0,
'outpost' => sanitize_text_field((string) ($new_instance['outpost'] ?? '')),
'outer' => sanitize_text_field((string) ($new_instance['outer'] ?? '')),
];
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'] ?? 5))),
'show_avatar' => !empty($new_instance['show_avatar']) ? 1 : 0,
'outpost' => sanitize_text_field((string) ($new_instance['outpost'] ?? '')),
'outer' => sanitize_text_field((string) ($new_instance['outer'] ?? '')),
'outer_keywords' => sanitize_text_field((string) ($new_instance['outer_keywords'] ?? '')),
]);
}
}
+24 -6
View File
@@ -7,9 +7,12 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class SearchWidget extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct('aksrt_search', esc_html__('AKSRT 搜索', 'aksrt'), [
@@ -32,10 +35,20 @@ class SearchWidget extends \WP_Widget {
$show_kw = !empty($instance['show_keywords']);
$show_history = !empty($instance['show_history']);
$show_types = !empty($instance['show_type']) || !isset($instance['show_type']);
// NOTE: SearchWidget owns a business `show_type` checkbox (search-type
// toggle) that shares the key name with the common `show_type` (device
// visibility). To avoid an admin-form collision, this widget does NOT
// render the shared common-form section (render_common_form_section is
// intentionally not called). The shared affix helper is still wired in
// widget() via aksrt_widget_affix_attrs(), and update_common_fields()
// persists common keys — but note the shared show_type cannot be set
// here, so display_callback treats it as 'all' (always visible).
$show_cat = !empty($instance['show_input_cat']);
$kw_title = sanitize_text_field((string) ($instance['keywords_title'] ?? __('热门搜索', 'aksrt')));
$more_but = sanitize_text_field((string) ($instance['more_but'] ?? ''));
$more_url = esc_url_raw(trim((string) ($instance['more_but_url'] ?? '')));
// Affix helper (shared layer sidebar_affix).
$affix = function_exists('aksrt_widget_affix_attrs') ? aksrt_widget_affix_attrs($instance) : ['class' => '', 'attr' => ''];
$state = function_exists('aksrt_get_search_form_state')
? aksrt_get_search_form_state()
@@ -109,7 +122,7 @@ class SearchWidget extends \WP_Widget {
$uid = 'aksrt-sw-' . esc_attr($this->id);
?>
<div class="widget-search-box aksrt-search-widget search-input<?php echo !empty($instance['in_affix']) ? ' is-affix' : ''; ?>" id="<?php echo $uid; ?>"<?php echo !empty($instance['in_affix']) ? ' data-affix="true"' : ''; ?>>
<div class="widget-search-box aksrt-search-widget search-input<?php echo $affix['class']; ?>" id="<?php echo $uid; ?>"<?php echo $affix['attr']; ?>>
<form
role="search"
method="get"
@@ -239,8 +252,7 @@ class SearchWidget extends \WP_Widget {
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('show_type')); ?>" value="1" <?php checked(!empty($instance['show_type']) || !isset($instance['show_type'])); ?>> <?php esc_html_e('显示类型切换(≥2 种时)', 'aksrt'); ?></label><br>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('show_input_cat')); ?>" value="1" <?php checked(!empty($instance['show_input_cat'])); ?>> <?php esc_html_e('显示分类筛选', 'aksrt'); ?></label><br>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('show_keywords')); ?>" value="1" <?php checked(!empty($instance['show_keywords'])); ?>> <?php esc_html_e('显示热门关键词', 'aksrt'); ?></label><br>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('show_history')); ?>" value="1" <?php checked(!empty($instance['show_history'])); ?>> <?php esc_html_e('显示搜索历史', 'aksrt'); ?></label><br>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('in_affix')); ?>" value="1" <?php checked(!empty($instance['in_affix'])); ?>> <?php esc_html_e('侧栏随动', 'aksrt'); ?></label>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('show_history')); ?>" value="1" <?php checked(!empty($instance['show_history'])); ?>> <?php esc_html_e('显示搜索历史', 'aksrt'); ?></label>
</p>
<p>
<label><?php esc_html_e('默认类型', 'aksrt'); ?>
@@ -281,11 +293,18 @@ class SearchWidget extends \WP_Widget {
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('more_but_url')); ?>" value="<?php echo esc_attr((string) ($instance['more_but_url'] ?? '')); ?>">
</label>
</p>
<?php // Common-form section intentionally skipped: SearchWidget owns a
// business `show_type` checkbox (search-type toggle) that collides
// with the shared common `show_type` (device visibility). The
// shared affix/display/animation/layout fields are therefore not
// rendered here; update_common_fields() below still persists any
// common keys submitted through other surfaces, and the affix
// helper is wired in widget() via aksrt_widget_affix_attrs(). ?>
<?php
}
public function update($new_instance, $old_instance): array {
return [
return $this->update_common_fields($new_instance, $old_instance, [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'placeholder' => sanitize_text_field((string) ($new_instance['placeholder'] ?? '')),
'show_keywords' => !empty($new_instance['show_keywords']) ? 1 : 0,
@@ -299,7 +318,6 @@ class SearchWidget extends \WP_Widget {
'keywords' => sanitize_textarea_field((string) ($new_instance['keywords'] ?? '')),
'more_but' => sanitize_text_field((string) ($new_instance['more_but'] ?? '')),
'more_but_url' => esc_url_raw(trim((string) ($new_instance['more_but_url'] ?? ''))),
'in_affix' => !empty($new_instance['in_affix']) ? 1 : 0,
];
]);
}
}
+9 -2
View File
@@ -7,9 +7,13 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class TabPostsWidget extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
@@ -106,6 +110,7 @@ class TabPostsWidget extends \WP_Widget {
return [
'count' => max(1, min(30, $count)),
'orderby' => sanitize_key((string) ($tab['orderby'] ?? 'date')),
'order' => strtoupper(sanitize_key((string) ($tab['order'] ?? 'desc'))) === 'ASC' ? 'ASC' : 'DESC',
'cat' => absint($tab['category_id'] ?? 0),
'topics' => sanitize_text_field((string) ($tab['topics'] ?? '')),
'limit_day' => max(0, (int) ($tab['limit_day'] ?? 0)),
@@ -348,7 +353,8 @@ class TabPostsWidget extends \WP_Widget {
</p>
</div>
<?php endforeach;
}
$this->render_common_form_section($instance);
}
public function update($new_instance, $old_instance): array {
$style = sanitize_key((string) ($new_instance['style'] ?? 'mini'));
@@ -380,6 +386,7 @@ class TabPostsWidget extends \WP_Widget {
'title' => sanitize_text_field((string) ($tab['title'] ?? '')),
'category_id' => absint($tab['category_id'] ?? 0),
'orderby' => $orderby,
'order' => strtoupper(sanitize_key((string) ($tab['order'] ?? 'desc'))) === 'ASC' ? 'ASC' : 'DESC',
'count' => max(0, min(20, (int) ($tab['count'] ?? 0))),
'topics' => sanitize_text_field((string) ($tab['topics'] ?? '')),
'limit_day' => max(0, (int) ($tab['limit_day'] ?? 0)),
@@ -389,6 +396,6 @@ class TabPostsWidget extends \WP_Widget {
}
}
$result['tabs'] = !empty($tabs) ? $tabs : 'latest,comments,random';
return $result;
return $this->update_common_fields($new_instance, $old_instance, $result);
}
}
+45 -27
View File
@@ -7,9 +7,12 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class TagCloudWidget extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
@@ -27,20 +30,24 @@ class TagCloudWidget extends \WP_Widget {
if (!taxonomy_exists($taxonomy)) {
$taxonomy = 'post_tag';
}
$number = max(1, min(80, (int) ($instance['count'] ?? $instance['number'] ?? 15)));
$orderby = sanitize_key((string) ($instance['orderby'] ?? 'count'));
if (!in_array($orderby, ['count', 'name', 'id'], true)) {
$orderby = 'count';
$number = max(1, min(100, (int) ($instance['count'] ?? $instance['number'] ?? 20)));
$orderby = sanitize_key((string) ($instance['orderby'] ?? 'name'));
// Zibll widget_ui_tag_cloud: name / count / term_id(创建时间).
if (!in_array($orderby, ['count', 'name', 'term_id', 'id'], true)) {
$orderby = 'name';
}
// Zibll `order` ASC/DESC (default ASC).
$order = strtoupper((string) ($instance['order'] ?? 'ASC')) === 'DESC' ? 'DESC' : 'ASC';
$show_count = !empty($instance['show_count']);
$blank = !empty($instance['blank']);
$color = sanitize_html_class((string) ($instance['color'] ?? 'rand'));
$fixed_width = !empty($instance['fixed_width']);
$wp_orderby = $orderby === 'id' ? 'term_id' : $orderby;
$tags = get_terms([
'taxonomy' => $taxonomy,
'orderby' => $orderby,
'order' => $orderby === 'name' ? 'ASC' : 'DESC',
'orderby' => $wp_orderby,
'order' => $order,
'number' => $number,
'hide_empty' => !empty($instance['hide_empty']),
]);
@@ -101,12 +108,12 @@ class TagCloudWidget extends \WP_Widget {
if ($title !== '') {
echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
$in_affix = !empty($instance['in_affix']);
if ($in_affix) {
echo '<div class="is-affix" data-affix="true">';
$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 ($in_affix) {
if ($affix['class'] !== '') {
echo '</div>';
}
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
@@ -131,28 +138,38 @@ class TagCloudWidget extends \WP_Widget {
</p>
<p>
<label><?php esc_html_e('标签数量:', 'aksrt'); ?>
<input class="tiny-text" type="number" min="1" max="80" name="<?php echo esc_attr($this->get_field_name('count')); ?>" value="<?php echo esc_attr((string) ((int) ($instance['count'] ?? 15))); ?>">
<input class="tiny-text" type="number" min="1" max="100" name="<?php echo esc_attr($this->get_field_name('count')); ?>" value="<?php echo esc_attr((string) ((int) ($instance['count'] ?? 20))); ?>">
</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="name" <?php selected(($instance['orderby'] ?? 'name'), 'name'); ?>><?php esc_html_e('名称', 'aksrt'); ?></option>
<option value="count" <?php selected(($instance['orderby'] ?? ''), 'count'); ?>><?php esc_html_e('文章数量', 'aksrt'); ?></option>
<option value="term_id" <?php selected(($instance['orderby'] ?? ''), 'term_id'); ?>><?php esc_html_e('创建时间', 'aksrt'); ?></option>
</select>
<select name="<?php echo esc_attr($this->get_field_name('order')); ?>">
<option value="ASC" <?php selected(($instance['order'] ?? 'ASC'), 'ASC'); ?>><?php esc_html_e('升序', 'aksrt'); ?></option>
<option value="DESC" <?php selected(($instance['order'] ?? ''), 'DESC'); ?>><?php esc_html_e('降序', 'aksrt'); ?></option>
</select>
</label>
</p>
<p>
<label><?php esc_html_e('颜色', 'aksrt'); ?>
<select name="<?php echo esc_attr($this->get_field_name('color')); ?>">
<option value="rand" <?php selected(($instance['color'] ?? 'rand'), 'rand'); ?>><?php esc_html_e('随机', 'aksrt'); ?></option>
<option value="c-blue" <?php selected(($instance['color'] ?? ''), 'c-blue'); ?>><?php esc_html_e('蓝', 'aksrt'); ?></option>
<option value="c-green" <?php selected(($instance['color'] ?? ''), 'c-green'); ?>><?php esc_html_e('绿', 'aksrt'); ?></option>
<option value="c-yellow" <?php selected(($instance['color'] ?? ''), 'c-yellow'); ?>><?php esc_html_e('黄', 'aksrt'); ?></option>
<option value="c-purple" <?php selected(($instance['color'] ?? ''), 'c-purple'); ?>><?php esc_html_e('紫', 'aksrt'); ?></option>
<option value="c-red" <?php selected(($instance['color'] ?? ''), 'c-red'); ?>><?php esc_html_e('红', 'aksrt'); ?></option>
<option value="" <?php selected(($instance['color'] ?? ''), ''); ?>><?php esc_html_e('默认', 'aksrt'); ?></option>
<option value="rand" <?php selected(($instance['color'] ?? 'rand'), 'rand'); ?>><?php esc_html_e('随机颜色', 'aksrt'); ?></option>
<option value="c-hui" <?php selected(($instance['color'] ?? ''), 'c-hui'); ?>><?php esc_html_e('灰色', 'aksrt'); ?></option>
<option value="c-blue" <?php selected(($instance['color'] ?? ''), 'c-blue'); ?>><?php esc_html_e('蓝色', 'aksrt'); ?></option>
<option value="c-blue-2" <?php selected(($instance['color'] ?? ''), 'c-blue-2'); ?>><?php esc_html_e('深蓝色', 'aksrt'); ?></option>
<option value="c-cyan" <?php selected(($instance['color'] ?? ''), 'c-cyan'); ?>><?php esc_html_e('青色', 'aksrt'); ?></option>
<option value="c-yellow" <?php selected(($instance['color'] ?? ''), 'c-yellow'); ?>><?php esc_html_e('黄色', 'aksrt'); ?></option>
<option value="c-yellow-2" <?php selected(($instance['color'] ?? ''), 'c-yellow-2'); ?>><?php esc_html_e('橙黄色', 'aksrt'); ?></option>
<option value="c-green" <?php selected(($instance['color'] ?? ''), 'c-green'); ?>><?php esc_html_e('绿色', 'aksrt'); ?></option>
<option value="c-green-2" <?php selected(($instance['color'] ?? ''), 'c-green-2'); ?>><?php esc_html_e('墨绿色', 'aksrt'); ?></option>
<option value="c-purple" <?php selected(($instance['color'] ?? ''), 'c-purple'); ?>><?php esc_html_e('紫色', 'aksrt'); ?></option>
<option value="c-purple-2" <?php selected(($instance['color'] ?? ''), 'c-purple-2'); ?>><?php esc_html_e('深紫色', 'aksrt'); ?></option>
<option value="c-red" <?php selected(($instance['color'] ?? ''), 'c-red'); ?>><?php esc_html_e('粉红色', 'aksrt'); ?></option>
<option value="c-red-2" <?php selected(($instance['color'] ?? ''), 'c-red-2'); ?>><?php esc_html_e('红色', 'aksrt'); ?></option>
</select>
</label>
</p>
@@ -161,24 +178,25 @@ class TagCloudWidget extends \WP_Widget {
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('blank')); ?>" value="1" <?php checked(!empty($instance['blank'])); ?>> <?php esc_html_e('新窗口', 'aksrt'); ?></label><br>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('fixed_width')); ?>" value="1" <?php checked(!empty($instance['fixed_width'])); ?>> <?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(!empty($instance['hide_empty'])); ?>> <?php esc_html_e('隐藏空标签', 'aksrt'); ?></label><br>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('scale')); ?>" value="1" <?php checked(!empty($instance['scale'])); ?>> <?php esc_html_e('按数量缩放字号', 'aksrt'); ?></label><br>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('in_affix')); ?>" value="1" <?php checked(!empty($instance['in_affix'])); ?>> <?php esc_html_e('侧栏随动', 'aksrt'); ?></label>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('scale')); ?>" value="1" <?php checked(!empty($instance['scale'])); ?>> <?php esc_html_e('按数量缩放字号', 'aksrt'); ?></label>
</p>
<?php $this->render_common_form_section($instance); ?>
<?php
}
public function update($new_instance, $old_instance): array {
return [
return $this->update_common_fields($new_instance, $old_instance, [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'taxonomy' => sanitize_key((string) ($new_instance['taxonomy'] ?? 'post_tag')),
'count' => max(1, min(80, (int) ($new_instance['count'] ?? 15))),
'orderby' => in_array(($new_instance['orderby'] ?? ''), ['count', 'name', 'id'], true) ? $new_instance['orderby'] : 'count',
'count' => max(1, min(100, (int) ($new_instance['count'] ?? 20))),
'orderby' => in_array(($new_instance['orderby'] ?? ''), ['count', 'name', 'term_id', 'id'], true) ? $new_instance['orderby'] : 'name',
'order' => strtoupper((string) ($new_instance['order'] ?? 'ASC')) === 'DESC' ? 'DESC' : 'ASC',
'color' => sanitize_html_class((string) ($new_instance['color'] ?? 'rand')),
'show_count' => !empty($new_instance['show_count']) ? 1 : 0,
'blank' => !empty($new_instance['blank']) ? 1 : 0,
'fixed_width' => !empty($new_instance['fixed_width']) ? 1 : 0,
'hide_empty' => !empty($new_instance['hide_empty']) ? 1 : 0,
'scale' => !empty($new_instance['scale']) ? 1 : 0,
];
]);
}
}
@@ -0,0 +1,73 @@
<?php
/**
* CommonWidgetFields trait — single seat-belt for shared widget form/update.
*
* Every AKSRT widget `use`s this trait so the Zibll common-field family
* (title block, display rules, affix, animation, layout bg) is declared and
* persisted in exactly one place: inc/modules/widget-common.php.
*
* Widgets keep only their business fields; the trait wires the shared section
* into form() and folds the shared keys into update().
*
* @package AKSRT\Widgets\Traits
*/
namespace AKSRT\Widgets\Traits;
defined('ABSPATH') || exit;
trait CommonWidgetFields {
/**
* Whether this widget should render the shared common-form section.
*
* Override and return false for pure-display widgets with no admin form.
* Default: true (every widget gets the shared block).
*
* @return bool
*/
protected function use_common_form(): bool {
return true;
}
/**
* Render the shared common-form section at the end of a widget's form().
*
* Call from form(): render business fields, then `$this->render_common_form_section($instance);`.
*
* @param array<string,mixed> $instance Widget instance.
*/
protected function render_common_form_section(array $instance): void {
if (!$this->use_common_form()) {
return;
}
if (function_exists('aksrt_widget_render_common_form')) {
aksrt_widget_render_common_form($instance);
}
}
/**
* Merge shared common fields into a widget's update() result.
*
* Usage in update():
* return $this->update_common_fields($new_instance, $old_instance, [
* 'title' => sanitize_text_field(...),
* ...business fields...
* ]);
*
* @param array<string,mixed> $new_instance New submitted instance.
* @param array<string,mixed> $old_instance Previous instance.
* @param array<string,mixed> $business Sanitized business fields to keep.
* @return array<string,mixed>
*/
protected function update_common_fields(array $new_instance, array $old_instance, array $business = []): array {
if (!$this->use_common_form()) {
return $business;
}
if (function_exists('aksrt_widget_update_common')) {
$common = aksrt_widget_update_common($new_instance, $old_instance);
return array_merge($business, $common);
}
return $business;
}
}
+8 -3
View File
@@ -7,9 +7,13 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class WidgetAnnouncement extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
@@ -148,7 +152,8 @@ class WidgetAnnouncement extends \WP_Widget {
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('blank')); ?>" value="1" <?php checked(!empty($instance['blank'])); ?>> <?php esc_html_e('链接新窗口', 'aksrt'); ?></label>
</p>
<?php
}
$this->render_common_form_section($instance);
}
/**
* @param array<string,mixed> $new_instance
@@ -160,7 +165,7 @@ class WidgetAnnouncement extends \WP_Widget {
if (!in_array($mode, ['swiper', 'marquee'], true)) {
$mode = 'swiper';
}
return [
$business = [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'items' => sanitize_textarea_field((string) ($new_instance['items'] ?? '')),
'icon' => sanitize_text_field((string) ($new_instance['icon'] ?? 'fas fa-bullhorn')),
@@ -168,6 +173,6 @@ class WidgetAnnouncement extends \WP_Widget {
'mode' => $mode,
'interval' => max(2, min(15, (int) ($new_instance['interval'] ?? 4))),
'blank' => !empty($new_instance['blank']) ? 1 : 0,
];
]; return $this->update_common_fields($new_instance, $old_instance, $business);
}
}
+81 -17
View File
@@ -7,9 +7,12 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class WidgetAuthorCard extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
@@ -55,6 +58,13 @@ class WidgetAuthorCard extends \WP_Widget {
}
$limit = max(0, min(12, (int) ($instance['limit'] ?? 6)));
$orderby = sanitize_key((string) ($instance['orderby'] ?? 'date'));
// Zibll button palettes / button_2 type (shown only when viewer is the author).
$show_button = !isset($instance['show_button']) || !empty($instance['show_button']);
$btn1_text = (string) ($instance['button_1_text'] ?? __('发布文章', 'aksrt'));
$btn2_text = (string) ($instance['button_2_text'] ?? __('用户中心', 'aksrt'));
$btn1_class = WidgetUserCard::sanitize_palette((string) ($instance['button_1_class'] ?? 'jb-pink'), 'jb-pink');
$btn2_class = WidgetUserCard::sanitize_palette((string) ($instance['button_2_class'] ?? 'jb-blue'), 'jb-blue');
$btn2_type = (string) ($instance['button_2'] ?? 'center');
$avatar = function_exists('aksrt_get_avatar_url')
? aksrt_get_avatar_url($author_id, ['size' => 120])
@@ -84,15 +94,15 @@ class WidgetAuthorCard extends \WP_Widget {
? aksrt_get_user_name(['id' => $author_id, 'follow' => false, 'class' => 'flex ac jc'])
: '<a href="' . esc_url($home) . '">' . esc_html($user->display_name) . '</a>';
$in_affix = !empty($instance['in_affix']);
$affix = function_exists('aksrt_widget_affix_attrs') ? aksrt_widget_affix_attrs($instance) : ['class' => '', 'attr' => ''];
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
}
$card_cls = 'aksrt-author-card user-card author zib-widget' . ($show_bg ? ' has-cover' : '') . ($in_affix ? ' is-affix' : '');
echo '<div class="' . esc_attr($card_cls) . '"' . ($in_affix ? ' data-affix="true"' : '') . '>';
$card_cls = 'aksrt-author-card user-card author zib-widget' . ($show_bg ? ' has-cover' : '') . $affix['class'];
echo '<div class="' . esc_attr($card_cls) . '"' . $affix['attr'] . '>';
if ($show_bg) {
$bg = $cover !== '' ? ' style="background-image:url(' . esc_url($cover) . ')"' : '';
echo '<div class="aksrt-author-card__cover user-cover graphic"' . $bg . '></div>';
@@ -144,6 +154,17 @@ class WidgetAuthorCard extends \WP_Widget {
echo '<button type="button" class="aksrt-author-card__btn aksrt-author-card__follow but' . ($following ? ' is-following' : '') . '" data-aksrt-follow="' . (int) $author_id . '">' . esc_html($label) . '</button>';
}
echo '<a class="aksrt-author-card__btn but" href="' . esc_url($home) . '">' . esc_html__('查看主页', 'aksrt') . '</a>';
// Zibll: button_1 / button_2 only render when the logged-in viewer IS the author.
if ($show_button && $cuid > 0 && $cuid === $author_id) {
$center_url = function_exists('aksrt_get_user_center_url') ? aksrt_get_user_center_url() : $home;
$btn2_url = $btn2_type === 'home' ? $home : $center_url;
$compose_url = $home;
if (function_exists('aksrt_get_user_center_url')) {
$compose_url = aksrt_get_user_center_url('posts');
}
echo '<a class="aksrt-author-card__btn but ' . esc_attr($btn1_class) . '" href="' . esc_url($compose_url) . '">' . esc_html($btn1_text) . '</a>';
echo '<a class="aksrt-author-card__btn but ' . esc_attr($btn2_class) . '" href="' . esc_url($btn2_url) . '">' . esc_html($btn2_text) . '</a>';
}
echo '</div>';
echo '</div>'; // .user-content
@@ -204,15 +225,51 @@ class WidgetAuthorCard extends \WP_Widget {
<label><?php esc_html_e('排序', 'aksrt'); ?>
<select name="<?php echo esc_attr($this->get_field_name('orderby')); ?>">
<option value="date" <?php selected(($instance['orderby'] ?? 'date'), 'date'); ?>><?php esc_html_e('最新', 'aksrt'); ?></option>
<option value="modified" <?php selected(($instance['orderby'] ?? ''), 'modified'); ?>><?php esc_html_e('最近更新', 'aksrt'); ?></option>
<option value="views" <?php selected(($instance['orderby'] ?? ''), 'views'); ?>><?php esc_html_e('阅读量', 'aksrt'); ?></option>
<option value="like" <?php selected(($instance['orderby'] ?? ''), 'like'); ?>><?php esc_html_e('点赞', 'aksrt'); ?></option>
<option value="comment_count" <?php selected(($instance['orderby'] ?? ''), 'comment_count'); ?>><?php esc_html_e('评论数', 'aksrt'); ?></option>
<option value="favorite" <?php selected(($instance['orderby'] ?? ''), 'favorite'); ?>><?php esc_html_e('收藏', 'aksrt'); ?></option>
<option value="rand" <?php selected(($instance['orderby'] ?? ''), 'rand'); ?>><?php esc_html_e('随机', 'aksrt'); ?></option>
</select>
</label>
</p>
<hr style="margin:12px 0;">
<p style="font-weight:600;margin-bottom:8px;"><?php esc_html_e('按钮(仅当登录用户为作者时显示)', 'aksrt'); ?></p>
<p>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('in_affix')); ?>" value="1" <?php checked(!empty($instance['in_affix'])); ?>> <?php esc_html_e('侧栏随动', 'aksrt'); ?></label>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('show_button')); ?>" value="1" <?php checked(!isset($instance['show_button']) || !empty($instance['show_button'])); ?>> <?php esc_html_e('显示按钮', 'aksrt'); ?></label>
</p>
<p>
<label><?php esc_html_e('按钮1文案', 'aksrt'); ?>
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('button_1_text')); ?>" value="<?php echo esc_attr((string) ($instance['button_1_text'] ?? __('发布文章', 'aksrt'))); ?>">
</label>
<label><?php esc_html_e('按钮1颜色', 'aksrt'); ?>
<select name="<?php echo esc_attr($this->get_field_name('button_1_class')); ?>">
<?php foreach (WidgetUserCard::palette_opts() as $k => $label) : ?>
<option value="<?php echo esc_attr($k); ?>" <?php selected(($instance['button_1_class'] ?? 'jb-pink'), $k); ?>><?php echo esc_html($label); ?></option>
<?php endforeach; ?>
</select>
</label>
</p>
<p>
<label><?php esc_html_e('按钮2类型', 'aksrt'); ?>
<select name="<?php echo esc_attr($this->get_field_name('button_2')); ?>">
<option value="home" <?php selected(($instance['button_2'] ?? 'center'), 'home'); ?>><?php esc_html_e('个人主页', 'aksrt'); ?></option>
<option value="center" <?php selected(($instance['button_2'] ?? 'center'), 'center'); ?>><?php esc_html_e('用户中心', 'aksrt'); ?></option>
</select>
</label>
<label><?php esc_html_e('按钮2文案', 'aksrt'); ?>
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('button_2_text')); ?>" value="<?php echo esc_attr((string) ($instance['button_2_text'] ?? __('用户中心', 'aksrt'))); ?>">
</label>
<label><?php esc_html_e('按钮2颜色', 'aksrt'); ?>
<select name="<?php echo esc_attr($this->get_field_name('button_2_class')); ?>">
<?php foreach (WidgetUserCard::palette_opts() as $k => $label) : ?>
<option value="<?php echo esc_attr($k); ?>" <?php selected(($instance['button_2_class'] ?? 'jb-blue'), $k); ?>><?php echo esc_html($label); ?></option>
<?php endforeach; ?>
</select>
</label>
</p>
<?php $this->render_common_form_section($instance); ?>
<?php
}
@@ -222,20 +279,27 @@ class WidgetAuthorCard extends \WP_Widget {
$post_style = 'mini';
}
$orderby = sanitize_key((string) ($new_instance['orderby'] ?? 'date'));
if (!in_array($orderby, ['date', 'views', 'comment_count', 'modified', 'rand'], true)) {
if (!in_array($orderby, ['date', 'views', 'comment_count', 'modified', 'like', 'favorite', 'rand'], true)) {
$orderby = 'date';
}
return [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'user_id' => max(0, (int) ($new_instance['user_id'] ?? 0)),
'show_img_bg' => !empty($new_instance['show_img_bg']) ? 1 : 0,
'show_stats' => !empty($new_instance['show_stats']) ? 1 : 0,
'show_follow' => !empty($new_instance['show_follow']) ? 1 : 0,
'show_badge' => !empty($new_instance['show_badge']) ? 1 : 0,
'show_posts' => !empty($new_instance['show_posts']) ? 1 : 0,
'limit' => max(0, min(12, (int) ($new_instance['limit'] ?? 6))),
'post_style' => $post_style,
'orderby' => $orderby,
];
return $this->update_common_fields($new_instance, $old_instance, [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'user_id' => max(0, (int) ($new_instance['user_id'] ?? 0)),
'show_img_bg' => !empty($new_instance['show_img_bg']) ? 1 : 0,
'show_stats' => !empty($new_instance['show_stats']) ? 1 : 0,
'show_follow' => !empty($new_instance['show_follow']) ? 1 : 0,
'show_badge' => !empty($new_instance['show_badge']) ? 1 : 0,
'show_posts' => !empty($new_instance['show_posts']) ? 1 : 0,
'show_button' => !empty($new_instance['show_button']) ? 1 : 0,
'limit' => max(0, min(12, (int) ($new_instance['limit'] ?? 6))),
'post_style' => $post_style,
'orderby' => $orderby,
'button_1' => 'post',
'button_1_text'=> sanitize_text_field((string) ($new_instance['button_1_text'] ?? '')),
'button_1_class'=> WidgetUserCard::sanitize_palette((string) ($new_instance['button_1_class'] ?? 'jb-pink'), 'jb-pink'),
'button_2' => in_array((string) ($new_instance['button_2'] ?? 'center'), ['home', 'center'], true) ? $new_instance['button_2'] : 'center',
'button_2_text'=> sanitize_text_field((string) ($new_instance['button_2_text'] ?? '')),
'button_2_class'=> WidgetUserCard::sanitize_palette((string) ($new_instance['button_2_class'] ?? 'jb-blue'), 'jb-blue'),
]);
}
}
+26 -5
View File
@@ -7,9 +7,13 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class WidgetBookmarkLinks extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
@@ -29,12 +33,18 @@ class WidgetBookmarkLinks extends \WP_Widget {
$orderby = 'name';
}
$order = strtoupper((string) ($instance['links_order'] ?? 'ASC')) === 'DESC' ? 'DESC' : 'ASC';
// Zibll widget_ui_links_lists_2: type card/bigcard/image/simple.
$type = sanitize_key((string) ($instance['type'] ?? 'card'));
if (!in_array($type, ['card', 'bigcard', 'image', 'simple'], true)) {
$type = 'card';
}
// Zibll alignment ('' / center / right).
$align = sanitize_key((string) ($instance['alignment'] ?? ''));
if (!in_array($align, ['', 'center', 'right'], true)) {
$align = '';
}
$go_link = !empty($instance['go_link']);
$blank = !isset($instance['blank']) || !empty($instance['blank']);
$blank = !empty($instance['blank']);
$nofollow = !isset($instance['nofollow']) || !empty($instance['nofollow']);
$show_box = !empty($instance['show_box']);
@@ -65,7 +75,7 @@ class WidgetBookmarkLinks extends \WP_Widget {
echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
$wrap = 'aksrt-bookmark-links links-box style-' . $type . ($show_box ? ' has-box' : '');
$wrap = 'aksrt-bookmark-links links-box style-' . $type . ($show_box ? ' has-box' : '') . ($align ? ' text-' . $align : '');
echo '<div class="' . esc_attr($wrap) . '">';
if (function_exists('aksrt_links_box')) {
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
@@ -128,6 +138,15 @@ class WidgetBookmarkLinks extends \WP_Widget {
<option value="DESC" <?php selected(($instance['links_order'] ?? ''), 'DESC'); ?>><?php esc_html_e('降序', 'aksrt'); ?></option>
</select>
</p>
<p>
<label><?php esc_html_e('对齐方式', 'aksrt'); ?>
<select name="<?php echo esc_attr($this->get_field_name('alignment')); ?>">
<option value="" <?php selected(($instance['alignment'] ?? ''), ''); ?>><?php esc_html_e('左对齐', 'aksrt'); ?></option>
<option value="center" <?php selected(($instance['alignment'] ?? ''), 'center'); ?>><?php esc_html_e('居中', 'aksrt'); ?></option>
<option value="right" <?php selected(($instance['alignment'] ?? ''), 'right'); ?>><?php esc_html_e('右对齐', 'aksrt'); ?></option>
</select>
</label>
</p>
<p>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('blank')); ?>" value="1" <?php checked(!isset($instance['blank']) || !empty($instance['blank'])); ?>> <?php esc_html_e('新窗口', 'aksrt'); ?></label><br>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('nofollow')); ?>" value="1" <?php checked(!isset($instance['nofollow']) || !empty($instance['nofollow'])); ?>> <?php esc_html_e('nofollow', 'aksrt'); ?></label><br>
@@ -135,24 +154,26 @@ class WidgetBookmarkLinks extends \WP_Widget {
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('show_box')); ?>" value="1" <?php checked(!empty($instance['show_box'])); ?>> <?php esc_html_e('外框盒子', 'aksrt'); ?></label>
</p>
<?php
}
$this->render_common_form_section($instance);
}
public function update($new_instance, $old_instance): array {
$type = sanitize_key((string) ($new_instance['type'] ?? 'card'));
if (!in_array($type, ['card', 'bigcard', 'image', 'simple'], true)) {
$type = 'card';
}
return [
$business = [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'links_cats' => sanitize_text_field((string) ($new_instance['links_cats'] ?? '')),
'links_limit' => max(0, min(100, (int) ($new_instance['links_limit'] ?? 12))),
'links_orderby' => sanitize_key((string) ($new_instance['links_orderby'] ?? 'name')),
'links_order' => strtoupper((string) ($new_instance['links_order'] ?? 'ASC')) === 'DESC' ? 'DESC' : 'ASC',
'type' => $type,
'alignment' => in_array((string) ($new_instance['alignment'] ?? ''), ['', 'center', 'right'], true) ? $new_instance['alignment'] : '',
'blank' => !empty($new_instance['blank']) ? 1 : 0,
'nofollow' => !empty($new_instance['nofollow']) ? 1 : 0,
'go_link' => !empty($new_instance['go_link']) ? 1 : 0,
'show_box' => !empty($new_instance['show_box']) ? 1 : 0,
];
]; return $this->update_common_fields($new_instance, $old_instance, $business);
}
}
+8 -3
View File
@@ -7,9 +7,13 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class WidgetComboInfo extends \WP_Widget {
use CommonWidgetFields;
private const BANDS = ['blue', 'cyan', 'green', 'yellow', 'pink', 'purple'];
@@ -250,10 +254,11 @@ class WidgetComboInfo extends \WP_Widget {
</label>
</p>
<?php
}
$this->render_common_form_section($instance);
}
public function update($new_instance, $old_instance): array {
return [
$business = [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'cards' => $this->normalize_cards($new_instance['cards'] ?? []),
'pc_row' => max(1, min(6, (int) ($new_instance['pc_row'] ?? 4))),
@@ -265,6 +270,6 @@ class WidgetComboInfo extends \WP_Widget {
'go_link' => !empty($new_instance['go_link']) ? 1 : 0,
'obs_animation' => sanitize_key((string) ($new_instance['obs_animation'] ?? '')),
'animation_repeat'=> !empty($new_instance['animation_repeat']) ? 1 : 0,
];
]; return $this->update_common_fields($new_instance, $old_instance, $business);
}
}
+8 -3
View File
@@ -7,9 +7,13 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class WidgetCoverCards extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
@@ -200,7 +204,8 @@ class WidgetCoverCards extends \WP_Widget {
<small><?php esc_html_e('字段: image, video, title, link, target, hide(pc|m), height, mask', 'aksrt'); ?></small>
</p>
<?php
}
$this->render_common_form_section($instance);
}
/**
* @param array<string,mixed> $new_instance
@@ -208,7 +213,7 @@ class WidgetCoverCards extends \WP_Widget {
* @return array<string,mixed>
*/
public function update($new_instance, $old_instance): array {
return [
$business = [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'pc_row' => max(1, min(6, (int) ($new_instance['pc_row'] ?? 2))),
'm_row' => max(1, min(3, (int) ($new_instance['m_row'] ?? 1))),
@@ -220,6 +225,6 @@ class WidgetCoverCards extends \WP_Widget {
'font_bold' => !empty($new_instance['font_bold']) ? 1 : 0,
'font_color' => sanitize_hex_color((string) ($new_instance['font_color'] ?? '')) ?: '',
'items' => $this->normalize($new_instance['items'] ?? []),
];
]; return $this->update_common_fields($new_instance, $old_instance, $business);
}
}
+8 -3
View File
@@ -7,9 +7,13 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class WidgetFaq extends \WP_Widget {
use CommonWidgetFields;
private const BADGE_TONES = ['purple', 'pink', 'green', 'orange', 'blue'];
@@ -332,10 +336,11 @@ class WidgetFaq extends \WP_Widget {
</label>
</p>
<?php
}
$this->render_common_form_section($instance);
}
public function update($new_instance, $old_instance): array {
return [
$business = [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'search_s' => !empty($new_instance['search_s']) ? 1 : 0,
'search_placeholder' => sanitize_text_field((string) ($new_instance['search_placeholder'] ?? '')),
@@ -350,6 +355,6 @@ class WidgetFaq extends \WP_Widget {
'help_icon' => sanitize_text_field((string) ($new_instance['help_icon'] ?? '')),
'help_btn_text' => sanitize_text_field((string) ($new_instance['help_btn_text'] ?? '')),
'help_btn_url' => esc_url_raw(trim((string) ($new_instance['help_btn_url'] ?? ''))),
];
]; return $this->update_common_fields($new_instance, $old_instance, $business);
}
}
+8 -3
View File
@@ -7,9 +7,13 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class WidgetFeatureIntro extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
@@ -206,10 +210,11 @@ class WidgetFeatureIntro extends \WP_Widget {
</label>
</p>
<?php
}
$this->render_common_form_section($instance);
}
public function update($new_instance, $old_instance): array {
return [
$business = [
'heading' => sanitize_text_field((string) ($new_instance['heading'] ?? '')),
'description' => wp_kses_post((string) ($new_instance['description'] ?? '')),
'slides' => $this->normalize_slides($new_instance['slides'] ?? []),
@@ -220,6 +225,6 @@ class WidgetFeatureIntro extends \WP_Widget {
'stat_number' => sanitize_text_field((string) ($new_instance['stat_number'] ?? '')),
'stat_suffix' => sanitize_text_field((string) ($new_instance['stat_suffix'] ?? '')),
'stat_label' => sanitize_text_field((string) ($new_instance['stat_label'] ?? '')),
];
]; return $this->update_common_fields($new_instance, $old_instance, $business);
}
}
+8 -3
View File
@@ -7,9 +7,13 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class WidgetForumBoardList extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
@@ -180,7 +184,8 @@ class WidgetForumBoardList extends \WP_Widget {
<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>
</p>
<?php
}
$this->render_common_form_section($instance);
}
public function update($new_instance, $old_instance): array {
$orderby = sanitize_key((string) ($new_instance['orderby'] ?? 'menu_order'));
@@ -188,12 +193,12 @@ class WidgetForumBoardList extends \WP_Widget {
if (!in_array($orderby, $allowed, true)) {
$orderby = 'menu_order';
}
return [
$business = [
'title' => sanitize_text_field($new_instance['title'] ?? ''),
'count' => max(1, min(50, (int) ($new_instance['count'] ?? 30))),
'orderby' => $orderby,
'style' => ($new_instance['style'] ?? '') === 'mini' ? 'mini' : 'list',
'hide_empty' => !empty($new_instance['hide_empty']) ? 1 : 0,
];
]; return $this->update_common_fields($new_instance, $old_instance, $business);
}
}
+9 -8
View File
@@ -7,9 +7,12 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class WidgetForumHotTopics extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
@@ -179,9 +182,9 @@ class WidgetForumHotTopics extends \WP_Widget {
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$rest = rest_url('aksrt/v1/widgets/forum-topics');
$in_affix = !empty($instance['in_affix']);
$wrap = 'ajaxpager' . ($in_affix ? ' is-affix' : '');
echo '<div class="' . esc_attr($wrap) . '" data-aksrt-ajaxpager data-aksrt-ajax-url="' . esc_url($rest) . '"' . ($in_affix ? ' data-affix="true"' : '') . '>';
$affix = function_exists('aksrt_widget_affix_attrs') ? aksrt_widget_affix_attrs($instance) : ['class' => '', 'attr' => ''];
$wrap = 'ajaxpager' . $affix['class'];
echo '<div class="' . esc_attr($wrap) . '" data-aksrt-ajaxpager data-aksrt-ajax-url="' . esc_url($rest) . '"' . $affix['attr'] . '>';
echo $payload['html']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $payload['paginate_html']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo '<div class="post_ajax_loader" data-aksrt-ajax-loader data-aksrt-ajax-loader-hide hidden></div>';
@@ -245,9 +248,7 @@ class WidgetForumHotTopics extends \WP_Widget {
</select>
</label>
</p>
<p>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('in_affix')); ?>" value="1" <?php checked(!empty($instance['in_affix'])); ?>> <?php esc_html_e('侧栏随动', 'aksrt'); ?></label>
</p>
<?php $this->render_common_form_section($instance); ?>
<?php
}
@@ -260,7 +261,7 @@ class WidgetForumHotTopics extends \WP_Widget {
if (!in_array($style, ['mini', 'rank'], true)) {
$style = 'mini';
}
return [
return $this->update_common_fields($new_instance, $old_instance, [
'title' => sanitize_text_field($new_instance['title'] ?? ''),
'count' => max(1, min(20, (int) ($new_instance['count'] ?? 5))),
'period' => in_array($new_instance['period'] ?? '', ['all', 'week', 'month'], true) ? $new_instance['period'] : 'week',
@@ -268,6 +269,6 @@ class WidgetForumHotTopics extends \WP_Widget {
'style' => $style,
'plate' => max(0, (int) ($new_instance['plate'] ?? 0)),
'paginate' => in_array(sanitize_key((string) ($new_instance['paginate'] ?? 'ajax')), ['none', 'ajax', 'number'], true) ? sanitize_key((string) ($new_instance['paginate'] ?? 'ajax')) : 'ajax',
];
]);
}
}
+8 -3
View File
@@ -7,9 +7,13 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class WidgetForumPlateInfo extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
@@ -156,14 +160,15 @@ class WidgetForumPlateInfo extends \WP_Widget {
</p>
<p class="description"><?php esc_html_e('仅在版块页或帖子页显示对应版块信息。', 'aksrt'); ?></p>
<?php
}
$this->render_common_form_section($instance);
}
public function update($new_instance, $old_instance): array {
return [
$business = [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'show_stats' => !empty($new_instance['show_stats']) ? 1 : 0,
'show_follow' => !empty($new_instance['show_follow']) ? 1 : 0,
'show_compose' => !empty($new_instance['show_compose']) ? 1 : 0,
];
]; return $this->update_common_fields($new_instance, $old_instance, $business);
}
}
+8 -3
View File
@@ -7,9 +7,13 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class WidgetForumPlateLists extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
@@ -260,20 +264,21 @@ class WidgetForumPlateLists extends \WP_Widget {
</label>
</p>
<?php
}
$this->render_common_form_section($instance);
}
public function update($new_instance, $old_instance): array {
$orderby = sanitize_key((string) ($new_instance['orderby'] ?? 'menu_order'));
if (!in_array($orderby, ['menu_order', 'date', 'posts', 'views', 'follow'], true)) {
$orderby = 'menu_order';
}
return [
$business = [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'showposts' => max(1, min(30, (int) ($new_instance['showposts'] ?? 8))),
'orderby' => $orderby,
'style' => ($new_instance['style'] ?? '') === 'card' ? 'card' : 'default',
'filter' => ($new_instance['filter'] ?? '') === 'is_hot' ? 'is_hot' : '',
'cat' => sanitize_text_field((string) ($new_instance['cat'] ?? '')),
];
]; return $this->update_common_fields($new_instance, $old_instance, $business);
}
}
@@ -7,9 +7,12 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class WidgetForumPlateModerator extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
@@ -69,7 +72,7 @@ class WidgetForumPlateModerator extends \WP_Widget {
if ($desc === '') {
$desc = __('欢迎有热情的用户申请成为版主,协助管理社区秩序。', 'aksrt');
}
obs_start();
ob_start();
echo '<button type="button" class="but hollow c-blue p2-10 em09" data-aksrt-mod-apply-open="' . esc_attr((string) $uid) . '">' . esc_html__('申请版主', 'aksrt') . '</button>';
echo '<div class="aksrt-mod-apply-modal" id="' . esc_attr((string) $uid) . '" hidden>';
echo '<div class="aksrt-mod-apply-modal__backdrop" data-aksrt-mod-apply-close></div>';
@@ -147,7 +150,7 @@ class WidgetForumPlateModerator extends \WP_Widget {
return ['html' => ''];
}
obs_start();
ob_start();
echo '<div class="zib-widget moderator-box aksrt-plate-mods style-' . esc_attr($style) . ' ajax-item" data-aksrt-plate-mods="1">';
if ($title_text !== '' || $apply_html !== '') {
echo '<div class="mb10 flex jsb ac aksrt-plate-mods__head">';
@@ -230,15 +233,16 @@ class WidgetForumPlateModerator extends \WP_Widget {
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('apply_btn')); ?>" value="1" <?php checked(!isset($instance['apply_btn']) || !empty($instance['apply_btn'])); ?>> <?php esc_html_e('显示申请版主入口', 'aksrt'); ?></label>
</p>
<p class="description"><?php esc_html_e('仅在版块/帖子页显示。', 'aksrt'); ?></p>
<?php $this->render_common_form_section($instance); ?>
<?php
}
public function update($new_instance, $old_instance): array {
return [
return $this->update_common_fields($new_instance, $old_instance, [
'title_text' => sanitize_text_field((string) ($new_instance['title_text'] ?? '')),
'style' => ($new_instance['style'] ?? '') === 'grid' ? 'grid' : 'list',
'show_avatar' => !empty($new_instance['show_avatar']) ? 1 : 0,
'apply_btn' => !empty($new_instance['apply_btn']) ? 1 : 0,
];
]);
}
}
@@ -7,9 +7,12 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class WidgetForumRecentTopics extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
@@ -40,9 +43,9 @@ class WidgetForumRecentTopics extends \WP_Widget {
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
$rest = rest_url('aksrt/v1/widgets/forum-topics');
$in_affix = !empty($instance['in_affix']);
$wrap = 'ajaxpager' . ($in_affix ? ' is-affix' : '');
echo '<div class="' . esc_attr($wrap) . '" data-aksrt-ajaxpager data-aksrt-ajax-url="' . esc_url($rest) . '"' . ($in_affix ? ' data-affix="true"' : '') . '>';
$affix = function_exists('aksrt_widget_affix_attrs') ? aksrt_widget_affix_attrs($instance) : ['class' => '', 'attr' => ''];
$wrap = 'ajaxpager' . $affix['class'];
echo '<div class="' . esc_attr($wrap) . '" data-aksrt-ajaxpager data-aksrt-ajax-url="' . esc_url($rest) . '"' . $affix['attr'] . '>';
echo $payload['html']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $payload['paginate_html']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo '<div class="post_ajax_loader" data-aksrt-ajax-loader data-aksrt-ajax-loader-hide hidden></div>';
@@ -86,9 +89,7 @@ class WidgetForumRecentTopics extends \WP_Widget {
</select>
</label>
</p>
<p>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('in_affix')); ?>" value="1" <?php checked(!empty($instance['in_affix'])); ?>> <?php esc_html_e('侧栏随动', 'aksrt'); ?></label>
</p>
<?php $this->render_common_form_section($instance); ?>
<?php
}
@@ -97,13 +98,12 @@ class WidgetForumRecentTopics extends \WP_Widget {
if (!in_array($style, ['mini', 'rank'], true)) {
$style = 'mini';
}
return [
return $this->update_common_fields($new_instance, $old_instance, [
'title' => sanitize_text_field($new_instance['title'] ?? ''),
'count' => max(1, min(20, (int) ($new_instance['count'] ?? 5))),
'plate' => max(0, (int) ($new_instance['plate'] ?? 0)),
'style' => $style,
'paginate' => in_array(sanitize_key((string) ($new_instance['paginate'] ?? 'ajax')), ['none', 'ajax', 'number'], true) ? sanitize_key((string) ($new_instance['paginate'] ?? 'ajax')) : 'ajax',
'in_affix' => !empty($new_instance['in_affix']) ? 1 : 0,
];
]);
}
}
+8 -3
View File
@@ -7,9 +7,13 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class WidgetForumTabTopics extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
@@ -165,19 +169,20 @@ class WidgetForumTabTopics extends \WP_Widget {
</label>
</p>
<?php
}
$this->render_common_form_section($instance);
}
public function update($new_instance, $old_instance): array {
$style = sanitize_key((string) ($new_instance['style'] ?? 'mini'));
if (!in_array($style, ['mini', 'rank'], true)) {
$style = 'mini';
}
return [
$business = [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'count' => max(1, min(20, (int) ($new_instance['count'] ?? 6))),
'style' => $style,
'plate' => max(0, (int) ($new_instance['plate'] ?? 0)),
'tabs' => sanitize_textarea_field((string) ($new_instance['tabs'] ?? '')),
];
]; return $this->update_common_fields($new_instance, $old_instance, $business);
}
}
+8 -3
View File
@@ -7,9 +7,13 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class WidgetForumTopicLists extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
@@ -271,10 +275,11 @@ class WidgetForumTopicLists extends \WP_Widget {
</label>
</p>
<?php
}
$this->render_common_form_section($instance);
}
public function update($new_instance, $old_instance): array {
return [
$business = [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'count' => max(1, min(30, (int) ($new_instance['count'] ?? 8))),
'orderby' => sanitize_key((string) ($new_instance['orderby'] ?? 'date')),
@@ -282,6 +287,6 @@ class WidgetForumTopicLists extends \WP_Widget {
'plate_id' => max(0, (int) ($new_instance['plate_id'] ?? 0)),
'current_plate' => !empty($new_instance['current_plate']) ? 1 : 0,
'paginate' => in_array(sanitize_key((string) ($new_instance['paginate'] ?? 'ajax')), ['none', 'ajax', 'number'], true) ? sanitize_key((string) ($new_instance['paginate'] ?? 'ajax')) : 'ajax',
];
]; return $this->update_common_fields($new_instance, $old_instance, $business);
}
}
+8 -3
View File
@@ -7,9 +7,13 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class WidgetForumTopicTerms extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
@@ -153,20 +157,21 @@ class WidgetForumTopicTerms extends \WP_Widget {
<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>
</p>
<?php
}
$this->render_common_form_section($instance);
}
public function update($new_instance, $old_instance): array {
$orderby = sanitize_key((string) ($new_instance['orderby'] ?? 'count'));
if (!in_array($orderby, ['count', 'name', 'views'], true)) {
$orderby = 'count';
}
return [
$business = [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'number' => max(1, min(40, (int) ($new_instance['number'] ?? 12))),
'orderby' => $orderby,
'style' => ($new_instance['style'] ?? '') === 'card' ? 'card' : 'mini',
'include' => sanitize_text_field((string) ($new_instance['include'] ?? '')),
'hide_empty' => !empty($new_instance['hide_empty']) ? 1 : 0,
];
]; return $this->update_common_fields($new_instance, $old_instance, $business);
}
}
+8 -3
View File
@@ -7,9 +7,13 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class WidgetHighlights extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
@@ -187,7 +191,8 @@ class WidgetHighlights extends \WP_Widget {
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('go_link')); ?>" value="1" <?php checked(!empty($instance['go_link'])); ?>> <?php esc_html_e('外链跳转页', 'aksrt'); ?></label>
</p>
<?php
}
$this->render_common_form_section($instance);
}
/**
* @param array<string,mixed> $new_instance
@@ -195,12 +200,12 @@ class WidgetHighlights extends \WP_Widget {
* @return array<string,mixed>
*/
public function update($new_instance, $old_instance): array {
return [
$business = [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'cards' => $this->normalize($new_instance['cards'] ?? []),
'go_link' => !empty($new_instance['go_link']) ? 1 : 0,
'obs_animation' => sanitize_key((string) ($new_instance['obs_animation'] ?? 'slideup')),
'animation_repeat' => !empty($new_instance['animation_repeat']) ? 1 : 0,
];
]; return $this->update_common_fields($new_instance, $old_instance, $business);
}
}
+8 -3
View File
@@ -7,9 +7,13 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class WidgetIconCoverCards extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
@@ -183,7 +187,8 @@ class WidgetIconCoverCards extends \WP_Widget {
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('go_link')); ?>" value="1" <?php checked(!empty($instance['go_link'])); ?>> <?php esc_html_e('外链跳转页', 'aksrt'); ?></label>
</p>
<?php
}
$this->render_common_form_section($instance);
}
/**
* @param array<string,mixed> $new_instance
@@ -191,7 +196,7 @@ class WidgetIconCoverCards extends \WP_Widget {
* @return array<string,mixed>
*/
public function update($new_instance, $old_instance): array {
return [
$business = [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'cards' => $this->normalize($new_instance['cards'] ?? []),
'pc_row' => max(1, min(4, (int) ($new_instance['pc_row'] ?? 2))),
@@ -200,6 +205,6 @@ class WidgetIconCoverCards extends \WP_Widget {
'icon_radius4' => !empty($new_instance['icon_radius4']) ? 1 : 0,
'show_widget_bg' => !empty($new_instance['show_widget_bg']) ? 1 : 0,
'go_link' => !empty($new_instance['go_link']) ? 1 : 0,
];
]; return $this->update_common_fields($new_instance, $old_instance, $business);
}
}
+8 -3
View File
@@ -7,9 +7,13 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class WidgetIframe extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
@@ -137,7 +141,8 @@ class WidgetIframe extends \WP_Widget {
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('allowfullscreen')); ?>" value="1" <?php checked(!empty($instance['allowfullscreen'])); ?>> <?php esc_html_e('允许全屏', 'aksrt'); ?></label>
</p>
<?php
}
$this->render_common_form_section($instance);
}
/**
* @param array<string,mixed> $new_instance
@@ -145,11 +150,11 @@ class WidgetIframe extends \WP_Widget {
* @return array<string,mixed>
*/
public function update($new_instance, $old_instance): array {
return [
$business = [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'url' => trim((string) ($new_instance['url'] ?? '')),
'aspect' => max(20, min(300, (int) ($new_instance['aspect'] ?? 55))),
'allowfullscreen' => !empty($new_instance['allowfullscreen']) ? 1 : 0,
];
]; return $this->update_common_fields($new_instance, $old_instance, $business);
}
}
+8 -3
View File
@@ -7,9 +7,13 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class WidgetLangSwitch extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
@@ -101,7 +105,8 @@ class WidgetLangSwitch extends \WP_Widget {
</p>
<p><small><?php esc_html_e('需要安装 Polylang 或 WPML 等多语言插件才能生效。', 'aksrt'); ?></small></p>
<?php
}
$this->render_common_form_section($instance);
}
/**
* @param array<string,mixed> $new_instance
@@ -113,11 +118,11 @@ class WidgetLangSwitch extends \WP_Widget {
if (!in_array($style, ['dropdown', 'flags', 'inline'], true)) {
$style = 'dropdown';
}
return [
$business = [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'style' => $style,
'show_name' => !empty($new_instance['show_name']) ? 1 : 0,
'hide_current' => !empty($new_instance['hide_current']) ? 1 : 0,
];
]; return $this->update_common_fields($new_instance, $old_instance, $business);
}
}
+28 -19
View File
@@ -7,9 +7,12 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class WidgetMainPosts extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
@@ -32,6 +35,8 @@ class WidgetMainPosts extends \WP_Widget {
$orderby = sanitize_key((string) ($instance['orderby'] ?? 'date'));
$cat = (int) ($instance['cat'] ?? 0);
$limit_day = max(0, (int) ($instance['limit_day'] ?? 0));
// Zibll `order` parity: asc/desc. Default desc.
$order = strtoupper(sanitize_key((string) ($instance['order'] ?? 'desc'))) === 'ASC' ? 'ASC' : 'DESC';
$query = [
'post_type' => 'post',
@@ -50,37 +55,37 @@ class WidgetMainPosts extends \WP_Widget {
case 'views':
$query['meta_key'] = 'aksrt_post_views';
$query['orderby'] = 'meta_value_num';
$query['order'] = 'DESC';
$query['order'] = $order;
break;
case 'comments':
$query['orderby'] = 'comment_count';
$query['order'] = 'DESC';
$query['order'] = $order;
break;
case 'likes':
$query['meta_key'] = '_aksrt_like_count';
$query['orderby'] = 'meta_value_num';
$query['order'] = 'DESC';
$query['order'] = $order;
break;
case 'favorites':
$query['meta_key'] = '_aksrt_favorite_count';
$query['orderby'] = 'meta_value_num';
$query['order'] = 'DESC';
$query['order'] = $order;
break;
case 'downloads':
$query['meta_key'] = '_aksrt_res_download_count';
$query['orderby'] = 'meta_value_num';
$query['order'] = 'DESC';
$query['order'] = $order;
break;
case 'rand':
$query['orderby'] = 'rand';
break;
case 'modified':
$query['orderby'] = 'modified';
$query['order'] = 'DESC';
$query['order'] = $order;
break;
default:
$query['orderby'] = 'date';
$query['order'] = 'DESC';
$query['order'] = $order;
}
// Topics taxonomy (CSV term IDs).
@@ -246,6 +251,7 @@ class WidgetMainPosts extends \WP_Widget {
$base_qs = array_filter([
'count' => $count,
'orderby' => sanitize_key((string) ($instance['orderby'] ?? 'date')),
'order' => strtoupper(sanitize_key((string) ($instance['order'] ?? 'desc'))) === 'ASC' ? 'ASC' : 'DESC',
'cat' => (int) ($instance['cat'] ?? 0),
'limit_day' => (int) ($instance['limit_day'] ?? 0),
'topics' => (string) ($instance['topics'] ?? ''),
@@ -255,7 +261,7 @@ class WidgetMainPosts extends \WP_Widget {
'show_meta' => !isset($instance['show_meta']) || !empty($instance['show_meta']) ? 1 : 0,
'show_number' => !empty($instance['show_number']) ? 1 : 0,
'paginate' => $paginate,
'load_mode' => sanitize_key((string) ($new_instance['load_mode'] ?? 'detail')) === 'ajax' ? 'ajax' : 'detail',
'load_mode' => sanitize_key((string) ($instance['load_mode'] ?? 'detail')) === 'ajax' ? 'ajax' : 'detail',
], static function ($v) {
return $v !== '' && $v !== null;
});
@@ -311,6 +317,7 @@ class WidgetMainPosts extends \WP_Widget {
'page' => 1,
'count' => $count,
'orderby' => sanitize_key((string) ($instance['orderby'] ?? 'date')),
'order' => strtoupper(sanitize_key((string) ($instance['order'] ?? 'desc'))) === 'ASC' ? 'ASC' : 'DESC',
'cat' => (int) ($instance['cat'] ?? 0),
'limit_day' => (int) ($instance['limit_day'] ?? 0),
'topics' => (string) ($instance['topics'] ?? ''),
@@ -320,19 +327,17 @@ class WidgetMainPosts extends \WP_Widget {
'show_meta' => !isset($instance['show_meta']) || !empty($instance['show_meta']) ? 1 : 0,
'show_number' => !empty($instance['show_number']) ? 1 : 0,
'paginate' => $paginate,
'load_mode' => sanitize_key((string) ($new_instance['load_mode'] ?? 'detail')) === 'ajax' ? 'ajax' : 'detail',
'load_mode' => sanitize_key((string) ($instance['load_mode'] ?? 'detail')) === 'ajax' ? 'ajax' : 'detail',
], static function ($v) {
return $v !== '' && $v !== null;
}));
$in_affix = !empty($instance['in_affix']);
$affix = function_exists('aksrt_widget_affix_attrs') ? aksrt_widget_affix_attrs($instance) : ['class' => '', 'attr' => ''];
$wrap_cls = 'aksrt-main-posts style-' . $style . ' ajaxpager';
if ($style === 'mini') {
$wrap_cls .= ' zib-widget posts-mini-lists';
}
if ($in_affix) {
$wrap_cls .= ' is-affix';
}
echo '<div class="' . esc_attr($wrap_cls) . '" data-aksrt-ajaxpager data-aksrt-ajax-url="' . esc_url($rest) . '" data-load-mode="' . esc_attr($load_mode) . '" data-query="' . esc_attr($qs) . '"' . ($load_mode === 'ajax' ? ' data-loaded="0"' : ' data-loaded="1"') . ($in_affix ? ' data-affix="true"' : '') . '>';
$wrap_cls .= $affix['class'];
echo '<div class="' . esc_attr($wrap_cls) . '" data-aksrt-ajaxpager data-aksrt-ajax-url="' . esc_url($rest) . '" data-load-mode="' . esc_attr($load_mode) . '" data-query="' . esc_attr($qs) . '"' . ($load_mode === 'ajax' ? ' data-loaded="0"' : ' data-loaded="1"') . $affix['attr'] . '>';
if ($load_mode === 'ajax') {
// Skeleton first paint (zibll load_mode=ajax).
@@ -389,6 +394,10 @@ class WidgetMainPosts extends \WP_Widget {
<option value="downloads" <?php selected(($instance['orderby'] ?? ''), 'downloads'); ?>><?php esc_html_e('下载', 'aksrt'); ?></option>
<option value="rand" <?php selected(($instance['orderby'] ?? ''), 'rand'); ?>><?php esc_html_e('随机', 'aksrt'); ?></option>
</select>
<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>
</label>
</p>
<p>
@@ -439,9 +448,9 @@ class WidgetMainPosts extends \WP_Widget {
<p>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('show_thumb')); ?>" value="1" <?php checked(!isset($instance['show_thumb']) || !empty($instance['show_thumb'])); ?>> <?php esc_html_e('显示缩略图', 'aksrt'); ?></label><br>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('show_meta')); ?>" value="1" <?php checked(!isset($instance['show_meta']) || !empty($instance['show_meta'])); ?>> <?php esc_html_e('显示 meta', 'aksrt'); ?></label><br>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('show_number')); ?>" value="1" <?php checked(!empty($instance['show_number'])); ?>> <?php esc_html_e('显示序号', 'aksrt'); ?></label><br>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('in_affix')); ?>" value="1" <?php checked(!empty($instance['in_affix'])); ?>> <?php esc_html_e('侧栏随动', 'aksrt'); ?></label>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('show_number')); ?>" value="1" <?php checked(!empty($instance['show_number'])); ?>> <?php esc_html_e('显示序号', 'aksrt'); ?></label>
</p>
<?php $this->render_common_form_section($instance); ?>
<?php
}
@@ -458,11 +467,12 @@ class WidgetMainPosts extends \WP_Widget {
if (!in_array($paginate, ['none', 'ajax', 'number'], true)) {
$paginate = 'none';
}
return [
return $this->update_common_fields($new_instance, $old_instance, [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'count' => max(1, min(30, (int) ($new_instance['count'] ?? 6))),
'style' => $style,
'orderby' => $orderby,
'order' => strtoupper(sanitize_key((string) ($new_instance['order'] ?? 'desc'))) === 'ASC' ? 'ASC' : 'DESC',
'cat' => max(0, (int) ($new_instance['cat'] ?? 0)),
'limit_day' => max(0, (int) ($new_instance['limit_day'] ?? 0)),
'topics' => sanitize_text_field((string) ($new_instance['topics'] ?? '')),
@@ -472,7 +482,6 @@ class WidgetMainPosts extends \WP_Widget {
'show_thumb' => !empty($new_instance['show_thumb']) ? 1 : 0,
'show_meta' => !empty($new_instance['show_meta']) ? 1 : 0,
'show_number' => !empty($new_instance['show_number']) ? 1 : 0,
'in_affix' => !empty($new_instance['in_affix']) ? 1 : 0,
];
]);
}
}
+35 -7
View File
@@ -9,9 +9,13 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class WidgetOnelinePosts extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
@@ -33,6 +37,7 @@ class WidgetOnelinePosts extends \WP_Widget {
$orderby = sanitize_key((string) ($instance['orderby'] ?? 'date'));
$cat = (int) ($instance['cat'] ?? 0);
$limit_day = max(0, (int) ($instance['limit_day'] ?? 0));
$order = strtoupper(sanitize_key((string) ($instance['order'] ?? 'desc'))) === 'ASC' ? 'ASC' : 'DESC';
$query = [
'post_type' => 'post',
'post_status' => 'publish',
@@ -50,18 +55,32 @@ class WidgetOnelinePosts extends \WP_Widget {
case 'views':
$query['meta_key'] = 'aksrt_post_views';
$query['orderby'] = 'meta_value_num';
$query['order'] = 'DESC';
$query['order'] = $order;
break;
case 'comments':
$query['orderby'] = 'comment_count';
$query['order'] = 'DESC';
$query['order'] = $order;
break;
case 'likes':
$query['meta_key'] = '_aksrt_like_count';
$query['orderby'] = 'meta_value_num';
$query['order'] = $order;
break;
case 'favorites':
$query['meta_key'] = '_aksrt_favorite_count';
$query['orderby'] = 'meta_value_num';
$query['order'] = $order;
break;
case 'modified':
$query['orderby'] = 'modified';
$query['order'] = $order;
break;
case 'rand':
$query['orderby'] = 'rand';
break;
default:
$query['orderby'] = 'date';
$query['order'] = 'DESC';
$query['order'] = $order;
}
$topics_raw = trim((string) ($instance['topics'] ?? ''));
if ($topics_raw !== '' && taxonomy_exists('topics')) {
@@ -217,10 +236,17 @@ class WidgetOnelinePosts extends \WP_Widget {
<label><?php esc_html_e('排序', 'aksrt'); ?>
<select name="<?php echo esc_attr($this->get_field_name('orderby')); ?>">
<option value="date" <?php selected(($instance['orderby'] ?? 'date'), 'date'); ?>><?php esc_html_e('时间', 'aksrt'); ?></option>
<option value="modified" <?php selected(($instance['orderby'] ?? ''), 'modified'); ?>><?php esc_html_e('更新', 'aksrt'); ?></option>
<option value="views" <?php selected(($instance['orderby'] ?? ''), 'views'); ?>><?php esc_html_e('浏览', 'aksrt'); ?></option>
<option value="comments" <?php selected(($instance['orderby'] ?? ''), 'comments'); ?>><?php esc_html_e('评论', 'aksrt'); ?></option>
<option value="likes" <?php selected(($instance['orderby'] ?? ''), 'likes'); ?>><?php esc_html_e('点赞', 'aksrt'); ?></option>
<option value="favorites" <?php selected(($instance['orderby'] ?? ''), 'favorites'); ?>><?php esc_html_e('收藏', 'aksrt'); ?></option>
<option value="rand" <?php selected(($instance['orderby'] ?? ''), 'rand'); ?>><?php esc_html_e('随机', 'aksrt'); ?></option>
</select>
<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>
</label>
</p>
<p>
@@ -249,7 +275,8 @@ class WidgetOnelinePosts extends \WP_Widget {
</label>
</p>
<?php
}
$this->render_common_form_section($instance);
}
/**
* @param array<string,mixed> $new_instance
@@ -262,18 +289,19 @@ class WidgetOnelinePosts extends \WP_Widget {
$style = 'card';
}
$orderby = sanitize_key((string) ($new_instance['orderby'] ?? 'date'));
if (!in_array($orderby, ['date', 'views', 'comments', 'rand'], true)) {
if (!in_array($orderby, ['date', 'modified', 'views', 'comments', 'likes', 'favorites', 'rand'], true)) {
$orderby = 'date';
}
return [
$business = [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'count' => max(1, min(20, (int) ($new_instance['count'] ?? 8))),
'style' => $style,
'orderby' => $orderby,
'order' => strtoupper(sanitize_key((string) ($new_instance['order'] ?? 'desc'))) === 'ASC' ? 'ASC' : 'DESC',
'cat' => max(0, (int) ($new_instance['cat'] ?? 0)),
'limit_day' => max(0, (int) ($new_instance['limit_day'] ?? 0)),
'topics' => sanitize_text_field((string) ($new_instance['topics'] ?? '')),
'pay_type' => sanitize_key((string) ($new_instance['pay_type'] ?? '')),
];
]; return $this->update_common_fields($new_instance, $old_instance, $business);
}
}
+9 -8
View File
@@ -7,9 +7,12 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class WidgetPaidPosts extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
@@ -187,10 +190,10 @@ class WidgetPaidPosts extends \WP_Widget {
echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
$in_affix = !empty($instance['in_affix']);
$affix = function_exists('aksrt_widget_affix_attrs') ? aksrt_widget_affix_attrs($instance) : ['class' => '', 'attr' => ''];
$rest = rest_url('aksrt/v1/widgets/paid-posts');
$wrap = 'aksrt-paid-posts-wrap ajaxpager' . ($in_affix ? ' is-affix' : '');
echo '<div class="' . esc_attr($wrap) . '" data-aksrt-ajaxpager data-aksrt-ajax-url="' . esc_url($rest) . '"' . ($in_affix ? ' data-affix="true"' : '') . '>';
$wrap = 'aksrt-paid-posts-wrap ajaxpager' . $affix['class'];
echo '<div class="' . esc_attr($wrap) . '" data-aksrt-ajaxpager data-aksrt-ajax-url="' . esc_url($rest) . '"' . $affix['attr'] . '>';
echo $payload['html']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $payload['paginate_html']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo '<div class="post_ajax_loader" data-aksrt-ajax-loader data-aksrt-ajax-loader-hide hidden></div>';
@@ -248,9 +251,7 @@ class WidgetPaidPosts extends \WP_Widget {
<input class="tiny-text" type="number" min="0" name="<?php echo esc_attr($this->get_field_name('limit_day')); ?>" value="<?php echo esc_attr((string) ((int) ($instance['limit_day'] ?? 0))); ?>">
</label>
</p>
<p>
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('in_affix')); ?>" value="1" <?php checked(!empty($instance['in_affix'])); ?>> <?php esc_html_e('侧栏随动', 'aksrt'); ?></label>
</p>
<?php $this->render_common_form_section($instance); ?>
<?php
}
@@ -259,7 +260,7 @@ class WidgetPaidPosts extends \WP_Widget {
if (!in_array($orderby, ['date', 'views', 'price'], true)) {
$orderby = 'date';
}
return [
return $this->update_common_fields($new_instance, $old_instance, [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'count' => max(1, min(20, (int) ($new_instance['count'] ?? 6))),
'orderby' => $orderby,
@@ -267,6 +268,6 @@ class WidgetPaidPosts extends \WP_Widget {
'cat' => max(0, (int) ($new_instance['cat'] ?? 0)),
'limit_day' => max(0, (int) ($new_instance['limit_day'] ?? 0)),
'paginate' => in_array(sanitize_key((string) ($new_instance['paginate'] ?? 'ajax')), ['none', 'ajax', 'number'], true) ? sanitize_key((string) ($new_instance['paginate'] ?? 'ajax')) : 'ajax',
];
]);
}
}
+8 -3
View File
@@ -7,9 +7,13 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class WidgetPartnersScroll extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
@@ -326,10 +330,11 @@ class WidgetPartnersScroll extends \WP_Widget {
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('show_widget_bg')); ?>" value="1" <?php checked(!empty($instance['show_widget_bg']) || !isset($instance['show_widget_bg'])); ?>> <?php esc_html_e('显示背景盒子', 'aksrt'); ?></label>
</p>
<?php
}
$this->render_common_form_section($instance);
}
public function update($new_instance, $old_instance): array {
return [
$business = [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'link_type' => in_array(($new_instance['link_type'] ?? ''), ['manual', 'link'], true) ? $new_instance['link_type'] : 'manual',
'partners' => $this->normalize_manual($new_instance['partners'] ?? []),
@@ -350,6 +355,6 @@ class WidgetPartnersScroll extends \WP_Widget {
'end_fade' => !empty($new_instance['end_fade']) ? 1 : 0,
'go_link' => !empty($new_instance['go_link']) ? 1 : 0,
'show_widget_bg' => !empty($new_instance['show_widget_bg']) ? 1 : 0,
];
]; return $this->update_common_fields($new_instance, $old_instance, $business);
}
}
+7 -2
View File
@@ -7,9 +7,13 @@
namespace AKSRT\Widgets;
use AKSRT\Widgets\Traits\CommonWidgetFields;
defined('ABSPATH') || exit;
class WidgetPostPayBox extends \WP_Widget {
use CommonWidgetFields;
public function __construct() {
parent::__construct(
@@ -168,7 +172,8 @@ class WidgetPostPayBox extends \WP_Widget {
</label>
</p>
<?php
}
$this->render_common_form_section($instance);
}
public function update($new_instance, $old_instance): array {
$theme = sanitize_html_class((string) ($new_instance['theme'] ?? 'jb-red'));
@@ -176,6 +181,6 @@ class WidgetPostPayBox extends \WP_Widget {
if (!in_array($theme, $allowed, true)) {
$theme = 'jb-red';
}
return ['theme' => $theme];
$business = ['theme' => $theme]; return $this->update_common_fields($new_instance, $old_instance, $business);
}
}

Some files were not shown because too many files have changed in this diff Show More