diff --git a/.gitignore b/.gitignore
index ae6109b2..9ec76239 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
diff --git a/assets/css/admin/pay-meta-box.css b/assets/css/admin/pay-meta-box.css
new file mode 100644
index 00000000..210a8342
--- /dev/null
+++ b/assets/css/admin/pay-meta-box.css
@@ -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;
+ }
+}
diff --git a/assets/css/admin/post-extension.css b/assets/css/admin/post-extension.css
new file mode 100644
index 00000000..c210d770
--- /dev/null
+++ b/assets/css/admin/post-extension.css
@@ -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;
+ }
+}
diff --git a/assets/css/frontend/pages/submit.css b/assets/css/frontend/pages/submit.css
index fd816f49..0f1c371b 100644
--- a/assets/css/frontend/pages/submit.css
+++ b/assets/css/frontend/pages/submit.css
@@ -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;
}
}
diff --git a/assets/js/admin-pay-meta-box.js b/assets/js/admin-pay-meta-box.js
new file mode 100644
index 00000000..4e1c5aa5
--- /dev/null
+++ b/assets/js/admin-pay-meta-box.js
@@ -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();
+})();
diff --git a/assets/js/admin-post-extension.js b/assets/js/admin-post-extension.js
new file mode 100644
index 00000000..553dd300
--- /dev/null
+++ b/assets/js/admin-post-extension.js
@@ -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);
diff --git a/assets/js/front-submit.js b/assets/js/front-submit.js
index ae62eeed..feb50a7d 100644
--- a/assets/js/front-submit.js
+++ b/assets/js/front-submit.js
@@ -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 = '
';
@@ -419,15 +436,15 @@
html =
'
';
+ '" alt="">';
} else if (t === 'video') {
html =
(d.pic
? '
'
- : '
') +
- '';
+ : '
') +
+ '';
} else {
- html = 'Cover
';
+ html = '设置文章封面
';
}
preview.innerHTML = html;
var clearBtn = qs('[data-featured-action="close"]');
diff --git a/inc/helpers/aksrt-frontend-tags.php b/inc/helpers/aksrt-frontend-tags.php
index d51641b7..3bb288c4 100644
--- a/inc/helpers/aksrt-frontend-tags.php
+++ b/inc/helpers/aksrt-frontend-tags.php
@@ -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;
diff --git a/inc/helpers/aksrt-resource.php b/inc/helpers/aksrt-resource.php
index b950542f..b720760d 100644
--- a/inc/helpers/aksrt-resource.php
+++ b/inc/helpers/aksrt-resource.php
@@ -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 !== '') {
diff --git a/inc/helpers/posts-renderer/post-data.php b/inc/helpers/posts-renderer/post-data.php
index 10beed35..4a34ec66 100644
--- a/inc/helpers/posts-renderer/post-data.php
+++ b/inc/helpers/posts-renderer/post-data.php
@@ -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');
}
diff --git a/inc/helpers/template-tags/site-layout.php b/inc/helpers/template-tags/site-layout.php
index 4014321f..3b6e6754 100644
--- a/inc/helpers/template-tags/site-layout.php
+++ b/inc/helpers/template-tags/site-layout.php
@@ -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;
diff --git a/src/Core/DomainModel.php b/src/Core/DomainModel.php
index 516677e4..7aed1f31 100644
--- a/src/Core/DomainModel.php
+++ b/src/Core/DomainModel.php
@@ -63,34 +63,22 @@ class DomainModel {
/**
* @return array
*/
- 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' => '',
- 'featured_slide' => '',
- 'featured_video' => '',
- // Episode list: [ ['url' => string, 'title' => string], ... ]
- 'featured_video_episode' => [],
- 'subtitle' => '',
- 'views' => 0,
- 'likes' => 0,
- 'maxheight' => 0,
- ];
- }
+ public static function default_post_meta(): array {
+ return [
+ 'thumbnail_url' => '',
+ 'cover_image' => '',
+ 'featured_slide' => '',
+ 'featured_video' => '',
+ 'featured_video_title' => '',
+ 'featured_video_episode' => [],
+ 'subtitle' => '',
+ 'views' => 0,
+ 'like' => 0,
+ 'show_layout' => 'false',
+ 'no_article-navs' => false,
+ 'article_maxheight_xz' => false,
+ ];
+ }
/**
* @return array
@@ -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 电商增强字段
@@ -150,15 +152,14 @@ class DomainModel {
/**
* @return array
*/
- public static function pay_type_labels(): array {
- return [
- self::PAY_TYPE_NO => '不付费',
- self::PAY_TYPE_READING => '付费阅读',
- self::PAY_TYPE_DOWNLOAD => '付费下载',
- self::PAY_TYPE_GALLERY => '付费图库',
- self::PAY_TYPE_VIDEO => '付费视频',
- self::PAY_TYPE_VIP => 'VIP 专属',
- ];
+ public static function pay_type_labels(): array {
+ return [
+ self::PAY_TYPE_NO => '关闭',
+ self::PAY_TYPE_READING => '付费阅读',
+ self::PAY_TYPE_DOWNLOAD => '付费下载',
+ self::PAY_TYPE_GALLERY => '付费图片',
+ self::PAY_TYPE_VIDEO => '付费视频',
+ ];
}
public static function normalize_pay_type(string $pay_type): string {
@@ -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
+ */
+ 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';
diff --git a/src/Core/FrontSubmitFeatured.php b/src/Core/FrontSubmitFeatured.php
index 6d78d422..63a64f1c 100644
--- a/src/Core/FrontSubmitFeatured.php
+++ b/src/Core/FrontSubmitFeatured.php
@@ -12,172 +12,173 @@ defined('ABSPATH') || exit;
*/
trait FrontSubmitFeatured {
- /**
- * @param array $data
- */
- private function save_featured_meta(int $post_id, array $data, int $user_id): void {
- if (empty($data['featured_data']) && empty($data['featured_type'])) {
- return;
- }
+ /**
+ * @param array $data
+ */
+ private function save_featured_meta(int $post_id, array $data, int $user_id): void {
+ if (empty($data['featured_data'])) {
+ return;
+ }
- $featured = $data['featured_data'] ?? null;
- if (is_string($featured)) {
- $decoded = json_decode(wp_unslash($featured), true);
- $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,
- ];
- }
+ $featured = $data['featured_data'] ?? null;
+ if (is_string($featured)) {
+ $decoded = json_decode(wp_unslash($featured), true);
+ $featured = is_array($decoded) ? $decoded : null;
+ }
+ if (!is_array($featured)) {
+ return;
+ }
- $type = sanitize_key((string) ($featured['type'] ?? ''));
- $payload = is_array($featured['data'] ?? null) ? $featured['data'] : $featured;
+ $type = sanitize_key((string) ($featured['type'] ?? ''));
+ $payload = is_array($featured['data'] ?? null) ? $featured['data'] : $featured;
- $post_meta = get_post_meta($post_id, DomainModel::POST_META_KEY, true);
- if (!is_array($post_meta)) {
- $post_meta = DomainModel::default_post_meta();
- }
+ $post_meta = get_post_meta($post_id, DomainModel::POST_META_KEY, true);
+ 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'] ?? ''));
- if ($pic !== '' && !$this->user_can_cap('upload_submit_image', $user_id)) {
- $pic = '';
- }
- $post_meta['featured_video'] = $video;
- $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;
- $this->maybe_set_thumbnail_url($post_id, $pic);
- }
- break;
+ 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'] ?? ''));
+ $pic = esc_url_raw((string) ($payload['pic'] ?? ''));
+ if ($pic !== '' && !$this->user_can_cap('upload_submit_image', $user_id)) {
+ $pic = '';
+ }
+ $post_meta['featured_video'] = $video;
+ $post_meta['featured_slide'] = '';
+ if ($pic !== '' && $this->can_use_front_submit_image_url($pic, $user_id)) {
+ $post_meta['thumbnail_url'] = $pic;
+ $post_meta['cover_image'] = $pic;
+ $this->maybe_set_thumbnail_url($post_id, $pic);
+ }
+ break;
- case 'slide':
- 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'] ?? [];
- $urls = [];
- if (is_string($ids)) {
- $ids = array_filter(array_map('trim', explode(',', $ids)));
- }
- if (is_array($ids)) {
- foreach ($ids as $item) {
- if (is_array($item)) {
- $u = esc_url_raw((string) ($item['url'] ?? ''));
- $attachment_id = absint($item['id'] ?? 0);
- 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) {
- $urls[] = $src;
- }
- } elseif ($u !== '' && $this->user_can_cap('upload_submit_image', $user_id)) {
- $urls[] = $u;
- }
- } elseif (is_numeric($item)) {
- $attachment_id = (int) $item;
- $src = $this->can_use_front_submit_image($attachment_id, $user_id)
- ? wp_get_attachment_image_url($attachment_id, 'full')
- : false;
- if ($src) {
- $urls[] = $src;
- }
- } else {
- $u = esc_url_raw((string) $item);
- if ($u !== '' && $this->user_can_cap('upload_submit_image', $user_id)) {
- $urls[] = $u;
- }
- }
- }
- }
- $post_meta['featured_slide'] = implode(',', $urls);
- $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];
- $this->maybe_set_thumbnail_url($post_id, $urls[0]);
- }
- break;
+ case 'slide':
+ if (!$this->user_can_cap('new_post_slide_cover', $user_id) && !$this->user_can_cap('new_post_image_cover', $user_id)) {
+ return;
+ }
+ $ids = array_is_list($payload) ? $payload : [];
+ $attachment_ids = [];
+ $urls = [];
+ if (is_string($ids)) {
+ $ids = array_filter(array_map('trim', explode(',', $ids)));
+ }
+ if (is_array($ids)) {
+ foreach ($ids as $item) {
+ if (is_array($item)) {
+ $u = esc_url_raw((string) ($item['url'] ?? ''));
+ $attachment_id = absint($item['id'] ?? 0);
+ 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)) {
+ $urls[] = $u;
+ }
+ } elseif (is_numeric($item)) {
+ $attachment_id = (int) $item;
+ $src = $this->can_use_front_submit_image($attachment_id, $user_id)
+ ? wp_get_attachment_image_url($attachment_id, 'full')
+ : false;
+ if ($src) {
+ $attachment_ids[] = $attachment_id;
+ $urls[] = $src;
+ }
+ } else {
+ $u = esc_url_raw((string) $item);
+ if ($u !== '' && $this->user_can_cap('upload_submit_image', $user_id)) {
+ $urls[] = $u;
+ }
+ }
+ }
+ }
+ $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_image'] = $urls[0];
+ $this->maybe_set_thumbnail_url($post_id, $urls[0]);
+ }
+ break;
- case 'image':
- if (!$this->user_can_cap('new_post_image_cover', $user_id)) {
- return;
- }
- $url = esc_url_raw((string) ($payload['url'] ?? $payload['image'] ?? ''));
- $id = absint($payload['id'] ?? 0);
- if ($id > 0 && !$this->can_use_front_submit_image($id, $user_id)) {
- return;
- }
- if ($url === '' && $id > 0) {
- $src = wp_get_attachment_image_url($id, 'full');
- if ($src) {
- $url = $src;
- }
- }
- $post_meta['featured_video'] = '';
- $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;
- if ($id > 0) {
- set_post_thumbnail($post_id, $id);
- } elseif ($this->user_can_cap('upload_submit_image', $user_id)) {
- $this->maybe_set_thumbnail_url($post_id, $url);
- }
- }
- break;
+ case 'image':
+ if (!$this->user_can_cap('new_post_image_cover', $user_id)) {
+ return;
+ }
+ $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;
+ }
+ if ($url === '' && $id > 0) {
+ $src = wp_get_attachment_image_url($id, 'full');
+ if ($src) {
+ $url = $src;
+ }
+ }
+ $post_meta['featured_video'] = '';
+ $post_meta['featured_slide'] = '';
+ if ($url !== '' && $this->can_use_front_submit_image_url($url, $user_id)) {
+ $post_meta['thumbnail_url'] = $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)) {
+ $this->maybe_set_thumbnail_url($post_id, $url);
+ }
+ }
+ break;
- case 'close':
- case 'none':
- case 'clear':
- $post_meta['featured_video'] = '';
- $post_meta['featured_slide'] = '';
- $post_meta['thumbnail_url'] = '';
- $post_meta['cover_images'] = '';
- delete_post_thumbnail($post_id);
- break;
+ case 'close':
+ case 'none':
+ case 'clear':
+ $post_meta['featured_video'] = '';
+ $post_meta['featured_slide'] = '';
+ $post_meta['thumbnail_url'] = '';
+ $post_meta['cover_image'] = '';
+ delete_post_thumbnail($post_id);
+ break;
- default:
- return;
- }
+ default:
+ return;
+ }
- update_post_meta($post_id, DomainModel::POST_META_KEY, $post_meta);
- }
+ unset($post_meta['views'], $post_meta['like']);
+ update_post_meta($post_id, DomainModel::POST_META_KEY, $post_meta);
+ }
- private function maybe_set_thumbnail_url(int $post_id, string $url): void {
- if ($url === '') {
- return;
- }
- // Prefer attachment if URL is local media.
- $attach_id = attachment_url_to_postid($url);
- if ($attach_id > 0) {
- set_post_thumbnail($post_id, $attach_id);
- return;
- }
- $this->set_thumbnail_from_url($post_id, $url);
- }
+ private function maybe_set_thumbnail_url(int $post_id, string $url): void {
+ if ($url === '') {
+ return;
+ }
+ // Prefer attachment if URL is local media.
+ $attach_id = attachment_url_to_postid($url);
+ if ($attach_id > 0) {
+ set_post_thumbnail($post_id, $attach_id);
+ return;
+ }
+ $this->set_thumbnail_from_url($post_id, $url);
+ }
- private function can_use_front_submit_image(int $attachment_id, int $user_id): bool {
- if ($attachment_id <= 0 || !$this->user_can_cap('upload_submit_image', $user_id)) {
- return false;
- }
- $attachment = get_post($attachment_id);
- return $attachment instanceof \WP_Post
- && $attachment->post_type === 'attachment'
- && (int) $attachment->post_author === $user_id
- && wp_attachment_is_image($attachment_id);
- }
+ private function can_use_front_submit_image(int $attachment_id, int $user_id): bool {
+ if ($attachment_id <= 0 || !$this->user_can_cap('upload_submit_image', $user_id)) {
+ return false;
+ }
+ $attachment = get_post($attachment_id);
+ return $attachment instanceof \WP_Post
+ && $attachment->post_type === 'attachment'
+ && (int) $attachment->post_author === $user_id
+ && wp_attachment_is_image($attachment_id);
+ }
- private function can_use_front_submit_image_url(string $url, int $user_id): bool {
- $attachment_id = attachment_url_to_postid($url);
- return $attachment_id > 0 && $this->can_use_front_submit_image($attachment_id, $user_id);
- }
+ private function can_use_front_submit_image_url(string $url, int $user_id): bool {
+ $attachment_id = attachment_url_to_postid($url);
+ return $attachment_id > 0 && $this->can_use_front_submit_image($attachment_id, $user_id);
+ }
}
diff --git a/src/Core/FrontSubmitPay.php b/src/Core/FrontSubmitPay.php
index 1582fb70..61091d5f 100644
--- a/src/Core/FrontSubmitPay.php
+++ b/src/Core/FrontSubmitPay.php
@@ -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::normalize_pay_type((string) ($raw['pay_type'] ?? DomainModel::PAY_TYPE_READING));
+ if ($pay_type === DomainModel::PAY_TYPE_NO) {
$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);
- 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);
diff --git a/src/Core/FrontSubmitSave.php b/src/Core/FrontSubmitSave.php
index 31098434..ebc806a8 100644
--- a/src/Core/FrontSubmitSave.php
+++ b/src/Core/FrontSubmitSave.php
@@ -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);
diff --git a/src/Core/PostMetaBox.php b/src/Core/PostMetaBox.php
index 2515e775..2d6dfd21 100644
--- a/src/Core/PostMetaBox.php
+++ b/src/Core/PostMetaBox.php
@@ -1,5 +1,7 @@
- */
- public static function sanitize_video_episodes($raw): array {
- if (!is_array($raw)) {
- return [];
- }
- $out = [];
- foreach ($raw as $row) {
- if (!is_array($row)) {
- continue;
- }
- $url = esc_url_raw((string) ($row['url'] ?? ''));
- if ($url === '') {
- continue;
- }
- $out[] = [
- 'url' => $url,
- 'title' => sanitize_text_field((string) ($row['title'] ?? '')),
- ];
- if (count($out) >= 50) {
- break;
- }
- }
- return $out;
- }
-
- /**
- * Get a single post meta value.
- */
- public static function get_meta_value(int $post_id, string $key, $default = '') {
- $meta = self::get_meta($post_id);
- return $meta[$key] ?? $default;
- }
-
- /**
- * Register the meta box.
- */
- public function register_meta_box(): void {
- add_meta_box(
- 'aksrt_post_meta',
- __('AKSRT 文章设置', 'aksrt'),
- [$this, 'render_meta_box'],
- 'post',
- 'side',
- 'default'
- );
-
- add_meta_box(
- 'aksrt_page_meta',
- __('AKSRT 页面设置', 'aksrt'),
- [$this, 'render_page_meta_box'],
- 'page',
- 'side',
- 'default'
- );
-
- // §3.8 custom filter metabox (side)
- $filter_settings = class_exists('\AKSRT\Settings\Settings')
- ? \AKSRT\Settings\Settings::get_post_filter_settings()
- : ['custom_filter' => []];
- if (!empty($filter_settings['custom_filter'])) {
- add_meta_box(
- 'aksrt_custom_filter_meta',
- __('高级筛选', 'aksrt'),
- [$this, 'render_custom_filter_meta_box'],
- 'post',
- 'side',
- 'default'
- );
- }
- }
-
- /**
- * Render the page meta box (page_header_style / cover override).
- */
- public function render_page_meta_box(\WP_Post $post): void {
- $header_style = get_post_meta($post->ID, 'page_header_style', true);
- $cover_img = get_post_meta($post->ID, 'page_header_cover_img', true);
- wp_nonce_field('aksrt_page_meta', 'aksrt_page_meta_nonce');
- ?>
-
-
-
-
-
-
-
- ID);
- wp_nonce_field('aksrt_post_meta', 'aksrt_post_meta_nonce');
- ?>
-
-
-
-
-
-
-
- ID, '_aksrt_show_layout', true);
- if (!in_array($show_layout, ['no_sidebar', 'sidebar_left', 'sidebar_right'], true)) {
- $show_layout = '';
- }
- ?>
-
-
-
-
-
-
-
- __('阅读进度条', 'aksrt'),
- 'enable_lightbox' => __('图片灯箱', 'aksrt'),
- 'show_related' => __('相关推荐', 'aksrt'),
- 'enable_like' => __('点赞按钮', 'aksrt'),
- 'enable_favorite' => __('收藏按钮', 'aksrt'),
- 'enable_share' => __('分享按钮', 'aksrt'),
- 'enable_print' => __('打印按钮', 'aksrt'),
- 'enable_pdf' => __('PDF 导出按钮', 'aksrt'),
- ];
- foreach ($toggle_fields as $field_key => $field_label) :
- ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- []];
- $dimensions = $filter_settings['custom_filter'];
-
- if (empty($dimensions)) {
- echo '' . esc_html__('尚未定义筛选维度,请前往 文章设置 → 高级筛选 添加。', 'aksrt') . '
';
- return;
- }
-
- foreach ($dimensions as $dim) :
- $meta_key = '_aksrt_cf_' . $dim['key'];
- $current = get_post_meta($post->ID, $meta_key, true);
- ?>
-
-
-
-
- save_quick_edit($post_id);
- // Bulk pay fields may be submitted together with QE flag on bulk form.
- $this->save_bulk_pay_fields($post_id);
- return;
- }
-
- // Bulk-only pay fields without aksrt_qe_flag (when only pay row used).
- if (!empty($_POST['aksrt_bulk_pay_flag'])) {
- $this->save_bulk_pay_fields($post_id);
- // Continue only if full meta also posted; otherwise done.
- if (!isset($_POST['aksrt_post_meta'])) {
- return;
- }
- }
-
- $raw = $_POST['aksrt_post_meta'] ?? [];
- $sanitized = [
- 'cover_images' => isset($raw['cover_images']) ? sanitize_text_field($raw['cover_images']) : '',
- 'custom_excerpt' => isset($raw['custom_excerpt']) ? sanitize_textarea_field($raw['custom_excerpt']) : '',
- 'header_image_mode' => isset($raw['header_image_mode']) ? sanitize_key($raw['header_image_mode']) : '',
- 'show_reading_progress' => isset($raw['show_reading_progress']) ? sanitize_key((string) $raw['show_reading_progress']) : '',
- 'enable_lightbox' => isset($raw['enable_lightbox']) ? sanitize_key((string) $raw['enable_lightbox']) : '',
- 'show_related' => isset($raw['show_related']) ? sanitize_key((string) $raw['show_related']) : '',
- 'related_count' => isset($raw['related_count']) ? (int) $raw['related_count'] : 0,
- 'enable_like' => isset($raw['enable_like']) ? sanitize_key((string) $raw['enable_like']) : '',
- 'enable_favorite' => isset($raw['enable_favorite']) ? sanitize_key((string) $raw['enable_favorite']) : '',
- 'enable_share' => isset($raw['enable_share']) ? sanitize_key((string) $raw['enable_share']) : '',
- 'enable_print' => isset($raw['enable_print']) ? sanitize_key((string) $raw['enable_print']) : '',
- 'enable_pdf' => isset($raw['enable_pdf']) ? sanitize_key((string) $raw['enable_pdf']) : '',
- 'copyright_text' => isset($raw['copyright_text']) ? sanitize_textarea_field($raw['copyright_text']) : '',
- 'is_featured' => !empty($raw['is_featured']),
- // §3.8 文章扩展字段
- 'thumbnail_url' => isset($raw['thumbnail_url']) ? esc_url_raw((string) $raw['thumbnail_url']) : '',
- 'featured_slide' => isset($raw['featured_slide']) ? sanitize_text_field($raw['featured_slide']) : '',
- 'featured_video' => isset($raw['featured_video']) ? esc_url_raw((string) $raw['featured_video']) : '',
- 'featured_video_episode' => self::sanitize_video_episodes($raw['featured_video_episode'] ?? []),
- 'subtitle' => isset($raw['subtitle']) ? sanitize_text_field($raw['subtitle']) : '',
- 'views' => isset($raw['views']) ? max(0, (int) $raw['views']) : 0,
- 'likes' => isset($raw['likes']) ? max(0, (int) $raw['likes']) : 0,
- 'maxheight' => isset($raw['maxheight']) ? max(0, (int) $raw['maxheight']) : 0,
- ];
-
- $valid_header_modes = ['', 'featured', 'auto', 'none'];
- if (!in_array($sanitized['header_image_mode'], $valid_header_modes, true)) {
- $sanitized['header_image_mode'] = '';
- }
-
- foreach (['show_reading_progress', 'enable_lightbox', 'show_related', 'enable_like', 'enable_favorite', 'enable_share', 'enable_print', 'enable_pdf'] as $toggle_key) {
- if (!in_array($sanitized[$toggle_key], ['', '0', '1'], true)) {
- $sanitized[$toggle_key] = '';
- }
- }
-
- $sanitized['related_count'] = max(0, min(20, (int) $sanitized['related_count']));
-
- update_post_meta($post_id, self::META_KEY, $sanitized);
-
- // §3.8 Save custom filter dimension values as individual post meta
- if (isset($_POST['aksrt_custom_filter_meta_nonce']) && wp_verify_nonce($_POST['aksrt_custom_filter_meta_nonce'], 'aksrt_custom_filter_meta')) {
- if (!defined('DOING_AUTOSAVE') && current_user_can('edit_post', $post_id)) {
- $cf_settings = class_exists('\AKSRT\Settings\Settings')
- ? \AKSRT\Settings\Settings::get_post_filter_settings()
- : ['custom_filter' => []];
- foreach ($cf_settings['custom_filter'] as $dim) {
- $meta_key = '_aksrt_cf_' . $dim['key'];
- $value = isset($_POST[$meta_key]) ? sanitize_key((string) $_POST[$meta_key]) : '';
- if ($value !== '') {
- update_post_meta($post_id, $meta_key, $value);
- } else {
- delete_post_meta($post_id, $meta_key);
- }
- }
- }
- }
- }
-
- /**
- * §3.8 Add custom columns to the posts list table.
- */
- public function add_columns(array $columns): array {
- $columns['aksrt_views'] = __('阅读量', 'aksrt');
- $columns['aksrt_likes'] = __('点赞数', 'aksrt');
- $columns['aksrt_sales'] = __('销售数据', 'aksrt');
- return $columns;
- }
-
- /**
- * §3.8 Render custom column content.
- */
- public function render_column(string $column, int $post_id): void {
- $meta = self::get_meta($post_id);
- if ($column === 'aksrt_views') {
- $views = (int) ($meta['views'] ?? 0);
- echo esc_html($views > 0 ? (string) $views : '—');
- } elseif ($column === 'aksrt_likes') {
- $likes = (int) ($meta['likes'] ?? 0);
- $mh = (int) ($meta['maxheight'] ?? 0);
- echo '' . esc_html($likes > 0 ? (string) $likes : '—') . '';
- } elseif ($column === 'aksrt_sales') {
- $this->render_sales_column($post_id);
- }
- }
-
- /**
- * Content-pay sales summary for posts list (legacy 销售数据 column parity).
- */
- private function render_sales_column(int $post_id): void {
- if (!function_exists('aksrt_get_pay_meta') || !class_exists('AKSRT\\Core\\DomainModel')) {
- echo '—';
- return;
- }
- $pay = aksrt_get_pay_meta($post_id);
- $pay_type = DomainModel::normalize_pay_type((string) ($pay['pay_type'] ?? DomainModel::PAY_TYPE_NO));
- if ($pay_type === DomainModel::PAY_TYPE_NO) {
- echo '' . esc_html__('免费', 'aksrt') . '';
- return;
- }
- $labels = DomainModel::pay_type_labels();
- $type_label = $labels[$pay_type] ?? $pay_type;
- $modo = (($pay['pay_modo'] ?? '0') === 'points') ? 'points' : '0';
- if ($modo === 'points') {
- $price_text = sprintf(__('%d 积分', 'aksrt'), max(0, (int) ($pay['points_price'] ?? 0)));
- } else {
- $price_text = '¥' . (string) ($pay['price'] ?? '0.00');
- }
- $meta_sales = (int) get_post_meta($post_id, DomainModel::RESOURCE_SALES_VOLUME_KEY, true);
- $db_sales = 0;
- $revenue = '0.00';
- if (class_exists('\AKSRT\Payment\OrderRepository')) {
- $map = \AKSRT\Payment\OrderRepository::get_instance()->get_content_sales_map(
- [$post_id],
- DomainModel::ORDER_PRODUCT_RESOURCE
- );
- if (isset($map[$post_id])) {
- $db_sales = (int) ($map[$post_id]['sales_count'] ?? 0);
- $revenue = (string) ($map[$post_id]['revenue'] ?? '0.00');
- }
- }
- $orders_url = admin_url('admin.php?page=aksrt_orders&product_type=resource&product_id=' . $post_id);
- $products_url = admin_url('admin.php?page=aksrt_pay_products&s=' . $post_id);
-
- echo '';
- echo '
' . esc_html((string) $type_label) . '';
- echo '
' . esc_html($price_text);
- echo '
' . esc_html(sprintf(__('Meta销量 %d', 'aksrt'), max(0, $meta_sales)));
- if ($db_sales > 0) {
- printf(
- '
%s',
- esc_url($orders_url),
- esc_attr__('查看销售明细', 'aksrt'),
- esc_html(sprintf(__('真实销量 %d · ¥%s', 'aksrt'), $db_sales, $revenue))
- );
- } else {
- echo '
' . esc_html__('暂无订单', 'aksrt') . '';
- }
- echo '
' . esc_html__('付费文章', 'aksrt') . '';
- if ($pay_type === DomainModel::PAY_TYPE_DOWNLOAD) {
- $dl_url = admin_url('admin.php?page=aksrt_download_logs&post_id=' . $post_id);
- echo ' ·
' . esc_html__('下载记录', 'aksrt') . '';
- }
- echo '
';
- }
-
- /**
- * Dropdown filter for content pay_type on edit.php?post_type=post.
- */
- public function render_pay_type_filter(string $post_type): void {
- if ($post_type !== 'post' || !class_exists('AKSRT\\Core\\DomainModel')) {
- return;
- }
- $current = isset($_GET['aksrt_pay_type']) ? sanitize_key((string) wp_unslash($_GET['aksrt_pay_type'])) : '';
- $labels = DomainModel::pay_type_labels();
- echo '';
- }
-
- /**
- * Apply aksrt_pay_type filter via denormalized _aksrt_pay_type meta.
- */
- public function filter_posts_by_pay_type(\WP_Query $query): void {
- if (!is_admin() || !$query->is_main_query()) {
- return;
- }
- $screen_post_type = $query->get('post_type');
- if ($screen_post_type !== 'post') {
- return;
- }
- if (empty($_GET['aksrt_pay_type'])) {
- return;
- }
- $pay_type = sanitize_key((string) wp_unslash($_GET['aksrt_pay_type']));
- if ($pay_type === '' || !class_exists('AKSRT\\Core\\DomainModel')) {
- return;
- }
- $pay_type = DomainModel::normalize_pay_type($pay_type);
- $meta_query = (array) $query->get('meta_query');
- if ($pay_type === DomainModel::PAY_TYPE_NO) {
- $meta_query[] = [
- 'relation' => 'OR',
- [
- 'key' => DomainModel::PAY_TYPE_KEY,
- 'value' => DomainModel::PAY_TYPE_NO,
- 'compare' => '=',
- ],
- [
- 'key' => DomainModel::PAY_TYPE_KEY,
- 'compare' => 'NOT EXISTS',
- ],
- ];
- } else {
- $meta_query[] = [
- 'key' => DomainModel::PAY_TYPE_KEY,
- 'value' => $pay_type,
- 'compare' => '=',
- ];
- }
- $query->set('meta_query', $meta_query);
- }
-
- /**
- * §3.8 Render quick edit and bulk edit fields.
- */
- public function render_quick_edit(string $column_name, string $post_type): void {
- if ($column_name !== 'aksrt_views' || $post_type !== 'post') {
- return;
- }
- wp_nonce_field('aksrt_post_meta', 'aksrt_post_meta_nonce_qe');
- ?>
-
- decimal();
- } catch (\InvalidArgumentException $e) {
- return;
- }
- }
- if ($points_raw !== '') {
- if (!preg_match('/^(0|[1-9]\d*)$/', $points_raw)) {
- return;
- }
- $meta['points_price'] = (int) $points_raw;
- }
- if ($virtual_raw !== '' && is_numeric($virtual_raw)) {
- $meta['virtual_sales'] = max(0, (int) $virtual_raw);
- }
-
- $meta = DomainModel::sanitize_pay_meta($meta);
- update_post_meta($post_id, DomainModel::PAY_META_KEY, $meta);
- update_post_meta($post_id, DomainModel::PAY_TYPE_KEY, (string) ($meta['pay_type'] ?? DomainModel::PAY_TYPE_NO));
- update_post_meta(
- $post_id,
- DomainModel::PAY_PRICE_KEY,
- ($meta['pay_modo'] ?? '0') === 'points'
- ? (int) ($meta['points_price'] ?? 0)
- : (string) ($meta['price'] ?? '0.00')
- );
- if (class_exists('AKSRT\\Core\\DomainModel') && method_exists('AKSRT\\Core\\DomainModel', 'is_resource_pay_type')) {
- update_post_meta(
- $post_id,
- DomainModel::RESOURCE_MARKER_KEY,
- DomainModel::is_resource_pay_type((string) ($meta['pay_type'] ?? 'no')) ? 1 : 0
- );
- }
- }
-
- /**
- * §3.8 Quick / Bulk edit merge-mode save.
- * Only updates fields that were actually submitted (non-sentinel, non-empty).
- * Existing meta values for omitted fields are preserved.
- */
- private function save_quick_edit(int $post_id): void {
- $existing = self::get_meta($post_id);
-
- $views = isset($_POST['aksrt_qe_views']) && $_POST['aksrt_qe_views'] !== ''
- ? max(0, (int) $_POST['aksrt_qe_views'])
- : (int) ($existing['views'] ?? 0);
-
- $likes = isset($_POST['aksrt_qe_likes']) && $_POST['aksrt_qe_likes'] !== ''
- ? max(0, (int) $_POST['aksrt_qe_likes'])
- : (int) ($existing['likes'] ?? 0);
-
- $mh = isset($_POST['aksrt_qe_maxheight']) && $_POST['aksrt_qe_maxheight'] !== ''
- ? max(0, (int) $_POST['aksrt_qe_maxheight'])
- : (int) ($existing['maxheight'] ?? 0);
-
- $existing['views'] = $views;
- $existing['likes'] = $likes;
- $existing['maxheight'] = $mh;
-
- update_post_meta($post_id, self::META_KEY, $existing);
- }
-
- /**
- * §3.8 Output inline JS on edit.php to populate quick edit fields from column data attributes.
- */
- public function admin_quick_edit_script(): void {
- $screen = function_exists('get_current_screen') ? get_current_screen() : null;
- if (!$screen || $screen->base !== 'edit' || $screen->post_type !== 'post') {
- return;
- }
- ?>
-
- */
+ public static function get_meta(int $post_id): array {
+ $saved = get_post_meta($post_id, self::META_KEY, true);
+ $defaults = DomainModel::default_post_meta();
+ $meta = array_merge($defaults, is_array($saved) ? array_intersect_key($saved, $defaults) : []);
+ $meta['views'] = max(0, (int) get_post_meta($post_id, DomainModel::POST_VIEW_COUNT_KEY, true));
+ $meta['like'] = max(0, (int) get_post_meta($post_id, '_aksrt_like_count', true));
+ $meta['featured_video_episode'] = self::sanitize_video_episodes($meta['featured_video_episode'] ?? []);
+ return $meta;
+ }
+
+ public static function get_meta_value(int $post_id, string $key, $default = '') {
+ $meta = self::get_meta($post_id);
+ return array_key_exists($key, $meta) ? $meta[$key] : $default;
+ }
+
+ /**
+ * @param mixed $raw Raw episode rows.
+ * @return array
+ */
+ public static function sanitize_video_episodes($raw): array {
+ if (!is_array($raw)) {
+ return [];
+ }
+ $episodes = [];
+ foreach ($raw 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);
+ }
+
+ public function register_meta_boxes(): void {
+ add_meta_box('post_main', __('文章扩展', 'aksrt'), [$this, 'render_meta_box'], 'post', 'side', 'high');
+
+ $settings = class_exists('\AKSRT\Settings\Settings')
+ ? \AKSRT\Settings\Settings::get_post_filter_settings()
+ : [];
+ if (!empty($settings['custom_filter'])) {
+ add_meta_box(
+ 'aksrt_custom_filter_meta',
+ __('高级自定义筛选', 'aksrt'),
+ [$this, 'render_custom_filter_meta_box'],
+ 'post',
+ 'side',
+ 'default'
+ );
+ }
+ }
+
+ 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/post-extension.css';
+ $js = AKSRT_DIR . '/assets/js/admin-post-extension.js';
+ if (is_file($css)) {
+ wp_enqueue_style('aksrt-post-extension', AKSRT_URI . '/assets/css/admin/post-extension.css', [], (string) filemtime($css));
+ }
+ if (is_file($js)) {
+ wp_enqueue_script('aksrt-post-extension', AKSRT_URI . '/assets/js/admin-post-extension.js', ['jquery'], (string) filemtime($js), true);
+ }
+ }
+
+ public function render_meta_box(\WP_Post $post): void {
+ $meta = self::get_meta($post->ID);
+ $settings = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_all() : [];
+ $show_image = !array_key_exists('post_article_image_cover', $settings) || !empty($settings['post_article_image_cover']);
+ $show_slide = !array_key_exists('list_thumb_slides_s', $settings)
+ || !empty($settings['list_thumb_slides_s'])
+ || !empty($settings['post_article_slide_cover']);
+ $show_video = !array_key_exists('list_thumb_video_s', $settings)
+ || !empty($settings['list_thumb_video_s'])
+ || !empty($settings['post_article_video_cover']);
+
+ wp_nonce_field('aksrt_post_extension_save', 'aksrt_post_extension_nonce');
+ ?>
+
+ render_upload_field(
+ 'thumbnail_url',
+ __('外链特色图像', 'aksrt'),
+ (string) $meta['thumbnail_url'],
+ 'image',
+ __('支持直接输入链接用作特色图像', 'aksrt') . '
' . __('此处设置仅在未设置 WordPress 特色图片时有效', 'aksrt') . ''
+ );
+ if ($show_image) {
+ $this->render_upload_field('cover_image', __('封面图', 'aksrt'), (string) $meta['cover_image'], 'image', __('在文章页顶部显示封面图', 'aksrt'));
+ }
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
特色图像及封面图)', 'aksrt'); ?>
+
+
+
+
+
+ render_upload_field('featured_video', __('特色视频', 'aksrt'), (string) $meta['featured_video'], 'video', __('为该文章显示视频封面(优先级 > 幻灯片封面)', 'aksrt'), __('选择视频或填写视频地址', 'aksrt')); ?>
+
+
+
+
+
+
+ $episode) : ?>
+ render_episode_row((int) $index, (array) $episode); ?>
+
+
+
+
render_episode_row('__INDEX__', []); ?>
+
+
+
+
+ render_text_field('subtitle', __('副标题', 'aksrt'), (string) $meta['subtitle']); ?>
+ render_number_field('views', __('阅读量', 'aksrt'), (int) $meta['views']); ?>
+ render_number_field('like', __('点赞数', 'aksrt'), (int) $meta['like']); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ID, '_aksrt_cf_' . $key, true);
+ echo '' . esc_html($name) . '';
+ foreach ($vals as $value) {
+ $value_key = sanitize_key((string) ($value['key'] ?? ''));
+ $value_name = sanitize_text_field((string) ($value['name'] ?? $value_key));
+ if ($value_key === '') {
+ continue;
+ }
+ printf('', esc_attr($key), esc_attr($value_key), checked(in_array($value_key, array_map('strval', $current), true), true, false), esc_html($value_name));
+ }
+ echo '
';
+ }
+ }
+
+ public function save_meta_box(int $post_id, \WP_Post $post): void {
+ 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 (isset($_POST['aksrt_post_extension_nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash((string) $_POST['aksrt_post_extension_nonce'])), 'aksrt_post_extension_save')) {
+ $raw = isset($_POST['aksrt_post_extension']) && is_array($_POST['aksrt_post_extension']) ? wp_unslash($_POST['aksrt_post_extension']) : [];
+ $layout = sanitize_key((string) ($raw['show_layout'] ?? 'false'));
+ if (!in_array($layout, ['false', 'no_sidebar', 'sidebar_left', 'sidebar_right'], true)) {
+ $layout = 'false';
+ }
+ $slide_ids = array_values(array_filter(array_map('absint', preg_split('/[\s,]+/', (string) ($raw['featured_slide'] ?? '')) ?: [])));
+ $meta = [
+ 'thumbnail_url' => esc_url_raw((string) ($raw['thumbnail_url'] ?? '')),
+ 'cover_image' => esc_url_raw((string) ($raw['cover_image'] ?? '')),
+ 'featured_slide' => implode(',', array_slice($slide_ids, 0, 50)),
+ 'featured_video' => esc_url_raw((string) ($raw['featured_video'] ?? '')),
+ 'featured_video_title' => sanitize_text_field((string) ($raw['featured_video_title'] ?? '')),
+ 'featured_video_episode' => self::sanitize_video_episodes($raw['featured_video_episode'] ?? []),
+ 'subtitle' => sanitize_text_field((string) ($raw['subtitle'] ?? '')),
+ 'show_layout' => $layout,
+ 'no_article-navs' => !empty($raw['no_article-navs']),
+ 'article_maxheight_xz' => !empty($raw['article_maxheight_xz']),
+ ];
+ update_post_meta($post_id, self::META_KEY, $meta);
+ update_post_meta($post_id, DomainModel::POST_VIEW_COUNT_KEY, max(0, (int) ($raw['views'] ?? 0)));
+ update_post_meta($post_id, '_aksrt_like_count', max(0, (int) ($raw['like'] ?? 0)));
+ }
+
+ if (isset($_POST['aksrt_custom_filter_nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash((string) $_POST['aksrt_custom_filter_nonce'])), 'aksrt_custom_filter_save')) {
+ $settings = \AKSRT\Settings\Settings::get_post_filter_settings();
+ $dimensions = is_array($settings['custom_filter'] ?? null) ? $settings['custom_filter'] : [];
+ $submitted = isset($_POST['aksrt_custom_filter']) && is_array($_POST['aksrt_custom_filter']) ? wp_unslash($_POST['aksrt_custom_filter']) : [];
+ foreach ($dimensions as $dimension) {
+ $key = sanitize_key((string) ($dimension['key'] ?? ''));
+ if ($key === '') {
+ continue;
+ }
+ $allowed = array_values(array_filter(array_map(static fn($value): string => sanitize_key((string) ($value['key'] ?? '')), (array) ($dimension['vals'] ?? []))));
+ $values = array_values(array_intersect($allowed, array_map('sanitize_key', (array) ($submitted[$key] ?? []))));
+ if ($values) {
+ update_post_meta($post_id, '_aksrt_cf_' . $key, $values);
+ } else {
+ delete_post_meta($post_id, '_aksrt_cf_' . $key);
+ }
+ }
+ }
+ }
}
diff --git a/src/Core/ResourceMetaBox.php b/src/Core/ResourceMetaBox.php
index 972f0dce..308fa53c 100644
--- a/src/Core/ResourceMetaBox.php
+++ b/src/Core/ResourceMetaBox.php
@@ -1,5 +1,7 @@
post_type !== 'post') {
+ return;
+ }
- $meta = function_exists('aksrt_get_pay_meta')
- ? aksrt_get_pay_meta($post)
- : DomainModel::default_pay_meta();
+ 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);
+ }
+ }
- $pay_types = aksrt_get_pay_types();
- $is_paid = ($meta['pay_type'] ?? 'no') !== 'no';
+ 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();
+ $gallery_ids = [];
+ foreach ((array) ($meta['gallery'] ?? []) as $url) {
+ $id = attachment_url_to_postid((string) $url);
+ if ($id > 0) {
+ $gallery_ids[] = $id;
+ }
+ }
+ ?>
+
+ radio('pay_type', __('付费模式', 'aksrt'), (string) ($meta['pay_type'] ?? 'no'), aksrt_get_pay_types()); ?>
+
+ 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'),
+ ]);
+ ?>
- $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)
- : '';
+
+ number('points_price', __('积分售价', 'aksrt'), (string) ($meta['points_price'] ?? 0), '1', __('积分', 'aksrt')); ?>
+ number('vip1_points', __('VIP1 积分售价', 'aksrt'), (string) ($meta['vip1_points'] ?? 0), '1', __('积分', 'aksrt'), __('填 0 则为 VIP1 免费', 'aksrt')); ?>
+ number('vip2_points', __('VIP2 积分售价', 'aksrt'), (string) ($meta['vip2_points'] ?? 0), '1', __('积分', 'aksrt'), __('会员价格不能高于售价,填 0 则为 VIP2 免费。', 'aksrt')); ?>
+
+
+ number('price', __('执行价', 'aksrt'), (string) ($meta['price'] ?? '0.00'), '0.01', '', __('填 0 则为免费', 'aksrt')); ?>
+ number('original_price', __('原价', 'aksrt'), (string) ($meta['original_price'] ?? '0.00'), '0.01', '', __('显示在执行价格前面,并划掉。', 'aksrt')); ?>
+ textarea('promotion_tag', __('促销标签', 'aksrt'), (string) ($meta['promotion_tag'] ?? ''), 1); ?>
+ number('vip1_price', __('VIP1 价格', 'aksrt'), (string) ($meta['vip1_price'] ?? '0.00'), '0.01', '', __('填 0 则为 VIP1 免费', 'aksrt')); ?>
+ number('vip2_price', __('VIP2 价格', 'aksrt'), (string) ($meta['vip2_price'] ?? '0.00'), '0.01', '', __('会员价格不能高于执行价,填 0 则为 VIP2 免费。', 'aksrt')); ?>
+
- $gallery_json = !empty($meta['gallery'])
- ? wp_json_encode($meta['gallery'], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)
- : '';
- ?>
-
+ number('pay_count', __('销量浮动', 'aksrt'), (string) ($meta['pay_count'] ?? 0), '1', '', __('为真实销量增加或减少的数量。', 'aksrt'), false); ?>
-
-
-
-
-
-
-
+
+
+ number('gallery_show', __('免费查看', 'aksrt'), (string) ($meta['gallery_show'] ?? 1), '1', __('张', 'aksrt'), __('不能大于付费图片数量,否则无效。', 'aksrt')); ?>
+
-
-
-
+
+ upload('video_url', __('视频资源', 'aksrt'), (string) ($meta['video_url'] ?? ''), 'video', __('输入视频地址或选择、上传本地视频。', 'aksrt')); ?>
+ upload('video_pic', __('视频封面(可选)', 'aksrt'), (string) ($meta['video_pic'] ?? ''), 'image'); ?>
+ text('video_title', __('本集标题', 'aksrt'), (string) ($meta['video_title'] ?? ''), __('如需添加剧集则需填写此处', 'aksrt'), __('第1集', 'aksrt')); ?>
+ group('video_episode', __('更多剧集', 'aksrt'), (array) ($meta['video_episode'] ?? []), 'video_row', __('添加剧集', 'aksrt'), __('为付费视频添加更多剧集。', 'aksrt')); ?>
+ number('video_scale_height', __('固定长宽比例', 'aksrt'), (string) ($meta['video_scale_height'] ?? 0), '5', '%', __('为 0 则不固定长宽比例。', 'aksrt')); ?>
+
-
+
+ group('downloads', __('资源下载', 'aksrt'), (array) ($meta['downloads'] ?? []), 'download_row', __('添加资源', 'aksrt')); ?>
+ group('attributes', __('资源属性', 'aksrt'), (array) ($meta['attributes'] ?? []), 'attribute_row', __('添加属性', 'aksrt')); ?>
+ text('demo_url', __('演示地址', 'aksrt'), (string) ($meta['demo_url'] ?? ''), '', 'https://', 'url'); ?>
+
-
-
-
+ text('pay_title', __('商品标题', 'aksrt'), (string) ($meta['pay_title'] ?? ''), __('可选,如需单独显示付费内容标题请填写。', 'aksrt')); ?>
+ textarea('pay_doc', __('商品简介', 'aksrt'), (string) ($meta['pay_doc'] ?? ''), 1, __('可选,如需单独显示付费内容介绍请填写。', 'aksrt')); ?>
+ textarea('pay_details', __('更多详情', 'aksrt'), (string) ($meta['pay_details'] ?? ''), 3, __('显示在付费内容卡片下方,支持安全 HTML。', 'aksrt')); ?>
+ textarea('pay_extra_hide', __('额外隐藏内容', 'aksrt'), (string) ($meta['pay_extra_hide'] ?? ''), 3, __('付费后显示的额外隐藏内容,支持安全 HTML。', 'aksrt')); ?>
-
+
[hidecontent type="payshow"] [/hidecontent]
+
+
+
-
-
-
+ /** @param array
$options */
+ private function radio(string $id, string $title, string $value, array $options, string $description = ''): void {
+ ?>
+
+
-
-
-
+ private function number(string $id, string $title, string $value, string $step, string $unit = '', string $description = '', bool $non_negative = true): void {
+ ?>
+
+
-
-
-
-
-
+ private function text(string $id, string $title, string $value, string $description = '', string $placeholder = '', string $type = 'text'): void {
+ ?>
+
+
-
-
-
+ private function textarea(string $id, string $title, string $value, int $rows, string $description = ''): void {
+ ?>
+
+
-
-
-
-
+ private function upload(string $id, string $title, string $value, string $library, string $description = ''): void {
+ ?>
+
+
-
-
+ /** @param array
> $rows */
+ private function group(string $id, string $title, array $rows, string $renderer, string $button, string $description = ''): void {
+ ?>
+
+
-
-
-
+ /** @param int|string $index */
+ private function video_row($index, array $row): void {
+ ?>
+
+
-
-
-
+ /** @param int|string $index */
+ private function download_row($index, array $row): void {
+ ?>
+
+
-
-
-
+ /** @param int|string $index */
+ private function attribute_row($index, array $row): void {
+ ?>
+
+
-
-
-
+ public function save(int $post_id, \WP_Post $post): void {
+ 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 (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;
+ }
-
-
-
-
-
+ $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;
+ }
+ }
-
-
+ $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'] ?? ''),
+ ]);
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
0 表示限量,售完即止。', 'aksrt'); ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- post_type !== 'post') return;
-
- if (empty($_POST['aksrt_pay_meta_nonce'])
- || !wp_verify_nonce((string) $_POST['aksrt_pay_meta_nonce'], 'aksrt_pay_meta_save')) {
- 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';
- }
-
- // 价格字段
- $pay_modo = sanitize_key((string) ($_POST['aksrt_pay_modo'] ?? '0'));
- if ($pay_modo !== 'points') {
- $pay_modo = '0';
- }
- $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')),
- ]);
-
- 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);
+ }
}
diff --git a/src/Core/SEOMetaBox.php b/src/Core/SEOMetaBox.php
index 8aa52a8d..594d3d43 100644
--- a/src/Core/SEOMetaBox.php
+++ b/src/Core/SEOMetaBox.php
@@ -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',
@@ -57,20 +57,35 @@ class SEOMetaBox {
$ai_btn = $this->render_ai_button('post', (int) $post->ID);
$summary_btn = $this->render_summary_button((int) $post->ID);
?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
post_content), 38, '…')); ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -220,15 +235,22 @@ class SEOMetaBox {
],
]);
- wp_add_inline_style('wp-admin', '
+ wp_add_inline_style('wp-admin', '
.aksrt-ai-seo-button{display:inline-block;margin-left:8px;vertical-align:middle}
.aksrt-ai-seo-generate,.aksrt-ai-summary-generate{cursor:pointer}
.aksrt-ai-seo-generate.is-loading,.aksrt-ai-summary-generate.is-loading{opacity:.6;pointer-events:none}
.aksrt-ai-seo-msg{display:block;margin-top:6px;font-size:12px}
.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-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}
+ ');
}
private function render_ai_button(string $type, int $id): string {
diff --git a/templates/pages/page-submit.php b/templates/pages/page-submit.php
index d8d26db7..1643272b 100644
--- a/templates/pages/page-submit.php
+++ b/templates/pages/page-submit.php
@@ -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();
?>
-
-