进行了一些小修复

This commit is contained in:
2026-08-26 16:32:18 +08:00
parent 992af2119d
commit 2cb78edabb
26 changed files with 2091 additions and 2383 deletions
+1
View File
@@ -16,6 +16,7 @@
!/tests/fixtures/page-visual-matrix-v1.json
!/tests/widget-system/
/tests/widget-system/*
!/tests/widget-system/post-editor-reference-contract.test.cjs
!/tests/widget-system/post-visual-contract.test.cjs
!/tests/widget-system/lazyload-contract.test.cjs
!/tests/widget-system/toast-contract.test.cjs
+162
View File
@@ -0,0 +1,162 @@
.aksrt-pay-meta {
margin: -6px -12px -12px;
}
.aksrt-pay-meta__field {
box-sizing: border-box;
display: grid;
grid-template-columns: 22% minmax(0, 1fr);
gap: 14px;
margin: 0;
padding: 16px 18px;
border-top: 1px solid #eee;
}
.aksrt-pay-meta__field:first-child {
border-top: 0;
}
.aksrt-pay-meta__field.compact {
padding-top: 8px;
}
.aksrt-pay-meta__title {
box-sizing: border-box;
margin: 0;
padding-top: 6px;
color: #0991f5;
font-size: 13px;
font-weight: 600;
line-height: 1.45;
text-align: right;
}
.aksrt-pay-meta__control,
.aksrt-pay-meta__control input[type="text"],
.aksrt-pay-meta__control input[type="url"],
.aksrt-pay-meta__control input[type="number"],
.aksrt-pay-meta__control textarea,
.aksrt-pay-meta__control select {
box-sizing: border-box;
min-width: 0;
width: 100%;
}
.aksrt-pay-meta__control .description {
margin: 6px 0 0;
color: #777;
font-size: 12px;
line-height: 1.55;
}
.aksrt-pay-meta__radios {
display: flex;
flex-wrap: wrap;
gap: 8px 18px;
padding-top: 5px;
}
.aksrt-pay-meta__radios .description {
flex-basis: 100%;
}
.aksrt-pay-meta__inline,
.aksrt-pay-meta__number,
.aksrt-pay-meta__upload,
.aksrt-pay-meta__buttons,
.aksrt-pay-meta__group--inline {
display: flex;
align-items: center;
gap: 7px;
}
.aksrt-pay-meta__inline input {
flex: 1;
}
.aksrt-pay-meta__inline select {
flex: 0 0 110px;
}
.aksrt-pay-meta__number input,
.aksrt-pay-meta__upload input {
flex: 1;
}
.aksrt-pay-meta__number > span {
flex: 0 0 auto;
color: #777;
}
.aksrt-pay-meta__upload {
flex-wrap: wrap;
}
.aksrt-pay-meta__upload .button-link-delete {
flex-basis: 100%;
text-align: left;
}
.aksrt-pay-meta__gallery {
display: grid;
grid-template-columns: repeat(6, minmax(0, 1fr));
gap: 7px;
margin-bottom: 8px;
}
.aksrt-pay-meta__gallery:empty {
display: none;
}
.aksrt-pay-meta__gallery img {
display: block;
width: 100%;
aspect-ratio: 1;
border-radius: 4px;
object-fit: cover;
}
.aksrt-pay-meta__group {
display: grid;
gap: 7px;
margin: 0 0 10px;
padding: 10px;
border: 1px solid #e3e5e8;
border-radius: 4px;
background: #f8f9fa;
}
.aksrt-pay-meta__group--inline {
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto;
}
.aksrt-pay-meta__group > .button-link-delete {
justify-self: start;
}
.aksrt-pay-meta__notice {
margin: 16px 18px;
padding: 12px 14px;
border-left: 4px solid #ffb900;
background: #fff8e5;
line-height: 1.7;
}
@media (max-width: 782px) {
.aksrt-pay-meta__field {
grid-template-columns: 1fr;
}
.aksrt-pay-meta__title {
padding-top: 0;
text-align: left;
}
.aksrt-pay-meta__gallery {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.aksrt-pay-meta__group--inline {
grid-template-columns: 1fr;
}
}
+159
View File
@@ -0,0 +1,159 @@
.aksrt-post-extension {
margin: -6px -12px -12px;
}
.aksrt-post-extension__field {
box-sizing: border-box;
display: grid;
grid-template-columns: 34% minmax(0, 1fr);
gap: 10px;
margin: 0;
padding: 14px 12px;
border-top: 1px solid #eee;
}
.aksrt-post-extension__field:first-child {
border-top: 0;
}
.aksrt-post-extension__title {
box-sizing: border-box;
margin: 0;
padding-top: 6px;
color: #0991f5;
font-size: 13px;
font-weight: 600;
line-height: 1.4;
text-align: right;
}
.aksrt-post-extension__control,
.aksrt-post-extension__control input[type="text"],
.aksrt-post-extension__control input[type="url"],
.aksrt-post-extension__control input[type="number"] {
box-sizing: border-box;
min-width: 0;
width: 100%;
}
.aksrt-post-extension__control input[type="text"],
.aksrt-post-extension__control input[type="url"],
.aksrt-post-extension__control input[type="number"] {
min-height: 30px;
line-height: 30px;
}
.aksrt-post-extension__control .description {
display: block;
margin: 5px 0 0;
color: #777;
font-size: 12px;
line-height: 1.55;
}
.aksrt-post-extension__danger {
color: #ff4646;
}
.aksrt-post-extension__upload {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
gap: 5px;
}
.aksrt-post-extension__upload .button-link-delete {
grid-column: 1 / -1;
justify-self: start;
}
.aksrt-post-extension__buttons {
display: flex;
align-items: center;
gap: 8px;
margin-top: 7px;
}
.aksrt-post-extension__gallery {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 5px;
}
.aksrt-post-extension__gallery:empty {
display: none;
}
.aksrt-post-extension__gallery img {
display: block;
width: 100%;
aspect-ratio: 1;
border-radius: 3px;
object-fit: cover;
}
.aksrt-post-extension__radios {
display: grid;
gap: 7px;
}
.aksrt-post-extension__radios label,
.aksrt-custom-filter-field label {
display: block;
margin: 0;
}
.aksrt-post-extension__field--check {
display: block;
padding-left: calc(34% + 22px);
}
.aksrt-post-extension__episode {
position: relative;
margin: 8px 0;
padding: 8px;
border: 1px solid #e5e5e5;
border-radius: 4px;
background: rgba(255, 255, 255, 0.45);
}
.aksrt-post-extension__episode > input {
margin-bottom: 5px;
}
.aksrt-post-extension__episode > .button-link-delete {
margin-top: 6px;
}
.aksrt-custom-filter-field {
padding: 9px 0;
border-bottom: 1px solid #eee;
}
.aksrt-custom-filter-field:last-child {
border-bottom: 0;
}
.aksrt-custom-filter-field strong {
display: block;
margin-bottom: 7px;
color: #0991f5;
}
body.block-editor-page #post_main .inside {
margin: 0;
}
@media (max-width: 782px) {
.aksrt-post-extension__field {
grid-template-columns: 1fr;
}
.aksrt-post-extension__title {
padding-top: 0;
text-align: left;
}
.aksrt-post-extension__field--check {
padding-left: 12px;
}
}
+355 -432
View File
@@ -1,299 +1,311 @@
/* Article compose / front submit page */
.aksrt-article-compose {
padding-top: 24px;
padding-bottom: 48px;
/* Front-end article editor — follows the reference theme's newposts page. */
.newposts-wrap {
display: flex;
align-items: flex-start;
gap: 15px;
width: 100%;
margin: 20px 0 30px;
}
.aksrt-article-compose__layout {
display: grid;
grid-template-columns: 1fr 320px;
gap: 20px;
align-items: start;
}
.aksrt-submit-main {
.newposts-wrap > .content-layout {
flex: 1 1 auto;
min-width: 0;
}
.aksrt-submit-sidebar {
display: flex;
flex-direction: column;
gap: 14px;
}
.aksrt-submit-panel {
background: var(--surface-1, #fff);
border-radius: var(--main-radius, 8px);
box-shadow: var(--shadow-sm, 0 1px 4px rgba(0, 0, 0, 0.06));
}
.aksrt-submit-panel--editor {
padding: 16px 18px 14px;
min-height: 60vh;
}
.aksrt-submit-panel__title {
margin: 0;
padding: 12px 16px;
font-size: 14px;
font-weight: 600;
border-bottom: 1px solid var(--color-line, #eceff4);
}
.aksrt-submit-panel__body {
padding: 14px 16px;
}
.aksrt-submit-title-wrap {
margin: 8px 0 12px;
}
.aksrt-submit-title {
width: 100%;
border: 0;
border-bottom: 1px solid var(--color-line, #e5e7eb);
font-size: 22px;
font-weight: 600;
line-height: 1.4;
resize: none;
outline: none;
background: transparent;
color: var(--color-ink, #1f2329);
padding: 8px 0;
}
.aksrt-submit-title:focus {
border-bottom-color: var(--color-primary, #3b82f6);
}
.aksrt-submit-editor-wrap .wp-editor-container {
border: none;
}
.aksrt-submit-editor-wrap .mce-toolbar-grp {
background: var(--surface-1, #fff);
border-bottom: 1px solid var(--color-line, #eceff4);
}
.aksrt-submit-shortcode-tools {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 10px;
}
.aksrt-submit-status-row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
margin-top: 12px;
font-size: 13px;
}
.aksrt-submit-nocan {
min-height: 40vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: var(--color-ink-3, #8a909c);
text-align: center;
}
.aksrt-submit-featured {
margin-bottom: 16px;
border: 1px dashed var(--color-line, #d9dee7);
border-radius: 8px;
overflow: hidden;
background: var(--surface-2, #f7f8fa);
}
.aksrt-submit-featured__preview {
position: relative;
aspect-ratio: 2 / 1;
display: flex;
align-items: center;
justify-content: center;
background: var(--surface-2, #f3f4f6);
}
.aksrt-submit-featured__preview img {
width: 100%;
height: 100%;
object-fit: cover;
}
.aksrt-submit-featured__empty {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
color: var(--color-ink-3, #8a909c);
font-size: 13px;
}
.aksrt-submit-featured__badge {
position: absolute;
right: 10px;
bottom: 10px;
background: rgba(0, 0, 0, 0.55);
color: #fff;
border-radius: 999px;
padding: 4px 10px;
font-size: 12px;
}
.aksrt-submit-featured__actions {
display: flex;
flex-wrap: wrap;
gap: 8px;
padding: 10px 12px;
border-top: 1px solid var(--color-line, #eceff4);
background: #fff;
}
.aksrt-submit-drop {
border: 1px solid var(--color-line, #eceff4);
border-radius: 6px;
margin-bottom: 8px;
overflow: hidden;
}
.aksrt-submit-drop > summary {
list-style: none;
cursor: pointer;
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 12px;
font-size: 13px;
}
.aksrt-submit-drop > summary::-webkit-details-marker {
display: none;
}
.aksrt-submit-drop[open] > summary {
border-bottom: 1px solid var(--color-line, #eceff4);
background: var(--surface-2, #fafbfc);
}
.aksrt-submit-drop__body {
padding: 10px 12px;
max-height: 220px;
overflow: auto;
}
.aksrt-submit-check {
display: flex;
align-items: center;
gap: 8px;
padding: 4px 0;
font-size: 13px;
cursor: pointer;
font-weight: 400;
}
.aksrt-submit-tags {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.aksrt-submit-tag {
display: inline-flex;
cursor: pointer;
font-weight: 400;
}
.aksrt-submit-tag input {
display: none;
}
.aksrt-submit-tag span {
display: inline-block;
padding: 4px 10px;
border-radius: 999px;
background: var(--surface-2, #f3f4f6);
font-size: 12px;
}
.aksrt-submit-tag.is-active span,
.aksrt-submit-tag:has(input:checked) span {
background: #dbeafe;
color: #2563eb;
}
.aksrt-submit-field {
.newposts-wrap > .sidebar {
display: block;
margin-top: 12px;
flex: 0 0 300px;
width: 300px;
}
.aksrt-submit-field__label {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 13px;
font-weight: 600;
margin-bottom: 6px;
.newposts-wrap .zib-widget {
margin-bottom: 15px;
padding: 15px;
border-radius: var(--main-radius, 8px);
background: var(--main-bg-color, #fff);
box-shadow: 0 0 10px var(--main-shadow, rgba(116, 116, 116, 0.08));
}
.aksrt-submit-hint {
margin: 0 0 8px;
font-size: 12px;
color: var(--color-ink-3, #8a909c);
.editor-main-box {
overflow: hidden;
}
.aksrt-form-input {
width: 100%;
border: 1px solid var(--color-line, #e5e7eb);
border-radius: 6px;
padding: 8px 10px;
font-size: 13px;
background: #fff;
color: inherit;
box-sizing: border-box;
.new-title {
min-height: 48px;
padding: 8px 5px;
border: 0;
background: transparent;
box-shadow: none;
font-size: 20px;
font-weight: 700;
line-height: 1.5;
resize: none;
}
.aksrt-form-input:focus {
outline: none;
border-color: var(--color-primary, #3b82f6);
.new-title:focus {
outline: 0;
box-shadow: none;
}
.aksrt-submit-pay__row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
padding: 8px 0;
.newposts-title {
margin-bottom: 12px;
}
.aksrt-switch {
position: relative;
display: inline-flex;
cursor: pointer;
.newposts-editor .wp-editor-wrap,
.newposts-editor .wp-editor-container {
border: 0;
}
.aksrt-switch input {
.newposts-editor .mce-top-part.affix {
position: fixed;
}
.newposts-editor .mce-top-part.affix-bottom {
position: absolute;
opacity: 0;
width: 0;
height: 0;
}
.aksrt-switch__ui {
width: 40px;
height: 22px;
border-radius: 999px;
background: #d1d5db;
position: relative;
.newposts-editor .wp-editor-container {
overflow: hidden;
border-radius: 0;
}
.newposts-editor .mce-toolbar-grp,
.newposts-editor div.mce-toolbar-grp {
border-bottom: 0;
background: var(--main-bg-color, #fff);
}
.newposts-editor .mce-flow-layout {
overflow-x: auto;
padding: 0;
opacity: 0.8;
white-space: normal;
transition: opacity 0.2s;
}
.newposts-editor .mce-flow-layout:hover {
opacity: 0.9;
}
.newposts-editor div.mce-statusbar {
border-top: 1px solid var(--main-border-color, rgba(50, 50, 50, 0.08));
}
.newposts-editor .mce-toolbar .mce-btn-group .mce-btn,
.newposts-editor .qt-dfw {
margin: 4px;
border-color: transparent !important;
border-radius: 4px;
transition: 0.2s;
}
.aksrt-switch__ui::after {
content: "";
.newposts-editor .mce-toolbar .mce-btn-group .mce-btn:focus,
.newposts-editor .mce-toolbar .mce-btn-group .mce-btn:hover,
.newposts-editor .qt-dfw:focus,
.newposts-editor .qt-dfw:hover {
border-color: transparent;
background: var(--main-border-color, rgba(50, 50, 50, 0.08));
color: var(--focus-color, #f04494);
box-shadow: none;
}
.newposts-editor .mce-toolbar .mce-btn-group .mce-btn.mce-active,
.newposts-editor .mce-toolbar .mce-btn-group .mce-btn:active,
.newposts-editor .qt-dfw.active {
background: var(--main-border-color, rgba(50, 50, 50, 0.08));
box-shadow: inset 0 0 0 1px var(--focus-color, #f04494);
opacity: 1;
}
.newposts-editor-tools {
display: flex;
flex-wrap: wrap;
gap: 6px;
padding: 8px 0 0;
}
.newposts-editor-tools .but {
padding: 4px 9px;
font-size: 12px;
}
.featured-edit {
position: relative;
overflow: hidden;
border-radius: var(--main-radius, 8px);
}
.featured-edit .featured-preview:empty {
display: none;
}
.featured-edit .featured-preview > img,
.featured-edit .featured-video-placeholder {
display: block;
width: 100%;
aspect-ratio: 2 / 1;
object-fit: cover;
}
.featured-edit .featured-video-placeholder,
.featured-edit .featured-empty {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
gap: 8px;
color: var(--muted-2-color, #999);
}
.featured-edit .featured-type-badge {
position: absolute;
right: 12px;
bottom: 12px;
padding: 4px 8px;
border-radius: 30px;
background: rgba(0, 0, 0, 0.55);
color: #fff;
}
.featured-edit .btns-full {
height: 150px;
border: 2px dashed var(--main-border-color, rgba(50, 50, 50, 0.08));
border-radius: var(--main-radius, 8px);
background: var(--muted-border-color, rgba(50, 50, 50, 0.04));
}
.featured-edit .btns-full .but {
margin: 10px;
font-size: 1.15em;
}
.featured-loaded .btns-full {
position: absolute;
z-index: 11;
top: 0;
right: 0;
height: auto;
padding: 3px 5px;
border: 0;
border-radius: 0 0 0 22px;
background: rgba(0, 0, 0, 0.5);
}
.featured-loaded .btns-full .but {
margin: 4px;
padding: 5px 8px;
background: rgba(255, 255, 255, 0.12);
color: #fff;
font-size: 1em;
}
.featured-loaded.image .btn-video,
.featured-loaded.image .btn-slide,
.featured-loaded.slide .btn-video,
.featured-loaded.slide .btn-image,
.featured-loaded.video .btn-image,
.featured-loaded.video .btn-slide {
display: none;
}
.editor-main-box .featured-loaded {
margin: -15px -15px 20px;
border-radius: var(--main-radius, 8px) var(--main-radius, 8px) 0 0;
}
.term-select-box .drop-select {
position: relative;
}
.term-select-box .drop-btn {
width: 100%;
min-height: 38px;
border: 1px solid var(--main-border-color, rgba(50, 50, 50, 0.08));
background: var(--muted-bg-color, rgba(50, 50, 50, 0.03));
color: var(--main-color, #333);
text-align: left;
cursor: pointer;
}
.term-select-box .drop-btn .fa-angle-right {
transition: transform 0.2s;
}
.term-select-box .drop-select.open .drop-btn .fa-angle-right {
transform: rotate(90deg);
}
.term-select-box .dropdown-menu {
position: static;
display: none;
width: 100%;
margin: -4px 0 6px;
padding: 4px 0;
border: 1px solid var(--main-border-color, rgba(50, 50, 50, 0.08));
border-top: 0;
border-radius: 0 0 var(--main-radius, 8px) var(--main-radius, 8px);
background: var(--main-bg-color, #fff);
box-shadow: none;
}
.term-select-box .drop-select.open .dropdown-menu {
display: block;
}
.select-drop-box {
max-height: 260px;
overflow-y: auto;
}
.select-drop-box .aksrt-submit-check {
display: flex;
align-items: center;
gap: 7px;
padding: 5px 2px;
cursor: pointer;
}
.newpost-filter-tags {
display: flex;
flex-wrap: wrap;
gap: 5px;
}
.newpost-filter-tags .tag-list input {
position: absolute;
opacity: 0;
}
.newpost-filter-tags .tag-list.active .badg,
.newpost-filter-tags .tag-list:has(input:checked) .badg {
background: var(--focus-color, #f04494);
color: #fff;
}
.newpost-pay-box .padding-h10 {
padding: 10px 0;
}
.newpost-pay-box .border-bottom {
border-bottom: 1px solid var(--main-border-color, rgba(50, 50, 50, 0.08));
}
.newpost-pay-box .form-switch-label {
margin: 0;
cursor: pointer;
}
.newpost-pay-box .form-switch {
position: relative;
display: block;
width: 42px;
height: 22px;
border-radius: 30px;
background: #d9dde2;
transition: 0.2s;
}
.newpost-pay-box .form-switch::after {
position: absolute;
top: 2px;
left: 2px;
@@ -301,187 +313,98 @@
height: 18px;
border-radius: 50%;
background: #fff;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
content: "";
transition: 0.2s;
}
.aksrt-switch input:checked + .aksrt-switch__ui {
background: var(--color-primary, #3b82f6);
.newpost-pay-box input:checked + .form-switch {
background: var(--focus-color, #f04494);
}
.aksrt-switch input:checked + .aksrt-switch__ui::after {
transform: translateX(18px);
.newpost-pay-box input:checked + .form-switch::after {
transform: translateX(20px);
}
.aksrt-submit-segmented {
display: inline-flex;
gap: 4px;
}
.aksrt-submit-segmented .but.active {
background: var(--color-primary, #3b82f6);
color: #fff;
border-color: var(--color-primary, #3b82f6);
}
.aksrt-submit-pay-downloads__item {
display: grid;
gap: 6px;
margin-bottom: 10px;
padding-bottom: 10px;
border-bottom: 1px dashed var(--color-line, #eceff4);
}
.aksrt-submit-actions {
text-align: center;
}
.aksrt-submit-ready {
color: var(--color-ink-3, #8a909c);
letter-spacing: 0.08em;
font-size: 12px;
margin: 0 0 12px;
}
.aksrt-submit-actions__btns {
display: flex;
flex-wrap: wrap;
gap: 10px;
justify-content: center;
}
.aksrt-submit-actions__btns .but {
flex: 1 1 120px;
min-height: 40px;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
border-radius: 6px;
border: 0;
cursor: pointer;
font-size: 14px;
}
.aksrt-submit-actions__btns .jb-green {
background: linear-gradient(135deg, #34d399, #10b981);
color: #fff;
}
.aksrt-submit-actions__btns .jb-blue {
background: linear-gradient(135deg, #60a5fa, #3b82f6);
color: #fff;
}
.aksrt-submit-actions__btns .but:disabled {
opacity: 0.65;
cursor: wait;
}
.aksrt-submit-drafts__item {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
padding: 8px 0;
border-bottom: 1px solid var(--color-line, #f0f1f3);
}
.aksrt-submit-drafts__link {
flex: 1;
min-width: 0;
text-decoration: none;
color: inherit;
display: flex;
flex-direction: column;
gap: 2px;
}
.aksrt-submit-drafts__name {
font-size: 13px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.aksrt-submit-drafts__link time {
font-size: 11px;
color: var(--color-ink-3, #8a909c);
}
.aksrt-submit-drafts__delete {
border: 0;
background: transparent;
color: #ef4444;
cursor: pointer;
padding: 6px;
}
.aksrt-article-compose .but {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 6px 12px;
border-radius: 6px;
border: 1px solid var(--color-line, #e5e7eb);
background: #fff;
color: var(--color-ink-2, #444);
font-size: 12px;
cursor: pointer;
text-decoration: none;
}
.aksrt-article-compose .but.hollow {
background: transparent;
}
.aksrt-article-compose .but.c-blue {
color: #2563eb;
border-color: #bfdbfe;
}
.aksrt-article-compose .but.c-yellow {
color: #b45309;
border-color: #fde68a;
}
.aksrt-article-compose .but.c-red {
color: #dc2626;
border-color: #fecaca;
}
.aksrt-article-compose .but.cir {
width: 32px;
height: 32px;
border-radius: 50%;
justify-content: center;
.newpost-pay-price .line-form-input {
width: 130px;
padding: 0;
border: 0;
background: transparent;
box-shadow: none;
font-size: 2em;
}
.aksrt-article-compose .badg {
display: inline-block;
padding: 3px 8px;
border-radius: 4px;
background: var(--surface-2, #f3f4f6);
color: var(--color-ink-3, #8a909c);
font-size: 12px;
.pay-dawnload-edit .lists-box {
border-radius: var(--main-radius, 8px);
background: var(--muted-border-color, rgba(50, 50, 50, 0.04));
}
body.aksrt-hide-float #float-right-btns,
body.aksrt-hide-tabbar #footer-tabbar,
body.aksrt-hide-tabbar .footer-tabbar-placeholder {
.pay-dawnload-edit .lists-item {
gap: 5px;
padding: 6px 8px;
word-break: break-all;
}
.pay-dawnload-edit .lists-item + .lists-item {
border-top: 1px solid var(--main-border-color, rgba(50, 50, 50, 0.08));
}
.pay-dawnload-edit .lists-item .form-control {
min-width: 0;
padding: 5px 7px;
}
.newposts-wrap [hidden] {
display: none !important;
}
.newposts-wrap .but-average {
display: flex;
align-items: stretch;
gap: 10px;
}
.newposts-wrap .but-average > * {
flex: 1 1 0;
}
.newposts-wrap .new-posts-submit {
justify-content: center;
}
@media (max-width: 991px) {
.aksrt-article-compose__layout {
grid-template-columns: 1fr;
.newposts-wrap {
display: block;
float: none;
}
.aksrt-submit-sidebar {
order: 2;
.newposts-wrap > .sidebar {
width: 100%;
}
}
.aksrt-submit-main {
order: 1;
@media (max-width: 767px) {
.newposts-wrap {
margin-top: 10px;
}
.newposts-wrap .zib-widget {
border-radius: 0;
}
.editor-main-box .featured-loaded,
.editor-main-box .featured-loaded img {
border-radius: 0;
}
.newposts-editor .mce-toolbar .mce-btn-group .mce-btn,
.newposts-editor .qt-dfw {
margin: 2px;
}
.pay-dawnload-edit .lists-item {
align-items: stretch;
flex-direction: column;
}
}
+136
View File
@@ -0,0 +1,136 @@
(function () {
'use strict';
var root = document.querySelector('[data-aksrt-pay-meta]');
if (!root) return;
function selected(name) {
var input = root.querySelector('input[name="aksrt_pay[' + name + ']"]:checked');
return input ? input.value : '';
}
function syncDependencies() {
var type = selected('pay_type');
var modo = selected('pay_modo');
var enabled = type !== 'no';
var enabledBox = root.querySelector('[data-pay-enabled]');
if (enabledBox) enabledBox.hidden = !enabled;
root.querySelectorAll('[data-pay-type]').forEach(function (field) {
field.hidden = field.getAttribute('data-pay-type') !== type;
});
root.querySelectorAll('[data-pay-modo]').forEach(function (field) {
field.hidden = field.getAttribute('data-pay-modo') !== modo;
});
var expired = root.querySelector('[data-order-expired]');
var expiredFields = root.querySelector('[data-order-expired-fields]');
if (expiredFields) expiredFields.hidden = !expired || !expired.checked;
}
function openMedia(button) {
if (!window.wp || !wp.media) return;
var wrap = button.closest('.aksrt-pay-meta__upload');
var input = wrap && wrap.querySelector('[data-media-input]');
if (!input) return;
var library = button.getAttribute('data-library') || '';
var options = { title: library === 'video' ? '选择视频' : '选择文件', multiple: false };
if (library) options.library = { type: library };
var frame = wp.media(options);
frame.on('select', function () {
var item = frame.state().get('selection').first().toJSON();
input.value = item.url || '';
input.dispatchEvent(new Event('change', { bubbles: true }));
});
frame.open();
}
function renderGallery() {
if (!window.wp || !wp.media) return;
var input = root.querySelector('[data-gallery-input]');
var preview = root.querySelector('[data-gallery-preview]');
if (!input || !preview) return;
preview.innerHTML = '';
input.value.split(',').map(function (id) { return parseInt(id, 10); }).filter(Boolean).forEach(function (id) {
wp.media.attachment(id).fetch().then(function () {
var data = wp.media.attachment(id).toJSON();
var image = document.createElement('img');
image.src = (data.sizes && data.sizes.thumbnail ? data.sizes.thumbnail.url : data.url) || '';
image.alt = data.alt || '';
preview.appendChild(image);
});
});
}
function openGallery() {
if (!window.wp || !wp.media) return;
var input = root.querySelector('[data-gallery-input]');
if (!input) return;
var frame = wp.media({ title: '新增图片', library: { type: 'image' }, multiple: true });
frame.on('open', function () {
input.value.split(',').map(function (id) { return parseInt(id, 10); }).filter(Boolean).forEach(function (id) {
frame.state().get('selection').add(wp.media.attachment(id));
});
});
frame.on('select', function () {
input.value = frame.state().get('selection').map(function (item) { return item.id; }).join(',');
renderGallery();
});
frame.open();
}
function addGroup(button) {
var id = button.getAttribute('data-group-add');
var list = root.querySelector('[data-group-list="' + id + '"]');
var template = root.querySelector('[data-group-template="' + id + '"]');
if (!list || !template) return;
var index = list.querySelectorAll('[data-group-row]').length;
list.insertAdjacentHTML('beforeend', template.innerHTML.replaceAll('__INDEX__', String(index)));
}
root.addEventListener('click', function (event) {
var target = event.target;
var media = target.closest('[data-media-select]');
if (media) {
event.preventDefault();
openMedia(media);
return;
}
var clear = target.closest('[data-media-clear]');
if (clear) {
event.preventDefault();
var wrap = clear.closest('.aksrt-pay-meta__upload');
var input = wrap && wrap.querySelector('[data-media-input]');
if (input) input.value = '';
return;
}
if (target.closest('[data-gallery-select]')) {
event.preventDefault();
openGallery();
return;
}
if (target.closest('[data-gallery-clear]')) {
event.preventDefault();
var gallery = root.querySelector('[data-gallery-input]');
if (gallery) gallery.value = '';
renderGallery();
return;
}
var add = target.closest('[data-group-add]');
if (add) {
event.preventDefault();
addGroup(add);
return;
}
var remove = target.closest('[data-group-remove]');
if (remove) {
event.preventDefault();
var row = remove.closest('[data-group-row]');
if (row) row.remove();
}
});
root.addEventListener('change', syncDependencies);
renderGallery();
syncDependencies();
})();
+123
View File
@@ -0,0 +1,123 @@
(function ($) {
'use strict';
var root = document.querySelector('[data-aksrt-post-extension]');
if (!root || !window.wp || !wp.media) return;
function openMedia(button) {
var wrap = button.closest('.aksrt-post-extension__upload');
var input = wrap && wrap.querySelector('[data-media-input]');
if (!input) return;
var library = button.getAttribute('data-library') || 'image';
var frame = wp.media({
title: library === 'video' ? '选择视频' : '选择图像',
library: { type: library },
multiple: false
});
frame.on('select', function () {
var item = frame.state().get('selection').first().toJSON();
input.value = item.url || '';
input.dispatchEvent(new Event('change', { bubbles: true }));
});
frame.open();
}
function renderGallery() {
var input = root.querySelector('[data-gallery-input]');
var preview = root.querySelector('[data-gallery-preview]');
if (!input || !preview) return;
preview.innerHTML = '';
var ids = input.value.split(',').map(function (id) { return parseInt(id, 10); }).filter(Boolean);
ids.forEach(function (id) {
wp.media.attachment(id).fetch().then(function () {
var data = wp.media.attachment(id).toJSON();
var img = document.createElement('img');
img.src = (data.sizes && data.sizes.thumbnail ? data.sizes.thumbnail.url : data.url) || '';
img.alt = data.alt || '';
preview.appendChild(img);
});
});
}
function openGallery() {
var input = root.querySelector('[data-gallery-input]');
if (!input) return;
var frame = wp.media({ title: '添加图像', library: { type: 'image' }, multiple: true });
frame.on('open', function () {
input.value.split(',').map(function (id) { return parseInt(id, 10); }).filter(Boolean).forEach(function (id) {
frame.state().get('selection').add(wp.media.attachment(id));
});
});
frame.on('select', function () {
input.value = frame.state().get('selection').map(function (item) { return item.id; }).join(',');
renderGallery();
});
frame.open();
}
function syncVideoDependencies() {
var input = root.querySelector('input[name="aksrt_post_extension[featured_video]"]');
root.querySelectorAll('[data-video-dependent]').forEach(function (field) {
field.hidden = !input || !input.value.trim();
});
}
root.addEventListener('click', function (event) {
var mediaButton = event.target.closest('[data-media-select]');
if (mediaButton) {
event.preventDefault();
openMedia(mediaButton);
return;
}
var clear = event.target.closest('[data-media-clear]');
if (clear) {
event.preventDefault();
var wrap = clear.closest('.aksrt-post-extension__upload');
var input = wrap && wrap.querySelector('[data-media-input]');
if (input) {
input.value = '';
input.dispatchEvent(new Event('change', { bubbles: true }));
}
return;
}
if (event.target.closest('[data-gallery-select]')) {
event.preventDefault();
openGallery();
return;
}
if (event.target.closest('[data-gallery-clear]')) {
event.preventDefault();
var galleryInput = root.querySelector('[data-gallery-input]');
if (galleryInput) galleryInput.value = '';
renderGallery();
return;
}
if (event.target.closest('[data-episode-add]')) {
event.preventDefault();
var list = root.querySelector('[data-episode-list]');
var template = root.querySelector('[data-episode-template]');
if (!list || !template) return;
var index = list.querySelectorAll('[data-episode-row]').length;
list.insertAdjacentHTML('beforeend', template.innerHTML.replaceAll('__INDEX__', String(index)));
return;
}
var remove = event.target.closest('[data-episode-remove]');
if (remove) {
event.preventDefault();
var row = remove.closest('[data-episode-row]');
if (row) row.remove();
}
});
root.addEventListener('input', syncVideoDependencies);
root.addEventListener('change', syncVideoDependencies);
renderGallery();
syncVideoDependencies();
})(jQuery);
+27 -10
View File
@@ -110,6 +110,7 @@
this.bindLeaveWarning();
this.bindAi();
this.bindTagActive();
this.bindDropSelect();
this.loadFeaturedFromDom();
},
@@ -160,11 +161,12 @@
},
bindTagActive: function () {
qsa('.aksrt-submit-tag input').forEach(function (input) {
qsa('.aksrt-submit-tag input, .newpost-filter-tags input').forEach(function (input) {
var sync = function () {
var tag = input.closest('.aksrt-submit-tag');
var tag = input.closest('.aksrt-submit-tag, .tag-list');
if (tag) {
tag.classList.toggle('is-active', input.checked);
tag.classList.toggle('active', input.checked);
}
};
input.addEventListener('change', sync);
@@ -172,6 +174,18 @@
});
},
bindDropSelect: function () {
qsa('.term-select-box [data-toggle-class="open"]').forEach(function (button) {
button.addEventListener('click', function () {
var box = button.closest('.drop-select');
if (!box) return;
var open = !box.classList.contains('open');
box.classList.toggle('open', open);
button.setAttribute('aria-expanded', open ? 'true' : 'false');
});
});
},
bindSubmitButtons: function () {
var self = this;
qsa('.aksrt-submit-btn', this.form).forEach(function (btn) {
@@ -393,11 +407,7 @@
setFeatured: function (type, data) {
this.featured = { type: type || '', data: data || {} };
var typeInput = qs('[data-featured-type]');
var jsonInput = qs('[data-featured-json]');
if (typeInput) {
typeInput.value = type || '';
}
if (jsonInput) {
jsonInput.value = JSON.stringify(this.featured);
}
@@ -412,6 +422,13 @@
}
var t = this.featured.type;
var d = this.featured.data || {};
var box = qs('.featured-edit');
if (box) {
box.classList.remove('featured-loaded', 'image', 'slide', 'video');
if (t && t !== 'close') {
box.classList.add('featured-loaded', t);
}
}
var html = '';
if (t === 'image' && d.url) {
html = '<img src="' + d.url + '" alt="">';
@@ -419,15 +436,15 @@
html =
'<img src="' +
d[0].url +
'" alt=""><span class="aksrt-submit-featured__badge"><i class="fa fas fa-image"></i></span>';
'" alt=""><span class="featured-type-badge"><i class="fa fas fa-image"></i></span>';
} else if (t === 'video') {
html =
(d.pic
? '<img src="' + d.pic + '" alt="">'
: '<div class="aksrt-submit-featured__video-placeholder"><i class="fa fas fa-play"></i></div>') +
'<span class="aksrt-submit-featured__badge"><i class="fa fas fa-video"></i></span>';
: '<div class="featured-video-placeholder"><i class="fa fas fa-play"></i></div>') +
'<span class="featured-type-badge"><i class="fa fas fa-video"></i></span>';
} else {
html = '<div class="aksrt-submit-featured__empty"><i class="fa fas fa-image"></i><span>Cover</span></div>';
html = '<div class="featured-empty"><i class="fa fas fa-image"></i><span>设置文章封面</span></div>';
}
preview.innerHTML = html;
var clearBtn = qs('[data-featured-action="close"]');
+12 -37
View File
@@ -127,37 +127,16 @@ if (!function_exists('aksrt_resolve_single_post_display_settings')) :
$settings['authordesc'] = !empty($settings['authordesc_s']);
}
$meta = class_exists('\AKSRT\Core\PostMetaBox') ? \AKSRT\Core\PostMetaBox::get_meta($post->ID) : [];
foreach ([
'show_reading_progress' => 'reading_progress',
'enable_lightbox' => 'lightbox',
'show_related' => 'related_enabled',
'enable_like' => 'like_enabled',
'enable_favorite' => 'favorite_enabled',
'enable_share' => 'share_enabled',
'enable_print' => 'print_enabled',
'enable_pdf' => 'pdf_export_enabled',
] as $meta_key => $setting_key) {
if (($meta[$meta_key] ?? '') === '') {
continue;
}
$settings[$setting_key] = $meta[$meta_key] === '1';
}
if (!empty($meta['related_count'])) {
$settings['related_count'] = max(1, min(20, (int) $meta['related_count']));
}
// Per-post maxheight override (legacy article_maxheight_xz). Global may already set maxheight_enabled.
$meta_maxheight = (int) ($meta['maxheight'] ?? 0);
if ($meta_maxheight > 0) {
if (!empty($meta['article_maxheight_xz'])) {
$settings['maxheight_enabled'] = true;
$settings['maxheight'] = $meta_maxheight;
} elseif (empty($settings['maxheight_enabled'])) {
$settings['maxheight_enabled'] = false;
$settings['maxheight'] = max(0, (int) ($settings['maxheight'] ?? 0));
}
$cover_mode = (string) ($meta['header_image_mode'] ?? '');
$cover_mode = '';
$cover_url = '';
$cover_type = 'none';
// Check global cover toggles; per-post header_image_mode='none' overrides all.
// Cover availability follows the global article display settings.
$image_cover_on = !empty($settings['article_image_cover']);
$slide_cover_on = !empty($settings['article_slide_cover']);
$video_cover_on = !empty($settings['article_video_cover']);
@@ -174,16 +153,13 @@ if (!function_exists('aksrt_resolve_single_post_display_settings')) :
}
}
}
// 2. Slide cover — featured_slide URLs and/or cover_images (attachment IDs).
// 2. Slide cover — the reference editor stores attachment IDs.
$slide_images = [];
if ($slide_cover_on) {
$raw_slides = [];
if (!empty($meta['featured_slide'])) {
$raw_slides = array_merge($raw_slides, preg_split('/[,\s]+/', (string) $meta['featured_slide']) ?: []);
}
if (!empty($meta['cover_images'])) {
$raw_slides = array_merge($raw_slides, preg_split('/[,\s]+/', (string) $meta['cover_images']) ?: []);
}
foreach ($raw_slides as $token) {
$token = trim((string) $token);
if ($token === '') {
@@ -217,11 +193,7 @@ if (!function_exists('aksrt_resolve_single_post_display_settings')) :
$slide_images = [];
}
} elseif ($image_cover_on) {
if ($cover_mode === 'auto') {
$cover_url = function_exists('aksrt_get_first_content_image_url')
? (string) aksrt_get_first_content_image_url($post->ID)
: '';
}
$cover_url = esc_url_raw((string) ($meta['cover_image'] ?? ''));
if ($cover_url === '' && has_post_thumbnail($post)) {
$cover_url = (string) get_the_post_thumbnail_url($post, 'full');
}
@@ -238,6 +210,13 @@ if (!function_exists('aksrt_resolve_single_post_display_settings')) :
$settings['featured_video'] = (string) ($meta['featured_video'] ?? '');
// New route: episode list only.
$episodes = [];
$main_video = esc_url_raw((string) ($meta['featured_video'] ?? ''));
if ($main_video !== '') {
$episodes[] = [
'url' => $main_video,
'title' => sanitize_text_field((string) ($meta['featured_video_title'] ?? '')),
];
}
$raw_eps = $meta['featured_video_episode'] ?? [];
if (is_array($raw_eps)) {
foreach ($raw_eps as $row) {
@@ -265,10 +244,6 @@ if (($settings['cover_type'] ?? '') === 'video' && (string) ($settings['cover_ur
$settings['video_episodes'] = $episodes;
$settings['episode_count'] = count($episodes);
$settings['video_poster'] = (string) ($meta['cover_image'] ?? '');
$meta_copyright = trim((string) ($meta['copyright_text'] ?? ''));
if ($meta_copyright !== '') {
$settings['copyright_text'] = $meta_copyright;
}
return $settings;
}
endif;
+4 -4
View File
@@ -265,7 +265,7 @@ if (!function_exists('aksrt_resource_preview_images')) {
/**
* Collect preview images for resource cards and archive thumbnails.
*
* Priority: curated cover_images meta -> featured image -> gallery/content images.
* Priority: curated featured-slide images -> featured image -> gallery/content images.
*
* @param int|WP_Post|null $post
* @param int $limit
@@ -281,9 +281,9 @@ if (!function_exists('aksrt_resource_preview_images')) {
$images = [];
if (class_exists('\AKSRT\Core\PostMetaBox')) {
$cover_images = (string) \AKSRT\Core\PostMetaBox::get_meta_value($p->ID, 'cover_images', '');
if ($cover_images !== '') {
$cover_ids = array_filter(array_map('absint', preg_split('/[\s,]+/', $cover_images) ?: []));
$featured_slide = (string) \AKSRT\Core\PostMetaBox::get_meta_value($p->ID, 'featured_slide', '');
if ($featured_slide !== '') {
$cover_ids = array_filter(array_map('absint', preg_split('/[\s,]+/', $featured_slide) ?: []));
foreach ($cover_ids as $attachment_id) {
$url = (string) wp_get_attachment_image_url($attachment_id, 'medium_large');
if ($url !== '') {
+1 -4
View File
@@ -49,10 +49,7 @@ if (!function_exists('aksrt_renderer_collect_post_data')) {
$is_mult_thumb = true;
}
$custom_excerpt = class_exists('\AKSRT\Core\PostMetaBox')
? (string) \AKSRT\Core\PostMetaBox::get_meta_value($post_id, 'custom_excerpt', '')
: '';
$excerpt = $custom_excerpt !== '' ? $custom_excerpt : (string) get_the_excerpt($post_id);
$excerpt = (string) get_the_excerpt($post_id);
if ($excerpt === '') {
$excerpt = (string) get_post_field('post_content', $post_id, 'raw');
}
+4 -3
View File
@@ -78,8 +78,7 @@ if (!function_exists('aksrt_get_post_show_layout')):
/**
* legacy-compatible post/page layout override.
*
* Meta key: `_aksrt_show_layout`
* Values: '' (follow) | no_sidebar | sidebar_left | sidebar_right
* Values: false (follow) | no_sidebar | sidebar_left | sidebar_right
*
* @param int|null $post_id Post ID.
*/
@@ -88,7 +87,9 @@ function aksrt_get_post_show_layout($post_id = null): string {
if ($post_id <= 0) {
return '';
}
$value = (string) get_post_meta($post_id, '_aksrt_show_layout', true);
$value = class_exists('\AKSRT\Core\PostMetaBox')
? (string) \AKSRT\Core\PostMetaBox::get_meta_value($post_id, 'show_layout', 'false')
: 'false';
return in_array($value, ['no_sidebar', 'sidebar_left', 'sidebar_right'], true) ? $value : '';
}
endif;
+62 -21
View File
@@ -65,30 +65,18 @@ class DomainModel {
*/
public static function default_post_meta(): array {
return [
'cover_images' => '',
'custom_excerpt' => '',
'header_image_mode' => '',
'show_reading_progress' => '',
'enable_lightbox' => '',
'show_related' => '',
'related_count' => 0,
'enable_like' => '',
'enable_favorite' => '',
'enable_share' => '',
'enable_print' => '',
'enable_pdf' => '',
'copyright_text' => '',
'is_featured' => false,
// §3.8 文章扩展字段
'thumbnail_url' => '',
'cover_image' => '',
'featured_slide' => '',
'featured_video' => '',
// Episode list: [ ['url' => string, 'title' => string], ... ]
'featured_video_title' => '',
'featured_video_episode' => [],
'subtitle' => '',
'views' => 0,
'likes' => 0,
'maxheight' => 0,
'like' => 0,
'show_layout' => 'false',
'no_article-navs' => false,
'article_maxheight_xz' => false,
];
}
@@ -112,23 +100,37 @@ class DomainModel {
return [
'pay_type' => self::PAY_TYPE_NO,
'pay_modo' => '0', // '0' cash | 'points' credits (legacy)
'order_expired_s' => false,
'order_expired_time' => 0,
'order_expired_unit' => 'day',
'price' => '0.00',
'points_price' => 0,
'original_price' => '0.00',
'promotion_tag' => '',
'vip1_price' => '0.00',
'vip2_price' => '0.00',
'vip1_points' => 0,
'vip2_points' => 0,
'pay_limit' => 0, // 0 all | 1 VIP1+ | 2 VIP2 only (legacy)
'pay_count' => 0,
'version' => '',
'size' => '',
'license' => '',
'compat' => '',
'demo_url' => '',
'video_url' => '',
'video_pic' => '',
'video_title' => '',
'video_episode' => [],
'video_scale_height' => 0,
'downloads' => [],
'attributes' => [],
'gallery' => [],
'gallery_show' => 1,
'pay_title' => '',
'pay_doc' => '',
'pay_details' => '',
'pay_extra_hide' => '',
'require_login' => false,
'require_captcha' => false,
// §3.8 电商增强字段
@@ -152,12 +154,11 @@ class DomainModel {
*/
public static function pay_type_labels(): array {
return [
self::PAY_TYPE_NO => '不付费',
self::PAY_TYPE_NO => '关闭',
self::PAY_TYPE_READING => '付费阅读',
self::PAY_TYPE_DOWNLOAD => '付费下载',
self::PAY_TYPE_GALLERY => '付费图库',
self::PAY_TYPE_GALLERY => '付费图片',
self::PAY_TYPE_VIDEO => '付费视频',
self::PAY_TYPE_VIP => 'VIP 专属',
];
}
@@ -315,24 +316,39 @@ class DomainModel {
$meta['pay_type'] = self::normalize_pay_type((string) ($raw['pay_type'] ?? $meta['pay_type']));
$pay_modo = sanitize_key((string) ($raw['pay_modo'] ?? '0'));
$meta['pay_modo'] = $pay_modo === 'points' ? 'points' : '0';
$meta['order_expired_s'] = !empty($raw['order_expired_s']);
$meta['order_expired_time'] = max(0, (int) ($raw['order_expired_time'] ?? 0));
$order_expired_unit = sanitize_key((string) ($raw['order_expired_unit'] ?? 'day'));
$meta['order_expired_unit'] = in_array($order_expired_unit, ['day', 'hour', 'minute'], true) ? $order_expired_unit : 'day';
$meta['price'] = self::sanitize_balance_decimal($raw['price'] ?? '0');
$meta['points_price'] = self::sanitize_points($raw['points_price'] ?? 0);
$meta['original_price'] = self::sanitize_balance_decimal($raw['original_price'] ?? '0');
$meta['promotion_tag'] = wp_kses_post((string) ($raw['promotion_tag'] ?? ''));
$meta['vip1_price'] = self::sanitize_balance_decimal($raw['vip1_price'] ?? '0');
$meta['vip2_price'] = self::sanitize_balance_decimal($raw['vip2_price'] ?? '0');
$meta['vip1_points'] = self::sanitize_points($raw['vip1_points'] ?? 0);
$meta['vip2_points'] = self::sanitize_points($raw['vip2_points'] ?? 0);
$pay_limit = (int) ($raw['pay_limit'] ?? 0);
$meta['pay_limit'] = in_array($pay_limit, [0, 1, 2], true) ? $pay_limit : 0;
$meta['pay_count'] = (int) ($raw['pay_count'] ?? 0);
$meta['version'] = sanitize_text_field((string) ($raw['version'] ?? ''));
$meta['size'] = sanitize_text_field((string) ($raw['size'] ?? ''));
$meta['license'] = sanitize_text_field((string) ($raw['license'] ?? ''));
$meta['compat'] = sanitize_text_field((string) ($raw['compat'] ?? ''));
$meta['demo_url'] = esc_url_raw((string) ($raw['demo_url'] ?? ''));
$meta['video_url'] = esc_url_raw((string) ($raw['video_url'] ?? ''));
$meta['video_pic'] = esc_url_raw((string) ($raw['video_pic'] ?? ''));
$meta['video_title'] = sanitize_text_field((string) ($raw['video_title'] ?? ''));
$meta['video_episode'] = self::sanitize_video_episodes($raw['video_episode'] ?? []);
$meta['video_scale_height'] = max(0, min(200, (int) ($raw['video_scale_height'] ?? 0)));
$meta['downloads'] = self::sanitize_downloads($raw['downloads'] ?? []);
$meta['attributes'] = self::sanitize_attributes($raw['attributes'] ?? []);
$meta['gallery'] = self::sanitize_gallery($raw['gallery'] ?? []);
$meta['gallery_show'] = max(0, (int) ($raw['gallery_show'] ?? 1));
$meta['pay_title'] = sanitize_text_field((string) ($raw['pay_title'] ?? ''));
$meta['pay_doc'] = wp_kses_post((string) ($raw['pay_doc'] ?? ''));
$meta['pay_details'] = wp_kses_post((string) ($raw['pay_details'] ?? ''));
$meta['pay_extra_hide'] = wp_kses_post((string) ($raw['pay_extra_hide'] ?? ''));
$meta['require_login'] = !empty($raw['require_login']);
$meta['require_captcha'] = !empty($raw['require_captcha']);
@@ -353,6 +369,31 @@ class DomainModel {
return $meta;
}
/**
* @param mixed $value Raw video episode rows.
* @return array<int, array{title:string,url:string}>
*/
private static function sanitize_video_episodes($value): array {
if (!is_array($value)) {
return [];
}
$episodes = [];
foreach ($value as $row) {
if (!is_array($row)) {
continue;
}
$url = esc_url_raw((string) ($row['url'] ?? ''));
if ($url === '') {
continue;
}
$episodes[] = [
'title' => sanitize_text_field((string) ($row['title'] ?? '')),
'url' => $url,
];
}
return array_slice($episodes, 0, 50);
}
private static function sanitize_balance_decimal($value): string {
if (!is_string($value)) {
return '0.00';
+16 -15
View File
@@ -16,7 +16,7 @@ trait FrontSubmitFeatured {
* @param array<string, mixed> $data
*/
private function save_featured_meta(int $post_id, array $data, int $user_id): void {
if (empty($data['featured_data']) && empty($data['featured_type'])) {
if (empty($data['featured_data'])) {
return;
}
@@ -26,11 +26,7 @@ trait FrontSubmitFeatured {
$featured = is_array($decoded) ? $decoded : null;
}
if (!is_array($featured)) {
// Flat payload: featured_type + fields.
$featured = [
'type' => sanitize_key((string) ($data['featured_type'] ?? '')),
'data' => $data['featured_payload'] ?? $data,
];
return;
}
$type = sanitize_key((string) ($featured['type'] ?? ''));
@@ -40,14 +36,15 @@ trait FrontSubmitFeatured {
if (!is_array($post_meta)) {
$post_meta = DomainModel::default_post_meta();
}
$post_meta = array_intersect_key($post_meta, DomainModel::default_post_meta());
switch ($type) {
case 'video':
if (!$this->user_can_cap('new_post_video_cover', $user_id) && !$this->user_can_cap('new_post_image_cover', $user_id)) {
return;
}
$video = esc_url_raw((string) ($payload['url'] ?? $payload['video'] ?? ''));
$pic = esc_url_raw((string) ($payload['pic'] ?? $payload['poster'] ?? $payload['cover'] ?? ''));
$video = esc_url_raw((string) ($payload['url'] ?? ''));
$pic = esc_url_raw((string) ($payload['pic'] ?? ''));
if ($pic !== '' && !$this->user_can_cap('upload_submit_image', $user_id)) {
$pic = '';
}
@@ -55,7 +52,7 @@ trait FrontSubmitFeatured {
$post_meta['featured_slide'] = '';
if ($pic !== '' && $this->can_use_front_submit_image_url($pic, $user_id)) {
$post_meta['thumbnail_url'] = $pic;
$post_meta['cover_images'] = $pic;
$post_meta['cover_image'] = $pic;
$this->maybe_set_thumbnail_url($post_id, $pic);
}
break;
@@ -64,7 +61,8 @@ trait FrontSubmitFeatured {
if (!$this->user_can_cap('new_post_slide_cover', $user_id) && !$this->user_can_cap('new_post_image_cover', $user_id)) {
return;
}
$ids = $payload['ids'] ?? $payload['slides'] ?? $payload['images'] ?? [];
$ids = array_is_list($payload) ? $payload : [];
$attachment_ids = [];
$urls = [];
if (is_string($ids)) {
$ids = array_filter(array_map('trim', explode(',', $ids)));
@@ -77,6 +75,7 @@ trait FrontSubmitFeatured {
if ($attachment_id > 0 && $this->can_use_front_submit_image($attachment_id, $user_id)) {
$src = wp_get_attachment_image_url($attachment_id, 'full');
if ($src) {
$attachment_ids[] = $attachment_id;
$urls[] = $src;
}
} elseif ($u !== '' && $this->user_can_cap('upload_submit_image', $user_id)) {
@@ -88,6 +87,7 @@ trait FrontSubmitFeatured {
? wp_get_attachment_image_url($attachment_id, 'full')
: false;
if ($src) {
$attachment_ids[] = $attachment_id;
$urls[] = $src;
}
} else {
@@ -98,11 +98,11 @@ trait FrontSubmitFeatured {
}
}
}
$post_meta['featured_slide'] = implode(',', $urls);
$post_meta['featured_slide'] = implode(',', array_values(array_unique($attachment_ids)));
$post_meta['featured_video'] = '';
if (!empty($urls[0]) && $this->can_use_front_submit_image_url((string) $urls[0], $user_id)) {
$post_meta['thumbnail_url'] = $urls[0];
$post_meta['cover_images'] = $urls[0];
$post_meta['cover_image'] = $urls[0];
$this->maybe_set_thumbnail_url($post_id, $urls[0]);
}
break;
@@ -111,7 +111,7 @@ trait FrontSubmitFeatured {
if (!$this->user_can_cap('new_post_image_cover', $user_id)) {
return;
}
$url = esc_url_raw((string) ($payload['url'] ?? $payload['image'] ?? ''));
$url = esc_url_raw((string) ($payload['url'] ?? ''));
$id = absint($payload['id'] ?? 0);
if ($id > 0 && !$this->can_use_front_submit_image($id, $user_id)) {
return;
@@ -126,7 +126,7 @@ trait FrontSubmitFeatured {
$post_meta['featured_slide'] = '';
if ($url !== '' && $this->can_use_front_submit_image_url($url, $user_id)) {
$post_meta['thumbnail_url'] = $url;
$post_meta['cover_images'] = $url;
$post_meta['cover_image'] = $url;
if ($id > 0) {
set_post_thumbnail($post_id, $id);
} elseif ($this->user_can_cap('upload_submit_image', $user_id)) {
@@ -141,7 +141,7 @@ trait FrontSubmitFeatured {
$post_meta['featured_video'] = '';
$post_meta['featured_slide'] = '';
$post_meta['thumbnail_url'] = '';
$post_meta['cover_images'] = '';
$post_meta['cover_image'] = '';
delete_post_thumbnail($post_id);
break;
@@ -149,6 +149,7 @@ trait FrontSubmitFeatured {
return;
}
unset($post_meta['views'], $post_meta['like']);
update_post_meta($post_id, DomainModel::POST_META_KEY, $post_meta);
}
+14 -27
View File
@@ -23,13 +23,13 @@ trait FrontSubmitPay {
}
// Only touch pay meta when the form sent pay fields.
$has_pay_payload = isset($data['aksrt_pay']) || isset($data['pay_enabled']) || isset($data['aksrtpay_s']) || isset($data['pay_type']);
$has_pay_payload = isset($data['aksrt_pay']) || isset($data['pay_enabled']);
if (!$has_pay_payload) {
return ['success' => true];
}
$raw = is_array($data['aksrt_pay'] ?? null) ? $data['aksrt_pay'] : [];
$enabled = !empty($data['pay_enabled']) || !empty($data['aksrtpay_s']);
$enabled = !empty($data['pay_enabled']);
$existing = function_exists('aksrt_get_pay_meta') ? aksrt_get_pay_meta($post_id) : DomainModel::default_pay_meta();
$meta = is_array($existing) ? $existing : DomainModel::default_pay_meta();
@@ -40,22 +40,15 @@ trait FrontSubmitPay {
return ['success' => true];
}
$pay_modo = sanitize_key((string) ($raw['pay_modo'] ?? $data['pay_modo'] ?? '0'));
$pay_modo = sanitize_key((string) ($raw['pay_modo'] ?? '0'));
$pay_modo = $pay_modo === 'points' ? 'points' : '0';
$pay_type = sanitize_key((string) ($raw['pay_type'] ?? $data['pay_type'] ?? DomainModel::PAY_TYPE_READING));
if ($pay_type === '1' || $pay_type === 'reading' || $pay_type === 'payshow') {
$pay_type = DomainModel::PAY_TYPE_READING;
} elseif ($pay_type === '2' || $pay_type === 'download') {
$pay_type = DomainModel::PAY_TYPE_DOWNLOAD;
} else {
$pay_type = DomainModel::normalize_pay_type($pay_type);
$pay_type = DomainModel::normalize_pay_type((string) ($raw['pay_type'] ?? DomainModel::PAY_TYPE_READING));
if ($pay_type === DomainModel::PAY_TYPE_NO) {
$pay_type = DomainModel::PAY_TYPE_READING;
}
}
$downloads = $this->normalize_front_downloads($raw['downloads'] ?? $data['pay_download'] ?? [], $user_id);
$downloads = $this->normalize_front_downloads($raw['downloads'] ?? [], $user_id);
if (!empty($downloads) && $this->user_can_cap('new_post_pay_download', $user_id)) {
$pay_type = DomainModel::PAY_TYPE_DOWNLOAD;
$meta['downloads'] = $downloads;
@@ -93,10 +86,10 @@ trait FrontSubmitPay {
if ($vip_manual) {
$vip1 = $vip1_enabled
? $this->parse_front_points($raw['vip1_points'] ?? $raw['vip_1_points'] ?? null)
? $this->parse_front_points($raw['vip1_points'] ?? null)
: 0;
$vip2 = $vip2_enabled
? $this->parse_front_points($raw['vip2_points'] ?? $raw['vip_2_points'] ?? null)
? $this->parse_front_points($raw['vip2_points'] ?? null)
: 0;
if (($vip1_enabled && $vip1 === null) || ($vip2_enabled && $vip2 === null)) {
return ['success' => false, 'message' => __('请设置正确的会员积分价格', 'aksrt')];
@@ -121,7 +114,7 @@ trait FrontSubmitPay {
$meta['vip1_price'] = '0.00';
$meta['vip2_price'] = '0.00';
} else {
$price_minor = $this->parse_front_cash_minor($raw['price'] ?? $raw['pay_price'] ?? $data['pay_price'] ?? null);
$price_minor = $this->parse_front_cash_minor($raw['price'] ?? null);
$min_minor = $this->parse_front_cash_minor($settings['pay_price_min'] ?? '0') ?? 0;
$max_minor = $this->parse_front_cash_minor($settings['pay_price_max'] ?? '0') ?? 0;
if ($price_minor === null) {
@@ -139,10 +132,10 @@ trait FrontSubmitPay {
if ($vip_manual) {
$vip1_minor = $vip1_enabled
? $this->parse_front_cash_minor($raw['vip1_price'] ?? $raw['vip_1_price'] ?? null)
? $this->parse_front_cash_minor($raw['vip1_price'] ?? null)
: 0;
$vip2_minor = $vip2_enabled
? $this->parse_front_cash_minor($raw['vip2_price'] ?? $raw['vip_2_price'] ?? null)
? $this->parse_front_cash_minor($raw['vip2_price'] ?? null)
: 0;
if (($vip1_enabled && $vip1_minor === null) || ($vip2_enabled && $vip2_minor === null)) {
return ['success' => false, 'message' => __('请设置正确的会员价格', 'aksrt')];
@@ -168,13 +161,8 @@ trait FrontSubmitPay {
$meta['vip2_points'] = 0;
}
$pay_limit = (int) ($raw['pay_limit'] ?? $data['pay_limit'] ?? 0);
$pay_limit = (int) ($raw['pay_limit'] ?? 0);
$meta['pay_limit'] = in_array($pay_limit, [0, 1, 2], true) ? $pay_limit : 0;
$meta['require_login'] = !empty($raw['require_login']) || !empty($data['require_login']);
$meta['coupon_s'] = !empty($raw['coupon_s']) || !empty($data['coupon_s']);
if (isset($raw['coupon_desc']) || isset($data['coupon_desc'])) {
$meta['coupon_desc'] = sanitize_textarea_field((string) ($raw['coupon_desc'] ?? $data['coupon_desc'] ?? ''));
}
$this->write_pay_meta($post_id, $meta);
return ['success' => true];
@@ -227,8 +215,7 @@ trait FrontSubmitPay {
if (!is_array($row)) {
continue;
}
// Accept legacy-ish keys + AKSRT keys.
$url = (string) ($row['url'] ?? $row['link'] ?? '');
$url = (string) ($row['url'] ?? '');
$url = esc_url_raw(trim($url));
if ($url === '') {
continue;
@@ -238,10 +225,10 @@ trait FrontSubmitPay {
continue;
}
$out[] = [
'label' => sanitize_text_field((string) ($row['label'] ?? $row['name'] ?? '')),
'label' => sanitize_text_field((string) ($row['label'] ?? '')),
'url' => $url,
'type' => $attachment_id > 0 ? 'direct' : 'external',
'extract_code' => sanitize_text_field((string) ($row['extract_code'] ?? $row['copy_val'] ?? $row['more'] ?? '')),
'extract_code' => sanitize_text_field((string) ($row['extract_code'] ?? '')),
];
}
return DomainModel::sanitize_downloads($out);
-10
View File
@@ -181,16 +181,6 @@ trait FrontSubmitSave {
$this->save_featured_meta((int) $in_id, $data, $user_id);
// Legacy thumbnail URL fallback.
$thumbnail_url = esc_url_raw((string) ($data['thumbnail'] ?? ''));
if ($thumbnail_url !== '' && empty($data['featured_data']) && $this->user_can_cap('upload_submit_image', $user_id)) {
$attachment_id = attachment_url_to_postid($thumbnail_url);
$attachment = $attachment_id > 0 ? get_post($attachment_id) : null;
if ($attachment instanceof \WP_Post && (int) $attachment->post_author === $user_id && wp_attachment_is_image($attachment_id)) {
set_post_thumbnail((int) $in_id, $attachment_id);
}
}
$post = get_post((int) $in_id);
if ($post instanceof \WP_Post) {
do_action($is_edit ? 'aksrt_new_edit_posts' : 'aksrt_new_add_posts', $post);
+219 -703
View File
File diff suppressed because it is too large Load Diff
+200 -487
View File
@@ -1,5 +1,7 @@
<?php
/**
* Article paid-content fields shared by the block and classic editors.
*
* @package AKSRT
*/
@@ -14,545 +16,256 @@ class ResourceMetaBox {
private function __construct() {}
public static function get_instance(): self {
if (null === self::$instance) self::$instance = new 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_post', [$this, 'save'], 10, 2);
add_action('admin_enqueue_scripts', [$this, 'enqueue_assets']);
}
public function register_meta_box(): void {
add_meta_box(
'aksrt_pay_meta',
__('付费内容设置', 'aksrt'),
[$this, 'render'],
'post',
'normal',
'high'
);
add_meta_box('aksrt_pay_meta', __('付费功能', 'aksrt'), [$this, 'render'], 'post', 'normal', 'high');
}
public function enqueue_assets(string $hook): void {
if (!in_array($hook, ['post.php', 'post-new.php'], true)) {
return;
}
$screen = function_exists('get_current_screen') ? get_current_screen() : null;
if (!$screen || $screen->post_type !== 'post') {
return;
}
wp_enqueue_media();
$css = AKSRT_DIR . '/assets/css/admin/pay-meta-box.css';
$js = AKSRT_DIR . '/assets/js/admin-pay-meta-box.js';
if (is_file($css)) {
wp_enqueue_style('aksrt-pay-meta-box', AKSRT_URI . '/assets/css/admin/pay-meta-box.css', [], (string) filemtime($css));
}
if (is_file($js)) {
wp_enqueue_script('aksrt-pay-meta-box', AKSRT_URI . '/assets/js/admin-pay-meta-box.js', [], (string) filemtime($js), true);
}
}
public function render(\WP_Post $post): void {
wp_nonce_field('aksrt_pay_meta_save', 'aksrt_pay_meta_nonce');
$meta = function_exists('aksrt_get_pay_meta')
? aksrt_get_pay_meta($post)
: DomainModel::default_pay_meta();
$pay_types = aksrt_get_pay_types();
$is_paid = ($meta['pay_type'] ?? 'no') !== 'no';
$downloads_json = !empty($meta['downloads'])
? wp_json_encode($meta['downloads'], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)
: '';
$attributes_json = !empty($meta['attributes'])
? wp_json_encode($meta['attributes'], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)
: '';
$gallery_json = !empty($meta['gallery'])
? wp_json_encode($meta['gallery'], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)
: '';
$meta = function_exists('aksrt_get_pay_meta') ? aksrt_get_pay_meta($post) : DomainModel::default_pay_meta();
$gallery_ids = [];
foreach ((array) ($meta['gallery'] ?? []) as $url) {
$id = attachment_url_to_postid((string) $url);
if ($id > 0) {
$gallery_ids[] = $id;
}
}
?>
<div class="aksrt-pay-meta" data-aksrt-pay-meta>
<?php $this->radio('pay_type', __('付费模式', 'aksrt'), (string) ($meta['pay_type'] ?? 'no'), aksrt_get_pay_types()); ?>
<div data-pay-enabled>
<?php
$this->radio('pay_limit', __('购买权限', 'aksrt'), (string) ($meta['pay_limit'] ?? 0), [
'0' => __('所有人可购买', 'aksrt'),
'1' => __('VIP1 及以上会员可购买', 'aksrt'),
'2' => __('仅 VIP2 可购买', 'aksrt'),
], __('设置会员专享资源时,请同时配置对应的会员价格。', 'aksrt'));
$this->radio('pay_modo', __('支付类型', 'aksrt'), (string) ($meta['pay_modo'] ?? '0'), [
'0' => __('普通商品(金钱购买)', 'aksrt'),
'points' => __('积分商品(积分购买,依赖于用户积分功能)', 'aksrt'),
]);
?>
<style>
.aksrt-pmb__row { display: grid; grid-template-columns: 140px 1fr; gap: 12px 16px; align-items: start; margin-bottom: 12px; }
.aksrt-pmb__row label { font-weight: 600; color: #1f2937; padding-top: 6px; font-size: 13px; }
.aksrt-pmb__row input[type="text"], .aksrt-pmb__row input[type="number"], .aksrt-pmb__row input[type="url"], .aksrt-pmb__row textarea, .aksrt-pmb__row select { width: 100%; max-width: 560px; }
.aksrt-pmb__row textarea { min-height: 120px; font-family: Consolas, "Liberation Mono", monospace; font-size: 12px; }
.aksrt-pmb__hint { color: #6b7280; font-size: 12px; margin-top: 4px; line-height: 1.5; }
.aksrt-pmb__section { margin-top: 16px; padding-top: 16px; border-top: 1px solid #e5e7eb; }
.aksrt-pmb__section-title { font-size: 14px; font-weight: 700; color: #111827; margin-bottom: 12px; }
.aksrt-pmb__toggle { display: flex; align-items: center; gap: 8px; padding: 12px; background: #f3f4f6; border-radius: 6px; margin-bottom: 16px; }
.aksrt-pmb__toggle label { font-weight: 600; }
.aksrt-pmb__vip-row { display: flex; gap: 12px; flex-wrap: wrap; }
.aksrt-pmb__vip-row > div { flex: 1; min-width: 120px; }
.aksrt-pmb__vip-row label { font-weight: 400; font-size: 12px; color: #6b7280; }
.aksrt-pmb__tools { margin-top: 6px; display: flex; gap: 6px; flex-wrap: wrap; }
.aksrt-pmb__tools button { font-size: 12px; }
.aksrt-pmb__error { color: #dc2626; font-size: 12px; display: none; margin-top: 4px; }
.aksrt-pmb__fields[data-disabled="1"] { opacity: 0.45; pointer-events: none; }
</style>
<div class="aksrt-pmb__toggle">
<label for="aksrt_pay_type"><?php esc_html_e('付费类型', 'aksrt'); ?></label>
<select id="aksrt_pay_type" name="aksrt_pay_type" style="min-width:160px;">
<?php foreach ($pay_types as $key => $label) : ?>
<option value="<?php echo esc_attr($key); ?>" <?php selected($meta['pay_type'], $key); ?>>
<?php echo esc_html($label); ?>
</option>
<?php endforeach; ?>
<div class="aksrt-pay-meta__field">
<div class="aksrt-pay-meta__title"><?php esc_html_e('订单时效', 'aksrt'); ?></div>
<div class="aksrt-pay-meta__control">
<label><input type="checkbox" name="aksrt_pay[order_expired_s]" value="1" <?php checked(!empty($meta['order_expired_s'])); ?> data-order-expired> <?php esc_html_e('启用订单有效期', 'aksrt'); ?></label>
<p class="description"><?php esc_html_e('购买后仅在有效期内可查看内容,超时后需再次购买。', 'aksrt'); ?></p>
</div>
</div>
<div class="aksrt-pay-meta__field compact" data-order-expired-fields>
<div class="aksrt-pay-meta__title"><?php esc_html_e('有效时间', 'aksrt'); ?></div>
<div class="aksrt-pay-meta__control aksrt-pay-meta__inline">
<input type="number" min="0" step="1" name="aksrt_pay[order_expired_time]" value="<?php echo esc_attr((string) ($meta['order_expired_time'] ?? 0)); ?>">
<select name="aksrt_pay[order_expired_unit]">
<option value="day" <?php selected((string) ($meta['order_expired_unit'] ?? 'day'), 'day'); ?>><?php esc_html_e('天', 'aksrt'); ?></option>
<option value="hour" <?php selected((string) ($meta['order_expired_unit'] ?? 'day'), 'hour'); ?>><?php esc_html_e('小时', 'aksrt'); ?></option>
<option value="minute" <?php selected((string) ($meta['order_expired_unit'] ?? 'day'), 'minute'); ?>><?php esc_html_e('分钟', 'aksrt'); ?></option>
</select>
<span class="aksrt-pmb__hint">
<?php esc_html_e('选择「不付费」则为普通文章;选择其他类型将启用对应的付费功能。', 'aksrt'); ?>
</span>
</div>
<p class="aksrt-pmb__hint" id="aksrt-pmb-reading-hint" hidden>
<?php esc_html_e('付费阅读价格只作用于正文中的“付费可见”片段;未插入片段时,正文保持公开。', 'aksrt'); ?>
</p>
<div class="aksrt-pmb__fields" id="aksrt-pmb-fields" data-disabled="<?php echo $is_paid ? '0' : '1'; ?>">
<!-- 基础价格 -->
<div class="aksrt-pmb__section">
<div class="aksrt-pmb__section-title"><?php esc_html_e('价格设置', 'aksrt'); ?></div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_modo"><?php esc_html_e('支付方式', 'aksrt'); ?></label>
<div>
<select id="aksrt_pay_modo" name="aksrt_pay_modo" style="min-width:160px;">
<option value="0" <?php selected((string) ($meta['pay_modo'] ?? '0'), '0'); ?>><?php esc_html_e('现金', 'aksrt'); ?></option>
<option value="points" <?php selected((string) ($meta['pay_modo'] ?? '0'), 'points'); ?>><?php esc_html_e('积分', 'aksrt'); ?></option>
</select>
<p class="aksrt-pmb__hint"><?php esc_html_e('对齐 legacy pay_modo:现金价或积分价结算。', 'aksrt'); ?></p>
</div>
</div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_price"><?php esc_html_e('现金价格', 'aksrt'); ?></label>
<div>
<input type="number" id="aksrt_pay_price" name="aksrt_pay_price" value="<?php echo esc_attr((string) $meta['price']); ?>" min="0" step="0.01">
<p class="aksrt-pmb__hint"><?php esc_html_e('支付方式为「现金」时使用。0 = 免费。', 'aksrt'); ?></p>
<div data-pay-modo="points">
<?php $this->number('points_price', __('积分售价', 'aksrt'), (string) ($meta['points_price'] ?? 0), '1', __('积分', 'aksrt')); ?>
<?php $this->number('vip1_points', __('VIP1 积分售价', 'aksrt'), (string) ($meta['vip1_points'] ?? 0), '1', __('积分', 'aksrt'), __('填 0 则为 VIP1 免费', 'aksrt')); ?>
<?php $this->number('vip2_points', __('VIP2 积分售价', 'aksrt'), (string) ($meta['vip2_points'] ?? 0), '1', __('积分', 'aksrt'), __('会员价格不能高于售价,填 0 则为 VIP2 免费。', 'aksrt')); ?>
</div>
<div data-pay-modo="0">
<?php $this->number('price', __('执行价', 'aksrt'), (string) ($meta['price'] ?? '0.00'), '0.01', '', __('填 0 则为免费', 'aksrt')); ?>
<?php $this->number('original_price', __('原价', 'aksrt'), (string) ($meta['original_price'] ?? '0.00'), '0.01', '', __('显示在执行价格前面,并划掉。', 'aksrt')); ?>
<?php $this->textarea('promotion_tag', __('促销标签', 'aksrt'), (string) ($meta['promotion_tag'] ?? ''), 1); ?>
<?php $this->number('vip1_price', __('VIP1 价格', 'aksrt'), (string) ($meta['vip1_price'] ?? '0.00'), '0.01', '', __('填 0 则为 VIP1 免费', 'aksrt')); ?>
<?php $this->number('vip2_price', __('VIP2 价格', 'aksrt'), (string) ($meta['vip2_price'] ?? '0.00'), '0.01', '', __('会员价格不能高于执行价,填 0 则为 VIP2 免费。', 'aksrt')); ?>
</div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_points_price"><?php esc_html_e('积分价格', 'aksrt'); ?></label>
<div>
<input type="number" id="aksrt_pay_points_price" name="aksrt_pay_points_price" value="<?php echo esc_attr((string) ($meta['points_price'] ?? 0)); ?>" min="0" step="1">
<p class="aksrt-pmb__hint"><?php esc_html_e('支付方式为「积分」时使用。0 = 免费。', 'aksrt'); ?></p>
<?php $this->number('pay_count', __('销量浮动', 'aksrt'), (string) ($meta['pay_count'] ?? 0), '1', '', __('为真实销量增加或减少的数量。', 'aksrt'), false); ?>
<div data-pay-type="gallery">
<div class="aksrt-pay-meta__field">
<div class="aksrt-pay-meta__title"><?php esc_html_e('付费图片', 'aksrt'); ?></div>
<div class="aksrt-pay-meta__control">
<input type="hidden" name="aksrt_pay[gallery_ids]" value="<?php echo esc_attr(implode(',', $gallery_ids)); ?>" data-gallery-input>
<div class="aksrt-pay-meta__gallery" data-gallery-preview></div>
<div class="aksrt-pay-meta__buttons"><button type="button" class="button" data-gallery-select><?php esc_html_e('新增图片', 'aksrt'); ?></button><button type="button" class="button-link-delete" data-gallery-clear><?php esc_html_e('清空图片', 'aksrt'); ?></button></div>
</div>
</div>
<?php $this->number('gallery_show', __('免费查看', 'aksrt'), (string) ($meta['gallery_show'] ?? 1), '1', __('张', 'aksrt'), __('不能大于付费图片数量,否则无效。', 'aksrt')); ?>
</div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_original_price"><?php esc_html_e('原价(划线价)', 'aksrt'); ?></label>
<div>
<input type="number" id="aksrt_pay_original_price" name="aksrt_pay_original_price" value="<?php echo esc_attr((string) ($meta['original_price'] ?? '')); ?>" min="0" step="0.01" placeholder="<?php esc_attr_e('可选,显示划线效果', 'aksrt'); ?>">
</div>
<div data-pay-type="video">
<?php $this->upload('video_url', __('视频资源', 'aksrt'), (string) ($meta['video_url'] ?? ''), 'video', __('输入视频地址或选择、上传本地视频。', 'aksrt')); ?>
<?php $this->upload('video_pic', __('视频封面(可选)', 'aksrt'), (string) ($meta['video_pic'] ?? ''), 'image'); ?>
<?php $this->text('video_title', __('本集标题', 'aksrt'), (string) ($meta['video_title'] ?? ''), __('如需添加剧集则需填写此处', 'aksrt'), __('第1集', 'aksrt')); ?>
<?php $this->group('video_episode', __('更多剧集', 'aksrt'), (array) ($meta['video_episode'] ?? []), 'video_row', __('添加剧集', 'aksrt'), __('为付费视频添加更多剧集。', 'aksrt')); ?>
<?php $this->number('video_scale_height', __('固定长宽比例', 'aksrt'), (string) ($meta['video_scale_height'] ?? 0), '5', '%', __('为 0 则不固定长宽比例。', 'aksrt')); ?>
</div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_limit"><?php esc_html_e('购买限制', 'aksrt'); ?></label>
<div>
<select id="aksrt_pay_limit" name="aksrt_pay_limit" style="min-width:200px;">
<option value="0" <?php selected((int) ($meta['pay_limit'] ?? 0), 0); ?>><?php esc_html_e('所有用户可购买', 'aksrt'); ?></option>
<option value="1" <?php selected((int) ($meta['pay_limit'] ?? 0), 1); ?>><?php esc_html_e('仅 VIP1 及以上', 'aksrt'); ?></option>
<option value="2" <?php selected((int) ($meta['pay_limit'] ?? 0), 2); ?>><?php esc_html_e('仅 VIP2', 'aksrt'); ?></option>
</select>
<p class="aksrt-pmb__hint"><?php esc_html_e('对齐 legacy pay_limit:限制购买人群(会员专属资源)。', 'aksrt'); ?></p>
</div>
<div data-pay-type="download">
<?php $this->group('downloads', __('资源下载', 'aksrt'), (array) ($meta['downloads'] ?? []), 'download_row', __('添加资源', 'aksrt')); ?>
<?php $this->group('attributes', __('资源属性', 'aksrt'), (array) ($meta['attributes'] ?? []), 'attribute_row', __('添加属性', 'aksrt')); ?>
<?php $this->text('demo_url', __('演示地址', 'aksrt'), (string) ($meta['demo_url'] ?? ''), '', 'https://', 'url'); ?>
</div>
<div class="aksrt-pmb__row">
<label><?php esc_html_e('VIP 专属价', 'aksrt'); ?></label>
<div>
<div class="aksrt-pmb__vip-row">
<div>
<label for="aksrt_pay_vip1_price"><?php esc_html_e('VIP1 现金价', 'aksrt'); ?></label>
<input type="number" id="aksrt_pay_vip1_price" name="aksrt_pay_vip1_price" value="<?php echo esc_attr((string) ($meta['vip1_price'] ?? '')); ?>" min="0" step="0.01" placeholder="0">
</div>
<div>
<label for="aksrt_pay_vip2_price"><?php esc_html_e('VIP2 现金价', 'aksrt'); ?></label>
<input type="number" id="aksrt_pay_vip2_price" name="aksrt_pay_vip2_price" value="<?php echo esc_attr((string) ($meta['vip2_price'] ?? '')); ?>" min="0" step="0.01" placeholder="0">
</div>
<div>
<label for="aksrt_pay_vip1_points"><?php esc_html_e('VIP1 积分价', 'aksrt'); ?></label>
<input type="number" id="aksrt_pay_vip1_points" name="aksrt_pay_vip1_points" value="<?php echo esc_attr((string) ($meta['vip1_points'] ?? '')); ?>" min="0" step="1" placeholder="0">
</div>
<div>
<label for="aksrt_pay_vip2_points"><?php esc_html_e('VIP2 积分价', 'aksrt'); ?></label>
<input type="number" id="aksrt_pay_vip2_points" name="aksrt_pay_vip2_points" value="<?php echo esc_attr((string) ($meta['vip2_points'] ?? '')); ?>" min="0" step="1" placeholder="0">
</div>
</div>
<p class="aksrt-pmb__hint"><?php esc_html_e('VIP 用户专属价。留空或 0 表示不启用该档位优惠。', 'aksrt'); ?></p>
</div>
</div>
</div>
<?php $this->text('pay_title', __('商品标题', 'aksrt'), (string) ($meta['pay_title'] ?? ''), __('可选,如需单独显示付费内容标题请填写。', 'aksrt')); ?>
<?php $this->textarea('pay_doc', __('商品简介', 'aksrt'), (string) ($meta['pay_doc'] ?? ''), 1, __('可选,如需单独显示付费内容介绍请填写。', 'aksrt')); ?>
<?php $this->textarea('pay_details', __('更多详情', 'aksrt'), (string) ($meta['pay_details'] ?? ''), 3, __('显示在付费内容卡片下方,支持安全 HTML。', 'aksrt')); ?>
<?php $this->textarea('pay_extra_hide', __('额外隐藏内容', 'aksrt'), (string) ($meta['pay_extra_hide'] ?? ''), 3, __('付费后显示的额外隐藏内容,支持安全 HTML。', 'aksrt')); ?>
<!-- 资源信息(download/gallery/video 类型显示) -->
<div class="aksrt-pmb__section" id="aksrt-pmb-resource-fields">
<div class="aksrt-pmb__section-title"><?php esc_html_e('资源信息', 'aksrt'); ?></div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_version"><?php esc_html_e('版本', 'aksrt'); ?></label>
<input type="text" id="aksrt_pay_version" name="aksrt_pay_version" value="<?php echo esc_attr((string) $meta['version']); ?>" placeholder="v1.0.0">
</div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_size"><?php esc_html_e('文件大小', 'aksrt'); ?></label>
<input type="text" id="aksrt_pay_size" name="aksrt_pay_size" value="<?php echo esc_attr((string) $meta['size']); ?>" placeholder="210 MB">
</div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_compat"><?php esc_html_e('兼容性', 'aksrt'); ?></label>
<input type="text" id="aksrt_pay_compat" name="aksrt_pay_compat" value="<?php echo esc_attr((string) $meta['compat']); ?>" placeholder="WordPress 6.4+">
</div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_license"><?php esc_html_e('授权', 'aksrt'); ?></label>
<input type="text" id="aksrt_pay_license" name="aksrt_pay_license" value="<?php echo esc_attr((string) $meta['license']); ?>" placeholder="MIT / GPL / 自用">
</div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_demo_url"><?php esc_html_e('演示地址', 'aksrt'); ?></label>
<input type="url" id="aksrt_pay_demo_url" name="aksrt_pay_demo_url" value="<?php echo esc_attr((string) $meta['demo_url']); ?>" placeholder="https://">
<div class="aksrt-pay-meta__notice"><strong><?php esc_html_e('付费阅读', 'aksrt'); ?></strong> <?php esc_html_e('需配合古腾堡“隐藏内容块”或经典编辑器短代码使用:', 'aksrt'); ?> <code>[hidecontent type="payshow"]</code> <?php esc_html_e('隐藏内容', 'aksrt'); ?> <code>[/hidecontent]</code></div>
</div>
</div>
<!-- 自定义属性 -->
<div class="aksrt-pmb__section">
<div class="aksrt-pmb__section-title"><?php esc_html_e('自定义属性', 'aksrt'); ?></div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_attributes"><?php esc_html_e('属性列表', 'aksrt'); ?></label>
<div>
<textarea id="aksrt_pay_attributes" name="aksrt_pay_attributes" placeholder='[
{"key": "格式", "value": "PSD"},
{"key": "分辨率", "value": "4K"}
]'><?php echo esc_textarea($attributes_json); ?></textarea>
<p class="aksrt-pmb__hint"><?php esc_html_e('JSON 数组。每项:key(属性名)/ value(属性值)。', 'aksrt'); ?></p>
</div>
</div>
</div>
<!-- 下载源(download 类型显示) -->
<div class="aksrt-pmb__section" id="aksrt-pmb-download-fields">
<div class="aksrt-pmb__section-title"><?php esc_html_e('下载源', 'aksrt'); ?></div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_downloads"><?php esc_html_e('下载链接', 'aksrt'); ?></label>
<div>
<textarea id="aksrt_pay_downloads" name="aksrt_pay_downloads" placeholder='[
{
"label": "百度网盘",
"url": "https://pan.baidu.com/s/xxx",
"type": "net_disk",
"extract_code": "abcd"
}
]'><?php echo esc_textarea($downloads_json); ?></textarea>
<div class="aksrt-pmb__error" id="aksrt-pmb-dl-err"></div>
<div class="aksrt-pmb__tools">
<button type="button" class="button button-small" id="aksrt-pmb-add-dl"><?php esc_html_e('追加示例条目', 'aksrt'); ?></button>
<button type="button" class="button button-small" id="aksrt-pmb-fmt-dl"><?php esc_html_e('格式化 JSON', 'aksrt'); ?></button>
</div>
<p class="aksrt-pmb__hint"><?php esc_html_e('JSON 数组。每项:label / url / type(direct | net_disk | external)/ extract_code。', 'aksrt'); ?></p>
</div>
</div>
</div>
<!-- 图库 URL(gallery 类型显示) -->
<div class="aksrt-pmb__section" id="aksrt-pmb-gallery-fields" style="display:none;">
<div class="aksrt-pmb__section-title"><?php esc_html_e('付费图库', 'aksrt'); ?></div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_gallery"><?php esc_html_e('图片 URL 列表', 'aksrt'); ?></label>
<div>
<textarea id="aksrt_pay_gallery" name="aksrt_pay_gallery" placeholder='[
"https://example.com/img1.jpg",
"https://example.com/img2.jpg"
]'><?php echo esc_textarea($gallery_json); ?></textarea>
<p class="aksrt-pmb__hint"><?php esc_html_e('JSON 数组。每项为图片 URL。付费前图片将被模糊遮罩。', 'aksrt'); ?></p>
</div>
</div>
</div>
<!-- 视频 URL(video 类型显示) -->
<div class="aksrt-pmb__section" id="aksrt-pmb-video-fields" style="display:none;">
<div class="aksrt-pmb__section-title"><?php esc_html_e('付费视频', 'aksrt'); ?></div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_video_url"><?php esc_html_e('视频 URL', 'aksrt'); ?></label>
<div>
<input type="url" id="aksrt_pay_video_url" name="aksrt_pay_video_url" value="<?php echo esc_attr((string) ($meta['video_url'] ?? '')); ?>" placeholder="https://example.com/video.mp4">
<p class="aksrt-pmb__hint"><?php esc_html_e('支持 MP4 直链或视频平台嵌入链接。', 'aksrt'); ?></p>
</div>
</div>
</div>
<!-- 选项覆盖 -->
<div class="aksrt-pmb__section">
<div class="aksrt-pmb__section-title"><?php esc_html_e('选项覆盖', 'aksrt'); ?></div>
<div class="aksrt-pmb__row">
<label><?php esc_html_e('单条覆盖', 'aksrt'); ?></label>
<div>
<div style="display:flex; align-items:center; gap:6px; margin-bottom:6px;">
<input type="checkbox" id="aksrt_pay_require_login" name="aksrt_pay_require_login" value="1" <?php checked($meta['require_login']); ?>>
<label for="aksrt_pay_require_login" style="font-weight:400;padding:0;"><?php esc_html_e('强制登录后才可访问', 'aksrt'); ?></label>
</div>
<div style="display:flex; align-items:center; gap:6px;">
<input type="checkbox" id="aksrt_pay_require_captcha" name="aksrt_pay_require_captcha" value="1" <?php checked($meta['require_captcha']); ?>>
<label for="aksrt_pay_require_captcha" style="font-weight:400;padding:0;"><?php esc_html_e('强制需要人机验证', 'aksrt'); ?></label>
</div>
<p class="aksrt-pmb__hint"><?php esc_html_e('全局开关在后台统一控制,这里仅作为单条强制覆盖。', 'aksrt'); ?></p>
</div>
</div>
</div>
<!-- §3.8 电商增强 -->
<div class="aksrt-pmb__section">
<div class="aksrt-pmb__section-title"><?php esc_html_e('电商增强', 'aksrt'); ?></div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_virtual_sales"><?php esc_html_e('虚拟销量', 'aksrt'); ?></label>
<div>
<input type="number" id="aksrt_pay_virtual_sales" name="aksrt_pay_virtual_sales" value="<?php echo esc_attr((string) ($meta['virtual_sales'] ?? 0)); ?>" min="0" step="1">
<p class="aksrt-pmb__hint"><?php esc_html_e('在前端展示的销量基础上叠加此数值,用于展示效果。', 'aksrt'); ?></p>
</div>
</div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_limit_over_price"><?php esc_html_e('超额下载补价', 'aksrt'); ?></label>
<div>
<input type="number" id="aksrt_pay_limit_over_price" name="aksrt_pay_limit_over_price" value="<?php echo esc_attr((string) ($meta['download_limit_over_price'] ?? 0)); ?>" min="0" step="0.01">
<p class="aksrt-pmb__hint"><?php esc_html_e('免费日限额用尽后允许购买的补价(现金/积分随支付方式)。0 = 不允许超额购买。对齐 legacy download_limit_over_price。', 'aksrt'); ?></p>
</div>
</div>
<div class="aksrt-pmb__row">
<label><?php esc_html_e('优惠码', 'aksrt'); ?></label>
<div>
<div style="display:flex; align-items:center; gap:6px; margin-bottom:8px;">
<input type="checkbox" id="aksrt_pay_coupon_s" name="aksrt_pay_coupon_s" value="1" <?php checked(!empty($meta['coupon_s'])); ?>>
<label for="aksrt_pay_coupon_s" style="font-weight:400;padding:0;"><?php esc_html_e('允许使用优惠码(全站开关关闭时仍可对本篇启用)', 'aksrt'); ?></label>
</div>
<textarea id="aksrt_pay_coupon_desc" name="aksrt_pay_coupon_desc" style="min-height:60px;" placeholder="<?php esc_attr_e('本篇优惠码说明,可留空', 'aksrt'); ?>"><?php echo esc_textarea((string) ($meta['coupon_desc'] ?? '')); ?></textarea>
<p class="aksrt-pmb__hint"><?php esc_html_e('对齐 legacy coupon_s / coupon_desc。积分支付不使用优惠码。', 'aksrt'); ?></p>
</div>
</div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_rebate_discount"><?php esc_html_e('推广优惠金额', 'aksrt'); ?></label>
<div>
<input type="number" id="aksrt_pay_rebate_discount" name="aksrt_pay_rebate_discount" value="<?php echo esc_attr((string) ($meta['pay_rebate_discount'] ?? 0)); ?>" min="0" step="0.01">
<p class="aksrt-pmb__hint"><?php esc_html_e('有有效推荐人且开启推广返佣时,现金购买可先减此金额(买家侧推广折扣,对齐 legacy pay_rebate_discount)。0 = 不启用。', 'aksrt'); ?></p>
</div>
</div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_stock"><?php esc_html_e('库存', 'aksrt'); ?></label>
<div>
<input type="number" id="aksrt_pay_stock" name="aksrt_pay_stock" value="<?php echo esc_attr((string) ($meta['stock'] ?? -1)); ?>" min="-1" step="1">
<p class="aksrt-pmb__hint"><?php esc_html_e('-1 表示无限库存;>0 表示限量,售完即止。', 'aksrt'); ?></p>
</div>
</div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_purchase_limit"><?php esc_html_e('限购数量', 'aksrt'); ?></label>
<div>
<input type="number" id="aksrt_pay_purchase_limit" name="aksrt_pay_purchase_limit" value="<?php echo esc_attr((string) ($meta['purchase_limit'] ?? 0)); ?>" min="0" step="1">
<p class="aksrt-pmb__hint"><?php esc_html_e('每用户最多可购买次数,0 表示不限购。', 'aksrt'); ?></p>
</div>
</div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_card_keys"><?php esc_html_e('自动发货卡密', 'aksrt'); ?></label>
<div>
<textarea id="aksrt_pay_card_keys" name="aksrt_pay_card_keys" placeholder="<?php esc_attr_e('每行一个卡密/激活码,购买后自动发放一个', 'aksrt'); ?>"><?php echo esc_textarea(implode("\n", $meta['card_keys'] ?? [])); ?></textarea>
<p class="aksrt-pmb__hint"><?php esc_html_e('每行一个卡密,购买后自动消耗一个并发给用户。留空则不自动发货。', 'aksrt'); ?></p>
</div>
</div>
<div class="aksrt-pmb__row">
<label for="aksrt_pay_free_preview"><?php esc_html_e('免费试读段落', 'aksrt'); ?></label>
<div>
<textarea id="aksrt_pay_free_preview" name="aksrt_pay_free_preview" style="min-height:80px;" placeholder="<?php esc_attr_e('付费内容的免费预览段落(支持 HTML)', 'aksrt'); ?>"><?php echo esc_textarea($meta['free_preview'] ?? ''); ?></textarea>
<p class="aksrt-pmb__hint"><?php esc_html_e('未付费用户可见的预览内容,支持基础 HTML。留空则不显示预览。', 'aksrt'); ?></p>
</div>
</div>
<div class="aksrt-pmb__row">
<label><?php esc_html_e('销量显示', 'aksrt'); ?></label>
<div style="display:flex; align-items:center; gap:6px;">
<input type="checkbox" id="aksrt_pay_show_sales" name="aksrt_pay_show_sales" value="1" <?php checked($meta['show_sales'] ?? true); ?>>
<label for="aksrt_pay_show_sales" style="font-weight:400;padding:0;"><?php esc_html_e('在前端显示销量', 'aksrt'); ?></label>
</div>
</div>
</div>
</div>
<script>
(function () {
var typeSelect = document.getElementById('aksrt_pay_type');
var fields = document.getElementById('aksrt-pmb-fields');
var dlFields = document.getElementById('aksrt-pmb-download-fields');
var galleryFields = document.getElementById('aksrt-pmb-gallery-fields');
var videoFields = document.getElementById('aksrt-pmb-video-fields');
var resourceFields = document.getElementById('aksrt-pmb-resource-fields');
function updateVisibility() {
var t = typeSelect.value;
var isPaid = t !== 'no';
fields.setAttribute('data-disabled', isPaid ? '0' : '1');
var readingHint = document.getElementById('aksrt-pmb-reading-hint');
if (readingHint) readingHint.hidden = t !== 'reading';
// 根据付费类型显示/隐藏相关字段
if (dlFields) dlFields.style.display = (t === 'download') ? '' : 'none';
if (galleryFields) galleryFields.style.display = (t === 'gallery') ? '' : 'none';
if (videoFields) videoFields.style.display = (t === 'video') ? '' : 'none';
if (resourceFields) resourceFields.style.display = (t === 'download' || t === 'gallery' || t === 'video') ? '' : 'none';
<?php
}
if (typeSelect) {
typeSelect.addEventListener('change', updateVisibility);
updateVisibility();
/** @param array<string, string> $options */
private function radio(string $id, string $title, string $value, array $options, string $description = ''): void {
?>
<fieldset class="aksrt-pay-meta__field"><legend class="aksrt-pay-meta__title"><?php echo esc_html($title); ?></legend><div class="aksrt-pay-meta__control aksrt-pay-meta__radios">
<?php foreach ($options as $option => $label) : ?><label><input type="radio" name="aksrt_pay[<?php echo esc_attr($id); ?>]" value="<?php echo esc_attr((string) $option); ?>" <?php checked($value, (string) $option); ?>> <?php echo esc_html($label); ?></label><?php endforeach; ?>
<?php if ($description !== '') : ?><p class="description"><?php echo esc_html($description); ?></p><?php endif; ?>
</div></fieldset>
<?php
}
// 下载源 JSON 工具
var dlTa = document.getElementById('aksrt_pay_downloads');
var dlErr = document.getElementById('aksrt-pmb-dl-err');
var addDlBtn = document.getElementById('aksrt-pmb-add-dl');
var fmtDlBtn = document.getElementById('aksrt-pmb-fmt-dl');
if (addDlBtn && dlTa) {
addDlBtn.addEventListener('click', function () {
var sample = {label: '百度网盘', url: 'https://pan.baidu.com/s/xxx', type: 'net_disk', extract_code: ''};
try {
var arr = dlTa.value.trim() ? JSON.parse(dlTa.value) : [];
arr.push(sample);
dlTa.value = JSON.stringify(arr, null, 2);
if (dlErr) dlErr.style.display = 'none';
} catch (e) {
if (dlErr) { dlErr.textContent = 'JSON 解析错误: ' + e.message; dlErr.style.display = 'block'; }
}
});
private function number(string $id, string $title, string $value, string $step, string $unit = '', string $description = '', bool $non_negative = true): void {
?>
<div class="aksrt-pay-meta__field"><div class="aksrt-pay-meta__title"><label for="aksrt-pay-<?php echo esc_attr($id); ?>"><?php echo esc_html($title); ?></label></div><div class="aksrt-pay-meta__control"><div class="aksrt-pay-meta__number"><input id="aksrt-pay-<?php echo esc_attr($id); ?>" type="number" <?php echo $non_negative ? 'min="0"' : ''; ?> step="<?php echo esc_attr($step); ?>" name="aksrt_pay[<?php echo esc_attr($id); ?>]" value="<?php echo esc_attr($value); ?>"><?php if ($unit !== '') : ?><span><?php echo esc_html($unit); ?></span><?php endif; ?></div><?php if ($description !== '') : ?><p class="description"><?php echo esc_html($description); ?></p><?php endif; ?></div></div>
<?php
}
if (fmtDlBtn && dlTa) {
fmtDlBtn.addEventListener('click', function () {
try {
var arr = JSON.parse(dlTa.value);
dlTa.value = JSON.stringify(arr, null, 2);
if (dlErr) dlErr.style.display = 'none';
} catch (e) {
if (dlErr) { dlErr.textContent = 'JSON 解析错误: ' + e.message; dlErr.style.display = 'block'; }
private function text(string $id, string $title, string $value, string $description = '', string $placeholder = '', string $type = 'text'): void {
?>
<div class="aksrt-pay-meta__field"><div class="aksrt-pay-meta__title"><label for="aksrt-pay-<?php echo esc_attr($id); ?>"><?php echo esc_html($title); ?></label></div><div class="aksrt-pay-meta__control"><input id="aksrt-pay-<?php echo esc_attr($id); ?>" type="<?php echo esc_attr($type); ?>" name="aksrt_pay[<?php echo esc_attr($id); ?>]" value="<?php echo esc_attr($value); ?>" placeholder="<?php echo esc_attr($placeholder); ?>"><?php if ($description !== '') : ?><p class="description"><?php echo esc_html($description); ?></p><?php endif; ?></div></div>
<?php
}
});
private function textarea(string $id, string $title, string $value, int $rows, string $description = ''): void {
?>
<div class="aksrt-pay-meta__field"><div class="aksrt-pay-meta__title"><label for="aksrt-pay-<?php echo esc_attr($id); ?>"><?php echo esc_html($title); ?></label></div><div class="aksrt-pay-meta__control"><textarea id="aksrt-pay-<?php echo esc_attr($id); ?>" rows="<?php echo esc_attr((string) $rows); ?>" name="aksrt_pay[<?php echo esc_attr($id); ?>]"><?php echo esc_textarea($value); ?></textarea><?php if ($description !== '') : ?><p class="description"><?php echo esc_html($description); ?></p><?php endif; ?></div></div>
<?php
}
})();
</script>
private function upload(string $id, string $title, string $value, string $library, string $description = ''): void {
?>
<div class="aksrt-pay-meta__field"><div class="aksrt-pay-meta__title"><label for="aksrt-pay-<?php echo esc_attr($id); ?>"><?php echo esc_html($title); ?></label></div><div class="aksrt-pay-meta__control"><div class="aksrt-pay-meta__upload"><input id="aksrt-pay-<?php echo esc_attr($id); ?>" type="url" name="aksrt_pay[<?php echo esc_attr($id); ?>]" value="<?php echo esc_attr($value); ?>" data-media-input><button type="button" class="button" data-media-select data-library="<?php echo esc_attr($library); ?>"><?php esc_html_e('选择', 'aksrt'); ?></button><button type="button" class="button-link-delete" data-media-clear><?php esc_html_e('移除', 'aksrt'); ?></button></div><?php if ($description !== '') : ?><p class="description"><?php echo esc_html($description); ?></p><?php endif; ?></div></div>
<?php
}
/** @param array<int, array<string, mixed>> $rows */
private function group(string $id, string $title, array $rows, string $renderer, string $button, string $description = ''): void {
?>
<div class="aksrt-pay-meta__field"><div class="aksrt-pay-meta__title"><?php echo esc_html($title); ?></div><div class="aksrt-pay-meta__control"><?php if ($description !== '') : ?><p class="description"><?php echo esc_html($description); ?></p><?php endif; ?><div data-group-list="<?php echo esc_attr($id); ?>"><?php foreach ($rows as $index => $row) { $this->{$renderer}((int) $index, (array) $row); } ?></div><button type="button" class="button" data-group-add="<?php echo esc_attr($id); ?>"><?php echo esc_html($button); ?></button><template data-group-template="<?php echo esc_attr($id); ?>"><?php $this->{$renderer}('__INDEX__', []); ?></template></div></div>
<?php
}
/** @param int|string $index */
private function video_row($index, array $row): void {
?>
<div class="aksrt-pay-meta__group" data-group-row><input type="text" name="aksrt_pay[video_episode][<?php echo esc_attr((string) $index); ?>][title]" value="<?php echo esc_attr((string) ($row['title'] ?? '')); ?>" placeholder="<?php esc_attr_e('剧集标题', 'aksrt'); ?>"><div class="aksrt-pay-meta__upload"><input type="url" name="aksrt_pay[video_episode][<?php echo esc_attr((string) $index); ?>][url]" value="<?php echo esc_attr((string) ($row['url'] ?? '')); ?>" placeholder="<?php esc_attr_e('选择视频或填写视频地址', 'aksrt'); ?>" data-media-input><button type="button" class="button" data-media-select data-library="video"><?php esc_html_e('选择', 'aksrt'); ?></button></div><button type="button" class="button-link-delete" data-group-remove><?php esc_html_e('移除', 'aksrt'); ?></button></div>
<?php
}
/** @param int|string $index */
private function download_row($index, array $row): void {
?>
<div class="aksrt-pay-meta__group" data-group-row><div class="aksrt-pay-meta__upload"><input type="url" name="aksrt_pay[downloads][<?php echo esc_attr((string) $index); ?>][url]" value="<?php echo esc_attr((string) ($row['url'] ?? '')); ?>" placeholder="<?php esc_attr_e('上传文件或输入下载地址', 'aksrt'); ?>" data-media-input><button type="button" class="button" data-media-select data-library=""><?php esc_html_e('选择', 'aksrt'); ?></button></div><input type="text" name="aksrt_pay[downloads][<?php echo esc_attr((string) $index); ?>][label]" value="<?php echo esc_attr((string) ($row['label'] ?? '')); ?>" placeholder="<?php esc_attr_e('按钮文案', 'aksrt'); ?>"><textarea rows="1" name="aksrt_pay[downloads][<?php echo esc_attr((string) $index); ?>][extract_code]" placeholder="<?php esc_attr_e('资源备注、提取码或解压密码', 'aksrt'); ?>"><?php echo esc_textarea((string) ($row['extract_code'] ?? '')); ?></textarea><button type="button" class="button-link-delete" data-group-remove><?php esc_html_e('移除', 'aksrt'); ?></button></div>
<?php
}
/** @param int|string $index */
private function attribute_row($index, array $row): void {
?>
<div class="aksrt-pay-meta__group aksrt-pay-meta__group--inline" data-group-row><input type="text" name="aksrt_pay[attributes][<?php echo esc_attr((string) $index); ?>][key]" value="<?php echo esc_attr((string) ($row['key'] ?? '')); ?>" placeholder="<?php esc_attr_e('属性名称', 'aksrt'); ?>"><input type="text" name="aksrt_pay[attributes][<?php echo esc_attr((string) $index); ?>][value]" value="<?php echo esc_attr((string) ($row['value'] ?? '')); ?>" placeholder="<?php esc_attr_e('属性内容', 'aksrt'); ?>"><button type="button" class="button-link-delete" data-group-remove><?php esc_html_e('移除', 'aksrt'); ?></button></div>
<?php
}
public function save(int $post_id, \WP_Post $post): void {
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
if (wp_is_post_revision($post_id)) return;
if ($post->post_type !== 'post') return;
if (empty($_POST['aksrt_pay_meta_nonce'])
|| !wp_verify_nonce((string) $_POST['aksrt_pay_meta_nonce'], 'aksrt_pay_meta_save')) {
if ($post->post_type !== 'post' || (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) || wp_is_post_revision($post_id) || !current_user_can('edit_post', $post_id)) {
return;
}
if (!current_user_can('edit_post', $post_id)) return;
// 付费类型
$allowed_types = array_keys(aksrt_get_pay_types());
$pay_type = sanitize_key((string) ($_POST['aksrt_pay_type'] ?? 'no'));
if (!in_array($pay_type, $allowed_types, true)) {
$pay_type = 'no';
if (empty($_POST['aksrt_pay_meta_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash((string) $_POST['aksrt_pay_meta_nonce'])), 'aksrt_pay_meta_save')) {
return;
}
// 价格字段
$pay_modo = sanitize_key((string) ($_POST['aksrt_pay_modo'] ?? '0'));
if ($pay_modo !== 'points') {
$pay_modo = '0';
$raw = isset($_POST['aksrt_pay']) && is_array($_POST['aksrt_pay']) ? wp_unslash($_POST['aksrt_pay']) : [];
$gallery = [];
$ids = array_values(array_filter(array_map('absint', preg_split('/[\s,]+/', (string) ($raw['gallery_ids'] ?? '')) ?: [])));
foreach (array_slice($ids, 0, 100) as $attachment_id) {
$url = wp_get_attachment_image_url($attachment_id, 'full');
if ($url) {
$gallery[] = $url;
}
$price = trim((string) wp_unslash($_POST['aksrt_pay_price'] ?? '0'));
$points_price = max(0, (int) ($_POST['aksrt_pay_points_price'] ?? 0));
$original_price = trim((string) wp_unslash($_POST['aksrt_pay_original_price'] ?? '0'));
$vip1_price = trim((string) wp_unslash($_POST['aksrt_pay_vip1_price'] ?? '0'));
$vip2_price = trim((string) wp_unslash($_POST['aksrt_pay_vip2_price'] ?? '0'));
$vip1_points = max(0, (int) ($_POST['aksrt_pay_vip1_points'] ?? 0));
$vip2_points = max(0, (int) ($_POST['aksrt_pay_vip2_points'] ?? 0));
$pay_limit = (int) ($_POST['aksrt_pay_limit'] ?? 0);
if (!in_array($pay_limit, [0, 1, 2], true)) {
$pay_limit = 0;
}
// 资源信息
$version = sanitize_text_field((string) ($_POST['aksrt_pay_version'] ?? ''));
$size = sanitize_text_field((string) ($_POST['aksrt_pay_size'] ?? ''));
$compat = sanitize_text_field((string) ($_POST['aksrt_pay_compat'] ?? ''));
$license = sanitize_text_field((string) ($_POST['aksrt_pay_license'] ?? ''));
$demo_url = esc_url_raw((string) ($_POST['aksrt_pay_demo_url'] ?? ''));
$video_url = esc_url_raw((string) ($_POST['aksrt_pay_video_url'] ?? ''));
// 布尔字段
$require_login = !empty($_POST['aksrt_pay_require_login']);
$require_captcha = !empty($_POST['aksrt_pay_require_captcha']);
// JSON 字段
$downloads = $this->parse_json_array($_POST['aksrt_pay_downloads'] ?? '');
$attributes = $this->parse_json_array($_POST['aksrt_pay_attributes'] ?? '');
$gallery = $this->parse_json_array($_POST['aksrt_pay_gallery'] ?? '');
// 清洗下载源
if ($downloads !== null && class_exists('AKSRT\\Core\\Setup') && method_exists('AKSRT\\Core\\Setup', 'sanitize_downloads_meta')) {
$downloads = Setup::sanitize_downloads_meta($downloads);
}
// 保存统一元数据
$pay_meta = DomainModel::sanitize_pay_meta([
'pay_type' => $pay_type,
'pay_modo' => $pay_modo,
'price' => $price,
'points_price' => $points_price,
'original_price' => $original_price,
'vip1_price' => $vip1_price,
'vip2_price' => $vip2_price,
'vip1_points' => $vip1_points,
'vip2_points' => $vip2_points,
'pay_limit' => $pay_limit,
'version' => $version,
'size' => $size,
'compat' => $compat,
'license' => $license,
'demo_url' => $demo_url,
'video_url' => $video_url,
'downloads' => $downloads ?? [],
'attributes' => $attributes ?? [],
'gallery' => $gallery ?? [],
'require_login' => $require_login,
'require_captcha' => $require_captcha,
// §3.8 电商增强字段
'virtual_sales' => $_POST['aksrt_pay_virtual_sales'] ?? 0,
'stock' => $_POST['aksrt_pay_stock'] ?? -1,
'purchase_limit' => $_POST['aksrt_pay_purchase_limit'] ?? 0,
'card_keys' => isset($_POST['aksrt_pay_card_keys']) ? array_filter(array_map('trim', explode("\n", (string) $_POST['aksrt_pay_card_keys']))) : [],
'free_preview' => $_POST['aksrt_pay_free_preview'] ?? '',
'show_sales' => !empty($_POST['aksrt_pay_show_sales']),
'download_limit_over_price' => trim((string) wp_unslash($_POST['aksrt_pay_limit_over_price'] ?? '0')),
'coupon_s' => !empty($_POST['aksrt_pay_coupon_s']),
'coupon_desc' => (string) ($_POST['aksrt_pay_coupon_desc'] ?? ''),
'pay_rebate_discount' => trim((string) wp_unslash($_POST['aksrt_pay_rebate_discount'] ?? '0')),
$meta = DomainModel::sanitize_pay_meta([
'pay_type' => $raw['pay_type'] ?? 'no',
'pay_limit' => $raw['pay_limit'] ?? 0,
'pay_modo' => $raw['pay_modo'] ?? '0',
'order_expired_s' => !empty($raw['order_expired_s']),
'order_expired_time' => $raw['order_expired_time'] ?? 0,
'order_expired_unit' => $raw['order_expired_unit'] ?? 'day',
'price' => (string) ($raw['price'] ?? '0'),
'points_price' => $raw['points_price'] ?? 0,
'original_price' => (string) ($raw['original_price'] ?? '0'),
'promotion_tag' => (string) ($raw['promotion_tag'] ?? ''),
'vip1_price' => (string) ($raw['vip1_price'] ?? '0'),
'vip2_price' => (string) ($raw['vip2_price'] ?? '0'),
'vip1_points' => $raw['vip1_points'] ?? 0,
'vip2_points' => $raw['vip2_points'] ?? 0,
'pay_count' => $raw['pay_count'] ?? 0,
'gallery' => $gallery,
'gallery_show' => $raw['gallery_show'] ?? 1,
'video_url' => (string) ($raw['video_url'] ?? ''),
'video_pic' => (string) ($raw['video_pic'] ?? ''),
'video_title' => (string) ($raw['video_title'] ?? ''),
'video_episode' => $raw['video_episode'] ?? [],
'video_scale_height' => $raw['video_scale_height'] ?? 0,
'downloads' => $raw['downloads'] ?? [],
'attributes' => $raw['attributes'] ?? [],
'demo_url' => (string) ($raw['demo_url'] ?? ''),
'pay_title' => (string) ($raw['pay_title'] ?? ''),
'pay_doc' => (string) ($raw['pay_doc'] ?? ''),
'pay_details' => (string) ($raw['pay_details'] ?? ''),
'pay_extra_hide' => (string) ($raw['pay_extra_hide'] ?? ''),
]);
update_post_meta($post_id, DomainModel::PAY_META_KEY, $pay_meta);
// 同步标量字段用于查询(模仿 legacy 的 aksrtpay_type 同步)
update_post_meta($post_id, DomainModel::PAY_TYPE_KEY, (string) $pay_meta['pay_type']);
$query_price = ($pay_meta['pay_modo'] === 'points')
? (int) $pay_meta['points_price']
: (string) $pay_meta['price'];
update_post_meta($post_id, DomainModel::PAY_PRICE_KEY, $query_price);
update_post_meta($post_id, '_aksrt_pay_modo', (string) $pay_meta['pay_modo']);
update_post_meta($post_id, '_aksrt_pay_limit', (int) $pay_meta['pay_limit']);
// 清理旧的 _aksrt_is_resource 标记
update_post_meta(
$post_id,
DomainModel::RESOURCE_MARKER_KEY,
DomainModel::is_resource_pay_type((string) $pay_meta['pay_type']) ? 1 : 0
);
}
/**
* 安全解析 JSON 数组。
*/
private function parse_json_array(string $raw): ?array {
$raw = trim($raw);
if ($raw === '') return [];
$decoded = json_decode($raw, true);
return is_array($decoded) ? $decoded : null;
update_post_meta($post_id, DomainModel::PAY_META_KEY, $meta);
update_post_meta($post_id, DomainModel::PAY_TYPE_KEY, (string) $meta['pay_type']);
update_post_meta($post_id, DomainModel::PAY_PRICE_KEY, $meta['pay_modo'] === 'points' ? (int) $meta['points_price'] : (string) $meta['price']);
update_post_meta($post_id, '_aksrt_pay_modo', (string) $meta['pay_modo']);
update_post_meta($post_id, '_aksrt_pay_limit', (int) $meta['pay_limit']);
update_post_meta($post_id, DomainModel::RESOURCE_MARKER_KEY, DomainModel::is_resource_pay_type((string) $meta['pay_type']) ? 1 : 0);
}
}
+27 -5
View File
@@ -38,7 +38,7 @@ class SEOMetaBox {
foreach (['post', 'page'] as $screen) {
add_meta_box(
'aksrt_seo_meta',
__('独立 SEO', 'aksrt'),
__('独立SEO', 'aksrt'),
[$this, 'render_meta_box'],
$screen,
'normal',
@@ -58,19 +58,34 @@ class SEOMetaBox {
$summary_btn = $this->render_summary_button((int) $post->ID);
?>
<div class="aksrt-seo-meta-box">
<div class="notice notice-warning inline"><p><?php esc_html_e('系统默认调用标题、分类标签、描述等自动生成SEO内容,一般无需单独设置SEO内容;如需覆盖,请在下方自定义。', 'aksrt'); ?></p></div>
<div class="aksrt-seo-preview">
<strong><?php esc_html_e('SEO预览', 'aksrt'); ?></strong>
<div class="aksrt-seo-preview__title"><?php echo esc_html($title !== '' ? $title : get_the_title($post)); ?></div>
<div class="aksrt-seo-preview__url"><?php echo esc_html(get_permalink($post)); ?></div>
<div class="aksrt-seo-preview__description"><?php echo esc_html($description !== '' ? $description : wp_trim_words(wp_strip_all_tags((string) $post->post_content), 38, '…')); ?></div>
</div>
<p>
<label for="aksrt_seo_title"><strong><?php esc_html_e('SEO 标题', 'aksrt'); ?></strong></label>
<label for="aksrt_seo_title"><strong><?php esc_html_e('自定义SEO标题', 'aksrt'); ?></strong></label>
<input type="text" class="widefat" id="aksrt_seo_title" name="aksrt_seo_title" value="<?php echo esc_attr($title); ?>" />
<span class="description"><?php esc_html_e('Title 一般建议15到30个字符', 'aksrt'); ?></span>
</p>
<p>
<label for="aksrt_seo_keywords"><strong><?php esc_html_e('SEO 关键词', 'aksrt'); ?></strong></label>
<label for="aksrt_seo_keywords"><strong><?php esc_html_e('自定义SEO关键词', 'aksrt'); ?></strong></label>
<input type="text" class="widefat" id="aksrt_seo_keywords" name="aksrt_seo_keywords" value="<?php echo esc_attr($keywords); ?>" placeholder="<?php esc_attr_e('多个关键词用英文逗号分隔', 'aksrt'); ?>" />
<span class="description"><?php esc_html_e('Keywords 每个关键词用逗号隔开', 'aksrt'); ?></span>
</p>
<p>
<label for="aksrt_seo_description"><strong><?php esc_html_e('SEO 描述', 'aksrt'); ?></strong></label>
<label for="aksrt_seo_description"><strong><?php esc_html_e('自定义SEO描述', 'aksrt'); ?></strong></label>
<textarea class="widefat" rows="3" id="aksrt_seo_description" name="aksrt_seo_description"><?php echo esc_textarea($description); ?></textarea>
<span class="description"><?php esc_html_e('建议 50–150 字。', 'aksrt'); ?><?php echo $ai_btn; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
<span class="description"><?php esc_html_e('Description 一般建议50到150个字符', 'aksrt'); ?><?php echo $ai_btn; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
</p>
<details class="aksrt-seo-advice"><summary><span class="dashicons dashicons-star-filled"></span> <?php esc_html_e('SEO优化建议', 'aksrt'); ?></summary><div>
<p><strong><?php esc_html_e('SEO标题:', 'aksrt'); ?></strong><?php esc_html_e('标题应紧扣主要内容,重要关键词靠前,避免关键词堆砌。', 'aksrt'); ?></p>
<p><strong><?php esc_html_e('SEO关键词:', 'aksrt'); ?></strong><?php esc_html_e('一般建议4到8个,并与网站定位及正文保持一致。', 'aksrt'); ?></p>
<p><strong><?php esc_html_e('SEO描述:', 'aksrt'); ?></strong><?php esc_html_e('使用一段通顺、有吸引力的文字精炼概括页面内容。', 'aksrt'); ?></p>
<p class="description"><?php esc_html_e('优化建议来自互联网,仅供参考。', 'aksrt'); ?></p>
</div></details>
<?php if ($summary_btn !== '') : ?>
<p class="aksrt-ai-summary-row">
<span class="description"><?php esc_html_e('写作辅助:根据正文生成摘要并填入「摘要」字段(不自动保存)。', 'aksrt'); ?></span>
@@ -228,6 +243,13 @@ class SEOMetaBox {
.aksrt-ai-seo-msg.is-error{color:#b32d2e}
.aksrt-ai-seo-msg.is-ok{color:#007017}
.aksrt-ai-summary-row{margin-top:8px;padding-top:8px;border-top:1px solid #eee}
.aksrt-seo-preview{margin:12px 0;padding:14px;border:1px solid #e5e5e5;border-radius:4px;background:#fff}
.aksrt-seo-preview__title{margin-top:8px;color:#1a0dab;font-size:18px;line-height:1.3}
.aksrt-seo-preview__url{margin:4px 0;color:#188038;font-size:13px;word-break:break-all}
.aksrt-seo-preview__description{color:#4d5156;line-height:1.5}
.aksrt-seo-advice{margin-top:12px;border:1px solid #e5e5e5;border-radius:4px}
.aksrt-seo-advice summary{padding:10px 12px;cursor:pointer;font-weight:600}
.aksrt-seo-advice>div{padding:0 12px 10px}
');
}
+9 -7
View File
@@ -3,7 +3,7 @@
* Template Name: AKSRT - 写文章/投稿
* Template Post Type: page
*
* 前台文章投稿页(对齐 legacy newposts:双栏 + TinyMCE + 封面 + 付费 + 草稿)。
* Front-end article submission page matching the reference newposts layout.
* 注意:page-newposts.php 是「最新文章」列表,不是投稿。
*
* @package AKSRT
@@ -124,17 +124,19 @@ if (file_exists($js)) {
get_header();
?>
<main id="main-content" class="site-main container aksrt-article-compose" tabindex="-1">
<form class="aksrt-submit-form aksrt-article-compose__form" id="aksrt-submit-form" data-aksrt-submit-form data-dirty="false" novalidate>
<div class="aksrt-article-compose__layout">
<main id="main-content" role="main" class="container" tabindex="-1">
<form id="aksrt-submit-form" data-aksrt-submit-form data-dirty="false">
<div class="content-wrap newposts-wrap">
<div class="content-layout">
<?php
$main = get_template_directory() . '/templates/parts/submit/editor-main.php';
if (file_exists($main)) {
include $main;
}
?>
<aside class="aksrt-submit-sidebar">
<?php \AKSRT\WidgetSystem\WidgetAreas::render('newposts_sidebar_top', 'aksrt-submit-sidebar__widgets'); ?>
</div>
<aside class="sidebar">
<?php \AKSRT\WidgetSystem\WidgetAreas::render('newposts_sidebar_top'); ?>
<?php
$terms = get_template_directory() . '/templates/parts/submit/sidebar-terms.php';
if (file_exists($terms)) {
@@ -149,7 +151,7 @@ get_header();
include $actions;
}
?>
<?php \AKSRT\WidgetSystem\WidgetAreas::render('newposts_sidebar_bottom', 'aksrt-submit-sidebar__widgets'); ?>
<?php \AKSRT\WidgetSystem\WidgetAreas::render('newposts_sidebar_bottom'); ?>
</aside>
</div>
</form>
+12 -4
View File
@@ -66,13 +66,21 @@ if ($edit_post) {
];
} elseif (!empty($post_meta['featured_slide'])) {
$slides = array_filter(array_map('trim', explode(',', (string) $post_meta['featured_slide'])));
$slide_data = [];
foreach ($slides as $attachment_id) {
$id = absint($attachment_id);
$url = $id > 0 ? wp_get_attachment_image_url($id, 'full') : false;
if ($url) {
$slide_data[] = ['id' => $id, 'url' => $url];
}
}
if ($slide_data) {
$featured_args['type'] = 'slide';
$featured_args['data'] = array_map(static function ($u) {
return ['url' => $u];
}, $slides);
$featured_args['data'] = $slide_data;
}
} else {
$thumb = get_the_post_thumbnail_url($edit_post, 'full')
?: (string) ($post_meta['thumbnail_url'] ?? $post_meta['cover_images'] ?? '');
?: (string) ($post_meta['thumbnail_url'] ?? $post_meta['cover_image'] ?? '');
if ($thumb !== '') {
$featured_args['type'] = 'image';
$featured_args['data'] = ['url' => $thumb];
+13 -13
View File
@@ -11,8 +11,7 @@ $settings = $ctx['settings'];
$edit_post = $ctx['edit_post'];
$is_can_new = !empty($ctx['is_can_new']);
?>
<div class="aksrt-submit-main">
<div class="aksrt-submit-panel aksrt-submit-panel--editor">
<div class="zib-widget full-widget-sm editor-main-box" style="min-height:60vh;">
<?php
$featured_path = get_template_directory() . '/templates/parts/submit/featured.php';
if (file_exists($featured_path)) {
@@ -20,27 +19,29 @@ $is_can_new = !empty($ctx['is_can_new']);
}
?>
<div class="aksrt-submit-title-wrap">
<div class="relative newposts-title">
<textarea
name="post_title"
id="submit-title"
class="aksrt-submit-title"
class="line-form-input input-lg new-title"
tabindex="1"
rows="1"
maxlength="<?php echo esc_attr((string) ((int) ($settings['title_max'] ?? 30) * 4)); ?>"
placeholder="<?php echo esc_attr((string) ($settings['title_placeholder'] ?? __('请输入标题', 'aksrt'))); ?>"
data-auto-height
data-max-height="<?php echo wp_is_mobile() ? '110' : '78'; ?>"
><?php echo esc_textarea((string) $ctx['current_title']); ?></textarea>
<i class="line-form-line"></i>
</div>
<?php if (!$is_can_new) : ?>
<div class="aksrt-submit-nocan">
<div class="aksrt-submit-nocan__icon"><i class="fa fas fa-lock"></i></div>
<div class="flex jc newposts-nocan" style="min-height:50vh;">
<div class="text-center"><div><i class="fa fas fa-lock"></i></div>
<p><?php esc_html_e('无法发布', 'aksrt'); ?></p>
<p class="muted"><?php esc_html_e('你当前没有前台投稿权限,请联系管理员开通。', 'aksrt'); ?></p>
<p class="muted-color"><?php esc_html_e('你当前没有前台投稿权限,请联系管理员开通。', 'aksrt'); ?></p></div>
</div>
<?php else : ?>
<div class="aksrt-submit-editor-wrap">
<div class="newposts-editor">
<?php
$toolbar1 = 'formatselect,bold,italic,bullist,numlist,alignleft,aligncenter,alignright,link,blockquote,code,fullscreen';
$plugins = 'paste,link,lists,fullscreen,wordpress,wplink,charmap';
@@ -64,7 +65,7 @@ $is_can_new = !empty($ctx['is_can_new']);
</div>
<?php if (!empty($ctx['can_hide']) || !empty($ctx['can_pay']) || !empty($ctx['can_upload_img'])) : ?>
<div class="aksrt-submit-shortcode-tools">
<div class="newposts-editor-tools">
<?php if (!empty($ctx['can_upload_img'])) : ?>
<button type="button" class="but" data-insert-upload-image>
<i class="fa fas fa-image"></i> <?php esc_html_e('上传图片', 'aksrt'); ?>
@@ -90,8 +91,8 @@ $is_can_new = !empty($ctx['is_can_new']);
<?php endif; ?>
<?php endif; ?>
<div class="aksrt-submit-status-row">
<span class="aksrt-submit-view-btn">
<div class="flex ac jsb mt10">
<span class="view-btn aksrt-submit-view-btn">
<?php if ($edit_post) : ?>
<a class="but c-blue" target="_blank" href="<?php echo esc_url(get_permalink($edit_post)); ?>">
<i class="fa fas fa-file-alt"></i> <?php esc_html_e('预览文章', 'aksrt'); ?>
@@ -121,11 +122,10 @@ $is_can_new = !empty($ctx['is_can_new']);
<?php endif; ?>
<?php endif; ?>
</span>
<span class="aksrt-submit-modified-time">
<span class="modified-time aksrt-submit-modified-time">
<?php if (!empty($ctx['post_modified'])) : ?>
<span class="badg"><?php echo esc_html(sprintf(__('最后保存:%s', 'aksrt'), $ctx['post_modified'])); ?></span>
<?php endif; ?>
</span>
</div>
</div>
</div>
+11 -12
View File
@@ -21,51 +21,50 @@ $can_image = !empty($ctx['can_cover_image']);
$can_slide = !empty($ctx['can_cover_slide']);
$can_video = !empty($ctx['can_cover_video']);
?>
<div class="aksrt-submit-featured featured-edit"
<div class="featured-edit<?php echo $type !== '' ? ' featured-loaded ' . esc_attr($type) : ''; ?>"
data-featured-args="<?php echo esc_attr(wp_json_encode($args)); ?>"
data-can-image="<?php echo $can_image ? '1' : '0'; ?>"
data-can-slide="<?php echo $can_slide ? '1' : '0'; ?>"
data-can-video="<?php echo $can_video ? '1' : '0'; ?>">
<div class="aksrt-submit-featured__preview" data-featured-preview>
<div class="featured-preview" data-featured-preview>
<?php if ($type === 'image' && !empty($data['url'])) : ?>
<img src="<?php echo esc_url((string) $data['url']); ?>" alt="">
<?php elseif ($type === 'slide' && is_array($data) && !empty($data[0]['url'])) : ?>
<img src="<?php echo esc_url((string) $data[0]['url']); ?>" alt="">
<span class="aksrt-submit-featured__badge"><i class="fa fas fa-image"></i></span>
<span class="featured-type-badge"><i class="fa fas fa-image"></i></span>
<?php elseif ($type === 'video') : ?>
<?php if (!empty($data['pic'])) : ?>
<img src="<?php echo esc_url((string) $data['pic']); ?>" alt="">
<?php else : ?>
<div class="aksrt-submit-featured__video-placeholder"><i class="fa fas fa-play"></i></div>
<div class="featured-video-placeholder"><i class="fa fas fa-play"></i></div>
<?php endif; ?>
<span class="aksrt-submit-featured__badge"><i class="fa fas fa-video"></i></span>
<span class="featured-type-badge"><i class="fa fas fa-video"></i></span>
<?php else : ?>
<div class="aksrt-submit-featured__empty">
<div class="featured-empty">
<i class="fa fas fa-image"></i>
<span><?php esc_html_e('设置文章封面', 'aksrt'); ?></span>
</div>
<?php endif; ?>
</div>
<div class="aksrt-submit-featured__actions">
<div class="btns-full flex ac jc">
<?php if ($can_image) : ?>
<button type="button" class="but" data-featured-action="image">
<button type="button" class="but btn-image" data-featured-action="image">
<i class="fa fas fa-image"></i> <?php esc_html_e('图片', 'aksrt'); ?>
</button>
<?php endif; ?>
<?php if ($can_slide) : ?>
<button type="button" class="but" data-featured-action="slide">
<button type="button" class="but btn-slide" data-featured-action="slide">
<i class="fa fas fa-image"></i> <?php esc_html_e('幻灯片', 'aksrt'); ?>
</button>
<?php endif; ?>
<?php if ($can_video) : ?>
<button type="button" class="but" data-featured-action="video">
<button type="button" class="but btn-video" data-featured-action="video">
<i class="fa fas fa-video"></i> <?php esc_html_e('视频', 'aksrt'); ?>
</button>
<?php endif; ?>
<button type="button" class="but c-red" data-featured-action="close" <?php echo $type === '' ? 'hidden' : ''; ?>>
<button type="button" class="but c-red btn-delete" data-featured-action="close" <?php echo $type === '' ? 'hidden' : ''; ?>>
<i class="fa fas fa-trash-alt"></i> <?php esc_html_e('清除', 'aksrt'); ?>
</button>
</div>
<input type="hidden" name="featured_type" value="<?php echo esc_attr($type); ?>" data-featured-type>
<input type="hidden" name="featured_data" value="<?php echo esc_attr(wp_json_encode(['type' => $type, 'data' => $data])); ?>" data-featured-json>
</div>
+51 -127
View File
@@ -1,163 +1,87 @@
<?php
/**
* Pay content sidebar card.
*
* @var array $ctx
* @package AKSRT
*/
/** @var array $ctx */
defined('ABSPATH') || exit;
if (empty($ctx['can_pay'])) {
return;
}
if (empty($ctx['can_pay'])) return;
$settings = $ctx['settings'];
$pay = is_array($ctx['pay_meta'] ?? null) ? $ctx['pay_meta'] : [];
$enabled = !empty($pay['pay_type']) && ($pay['pay_type'] !== 'no');
$modo = (($pay['pay_modo'] ?? '0') === 'points') ? 'points' : '0';
$enabled = !empty($pay['pay_type']) && $pay['pay_type'] !== 'no';
$modo = ($pay['pay_modo'] ?? '0') === 'points' ? 'points' : '0';
$points_on = !empty($ctx['points_enabled']);
$vip_manual = !empty($settings['pay_vip_price_s']);
$manual = !empty($settings['pay_vip_price_s']);
$vip1_on = !empty($ctx['vip']['vip1_enabled']);
$vip2_on = !empty($ctx['vip']['vip2_enabled']);
$vip1_name = (string) ($ctx['vip']['vip1_name'] ?? 'VIP1');
$vip2_name = (string) ($ctx['vip']['vip2_name'] ?? 'VIP2');
$downloads = is_array($pay['downloads'] ?? null) ? $pay['downloads'] : [];
if (!function_exists('aksrt_submit_pay_line')) {
function aksrt_submit_pay_line(string $label, string $name, string $value, string $step): void {
?>
<div class="relative mt6"><div class="flex ab"><div class="muted-color mb6 flex0"><?php echo esc_html($label); ?></div><input type="number" min="0" step="<?php echo esc_attr($step); ?>" name="<?php echo esc_attr($name); ?>" value="<?php echo esc_attr($value); ?>" class="line-form-input em2x key-color text-right"><i class="line-form-line"></i></div></div>
<?php
}
}
?>
<div class="aksrt-submit-panel aksrt-submit-pay dependency-box" data-pay-box>
<div class="aksrt-submit-panel__title"><?php esc_html_e('付费内容', 'aksrt'); ?></div>
<div class="aksrt-submit-panel__body">
<div class="aksrt-submit-pay__row">
<span><?php esc_html_e('内容付费', 'aksrt'); ?></span>
<label class="aksrt-switch">
<input type="checkbox" name="pay_enabled" value="1" <?php checked($enabled); ?> data-pay-toggle>
<span class="aksrt-switch__ui"></span>
</label>
<div class="zib-widget dependency-box mb10-sm newpost-pay-box" data-pay-box>
<div class="title-theme mb10"><?php esc_html_e('付费内容', 'aksrt'); ?></div>
<div class="flex ac jsb padding-h10 border-bottom">
<div class="flex ac"><?php esc_html_e('内容付费', 'aksrt'); ?></div>
<label class="form-switch-label"><input class="hide" type="checkbox" name="pay_enabled" value="1" <?php checked($enabled); ?> data-pay-toggle><span class="form-switch flex0"></span></label>
</div>
<div class="aksrt-submit-pay__body" data-pay-fields <?php echo $enabled ? '' : 'hidden'; ?>>
<div data-pay-fields <?php echo $enabled ? '' : 'hidden'; ?>>
<?php if ($points_on) : ?>
<div class="aksrt-submit-pay__row">
<span><?php esc_html_e('支付类型', 'aksrt'); ?></span>
<div class="aksrt-submit-segmented">
<button type="button" class="but <?php echo $modo !== 'points' ? 'active' : ''; ?>" data-pay-modo="0"><?php esc_html_e('现金', 'aksrt'); ?></button>
<button type="button" class="but <?php echo $modo === 'points' ? 'active' : ''; ?>" data-pay-modo="points"><?php esc_html_e('积分', 'aksrt'); ?></button>
<div class="flex ac jsb padding-h10">
<div class="flex ac"><?php esc_html_e('支付类型', 'aksrt'); ?></div>
<div class="but-average radius em09">
<button type="button" class="but p2-10 pointer<?php echo $modo !== 'points' ? ' active' : ''; ?>" data-pay-modo="0"><?php esc_html_e('现金支付', 'aksrt'); ?></button>
<button type="button" class="but p2-10 pointer<?php echo $modo === 'points' ? ' active' : ''; ?>" data-pay-modo="points"><?php esc_html_e('积分支付', 'aksrt'); ?></button>
</div>
</div>
<?php endif; ?>
<input type="hidden" name="aksrt_pay[pay_modo]" value="<?php echo esc_attr($modo); ?>" data-pay-modo-input>
<input type="hidden" name="aksrt_pay[pay_type]" value="<?php echo esc_attr((string) ($pay['pay_type'] ?? 'reading')); ?>" data-pay-type-input>
<div data-pay-cash <?php echo $modo === 'points' ? 'hidden' : ''; ?>>
<label class="aksrt-submit-field">
<span><?php esc_html_e('设置价格', 'aksrt'); ?></span>
<input type="number" step="0.01" min="0" name="aksrt_pay[price]" class="aksrt-form-input" value="<?php echo esc_attr((string) ($pay['price'] ?? '')); ?>">
</label>
<?php if ($vip_manual && $vip1_on) : ?>
<label class="aksrt-submit-field">
<span><?php echo esc_html(sprintf(__('%s 价格', 'aksrt'), $vip1_name)); ?></span>
<input type="number" step="0.01" min="0" name="aksrt_pay[vip1_price]" class="aksrt-form-input" value="<?php echo esc_attr((string) ($pay['vip1_price'] ?? '')); ?>">
</label>
<?php endif; ?>
<?php if ($vip_manual && $vip2_on) : ?>
<label class="aksrt-submit-field">
<span><?php echo esc_html(sprintf(__('%s 价格', 'aksrt'), $vip2_name)); ?></span>
<input type="number" step="0.01" min="0" name="aksrt_pay[vip2_price]" class="aksrt-form-input" value="<?php echo esc_attr((string) ($pay['vip2_price'] ?? '')); ?>">
</label>
<?php endif; ?>
<div class="mt10 newpost-pay-price" data-pay-cash <?php echo $modo === 'points' ? 'hidden' : ''; ?>>
<?php aksrt_submit_pay_line(__('设置价格', 'aksrt'), 'aksrt_pay[price]', (string) ($pay['price'] ?? ''), '0.01'); ?>
<?php if ($manual && $vip1_on) aksrt_submit_pay_line(sprintf(__('%s价格', 'aksrt'), $vip1_name), 'aksrt_pay[vip1_price]', (string) ($pay['vip1_price'] ?? ''), '0.01'); ?>
<?php if ($manual && $vip2_on) aksrt_submit_pay_line(sprintf(__('%s价格', 'aksrt'), $vip2_name), 'aksrt_pay[vip2_price]', (string) ($pay['vip2_price'] ?? ''), '0.01'); ?>
<?php if ($manual && ($vip1_on || $vip2_on)) : ?><div class="px12 mt6 muted-color"><?php esc_html_e('会员价不能高于普通价,为0则为会员免费', 'aksrt'); ?></div><?php endif; ?>
</div>
<div data-pay-points <?php echo $modo !== 'points' ? 'hidden' : ''; ?>>
<label class="aksrt-submit-field">
<span><?php esc_html_e('设置积分', 'aksrt'); ?></span>
<input type="number" step="1" min="0" name="aksrt_pay[points_price]" class="aksrt-form-input" value="<?php echo esc_attr((string) ($pay['points_price'] ?? '')); ?>">
</label>
<?php if ($vip_manual && $vip1_on) : ?>
<label class="aksrt-submit-field">
<span><?php echo esc_html(sprintf(__('%s 积分', 'aksrt'), $vip1_name)); ?></span>
<input type="number" step="1" min="0" name="aksrt_pay[vip1_points]" class="aksrt-form-input" value="<?php echo esc_attr((string) ($pay['vip1_points'] ?? '')); ?>">
</label>
<?php endif; ?>
<?php if ($vip_manual && $vip2_on) : ?>
<label class="aksrt-submit-field">
<span><?php echo esc_html(sprintf(__('%s 积分', 'aksrt'), $vip2_name)); ?></span>
<input type="number" step="1" min="0" name="aksrt_pay[vip2_points]" class="aksrt-form-input" value="<?php echo esc_attr((string) ($pay['vip2_points'] ?? '')); ?>">
</label>
<?php endif; ?>
<div class="mt10 newpost-pay-price" data-pay-points <?php echo $modo !== 'points' ? 'hidden' : ''; ?>>
<?php aksrt_submit_pay_line(__('设置积分', 'aksrt'), 'aksrt_pay[points_price]', (string) ($pay['points_price'] ?? ''), '1'); ?>
<?php if ($manual && $vip1_on) aksrt_submit_pay_line(sprintf(__('%s积分', 'aksrt'), $vip1_name), 'aksrt_pay[vip1_points]', (string) ($pay['vip1_points'] ?? ''), '1'); ?>
<?php if ($manual && $vip2_on) aksrt_submit_pay_line(sprintf(__('%s积分', 'aksrt'), $vip2_name), 'aksrt_pay[vip2_points]', (string) ($pay['vip2_points'] ?? ''), '1'); ?>
<?php if ($manual && ($vip1_on || $vip2_on)) : ?><div class="px12 mt6 muted-color"><?php esc_html_e('会员价不能高于普通价,为0则为会员免费', 'aksrt'); ?></div><?php endif; ?>
</div>
<div class="aksrt-submit-pay__row">
<span><?php esc_html_e('购买限制', 'aksrt'); ?></span>
<select name="aksrt_pay[pay_limit]" class="form-control">
<?php $pl = (int) ($pay['pay_limit'] ?? 0); ?>
<option value="0" <?php selected($pl, 0); ?>><?php esc_html_e('所有人可购', 'aksrt'); ?></option>
<option value="1" <?php selected($pl, 1); ?>><?php esc_html_e('仅 VIP1 及以上', 'aksrt'); ?></option>
<option value="2" <?php selected($pl, 2); ?>><?php esc_html_e('仅 VIP2', 'aksrt'); ?></option>
</select>
</div>
<div class="aksrt-submit-pay__row">
<span><?php esc_html_e('登录后可购', 'aksrt'); ?></span>
<label class="aksrt-switch">
<input type="checkbox" name="aksrt_pay[require_login]" value="1" <?php checked(!empty($pay['require_login'])); ?>>
<span class="aksrt-switch__ui"></span>
</label>
</div>
<div class="aksrt-submit-pay__row">
<span><?php esc_html_e('允许优惠码', 'aksrt'); ?></span>
<label class="aksrt-switch">
<input type="checkbox" name="aksrt_pay[coupon_s]" value="1" <?php checked(!empty($pay['coupon_s'])); ?>>
<span class="aksrt-switch__ui"></span>
</label>
</div>
<label class="aksrt-submit-field">
<span><?php esc_html_e('优惠说明', 'aksrt'); ?></span>
<input type="text" class="aksrt-form-input" name="aksrt_pay[coupon_desc]" value="<?php echo esc_attr((string) ($pay['coupon_desc'] ?? '')); ?>" placeholder="<?php esc_attr_e('收银台展示的优惠提示', 'aksrt'); ?>">
</label>
<?php if (!empty($ctx['can_pay_download'])) : ?>
<div class="aksrt-submit-pay-downloads" data-pay-downloads>
<div class="aksrt-submit-pay__row">
<strong><?php esc_html_e('付费下载', 'aksrt'); ?></strong>
<span>
<button type="button" class="but hollow c-blue" data-pay-download-add>
<i class="fa fas fa-plus"></i> <?php esc_html_e('添加资源', 'aksrt'); ?>
</button>
<?php if (!empty($ctx['can_upload_file'])) : ?>
<button type="button" class="but hollow" data-pay-download-upload>
<i class="fa fas fa-upload"></i> <?php esc_html_e('上传附件', 'aksrt'); ?>
</button>
<?php endif; ?>
</span>
<div class="mt10 pay-dawnload-edit" data-pay-downloads>
<div class="flex ac jsb padding-h10">
<b><?php esc_html_e('付费下载', 'aksrt'); ?></b>
<div>
<button type="button" class="pay-dawnload-add but hollow p2-10 px12 c-blue" data-pay-download-add><i class="fa fas fa-plus mr3"></i><?php esc_html_e('添加资源', 'aksrt'); ?></button>
<?php if (!empty($ctx['can_upload_file'])) : ?><button type="button" class="but hollow p2-10 px12" data-pay-download-upload><i class="fa fas fa-upload mr3"></i><?php esc_html_e('上传附件', 'aksrt'); ?></button><?php endif; ?>
</div>
<div class="aksrt-submit-pay-downloads__list" data-pay-download-list>
<?php foreach ($downloads as $i => $row) :
$url = (string) ($row['url'] ?? '');
$label = (string) ($row['label'] ?? '');
$code = (string) ($row['extract_code'] ?? '');
?>
<div class="aksrt-submit-pay-downloads__item" data-pay-download-item>
<input type="url" class="aksrt-form-input" data-pay-download-url name="aksrt_pay[downloads][<?php echo (int) $i; ?>][url]" value="<?php echo esc_attr($url); ?>" placeholder="<?php esc_attr_e('资源链接', 'aksrt'); ?>">
<input type="text" class="aksrt-form-input" data-pay-download-label name="aksrt_pay[downloads][<?php echo (int) $i; ?>][label]" value="<?php echo esc_attr($label); ?>" placeholder="<?php esc_attr_e('名称', 'aksrt'); ?>">
<input type="text" class="aksrt-form-input" name="aksrt_pay[downloads][<?php echo (int) $i; ?>][extract_code]" value="<?php echo esc_attr($code); ?>" placeholder="<?php esc_attr_e('提取码/备注', 'aksrt'); ?>">
<button type="button" class="but c-red cir" data-pay-download-remove title="<?php esc_attr_e('删除', 'aksrt'); ?>"><i class="fa fas fa-trash-alt"></i></button>
</div>
<div class="mt10 px12 lists-box" data-pay-download-list>
<?php foreach ($downloads as $index => $row) : ?>
<div class="flex ac jsb lists-item" data-pay-download-item>
<input class="form-control flex1" type="url" data-pay-download-url name="aksrt_pay[downloads][<?php echo (int) $index; ?>][url]" value="<?php echo esc_attr((string) ($row['url'] ?? '')); ?>" placeholder="<?php esc_attr_e('资源链接', 'aksrt'); ?>">
<input class="form-control" type="text" data-pay-download-label name="aksrt_pay[downloads][<?php echo (int) $index; ?>][label]" value="<?php echo esc_attr((string) ($row['label'] ?? '')); ?>" placeholder="<?php esc_attr_e('名称', 'aksrt'); ?>">
<input class="form-control" type="text" name="aksrt_pay[downloads][<?php echo (int) $index; ?>][extract_code]" value="<?php echo esc_attr((string) ($row['extract_code'] ?? '')); ?>" placeholder="<?php esc_attr_e('提取码/备注', 'aksrt'); ?>">
<button type="button" class="but cir c-red" data-pay-download-remove><i class="fa fas fa-trash"></i></button>
</div>
<?php endforeach; ?>
</div>
<template data-pay-download-tpl>
<div class="aksrt-submit-pay-downloads__item" data-pay-download-item>
<input type="url" class="aksrt-form-input" data-name="url" data-pay-download-url placeholder="<?php esc_attr_e('资源链接', 'aksrt'); ?>">
<input type="text" class="aksrt-form-input" data-name="label" data-pay-download-label placeholder="<?php esc_attr_e('名称', 'aksrt'); ?>">
<input type="text" class="aksrt-form-input" data-name="extract_code" placeholder="<?php esc_attr_e('提取码/备注', 'aksrt'); ?>">
<button type="button" class="but c-red cir" data-pay-download-remove title="<?php esc_attr_e('删除', 'aksrt'); ?>"><i class="fa fas fa-trash-alt"></i></button>
</div>
</template>
<template data-pay-download-tpl><div class="flex ac jsb lists-item" data-pay-download-item><input class="form-control flex1" type="url" data-name="url" data-pay-download-url placeholder="<?php esc_attr_e('资源链接', 'aksrt'); ?>"><input class="form-control" type="text" data-name="label" data-pay-download-label placeholder="<?php esc_attr_e('名称', 'aksrt'); ?>"><input class="form-control" type="text" data-name="extract_code" placeholder="<?php esc_attr_e('提取码/备注', 'aksrt'); ?>"><button type="button" class="but cir c-red" data-pay-download-remove><i class="fa fas fa-trash"></i></button></div></template>
</div>
<?php endif; ?>
<p class="aksrt-submit-hint"><?php esc_html_e('请确保已经在文中添加了付费可见的隐藏内容', 'aksrt'); ?></p>
</div>
<p class="aksrt-submit-hint" data-pay-off-hint <?php echo $enabled ? 'hidden' : ''; ?>>
<?php esc_html_e('如果您在文章中添加了付费可见的隐藏内容,请在此设置付费功能', 'aksrt'); ?>
</p>
<div class="em09 mt10 muted-2-color"><?php esc_html_e('请确保已经在文中添加了付费可见的隐藏内容或已添加付费下载内容', 'aksrt'); ?></div>
</div>
<div class="em09 mt10 muted-2-color" data-pay-off-hint <?php echo $enabled ? 'hidden' : ''; ?>><?php esc_html_e('如果您在文章中添加了付费可见的隐藏内容或需要设置付费下载资源,请在此设置付费功能', 'aksrt'); ?></div>
</div>
+14 -60
View File
@@ -1,85 +1,39 @@
<?php
/**
* Submit actions + drafts list.
*
* @var array $ctx
* @package AKSRT
*/
/** @var array $ctx */
defined('ABSPATH') || exit;
$settings = $ctx['settings'];
$is_can_new = !empty($ctx['is_can_new']);
$edit_post = $ctx['edit_post'];
$status = (string) $ctx['post_status'];
$can_draft = !empty($ctx['user_id']) && empty($ctx['is_guest']) && !empty($settings['draft_save']);
if ($edit_post && in_array($status, ['publish', 'pending'], true)) {
// Still allow draft button? legacy hides draft for publish/pending.
$can_draft = false;
}
$can_draft = !empty($ctx['user_id'])
&& empty($ctx['is_guest'])
&& !empty($settings['draft_save'])
&& !in_array($status, ['publish', 'pending'], true);
?>
<div class="aksrt-submit-panel">
<div class="aksrt-submit-panel__body aksrt-submit-actions">
<p class="aksrt-submit-ready"><?php esc_html_e('Are you ready', 'aksrt'); ?></p>
<div class="zib-widget">
<div class="text-center">
<p class="separator muted-3-color theme-box"><?php esc_html_e('Are you ready', 'aksrt'); ?></p>
<input type="hidden" name="posts_id" value="<?php echo esc_attr((string) (int) $ctx['edit_id']); ?>">
<input type="hidden" name="status" id="submit-status" value="pending">
<input type="hidden" name="_nonce" value="<?php echo esc_attr(wp_create_nonce('aksrt_submit')); ?>">
<?php if (!$is_can_new) : ?>
<p class="aksrt-submit-hint"><?php esc_html_e('暂无发布权限', 'aksrt'); ?></p>
<p class="em09 muted-3-color theme-box"><?php esc_html_e('暂无发布权限', 'aksrt'); ?></p>
<?php else : ?>
<?php if (!empty($ctx['is_guest'])) : ?>
<p class="aksrt-submit-hint"><?php esc_html_e('您当前未登录,不能保存草稿,文章提交后不可再修改!', 'aksrt'); ?></p>
<p class="em09 muted-3-color theme-box"><?php esc_html_e('您当前未登录,不能保存草稿,文章提交后不可再修改!', 'aksrt'); ?></p>
<?php endif; ?>
<?php if (function_exists('aksrt_is_captcha_enabled_for') && function_exists('aksrt_render_captcha') && aksrt_is_captcha_enabled_for('front_submit')) : ?>
<div class="aksrt-submit-captcha">
<?php aksrt_render_captcha('front_submit'); ?>
</div>
<div class="mb10"><?php aksrt_render_captcha('front_submit'); ?></div>
<?php endif; ?>
<div class="aksrt-submit-actions__btns">
<div class="but-average">
<?php if ($can_draft) : ?>
<button type="button" class="but jb-green aksrt-submit-btn" data-action="posts_draft">
<i class="fa fas fa-dot-circle"></i> <?php esc_html_e('保存草稿', 'aksrt'); ?>
</button>
<button type="button" class="but jb-green new-posts-submit aksrt-submit-btn padding-lg" data-action="posts_draft"><i class="fa fas fa-dot-circle mr6"></i><?php esc_html_e('保存草稿', 'aksrt'); ?></button>
<?php endif; ?>
<button type="button" class="but jb-blue aksrt-submit-btn" data-action="posts_save" id="aksrt-submit-btn">
<i class="fa fas fa-check-square"></i>
<?php
printf(
/* translators: %s: 发布|审核|保存 */
esc_html__('提交%s', 'aksrt'),
esc_html((string) $ctx['submit_label'])
);
?>
</button>
<button type="button" class="but jb-blue new-posts-submit aksrt-submit-btn padding-lg" data-action="posts_save" id="aksrt-submit-btn"><i class="fa fas fa-check-square mr6"></i><?php printf(esc_html__('提交%s', 'aksrt'), esc_html((string) $ctx['submit_label'])); ?></button>
</div>
<?php endif; ?>
</div>
</div>
<?php if (!empty($ctx['user_drafts'])) : ?>
<div class="aksrt-submit-panel aksrt-submit-drafts">
<div class="aksrt-submit-panel__title">
<i class="fa fas fa-file-alt"></i> <?php esc_html_e('我的草稿', 'aksrt'); ?>
</div>
<div class="aksrt-submit-panel__body">
<?php foreach ($ctx['user_drafts'] as $draft) :
$edit_url = function_exists('aksrt_get_new_post_url')
? aksrt_get_new_post_url((int) $draft->ID)
: add_query_arg('edit', $draft->ID, get_permalink());
?>
<div class="aksrt-submit-drafts__item" data-draft-id="<?php echo esc_attr((string) $draft->ID); ?>">
<a href="<?php echo esc_url($edit_url); ?>" class="aksrt-submit-drafts__link">
<span class="aksrt-submit-drafts__name"><?php echo esc_html($draft->post_title ?: __('无标题', 'aksrt')); ?></span>
<time><?php echo esc_html(get_the_modified_date('Y-m-d H:i', $draft)); ?></time>
</a>
<button type="button" class="aksrt-submit-drafts__delete" data-draft-id="<?php echo esc_attr((string) $draft->ID); ?>" title="<?php esc_attr_e('删除草稿', 'aksrt'); ?>">
<i class="fa fas fa-trash-alt"></i>
</button>
</div>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
+43 -78
View File
@@ -1,114 +1,79 @@
<?php
/**
* Sidebar: guest + terms + tags.
* Reference-theme article parameters sidebar.
*
* @var array $ctx
* @package AKSRT
*/
defined('ABSPATH') || exit;
$settings = $ctx['settings'];
$term_helper = get_template_directory() . '/templates/parts/submit/term-checks.php';
if (file_exists($term_helper)) {
require_once $term_helper;
}
require_once get_template_directory() . '/templates/parts/submit/term-checks.php';
?>
<?php if (!empty($ctx['is_guest'])) : ?>
<div class="aksrt-submit-panel">
<div class="aksrt-submit-panel__title"><?php esc_html_e('用户信息', 'aksrt'); ?></div>
<div class="aksrt-submit-panel__body">
<p class="aksrt-submit-hint"><?php esc_html_e('请输入昵称', 'aksrt'); ?></p>
<input class="aksrt-form-input" type="text" name="user_name" placeholder="<?php esc_attr_e('请输入昵称', 'aksrt'); ?>" required>
<p class="aksrt-submit-hint"><?php esc_html_e('请输入您的联系方式', 'aksrt'); ?></p>
<input class="aksrt-form-input" type="text" name="contact_details" placeholder="<?php esc_attr_e('输入联系方式', 'aksrt'); ?>">
</div>
<div class="zib-widget mb10-sm newpost-user-inputs">
<div class="title-theme mb10"><?php esc_html_e('用户信息', 'aksrt'); ?></div>
<p class="muted-3-color em09"><?php esc_html_e('请输入昵称', 'aksrt'); ?></p>
<div class="mb20"><input class="form-control" type="text" name="user_name" placeholder="<?php esc_attr_e('请输入昵称', 'aksrt'); ?>" required></div>
<p class="muted-3-color em09"><?php esc_html_e('请输入您的联系方式', 'aksrt'); ?></p>
<input class="form-control" type="text" name="contact_details" placeholder="<?php esc_attr_e('输入联系方式', 'aksrt'); ?>">
</div>
<?php endif; ?>
<div class="aksrt-submit-panel">
<div class="aksrt-submit-panel__title"><?php esc_html_e('文章参数', 'aksrt'); ?></div>
<div class="aksrt-submit-panel__body term-select-box">
<div class="zib-widget mb10-sm">
<div class="title-theme mb10"><?php esc_html_e('文章参数', 'aksrt'); ?></div>
<div class="term-select-box">
<?php if (!empty($settings['allow_category'])) : ?>
<details class="aksrt-submit-drop" open>
<summary>
<span><i class="fa fas fa-folder-open"></i> <?php esc_html_e('分类', 'aksrt'); ?></span>
<i class="fa fas fa-angle-right"></i>
</summary>
<div class="aksrt-submit-drop__body">
<div class="newpost-cat-drop drop-select dropdown dependency-box">
<button type="button" data-toggle-class="open" data-target=".newpost-cat-drop" class="flex ac jsb drop-btn form-control mb6" aria-expanded="false">
<span class="title-theme222"><i class="fa fas fa-folder-open mr6" aria-hidden="true"></i><?php esc_html_e('分类', 'aksrt'); ?></span><i class="ml6 fa fas fa-angle-right em12"></i>
</button>
<div class="dropdown-menu fluid">
<div class="mini-scrollbar padding-w10 select-drop-box">
<?php
if (!empty($ctx['categories'])) {
aksrt_submit_render_term_checks($ctx['categories'], array_map('intval', $ctx['current_cats']), 'category');
} else {
echo '<p class="aksrt-submit-hint">' . esc_html__('暂无可选择的分类', 'aksrt') . '</p>';
echo esc_html__('暂无可选择的分类', 'aksrt');
}
?>
</div>
</details>
</div>
</div>
<?php endif; ?>
<?php if (!empty($settings['custom_filter_s']) && !empty($ctx['custom_dimensions'])) : ?>
<?php foreach ($ctx['custom_dimensions'] as $dim) :
$key = sanitize_key((string) ($dim['key'] ?? ''));
if ($key === '') {
continue;
}
$name = (string) ($dim['name'] ?? $key);
$vals = is_array($dim['vals'] ?? null) ? $dim['vals'] : [];
$cur = $ctx['current_cf'][$key] ?? [];
if (!is_array($cur)) {
$cur = $cur === '' || $cur === null ? [] : [$cur];
}
$cur = array_map('strval', $cur);
<?php foreach ($ctx['custom_dimensions'] as $dimension) :
$key = sanitize_key((string) ($dimension['key'] ?? ''));
if ($key === '') continue;
$name = (string) ($dimension['name'] ?? $key);
$vals = is_array($dimension['vals'] ?? null) ? $dimension['vals'] : [];
$current = (array) ($ctx['current_cf'][$key] ?? []);
?>
<details class="aksrt-submit-drop">
<summary>
<span><i class="fa fas fa-filter"></i> <?php echo esc_html($name); ?></span>
<i class="fa fas fa-angle-right"></i>
</summary>
<div class="aksrt-submit-drop__body aksrt-submit-tags">
<?php foreach ($vals as $val) :
$vk = is_array($val) ? (string) ($val['key'] ?? '') : (string) $val;
$vn = is_array($val) ? (string) ($val['name'] ?? $vk) : (string) $val;
if ($vk === '') {
continue;
}
$active = in_array($vk, $cur, true);
<div class="newpost-custom-filter-<?php echo esc_attr($key); ?> drop-select dropdown dependency-box">
<button type="button" data-toggle-class="open" data-target=".newpost-custom-filter-<?php echo esc_attr($key); ?>" class="flex ac jsb drop-btn form-control mb6" aria-expanded="false">
<span class="title-theme222"><i class="fa fas fa-filter mr6" aria-hidden="true"></i><?php echo esc_html($name); ?></span><i class="ml6 fa fas fa-angle-right em12"></i>
</button>
<div class="dropdown-menu fluid"><div class="padding-10 select-drop-box newpost-filter-tags">
<?php foreach ($vals as $value) :
$value_key = (string) ($value['key'] ?? '');
$value_name = (string) ($value['name'] ?? $value_key);
if ($value_key === '') continue;
$active = in_array($value_key, array_map('strval', $current), true);
?>
<label class="aksrt-submit-tag <?php echo $active ? 'is-active' : ''; ?>">
<input type="checkbox" name="cf_<?php echo esc_attr($key); ?>[]" value="<?php echo esc_attr($vk); ?>" <?php checked($active); ?>>
<span><?php echo esc_html($vn); ?></span>
</label>
<label class="tag-list pointer<?php echo $active ? ' active' : ''; ?>"><input type="checkbox" name="cf_<?php echo esc_attr($key); ?>[]" value="<?php echo esc_attr($value_key); ?>" <?php checked($active); ?>><span class="badg mm3"><?php echo esc_html($value_name); ?></span></label>
<?php endforeach; ?>
</div></div>
</div>
</details>
<?php endforeach; ?>
<?php endif; ?>
</div>
<?php if (!empty($settings['allow_tags'])) : ?>
<div class="aksrt-submit-field">
<div class="aksrt-submit-field__label">
<?php esc_html_e('标签', 'aksrt'); ?>
<?php if (!empty($ctx['ai_available'])) : ?>
<button type="button" class="aksrt-ai-assist-btn" data-ai-action="tags" hidden><?php esc_html_e('AI 建议标签', 'aksrt'); ?></button>
<div class="title-theme mb10 mt20"><?php esc_html_e('标签', 'aksrt'); ?></div>
<p class="muted-3-color em09"><?php esc_html_e('填写文章的标签,每个标签用逗号隔开', 'aksrt'); ?></p>
<textarea class="form-control" rows="3" name="tags" placeholder="<?php esc_attr_e('输入文章标签', 'aksrt'); ?>" tabindex="6"><?php echo esc_textarea((string) $ctx['current_tags']); ?></textarea>
<?php endif; ?>
</div>
<p class="aksrt-submit-hint"><?php esc_html_e('填写文章的标签,每个标签用逗号隔开', 'aksrt'); ?></p>
<textarea class="aksrt-form-input" rows="3" name="tags" placeholder="<?php esc_attr_e('输入文章标签', 'aksrt'); ?>"><?php echo esc_textarea((string) $ctx['current_tags']); ?></textarea>
</div>
<?php endif; ?>
<?php if (!empty($settings['allow_excerpt'])) : ?>
<div class="aksrt-submit-field">
<div class="aksrt-submit-field__label">
<?php esc_html_e('摘要', 'aksrt'); ?>
<?php if (!empty($ctx['ai_available'])) : ?>
<button type="button" class="aksrt-ai-assist-btn" data-ai-action="summary" hidden><?php esc_html_e('AI 生成摘要', 'aksrt'); ?></button>
<?php endif; ?>
</div>
<textarea class="aksrt-form-input" rows="3" name="excerpt" maxlength="300" placeholder="<?php esc_attr_e('可选:文章摘要', 'aksrt'); ?>"><?php echo esc_textarea((string) $ctx['current_excerpt']); ?></textarea>
</div>
<?php endif; ?>
</div>
</div>
@@ -0,0 +1,92 @@
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const test = require('node:test');
const root = path.resolve(__dirname, '..', '..');
const read = (file) => fs.readFileSync(path.join(root, file), 'utf8');
test('block and classic editors share the reference article extension fields', () => {
const meta = read('src/Core/PostMetaBox.php');
const fields = [
'thumbnail_url',
'cover_image',
'featured_slide',
'featured_video',
'featured_video_title',
'featured_video_episode',
'subtitle',
'views',
'like',
'show_layout',
'no_article-navs',
'article_maxheight_xz',
];
assert.ok(meta.includes("add_meta_box('post_main', __('文章扩展'"));
assert.match(meta, /\['post\.php', 'post-new\.php'\]/);
assert.match(meta, /wp_enqueue_media\(\)/);
fields.forEach((field) => assert.match(meta, new RegExp(field.replace('-', '\\-'))));
assert.doesNotMatch(meta, /custom_excerpt|header_image_mode|show_reading_progress|enable_lightbox|enable_pdf|copyright_text|is_featured/);
});
test('front submit follows the reference newposts shell and cards', () => {
const page = read('templates/pages/page-submit.php');
const main = read('templates/parts/submit/editor-main.php');
const terms = read('templates/parts/submit/sidebar-terms.php');
const pay = read('templates/parts/submit/sidebar-pay.php');
const submit = read('templates/parts/submit/sidebar-submit.php');
assert.match(page, /content-wrap newposts-wrap/);
assert.match(page, /class="content-layout"/);
assert.match(page, /class="sidebar"/);
assert.match(main, /zib-widget full-widget-sm editor-main-box/);
assert.match(main, /relative newposts-title/);
assert.match(terms, /newpost-cat-drop drop-select dropdown dependency-box/);
assert.match(pay, /zib-widget dependency-box mb10-sm newpost-pay-box/);
assert.match(pay, /pay-dawnload-edit/);
assert.match(submit, /separator muted-3-color theme-box/);
assert.match(submit, /new-posts-submit/);
});
test('article paid-content metabox follows the reference field set without shop enhancements', () => {
const meta = read('src/Core/ResourceMetaBox.php');
const fields = [
'pay_type',
'pay_limit',
'pay_modo',
'order_expired_s',
'points_price',
'original_price',
'promotion_tag',
'gallery_show',
'video_episode',
'video_scale_height',
'downloads',
'attributes',
'pay_doc',
'pay_details',
'pay_extra_hide',
];
assert.match(meta, /add_meta_box\('aksrt_pay_meta', __\('付费功能'/);
fields.forEach((field) => assert.match(meta, new RegExp(field)));
assert.doesNotMatch(meta, /aksrt_pay_(?:stock|purchase_limit|card_keys|free_preview|require_login|require_captcha)/);
assert.doesNotMatch(meta, /电商增强|parse_json_array/);
});
test('ordinary article editor chain does not import shop or forum routes', () => {
const files = [
'src/Core/PostMetaBox.php',
'src/Core/FrontSubmit.php',
'src/Core/FrontSubmitSave.php',
'src/Core/FrontSubmitPay.php',
'src/Core/FrontSubmitFeatured.php',
'templates/pages/page-submit.php',
'templates/parts/submit/sidebar-pay.php',
];
const source = files.map(read).join('\n');
assert.doesNotMatch(source, /shop_product|forum_post|bbs_/i);
assert.doesNotMatch(source, /aksrtpay_s|featured_payload|vip_1_(?:price|points)|vip_2_(?:price|points)/);
});