进行了一些小修复
This commit is contained in:
@@ -0,0 +1,44 @@
|
|||||||
|
/* Content shown inside the front-submit TinyMCE iframe. */
|
||||||
|
.tinymce-hide {
|
||||||
|
position: relative;
|
||||||
|
margin: 18px 0;
|
||||||
|
padding: 38px 14px 14px;
|
||||||
|
border: 1px dashed rgba(240, 68, 148, 0.35);
|
||||||
|
border-radius: 6px;
|
||||||
|
background: rgba(240, 68, 148, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tinymce-hide > .hide-before,
|
||||||
|
.tinymce-hide > .hide-after {
|
||||||
|
margin: 0;
|
||||||
|
color: #f04494;
|
||||||
|
font-family: Consolas, Monaco, monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tinymce-hide > .hide-before {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
left: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tinymce-hide > .hide-after {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tinymce-hide > [contenteditable="true"] {
|
||||||
|
min-height: 38px;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: rgba(255, 255, 255, 0.75);
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tinymce-hide > [contenteditable="true"] > :first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tinymce-hide > [contenteditable="true"] > :last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
@@ -117,16 +117,170 @@
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.newposts-editor-tools {
|
.aksrt-editor-media {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 100080;
|
||||||
|
inset: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
align-items: center;
|
||||||
gap: 6px;
|
justify-content: center;
|
||||||
padding: 8px 0 0;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.newposts-editor-tools .but {
|
.aksrt-editor-media__backdrop {
|
||||||
padding: 4px 9px;
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.45);
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aksrt-editor-media__dialog {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
width: min(620px, calc(100vw - 20px));
|
||||||
|
max-height: calc(100vh - 20px);
|
||||||
|
overflow: hidden auto;
|
||||||
|
border-radius: var(--main-radius, 8px);
|
||||||
|
background: var(--main-bg-color, #fff);
|
||||||
|
box-shadow: 0 18px 60px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.aksrt-editor-media__close {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 2;
|
||||||
|
top: 8px;
|
||||||
|
right: 10px;
|
||||||
|
width: 34px;
|
||||||
|
height: 34px;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--muted-color, #777);
|
||||||
|
font-size: 26px;
|
||||||
|
line-height: 34px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aksrt-editor-media__title {
|
||||||
|
margin: 0;
|
||||||
|
padding: 18px 54px 14px 20px;
|
||||||
|
font-size: 17px;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aksrt-editor-media__tabs {
|
||||||
|
display: flex;
|
||||||
|
gap: 24px;
|
||||||
|
padding: 0 20px;
|
||||||
|
border-bottom: 1px solid var(--main-border-color, rgba(50, 50, 50, 0.08));
|
||||||
|
}
|
||||||
|
|
||||||
|
.aksrt-editor-media__tabs button {
|
||||||
|
position: relative;
|
||||||
|
padding: 10px 2px 12px;
|
||||||
|
border: 0;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--muted-color, #777);
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aksrt-editor-media__tabs button.is-active {
|
||||||
|
color: var(--focus-color, #f04494);
|
||||||
|
}
|
||||||
|
|
||||||
|
.aksrt-editor-media__tabs button.is-active::after {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: -1px;
|
||||||
|
left: 0;
|
||||||
|
height: 2px;
|
||||||
|
border-radius: 2px;
|
||||||
|
background: currentColor;
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
|
|
||||||
|
.aksrt-editor-media__panel {
|
||||||
|
display: none;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aksrt-editor-media__panel.is-active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aksrt-editor-media__panel > p {
|
||||||
|
margin: 0 0 10px;
|
||||||
|
color: var(--muted-color, #777);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aksrt-editor-media__panel textarea {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 120px;
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aksrt-editor-media__drop {
|
||||||
|
display: flex;
|
||||||
|
min-height: 180px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 20px;
|
||||||
|
border: 2px dashed var(--main-border-color, rgba(50, 50, 50, 0.12));
|
||||||
|
border-radius: var(--main-radius, 8px);
|
||||||
|
background: var(--muted-border-color, rgba(50, 50, 50, 0.03));
|
||||||
|
color: var(--muted-color, #777);
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aksrt-editor-media__drop:hover,
|
||||||
|
.aksrt-editor-media__drop.is-dragging {
|
||||||
|
border-color: var(--focus-color, #f04494);
|
||||||
|
background: rgba(240, 68, 148, 0.06);
|
||||||
|
color: var(--focus-color, #f04494);
|
||||||
|
}
|
||||||
|
|
||||||
|
.aksrt-editor-media__drop input {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.aksrt-editor-media__drop i {
|
||||||
|
font-size: 34px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aksrt-editor-media__drop span {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aksrt-editor-media__selection {
|
||||||
|
min-height: 20px;
|
||||||
|
margin-top: 10px;
|
||||||
|
color: var(--muted-color, #777);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aksrt-editor-media__submit {
|
||||||
|
display: block;
|
||||||
|
min-width: 110px;
|
||||||
|
margin: 14px 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.aksrt-editor-media-open {
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.featured-edit {
|
.featured-edit {
|
||||||
@@ -402,6 +556,21 @@
|
|||||||
margin: 2px;
|
margin: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.aksrt-editor-media {
|
||||||
|
align-items: flex-end;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aksrt-editor-media__dialog {
|
||||||
|
width: 100%;
|
||||||
|
max-height: 88vh;
|
||||||
|
border-radius: 12px 12px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aksrt-editor-media__drop {
|
||||||
|
min-height: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
.pay-dawnload-edit .lists-item {
|
.pay-dawnload-edit .lists-item {
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@@ -0,0 +1,362 @@
|
|||||||
|
/**
|
||||||
|
* Front-submit TinyMCE tools matching the reference article editor.
|
||||||
|
*
|
||||||
|
* @package AKSRT
|
||||||
|
*/
|
||||||
|
(function (window, document) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
if (!window.tinymce || !window.tinymce.PluginManager) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cfg = window.aksrtSubmitData || {};
|
||||||
|
var editorCfg = cfg.editor || {};
|
||||||
|
var i18n = cfg.i18n || {};
|
||||||
|
var activeModal = null;
|
||||||
|
|
||||||
|
function notify(message, type, duration, id) {
|
||||||
|
if (typeof window.notyf === 'function') {
|
||||||
|
window.notyf(
|
||||||
|
message,
|
||||||
|
type,
|
||||||
|
duration === undefined ? 5 : duration,
|
||||||
|
id || 'aksrt-front-submit-editor-image-toast'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeAttr(value) {
|
||||||
|
return String(value)
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/"/g, '"')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>');
|
||||||
|
}
|
||||||
|
|
||||||
|
function validImageUrl(value) {
|
||||||
|
var raw = String(value).trim();
|
||||||
|
if (!/^https?:\/\//i.test(raw)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
var url = new URL(raw);
|
||||||
|
return url.protocol === 'http:' || url.protocol === 'https:' ? url.href : '';
|
||||||
|
} catch (error) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function imageMarkup(url, attachmentId, alt) {
|
||||||
|
var safeUrl = validImageUrl(url);
|
||||||
|
if (!safeUrl) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
var idAttr = attachmentId ? ' data-edit-file-id="' + Number(attachmentId) + '"' : '';
|
||||||
|
var altAttr = alt ? ' alt="' + escapeAttr(alt) + '"' : ' alt=""';
|
||||||
|
return '<p><img src="' + escapeAttr(safeUrl) + '"' + idAttr + altAttr + '></p>';
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeImageModal() {
|
||||||
|
if (!activeModal) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
document.removeEventListener('keydown', activeModal.onKeydown);
|
||||||
|
activeModal.root.remove();
|
||||||
|
activeModal = null;
|
||||||
|
document.body.classList.remove('aksrt-editor-media-open');
|
||||||
|
}
|
||||||
|
|
||||||
|
function uploadImage(file) {
|
||||||
|
var upload = (cfg.uploads || {}).image || null;
|
||||||
|
if (!upload || !upload.url) {
|
||||||
|
return Promise.reject(new Error(i18n.uploadUnavailable || '当前账号不能上传图片'));
|
||||||
|
}
|
||||||
|
if (!file || !/^image\//.test(file.type || '')) {
|
||||||
|
return Promise.reject(new Error(i18n.invalidImage || '请选择有效的图片文件'));
|
||||||
|
}
|
||||||
|
if (upload.maxSize && file.size > upload.maxSize) {
|
||||||
|
return Promise.reject(new Error(i18n.fileTooLarge || '文件大小超过限制'));
|
||||||
|
}
|
||||||
|
|
||||||
|
var body = new FormData();
|
||||||
|
body.append('file', file);
|
||||||
|
return fetch(upload.url, {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'same-origin',
|
||||||
|
headers: { 'X-WP-Nonce': cfg.restNonce || '' },
|
||||||
|
body: body,
|
||||||
|
}).then(function (response) {
|
||||||
|
return response.json().catch(function () {
|
||||||
|
return {};
|
||||||
|
}).then(function (payload) {
|
||||||
|
if (!response.ok || !payload || !payload.success || !payload.url) {
|
||||||
|
throw new Error((payload && payload.message) || i18n.error || '图片上传失败');
|
||||||
|
}
|
||||||
|
return payload;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createImageModal(editor) {
|
||||||
|
closeImageModal();
|
||||||
|
|
||||||
|
var canUpload = !!editorCfg.canUploadImage && !!((cfg.uploads || {}).image);
|
||||||
|
var root = document.createElement('div');
|
||||||
|
root.className = 'aksrt-editor-media';
|
||||||
|
root.setAttribute('role', 'dialog');
|
||||||
|
root.setAttribute('aria-modal', 'true');
|
||||||
|
root.setAttribute('aria-labelledby', 'aksrt-editor-media-title');
|
||||||
|
root.innerHTML =
|
||||||
|
'<button type="button" class="aksrt-editor-media__backdrop" data-editor-media-close tabindex="-1" aria-label="' +
|
||||||
|
escapeAttr(i18n.close || '关闭') + '"></button>' +
|
||||||
|
'<div class="aksrt-editor-media__dialog">' +
|
||||||
|
'<button type="button" class="aksrt-editor-media__close" data-editor-media-close aria-label="' +
|
||||||
|
escapeAttr(i18n.close || '关闭') + '">×</button>' +
|
||||||
|
'<h3 id="aksrt-editor-media-title" class="aksrt-editor-media__title">' +
|
||||||
|
escapeAttr(i18n.insertImage || '插入图片') + '</h3>' +
|
||||||
|
'<div class="aksrt-editor-media__tabs" role="tablist">' +
|
||||||
|
(canUpload
|
||||||
|
? '<button type="button" class="is-active" role="tab" aria-selected="true" data-editor-media-tab="upload">' +
|
||||||
|
escapeAttr(i18n.uploadImage || '上传图片') + '</button>'
|
||||||
|
: '') +
|
||||||
|
'<button type="button" class="' + (canUpload ? '' : 'is-active') + '" role="tab" aria-selected="' +
|
||||||
|
(canUpload ? 'false' : 'true') + '" data-editor-media-tab="external">' +
|
||||||
|
escapeAttr(i18n.externalImage || '外链图片') + '</button>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div class="aksrt-editor-media__panels">' +
|
||||||
|
(canUpload
|
||||||
|
? '<section class="aksrt-editor-media__panel is-active" data-editor-media-panel="upload">' +
|
||||||
|
'<label class="aksrt-editor-media__drop">' +
|
||||||
|
'<input type="file" accept="image/gif,image/jpeg,image/png,image/webp" multiple data-editor-media-files>' +
|
||||||
|
'<i class="fa fas fa-cloud-upload-alt" aria-hidden="true"></i>' +
|
||||||
|
'<strong>' + escapeAttr(i18n.selectImages || '选择或拖入图片') + '</strong>' +
|
||||||
|
'<span>' + escapeAttr(i18n.imageUploadHint || '可批量选择,上传完成后自动插入正文') + '</span>' +
|
||||||
|
'</label>' +
|
||||||
|
'<div class="aksrt-editor-media__selection" data-editor-media-selection></div>' +
|
||||||
|
'<button type="button" class="but c-blue aksrt-editor-media__submit" data-editor-media-upload disabled>' +
|
||||||
|
escapeAttr(i18n.confirmUpload || '确认上传') + '</button>' +
|
||||||
|
'</section>'
|
||||||
|
: '') +
|
||||||
|
'<section class="aksrt-editor-media__panel ' + (canUpload ? '' : 'is-active') + '" data-editor-media-panel="external">' +
|
||||||
|
'<p>' + escapeAttr(i18n.externalImageHint || '每行填写一个图片地址:') + '</p>' +
|
||||||
|
'<textarea class="form-control" rows="5" placeholder="https://..." data-editor-media-urls></textarea>' +
|
||||||
|
'<button type="button" class="but c-blue aksrt-editor-media__submit" data-editor-media-external>' +
|
||||||
|
escapeAttr(i18n.confirmInsert || '确认插入') + '</button>' +
|
||||||
|
'</section>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>';
|
||||||
|
|
||||||
|
document.body.appendChild(root);
|
||||||
|
document.body.classList.add('aksrt-editor-media-open');
|
||||||
|
|
||||||
|
var fileInput = root.querySelector('[data-editor-media-files]');
|
||||||
|
var uploadButton = root.querySelector('[data-editor-media-upload]');
|
||||||
|
var selection = root.querySelector('[data-editor-media-selection]');
|
||||||
|
var drop = root.querySelector('.aksrt-editor-media__drop');
|
||||||
|
var files = [];
|
||||||
|
|
||||||
|
function showSelection() {
|
||||||
|
if (!selection || !uploadButton) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
selection.textContent = files.length
|
||||||
|
? (i18n.selectedImages || '已选择 %d 张图片').replace('%d', String(files.length))
|
||||||
|
: '';
|
||||||
|
uploadButton.disabled = files.length === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setFiles(nextFiles) {
|
||||||
|
files = Array.prototype.filter.call(nextFiles || [], function (file) {
|
||||||
|
return /^image\//.test(file.type || '');
|
||||||
|
});
|
||||||
|
showSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
root.addEventListener('click', function (event) {
|
||||||
|
var close = event.target.closest('[data-editor-media-close]');
|
||||||
|
if (close) {
|
||||||
|
event.preventDefault();
|
||||||
|
closeImageModal();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tab = event.target.closest('[data-editor-media-tab]');
|
||||||
|
if (tab) {
|
||||||
|
var name = tab.getAttribute('data-editor-media-tab');
|
||||||
|
root.querySelectorAll('[data-editor-media-tab]').forEach(function (item) {
|
||||||
|
var active = item === tab;
|
||||||
|
item.classList.toggle('is-active', active);
|
||||||
|
item.setAttribute('aria-selected', active ? 'true' : 'false');
|
||||||
|
});
|
||||||
|
root.querySelectorAll('[data-editor-media-panel]').forEach(function (panel) {
|
||||||
|
panel.classList.toggle('is-active', panel.getAttribute('data-editor-media-panel') === name);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.target.closest('[data-editor-media-external]')) {
|
||||||
|
var textarea = root.querySelector('[data-editor-media-urls]');
|
||||||
|
var urls = String(textarea ? textarea.value : '').split(/\r?\n/);
|
||||||
|
var html = urls.map(function (url) {
|
||||||
|
return imageMarkup(url, 0, '');
|
||||||
|
}).join('');
|
||||||
|
if (!html) {
|
||||||
|
notify(i18n.invalidImageUrl || '请填写有效的图片地址', 'warning');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
editor.insertContent(html + '<p></p>');
|
||||||
|
editor.nodeChanged();
|
||||||
|
closeImageModal();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.target.closest('[data-editor-media-upload]')) {
|
||||||
|
if (!files.length || !uploadButton) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uploadButton.disabled = true;
|
||||||
|
uploadButton.textContent = i18n.uploading || '正在上传…';
|
||||||
|
notify(i18n.uploading || '正在上传正文图片…', 'load', '');
|
||||||
|
Promise.all(files.map(uploadImage)).then(function (items) {
|
||||||
|
var html = items.map(function (item) {
|
||||||
|
return imageMarkup(item.url, item.attachment_id, item.name || '');
|
||||||
|
}).join('');
|
||||||
|
if (!html) {
|
||||||
|
throw new Error(i18n.error || '图片上传失败');
|
||||||
|
}
|
||||||
|
editor.insertContent(html + '<p></p>');
|
||||||
|
editor.nodeChanged();
|
||||||
|
notify(i18n.uploaded || '正文图片上传成功', 'success');
|
||||||
|
closeImageModal();
|
||||||
|
}).catch(function (error) {
|
||||||
|
notify(error.message || i18n.error || '正文图片上传失败', 'danger');
|
||||||
|
uploadButton.disabled = false;
|
||||||
|
uploadButton.textContent = i18n.confirmUpload || '确认上传';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (fileInput) {
|
||||||
|
fileInput.addEventListener('change', function () {
|
||||||
|
setFiles(fileInput.files);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (drop) {
|
||||||
|
['dragenter', 'dragover'].forEach(function (name) {
|
||||||
|
drop.addEventListener(name, function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
drop.classList.add('is-dragging');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
['dragleave', 'drop'].forEach(function (name) {
|
||||||
|
drop.addEventListener(name, function (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
drop.classList.remove('is-dragging');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
drop.addEventListener('drop', function (event) {
|
||||||
|
setFiles(event.dataTransfer && event.dataTransfer.files);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var onKeydown = function (event) {
|
||||||
|
if (event.key === 'Escape') {
|
||||||
|
closeImageModal();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
activeModal = { root: root, onKeydown: onKeydown };
|
||||||
|
document.addEventListener('keydown', onKeydown);
|
||||||
|
var focusTarget = root.querySelector('[data-editor-media-tab].is-active');
|
||||||
|
if (focusTarget) {
|
||||||
|
focusTarget.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectedContent(editor) {
|
||||||
|
var content = String(editor.selection.getContent() || '').trim();
|
||||||
|
return content ? '<p>' + content + '</p>' : '<p>' + escapeAttr(i18n.hiddenPlaceholder || '隐藏内容') + '</p>';
|
||||||
|
}
|
||||||
|
|
||||||
|
function insertHiddenContent(editor, item) {
|
||||||
|
var node = editor.selection.getNode();
|
||||||
|
var content = selectedContent(editor);
|
||||||
|
var suffix = '<p></p>';
|
||||||
|
if (node && editor.dom.hasClass(node, 'tinymce-hide')) {
|
||||||
|
var editable = node.querySelector('[contenteditable="true"]');
|
||||||
|
content = editable ? editable.innerHTML : content;
|
||||||
|
editor.dom.remove(node);
|
||||||
|
suffix = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.insertContent(
|
||||||
|
'<div class="tinymce-hide" data-aksrt-hide-type="' + escapeAttr(item.type) + '" contenteditable="false">' +
|
||||||
|
'<p class="hide-before">' + escapeAttr(item.open) + '</p>' +
|
||||||
|
'<div contenteditable="true">' + content + '</div>' +
|
||||||
|
'<p class="hide-after">' + escapeAttr(item.close) + '</p>' +
|
||||||
|
'</div>' + suffix
|
||||||
|
);
|
||||||
|
editor.nodeChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
window.tinymce.PluginManager.add('aksrt_editor_tools', function (editor) {
|
||||||
|
if (editorCfg.canUploadImage) {
|
||||||
|
editor.addButton('aksrt_image', {
|
||||||
|
text: '',
|
||||||
|
icon: 'image',
|
||||||
|
tooltip: i18n.insertImage || '插入图片',
|
||||||
|
onclick: function () {
|
||||||
|
createImageModal(editor);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (editorCfg.canHide) {
|
||||||
|
var menu = [
|
||||||
|
{
|
||||||
|
text: i18n.replyVisible || '回复可见',
|
||||||
|
type: 'reply',
|
||||||
|
open: '[reply]',
|
||||||
|
close: '[/reply]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: i18n.loginVisible || '登录可见',
|
||||||
|
type: 'login',
|
||||||
|
open: '[aksrt_hide mode="login"]',
|
||||||
|
close: '[/aksrt_hide]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: i18n.vipVisible || 'VIP 可见',
|
||||||
|
type: 'vip',
|
||||||
|
open: '[vip]',
|
||||||
|
close: '[/vip]',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
if (editorCfg.canPay) {
|
||||||
|
menu.push({
|
||||||
|
text: i18n.payVisible || '付费可见',
|
||||||
|
type: 'payshow',
|
||||||
|
open: '[payshow]',
|
||||||
|
close: '[/payshow]',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
menu = menu.map(function (item) {
|
||||||
|
return {
|
||||||
|
text: item.text,
|
||||||
|
onclick: function () {
|
||||||
|
insertHiddenContent(editor, item);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
editor.addButton('aksrt_hide', {
|
||||||
|
text: '',
|
||||||
|
icon: 'preview',
|
||||||
|
tooltip: i18n.hiddenContent || '隐藏内容',
|
||||||
|
type: 'menubutton',
|
||||||
|
stateSelector: '.tinymce-hide',
|
||||||
|
menu: menu,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})(window, document);
|
||||||
@@ -32,21 +32,6 @@
|
|||||||
return ta ? ta.value : '';
|
return ta ? ta.value : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
function insertEditorContent(html) {
|
|
||||||
if (window.tinymce) {
|
|
||||||
var ed = window.tinymce.get('post_content');
|
|
||||||
if (ed) {
|
|
||||||
ed.focus();
|
|
||||||
ed.insertContent(html);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var ta = qs('#post_content') || qs('[name="post_content"]');
|
|
||||||
if (ta) {
|
|
||||||
ta.value += html;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function serializeForm(form) {
|
function serializeForm(form) {
|
||||||
var data = {};
|
var data = {};
|
||||||
var fd = new FormData(form);
|
var fd = new FormData(form);
|
||||||
@@ -106,7 +91,6 @@
|
|||||||
this.bindDraftDelete();
|
this.bindDraftDelete();
|
||||||
this.bindFeatured();
|
this.bindFeatured();
|
||||||
this.bindPay();
|
this.bindPay();
|
||||||
this.bindShortcodes();
|
|
||||||
this.bindLeaveWarning();
|
this.bindLeaveWarning();
|
||||||
this.bindAi();
|
this.bindAi();
|
||||||
this.bindTagActive();
|
this.bindTagActive();
|
||||||
@@ -659,41 +643,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
bindShortcodes: function () {
|
|
||||||
document.addEventListener('click', function (e) {
|
|
||||||
var btn = e.target.closest('[data-insert-shortcode]');
|
|
||||||
if (!btn) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
e.preventDefault();
|
|
||||||
if (btn.hasAttribute('data-insert-upload-image')) {
|
|
||||||
var toastId = 'aksrt-front-submit-editor-image-toast';
|
|
||||||
App.pickUpload('image', 'image/*').then(function (file) {
|
|
||||||
if (!file) return null;
|
|
||||||
notyf(i18n.uploading || '正在上传正文图片…', 'load', '', toastId);
|
|
||||||
return App.uploadFile('image', file);
|
|
||||||
}).then(function (file) {
|
|
||||||
if (!file || !file.url) return;
|
|
||||||
insertEditorContent('<img src="' + file.url.replace(/"/g, '"') + '" alt="">');
|
|
||||||
notyf(i18n.uploaded || '正文图片上传成功', 'success', 5, toastId);
|
|
||||||
}).catch(function (error) {
|
|
||||||
notyf(error.message || i18n.error || '正文图片上传失败', 'danger', 5, toastId);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var type = btn.getAttribute('data-insert-shortcode');
|
|
||||||
if (type === 'hide') {
|
|
||||||
insertEditorContent('[hidecontent type="login"]' + (i18n.hidePlaceholder || '隐藏内容') + '[/hidecontent]');
|
|
||||||
} else if (type === 'payshow') {
|
|
||||||
insertEditorContent('[hidecontent type="payshow"]' + (i18n.payPlaceholder || '付费可见内容') + '[/hidecontent]');
|
|
||||||
} else if (type === 'reply') {
|
|
||||||
insertEditorContent('[hidecontent type="reply"]' + (i18n.replyPlaceholder || '回复后可见') + '[/hidecontent]');
|
|
||||||
} else if (type === 'vip') {
|
|
||||||
insertEditorContent('[vip]' + (i18n.vipPlaceholder || 'VIP 可见内容') + '[/vip]');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
bindAi: function () {
|
bindAi: function () {
|
||||||
var ai = cfg.ai || {};
|
var ai = cfg.ai || {};
|
||||||
if (!ai.available) {
|
if (!ai.available) {
|
||||||
|
|||||||
@@ -86,6 +86,11 @@ if (file_exists($js)) {
|
|||||||
'summary' => !empty($ctx['ai_available']),
|
'summary' => !empty($ctx['ai_available']),
|
||||||
'tags' => !empty($ctx['ai_available']),
|
'tags' => !empty($ctx['ai_available']),
|
||||||
],
|
],
|
||||||
|
'editor' => [
|
||||||
|
'canUploadImage' => !empty($ctx['can_upload_img']),
|
||||||
|
'canHide' => !empty($ctx['can_hide']),
|
||||||
|
'canPay' => !empty($ctx['can_pay']),
|
||||||
|
],
|
||||||
'uploads' => [
|
'uploads' => [
|
||||||
'image' => !empty($ctx['can_upload_img']) ? [
|
'image' => !empty($ctx['can_upload_img']) ? [
|
||||||
'url' => esc_url_raw(rest_url('aksrt/v1/front-submit/upload/image')),
|
'url' => esc_url_raw(rest_url('aksrt/v1/front-submit/upload/image')),
|
||||||
@@ -118,6 +123,25 @@ if (file_exists($js)) {
|
|||||||
'pickImage' => __('选择图片', 'aksrt'),
|
'pickImage' => __('选择图片', 'aksrt'),
|
||||||
'pickVideo' => __('选择视频封面图', 'aksrt'),
|
'pickVideo' => __('选择视频封面图', 'aksrt'),
|
||||||
'videoUrl' => __('请输入视频地址', 'aksrt'),
|
'videoUrl' => __('请输入视频地址', 'aksrt'),
|
||||||
|
'close' => __('关闭', 'aksrt'),
|
||||||
|
'insertImage' => __('插入图片', 'aksrt'),
|
||||||
|
'uploadImage' => __('上传图片', 'aksrt'),
|
||||||
|
'externalImage' => __('外链图片', 'aksrt'),
|
||||||
|
'selectImages' => __('选择或拖入图片', 'aksrt'),
|
||||||
|
'imageUploadHint' => __('可批量选择,上传完成后自动插入正文', 'aksrt'),
|
||||||
|
'selectedImages' => __('已选择 %d 张图片', 'aksrt'),
|
||||||
|
'confirmUpload' => __('确认上传', 'aksrt'),
|
||||||
|
'externalImageHint' => __('每行填写一个图片地址:', 'aksrt'),
|
||||||
|
'confirmInsert' => __('确认插入', 'aksrt'),
|
||||||
|
'invalidImage' => __('请选择有效的图片文件', 'aksrt'),
|
||||||
|
'invalidImageUrl' => __('请填写有效的图片地址', 'aksrt'),
|
||||||
|
'uploadUnavailable' => __('当前账号不能上传图片', 'aksrt'),
|
||||||
|
'hiddenContent' => __('隐藏内容', 'aksrt'),
|
||||||
|
'hiddenPlaceholder' => __('隐藏内容', 'aksrt'),
|
||||||
|
'replyVisible' => __('回复可见', 'aksrt'),
|
||||||
|
'loginVisible' => __('登录可见', 'aksrt'),
|
||||||
|
'vipVisible' => __('VIP 可见', 'aksrt'),
|
||||||
|
'payVisible' => __('付费可见', 'aksrt'),
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,8 +43,36 @@ $is_can_new = !empty($ctx['is_can_new']);
|
|||||||
<?php else : ?>
|
<?php else : ?>
|
||||||
<div class="newposts-editor">
|
<div class="newposts-editor">
|
||||||
<?php
|
<?php
|
||||||
$toolbar1 = 'formatselect,bold,italic,bullist,numlist,alignleft,aligncenter,alignright,link,blockquote,code,fullscreen';
|
$toolbar1 = ['formatselect', 'bold', 'italic', 'bullist', 'numlist', 'alignleft', 'aligncenter', 'alignright', 'link'];
|
||||||
|
if (!empty($ctx['can_upload_img'])) {
|
||||||
|
$toolbar1[] = 'aksrt_image';
|
||||||
|
}
|
||||||
|
if (!empty($ctx['can_hide'])) {
|
||||||
|
$toolbar1[] = 'aksrt_hide';
|
||||||
|
}
|
||||||
|
$toolbar1 = implode(',', array_merge($toolbar1, ['blockquote', 'code', 'fullscreen']));
|
||||||
$plugins = 'paste,link,lists,fullscreen,wordpress,wplink,charmap';
|
$plugins = 'paste,link,lists,fullscreen,wordpress,wplink,charmap';
|
||||||
|
$editor_plugin_path = get_template_directory() . '/assets/js/front-submit-editor.js';
|
||||||
|
$editor_plugin_url = get_template_directory_uri() . '/assets/js/front-submit-editor.js';
|
||||||
|
if (file_exists($editor_plugin_path)) {
|
||||||
|
$editor_plugin_url = add_query_arg('ver', (string) filemtime($editor_plugin_path), $editor_plugin_url);
|
||||||
|
}
|
||||||
|
$editor_css_path = get_template_directory() . '/assets/css/frontend/pages/submit-editor.css';
|
||||||
|
$editor_css_url = get_template_directory_uri() . '/assets/css/frontend/pages/submit-editor.css';
|
||||||
|
if (file_exists($editor_css_path)) {
|
||||||
|
$editor_css_url = add_query_arg('ver', (string) filemtime($editor_css_path), $editor_css_url);
|
||||||
|
}
|
||||||
|
$external_plugins = static function (array $external, string $editor_id) use ($editor_plugin_url): array {
|
||||||
|
if ($editor_id === 'post_content') {
|
||||||
|
$external['aksrt_editor_tools'] = $editor_plugin_url;
|
||||||
|
}
|
||||||
|
return $external;
|
||||||
|
};
|
||||||
|
$editor_content_css = static function (string $styles) use ($editor_css_url): string {
|
||||||
|
return $styles . ($styles !== '' ? ',' : '') . $editor_css_url;
|
||||||
|
};
|
||||||
|
add_filter('mce_external_plugins', $external_plugins, 10, 2);
|
||||||
|
add_filter('mce_css', $editor_content_css);
|
||||||
$editor_settings = [
|
$editor_settings = [
|
||||||
'textarea_name' => 'post_content',
|
'textarea_name' => 'post_content',
|
||||||
'textarea_rows' => 20,
|
'textarea_rows' => 20,
|
||||||
@@ -61,34 +89,10 @@ $is_can_new = !empty($ctx['is_can_new']);
|
|||||||
],
|
],
|
||||||
];
|
];
|
||||||
wp_editor((string) $ctx['current_content'], 'post_content', $editor_settings);
|
wp_editor((string) $ctx['current_content'], 'post_content', $editor_settings);
|
||||||
|
remove_filter('mce_external_plugins', $external_plugins, 10);
|
||||||
|
remove_filter('mce_css', $editor_content_css, 10);
|
||||||
?>
|
?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php if (!empty($ctx['can_hide']) || !empty($ctx['can_pay']) || !empty($ctx['can_upload_img'])) : ?>
|
|
||||||
<div class="newposts-editor-tools">
|
|
||||||
<?php if (!empty($ctx['can_upload_img'])) : ?>
|
|
||||||
<button type="button" class="but" data-insert-upload-image>
|
|
||||||
<i class="fa fas fa-image"></i> <?php esc_html_e('上传图片', 'aksrt'); ?>
|
|
||||||
</button>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if (!empty($ctx['can_hide'])) : ?>
|
|
||||||
<button type="button" class="but" data-insert-shortcode="hide" title="<?php esc_attr_e('插入登录后可见', 'aksrt'); ?>">
|
|
||||||
<i class="fa fas fa-eye-slash"></i> <?php esc_html_e('登录可见', 'aksrt'); ?>
|
|
||||||
</button>
|
|
||||||
<button type="button" class="but" data-insert-shortcode="reply" title="<?php esc_attr_e('插入回复后可见', 'aksrt'); ?>">
|
|
||||||
<i class="fa fas fa-comment-dots"></i> <?php esc_html_e('回复可见', 'aksrt'); ?>
|
|
||||||
</button>
|
|
||||||
<button type="button" class="but" data-insert-shortcode="vip" title="<?php esc_attr_e('插入 VIP 可见', 'aksrt'); ?>">
|
|
||||||
<i class="fa fas fa-certificate"></i> <?php esc_html_e('VIP 可见', 'aksrt'); ?>
|
|
||||||
</button>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if (!empty($ctx['can_pay'])) : ?>
|
|
||||||
<button type="button" class="but" data-insert-shortcode="payshow" title="<?php esc_attr_e('插入付费可见内容', 'aksrt'); ?>">
|
|
||||||
<i class="fa fas fa-lock"></i> <?php esc_html_e('付费可见', 'aksrt'); ?>
|
|
||||||
</button>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<div class="flex ac jsb mt10">
|
<div class="flex ac jsb mt10">
|
||||||
|
|||||||
@@ -49,6 +49,29 @@ test('front submit follows the reference newposts shell and cards', () => {
|
|||||||
assert.match(submit, /new-posts-submit/);
|
assert.match(submit, /new-posts-submit/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('front submit image and visibility actions live in the TinyMCE toolbar', () => {
|
||||||
|
const main = read('templates/parts/submit/editor-main.php');
|
||||||
|
const page = read('templates/pages/page-submit.php');
|
||||||
|
const submit = read('assets/js/front-submit.js');
|
||||||
|
const editor = read('assets/js/front-submit-editor.js');
|
||||||
|
|
||||||
|
assert.match(main, /\$toolbar1\[\]\s*=\s*'aksrt_image'/);
|
||||||
|
assert.match(main, /\$toolbar1\[\]\s*=\s*'aksrt_hide'/);
|
||||||
|
assert.match(main, /mce_external_plugins/);
|
||||||
|
assert.match(main, /aksrt_editor_tools/);
|
||||||
|
assert.match(editor, /PluginManager\.add\('aksrt_editor_tools'/);
|
||||||
|
assert.match(editor, /addButton\('aksrt_image'/);
|
||||||
|
assert.match(editor, /addButton\('aksrt_hide'/);
|
||||||
|
assert.match(editor, /type:\s*'menubutton'/);
|
||||||
|
assert.match(editor, /data-editor-media-tab="upload"/);
|
||||||
|
assert.match(editor, /data-editor-media-tab="external"/);
|
||||||
|
assert.match(editor, /\(cfg\.uploads \|\| \{\}\)\.image/);
|
||||||
|
assert.match(page, /'canUploadImage'\s*=>\s*!empty\(\$ctx\['can_upload_img'\]\)/);
|
||||||
|
assert.doesNotMatch(main, /newposts-editor-tools|data-insert-shortcode|data-insert-upload-image/);
|
||||||
|
assert.doesNotMatch(submit, /bindShortcodes|data-insert-shortcode|data-insert-upload-image/);
|
||||||
|
assert.doesNotMatch(editor, /hidecontent|shop|forum|bbs_/i);
|
||||||
|
});
|
||||||
|
|
||||||
test('article paid-content metabox follows the reference field set without shop enhancements', () => {
|
test('article paid-content metabox follows the reference field set without shop enhancements', () => {
|
||||||
const meta = read('src/Core/ResourceMetaBox.php');
|
const meta = read('src/Core/ResourceMetaBox.php');
|
||||||
const fields = [
|
const fields = [
|
||||||
|
|||||||
@@ -369,6 +369,8 @@ test('settings actions use rehydrated fixed-id notyf flows', () => {
|
|||||||
|
|
||||||
test('front submit actions use direct fixed-id notyf flows', () => {
|
test('front submit actions use direct fixed-id notyf flows', () => {
|
||||||
const submit = read('assets/js/front-submit.js');
|
const submit = read('assets/js/front-submit.js');
|
||||||
|
const editor = read('assets/js/front-submit-editor.js');
|
||||||
|
const source = submit + '\n' + editor;
|
||||||
const page = read('templates/pages/page-submit.php');
|
const page = read('templates/pages/page-submit.php');
|
||||||
|
|
||||||
[
|
[
|
||||||
@@ -382,7 +384,7 @@ test('front submit actions use direct fixed-id notyf flows', () => {
|
|||||||
'aksrt-front-submit-editor-image-toast',
|
'aksrt-front-submit-editor-image-toast',
|
||||||
'aksrt-front-submit-ai-tags-toast',
|
'aksrt-front-submit-ai-tags-toast',
|
||||||
'aksrt-front-submit-ai-summary-toast',
|
'aksrt-front-submit-ai-summary-toast',
|
||||||
].forEach((id) => assert.match(submit, new RegExp("['\"]" + id + "['\"]")));
|
].forEach((id) => assert.match(source, new RegExp("['\"]" + id + "['\"]")));
|
||||||
|
|
||||||
assert.match(submit, /submit: function[\s\S]*?notyf\([^;]+?'load'[^;]+?toastId[\s\S]*?notyf\([^;]+?'success'[^;]+?toastId[\s\S]*?notyf\([^;]+?'danger'[^;]+?toastId/);
|
assert.match(submit, /submit: function[\s\S]*?notyf\([^;]+?'load'[^;]+?toastId[\s\S]*?notyf\([^;]+?'success'[^;]+?toastId[\s\S]*?notyf\([^;]+?'danger'[^;]+?toastId/);
|
||||||
assert.match(submit, /bindDraftDelete: function[\s\S]*?'aksrt-front-submit-delete-draft-toast'[\s\S]*?'load'[\s\S]*?'success'[\s\S]*?'danger'/);
|
assert.match(submit, /bindDraftDelete: function[\s\S]*?'aksrt-front-submit-delete-draft-toast'[\s\S]*?'load'[\s\S]*?'success'[\s\S]*?'danger'/);
|
||||||
@@ -398,7 +400,7 @@ test('front submit actions use direct fixed-id notyf flows', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
assert.match(submit, /aksrt-front-submit-resource-upload-toast[\s\S]*?'load'[\s\S]*?'success'[\s\S]*?'danger'/);
|
assert.match(submit, /aksrt-front-submit-resource-upload-toast[\s\S]*?'load'[\s\S]*?'success'[\s\S]*?'danger'/);
|
||||||
assert.match(submit, /aksrt-front-submit-editor-image-toast[\s\S]*?'load'[\s\S]*?'success'[\s\S]*?'danger'/);
|
assert.match(editor, /aksrt-front-submit-editor-image-toast[\s\S]*?'load'[\s\S]*?'success'[\s\S]*?'danger'/);
|
||||||
assert.match(submit, /aksrt-front-submit-ai-tags-toast[\s\S]*?aksrt-front-submit-ai-summary-toast[\s\S]*?'load'[\s\S]*?'success'[\s\S]*?'danger'/);
|
assert.match(submit, /aksrt-front-submit-ai-tags-toast[\s\S]*?aksrt-front-submit-ai-summary-toast[\s\S]*?'load'[\s\S]*?'success'[\s\S]*?'danger'/);
|
||||||
assert.match(submit, /needContent[^;]+?'warning'/);
|
assert.match(submit, /needContent[^;]+?'warning'/);
|
||||||
assert.doesNotMatch(submit, /\b(?:window\.)?alert\s*\(/);
|
assert.doesNotMatch(submit, /\b(?:window\.)?alert\s*\(/);
|
||||||
|
|||||||
Reference in New Issue
Block a user