feat(pages): 1:1 网址导航 page-links (zibll links.php)

Add WP bookmarks-based navigation page with multi-category sticky nav,
search-engine cover, front submit modal/AJAX, page meta, and styles.
This commit is contained in:
2026-07-18 21:02:23 +08:00
parent fc9278e0ab
commit c27a36faff
11 changed files with 1707 additions and 4 deletions
+459
View File
@@ -0,0 +1,459 @@
/**
* AKSRT 网址导航 — zibll links page shell/styles.
*
* @package AKSRT
*/
/* ---- cards ---- */
.links-cat-header {
padding: 0 15px 10px;
}
.links-box:not(.zib-widget):not(.links-style-simple):not(.links-style-image) {
margin: -5px;
display: flex;
flex-wrap: wrap;
}
.links-box .links-card {
padding: 10px 4px 10px 8px;
min-width: 0;
margin: 5px;
width: calc(50% - 10px);
box-sizing: border-box;
}
.links-style-bigcard {
display: flex;
flex-wrap: wrap;
}
.links-box .links-card.big-card {
padding: 20px 10px 15px;
text-align: center;
}
.links-box:not(.zib-widget) .links-card {
background: var(--main-bg-color, var(--surface-1, #fff));
}
.author-minicard.links-card {
border-radius: 8px;
overflow: hidden;
box-shadow: 0 1px 4px var(--main-shadow, rgba(0, 0, 0, 0.06));
transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.author-minicard.links-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 14px var(--main-shadow, rgba(0, 0, 0, 0.1));
}
.links-card a {
display: block;
color: inherit;
text-decoration: none;
}
.links-card .list-inline {
display: flex;
align-items: center;
gap: 10px;
list-style: none;
margin: 0;
padding: 0;
}
.links-card .list-inline > li {
margin: 0;
}
.link-img {
--this-size: 42px;
width: var(--this-size);
height: var(--this-size);
flex-shrink: 0;
}
.link-img img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 4px;
display: block;
}
.links-card.big-card .link-img {
--this-size: 58px;
margin: 0 auto;
}
.links-card.big-card .link-img img {
border-radius: 100px;
}
.links-style-image {
--link-img-size: 60px;
--this-margin: 10px;
display: flex;
flex-wrap: wrap;
}
.links-style-image .link-img.link-only-img {
margin: var(--this-margin);
--this-size: var(--link-img-size);
}
.links-style-image .link-img.link-only-img img {
border-radius: 8px;
}
/* ---- fixed category nav ---- */
.fixed-wrap .fixed-wrap-nav.zib-widget,
.link-nav-box .zib-widget {
padding: 8px 15px;
}
.fixed-wrap-nav .tab-nav-theme {
border-bottom: none;
padding: 0;
list-style: none;
margin: 0;
}
.link-nav-box .nav > li > a {
display: block;
padding: 6px;
color: var(--muted-2-color, #888);
text-decoration: none;
}
.link-nav-box .nav > li > a:hover,
.link-nav-box .nav > .active > a {
color: var(--focus-color, var(--color-primary, #f04494));
}
.link-nav-submit,
.link-cat-submit {
margin-top: 10px;
}
.link-cat-submit {
display: none;
}
.links-page-container .title-theme {
font-weight: 600;
font-size: 1.15em;
margin-bottom: 6px;
}
/* ---- search cover ---- */
.link-page-search-cover.page-cover {
position: relative;
overflow: hidden;
padding-bottom: calc(25% + 100px);
margin-bottom: 20px;
}
.link-page-search-cover .fit-cover {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
.link-page-search-cover .header-slider-search {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: min(560px, 92%);
z-index: 2;
}
.link-page-search-cover .tab-nav-theme {
display: flex;
gap: 4px;
list-style: none;
padding: 0;
margin: 0 0 8px;
justify-content: center;
}
.link-page-search-cover .tab-nav-theme li a {
opacity: 0.7;
padding: 0 10px;
color: #fff;
text-decoration: none;
cursor: pointer;
}
.link-page-search-cover .tab-nav-theme li a:hover,
.link-page-search-cover .tab-nav-theme li.active a {
color: #fff;
opacity: 1;
font-weight: 600;
}
.link-page-search-cover .tab-pane {
display: none;
}
.link-page-search-cover .tab-pane.active,
.link-page-search-cover .tab-pane.in.active {
display: block;
}
.link-page-search-cover .line-form {
position: relative;
background: rgba(255, 255, 255, 0.92);
border-radius: 8px;
padding: 8px 12px;
}
.link-page-search-cover .line-form-input {
width: 100%;
border: 0;
background: transparent;
outline: none;
font-size: 15px;
padding: 8px 36px 8px 4px;
}
.link-page-search-cover .abs-right {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
}
.link-page-search-cover .abs-right button {
border: 0;
background: transparent;
cursor: pointer;
color: var(--muted-2-color, #666);
}
.link-page-search-cover .header-slider-search-more {
color: #fff;
text-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);
margin-bottom: 8px;
}
/* ---- submit modal ---- */
#submit-links-modal.modal {
position: fixed;
inset: 0;
z-index: 1050;
display: none;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.45);
padding: 16px;
}
#submit-links-modal.modal.is-open,
#submit-links-modal.modal.show {
display: flex;
}
#submit-links-modal .modal-dialog {
width: min(520px, 100%);
max-height: 90vh;
overflow: auto;
}
#submit-links-modal .modal-content {
background: var(--main-bg-color, #fff);
border-radius: 10px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.18);
}
#submit-links-modal .modal-body {
padding: 20px;
}
#submit-links-modal .close {
float: right;
border: 0;
background: transparent;
cursor: pointer;
font-size: 18px;
}
#submit-links-modal .preview-square {
width: 72px;
height: 72px;
border: 1px dashed var(--main-border-color, #ddd);
border-radius: 8px;
overflow: hidden;
display: inline-block;
}
#submit-links-modal .preview-square img {
width: 100%;
height: 100%;
object-fit: cover;
}
#submit-links-modal .hide {
display: none !important;
}
#submit-links-modal .form-control {
width: 100%;
padding: 8px 10px;
border: 1px solid var(--main-border-color, #ddd);
border-radius: 6px;
box-sizing: border-box;
}
#submit-links-modal .row.gutters-5 {
display: flex;
flex-wrap: wrap;
margin: 0 -5px;
}
#submit-links-modal .col-sm-6,
#submit-links-modal .col-sm-12 {
padding: 0 5px;
box-sizing: border-box;
width: 100%;
}
@media (min-width: 576px) {
#submit-links-modal .col-sm-6 {
width: 50%;
}
}
/* ---- responsive grid ---- */
@media (min-width: 992px) {
.links-box .links-card {
width: calc(25% - 10px);
}
.site-layout-1 .links-box .links-card,
.fluid-widget-wrap .links-box .links-card {
width: calc(20% - 10px);
}
.fluid-widget-wrap .links-box.links-style-bigcard .links-card,
.site-layout-1 .links-box.links-style-bigcard .links-card {
width: calc(16.6665% - 10px);
}
.sidebar .links-box .links-card {
width: calc(50% - 10px);
}
.fixed-wrap {
position: relative;
--fixed-width: 120px;
}
.fixed-wrap > .fixed-wrap-nav {
position: absolute;
width: var(--fixed-width);
}
.fixed-wrap > .fixed-wrap-nav + .fixed-wrap-content {
margin-left: calc(var(--fixed-width) + 15px);
}
.fixed-wrap-nav.affix {
position: sticky;
top: 80px;
}
.fixed-wrap .tab-nav-theme.mini-scrollbar {
display: flex;
flex-direction: column;
overflow: auto;
max-height: 70vh;
text-align: center;
border-bottom: none;
padding: 0;
margin: 0;
}
.fixed-wrap .tab-nav-theme.mini-scrollbar > li > a {
padding: 6px;
margin: 1px 0;
display: block;
font-size: 16px;
color: var(--muted-2-color, #888);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.fixed-wrap .tab-nav-theme.mini-scrollbar > li > a:hover {
color: var(--main-color, var(--color-primary));
}
.fixed-wrap .tab-nav-theme.mini-scrollbar > li.active > a {
color: var(--focus-color, var(--color-primary));
font-weight: 500;
background: color-mix(in srgb, var(--focus-color, var(--color-primary)) 12%, transparent);
border-radius: 6px;
}
.link-nav-submit {
display: block;
}
}
@media (max-width: 991px) {
.link-nav-submit {
display: none;
}
.link-cat-submit {
display: block;
}
.link-nav-box > .zib-widget {
background: transparent;
box-shadow: none;
padding: 0;
margin: 0;
}
.link-nav-box .nav {
display: flex;
gap: 8px;
overflow-x: auto;
margin: 0 0 10px;
padding: 0;
list-style: none;
}
.link-nav-box .nav > li {
display: inline-block;
flex: 0 0 auto;
}
.link-nav-box .nav > li > a {
padding: 2px 6px;
font-size: 1.05em;
white-space: nowrap;
}
}
@media (max-width: 640px) {
.links-box .links-card {
width: calc(50% - 10px);
}
.link-page-search-cover .tab-nav-theme li a {
padding: 0 3px;
font-size: 14px;
}
.links-page-container .fixed-wrap {
overflow: hidden;
}
}
+221
View File
@@ -0,0 +1,221 @@
/**
* AKSRT 网址导航页 — search tabs, sticky cat nav, submit modal/AJAX.
*
* @package AKSRT
*/
(function () {
'use strict';
function qs(sel, root) {
return (root || document).querySelector(sel);
}
function qsa(sel, root) {
return Array.prototype.slice.call((root || document).querySelectorAll(sel));
}
function ajaxUrl() {
if (window.aksrtData && window.aksrtData.ajaxUrl) return window.aksrtData.ajaxUrl;
if (window.ajaxurl) return window.ajaxurl;
return '/wp-admin/admin-ajax.php';
}
function toast(msg, type) {
if (window.aksrt && typeof window.aksrt.toast === 'function') {
window.aksrt.toast(msg, type || 'info');
return;
}
if (window.notyf) {
try {
window.notyf(msg, type === 'danger' ? 'danger' : '');
return;
} catch (e) { /* ignore */ }
}
// eslint-disable-next-line no-alert
window.alert(msg);
}
/* ---------- search engine tabs ---------- */
function initSearchTabs() {
var cover = qs('.link-page-search-cover');
if (!cover) return;
qsa('.tab-nav-theme a[data-aksrt-tab-target]', cover).forEach(function (a) {
a.addEventListener('click', function (e) {
e.preventDefault();
var target = a.getAttribute('data-aksrt-tab-target');
if (!target) return;
qsa('.tab-nav-theme li', cover).forEach(function (li) {
li.classList.remove('active');
});
if (a.parentElement) a.parentElement.classList.add('active');
qsa('.tab-pane', cover).forEach(function (pane) {
pane.classList.remove('active', 'in');
});
var pane = qs(target, cover);
if (pane) pane.classList.add('active', 'in');
});
});
}
/* ---------- sticky category nav highlight ---------- */
function initCatNav() {
var wrap = qs('.links-page-container .fixed-wrap');
if (!wrap) return;
var nav = qs('.link-nav-box', wrap);
var links = qsa('.link-nav-box a[href^="#"]', wrap);
var sections = links
.map(function (a) {
var id = a.getAttribute('href');
return id ? document.getElementById(id.replace(/^#/, '')) : null;
})
.filter(Boolean);
if (!links.length || !sections.length) return;
links.forEach(function (a) {
a.addEventListener('click', function (e) {
var id = a.getAttribute('href');
var el = id ? document.getElementById(id.replace(/^#/, '')) : null;
if (!el) return;
e.preventDefault();
var top = el.getBoundingClientRect().top + window.pageYOffset - 90;
window.scrollTo({ top: top, behavior: 'smooth' });
});
});
function updateActive() {
var y = window.pageYOffset + 120;
var current = sections[0];
for (var i = 0; i < sections.length; i++) {
if (sections[i].offsetTop <= y) current = sections[i];
}
if (!current) return;
links.forEach(function (a) {
var li = a.parentElement;
if (!li) return;
if (a.getAttribute('href') === '#' + current.id) {
li.classList.add('active');
} else {
li.classList.remove('active');
}
});
if (nav) nav.classList.add('affix');
}
window.addEventListener('scroll', updateActive, { passive: true });
updateActive();
}
/* ---------- submit modal ---------- */
function initSubmitModal() {
var modal = qs('#submit-links-modal');
if (!modal) return;
function openModal(e) {
if (e) e.preventDefault();
modal.classList.add('is-open', 'show');
modal.setAttribute('aria-hidden', 'false');
document.documentElement.classList.add('aksrt-modal-open');
}
function closeModal(e) {
if (e) e.preventDefault();
modal.classList.remove('is-open', 'show');
modal.setAttribute('aria-hidden', 'true');
document.documentElement.classList.remove('aksrt-modal-open');
}
qsa('a[href="#submit-links-modal"], [data-toggle="modal"][href="#submit-links-modal"]').forEach(function (btn) {
btn.addEventListener('click', openModal);
});
qsa('[data-dismiss="modal"]', modal).forEach(function (btn) {
btn.addEventListener('click', closeModal);
});
modal.addEventListener('click', function (e) {
if (e.target === modal) closeModal(e);
});
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape' && modal.classList.contains('is-open')) closeModal(e);
});
var fileInput = qs('.aksrt-links-logo-input', modal);
var preview = qs('.aksrt-links-logo-preview', modal);
if (fileInput && preview) {
fileInput.addEventListener('change', function () {
var file = fileInput.files && fileInput.files[0];
if (!file) return;
var url = URL.createObjectURL(file);
preview.src = url;
});
}
var form = qs('.aksrt-links-submit-form', modal);
if (!form) return;
form.addEventListener('submit', function (e) {
e.preventDefault();
var btn = qs('.aksrt-links-submit-btn', form);
if (btn) {
btn.disabled = true;
btn.classList.add('loading');
}
var fd = new FormData(form);
if (!fd.get('action')) fd.set('action', 'aksrt_frontend_links_submit');
fetch(ajaxUrl(), {
method: 'POST',
body: fd,
credentials: 'same-origin',
})
.then(function (r) {
return r.json();
})
.then(function (data) {
if (!data || data.error) {
toast((data && data.msg) || '提交失败', 'danger');
return;
}
toast(data.msg || '提交成功', 'success');
if (data.reload) {
window.setTimeout(function () {
window.location.reload();
}, 600);
} else {
closeModal();
form.reset();
}
})
.catch(function () {
toast('网络错误,请稍后重试', 'danger');
})
.finally(function () {
if (btn) {
btn.disabled = false;
btn.classList.remove('loading');
}
});
});
}
function init() {
if (!document.body.classList.contains('page-template-templates-pages-page-links-php')
&& !qs('.page-template-links')
&& !qs('.links-page-container')
&& !qs('#submit-links-modal')) {
// Still bind if modal exists alone.
}
initSearchTabs();
initCatNav();
initSubmitModal();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
+6
View File
@@ -249,6 +249,12 @@ if (class_exists('\AKSRT\Core\CommentActions')) {
if (class_exists('\AKSRT\Core\GoRedirect')) {
\AKSRT\Core\GoRedirect::get_instance()->init();
}
if (class_exists('\AKSRT\Core\LinksSubmit')) {
\AKSRT\Core\LinksSubmit::get_instance()->init();
}
if (class_exists('\AKSRT\Core\LinksPageMeta')) {
\AKSRT\Core\LinksPageMeta::get_instance()->init();
}
if (class_exists('\AKSRT\Messages\SiteNotice')) {
\AKSRT\Messages\SiteNotice::get_instance()->init();
}
+1
View File
@@ -43,6 +43,7 @@ require_once AKSRT_DIR . '/inc/helpers/aksrt-coupon-order.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-credits.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-null.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-ajax-list.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-links.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-identity.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-rewards.php';
require_once AKSRT_DIR . '/inc/helpers/menu-decoration.php';
+3 -4
View File
@@ -7,12 +7,11 @@ defined('ABSPATH') || exit;
add_filter('show_admin_bar', '__return_false');
// ============================================================
// 隐藏不需要的默认菜单项
// WordPress 链接管理(网址导航 / 书签)
// zibll enables link_manager; AKSRT keeps it for page-links.
// ============================================================
add_action('admin_menu', function () {
remove_menu_page('link-manager.php');
});
add_filter('pre_option_link_manager_enabled', '__return_true');
// ============================================================
// Gravatar 头像源替换(4层拦截:get_avatar_url / pre_get_avatar_data / avatar_defaults / ob_start 兜底)
+486
View File
@@ -0,0 +1,486 @@
<?php
/**
* 网址导航 / 书签链接渲染(zibll pages/links.php + zib_links_box parity).
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
if (!function_exists('aksrt_go_link')) :
/**
* Wrap external URL with /go/ intermediate page when enabled.
*/
function aksrt_go_link(string $url, bool $force = false): string {
$url = esc_url_raw($url);
if ($url === '') {
return '';
}
if (!$force && class_exists('\AKSRT\Settings\Settings') && empty(\AKSRT\Settings\Settings::get_value('seo_go_link', true))) {
return $url;
}
if (class_exists('\AKSRT\Core\GoRedirect')) {
return \AKSRT\Core\GoRedirect::get_instance()->create_go_link($url);
}
return $url;
}
endif;
if (!function_exists('aksrt_get_page_links_settings')) :
/**
* Read page_links_* meta for a page.
*
* @return array<string,mixed>
*/
function aksrt_get_page_links_settings(int $post_id = 0): array {
if ($post_id <= 0) {
$post_id = (int) get_queried_object_id();
}
$cats = get_post_meta($post_id, 'page_links_category', true);
if (!is_array($cats)) {
$cats = $cats !== '' && $cats !== null ? array_filter(array_map('intval', (array) $cats)) : [];
} else {
$cats = array_values(array_filter(array_map('intval', $cats)));
}
$search_types = get_post_meta($post_id, 'page_links_search_types', true);
if (!is_array($search_types)) {
$search_types = $search_types ? array_values(array_filter(array_map('sanitize_key', (array) $search_types))) : [];
} else {
$search_types = array_values(array_filter(array_map('sanitize_key', $search_types)));
}
$submit_cats = get_post_meta($post_id, 'page_links_submit_cats', true);
if (!is_array($submit_cats)) {
$submit_cats = $submit_cats !== '' && $submit_cats !== null ? array_filter(array_map('intval', (array) $submit_cats)) : [];
} else {
$submit_cats = array_values(array_filter(array_map('intval', $submit_cats)));
}
$style = sanitize_key((string) get_post_meta($post_id, 'page_links_style', true));
if (!in_array($style, ['card', 'bigcard', 'image', 'simple'], true)) {
$style = 'card';
}
$orderby = sanitize_key((string) get_post_meta($post_id, 'page_links_orderby', true));
if (!in_array($orderby, ['name', 'updated', 'rating', 'rand', 'id', 'length'], true)) {
$orderby = 'name';
}
$order = strtoupper((string) get_post_meta($post_id, 'page_links_order', true));
if (!in_array($order, ['ASC', 'DESC'], true)) {
$order = 'ASC';
}
$position = sanitize_key((string) get_post_meta($post_id, 'page_links_content_position', true));
if (!in_array($position, ['top', 'bottom'], true)) {
$position = 'top';
}
return [
'content_s' => (bool) get_post_meta($post_id, 'page_links_content_s', true),
'content_position' => $position,
'search_s' => (bool) get_post_meta($post_id, 'page_links_search_s', true),
'search_types' => $search_types,
'category' => $cats,
'style' => $style,
'limit' => max(0, (int) get_post_meta($post_id, 'page_links_limit', true)),
'orderby' => $orderby,
'order' => $order,
'go_s' => (bool) get_post_meta($post_id, 'page_links_go_s', true),
'blank_s' => (bool) get_post_meta($post_id, 'page_links_blank_s', true),
// Default nofollow ON (zibll default true).
'nofollow_s' => get_post_meta($post_id, 'page_links_nofollow_s', true) === ''
? true
: (bool) get_post_meta($post_id, 'page_links_nofollow_s', true),
'submit_s' => (bool) get_post_meta($post_id, 'page_links_submit_s', true),
'submit_sign_s' => get_post_meta($post_id, 'page_links_submit_sign_s', true) === ''
? true
: (bool) get_post_meta($post_id, 'page_links_submit_sign_s', true),
'submit_cats' => $submit_cats,
'submit_title' => (string) (get_post_meta($post_id, 'page_links_submit_title', true) ?: __('申请入驻', 'aksrt')),
'submit_dec' => (string) get_post_meta($post_id, 'page_links_submit_dec', true),
];
}
endif;
if (!function_exists('aksrt_links_box')) :
/**
* Render bookmark cards (zibll zib_links_box).
*
* @param array<int,object|array> $links Bookmark rows from get_bookmarks().
*/
function aksrt_links_box($links = [], string $type = 'card', bool $nofollow = true, bool $go_link = false, bool $blank_s = false): string {
if (empty($links) || !is_array($links)) {
return '';
}
$type = in_array($type, ['card', 'bigcard', 'image', 'simple'], true) ? $type : 'card';
$placeholder = get_template_directory_uri() . '/assets/img/placeholder.svg';
$html = '';
$i = 0;
foreach ($links as $raw) {
$link = (array) $raw;
if (empty($link['href']) && !empty($link['link_url'])) {
$link['href'] = $link['link_url'];
}
if (empty($link['title']) && !empty($link['link_name'])) {
$link['title'] = $link['link_name'];
}
if (empty($link['src']) && !empty($link['link_image'])) {
$link['src'] = $link['link_image'];
}
if (empty($link['desc']) && !empty($link['link_description'])) {
$link['desc'] = $link['link_description'];
}
if ($blank_s) {
$link['blank'] = true;
}
if (empty($link['blank']) && !empty($link['link_target'])) {
$link['blank'] = true;
}
$href = !empty($link['href']) ? (string) $link['href'] : '';
$title = !empty($link['title']) ? (string) $link['title'] : '';
if ($href === '' || $title === '') {
continue;
}
if ($go_link || !empty($link['go_link'])) {
$href = aksrt_go_link($href, true);
} else {
$href = esc_url($href);
}
$src = !empty($link['src']) ? esc_url((string) $link['src']) : '';
$blank = !empty($link['blank']) ? ' target="_blank"' : '';
$dec = !empty($link['desc']) ? (string) $link['desc'] : '';
$rel = $nofollow ? ' rel="nofollow noopener noreferrer"' : ' rel="noopener noreferrer"';
$img_src = $src !== '' ? $src : $placeholder;
$img = '<img class="lazyload avatar" src="' . esc_url($placeholder) . '" data-src="' . esc_url($img_src) . '" alt="" loading="lazy">';
$data_dec = $dec !== ''
? ' title="' . esc_attr($title) . '" data-content="' . esc_attr($dec) . '"'
: ' data-content="' . esc_attr($title) . '"';
if ($type === 'bigcard') {
$html .= '<div class="author-minicard links-card radius8 big-card">';
$html .= '<a' . $blank . ' href="' . esc_url($href) . '" title="' . esc_attr($dec) . '"' . $rel . '>';
$html .= '<div class="avatar-img link-img">' . $img . '</div>';
$html .= '<div class="text-ellipsis mt20 font-bold">' . esc_html($title) . '</div>';
if ($dec !== '') {
$html .= '<dd class="mt6 avatar-dest em09 muted-3-color text-ellipsis">' . esc_html($dec) . '</dd>';
}
$html .= '</a></div>';
} elseif ($type === 'image') {
$html .= '<a' . $blank . ' class="avatar-img link-img link-only-img" data-trigger="hover" data-toggle="popover" data-placement="top"'
. $data_dec . ' href="' . esc_url($href) . '"' . $rel . '>' . $img . '</a>';
} elseif ($type === 'simple') {
$sc = $i === 0 ? '' : 'icon-spot';
$html .= '<a' . $blank . ' class="' . esc_attr($sc) . '" data-trigger="hover" data-toggle="popover" data-placement="top"'
. $data_dec . ' href="' . esc_url($href) . '"' . $rel . '>' . esc_html($title) . '</a>';
} else {
$html .= '<div class="author-minicard links-card radius8"><a' . $blank . ' href="' . esc_url($href) . '" title="' . esc_attr($dec) . '"' . $rel . '>';
$html .= '<ul class="list-inline">';
$html .= '<li><div class="avatar-img link-img">' . $img . '</div></li>';
$html .= '<li><dl><dt class="text-ellipsis">' . esc_html($title) . '</dt>';
if ($dec !== '') {
$html .= '<dd class="mt3 avatar-dest em09 muted-3-color text-ellipsis">' . esc_html($dec) . '</dd>';
}
$html .= '</dl></li></ul></a></div>';
}
$i++;
}
return $html;
}
endif;
if (!function_exists('aksrt_page_links_html')) :
/**
* Build the multi/single category links block for a page.
*/
function aksrt_page_links_html(int $post_id = 0): string {
$post_id = $post_id > 0 ? $post_id : (int) get_queried_object_id();
$s = aksrt_get_page_links_settings($post_id);
$cats = $s['category'];
if (empty($cats)) {
if (current_user_can('manage_links')) {
return '<div class="mb20"><a class="author-minicard links-card radius8" href="' . esc_url(admin_url('link-manager.php')) . '" target="_blank" rel="noopener">'
. esc_html__('添加链接', 'aksrt') . '</a></div>';
}
return '<div class="author-minicard links-card radius8 mb20">' . esc_html__('暂无链接', 'aksrt') . '</div>';
}
$limit = (int) $s['limit'];
$orderby = (string) $s['orderby'];
$order = (string) $s['order'];
$style = (string) $s['style'];
$nofollow = (bool) $s['nofollow_s'];
$go_s = (bool) $s['go_s'];
$blank_s = (bool) $s['blank_s'];
$submit_s = (bool) $s['submit_s'];
$submit_title = (string) $s['submit_title'];
$is_mobile = wp_is_mobile();
$links_con = '';
if (count($cats) > 1) {
$link_cat_con = '';
$link_cat_nav = '';
foreach ($cats as $cat_id) {
$args = [
'orderby' => $orderby,
'order' => $order,
'limit' => $limit > 0 ? $limit : -1,
'category' => (int) $cat_id,
];
$_link = aksrt_links_box(get_bookmarks($args), $style, $nofollow, $go_s, $blank_s);
if (!$_link) {
continue;
}
$term = get_term((int) $cat_id, 'link_category');
if (!$term || is_wp_error($term)) {
continue;
}
$_id = 'links-' . (int) $cat_id;
$link_cat_con .= '<div class="mb20 links-cat-box"><div class="links-cat-header"><div class="title-theme"><div id="' . esc_attr($_id) . '">'
. esc_html($term->name) . '</div></div></div><div class="links-box links-style-' . esc_attr($style) . '">' . $_link . '</div></div>';
$link_cat_nav .= '<li class="n-H2"><a class="text-ellipsis" href="#' . esc_attr($_id) . '">' . esc_html($term->name) . '</a></li>';
}
if ($link_cat_con === '') {
$links_con = '';
} else {
$submit_nav = $submit_s
? '<div class="link-nav-submit"><a class="padding-h10 hollow but c-theme btn-block text-ellipsis" href="#submit-links-modal" data-toggle="modal">'
. esc_html($submit_title) . '</a></div>'
: '';
$submit_con = $submit_s
? '<div class="link-cat-submit"><a class="padding-h10 hollow but c-theme btn-block text-ellipsis" href="#submit-links-modal" data-toggle="modal">'
. esc_html($submit_title) . '</a></div>'
: '';
$offset_top = $is_mobile ? '-8' : '';
$offset_bottom = $is_mobile ? '20' : '-30';
$links_con = '<div class="fixed-wrap">'
. '<div class="fixed-wrap-nav affix-header-sm affix link-nav-box tab-auto-center-sm" data-offset-top="' . esc_attr((string) $offset_top) . '" data-offset-bottom="' . esc_attr((string) $offset_bottom) . '">'
. '<div class="zib-widget"><ul class="nav list-inline scroll-x mini-scrollbar tab-nav-theme">' . $link_cat_nav . '</ul></div>'
. $submit_nav
. '</div><div class="fixed-wrap-content">' . $link_cat_con . $submit_con . '</div></div>';
}
} else {
$cat_id = (int) $cats[0];
$args = [
'orderby' => $orderby,
'order' => $order,
'limit' => $limit > 0 ? $limit : -1,
'category' => $cat_id,
];
$submit_con = $submit_s
? '<div class="mt20"><a class="padding-h10 hollow but c-theme btn-block text-ellipsis" href="#submit-links-modal" data-toggle="modal">'
. esc_html($submit_title) . '</a></div>'
: '';
$links_con = '<div class="links-box links-style-' . esc_attr($style) . '">'
. aksrt_links_box(get_bookmarks($args), $style, $nofollow, $go_s, $blank_s)
. '</div>' . $submit_con;
}
if ($links_con === '') {
if (current_user_can('manage_links')) {
return '<div class="mb20"><a class="author-minicard links-card radius8" href="' . esc_url(admin_url('link-manager.php')) . '" target="_blank" rel="noopener">'
. esc_html__('添加链接', 'aksrt') . '</a></div>';
}
return '<div class="author-minicard links-card radius8 mb20">' . esc_html__('暂无链接', 'aksrt') . '</div>';
}
return '<div class="links-page-container mb20">' . $links_con . '</div>';
}
endif;
if (!function_exists('aksrt_link_get_search_cover')) :
/**
* Search cover with multi-engine tabs (zibll zib_link_get_search_cover).
*/
function aksrt_link_get_search_cover(int $post_id = 0): string {
$post_id = $post_id > 0 ? $post_id : (int) get_queried_object_id();
$s = aksrt_get_page_links_settings($post_id);
if (empty($s['search_s']) || empty($s['search_types'])) {
return '';
}
$search_types_args = [
'self' => [
'name' => __('站内', 'aksrt'),
'input_name' => 's',
'url' => home_url('/'),
],
'baidu' => [
'name' => __('百度', 'aksrt'),
'input_name' => 'wd',
'url' => 'https://www.baidu.com/s',
],
'google' => [
'name' => __('谷歌', 'aksrt'),
'input_name' => 'q',
'url' => 'https://www.google.com/search',
],
'bing' => [
'name' => __('必应', 'aksrt'),
'input_name' => 'q',
'url' => 'https://cn.bing.com/search',
],
'sogou' => [
'name' => __('搜狗', 'aksrt'),
'input_name' => 'query',
'url' => 'https://www.sogou.com/web',
],
'360' => [
'name' => '360',
'input_name' => 'q',
'url' => 'https://www.so.com/s',
],
];
$type_nav = '';
$type_con = '';
$i = 0;
foreach ($s['search_types'] as $k) {
if (!isset($search_types_args[$k])) {
continue;
}
$cfg = $search_types_args[$k];
$active = $i === 0 ? ' active' : '';
$pane_id = 'link-page-' . $post_id . '-' . $k;
$type_nav .= '<li class="' . esc_attr(trim($active)) . '"><a href="javascript:;" data-aksrt-tab-target="#' . esc_attr($pane_id) . '">'
. esc_html($cfg['name']) . '</a></li>';
$type_con .= '<div class="tab-pane' . ($i === 0 ? ' in active' : '') . '" id="' . esc_attr($pane_id) . '">'
. '<form method="get" target="_blank" class="padding-10 search-form" action="' . esc_url($cfg['url']) . '">'
. '<div class="line-form blur-bg"><div class="search-input-text">'
. '<input type="text" name="' . esc_attr($cfg['input_name']) . '" class="line-form-input" tabindex="1" value="" autocomplete="off">'
. '<i class="line-form-line"></i>'
. '<div class="scale-placeholder">' . esc_html(sprintf(__('%s搜索', 'aksrt'), $cfg['name'])) . '</div>'
. '<div class="abs-right muted-color"><button type="submit" tabindex="2" class="null" aria-label="' . esc_attr__('搜索', 'aksrt') . '">'
. '<i class="fa fa-search" aria-hidden="true"></i></button></div>'
. '</div></div></form></div>';
$i++;
}
if ($type_nav === '') {
return '';
}
$title = get_the_title($post_id);
$img = '';
$thumb_id = get_post_thumbnail_id($post_id);
if ($thumb_id) {
$image = wp_get_attachment_image_src($thumb_id, 'full');
$img = !empty($image[0]) ? (string) $image[0] : '';
}
if ($img === '') {
$img = (string) get_post_meta($post_id, 'page_header_cover_img', true);
}
if ($img === '' && class_exists('\AKSRT\Settings\Settings')) {
$img = (string) \AKSRT\Settings\Settings::get_value('page_header_cover_img', '');
}
if ($img === '') {
$img = get_template_directory_uri() . '/assets/img/placeholder.svg';
}
$src = get_template_directory_uri() . '/assets/img/placeholder.svg';
$search = '<div class="header-slider-search abs-center"><div class="header-slider-search-more text-center before">'
. '<div class="em14 font-bold mb10">' . esc_html($title) . '</div></div>'
. '<div class="search-input"><div class="flex jc"><ul class="list-inline scroll-x mini-scrollbar tab-nav-theme">' . $type_nav . '</ul></div>'
. '<div class="tab-content">' . $type_con . '</div></div></div>';
return '<div class="page-cover theme-box radius8 main-shadow link-page-search-cover">'
. '<img class="fit-cover no-scale lazyload" src="' . esc_url($src) . '" data-src="' . esc_url($img) . '" alt="">'
. $search . '</div>';
}
endif;
if (!function_exists('aksrt_submit_links_modal')) :
/**
* Frontend submit modal markup (zibll zib_submit_links_modal).
*
* @param array{title?:string,dec?:string,sign?:bool,cats?:int[]} $args
*/
function aksrt_submit_links_modal(array $args = []): string {
$defaults = [
'title' => __('申请入驻', 'aksrt'),
'dec' => '',
'sign' => true,
'cats' => [],
];
$args = array_merge($defaults, $args);
$title = (string) $args['title'];
$header = $title !== ''
? '<div class="mb20"><button type="button" class="close" data-dismiss="modal" aria-label="' . esc_attr__('关闭', 'aksrt') . '">'
. '<i class="fa fa-times" aria-hidden="true"></i></button>'
. '<b class="modal-title flex ac"><span class="toggle-radius mr10 b-theme"><i class="fa fa-pencil-square-o" aria-hidden="true"></i></span>'
. esc_html($title) . '</b></div>'
: '';
$body = '';
if (!empty($args['dec'])) {
$body .= '<div class="muted-box em09">' . wp_kses_post((string) $args['dec']) . '</div>';
}
$need_login = !empty($args['sign']) && !is_user_logged_in();
if ($need_login) {
$sign_url = function_exists('aksrt_get_login_url')
? aksrt_get_login_url()
: (function_exists('aksrt_get_sign_page_url') ? aksrt_get_sign_page_url('login') : wp_login_url());
$body .= '<div class="muted-box text-center">';
$body .= '<div class="mb20 muted-3-color">' . esc_html__('请先登录', 'aksrt') . '</div>';
$body .= '<p><a href="' . esc_url($sign_url) . '" class="signin-loader but c-blue padding-lg">'
. '<i class="fa fa-fw fa-sign-in mr10" aria-hidden="true"></i>' . esc_html__('登录', 'aksrt') . '</a></p>';
if (class_exists('\AKSRT\Auth\OAuth') && method_exists(\AKSRT\Auth\OAuth::get_instance(), 'render_social_buttons')) {
$social = (string) \AKSRT\Auth\OAuth::get_instance()->render_social_buttons(__('使用以下方式快速登录', 'aksrt'));
if ($social !== '') {
$body .= '<div class="social_loginbar">' . $social . '</div>';
}
}
$body .= '</div>';
} else {
$term_args = [
'taxonomy' => 'link_category',
'hide_empty' => false,
];
if (!empty($args['cats']) && is_array($args['cats'])) {
$term_args['include'] = array_map('intval', $args['cats']);
$term_args['orderby'] = 'include';
}
$terms = get_terms($term_args);
$cats_options = '';
if (!is_wp_error($terms) && !empty($terms)) {
foreach ($terms as $item) {
$cats_options .= '<option value="' . (int) $item->term_id . '">' . esc_html($item->name) . '</option>';
}
}
$cats_field = $cats_options !== ''
? '<div class="col-sm-12 mb10"><div class="em09 muted-2-color mb6">' . esc_html__('网站类别', 'aksrt') . '</div>'
. '<div class="form-select"><select name="link_category" class="form-control">' . $cats_options . '</select></div></div>'
: '';
$placeholder_upload = get_template_directory_uri() . '/assets/img/placeholder.svg';
$body .= '<form class="form-horizontal mt10 form-upload aksrt-links-submit-form" enctype="multipart/form-data">';
$body .= '<div class="row gutters-5">';
$body .= '<div class="col-sm-6 mb10"><div class="em09 muted-2-color mb6">' . esc_html__('网站名称(必填)', 'aksrt') . '</div>'
. '<input type="text" class="form-control" name="link_name" required placeholder="' . esc_attr__('请输入网站名称', 'aksrt') . '"></div>';
$body .= '<div class="col-sm-6 mb10"><div class="em09 muted-2-color mb6">' . esc_html__('网站地址(必填)', 'aksrt') . '</div>'
. '<input type="url" class="form-control" name="link_url" required placeholder="https://..."></div>';
$body .= '<div class="col-sm-12 mb10"><div class="em09 muted-2-color mb6">' . esc_html__('网站简介', 'aksrt') . '</div>'
. '<input type="text" class="form-control" name="link_description" placeholder="' . esc_attr__('一句话介绍网站', 'aksrt') . '"></div>';
$body .= $cats_field;
$body .= '<div class="col-sm-12 mb10"><div class="em09 muted-2-color mb6">' . esc_html__('LOGO图像', 'aksrt') . '</div>'
. '<label class="pointer"><div class="preview preview-square"><img class="fit-cover aksrt-links-logo-preview" src="'
. esc_url($placeholder_upload) . '" alt=""></div>'
. '<input class="hide aksrt-links-logo-input" type="file" accept="image/gif,image/jpeg,image/jpg,image/png" name="image"></label>'
. '<div class="px12 muted-2-color mb6">' . esc_html__('请选择正方形 LOGO,支持 jpg/png/gif', 'aksrt') . '</div></div>';
$body .= '</div>';
$body .= '<div class="text-right edit-footer">'
. '<button type="submit" class="but c-blue padding-lg aksrt-links-submit-btn"><i class="fa fa-check" aria-hidden="true"></i>'
. esc_html__('确认提交', 'aksrt') . '</button></div>';
$body .= wp_nonce_field('aksrt_frontend_links_submit', '_wpnonce', false, false);
$body .= '<input type="hidden" name="action" value="aksrt_frontend_links_submit">';
$body .= '</form>';
}
return '<div class="modal fade" id="submit-links-modal" tabindex="-1" role="dialog" aria-hidden="true">'
. '<div class="modal-dialog" role="document"><div class="modal-content"><div class="modal-body">'
. $header . $body
. '</div></div></div></div>';
}
endif;
+302
View File
@@ -0,0 +1,302 @@
<?php
/**
* Page meta for 网址导航 template (page_links_* keys, zibll metabox parity).
*
* @package AKSRT
*/
namespace AKSRT\Core;
defined('ABSPATH') || exit;
class LinksPageMeta {
private static $instance = null;
public static function get_instance(): self {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
public function init(): void {
add_action('add_meta_boxes', [$this, 'register_meta_box']);
add_action('save_post_page', [$this, 'save_meta_box']);
}
public function register_meta_box(): void {
add_meta_box(
'aksrt_page_links_meta',
__('网址导航设置', 'aksrt'),
[$this, 'render_meta_box'],
'page',
'normal',
'high'
);
}
/**
* Whether current page uses the links template.
*/
private function is_links_template(int $post_id): bool {
$tpl = (string) get_post_meta($post_id, '_wp_page_template', true);
return $tpl === 'templates/pages/page-links.php';
}
public function render_meta_box(\WP_Post $post): void {
$post_id = (int) $post->ID;
if (!$this->is_links_template($post_id)) {
echo '<p class="description">' . esc_html__('将页面模板设为「AKSRT - 网址导航」后在此配置分类、样式与提交模块。', 'aksrt') . '</p>';
return;
}
$s = function_exists('aksrt_get_page_links_settings')
? aksrt_get_page_links_settings($post_id)
: [];
wp_nonce_field('aksrt_page_links_meta', 'aksrt_page_links_meta_nonce');
$terms = get_terms([
'taxonomy' => 'link_category',
'hide_empty' => false,
]);
if (is_wp_error($terms)) {
$terms = [];
}
$selected_cats = array_map('intval', (array) ($s['category'] ?? []));
$submit_cats = array_map('intval', (array) ($s['submit_cats'] ?? []));
$search_types = (array) ($s['search_types'] ?? []);
$engine_options = [
'self' => __('站内', 'aksrt'),
'baidu' => __('百度', 'aksrt'),
'google' => __('谷歌', 'aksrt'),
'bing' => __('必应', 'aksrt'),
'sogou' => __('搜狗', 'aksrt'),
'360' => '360',
];
?>
<table class="form-table aksrt-page-links-meta" role="presentation">
<tr>
<th scope="row"><?php esc_html_e('显示页面内容', 'aksrt'); ?></th>
<td>
<label>
<input type="checkbox" name="page_links_content_s" value="1" <?php checked(!empty($s['content_s'])); ?>>
<?php esc_html_e('在链接列表上下方展示页面正文', 'aksrt'); ?>
</label>
<p>
<label>
<input type="radio" name="page_links_content_position" value="top" <?php checked(($s['content_position'] ?? 'top'), 'top'); ?>>
<?php esc_html_e('链接列表上面', 'aksrt'); ?>
</label>
&nbsp;
<label>
<input type="radio" name="page_links_content_position" value="bottom" <?php checked(($s['content_position'] ?? ''), 'bottom'); ?>>
<?php esc_html_e('链接列表下面', 'aksrt'); ?>
</label>
</p>
</td>
</tr>
<tr>
<th scope="row"><?php esc_html_e('搜索引擎模块', 'aksrt'); ?></th>
<td>
<label>
<input type="checkbox" name="page_links_search_s" value="1" <?php checked(!empty($s['search_s'])); ?>>
<?php esc_html_e('显示搜索封面(使用特色图作背景)', 'aksrt'); ?>
</label>
<p class="description"><?php esc_html_e('选择引擎(可多选,按住 Ctrl)', 'aksrt'); ?></p>
<select name="page_links_search_types[]" multiple class="widefat" size="6">
<?php foreach ($engine_options as $key => $label) : ?>
<option value="<?php echo esc_attr($key); ?>" <?php selected(in_array($key, $search_types, true)); ?>>
<?php echo esc_html($label); ?>
</option>
<?php endforeach; ?>
</select>
</td>
</tr>
<tr>
<th scope="row"><?php esc_html_e('链接分类(必选)', 'aksrt'); ?></th>
<td>
<?php if (empty($terms)) : ?>
<p class="description">
<?php
printf(
/* translators: %s: link manager URL */
wp_kses_post(__('暂无链接分类。请先在 <a href="%s">链接管理</a> 中创建分类与链接。', 'aksrt')),
esc_url(admin_url('link-manager.php'))
);
?>
</p>
<?php else : ?>
<select name="page_links_category[]" multiple class="widefat" size="8">
<?php foreach ($terms as $term) : ?>
<option value="<?php echo (int) $term->term_id; ?>" <?php selected(in_array((int) $term->term_id, $selected_cats, true)); ?>>
<?php echo esc_html($term->name . ' (' . (int) $term->count . ')'); ?>
</option>
<?php endforeach; ?>
</select>
<p class="description"><?php esc_html_e('多选时按导航目录展示;一个都不选则不显示链接。', 'aksrt'); ?></p>
<?php endif; ?>
</td>
</tr>
<tr>
<th scope="row"><?php esc_html_e('显示样式', 'aksrt'); ?></th>
<td>
<?php
$style = (string) ($s['style'] ?? 'card');
$styles = [
'card' => __('图文列表', 'aksrt'),
'bigcard' => __('大卡片', 'aksrt'),
'image' => __('纯图片', 'aksrt'),
];
foreach ($styles as $key => $label) :
?>
<label style="margin-right:12px;">
<input type="radio" name="page_links_style" value="<?php echo esc_attr($key); ?>" <?php checked($style, $key); ?>>
<?php echo esc_html($label); ?>
</label>
<?php endforeach; ?>
</td>
</tr>
<tr>
<th scope="row"><?php esc_html_e('数量与排序', 'aksrt'); ?></th>
<td>
<label>
<?php esc_html_e('每类最多', 'aksrt'); ?>
<input type="number" name="page_links_limit" value="<?php echo esc_attr((string) (int) ($s['limit'] ?? 0)); ?>" min="0" step="1" class="small-text">
<?php esc_html_e('个(0=不限)', 'aksrt'); ?>
</label>
<p>
<select name="page_links_orderby">
<?php
$orderbys = [
'name' => __('名称', 'aksrt'),
'updated' => __('更新时间', 'aksrt'),
'rating' => __('评分', 'aksrt'),
'rand' => __('随机', 'aksrt'),
];
$orderby = (string) ($s['orderby'] ?? 'name');
foreach ($orderbys as $key => $label) :
?>
<option value="<?php echo esc_attr($key); ?>" <?php selected($orderby, $key); ?>><?php echo esc_html($label); ?></option>
<?php endforeach; ?>
</select>
<select name="page_links_order">
<option value="ASC" <?php selected(($s['order'] ?? 'ASC'), 'ASC'); ?>><?php esc_html_e('升序', 'aksrt'); ?></option>
<option value="DESC" <?php selected(($s['order'] ?? ''), 'DESC'); ?>><?php esc_html_e('降序', 'aksrt'); ?></option>
</select>
</p>
</td>
</tr>
<tr>
<th scope="row"><?php esc_html_e('链接行为', 'aksrt'); ?></th>
<td>
<label><input type="checkbox" name="page_links_go_s" value="1" <?php checked(!empty($s['go_s'])); ?>> <?php esc_html_e('外链重定向 /go/', 'aksrt'); ?></label><br>
<label><input type="checkbox" name="page_links_blank_s" value="1" <?php checked(!empty($s['blank_s'])); ?>> <?php esc_html_e('新标签打开', 'aksrt'); ?></label><br>
<label><input type="checkbox" name="page_links_nofollow_s" value="1" <?php checked(!empty($s['nofollow_s'])); ?>> <?php esc_html_e('添加 nofollow', 'aksrt'); ?></label>
</td>
</tr>
<tr>
<th scope="row"><?php esc_html_e('提交链接', 'aksrt'); ?></th>
<td>
<label><input type="checkbox" name="page_links_submit_s" value="1" <?php checked(!empty($s['submit_s'])); ?>> <?php esc_html_e('显示申请入驻', 'aksrt'); ?></label><br>
<label><input type="checkbox" name="page_links_submit_sign_s" value="1" <?php checked(!empty($s['submit_sign_s'])); ?>> <?php esc_html_e('登录后才能提交', 'aksrt'); ?></label>
<p>
<label><?php esc_html_e('按钮标题', 'aksrt'); ?>
<input type="text" class="regular-text" name="page_links_submit_title" value="<?php echo esc_attr((string) ($s['submit_title'] ?? '')); ?>">
</label>
</p>
<p>
<label for="page_links_submit_dec"><?php esc_html_e('提交说明(HTML)', 'aksrt'); ?></label><br>
<textarea class="large-text" rows="5" id="page_links_submit_dec" name="page_links_submit_dec"><?php echo esc_textarea((string) ($s['submit_dec'] ?? '')); ?></textarea>
</p>
<?php if (!empty($terms)) : ?>
<p class="description"><?php esc_html_e('提交时可选分类(空=全部分类)', 'aksrt'); ?></p>
<select name="page_links_submit_cats[]" multiple class="widefat" size="6">
<?php foreach ($terms as $term) : ?>
<option value="<?php echo (int) $term->term_id; ?>" <?php selected(in_array((int) $term->term_id, $submit_cats, true)); ?>>
<?php echo esc_html($term->name); ?>
</option>
<?php endforeach; ?>
</select>
<?php endif; ?>
</td>
</tr>
</table>
<?php
}
public function save_meta_box(int $post_id): void {
if (!isset($_POST['aksrt_page_links_meta_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash((string) $_POST['aksrt_page_links_meta_nonce'])), 'aksrt_page_links_meta')) {
return;
}
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
return;
}
if (!current_user_can('edit_post', $post_id)) {
return;
}
if (!$this->is_links_template($post_id) && empty($_POST['page_links_category']) && empty($_POST['page_links_submit_s'])) {
// Still allow save if user just switched template in same request.
$tpl = isset($_POST['page_template']) ? (string) wp_unslash($_POST['page_template']) : '';
if ($tpl !== 'templates/pages/page-links.php') {
return;
}
}
$bool_keys = [
'page_links_content_s',
'page_links_search_s',
'page_links_go_s',
'page_links_blank_s',
'page_links_nofollow_s',
'page_links_submit_s',
'page_links_submit_sign_s',
];
foreach ($bool_keys as $key) {
update_post_meta($post_id, $key, isset($_POST[$key]) ? 1 : 0);
}
$position = isset($_POST['page_links_content_position']) ? sanitize_key((string) wp_unslash($_POST['page_links_content_position'])) : 'top';
if (!in_array($position, ['top', 'bottom'], true)) {
$position = 'top';
}
update_post_meta($post_id, 'page_links_content_position', $position);
$style = isset($_POST['page_links_style']) ? sanitize_key((string) wp_unslash($_POST['page_links_style'])) : 'card';
if (!in_array($style, ['card', 'bigcard', 'image', 'simple'], true)) {
$style = 'card';
}
update_post_meta($post_id, 'page_links_style', $style);
$orderby = isset($_POST['page_links_orderby']) ? sanitize_key((string) wp_unslash($_POST['page_links_orderby'])) : 'name';
if (!in_array($orderby, ['name', 'updated', 'rating', 'rand', 'id', 'length'], true)) {
$orderby = 'name';
}
update_post_meta($post_id, 'page_links_orderby', $orderby);
$order = isset($_POST['page_links_order']) ? strtoupper(sanitize_text_field(wp_unslash((string) $_POST['page_links_order']))) : 'ASC';
update_post_meta($post_id, 'page_links_order', $order === 'DESC' ? 'DESC' : 'ASC');
$limit = isset($_POST['page_links_limit']) ? max(0, (int) $_POST['page_links_limit']) : 0;
update_post_meta($post_id, 'page_links_limit', $limit);
$cats = isset($_POST['page_links_category']) ? array_values(array_filter(array_map('intval', (array) wp_unslash($_POST['page_links_category'])))) : [];
update_post_meta($post_id, 'page_links_category', $cats);
$submit_cats = isset($_POST['page_links_submit_cats']) ? array_values(array_filter(array_map('intval', (array) wp_unslash($_POST['page_links_submit_cats'])))) : [];
update_post_meta($post_id, 'page_links_submit_cats', $submit_cats);
$engines = isset($_POST['page_links_search_types']) ? array_values(array_filter(array_map('sanitize_key', (array) wp_unslash($_POST['page_links_search_types'])))) : [];
$allowed = ['self', 'baidu', 'google', 'bing', 'sogou', '360'];
$engines = array_values(array_intersect($engines, $allowed));
update_post_meta($post_id, 'page_links_search_types', $engines);
$title = isset($_POST['page_links_submit_title']) ? sanitize_text_field(wp_unslash((string) $_POST['page_links_submit_title'])) : '';
update_post_meta($post_id, 'page_links_submit_title', $title);
$dec = isset($_POST['page_links_submit_dec']) ? wp_kses_post(wp_unslash((string) $_POST['page_links_submit_dec'])) : '';
update_post_meta($post_id, 'page_links_submit_dec', $dec);
}
}
+123
View File
@@ -0,0 +1,123 @@
<?php
/**
* Frontend links (bookmarks) submit — zibll frontend_links_submit parity.
*
* @package AKSRT
*/
namespace AKSRT\Core;
defined('ABSPATH') || exit;
class LinksSubmit {
private static $instance = null;
public static function get_instance(): self {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
public function init(): void {
add_action('wp_ajax_aksrt_frontend_links_submit', [$this, 'ajax_submit']);
add_action('wp_ajax_nopriv_aksrt_frontend_links_submit', [$this, 'ajax_submit']);
}
/**
* AJAX: create a pending WordPress bookmark (link).
*/
public function ajax_submit(): void {
if (!check_ajax_referer('aksrt_frontend_links_submit', '_wpnonce', false)) {
wp_send_json(['error' => 1, 'ys' => 'danger', 'msg' => __('安全校验失败,请刷新后重试', 'aksrt')]);
}
if (isset($_COOKIE['aksrt_links_submit_time'])) {
wp_send_json(['error' => 1, 'ys' => 'danger', 'msg' => __('操作过于频繁,请稍候再试', 'aksrt')]);
}
$link_name = isset($_POST['link_name']) ? sanitize_text_field(wp_unslash((string) $_POST['link_name'])) : '';
$link_url = isset($_POST['link_url']) ? esc_url_raw(wp_unslash((string) $_POST['link_url'])) : '';
$link_desc = isset($_POST['link_description']) ? sanitize_text_field(wp_unslash((string) $_POST['link_description'])) : '';
$cat_raw = isset($_POST['link_category']) ? wp_unslash($_POST['link_category']) : [];
if ($link_name === '') {
wp_send_json(['error' => 1, 'ys' => 'danger', 'msg' => __('请填写网站名称', 'aksrt')]);
}
if ($link_url === '' || !wp_http_validate_url($link_url)) {
wp_send_json(['error' => 1, 'ys' => 'danger', 'msg' => __('网站地址格式错误', 'aksrt')]);
}
// Optional logo upload.
$logo_url = '';
if (!empty($_FILES['image']['name'])) {
require_once ABSPATH . 'wp-admin/includes/file.php';
require_once ABSPATH . 'wp-admin/includes/media.php';
require_once ABSPATH . 'wp-admin/includes/image.php';
$attachment_id = media_handle_upload('image', 0);
if (is_wp_error($attachment_id)) {
wp_send_json(['error' => 1, 'ys' => 'danger', 'msg' => $attachment_id->get_error_message()]);
}
$src = wp_get_attachment_image_src((int) $attachment_id, 'medium');
$logo_url = !empty($src[0]) ? (string) $src[0] : (string) wp_get_attachment_url((int) $attachment_id);
}
$categories = [];
if (is_array($cat_raw)) {
$categories = array_values(array_filter(array_map('intval', $cat_raw)));
} elseif ($cat_raw !== '' && $cat_raw !== null) {
$categories = [absint($cat_raw)];
}
// Duplicate URL guard.
global $wpdb;
$exists = (int) $wpdb->get_var(
$wpdb->prepare(
"SELECT COUNT(1) FROM {$wpdb->links} WHERE link_url = %s",
$link_url
)
);
if ($exists > 0) {
wp_send_json(['error' => 1, 'ys' => 'danger', 'msg' => __('您的链接已提交,请勿重复提交', 'aksrt')]);
}
if (!function_exists('wp_insert_link')) {
require_once ABSPATH . 'wp-admin/includes/bookmark.php';
}
$linkdata = [
'link_name' => $link_name,
'link_url' => $link_url,
'link_description' => $link_desc,
'link_image' => $logo_url,
// Non-admins create invisible links pending review (zibll parity).
'link_visible' => current_user_can('manage_links') ? 'Y' : 'N',
'link_category' => $categories,
];
$links_id = wp_insert_link($linkdata, true);
if (is_wp_error($links_id)) {
wp_send_json(['error' => 1, 'ys' => 'danger', 'msg' => $links_id->get_error_message()]);
}
// 30s client throttle cookie.
if (!headers_sent()) {
setcookie('aksrt_links_submit_time', (string) time(), time() + 30, COOKIEPATH ?: '/', COOKIE_DOMAIN, is_ssl(), true);
}
$msg = $linkdata['link_visible'] === 'Y'
? __('提交成功', 'aksrt')
: __('提交成功,等待管理员处理', 'aksrt');
/**
* Fires after a frontend link submission is stored.
*
* @param array $linkdata Sanitized bookmark data.
* @param int $links_id New link ID.
*/
do_action('aksrt_frontend_links_submit_success', $linkdata, (int) $links_id);
wp_send_json(['error' => 0, 'msg' => $msg, 'reload' => true]);
}
}
+23
View File
@@ -381,6 +381,29 @@ trait ScriptsPart1b {
);
}
// 网址导航页(zibll links.php)
if (is_page_template('templates/pages/page-links.php')) {
$links_css = get_template_directory() . '/assets/css/routes/links.css';
if (file_exists($links_css)) {
wp_enqueue_style(
'aksrt-route-links',
$base_uri . '/assets/css/routes/links.css',
['aksrt-tokens'],
(string) filemtime($links_css)
);
}
$links_js = get_template_directory() . '/assets/js/links-page.js';
if (file_exists($links_js)) {
wp_enqueue_script(
'aksrt-links-page',
$base_uri . '/assets/js/links-page.js',
['jquery'],
(string) filemtime($links_js),
true
);
}
}
// 交易中心共享壳层(购物车 / 订单 / 收益)
$is_commerce_center = is_page_template('templates/pages/page-orders.php')
|| is_page_template('templates/pages/page-cart.php')
+1
View File
@@ -46,6 +46,7 @@ class Templates {
'templates/pages/page-submit.php' => __('AKSRT - 写文章/投稿', 'aksrt'),
'templates/pages/page-posts-navs.php' => __('AKSRT - 文章导航', 'aksrt'),
'templates/pages/page-friends.php' => __('AKSRT - 友情链接', 'aksrt'),
'templates/pages/page-links.php' => __('AKSRT - 网址导航', 'aksrt'),
'templates/pages/page-orders.php' => __('AKSRT - 订单中心', 'aksrt'),
'templates/pages/page-revenue.php' => __('AKSRT - 收益中心', 'aksrt'),
'templates/pages/page-cart.php' => __('AKSRT - 购物车', 'aksrt'),
+82
View File
@@ -0,0 +1,82 @@
<?php
/**
* Template Name: AKSRT - 网址导航
* Template Post Type: page
*
* Zibll pages/links.php 1:1 — multi-category bookmarks, search cover, submit modal.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
$post_id = (int) get_queried_object_id();
$s = function_exists('aksrt_get_page_links_settings')
? aksrt_get_page_links_settings($post_id)
: [];
get_header();
?>
<main class="container page-content-default page-template-links">
<div class="content-wrap">
<div class="content-layout">
<?php
while (have_posts()) :
the_post();
// Optional page header (style 1 / 2 / 3 via existing page meta).
if (function_exists('aksrt_get_page_header_html')) {
echo aksrt_get_page_header_html($post_id); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
} else {
$header_style = (string) get_post_meta($post_id, 'page_header_style', true);
if ($header_style !== 'not') {
echo '<div class="theme-box radius8 main-bg main-shadow box-body mb20"><h1 class="article-title">' . esc_html(get_the_title()) . '</h1></div>';
}
}
if (function_exists('aksrt_link_get_search_cover')) {
echo aksrt_link_get_search_cover($post_id); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
$links_html = function_exists('aksrt_page_links_html') ? aksrt_page_links_html($post_id) : '';
$content_s = !empty($s['content_s']);
$position = (string) ($s['content_position'] ?? 'top');
if ($position !== 'top') {
echo $links_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
if ($content_s) {
echo '<div class="zib-widget"><article class="article wp-posts-content">';
the_content();
wp_link_pages([
'before' => '<p class="text-center post-nav-links radius8 padding-6">',
'after' => '</p>',
]);
echo '</article></div>';
}
if ($position === 'top') {
echo $links_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
endwhile;
if (comments_open() || get_comments_number()) {
comments_template();
}
?>
</div>
</div>
<?php get_sidebar(); ?>
</main>
<?php
if (!empty($s['submit_s']) && function_exists('aksrt_submit_links_modal')) {
echo aksrt_submit_links_modal([ // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
'title' => (string) ($s['submit_title'] ?? __('申请入驻', 'aksrt')),
'dec' => (string) ($s['submit_dec'] ?? ''),
'sign' => !empty($s['submit_sign_s']),
'cats' => (array) ($s['submit_cats'] ?? []),
]);
}
get_footer();