mirror of
https://github.com/typecho/typecho.git
synced 2025-01-16 12:09:41 +01:00
v1.3.0 (#1661)
* Add feed widget * add feed render * Add CommentPage widget * New theme (#1390) * 调整忽略目录 * add theme * fix theme scss build Co-authored-by: fen <f3nb0x@gmail.com> * s/is_writeable/is_writable/g * New upgrade method * merge new fixes from master * add pgsql ssl mode support (ref #1600) (#1623) * Feat/code refactor (#1626) * remove all magic methods, add type for class properties * refactor codes * fix all * refactor code * fix type * fix all * fix request is method * fix all * fix router * fix get page * fix 1.3.0 upgrade * [feat] support high resolution avatar * fix types in i18n component * Implement Ctrl+S or Command+S for save draft (#1628) * Implement Ctrl+S or Command+S for save draft * rename * add Typecho.savePost * fix upload file size * add new uploader * replace new uploader * fix textarea change * fix preview * refactor post edit * fix issue * fix page edit --------- Co-authored-by: joyqi <joyqi@segmentfault.com> Co-authored-by: joyqi <magike.net@gmail.com> * fix #1632 * Add svg to image types * Feat/tree pages (#1646) * add tree trait * finish category tree trait * support select fields * fix select fields * refactor admin trait * fix draft status * Add new contents type "revision" * minor refactor * add more tree view abstracts * add tree trait to pages * get ready for tree view pages * improve page edit * fix revision * fix slug * add router params delegate * fix params delegate * fix * fix * fix all * fix all * fix tree * fix page link * fix feed * fix page * fix permalink * fix permalink input * fix offset query * Support IDN (#1629) * Support IDN * use js * Optimize code * Optimize code * fix URL script * remove unnecessary use --------- Co-authored-by: joyqi <joyqi@segmentfault.com> * fix input element * fix #1651, close #1653 * Use json instead of serialize (#1624) * Use json instead of serialize * Fix Upgrade code * add tree trait * finish category tree trait * support select fields * fix select fields * refactor admin trait * fix draft status * Add new contents type "revision" * minor refactor * add more tree view abstracts * add tree trait to pages * get ready for tree view pages * improve page edit * fix revision * fix slug * add router params delegate * fix params delegate * fix * fix * fix all * fix all * fix tree * fix page link * fix feed * fix page * fix permalink * fix permalink input * fix offset query * Fix typo * remove proxy methods * remove unnecessary useage --------- Co-authored-by: joyqi <joyqi@segmentfault.com> Co-authored-by: joyqi <magike.net@gmail.com> * Fix Prevent XSS vulnerability in default theme (#1654) * Fix Prevent XSS vulnerability in default theme * Update var/Typecho/Db/Adapter/Pdo.php * fix the getter --------- Co-authored-by: joyqi <joyqi@segmentfault.com> * add throwCallback to widget response * fix: cut down fields when selecting recent posts * fix typo errors * fix typo errors * fix http client cookie * add throw finish * fix theme lang * fix default theme * fix query * add open graph and twitter card support add canonical link * fix canonical link meta * fix theme classic-22 * remove unnecessary scss file when packaging * init plugin signal * improve: remove feather-icon js file * fix: typo * improve: post detail layout * fix tags saving * improve: nav search * fix: theme screenshot * fix: theme page layout * remove php 7.2/7.3 env --------- Co-authored-by: fen <f3nb0x@gmail.com> Co-authored-by: Lu Fei <52o@qq52o.cn>
This commit is contained in:
parent
43c54328f7
commit
3caebb3b20
3
.github/workflows/Typecho-dev-Ci.yml
vendored
3
.github/workflows/Typecho-dev-Ci.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php: ['7.2', '7.3', '7.4', '8.0', '8.1']
|
||||
php: ['7.4', '8.0', '8.1', '8.2']
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
@ -43,6 +43,7 @@ jobs:
|
||||
mkdir build/usr/uploads/
|
||||
chmod 755 build/usr/uploads/
|
||||
rm -rf build/admin/src
|
||||
rm -rf build/usr/themes/classic-22/static/scss
|
||||
cd build && zip -q -r typecho.zip * && mv typecho.zip ../ && cd -
|
||||
- name: Upload a Build Artifact
|
||||
uses: WebFreak001/deploy-nightly@v1.1.0
|
||||
|
1
.github/workflows/Typecho-release-Ci.yml
vendored
1
.github/workflows/Typecho-release-Ci.yml
vendored
@ -16,6 +16,7 @@ jobs:
|
||||
mkdir build/usr/uploads/
|
||||
chmod 755 build/usr/uploads/
|
||||
rm -rf build/admin/src
|
||||
rm -rf build/usr/themes/classic-22/static/scss
|
||||
cd build && zip -q -r typecho.zip * && mv typecho.zip ../ && cd -
|
||||
- name: Upload Release Asset
|
||||
uses: shogo82148/actions-upload-release-asset@v1
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -34,4 +34,4 @@ usr/themes/*
|
||||
!usr/themes/default
|
||||
!usr/themes/classic-22
|
||||
node_modules/
|
||||
/tools/tmp/
|
||||
tools/tmp/
|
||||
|
@ -84,6 +84,7 @@ $backupFiles = \Widget\Backup::alloc()->listFiles();
|
||||
<?php
|
||||
include 'copyright.php';
|
||||
include 'common-js.php';
|
||||
include 'form-js.php';
|
||||
?>
|
||||
<script>
|
||||
$('#backup-secondary .typecho-option-tabs li').click(function() {
|
||||
|
@ -133,10 +133,6 @@
|
||||
.attr('rel', 'noopener noreferrer');
|
||||
});
|
||||
}
|
||||
|
||||
$('.main form').submit(function () {
|
||||
$('button[type=submit]', this).attr('disabled', 'disabled');
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
@ -15,7 +15,7 @@ if (!defined('__TYPECHO_ROOT_DIR__') && !@include_once __DIR__ . '/../config.inc
|
||||
\Widget\Init::alloc();
|
||||
|
||||
/** 注册一个初始化插件 */
|
||||
\Typecho\Plugin::factory('admin/common.php')->begin();
|
||||
\Typecho\Plugin::factory('admin/common.php')->call('begin');
|
||||
|
||||
\Widget\Options::alloc()->to($options);
|
||||
\Widget\User::alloc()->to($user);
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,24 +1 @@
|
||||
h1 { text-align: center; }
|
||||
|
||||
details summary { cursor: pointer; }
|
||||
|
||||
@keyframes fadein { from { opacity: 0; }
|
||||
to { opacity: 1; } }
|
||||
|
||||
.fresh .keep-word { display: none; }
|
||||
|
||||
.keep .fresh-word { display: none; }
|
||||
|
||||
form > .message { display: none; padding: 20px; border-radius: 5px; }
|
||||
|
||||
.message textarea { width: 100%; height: 200px; resize: none; margin: 10px 0; }
|
||||
|
||||
.message.fade { display: block; animation: fadein .5s linear; }
|
||||
|
||||
.message *:last-child { margin-bottom: 0; }
|
||||
|
||||
.message p { margin-top: 10px; }
|
||||
|
||||
.message p button { margin-left: 5px; }
|
||||
|
||||
.message p button:first-child { margin-left: 0; }
|
||||
h1{text-align:center}details summary{cursor:pointer}@keyframes fadein{from{opacity:0}to{opacity:1}}.fresh .keep-word{display:none}.keep .fresh-word{display:none}form>.message{display:none;padding:20px;border-radius:5px}.message textarea{width:100%;height:200px;resize:none;margin:10px 0}.message.fade{display:block;animation:fadein .5s linear}.message *:last-child{margin-bottom:0}.message p{margin-top:10px}.message p button{margin-left:5px}.message p button:first-child{margin-left:0}
|
||||
|
File diff suppressed because one or more lines are too long
@ -20,7 +20,7 @@ $(document).ready(function () {
|
||||
$(this).remove();
|
||||
});
|
||||
|
||||
$(this).parents('form').trigger('field');
|
||||
$(this).parents('form').trigger('change');
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -40,10 +40,6 @@ $(document).ready(function () {
|
||||
+ '<td><button type="button" class="btn btn-xs"><?php _e('删除'); ?></button></td></tr>',
|
||||
el = $(html).hide().appendTo('#custom-field table tbody').fadeIn();
|
||||
|
||||
$(':input', el).bind('input change', function () {
|
||||
$(this).parents('form').trigger('field');
|
||||
});
|
||||
|
||||
attachDeleteEvent(el);
|
||||
});
|
||||
});
|
||||
|
@ -1,17 +1,44 @@
|
||||
<?php if(!defined('__TYPECHO_ADMIN__')) exit; ?>
|
||||
<?php $content = !empty($post) ? $post : $page; if ($options->markdown): ?>
|
||||
<?php $content = !empty($post) ? $post : $page; ?>
|
||||
<script>
|
||||
(function () {
|
||||
$('#text').on('change', function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}).on('input', function () {
|
||||
$(this).parents('form').trigger('write');
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
<?php if (!$options->markdown): ?>
|
||||
<script>
|
||||
(function () {
|
||||
const textarea = $('#text');
|
||||
|
||||
// 原始的插入图片和文件
|
||||
Typecho.insertFileToEditor = function (file, url, isImage) {
|
||||
const sel = textarea.getSelection(),
|
||||
html = isImage ? '<img src="' + url + '" alt="' + file + '" />'
|
||||
: '<a href="' + url + '">' + file + '</a>',
|
||||
offset = (sel ? sel.start : 0) + html.length;
|
||||
|
||||
textarea.replaceSelection(html);
|
||||
textarea.setSelection(offset, offset);
|
||||
};
|
||||
})();
|
||||
</script>
|
||||
<?php else: ?>
|
||||
<script src="<?php $options->adminStaticUrl('js', 'hyperdown.js'); ?>"></script>
|
||||
<script src="<?php $options->adminStaticUrl('js', 'pagedown.js'); ?>"></script>
|
||||
<script src="<?php $options->adminStaticUrl('js', 'paste.js'); ?>"></script>
|
||||
<script src="<?php $options->adminStaticUrl('js', 'purify.js'); ?>"></script>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
var textarea = $('#text'),
|
||||
isFullScreen = false,
|
||||
const textarea = $('#text'),
|
||||
toolbar = $('<div class="editor" id="wmd-button-bar" />').insertBefore(textarea.parent()),
|
||||
preview = $('<div id="wmd-preview" class="wmd-hidetab" />').insertAfter('.editor');
|
||||
let isFullScreen = false;
|
||||
|
||||
var options = {}, isMarkdown = <?php echo intval($content->isMarkdown || !$content->have()); ?>;
|
||||
const options = {}, isMarkdown = <?php echo intval($content->isMarkdown || !$content->have()); ?>;
|
||||
|
||||
options.strings = {
|
||||
bold: '<?php _e('加粗'); ?> <strong> Ctrl+B',
|
||||
@ -59,13 +86,13 @@ $(document).ready(function () {
|
||||
help: '<?php _e('Markdown语法帮助'); ?>'
|
||||
};
|
||||
|
||||
var converter = new HyperDown(),
|
||||
const converter = new HyperDown(),
|
||||
editor = new Markdown.Editor(converter, '', options);
|
||||
|
||||
// 自动跟随
|
||||
converter.enableHtml(true);
|
||||
converter.enableLine(true);
|
||||
reloadScroll = scrollableEditor(textarea, preview);
|
||||
const reloadScroll = scrollableEditor(textarea, preview);
|
||||
|
||||
// 修正白名单
|
||||
converter.hook('makeHtml', function (html) {
|
||||
@ -82,7 +109,7 @@ $(document).ready(function () {
|
||||
|
||||
// 替换block
|
||||
html = html.replace(/<(iframe|embed)\s+([^>]*)>/ig, function (all, tag, src) {
|
||||
if (src[src.length - 1] == '/') {
|
||||
if (src[src.length - 1] === '/') {
|
||||
src = src.substring(0, src.length - 1);
|
||||
}
|
||||
|
||||
@ -94,25 +121,26 @@ $(document).ready(function () {
|
||||
});
|
||||
|
||||
editor.hooks.chain('onPreviewRefresh', function () {
|
||||
var images = $('img', preview), count = images.length;
|
||||
const images = $('img', preview);
|
||||
let count = images.length;
|
||||
|
||||
if (count == 0) {
|
||||
if (count === 0) {
|
||||
reloadScroll(true);
|
||||
} else {
|
||||
images.bind('load error', function () {
|
||||
count --;
|
||||
|
||||
if (count == 0) {
|
||||
if (count === 0) {
|
||||
reloadScroll(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
<?php \Typecho\Plugin::factory('admin/editor-js.php')->markdownEditor($content); ?>
|
||||
<?php \Typecho\Plugin::factory('admin/editor-js.php')->call('markdownEditor', $content); ?>
|
||||
|
||||
var th = textarea.height(), ph = preview.height(),
|
||||
uploadBtn = $('<button type="button" id="btn-fullscreen-upload" class="btn btn-link">'
|
||||
let th = textarea.height(), ph = preview.height();
|
||||
const uploadBtn = $('<button type="button" id="btn-fullscreen-upload" class="btn btn-link">'
|
||||
+ '<i class="i-upload"><?php _e('附件'); ?></i></button>')
|
||||
.prependTo('.submit .right')
|
||||
.click(function() {
|
||||
@ -129,7 +157,7 @@ $(document).ready(function () {
|
||||
th = textarea.height();
|
||||
ph = preview.height();
|
||||
$(document.body).addClass('fullscreen');
|
||||
var h = $(window).height() - toolbar.outerHeight();
|
||||
const h = $(window).height() - toolbar.outerHeight();
|
||||
|
||||
textarea.css('height', h);
|
||||
preview.css('height', h);
|
||||
@ -139,7 +167,7 @@ $(document).ready(function () {
|
||||
editor.hooks.chain('enterFullScreen', function () {
|
||||
$(document.body).addClass('fullscreen');
|
||||
|
||||
var h = window.screen.height - toolbar.outerHeight();
|
||||
const h = window.screen.height - toolbar.outerHeight();
|
||||
textarea.css('height', h);
|
||||
preview.css('height', h);
|
||||
isFullScreen = true;
|
||||
@ -156,19 +184,23 @@ $(document).ready(function () {
|
||||
textarea.trigger('input');
|
||||
});
|
||||
|
||||
editor.hooks.chain('save', function () {
|
||||
Typecho.savePost();
|
||||
});
|
||||
|
||||
function initMarkdown() {
|
||||
editor.run();
|
||||
|
||||
var imageButton = $('#wmd-image-button'),
|
||||
const imageButton = $('#wmd-image-button'),
|
||||
linkButton = $('#wmd-link-button');
|
||||
|
||||
Typecho.insertFileToEditor = function (file, url, isImage) {
|
||||
var button = isImage ? imageButton : linkButton;
|
||||
const button = isImage ? imageButton : linkButton;
|
||||
|
||||
options.strings[isImage ? 'imagename' : 'linkname'] = file;
|
||||
button.trigger('click');
|
||||
|
||||
var checkDialog = setInterval(function () {
|
||||
let checkDialog = setInterval(function () {
|
||||
if ($('.wmd-prompt-dialog').length > 0) {
|
||||
$('.wmd-prompt-dialog input').val(url).select();
|
||||
clearInterval(checkDialog);
|
||||
@ -177,12 +209,12 @@ $(document).ready(function () {
|
||||
}, 10);
|
||||
};
|
||||
|
||||
Typecho.uploadComplete = function (file) {
|
||||
Typecho.insertFileToEditor(file.title, file.url, file.isImage);
|
||||
Typecho.uploadComplete = function (attachment) {
|
||||
Typecho.insertFileToEditor(attachment.title, attachment.url, attachment.isImage);
|
||||
};
|
||||
|
||||
// 编辑预览切换
|
||||
var edittab = $('.editor').prepend('<div class="wmd-edittab"><a href="#wmd-editarea" class="active"><?php _e('撰写'); ?></a><a href="#wmd-preview"><?php _e('预览'); ?></a></div>'),
|
||||
const edittab = $('.editor').prepend('<div class="wmd-edittab"><a href="#wmd-editarea" class="active"><?php _e('撰写'); ?></a><a href="#wmd-preview"><?php _e('预览'); ?></a></div>'),
|
||||
editarea = $(textarea.parent()).attr("id", "wmd-editarea");
|
||||
|
||||
$(".wmd-edittab a").click(function() {
|
||||
@ -190,11 +222,11 @@ $(document).ready(function () {
|
||||
$(this).addClass("active");
|
||||
$("#wmd-editarea, #wmd-preview").addClass("wmd-hidetab");
|
||||
|
||||
var selected_tab = $(this).attr("href"),
|
||||
const selected_tab = $(this).attr("href"),
|
||||
selected_el = $(selected_tab).removeClass("wmd-hidetab");
|
||||
|
||||
// 预览时隐藏编辑器按钮
|
||||
if (selected_tab == "#wmd-preview") {
|
||||
if (selected_tab === "#wmd-preview") {
|
||||
$("#wmd-button-row").addClass("wmd-visualhide");
|
||||
} else {
|
||||
$("#wmd-button-row").removeClass("wmd-visualhide");
|
||||
@ -207,21 +239,30 @@ $(document).ready(function () {
|
||||
});
|
||||
|
||||
// 剪贴板复制图片
|
||||
textarea.pastableTextarea().on('pasteImage', function (e, data) {
|
||||
var name = data.name ? data.name.replace(/[\(\)\[\]\*#!]/g, '') : (new Date()).toISOString().replace(/\..+$/, '');
|
||||
if (!name.match(/\.[a-z0-9]{2,}$/i)) {
|
||||
var ext = data.blob.type.split('/').pop();
|
||||
name += '.' + ext;
|
||||
}
|
||||
textarea.bind('paste', function (e) {
|
||||
const items = (e.clipboardData || e.originalEvent.clipboardData).items;
|
||||
|
||||
Typecho.uploadFile(new File([data.blob], name), name);
|
||||
for (const item of items) {
|
||||
if (item.kind === 'file') {
|
||||
const file = item.getAsFile();
|
||||
|
||||
if (file.size > 0) {
|
||||
if (!file.name) {
|
||||
file.name = (new Date()).toISOString().replace(/\..+$/, '')
|
||||
+ '.' + file.type.split('/').pop();
|
||||
}
|
||||
|
||||
Typecho.uploadFile(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (isMarkdown) {
|
||||
initMarkdown();
|
||||
} else {
|
||||
var notice = $('<div class="message notice"><?php _e('这篇文章不是由Markdown语法创建的, 继续使用Markdown编辑它吗?'); ?> '
|
||||
const notice = $('<div class="message notice"><?php _e('这篇文章不是由Markdown语法创建的, 继续使用Markdown编辑它吗?'); ?> '
|
||||
+ '<button class="btn btn-xs primary yes"><?php _e('是'); ?></button> '
|
||||
+ '<button class="btn btn-xs no"><?php _e('否'); ?></button></div>')
|
||||
.hide().insertBefore(textarea).slideDown();
|
||||
|
@ -3,7 +3,7 @@
|
||||
include 'common.php';
|
||||
|
||||
$panel = $request->get('panel');
|
||||
$panelTable = unserialize($options->panelTable);
|
||||
$panelTable = $options->panelTable;
|
||||
|
||||
if (!isset($panelTable['file']) || !in_array(urlencode($panel), $panelTable['file'])) {
|
||||
throw new \Typecho\Plugin\Exception(_t('页面不存在'), 404);
|
||||
|
@ -1,20 +1,15 @@
|
||||
<?php if(!defined('__TYPECHO_ADMIN__')) exit; ?>
|
||||
<?php
|
||||
if (isset($post) && $post instanceof \Typecho\Widget && $post->have()) {
|
||||
$fileParentContent = $post;
|
||||
} elseif (isset($page) && $page instanceof \Typecho\Widget && $page->have()) {
|
||||
$fileParentContent = $page;
|
||||
}
|
||||
$phpMaxFilesize = function_exists('ini_get') ? trim(ini_get('upload_max_filesize')) : '0';
|
||||
|
||||
$phpMaxFilesize = function_exists('ini_get') ? trim(ini_get('upload_max_filesize')) : 0;
|
||||
if (preg_match("/^([0-9]+)([a-z]{1,2})?$/i", $phpMaxFilesize, $matches)) {
|
||||
$size = intval($matches[1]);
|
||||
$unit = $matches[2] ?? 'b';
|
||||
|
||||
if (preg_match("/^([0-9]+)([a-z]{1,2})$/i", $phpMaxFilesize, $matches)) {
|
||||
$phpMaxFilesize = strtolower($matches[1] . $matches[2] . (1 == strlen($matches[2]) ? 'b' : ''));
|
||||
$phpMaxFilesize = round($size * pow(1024, stripos('bkmgtpezy', $unit[0])));
|
||||
}
|
||||
?>
|
||||
|
||||
<script src="<?php $options->adminStaticUrl('js', 'moxie.js'); ?>"></script>
|
||||
<script src="<?php $options->adminStaticUrl('js', 'plupload.js'); ?>"></script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
function updateAttachmentNumber () {
|
||||
@ -29,24 +24,40 @@ $(document).ready(function() {
|
||||
}
|
||||
|
||||
balloon.html(count);
|
||||
} else if (0 == count && balloon.length > 0) {
|
||||
} else if (0 === count && balloon.length > 0) {
|
||||
balloon.remove();
|
||||
}
|
||||
}
|
||||
|
||||
$('.upload-area').bind({
|
||||
dragenter : function () {
|
||||
updateAttachmentNumber();
|
||||
|
||||
const uploadUrl = $('.upload-area').bind({
|
||||
dragenter : function (e) {
|
||||
$(this).parent().addClass('drag');
|
||||
},
|
||||
|
||||
dragover : function (e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
$(this).parent().addClass('drag');
|
||||
},
|
||||
|
||||
drop : function () {
|
||||
drop : function (e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
$(this).parent().removeClass('drag');
|
||||
|
||||
const files = e.originalEvent.dataTransfer.files;
|
||||
|
||||
if (files.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const file of files) {
|
||||
Typecho.uploadFile(file);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
dragend : function () {
|
||||
$(this).parent().removeClass('drag');
|
||||
},
|
||||
@ -54,31 +65,44 @@ $(document).ready(function() {
|
||||
dragleave : function () {
|
||||
$(this).parent().removeClass('drag');
|
||||
}
|
||||
}).data('url');
|
||||
|
||||
const btn = $('.upload-file');
|
||||
const fileInput = $('<input type="file" name="file" />').hide().insertAfter(btn);
|
||||
|
||||
btn.click(function () {
|
||||
fileInput.click();
|
||||
return false;
|
||||
});
|
||||
|
||||
updateAttachmentNumber();
|
||||
fileInput.change(function () {
|
||||
if (this.files.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Typecho.uploadFile(this.files[0]);
|
||||
});
|
||||
|
||||
function fileUploadStart (file) {
|
||||
$('<li id="' + file.id + '" class="loading">'
|
||||
+ file.name + '</li>').appendTo('#file-list');
|
||||
}
|
||||
|
||||
function fileUploadError (error) {
|
||||
var file = error.file, code = error.code, word;
|
||||
function fileUploadError (type, file) {
|
||||
let word = '<?php _e('上传出现错误'); ?>';
|
||||
|
||||
switch (code) {
|
||||
case plupload.FILE_SIZE_ERROR:
|
||||
switch (type) {
|
||||
case 'size':
|
||||
word = '<?php _e('文件大小超过限制'); ?>';
|
||||
break;
|
||||
case plupload.FILE_EXTENSION_ERROR:
|
||||
case 'type':
|
||||
word = '<?php _e('文件扩展名不被支持'); ?>';
|
||||
break;
|
||||
case plupload.FILE_DUPLICATE_ERROR:
|
||||
case 'duplicate':
|
||||
word = '<?php _e('文件已经上传过'); ?>';
|
||||
break;
|
||||
case plupload.HTTP_ERROR:
|
||||
case 'network':
|
||||
default:
|
||||
word = '<?php _e('上传出现错误'); ?>';
|
||||
break;
|
||||
}
|
||||
|
||||
@ -94,104 +118,91 @@ $(document).ready(function() {
|
||||
li.effect('highlight', {color : '#FBC2C4'}, 2000, function () {
|
||||
$(this).remove();
|
||||
});
|
||||
|
||||
// fix issue #341
|
||||
this.removeFile(file);
|
||||
}
|
||||
|
||||
var completeFile = null;
|
||||
function fileUploadComplete (id, url, data) {
|
||||
var li = $('#' + id).removeClass('loading').data('cid', data.cid)
|
||||
.data('url', data.url)
|
||||
.data('image', data.isImage)
|
||||
.html('<input type="hidden" name="attachment[]" value="' + data.cid + '" />'
|
||||
+ '<a class="insert" target="_blank" href="###" title="<?php _e('点击插入文件'); ?>">' + data.title + '</a><div class="info">' + data.bytes
|
||||
function fileUploadComplete (file, attachment) {
|
||||
const li = $('#' + file.id).removeClass('loading').data('cid', attachment.cid)
|
||||
.data('url', attachment.url)
|
||||
.data('image', attachment.isImage)
|
||||
.html('<input type="hidden" name="attachment[]" value="' + attachment.cid + '" />'
|
||||
+ '<a class="insert" target="_blank" href="###" title="<?php _e('点击插入文件'); ?>">'
|
||||
+ attachment.title + '</a><div class="info">' + attachment.bytes
|
||||
+ ' <a class="file" target="_blank" href="<?php $options->adminUrl('media.php'); ?>?cid='
|
||||
+ data.cid + '" title="<?php _e('编辑'); ?>"><i class="i-edit"></i></a>'
|
||||
+ attachment.cid + '" title="<?php _e('编辑'); ?>"><i class="i-edit"></i></a>'
|
||||
+ ' <a class="delete" href="###" title="<?php _e('删除'); ?>"><i class="i-delete"></i></a></div>')
|
||||
.effect('highlight', 1000);
|
||||
|
||||
|
||||
attachInsertEvent(li);
|
||||
attachDeleteEvent(li);
|
||||
updateAttachmentNumber();
|
||||
|
||||
if (!completeFile) {
|
||||
completeFile = data;
|
||||
}
|
||||
Typecho.uploadComplete(attachment);
|
||||
}
|
||||
|
||||
var uploader = null, tabFilesEl = $('#tab-files').bind('init', function () {
|
||||
uploader = new plupload.Uploader({
|
||||
browse_button : $('.upload-file').get(0),
|
||||
url : '<?php $security->index('/action/upload'
|
||||
. (isset($fileParentContent) ? '?cid=' . $fileParentContent->cid : '')); ?>',
|
||||
runtimes : 'html5,flash,html4',
|
||||
flash_swf_url : '<?php $options->adminStaticUrl('js', 'Moxie.swf'); ?>',
|
||||
drop_element : $('.upload-area').get(0),
|
||||
filters : {
|
||||
max_file_size : '<?php echo $phpMaxFilesize ?>',
|
||||
mime_types : [{'title' : '<?php _e('允许上传的文件'); ?>', 'extensions' : '<?php echo implode(',', $options->allowedAttachmentTypes); ?>'}],
|
||||
prevent_duplicates : true
|
||||
},
|
||||
Typecho.uploadFile = (function () {
|
||||
const types = '<?php echo json_encode($options->allowedAttachmentTypes); ?>';
|
||||
const maxSize = <?php echo $phpMaxFilesize ?>;
|
||||
const queue = [];
|
||||
let index = 0;
|
||||
|
||||
init : {
|
||||
FilesAdded : function (up, files) {
|
||||
for (var i = 0; i < files.length; i ++) {
|
||||
fileUploadStart(files[i]);
|
||||
}
|
||||
const getUrl = function () {
|
||||
const url = new URL(uploadUrl);
|
||||
const cid = $('input[name=cid]').val();
|
||||
|
||||
completeFile = null;
|
||||
uploader.start();
|
||||
},
|
||||
url.searchParams.append('cid', cid);
|
||||
return url.toString();
|
||||
};
|
||||
|
||||
UploadComplete : function () {
|
||||
if (completeFile) {
|
||||
Typecho.uploadComplete(completeFile);
|
||||
}
|
||||
},
|
||||
const upload = function () {
|
||||
const file = queue.shift();
|
||||
|
||||
FileUploaded : function (up, file, result) {
|
||||
if (200 == result.status) {
|
||||
var data = $.parseJSON(result.response);
|
||||
|
||||
if (data) {
|
||||
fileUploadComplete(file.id, data[0], data[1]);
|
||||
uploader.removeFile(file);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fileUploadError.call(uploader, {
|
||||
code : plupload.HTTP_ERROR,
|
||||
file : file
|
||||
});
|
||||
},
|
||||
|
||||
Error : function (up, error) {
|
||||
fileUploadError.call(uploader, error);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
uploader.init();
|
||||
});
|
||||
|
||||
Typecho.uploadFile = function (file, name) {
|
||||
if (!uploader) {
|
||||
$('#tab-files-btn').parent().trigger('click');
|
||||
}
|
||||
|
||||
var timer = setInterval(function () {
|
||||
if (!uploader) {
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
clearInterval(timer);
|
||||
timer = null;
|
||||
const data = new FormData();
|
||||
data.append('file', file);
|
||||
|
||||
uploader.addFile(file, name);
|
||||
}, 50);
|
||||
};
|
||||
fetch(getUrl(), {
|
||||
method: 'POST',
|
||||
body: data
|
||||
}).then(function (response) {
|
||||
if (response.ok) {
|
||||
return response.json();
|
||||
} else {
|
||||
throw new Error(response.statusText);
|
||||
}
|
||||
}).then(function (data) {
|
||||
if (data) {
|
||||
const [_, attachment] = data;
|
||||
fileUploadComplete(file, attachment);
|
||||
upload();
|
||||
} else {
|
||||
throw new Error('no data');
|
||||
}
|
||||
}).catch(function (error) {
|
||||
fileUploadError('network', file);
|
||||
upload();
|
||||
});
|
||||
};
|
||||
|
||||
return function (file) {
|
||||
file.id = 'upload-' + (index++);
|
||||
|
||||
if (file.size > maxSize) {
|
||||
return fileUploadError('size', file);
|
||||
}
|
||||
|
||||
const match = file.name.match(/\.([a-z0-9]+)$/i);
|
||||
if (!match || types.indexOf(match[1].toLowerCase()) < 0) {
|
||||
return fileUploadError('type', file);
|
||||
}
|
||||
|
||||
queue.push(file);
|
||||
fileUploadStart(file);
|
||||
upload();
|
||||
};
|
||||
})();
|
||||
|
||||
function attachInsertEvent (el) {
|
||||
$('.insert', el).click(function () {
|
||||
|
@ -13,7 +13,9 @@ if (isset($post) || isset($page)) {
|
||||
?>
|
||||
|
||||
<div id="upload-panel" class="p">
|
||||
<div class="upload-area" draggable="true"><?php _e('拖放文件到这里<br>或者 %s选择文件上传%s', '<a href="###" class="upload-file">', '</a>'); ?></div>
|
||||
<div class="upload-area" data-url="<?php $security->index('/action/upload'); ?>">
|
||||
<?php _e('拖放文件到这里<br>或者 %s选择文件上传%s', '<a href="###" class="upload-file">', '</a>'); ?>
|
||||
</div>
|
||||
<ul id="file-list">
|
||||
<?php while ($attachment->next()): ?>
|
||||
<li data-cid="<?php $attachment->cid(); ?>" data-url="<?php echo $attachment->attachment->url; ?>" data-image="<?php echo $attachment->attachment->isImage ? 1 : 0; ?>"><input type="hidden" name="attachment[]" value="<?php $attachment->cid(); ?>" />
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?php if(!defined('__TYPECHO_ADMIN__')) exit; ?>
|
||||
<?php \Typecho\Plugin::factory('admin/footer.php')->begin(); ?>
|
||||
<?php \Typecho\Plugin::factory('admin/footer.php')->call('begin'); ?>
|
||||
</body>
|
||||
</html>
|
||||
<?php
|
||||
/** 注册一个结束插件 */
|
||||
\Typecho\Plugin::factory('admin/footer.php')->end();
|
||||
\Typecho\Plugin::factory('admin/footer.php')->call('end');
|
||||
|
@ -2,25 +2,50 @@
|
||||
<script>
|
||||
(function () {
|
||||
$(document).ready(function () {
|
||||
var error = $('.typecho-option .error:first');
|
||||
const error = $('.typecho-option .error:first');
|
||||
|
||||
if (error.length > 0) {
|
||||
$('html,body').scrollTop(error.parents('.typecho-option').offset().top);
|
||||
}
|
||||
|
||||
$('form').submit(function () {
|
||||
if (this.submitted) {
|
||||
$('.main form').submit(function () {
|
||||
const self = $(this);
|
||||
|
||||
if (self.hasClass('submitting')) {
|
||||
return false;
|
||||
} else {
|
||||
this.submitted = true;
|
||||
$('button[type=submit]', this).attr('disabled', 'disabled');
|
||||
self.addClass('submitting');
|
||||
}
|
||||
}).on('submitted', function () {
|
||||
$('button[type=submit]', this).removeAttr('disabled');
|
||||
$(this).removeClass('submitting');
|
||||
});
|
||||
|
||||
$('label input[type=text]').click(function (e) {
|
||||
var check = $('#' + $(this).parents('label').attr('for'));
|
||||
const check = $('#' + $(this).parents('label').attr('for'));
|
||||
check.prop('checked', true);
|
||||
return false;
|
||||
});
|
||||
|
||||
$('.main form input[type="url"]').each(function () {
|
||||
const self = $(this);
|
||||
const input = $('<input type="hidden" />').attr('name', self.attr('name'));
|
||||
|
||||
function setInput() {
|
||||
const url = self.val();
|
||||
|
||||
try {
|
||||
const urlObj = new URL(url);
|
||||
input.val(urlObj.toString());
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
self.removeAttr('name').after(input).on('input', setInput);
|
||||
setInput();
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
@ -8,7 +8,7 @@ $header = '<link rel="stylesheet" href="' . $options->adminStaticUrl('css', 'nor
|
||||
<link rel="stylesheet" href="' . $options->adminStaticUrl('css', 'style.css', true) . '">';
|
||||
|
||||
/** 注册一个初始化插件 */
|
||||
$header = \Typecho\Plugin::factory('admin/header.php')->header($header);
|
||||
$header = \Typecho\Plugin::factory('admin/header.php')->call('header', $header);
|
||||
|
||||
?><!DOCTYPE HTML>
|
||||
<html>
|
||||
|
Binary file not shown.
File diff suppressed because one or more lines are too long
2
admin/js/jquery-ui.js
vendored
2
admin/js/jquery-ui.js
vendored
File diff suppressed because one or more lines are too long
2
admin/js/jquery.js
vendored
2
admin/js/jquery.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -21,7 +21,7 @@ include 'header.php';
|
||||
</p>
|
||||
<p>
|
||||
<label for="password" class="sr-only"><?php _e('密码'); ?></label>
|
||||
<input type="password" id="password" name="password" class="text-l w-100" placeholder="<?php _e('密码'); ?>" />
|
||||
<input type="password" id="password" name="password" class="text-l w-100" placeholder="<?php _e('密码'); ?>" required />
|
||||
</p>
|
||||
<p class="submit">
|
||||
<button type="submit" class="btn btn-l w-100 primary"><?php _e('登录'); ?></button>
|
||||
|
@ -120,7 +120,7 @@ $isAllComments = ('on' == $request->get('__typecho_all_comments') || 'on' == \Ty
|
||||
<td valign="top" class="kit-hidden-mb">
|
||||
<div class="comment-avatar">
|
||||
<?php if ('comment' == $comments->type): ?>
|
||||
<?php $comments->gravatar(40); ?>
|
||||
<?php $comments->gravatar(40, null, true); ?>
|
||||
<?php endif; ?>
|
||||
<?php if ('comment' != $comments->type): ?>
|
||||
<?php _e('引用'); ?>
|
||||
|
@ -35,6 +35,7 @@ $pages = \Widget\Contents\Page\Admin::alloc();
|
||||
</div>
|
||||
|
||||
<div class="search" role="search">
|
||||
<?php $pages->backLink(); ?>
|
||||
<?php if ('' != $request->keywords): ?>
|
||||
<a href="<?php $options->adminUrl('manage-pages.php'); ?>"><?php _e('« 取消筛选'); ?></a>
|
||||
<?php endif; ?>
|
||||
@ -61,7 +62,7 @@ $pages = \Widget\Contents\Page\Admin::alloc();
|
||||
<th class="kit-hidden-mb"></th>
|
||||
<th class="kit-hidden-mb"></th>
|
||||
<th><?php _e('标题'); ?></th>
|
||||
<th><?php _e('缩略名'); ?></th>
|
||||
<th><?php _e('子页面'); ?></th>
|
||||
<th class="kit-hidden-mb"><?php _e('作者'); ?></th>
|
||||
<th><?php _e('日期'); ?></th>
|
||||
</tr>
|
||||
@ -80,8 +81,10 @@ $pages = \Widget\Contents\Page\Admin::alloc();
|
||||
<td>
|
||||
<a href="<?php $options->adminUrl('write-page.php?cid=' . $pages->cid); ?>"><?php $pages->title(); ?></a>
|
||||
<?php
|
||||
if ($pages->hasSaved || 'page_draft' == $pages->type) {
|
||||
if ('page_draft' == $pages->type) {
|
||||
echo '<em class="status">' . _t('草稿') . '</em>';
|
||||
} elseif ($pages->revision) {
|
||||
echo '<em class="status">' . _t('有修订版') . '</em>';
|
||||
}
|
||||
|
||||
if ('hidden' == $pages->status) {
|
||||
@ -97,12 +100,18 @@ $pages = \Widget\Contents\Page\Admin::alloc();
|
||||
class="i-exlink"></i></a>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?php $pages->slug(); ?></td>
|
||||
<td>
|
||||
<?php if (count($pages->children) > 0): ?>
|
||||
<a href="<?php $options->adminUrl('manage-pages.php?parent=' . $pages->cid); ?>"><?php echo _n('一个页面', '%d个页面', count($pages->children)); ?></a>
|
||||
<?php else: ?>
|
||||
<a href="<?php $options->adminUrl('write-page.php?parent=' . $pages->cid); ?>"><?php echo _e('新增'); ?></a>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="kit-hidden-mb"><?php $pages->author(); ?></td>
|
||||
<td>
|
||||
<?php if ($pages->hasSaved): ?>
|
||||
<?php if ('page_draft' == $pages->type || $pages->revision): ?>
|
||||
<span class="description">
|
||||
<?php $modifyDate = new \Typecho\Date($pages->modified); ?>
|
||||
<?php $modifyDate = new \Typecho\Date($pages->revision ? $pages->revision['modified'] : $pages->modified); ?>
|
||||
<?php _e('保存于 %s', $modifyDate->word()); ?>
|
||||
</span>
|
||||
<?php else: ?>
|
||||
@ -132,7 +141,7 @@ include 'common-js.php';
|
||||
include 'table-js.php';
|
||||
?>
|
||||
|
||||
<?php if (!isset($request->status) || 'publish' == $request->get('status')): ?>
|
||||
<?php if (!$request->is('keywords')): ?>
|
||||
<script type="text/javascript">
|
||||
(function () {
|
||||
$(document).ready(function () {
|
||||
|
@ -148,8 +148,10 @@ $isAllPosts = ('on' == $request->get('__typecho_all_posts') || 'on' == \Typecho\
|
||||
<td>
|
||||
<a href="<?php $options->adminUrl('write-post.php?cid=' . $posts->cid); ?>"><?php $posts->title(); ?></a>
|
||||
<?php
|
||||
if ($posts->hasSaved || 'post_draft' == $posts->type) {
|
||||
if ('post_draft' == $posts->type) {
|
||||
echo '<em class="status">' . _t('草稿') . '</em>';
|
||||
} elseif ($posts->revision) {
|
||||
echo '<em class="status">' . _t('有修订版') . '</em>';
|
||||
}
|
||||
|
||||
if ('hidden' == $posts->status) {
|
||||
@ -175,19 +177,18 @@ $isAllPosts = ('on' == $request->get('__typecho_all_posts') || 'on' == \Typecho\
|
||||
href="<?php $options->adminUrl('manage-posts.php?__typecho_all_posts=off&uid=' . $posts->author->uid); ?>"><?php $posts->author(); ?></a>
|
||||
</td>
|
||||
<td class="kit-hidden-mb"><?php $categories = $posts->categories;
|
||||
$length = count($categories); ?>
|
||||
<?php foreach ($categories as $key => $val): ?>
|
||||
while ($categories->next()): ?>
|
||||
<?php echo '<a href="';
|
||||
$options->adminUrl('manage-posts.php?category=' . $val['mid']
|
||||
$options->adminUrl('manage-posts.php?category=' . $categories->mid
|
||||
. (isset($request->uid) ? '&uid=' . $request->filter('encode')->uid : '')
|
||||
. (isset($request->status) ? '&status=' . $request->filter('encode')->status : ''));
|
||||
echo '">' . $val['name'] . '</a>' . ($key < $length - 1 ? ', ' : ''); ?>
|
||||
<?php endforeach; ?>
|
||||
echo '">' . $categories->name . '</a>' . ($categories->sequence < $categories->length - 1 ? ', ' : ''); ?>
|
||||
<?php endwhile; ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($posts->hasSaved): ?>
|
||||
<?php if ('post_draft' == $posts->type || $posts->revision): ?>
|
||||
<span class="description">
|
||||
<?php $modifyDate = new \Typecho\Date($posts->modified); ?>
|
||||
<?php $modifyDate = new \Typecho\Date($posts->revision ? $posts->revision['modified'] : $posts->modified); ?>
|
||||
<?php _e('保存于 %s', $modifyDate->word()); ?>
|
||||
</span>
|
||||
<?php else: ?>
|
||||
|
140
admin/media.php
140
admin/media.php
@ -3,13 +3,7 @@ include 'common.php';
|
||||
include 'header.php';
|
||||
include 'menu.php';
|
||||
|
||||
$phpMaxFilesize = function_exists('ini_get') ? trim(ini_get('upload_max_filesize')) : 0;
|
||||
|
||||
if (preg_match("/^([0-9]+)([a-z]{1,2})$/i", $phpMaxFilesize, $matches)) {
|
||||
$phpMaxFilesize = strtolower($matches[1] . $matches[2] . (1 == strlen($matches[2]) ? 'b' : ''));
|
||||
}
|
||||
|
||||
$attachment = \Widget\Contents\Attachment\Edit::alloc();
|
||||
\Widget\Contents\Attachment\Edit::alloc()->prepare()->to($attachment);
|
||||
?>
|
||||
|
||||
<div class="main">
|
||||
@ -35,8 +29,9 @@ $attachment = \Widget\Contents\Attachment\Edit::alloc();
|
||||
</p>
|
||||
|
||||
<div id="upload-panel" class="p">
|
||||
<div class="upload-area"
|
||||
draggable="true"><?php _e('拖放文件到这里<br>或者 %s选择文件上传%s', '<a href="###" class="upload-file">', '</a>'); ?></div>
|
||||
<div class="upload-area" data-url="<?php $security->index('/action/upload?do=modify'); ?>">
|
||||
<?php _e('拖放文件到这里<br>或者 %s选择文件上传%s', '<a href="###" class="upload-file">', '</a>'); ?>
|
||||
</div>
|
||||
<ul id="file-list"></ul>
|
||||
</div>
|
||||
</div>
|
||||
@ -50,9 +45,8 @@ $attachment = \Widget\Contents\Attachment\Edit::alloc();
|
||||
<?php
|
||||
include 'copyright.php';
|
||||
include 'common-js.php';
|
||||
include 'file-upload-js.php';
|
||||
?>
|
||||
<script src="<?php $options->adminStaticUrl('js', 'moxie.js'); ?>"></script>
|
||||
<script src="<?php $options->adminStaticUrl('js', 'plupload.js'); ?>"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
$('#attachment-url').click(function () {
|
||||
@ -69,130 +63,16 @@ include 'common-js.php';
|
||||
return false;
|
||||
});
|
||||
|
||||
$('.upload-area').bind({
|
||||
dragenter: function () {
|
||||
$(this).parent().addClass('drag');
|
||||
},
|
||||
|
||||
dragover: function (e) {
|
||||
$(this).parent().addClass('drag');
|
||||
},
|
||||
|
||||
drop: function () {
|
||||
$(this).parent().removeClass('drag');
|
||||
},
|
||||
|
||||
dragend: function () {
|
||||
$(this).parent().removeClass('drag');
|
||||
},
|
||||
|
||||
dragleave: function () {
|
||||
$(this).parent().removeClass('drag');
|
||||
}
|
||||
});
|
||||
|
||||
function fileUploadStart(file) {
|
||||
$('<ul id="file-list"></ul>').appendTo('#upload-panel');
|
||||
$('<li id="' + file.id + '" class="loading">'
|
||||
+ file.name + '</li>').prependTo('#file-list');
|
||||
}
|
||||
|
||||
function fileUploadError(error) {
|
||||
var file = error.file, code = error.code, word;
|
||||
|
||||
switch (code) {
|
||||
case plupload.FILE_SIZE_ERROR:
|
||||
word = '<?php _e('文件大小超过限制'); ?>';
|
||||
break;
|
||||
case plupload.FILE_EXTENSION_ERROR:
|
||||
word = '<?php _e('文件扩展名不被支持'); ?>';
|
||||
break;
|
||||
case plupload.FILE_DUPLICATE_ERROR:
|
||||
word = '<?php _e('文件已经上传过'); ?>';
|
||||
break;
|
||||
case plupload.HTTP_ERROR:
|
||||
default:
|
||||
word = '<?php _e('上传出现错误'); ?>';
|
||||
break;
|
||||
Typecho.uploadComplete = function (attachment) {
|
||||
if (attachment.isImage) {
|
||||
$('.typecho-attachment-photo').attr('src', attachment.url + '?' + Math.random());
|
||||
}
|
||||
|
||||
var fileError = '<?php _e('%s 上传失败'); ?>'.replace('%s', file.name),
|
||||
li, exist = $('#' + file.id);
|
||||
|
||||
if (exist.length > 0) {
|
||||
li = exist.removeClass('loading').html(fileError);
|
||||
} else {
|
||||
$('<ul id="file-list"></ul>').appendTo('#upload-panel');
|
||||
li = $('<li>' + fileError + '<br />' + word + '</li>').prependTo('#file-list');
|
||||
}
|
||||
|
||||
li.effect('highlight', {color: '#FBC2C4'}, 2000, function () {
|
||||
$(this).remove();
|
||||
});
|
||||
}
|
||||
|
||||
function fileUploadComplete(id, url, data) {
|
||||
var img = $('.typecho-attachment-photo');
|
||||
|
||||
if (img.length > 0) {
|
||||
img.get(0).src = '<?php $attachment->attachment->url(); ?>?' + Math.random();
|
||||
}
|
||||
|
||||
$('#' + id).text('<?php _e('文件 %s 已经替换'); ?>'.replace('%s', data.title))
|
||||
$('#file-list li').text('<?php _e('文件 %s 已经替换'); ?>'.replace('%s', attachment.title))
|
||||
.effect('highlight', 1000, function () {
|
||||
$(this).remove();
|
||||
$('#file-list').remove();
|
||||
});
|
||||
}
|
||||
|
||||
var uploader = new plupload.Uploader({
|
||||
browse_button: $('.upload-file').get(0),
|
||||
url: '<?php $security->index('/action/upload?do=modify&cid=' . $attachment->cid); ?>',
|
||||
runtimes: 'html5,flash,html4',
|
||||
flash_swf_url: '<?php $options->adminStaticUrl('js', 'Moxie.swf'); ?>',
|
||||
drop_element: $('.upload-area').get(0),
|
||||
filters: {
|
||||
max_file_size: '<?php echo $phpMaxFilesize ?>',
|
||||
mime_types: [{
|
||||
'title': '<?php _e('允许上传的文件'); ?>',
|
||||
'extensions': '<?php $attachment->attachment->type(); ?>'
|
||||
}],
|
||||
prevent_duplicates: true
|
||||
},
|
||||
multi_selection: false,
|
||||
|
||||
init: {
|
||||
FilesAdded: function (up, files) {
|
||||
plupload.each(files, function (file) {
|
||||
fileUploadStart(file);
|
||||
});
|
||||
|
||||
uploader.start();
|
||||
},
|
||||
|
||||
FileUploaded: function (up, file, result) {
|
||||
if (200 == result.status) {
|
||||
var data = $.parseJSON(result.response);
|
||||
|
||||
if (data) {
|
||||
fileUploadComplete(file.id, data[0], data[1]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fileUploadError({
|
||||
code: plupload.HTTP_ERROR,
|
||||
file: file
|
||||
});
|
||||
},
|
||||
|
||||
Error: function (up, error) {
|
||||
fileUploadError(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
uploader.init();
|
||||
};
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
|
@ -5,7 +5,7 @@
|
||||
<?php $menu->output(); ?>
|
||||
</nav>
|
||||
<div class="operate">
|
||||
<?php \Typecho\Plugin::factory('admin/menu.php')->navBar(); ?><a title="<?php
|
||||
<?php \Typecho\Plugin::factory('admin/menu.php')->call('navBar'); ?><a title="<?php
|
||||
if ($user->logged > 0) {
|
||||
$logged = new \Typecho\Date($user->logged);
|
||||
_e('最后登录: %s', $logged->word());
|
||||
|
@ -11,7 +11,7 @@ $stat = \Widget\Stat::alloc();
|
||||
<?php include 'page-title.php'; ?>
|
||||
<div class="row typecho-page-main">
|
||||
<div class="col-mb-12 col-tb-3">
|
||||
<p><a href="https://gravatar.com/emails/"
|
||||
<p><a href="https://gravatar.com/"
|
||||
title="<?php _e('在 Gravatar 上修改头像'); ?>"><?php echo '<img class="profile-avatar" src="' . \Typecho\Common::gravatarUrl($user->mail, 220, 'X', 'mm', $request->isSecure()) . '" alt="' . $user->screenName . '" />'; ?></a>
|
||||
</p>
|
||||
<h2><?php $user->screenName(); ?></h2>
|
||||
@ -57,6 +57,6 @@ $stat = \Widget\Stat::alloc();
|
||||
include 'copyright.php';
|
||||
include 'common-js.php';
|
||||
include 'form-js.php';
|
||||
\Typecho\Plugin::factory('admin/profile.php')->bottom();
|
||||
\Typecho\Plugin::factory('admin/profile.php')->call('bottom');
|
||||
include 'footer.php';
|
||||
?>
|
||||
|
11714
admin/src/js/moxie.js
11714
admin/src/js/moxie.js
File diff suppressed because it is too large
Load Diff
@ -1767,6 +1767,8 @@ else
|
||||
hooks.addNoop("enterFakeFullScreen");
|
||||
hooks.addNoop("exitFullScreen");
|
||||
|
||||
hooks.addNoop("save");
|
||||
|
||||
this.getConverter = function () { return markdownConverter; }
|
||||
|
||||
var that = this,
|
||||
@ -3082,11 +3084,13 @@ else
|
||||
doClick(buttons.undo);
|
||||
}
|
||||
break;
|
||||
case "s":
|
||||
hooks.save();
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (key.preventDefault) {
|
||||
key.preventDefault();
|
||||
}
|
||||
|
@ -1,443 +0,0 @@
|
||||
// Generated by CoffeeScript 1.12.7
|
||||
|
||||
/*
|
||||
paste.js is an interface to read data ( text / image ) from clipboard in different browsers. It also contains several hacks.
|
||||
|
||||
https://github.com/layerssss/paste.js
|
||||
*/
|
||||
|
||||
(function() {
|
||||
var $, Paste, createHiddenEditable, dataURLtoBlob, isFocusable;
|
||||
|
||||
$ = window.jQuery;
|
||||
|
||||
$.paste = function(pasteContainer) {
|
||||
var pm;
|
||||
if (typeof console !== "undefined" && console !== null) {
|
||||
console.log("DEPRECATED: This method is deprecated. Please use $.fn.pastableNonInputable() instead.");
|
||||
}
|
||||
pm = Paste.mountNonInputable(pasteContainer);
|
||||
return pm._container;
|
||||
};
|
||||
|
||||
$.fn.pastableNonInputable = function() {
|
||||
var el, j, len, ref;
|
||||
ref = this;
|
||||
for (j = 0, len = ref.length; j < len; j++) {
|
||||
el = ref[j];
|
||||
if (el._pastable || $(el).is('textarea, input:text, [contenteditable]')) {
|
||||
continue;
|
||||
}
|
||||
Paste.mountNonInputable(el);
|
||||
el._pastable = true;
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
$.fn.pastableTextarea = function() {
|
||||
var el, j, len, ref;
|
||||
ref = this;
|
||||
for (j = 0, len = ref.length; j < len; j++) {
|
||||
el = ref[j];
|
||||
if (el._pastable || $(el).is(':not(textarea, input:text)')) {
|
||||
continue;
|
||||
}
|
||||
Paste.mountTextarea(el);
|
||||
el._pastable = true;
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
$.fn.pastableContenteditable = function() {
|
||||
var el, j, len, ref;
|
||||
ref = this;
|
||||
for (j = 0, len = ref.length; j < len; j++) {
|
||||
el = ref[j];
|
||||
if (el._pastable || $(el).is(':not([contenteditable])')) {
|
||||
continue;
|
||||
}
|
||||
Paste.mountContenteditable(el);
|
||||
el._pastable = true;
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
dataURLtoBlob = function(dataURL, sliceSize) {
|
||||
var b64Data, byteArray, byteArrays, byteCharacters, byteNumbers, contentType, i, m, offset, ref, slice;
|
||||
if (sliceSize == null) {
|
||||
sliceSize = 512;
|
||||
}
|
||||
if (!(m = dataURL.match(/^data\:([^\;]+)\;base64\,(.+)$/))) {
|
||||
return null;
|
||||
}
|
||||
ref = m, m = ref[0], contentType = ref[1], b64Data = ref[2];
|
||||
byteCharacters = atob(b64Data);
|
||||
byteArrays = [];
|
||||
offset = 0;
|
||||
while (offset < byteCharacters.length) {
|
||||
slice = byteCharacters.slice(offset, offset + sliceSize);
|
||||
byteNumbers = new Array(slice.length);
|
||||
i = 0;
|
||||
while (i < slice.length) {
|
||||
byteNumbers[i] = slice.charCodeAt(i);
|
||||
i++;
|
||||
}
|
||||
byteArray = new Uint8Array(byteNumbers);
|
||||
byteArrays.push(byteArray);
|
||||
offset += sliceSize;
|
||||
}
|
||||
return new Blob(byteArrays, {
|
||||
type: contentType
|
||||
});
|
||||
};
|
||||
|
||||
createHiddenEditable = function() {
|
||||
return $(document.createElement('div')).attr('contenteditable', true).attr('aria-hidden', true).attr('tabindex', -1).css({
|
||||
width: 1,
|
||||
height: 1,
|
||||
position: 'fixed',
|
||||
left: -100,
|
||||
overflow: 'hidden',
|
||||
opacity: 1e-17
|
||||
});
|
||||
};
|
||||
|
||||
isFocusable = function(element, hasTabindex) {
|
||||
var fieldset, focusableIfVisible, img, map, mapName, nodeName;
|
||||
map = void 0;
|
||||
mapName = void 0;
|
||||
img = void 0;
|
||||
focusableIfVisible = void 0;
|
||||
fieldset = void 0;
|
||||
nodeName = element.nodeName.toLowerCase();
|
||||
if ('area' === nodeName) {
|
||||
map = element.parentNode;
|
||||
mapName = map.name;
|
||||
if (!element.href || !mapName || map.nodeName.toLowerCase() !== 'map') {
|
||||
return false;
|
||||
}
|
||||
img = $('img[usemap=\'#' + mapName + '\']');
|
||||
return img.length > 0 && img.is(':visible');
|
||||
}
|
||||
if (/^(input|select|textarea|button|object)$/.test(nodeName)) {
|
||||
focusableIfVisible = !element.disabled;
|
||||
if (focusableIfVisible) {
|
||||
fieldset = $(element).closest('fieldset')[0];
|
||||
if (fieldset) {
|
||||
focusableIfVisible = !fieldset.disabled;
|
||||
}
|
||||
}
|
||||
} else if ('a' === nodeName) {
|
||||
focusableIfVisible = element.href || hasTabindex;
|
||||
} else {
|
||||
focusableIfVisible = hasTabindex;
|
||||
}
|
||||
focusableIfVisible = focusableIfVisible || $(element).is('[contenteditable]');
|
||||
return focusableIfVisible && $(element).is(':visible');
|
||||
};
|
||||
|
||||
Paste = (function() {
|
||||
Paste.prototype._target = null;
|
||||
|
||||
Paste.prototype._container = null;
|
||||
|
||||
Paste.mountNonInputable = function(nonInputable) {
|
||||
var paste;
|
||||
paste = new Paste(createHiddenEditable().appendTo(nonInputable), nonInputable);
|
||||
$(nonInputable).on('click', (function(_this) {
|
||||
return function(ev) {
|
||||
if (!(isFocusable(ev.target, false) || window.getSelection().toString())) {
|
||||
return paste._container.focus();
|
||||
}
|
||||
};
|
||||
})(this));
|
||||
paste._container.on('focus', (function(_this) {
|
||||
return function() {
|
||||
return $(nonInputable).addClass('pastable-focus');
|
||||
};
|
||||
})(this));
|
||||
return paste._container.on('blur', (function(_this) {
|
||||
return function() {
|
||||
return $(nonInputable).removeClass('pastable-focus');
|
||||
};
|
||||
})(this));
|
||||
};
|
||||
|
||||
Paste.mountTextarea = function(textarea) {
|
||||
var ctlDown, paste, ref, ref1;
|
||||
if ((typeof DataTransfer !== "undefined" && DataTransfer !== null ? DataTransfer.prototype : void 0) && ((ref = Object.getOwnPropertyDescriptor) != null ? (ref1 = ref.call(Object, DataTransfer.prototype, 'items')) != null ? ref1.get : void 0 : void 0)) {
|
||||
return this.mountContenteditable(textarea);
|
||||
}
|
||||
paste = new Paste(createHiddenEditable().insertBefore(textarea), textarea);
|
||||
ctlDown = false;
|
||||
$(textarea).on('keyup', function(ev) {
|
||||
var ref2;
|
||||
if ((ref2 = ev.keyCode) === 17 || ref2 === 224) {
|
||||
ctlDown = false;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
$(textarea).on('keydown', function(ev) {
|
||||
var ref2;
|
||||
if ((ref2 = ev.keyCode) === 17 || ref2 === 224) {
|
||||
ctlDown = true;
|
||||
}
|
||||
if ((ev.ctrlKey != null) && (ev.metaKey != null)) {
|
||||
ctlDown = ev.ctrlKey || ev.metaKey;
|
||||
}
|
||||
if (ctlDown && ev.keyCode === 86) {
|
||||
paste._textarea_focus_stolen = true;
|
||||
paste._container.focus();
|
||||
paste._paste_event_fired = false;
|
||||
setTimeout((function(_this) {
|
||||
return function() {
|
||||
if (!paste._paste_event_fired) {
|
||||
$(textarea).focus();
|
||||
return paste._textarea_focus_stolen = false;
|
||||
}
|
||||
};
|
||||
})(this), 1);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
$(textarea).on('paste', (function(_this) {
|
||||
return function() {};
|
||||
})(this));
|
||||
$(textarea).on('focus', (function(_this) {
|
||||
return function() {
|
||||
if (!paste._textarea_focus_stolen) {
|
||||
return $(textarea).addClass('pastable-focus');
|
||||
}
|
||||
};
|
||||
})(this));
|
||||
$(textarea).on('blur', (function(_this) {
|
||||
return function() {
|
||||
if (!paste._textarea_focus_stolen) {
|
||||
return $(textarea).removeClass('pastable-focus');
|
||||
}
|
||||
};
|
||||
})(this));
|
||||
$(paste._target).on('_pasteCheckContainerDone', (function(_this) {
|
||||
return function() {
|
||||
$(textarea).focus();
|
||||
return paste._textarea_focus_stolen = false;
|
||||
};
|
||||
})(this));
|
||||
return $(paste._target).on('pasteText', (function(_this) {
|
||||
return function(ev, data) {
|
||||
var content, curEnd, curStart;
|
||||
curStart = $(textarea).prop('selectionStart');
|
||||
curEnd = $(textarea).prop('selectionEnd');
|
||||
content = $(textarea).val();
|
||||
$(textarea).val("" + content.slice(0, curStart) + data.text + content.slice(curEnd));
|
||||
$(textarea)[0].setSelectionRange(curStart + data.text.length, curStart + data.text.length);
|
||||
return $(textarea).trigger('change');
|
||||
};
|
||||
})(this));
|
||||
};
|
||||
|
||||
Paste.mountContenteditable = function(contenteditable) {
|
||||
var paste;
|
||||
paste = new Paste(contenteditable, contenteditable);
|
||||
$(contenteditable).on('focus', (function(_this) {
|
||||
return function() {
|
||||
return $(contenteditable).addClass('pastable-focus');
|
||||
};
|
||||
})(this));
|
||||
return $(contenteditable).on('blur', (function(_this) {
|
||||
return function() {
|
||||
return $(contenteditable).removeClass('pastable-focus');
|
||||
};
|
||||
})(this));
|
||||
};
|
||||
|
||||
function Paste(_container, _target) {
|
||||
this._container = _container;
|
||||
this._target = _target;
|
||||
this._container = $(this._container);
|
||||
this._target = $(this._target).addClass('pastable');
|
||||
this._container.on('paste', (function(_this) {
|
||||
return function(ev) {
|
||||
var _i, clipboardData, file, fileType, item, j, k, l, len, len1, len2, pastedFilename, reader, ref, ref1, ref2, ref3, ref4, stringIsFilename, text;
|
||||
_this.originalEvent = (ev.originalEvent !== null ? ev.originalEvent : null);
|
||||
_this._paste_event_fired = true;
|
||||
if (((ref = ev.originalEvent) != null ? ref.clipboardData : void 0) != null) {
|
||||
clipboardData = ev.originalEvent.clipboardData;
|
||||
if (clipboardData.items) {
|
||||
pastedFilename = null;
|
||||
_this.originalEvent.pastedTypes = [];
|
||||
ref1 = clipboardData.items;
|
||||
for (j = 0, len = ref1.length; j < len; j++) {
|
||||
item = ref1[j];
|
||||
if (item.type.match(/^text\/(plain|rtf|html)/)) {
|
||||
_this.originalEvent.pastedTypes.push(item.type);
|
||||
}
|
||||
}
|
||||
ref2 = clipboardData.items;
|
||||
for (_i = k = 0, len1 = ref2.length; k < len1; _i = ++k) {
|
||||
item = ref2[_i];
|
||||
if (item.type.match(/^image\//)) {
|
||||
reader = new FileReader();
|
||||
reader.onload = function(event) {
|
||||
return _this._handleImage(event.target.result, _this.originalEvent, pastedFilename);
|
||||
};
|
||||
try {
|
||||
reader.readAsDataURL(item.getAsFile());
|
||||
} catch (error) {}
|
||||
ev.preventDefault();
|
||||
break;
|
||||
}
|
||||
if (item.type === 'text/plain') {
|
||||
if (_i === 0 && clipboardData.items.length > 1 && clipboardData.items[1].type.match(/^image\//)) {
|
||||
stringIsFilename = true;
|
||||
fileType = clipboardData.items[1].type;
|
||||
}
|
||||
item.getAsString(function(string) {
|
||||
if (stringIsFilename) {
|
||||
pastedFilename = string;
|
||||
return _this._target.trigger('pasteText', {
|
||||
text: string,
|
||||
isFilename: true,
|
||||
fileType: fileType,
|
||||
originalEvent: _this.originalEvent
|
||||
});
|
||||
} else {
|
||||
return _this._target.trigger('pasteText', {
|
||||
text: string,
|
||||
originalEvent: _this.originalEvent
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
if (item.type === 'text/rtf') {
|
||||
item.getAsString(function(string) {
|
||||
return _this._target.trigger('pasteTextRich', {
|
||||
text: string,
|
||||
originalEvent: _this.originalEvent
|
||||
});
|
||||
});
|
||||
}
|
||||
if (item.type === 'text/html') {
|
||||
item.getAsString(function(string) {
|
||||
return _this._target.trigger('pasteTextHtml', {
|
||||
text: string,
|
||||
originalEvent: _this.originalEvent
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (-1 !== Array.prototype.indexOf.call(clipboardData.types, 'text/plain')) {
|
||||
text = clipboardData.getData('Text');
|
||||
setTimeout(function() {
|
||||
return _this._target.trigger('pasteText', {
|
||||
text: text,
|
||||
originalEvent: _this.originalEvent
|
||||
});
|
||||
}, 1);
|
||||
}
|
||||
_this._checkImagesInContainer(function(src) {
|
||||
return _this._handleImage(src, _this.originalEvent);
|
||||
});
|
||||
}
|
||||
}
|
||||
if (clipboardData = window.clipboardData) {
|
||||
if ((ref3 = (text = clipboardData.getData('Text'))) != null ? ref3.length : void 0) {
|
||||
setTimeout(function() {
|
||||
_this._target.trigger('pasteText', {
|
||||
text: text,
|
||||
originalEvent: _this.originalEvent
|
||||
});
|
||||
return _this._target.trigger('_pasteCheckContainerDone');
|
||||
}, 1);
|
||||
} else {
|
||||
ref4 = clipboardData.files;
|
||||
for (l = 0, len2 = ref4.length; l < len2; l++) {
|
||||
file = ref4[l];
|
||||
_this._handleImage(URL.createObjectURL(file), _this.originalEvent);
|
||||
}
|
||||
_this._checkImagesInContainer(function(src) {});
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
})(this));
|
||||
}
|
||||
|
||||
Paste.prototype._handleImage = function(src, e, name) {
|
||||
var loader;
|
||||
if (src.match(/^webkit\-fake\-url\:\/\//)) {
|
||||
return this._target.trigger('pasteImageError', {
|
||||
message: "You are trying to paste an image in Safari, however we are unable to retieve its data."
|
||||
});
|
||||
}
|
||||
this._target.trigger('pasteImageStart');
|
||||
loader = new Image();
|
||||
loader.crossOrigin = "anonymous";
|
||||
loader.onload = (function(_this) {
|
||||
return function() {
|
||||
var blob, canvas, ctx, dataURL;
|
||||
canvas = document.createElement('canvas');
|
||||
canvas.width = loader.width;
|
||||
canvas.height = loader.height;
|
||||
ctx = canvas.getContext('2d');
|
||||
ctx.drawImage(loader, 0, 0, canvas.width, canvas.height);
|
||||
dataURL = null;
|
||||
try {
|
||||
dataURL = canvas.toDataURL('image/png');
|
||||
blob = dataURLtoBlob(dataURL);
|
||||
} catch (error) {}
|
||||
if (dataURL) {
|
||||
_this._target.trigger('pasteImage', {
|
||||
blob: blob,
|
||||
dataURL: dataURL,
|
||||
width: loader.width,
|
||||
height: loader.height,
|
||||
originalEvent: e,
|
||||
name: name
|
||||
});
|
||||
}
|
||||
return _this._target.trigger('pasteImageEnd');
|
||||
};
|
||||
})(this);
|
||||
loader.onerror = (function(_this) {
|
||||
return function() {
|
||||
_this._target.trigger('pasteImageError', {
|
||||
message: "Failed to get image from: " + src,
|
||||
url: src
|
||||
});
|
||||
return _this._target.trigger('pasteImageEnd');
|
||||
};
|
||||
})(this);
|
||||
return loader.src = src;
|
||||
};
|
||||
|
||||
Paste.prototype._checkImagesInContainer = function(cb) {
|
||||
var img, j, len, ref, timespan;
|
||||
timespan = Math.floor(1000 * Math.random());
|
||||
ref = this._container.find('img');
|
||||
for (j = 0, len = ref.length; j < len; j++) {
|
||||
img = ref[j];
|
||||
img["_paste_marked_" + timespan] = true;
|
||||
}
|
||||
return setTimeout((function(_this) {
|
||||
return function() {
|
||||
var k, len1, ref1;
|
||||
ref1 = _this._container.find('img');
|
||||
for (k = 0, len1 = ref1.length; k < len1; k++) {
|
||||
img = ref1[k];
|
||||
if (!img["_paste_marked_" + timespan]) {
|
||||
cb(img.src);
|
||||
$(img).remove();
|
||||
}
|
||||
}
|
||||
return _this._target.trigger('_pasteCheckContainerDone');
|
||||
};
|
||||
})(this), 1);
|
||||
};
|
||||
|
||||
return Paste;
|
||||
|
||||
})();
|
||||
|
||||
}).call(this);
|
File diff suppressed because it is too large
Load Diff
@ -10,7 +10,8 @@
|
||||
}
|
||||
})
|
||||
},
|
||||
uploadComplete : function (file) {}
|
||||
uploadComplete : function (attachment) {},
|
||||
savePost : function (cb) {},
|
||||
};
|
||||
})(window);
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
* Forms
|
||||
*/
|
||||
|
||||
input[type=text], input[type=password], input[type=email],
|
||||
input[type=text], input[type=url], input[type=password], input[type=email],
|
||||
textarea {
|
||||
background: #FFF;
|
||||
border: 1px solid #D9D9D6;
|
||||
|
@ -60,6 +60,6 @@ include 'menu.php';
|
||||
<?php
|
||||
include 'copyright.php';
|
||||
include 'common-js.php';
|
||||
\Typecho\Plugin::factory('admin/theme-editor.php')->bottom($files);
|
||||
\Typecho\Plugin::factory('admin/theme-editor.php')->call('bottom', $files);
|
||||
include 'footer.php';
|
||||
?>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?php if(!defined('__TYPECHO_ADMIN__')) exit; ?>
|
||||
<?php \Typecho\Plugin::factory('admin/write-js.php')->write(); ?>
|
||||
<?php \Typecho\Plugin::factory('admin/write-js.php')->call('write'); ?>
|
||||
<?php \Widget\Metas\Tag\Cloud::alloc('sort=count&desc=1&limit=200')->to($tags); ?>
|
||||
|
||||
<script src="<?php $options->adminStaticUrl('js', 'timepicker.js'); ?>"></script>
|
||||
@ -42,12 +42,12 @@ $(document).ready(function() {
|
||||
Typecho.editorResize('text', '<?php $security->index('/action/ajax?do=editorResize'); ?>');
|
||||
|
||||
// tag autocomplete 提示
|
||||
var tags = $('#tags'), tagsPre = [];
|
||||
const tags = $('#tags'), tagsPre = [];
|
||||
|
||||
if (tags.length > 0) {
|
||||
var items = tags.val().split(','), result = [];
|
||||
for (var i = 0; i < items.length; i ++) {
|
||||
var tag = items[i];
|
||||
const items = tags.val().split(',');
|
||||
for (let i = 0; i < items.length; i ++) {
|
||||
const tag = items[i];
|
||||
|
||||
if (!tag) {
|
||||
continue;
|
||||
@ -87,7 +87,7 @@ $(document).ready(function() {
|
||||
result = [];
|
||||
}
|
||||
|
||||
if (!result[0] || result[0]['id'] != query) {
|
||||
if (!result[0] || result[0]['id'] !== query) {
|
||||
result.unshift({
|
||||
id : val,
|
||||
tags : val
|
||||
@ -100,17 +100,17 @@ $(document).ready(function() {
|
||||
|
||||
// tag autocomplete 提示宽度设置
|
||||
$('#token-input-tags').focus(function() {
|
||||
var t = $('.token-input-dropdown'),
|
||||
const t = $('.token-input-dropdown'),
|
||||
offset = t.outerWidth() - t.width();
|
||||
t.width($('.token-input-list').outerWidth() - offset);
|
||||
});
|
||||
}
|
||||
|
||||
// 缩略名自适应宽度
|
||||
var slug = $('#slug');
|
||||
const slug = $('#slug');
|
||||
|
||||
if (slug.length > 0) {
|
||||
var wrap = $('<div />').css({
|
||||
const wrap = $('<div />').css({
|
||||
'position' : 'relative',
|
||||
'display' : 'inline-block'
|
||||
}),
|
||||
@ -126,10 +126,10 @@ $(document).ready(function() {
|
||||
'minWidth' : '5px',
|
||||
'position' : 'absolute',
|
||||
'width' : '100%'
|
||||
})), originalWidth = slug.width();
|
||||
}));
|
||||
|
||||
function justifySlugWidth() {
|
||||
var val = slug.val();
|
||||
const val = slug.val();
|
||||
justifySlug.text(val.length > 0 ? val : ' ');
|
||||
}
|
||||
|
||||
@ -137,91 +137,111 @@ $(document).ready(function() {
|
||||
justifySlugWidth();
|
||||
}
|
||||
|
||||
// 原始的插入图片和文件
|
||||
Typecho.insertFileToEditor = function (file, url, isImage) {
|
||||
var textarea = $('#text'), sel = textarea.getSelection(),
|
||||
html = isImage ? '<img src="' + url + '" alt="' + file + '" />'
|
||||
: '<a href="' + url + '">' + file + '</a>',
|
||||
offset = (sel ? sel.start : 0) + html.length;
|
||||
|
||||
textarea.replaceSelection(html);
|
||||
textarea.setSelection(offset, offset);
|
||||
};
|
||||
|
||||
var submitted = false, form = $('form[name=write_post],form[name=write_page]').submit(function () {
|
||||
submitted = true;
|
||||
}), formAction = form.attr('action'),
|
||||
// 处理保存文章的逻辑
|
||||
const form = $('form[name=write_post],form[name=write_page]'),
|
||||
idInput = $('input[name=cid]'),
|
||||
cid = idInput.val(),
|
||||
draft = $('input[name=draft]'),
|
||||
draftId = draft.length > 0 ? draft.val() : 0,
|
||||
btnSave = $('#btn-save').removeAttr('name').removeAttr('value'),
|
||||
btnSubmit = $('#btn-submit').removeAttr('name').removeAttr('value'),
|
||||
btnPreview = $('#btn-preview'),
|
||||
doAction = $('<input type="hidden" name="do" value="publish" />').appendTo(form),
|
||||
locked = false,
|
||||
autoSave = $('<span id="auto-save-message" class="left"></span>').prependTo('.submit');
|
||||
|
||||
let cid = idInput.val(),
|
||||
draftId = draft.length > 0 ? draft.val() : 0,
|
||||
changed = false,
|
||||
autoSave = $('<span id="auto-save-message" class="left"></span>').prependTo('.submit'),
|
||||
written = false,
|
||||
lastSaveTime = null;
|
||||
|
||||
$(':input', form).bind('input change', function (e) {
|
||||
var tagName = $(this).prop('tagName');
|
||||
|
||||
if (tagName.match(/(input|textarea)/i) && e.type == 'change') {
|
||||
return;
|
||||
}
|
||||
|
||||
changed = true;
|
||||
form.on('write', function () {
|
||||
written = true;
|
||||
form.trigger('datachange');
|
||||
});
|
||||
|
||||
form.bind('field', function () {
|
||||
changed = true;
|
||||
form.on('change', function () {
|
||||
if (written) {
|
||||
form.trigger('datachange');
|
||||
}
|
||||
});
|
||||
|
||||
$('button[name=do]').click(function () {
|
||||
$('input[name=do]').val($(this).val());
|
||||
});
|
||||
|
||||
// 自动检测离开页
|
||||
$(window).bind('beforeunload', function () {
|
||||
if (changed && !form.hasClass('submitting')) {
|
||||
return '<?php _e('内容已经改变尚未保存, 您确认要离开此页面吗?'); ?>';
|
||||
}
|
||||
});
|
||||
|
||||
// 发送保存请求
|
||||
function saveData(cb) {
|
||||
function callback(o) {
|
||||
Typecho.savePost = function(cb) {
|
||||
if (!changed) {
|
||||
cb && cb();
|
||||
return;
|
||||
}
|
||||
|
||||
const callback = function (o) {
|
||||
lastSaveTime = o.time;
|
||||
cid = o.cid;
|
||||
draftId = o.draftId;
|
||||
idInput.val(cid);
|
||||
autoSave.text('<?php _e('已保存'); ?>' + ' (' + o.time + ')').effect('highlight', 1000);
|
||||
locked = false;
|
||||
|
||||
btnSave.removeAttr('disabled');
|
||||
btnPreview.removeAttr('disabled');
|
||||
|
||||
if (!!cb) {
|
||||
cb(o)
|
||||
}
|
||||
}
|
||||
cb && cb();
|
||||
};
|
||||
|
||||
changed = false;
|
||||
btnSave.attr('disabled', 'disabled');
|
||||
btnPreview.attr('disabled', 'disabled');
|
||||
autoSave.text('<?php _e('正在保存'); ?>');
|
||||
|
||||
if (typeof FormData !== 'undefined') {
|
||||
var data = new FormData(form.get(0));
|
||||
data.append('do', 'save');
|
||||
const data = new FormData(form.get(0));
|
||||
data.append('do', 'save');
|
||||
form.triggerHandler('submit');
|
||||
|
||||
$.ajax({
|
||||
url: formAction,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
type: 'POST',
|
||||
data: data,
|
||||
success: callback
|
||||
});
|
||||
} else {
|
||||
var data = form.serialize() + '&do=save';
|
||||
$.post(formAction, data, callback, 'json');
|
||||
$.ajax({
|
||||
url: form.attr('action'),
|
||||
processData: false,
|
||||
contentType: false,
|
||||
type: 'POST',
|
||||
data: data,
|
||||
success: callback,
|
||||
error: function () {
|
||||
autoSave.text('<?php _e('保存失败, 请重试'); ?>');
|
||||
},
|
||||
complete: function () {
|
||||
form.trigger('submitted');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
<?php if ($options->autoSave): ?>
|
||||
// 自动保存
|
||||
let saveTimer = null;
|
||||
let stopAutoSave = false;
|
||||
|
||||
form.on('datachange', function () {
|
||||
changed = true;
|
||||
autoSave.text('<?php _e('尚未保存'); ?>' + (lastSaveTime ? ' (<?php _e('上次保存时间'); ?>: ' + lastSaveTime + ')' : ''));
|
||||
|
||||
if (saveTimer) {
|
||||
clearTimeout(saveTimer);
|
||||
}
|
||||
}
|
||||
|
||||
saveTimer = setTimeout(function () {
|
||||
!stopAutoSave && Typecho.savePost();
|
||||
}, 3000);
|
||||
}).on('submit', function () {
|
||||
stopAutoSave = true;
|
||||
}).on('submitted', function () {
|
||||
stopAutoSave = false;
|
||||
});
|
||||
<?php else: ?>
|
||||
form.on('datachange', function () {
|
||||
changed = true;
|
||||
});
|
||||
<?php endif; ?>
|
||||
|
||||
// 计算夏令时偏移
|
||||
var dstOffset = (function () {
|
||||
var d = new Date(),
|
||||
const dstOffset = (function () {
|
||||
const d = new Date(),
|
||||
jan = new Date(d.getFullYear(), 0, 1),
|
||||
jul = new Date(d.getFullYear(), 6, 1),
|
||||
stdOffset = Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset());
|
||||
@ -236,50 +256,14 @@ $(document).ready(function() {
|
||||
// 时区
|
||||
$('<input name="timezone" type="hidden" />').appendTo(form).val(- (new Date).getTimezoneOffset() * 60);
|
||||
|
||||
// 自动保存
|
||||
<?php if ($options->autoSave): ?>
|
||||
var autoSaveOnce = !!cid;
|
||||
|
||||
function autoSaveListener () {
|
||||
setInterval(function () {
|
||||
if (changed && !locked) {
|
||||
locked = true;
|
||||
saveData();
|
||||
}
|
||||
}, 10000);
|
||||
}
|
||||
|
||||
if (autoSaveOnce) {
|
||||
autoSaveListener();
|
||||
}
|
||||
|
||||
$('#text').bind('input propertychange', function () {
|
||||
if (!locked) {
|
||||
autoSave.text('<?php _e('尚未保存'); ?>' + (lastSaveTime ? ' (<?php _e('上次保存时间'); ?>: ' + lastSaveTime + ')' : ''));
|
||||
}
|
||||
|
||||
if (!autoSaveOnce) {
|
||||
autoSaveOnce = true;
|
||||
autoSaveListener();
|
||||
}
|
||||
});
|
||||
<?php endif; ?>
|
||||
|
||||
// 自动检测离开页
|
||||
$(window).bind('beforeunload', function () {
|
||||
if (changed && !submitted) {
|
||||
return '<?php _e('内容已经改变尚未保存, 您确认要离开此页面吗?'); ?>';
|
||||
}
|
||||
});
|
||||
|
||||
// 预览功能
|
||||
var isFullScreen = false;
|
||||
let isFullScreen = false;
|
||||
|
||||
function previewData(cid) {
|
||||
isFullScreen = $(document.body).hasClass('fullscreen');
|
||||
$(document.body).addClass('fullscreen preview');
|
||||
|
||||
var frame = $('<iframe frameborder="0" class="preview-frame preview-loading"></iframe>')
|
||||
const frame = $('<iframe frameborder="0" class="preview-frame preview-loading"></iframe>')
|
||||
.attr('src', './preview.php?cid=' + cid)
|
||||
.attr('sandbox', 'allow-same-origin allow-scripts')
|
||||
.appendTo(document.body);
|
||||
@ -292,36 +276,28 @@ $(document).ready(function() {
|
||||
}
|
||||
|
||||
function cancelPreview() {
|
||||
if (submitted) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isFullScreen) {
|
||||
$(document.body).removeClass('fullscreen');
|
||||
}
|
||||
|
||||
$(document.body).removeClass('preview');
|
||||
$('.preview-frame').remove();
|
||||
};
|
||||
}
|
||||
|
||||
$('#btn-cancel-preview').click(cancelPreview);
|
||||
|
||||
$(window).bind('message', function (e) {
|
||||
if (e.originalEvent.data == 'cancelPreview') {
|
||||
if (e.originalEvent.data === 'cancelPreview') {
|
||||
cancelPreview();
|
||||
}
|
||||
});
|
||||
|
||||
btnPreview.click(function () {
|
||||
if (changed) {
|
||||
locked = true;
|
||||
|
||||
if (confirm('<?php _e('修改后的内容需要保存后才能预览, 是否保存?'); ?>')) {
|
||||
saveData(function (o) {
|
||||
previewData(o.draftId);
|
||||
Typecho.savePost(function () {
|
||||
previewData(draftId);
|
||||
});
|
||||
} else {
|
||||
locked = false;
|
||||
}
|
||||
} else if (!!draftId) {
|
||||
previewData(draftId);
|
||||
@ -330,28 +306,13 @@ $(document).ready(function() {
|
||||
}
|
||||
});
|
||||
|
||||
btnSave.click(function () {
|
||||
doAction.attr('value', 'save');
|
||||
});
|
||||
|
||||
btnSubmit.click(function () {
|
||||
doAction.attr('value', 'publish');
|
||||
});
|
||||
|
||||
// 控制选项和附件的切换
|
||||
var fileUploadInit = false;
|
||||
$('#edit-secondary .typecho-option-tabs li').click(function() {
|
||||
$('#edit-secondary .typecho-option-tabs li').removeClass('active');
|
||||
$(this).addClass('active');
|
||||
$(this).parents('#edit-secondary').find('.tab-content').addClass('hidden');
|
||||
|
||||
var selected_tab = $(this).find('a').attr('href'),
|
||||
selected_el = $(selected_tab).removeClass('hidden');
|
||||
$('#edit-secondary .typecho-option-tabs li.active').removeClass('active');
|
||||
$('#edit-secondary .tab-content').addClass('hidden');
|
||||
|
||||
if (!fileUploadInit) {
|
||||
selected_el.trigger('init');
|
||||
fileUploadInit = true;
|
||||
}
|
||||
const activeTab = $(this).addClass('active').find('a').attr('href');
|
||||
$(activeTab).removeClass('hidden');
|
||||
|
||||
return false;
|
||||
});
|
||||
@ -364,9 +325,9 @@ $(document).ready(function() {
|
||||
|
||||
// 自动隐藏密码框
|
||||
$('#visibility').change(function () {
|
||||
var val = $(this).val(), password = $('#post-password');
|
||||
const val = $(this).val(), password = $('#post-password');
|
||||
|
||||
if ('password' == val) {
|
||||
if ('password' === val) {
|
||||
password.removeClass('hidden');
|
||||
} else {
|
||||
password.addClass('hidden');
|
||||
|
@ -2,7 +2,19 @@
|
||||
include 'common.php';
|
||||
include 'header.php';
|
||||
include 'menu.php';
|
||||
\Widget\Contents\Page\Edit::alloc()->to($page);
|
||||
|
||||
$page = \Widget\Contents\Page\Edit::alloc()->prepare();
|
||||
|
||||
$parentPageId = $page->getParent();
|
||||
$parentPages = [0 => _t('不选择')];
|
||||
$parents = \Widget\Contents\Page\Admin::allocWithAlias(
|
||||
'options',
|
||||
'ignoreRequest=1' . ($request->is('cid') ? '&ignore=' . $request->get('cid') : '')
|
||||
);
|
||||
|
||||
while ($parents->next()) {
|
||||
$parentPages[$parents->cid] = str_repeat(' ', $parents->levels) . $parents->title;
|
||||
}
|
||||
?>
|
||||
<div class="main">
|
||||
<div class="body container">
|
||||
@ -14,7 +26,7 @@ include 'menu.php';
|
||||
<?php if ($page->draft['cid'] != $page->cid): ?>
|
||||
<?php $pageModifyDate = new \Typecho\Date($page->draft['modified']); ?>
|
||||
<cite
|
||||
class="edit-draft-notice"><?php _e('当前正在编辑的是保存于%s的草稿, 你可以<a href="%s">删除它</a>', $pageModifyDate->word(),
|
||||
class="edit-draft-notice"><?php _e('你正在编辑的是保存于 %s 的修订版, 你也可以 <a href="%s">删除它</a>', $pageModifyDate->word(),
|
||||
$security->getIndex('/action/contents-page-edit?do=deleteDraft&cid=' . $page->cid)); ?></cite>
|
||||
<?php else: ?>
|
||||
<cite class="edit-draft-notice"><?php _e('当前正在编辑的是未发布的草稿'); ?></cite>
|
||||
@ -38,12 +50,18 @@ include 'menu.php';
|
||||
?>
|
||||
<p class="mono url-slug">
|
||||
<label for="slug" class="sr-only"><?php _e('网址缩略名'); ?></label>
|
||||
<?php echo preg_replace("/\{slug\}/i", $input, $permalink); ?>
|
||||
<?php echo preg_replace_callback("/\{(slug|directory)\}/i", function ($matches) use ($input) {
|
||||
if ($matches[1] == 'slug') {
|
||||
return $input;
|
||||
} else {
|
||||
return '{directory/' . $input . '}';
|
||||
}
|
||||
}, $permalink); ?>
|
||||
</p>
|
||||
<p>
|
||||
<label for="text" class="sr-only"><?php _e('页面内容'); ?></label>
|
||||
<textarea style="height: <?php $options->editorSize(); ?>px" autocomplete="off" id="text"
|
||||
name="text" class="w-100 mono"><?php echo htmlspecialchars($page->text ?? ''); ?></textarea>
|
||||
name="text" class="w-100 mono"><?php echo htmlspecialchars($page->text); ?></textarea>
|
||||
</p>
|
||||
|
||||
<?php include 'custom-fields.php'; ?>
|
||||
@ -53,6 +71,7 @@ include 'menu.php';
|
||||
class="i-caret-left"></i> <?php _e('取消预览'); ?></button>
|
||||
</span>
|
||||
<span class="right">
|
||||
<input type="hidden" name="do" value="publish" />
|
||||
<input type="hidden" name="cid" value="<?php $page->cid(); ?>"/>
|
||||
<button type="button" id="btn-preview" class="btn"><i
|
||||
class="i-exlink"></i> <?php _e('预览页面'); ?></button>
|
||||
@ -66,7 +85,7 @@ include 'menu.php';
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<?php \Typecho\Plugin::factory('admin/write-page.php')->content($page); ?>
|
||||
<?php \Typecho\Plugin::factory('admin/write-page.php')->call('content', $page); ?>
|
||||
</div>
|
||||
<div id="edit-secondary" class="col-mb-12 col-tb-3" role="complementary">
|
||||
<ul class="typecho-option-tabs clearfix">
|
||||
@ -104,7 +123,20 @@ include 'menu.php';
|
||||
<p class="description"><?php _e('如果你为此页面选择了一个自定义模板, 系统将按照你选择的模板文件展现它'); ?></p>
|
||||
</section>
|
||||
|
||||
<?php \Typecho\Plugin::factory('admin/write-page.php')->option($page); ?>
|
||||
<section class="typecho-post-option">
|
||||
<label for="parent" class="typecho-label"><?php _e('父级页面'); ?></label>
|
||||
<p>
|
||||
<select name="parent" id="parent">
|
||||
<?php foreach ($parentPages as $pageId => $pageTitle): ?>
|
||||
<option
|
||||
value="<?php echo $pageId; ?>"<?php if ($pageId == ($page->parent ?? $parentPageId)): ?> selected="true"<?php endif; ?>><?php echo $pageTitle; ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</p>
|
||||
<p class="description"><?php _e('如果你设定了父级页面, 此页面将作为子页面呈现'); ?></p>
|
||||
</section>
|
||||
|
||||
<?php \Typecho\Plugin::factory('admin/write-page.php')->call('option', $page); ?>
|
||||
|
||||
<button type="button" id="advance-panel-btn" class="btn btn-xs"><?php _e('高级选项'); ?> <i
|
||||
class="i-caret-down"></i></button>
|
||||
@ -136,7 +168,7 @@ include 'menu.php';
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<?php \Typecho\Plugin::factory('admin/write-page.php')->advanceOption($page); ?>
|
||||
<?php \Typecho\Plugin::factory('admin/write-page.php')->call('advanceOption', $page); ?>
|
||||
</div>
|
||||
<?php if ($page->have()): ?>
|
||||
<?php $modified = new \Typecho\Date($page->modified); ?>
|
||||
@ -167,7 +199,7 @@ include 'common-js.php';
|
||||
include 'form-js.php';
|
||||
include 'write-js.php';
|
||||
|
||||
\Typecho\Plugin::factory('admin/write-page.php')->trigger($plugged)->richEditor($page);
|
||||
\Typecho\Plugin::factory('admin/write-page.php')->trigger($plugged)->call('richEditor', $page);
|
||||
if (!$plugged) {
|
||||
include 'editor-js.php';
|
||||
}
|
||||
|
@ -2,7 +2,8 @@
|
||||
include 'common.php';
|
||||
include 'header.php';
|
||||
include 'menu.php';
|
||||
\Widget\Contents\Post\Edit::alloc()->to($post);
|
||||
|
||||
$post = \Widget\Contents\Post\Edit::alloc()->prepare();
|
||||
?>
|
||||
<div class="main">
|
||||
<div class="body container">
|
||||
@ -14,7 +15,7 @@ include 'menu.php';
|
||||
<?php if ($post->draft['cid'] != $post->cid): ?>
|
||||
<?php $postModifyDate = new \Typecho\Date($post->draft['modified']); ?>
|
||||
<cite
|
||||
class="edit-draft-notice"><?php _e('你正在编辑的是保存于 %s 的草稿, 你也可以 <a href="%s">删除它</a>', $postModifyDate->word(),
|
||||
class="edit-draft-notice"><?php _e('你正在编辑的是保存于 %s 的修订版, 你也可以 <a href="%s">删除它</a>', $postModifyDate->word(),
|
||||
$security->getIndex('/action/contents-post-edit?do=deleteDraft&cid=' . $post->cid)); ?></cite>
|
||||
<?php else: ?>
|
||||
<cite class="edit-draft-notice"><?php _e('当前正在编辑的是未发布的草稿'); ?></cite>
|
||||
@ -47,7 +48,7 @@ include 'menu.php';
|
||||
<p>
|
||||
<label for="text" class="sr-only"><?php _e('文章内容'); ?></label>
|
||||
<textarea style="height: <?php $options->editorSize(); ?>px" autocomplete="off" id="text"
|
||||
name="text" class="w-100 mono"><?php echo htmlspecialchars($post->text ?? ''); ?></textarea>
|
||||
name="text" class="w-100 mono"><?php echo htmlspecialchars($post->text); ?></textarea>
|
||||
</p>
|
||||
|
||||
<?php include 'custom-fields.php'; ?>
|
||||
@ -58,6 +59,7 @@ include 'menu.php';
|
||||
class="i-caret-left"></i> <?php _e('取消预览'); ?></button>
|
||||
</span>
|
||||
<span class="right">
|
||||
<input type="hidden" name="do" value="publish" />
|
||||
<input type="hidden" name="cid" value="<?php $post->cid(); ?>"/>
|
||||
<button type="button" id="btn-preview" class="btn"><i
|
||||
class="i-exlink"></i> <?php _e('预览文章'); ?></button>
|
||||
@ -71,7 +73,7 @@ include 'menu.php';
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<?php \Typecho\Plugin::factory('admin/write-post.php')->content($post); ?>
|
||||
<?php \Typecho\Plugin::factory('admin/write-post.php')->call('content', $post); ?>
|
||||
</div>
|
||||
|
||||
<div id="edit-secondary" class="col-mb-12 col-tb-3" role="complementary">
|
||||
@ -93,13 +95,7 @@ include 'menu.php';
|
||||
<label class="typecho-label"><?php _e('分类'); ?></label>
|
||||
<?php \Widget\Metas\Category\Rows::alloc()->to($category); ?>
|
||||
<ul>
|
||||
<?php
|
||||
if ($post->have()) {
|
||||
$categories = array_column($post->categories, 'mid');
|
||||
} else {
|
||||
$categories = [];
|
||||
}
|
||||
?>
|
||||
<?php $categories = $post->categories->toArray('mid'); ?>
|
||||
<?php while ($category->next()): ?>
|
||||
<li><?php echo str_repeat(' ', $category->levels); ?><input
|
||||
type="checkbox" id="category-<?php $category->mid(); ?>"
|
||||
@ -114,11 +110,11 @@ include 'menu.php';
|
||||
|
||||
<section class="typecho-post-option">
|
||||
<label for="token-input-tags" class="typecho-label"><?php _e('标签'); ?></label>
|
||||
<p><input id="tags" name="tags" type="text" value="<?php $post->tags(',', false); ?>"
|
||||
<p><input id="tags" name="tags" type="text" value="<?php $post->have() ? $post->tags(',', false) : ''; ?>"
|
||||
class="w-100 text"/></p>
|
||||
</section>
|
||||
|
||||
<?php \Typecho\Plugin::factory('admin/write-post.php')->option($post); ?>
|
||||
<?php \Typecho\Plugin::factory('admin/write-post.php')->call('option', $post); ?>
|
||||
|
||||
<button type="button" id="advance-panel-btn" class="btn btn-xs"><?php _e('高级选项'); ?> <i
|
||||
class="i-caret-down"></i></button>
|
||||
@ -172,7 +168,7 @@ include 'menu.php';
|
||||
<p class="description"><?php _e('每一行一个引用地址, 用回车隔开'); ?></p>
|
||||
</section>
|
||||
|
||||
<?php \Typecho\Plugin::factory('admin/write-post.php')->advanceOption($post); ?>
|
||||
<?php \Typecho\Plugin::factory('admin/write-post.php')->call('advanceOption', $post); ?>
|
||||
</div><!-- end #advance-panel -->
|
||||
|
||||
<?php if ($post->have()): ?>
|
||||
@ -204,13 +200,13 @@ include 'common-js.php';
|
||||
include 'form-js.php';
|
||||
include 'write-js.php';
|
||||
|
||||
\Typecho\Plugin::factory('admin/write-post.php')->trigger($plugged)->richEditor($post);
|
||||
\Typecho\Plugin::factory('admin/write-post.php')->trigger($plugged)->call('richEditor', $post);
|
||||
if (!$plugged) {
|
||||
include 'editor-js.php';
|
||||
}
|
||||
|
||||
include 'file-upload-js.php';
|
||||
include 'custom-fields-js.php';
|
||||
\Typecho\Plugin::factory('admin/write-post.php')->bottom($post);
|
||||
\Typecho\Plugin::factory('admin/write-post.php')->call('bottom', $post);
|
||||
include 'footer.php';
|
||||
?>
|
||||
|
37
install.php
37
install.php
@ -205,14 +205,14 @@ function install_get_default_routers(): array
|
||||
'comment_page' =>
|
||||
[
|
||||
'url' => '[permalink:string]/comment-page-[commentPage:digital]',
|
||||
'widget' => '\Widget\Archive',
|
||||
'action' => 'render',
|
||||
'widget' => '\Widget\CommentPage',
|
||||
'action' => 'action',
|
||||
],
|
||||
'feed' =>
|
||||
[
|
||||
'url' => '/feed[feed:string:0]',
|
||||
'widget' => '\Widget\Archive',
|
||||
'action' => 'feed',
|
||||
'widget' => '\Widget\Feed',
|
||||
'action' => 'render',
|
||||
],
|
||||
'feedback' =>
|
||||
[
|
||||
@ -241,7 +241,16 @@ function install_get_default_options(): array
|
||||
if (empty($options)) {
|
||||
$options = [
|
||||
'theme' => 'default',
|
||||
'theme:default' => 'a:2:{s:7:"logoUrl";N;s:12:"sidebarBlock";a:5:{i:0;s:15:"ShowRecentPosts";i:1;s:18:"ShowRecentComments";i:2;s:12:"ShowCategory";i:3;s:11:"ShowArchive";i:4;s:9:"ShowOther";}}',
|
||||
'theme:default' => json_encode([
|
||||
'logoUrl' => '',
|
||||
'sidebarBlock' => [
|
||||
'ShowRecentPosts',
|
||||
'ShowRecentComments',
|
||||
'ShowCategory',
|
||||
'ShowArchive',
|
||||
'ShowOther'
|
||||
]
|
||||
]),
|
||||
'timezone' => '28800',
|
||||
'lang' => install_get_lang(),
|
||||
'charset' => 'UTF-8',
|
||||
@ -256,7 +265,7 @@ function install_get_default_options(): array
|
||||
'frontArchive' => 0,
|
||||
'commentsRequireMail' => 1,
|
||||
'commentsWhitelist' => 0,
|
||||
'commentsRequireURL' => 0,
|
||||
'commentsRequireUrl' => 0,
|
||||
'commentsRequireModeration' => 0,
|
||||
'plugins' => 'a:0:{}',
|
||||
'commentDateFormat' => 'F jS, Y \a\t h:i a',
|
||||
@ -294,9 +303,9 @@ function install_get_default_options(): array
|
||||
'commentsAvatar' => 1,
|
||||
'commentsAvatarRating' => 'G',
|
||||
'commentsAntiSpam' => 1,
|
||||
'routingTable' => serialize(install_get_default_routers()),
|
||||
'actionTable' => 'a:0:{}',
|
||||
'panelTable' => 'a:0:{}',
|
||||
'routingTable' => json_encode(install_get_default_routers()),
|
||||
'actionTable' => json_encode([]),
|
||||
'panelTable' => json_encode([]),
|
||||
'attachmentTypes' => '@image@',
|
||||
'secret' => \Typecho\Common::randString(32, true),
|
||||
'installed' => 0,
|
||||
@ -759,7 +768,7 @@ function install_step_1_perform()
|
||||
$realUploadDir = \Typecho\Common::url($uploadDir, __TYPECHO_ROOT_DIR__);
|
||||
$writeable = true;
|
||||
if (is_dir($realUploadDir)) {
|
||||
if (!is_writeable($realUploadDir) || !is_readable($realUploadDir)) {
|
||||
if (!is_writable($realUploadDir) || !is_readable($realUploadDir)) {
|
||||
if (!@chmod($realUploadDir, 0755)) {
|
||||
$writeable = false;
|
||||
}
|
||||
@ -926,7 +935,7 @@ function install_step_2_perform()
|
||||
'dbDatabase' => null,
|
||||
'dbEngine' => 'InnoDB',
|
||||
'dbSslCa' => null,
|
||||
'dbSslVerify' => 'on',
|
||||
'dbSslVerify' => 'off',
|
||||
],
|
||||
'Pgsql' => [
|
||||
'dbHost' => 'localhost',
|
||||
@ -935,6 +944,7 @@ function install_step_2_perform()
|
||||
'dbPassword' => null,
|
||||
'dbCharset' => 'utf8',
|
||||
'dbDatabase' => null,
|
||||
'dbSslVerify' => 'off',
|
||||
],
|
||||
'SQLite' => [
|
||||
'dbFile' => __TYPECHO_ROOT_DIR__ . '/usr/' . uniqid() . '.db'
|
||||
@ -956,7 +966,7 @@ function install_step_2_perform()
|
||||
'dbAdapter' => $request->getServer('TYPECHO_DB_ADAPTER', install_get_current_db_driver()),
|
||||
'dbNext' => $request->getServer('TYPECHO_DB_NEXT', 'none'),
|
||||
'dbSslCa' => $request->getServer('TYPECHO_DB_SSL_CA'),
|
||||
'dbSslVerify' => $request->getServer('TYPECHO_DB_SSL_VERIFY', 'on'),
|
||||
'dbSslVerify' => $request->getServer('TYPECHO_DB_SSL_VERIFY', 'off'),
|
||||
];
|
||||
} else {
|
||||
$config = $request->from([
|
||||
@ -1024,6 +1034,7 @@ function install_step_2_perform()
|
||||
->addRule('dbCharset', 'required', _t('确认您的配置'))
|
||||
->addRule('dbCharset', 'enum', _t('确认您的配置'), ['utf8'])
|
||||
->addRule('dbDatabase', 'required', _t('确认您的配置'))
|
||||
->addRule('dbSslVerify', 'enum', _t('确认您的配置'), ['on', 'off'])
|
||||
->run($config);
|
||||
break;
|
||||
case 'SQLite':
|
||||
@ -1058,7 +1069,7 @@ function install_step_2_perform()
|
||||
|
||||
// bool ssl verify
|
||||
if (isset($dbConfig['sslVerify'])) {
|
||||
$dbConfig['sslVerify'] = $dbConfig['sslVerify'] == 'on';
|
||||
$dbConfig['sslVerify'] = $dbConfig['sslVerify'] == 'on' || !empty($dbConfig['sslCa']);
|
||||
}
|
||||
|
||||
if (isset($dbConfig['file']) && preg_match("/^[a-z0-9]+\.[a-z0-9]{2,}$/i", $dbConfig['file'])) {
|
||||
|
@ -76,8 +76,8 @@
|
||||
<li>
|
||||
<label class="typecho-label" for="dbSslVerify"><?php _e('启用数据库 SSL 服务端证书验证'); ?></label>
|
||||
<select name="dbSslVerify" id="dbSslVerify">
|
||||
<option value="on"><?php _e('启用'); ?></option>
|
||||
<option value="off"><?php _e('不启用'); ?></option>
|
||||
<option value="on"><?php _e('启用'); ?></option>
|
||||
</select>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -6,13 +6,6 @@
|
||||
<p class="description"><?php _e('您可能会使用 "%s"', 'localhost'); ?></p>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="typecho-option">
|
||||
<li>
|
||||
<label class="typecho-label" for="dbPort"><?php _e('数据库端口'); ?></label>
|
||||
<input type="text" class="text" name="dbPort" id="dbPort" value="5432"/>
|
||||
<p class="description"><?php _e('如果您不知道此选项的意义, 请保留默认设置'); ?></p>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="typecho-option">
|
||||
<li>
|
||||
<label class="typecho-label" for="dbUser"><?php _e('数据库用户名'); ?></label>
|
||||
@ -34,4 +27,28 @@
|
||||
</li
|
||||
</ul>
|
||||
|
||||
<input type="hidden" name="dbCharset" value="utf8" />
|
||||
|
||||
<details>
|
||||
<summary>
|
||||
<strong><?php _e('高级选项'); ?></strong>
|
||||
</summary>
|
||||
<ul class="typecho-option">
|
||||
<li>
|
||||
<label class="typecho-label" for="dbPort"><?php _e('数据库端口'); ?></label>
|
||||
<input type="text" class="text" name="dbPort" id="dbPort" value="5432"/>
|
||||
<p class="description"><?php _e('如果您不知道此选项的意义, 请保留默认设置'); ?></p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<input type="hidden" name="dbCharset" value="utf8" />
|
||||
|
||||
<ul class="typecho-option">
|
||||
<li>
|
||||
<label class="typecho-label" for="dbSslVerify"><?php _e('启用数据库 SSL 服务端证书验证'); ?></label>
|
||||
<select name="dbSslVerify" id="dbSslVerify">
|
||||
<option value="off"><?php _e('不启用'); ?></option>
|
||||
<option value="on"><?php _e('启用'); ?></option>
|
||||
</select>
|
||||
</li>
|
||||
</ul>
|
||||
</details>
|
||||
|
@ -5,6 +5,7 @@ const sass = require('node-sass'),
|
||||
UglifyJS = require("uglify-js"),
|
||||
srcDir = __dirname + '/../admin/src',
|
||||
distDir = __dirname + '/../admin',
|
||||
themeDir = __dirname + '/../usr/themes/classic-22',
|
||||
action = process.argv.pop();
|
||||
|
||||
let spriteImporter = SpriteMagicImporter({
|
||||
@ -29,16 +30,16 @@ let spriteImporter = SpriteMagicImporter({
|
||||
}
|
||||
});
|
||||
|
||||
function buildSass(file) {
|
||||
let outFile = distDir + '/css/' + file.split('.')[0] + '.css',
|
||||
sassDir = srcDir + '/scss';
|
||||
function buildSass(file, dist, sassDir)
|
||||
{
|
||||
let outFile = dist + '/' + file.split('.')[0] + '.css';
|
||||
console.log(color.green('processing ' + file));
|
||||
|
||||
sass.render({
|
||||
file: sassDir + '/' + file,
|
||||
outFile: outFile,
|
||||
includePaths: [sassDir],
|
||||
outputStyle: 'compact',
|
||||
outputStyle: 'compressed',
|
||||
importer: spriteImporter
|
||||
}, function (error, result) {
|
||||
if (error) {
|
||||
@ -51,17 +52,21 @@ function buildSass(file) {
|
||||
});
|
||||
}
|
||||
|
||||
function minifyJs(file) {
|
||||
function minifyJs(file, dist)
|
||||
{
|
||||
console.log(color.green('minify ' + file));
|
||||
let code = {};
|
||||
code[file] = fs.readFileSync(srcDir + '/js/' + file).toString('utf8');
|
||||
|
||||
fs.writeFileSync(distDir + '/js/' + file,
|
||||
UglifyJS.minify(code).code);
|
||||
fs.writeFileSync(
|
||||
dist + '/' + file,
|
||||
UglifyJS.minify(code).code
|
||||
);
|
||||
}
|
||||
|
||||
function listFiles(dir, regExp) {
|
||||
let files = fs.readdirSync(srcDir + dir), result = [];
|
||||
function listFiles(dir, regExp)
|
||||
{
|
||||
let files = fs.readdirSync(dir), result = [];
|
||||
|
||||
files.map(function (file) {
|
||||
if (file.match(regExp)) {
|
||||
@ -75,14 +80,20 @@ function listFiles(dir, regExp) {
|
||||
if (action === 'css') {
|
||||
console.log(color.blue('build css'));
|
||||
|
||||
listFiles('/scss', /^[a-z0-9-]+\.scss$/).forEach(function (file) {
|
||||
buildSass(file);
|
||||
listFiles(srcDir + '/scss', /^[a-z0-9-]+\.scss$/).forEach(function (file) {
|
||||
buildSass(file, distDir + '/css', srcDir + '/scss');
|
||||
});
|
||||
} else if (action === 'js') {
|
||||
console.log(color.blue('build js'));
|
||||
|
||||
listFiles('/js', /^[-\w]+\.js$/).forEach(function (file) {
|
||||
minifyJs(file);
|
||||
listFiles(srcDir + '/js', /^[-\w]+\.js$/).forEach(function (file) {
|
||||
minifyJs(file, distDir + '/js');
|
||||
});
|
||||
} else if (action === 'theme_css') {
|
||||
console.log(color.blue('build theme css'));
|
||||
|
||||
listFiles(themeDir + '/static/scss', /^[a-z0-9-]+\.scss$/).forEach(function (file) {
|
||||
buildSass(file, themeDir + '/static/css', themeDir + '/static/scss');
|
||||
});
|
||||
} else {
|
||||
console.log(color.red('Please choose correct action.'));
|
||||
|
7286
tools/package-lock.json
generated
7286
tools/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -4,11 +4,12 @@
|
||||
"description": "Typecho build tools",
|
||||
"main": "build.js",
|
||||
"engines": {
|
||||
"node": "16.x"
|
||||
"node": "16.x"
|
||||
},
|
||||
"scripts": {
|
||||
"build_js": "node build.js js",
|
||||
"build_css": "node build.js css"
|
||||
"build_css": "node build.js css",
|
||||
"build_css:theme": "node build.js theme_css"
|
||||
},
|
||||
"keywords": [
|
||||
"typecho"
|
||||
@ -16,8 +17,9 @@
|
||||
"author": "joyqi",
|
||||
"license": "GPL-2.0-only",
|
||||
"dependencies": {
|
||||
"@picocss/pico": "^1.5.0",
|
||||
"chalk": "^4.0.0",
|
||||
"node-sass": "^6.0.1",
|
||||
"node-sass": "^9.0.0",
|
||||
"sprite-magic-importer": "^1.6.2",
|
||||
"uglify-js": "^3.11.6"
|
||||
}
|
||||
|
19
usr/themes/classic-22/404.php
Normal file
19
usr/themes/classic-22/404.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
|
||||
<?php $this->need('header.php'); ?>
|
||||
|
||||
<main class="container">
|
||||
<div class="container-thin">
|
||||
<h1 class="text-center" style="font-size: 4rem; margin-bottom: 2rem">404</h1>
|
||||
<ul style="margin-bottom: 2rem">
|
||||
<li>当前页面无法访问,可能没权限或已删除。</li>
|
||||
<li>The current page is not accessible, may not have permission or has been deleted.</li>
|
||||
<li>La page actuelle n'est pas accessible, elle n'a peut-être pas de droits ou a été supprimée.</li>
|
||||
<li>No se puede acceder a la página actual, puede que no tenga permiso o que haya sido eliminada.</li>
|
||||
<li>Доступ к текущей странице невозможен, возможно, у нее нет разрешения или она была удалена.</li>
|
||||
<li>現在のページにアクセスできない、権限がない、または削除された可能性があります。</li>
|
||||
</ul>
|
||||
<p class="text-center"><a href="<?php $this->options->siteUrl(); ?>" role="button" class="secondary"><?php _e('回首页'); ?></a></p>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php $this->need('footer.php'); ?>
|
44
usr/themes/classic-22/comments.php
Normal file
44
usr/themes/classic-22/comments.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
|
||||
<div id="comments">
|
||||
<?php $this->comments()->to($comments); ?>
|
||||
<?php if ($comments->have()): ?>
|
||||
<h2 class="text-center"><?php $this->commentsNum(_t('暂无评论'), _t('1 条评论'), _t('%d 条评论')); ?></h2>
|
||||
|
||||
<?php $comments->listComments(array(
|
||||
'commentStatus' => _t('你的评论正等待审核'),
|
||||
'avatarSize' => 120,
|
||||
'defaultAvatar' => 'monsterid'
|
||||
)); ?>
|
||||
|
||||
<?php $comments->pageNav('« 前一页', '后一页 »'); ?>
|
||||
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($this->allow('comment')): ?>
|
||||
<div id="<?php $this->respondId(); ?>" class="respond">
|
||||
<div class="cancel-comment-reply">
|
||||
<?php $comments->cancelReply(); ?>
|
||||
</div>
|
||||
|
||||
<h5 id="response"><?php _e('你的评论'); ?></h5>
|
||||
|
||||
<form method="post" action="<?php $this->commentUrl() ?>" id="comment-form" role="form">
|
||||
<textarea placeholder="<?php _e('评论内容...'); ?>" rows="4" cols="50" name="text" id="textarea" required><?php $this->remember('text'); ?></textarea>
|
||||
<?php if ($this->user->hasLogin()): ?>
|
||||
<p>
|
||||
<?php _e('登录身份:'); ?><a href="<?php $this->options->profileUrl(); ?>"><?php $this->user->screenName(); ?></a><span class="mx-2 text-muted">·</span><a href="<?php $this->options->logoutUrl(); ?>"><?php _e('退出'); ?></a>
|
||||
</p>
|
||||
<?php else: ?>
|
||||
<div class="grid">
|
||||
<input type="text" placeholder="<?php _e('名字'); ?>" name="author" id="author" value="<?php $this->remember('author'); ?>" required/>
|
||||
<input type="email" placeholder="<?php _e('Email'); ?>" name="mail" id="mail" value="<?php $this->remember('mail'); ?>"<?php if ($this->options->commentsRequireMail): ?> required<?php endif; ?> />
|
||||
<input type="url" placeholder="<?php _e('网站 http://'); ?><?php if (!($this->options->commentsRequireUrl)): ?><?php _e('(选填)'); ?><?php endif; ?>" name="url" id="url" placeholder="<?php _e('https://'); ?>" value="<?php $this->remember('url'); ?>"<?php if ($this->options->commentsRequireUrl): ?> required<?php endif; ?> />
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<button type="submit"><?php _e('提交评论'); ?></button>
|
||||
</form>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="text-center text-muted"><?php _e('评论已关闭'); ?></div>
|
||||
<?php endif; ?>
|
||||
</div>
|
22
usr/themes/classic-22/footer.php
Normal file
22
usr/themes/classic-22/footer.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
|
||||
<footer class="site-footer container-fluid">
|
||||
<div class="d-flex justify-content-between container-inner">
|
||||
<ul class="list-inline text-muted">
|
||||
<li>© <?php echo date('Y'); ?> <a href="<?php $this->options->siteUrl(); ?>"><?php $this->options->title(); ?></a></li>
|
||||
<li><a href="<?php $this->options->feedUrl(); ?>"><?php _e('RSS'); ?></a></li>
|
||||
</ul>
|
||||
<ul class="list-inline text-muted">
|
||||
<li>
|
||||
<?php _e('由 <a href="https://typecho.org">Typecho</a> 强力驱动'); ?>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<?php $this->footer(); ?>
|
||||
<script>
|
||||
feather.replace();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
39
usr/themes/classic-22/functions.php
Normal file
39
usr/themes/classic-22/functions.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
|
||||
|
||||
function themeConfig($form)
|
||||
{
|
||||
$themeStyle = new \Typecho\Widget\Helper\Form\Element\Radio(
|
||||
'themeStyle',
|
||||
array(
|
||||
'auto' => _t('自动'),
|
||||
'light' => _t('浅色'),
|
||||
'dark' => _t('深色')
|
||||
),
|
||||
'auto',
|
||||
_t('外观风格')
|
||||
);
|
||||
|
||||
$form->addInput($themeStyle);
|
||||
}
|
||||
|
||||
function postMeta(
|
||||
\Widget\Archive $archive,
|
||||
string $metaType = 'archive'
|
||||
)
|
||||
{
|
||||
?>
|
||||
<header class="entry-header text-center">
|
||||
<h1 class="entry-title" itemprop="name headline">
|
||||
<a href="<?php $archive->permalink() ?>" itemprop="url"><?php $archive->title() ?></a>
|
||||
</h1>
|
||||
<?php if ($metaType != 'page'): ?>
|
||||
<ul class="entry-meta list-inline text-muted">
|
||||
<li class="feather-calendar"><time datetime="<?php $archive->date('c'); ?>" itemprop="datePublished"><?php $archive->date(); ?></time></li>
|
||||
<li class="feather-folder"><?php $archive->category(', '); ?></li>
|
||||
<li class="feather-message"><a href="<?php $archive->permalink() ?>#comments" itemprop="discussionUrl"><?php $archive->commentsNum(_t('暂无评论'), _t('1 条评论'), _t('%d 条评论')); ?></a></li>
|
||||
</ul>
|
||||
<?php endif; ?>
|
||||
</header>
|
||||
<?php
|
||||
}
|
60
usr/themes/classic-22/header.php
Normal file
60
usr/themes/classic-22/header.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-Hans" data-theme="<?php $this->options->themeStyle(); ?>">
|
||||
|
||||
<head>
|
||||
<meta charset="<?php $this->options->charset(); ?>">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?php $this->archiveTitle('', '', ' - '); ?><?php $this->options->title(); ?></title>
|
||||
|
||||
<link rel="stylesheet" href="<?php $this->options->themeUrl('static/css/style.css'); ?>">
|
||||
|
||||
<?php $this->header(); ?>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<header class="site-navbar container-fluid">
|
||||
<div class="container-inner">
|
||||
<nav>
|
||||
<ul class="site-name">
|
||||
<?php if ($this->options->logoUrl): ?>
|
||||
<li><a href="<?php $this->options->siteUrl(); ?>" class="brand"><img src="<?php $this->options->logoUrl() ?>" alt="<?php $this->options->title() ?>"></a></li>
|
||||
<?php else: ?>
|
||||
<li>
|
||||
<a href="<?php $this->options->siteUrl(); ?>" class="brand"><?php $this->options->title() ?></a>
|
||||
</li>
|
||||
<li class="desc"><?php $this->options->description() ?></li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<label for="nav-toggler" class="nav-toggler-btn"><img src="<?php $this->options->themeUrl('static/img/menu.svg'); ?>" alt="Menu"></label>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<nav class="site-nav">
|
||||
<input type="checkbox" id="nav-toggler">
|
||||
|
||||
<ul class="nav-menu">
|
||||
<li>
|
||||
<a href="<?php $this->options->siteUrl(); ?>"<?php if ($this->is('index')): ?> class="active"<?php endif; ?>><?php _e('首页'); ?></a>
|
||||
</li>
|
||||
|
||||
<?php \Widget\Contents\Page\Rows::alloc()->to($pages); ?>
|
||||
<?php while ($pages->next()): ?>
|
||||
<li>
|
||||
<a href="<?php $pages->permalink(); ?>"<?php if ($this->is('page', $pages->slug)): ?> class="active"<?php endif; ?>><?php $pages->title(); ?></a>
|
||||
</li>
|
||||
<?php endwhile; ?>
|
||||
<li>
|
||||
<form method="post" action="<?php $this->options->siteUrl(); ?>">
|
||||
<input type="search" id="s" name="s">
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
48
usr/themes/classic-22/index.php
Normal file
48
usr/themes/classic-22/index.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
/**
|
||||
* Just another official theme
|
||||
*
|
||||
* @package Classic 22
|
||||
* @author Typecho Team
|
||||
* @version 1.0
|
||||
* @link http://typecho.org
|
||||
*/
|
||||
|
||||
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
|
||||
$this->need('header.php');
|
||||
?>
|
||||
|
||||
<main class="container">
|
||||
<div class="container-thin">
|
||||
<?php if (!($this->is('index')) and !($this->is('post'))): ?>
|
||||
<h4 class="text-center text-muted">
|
||||
<?php $this->archiveTitle([
|
||||
'category' => _t('分类 %s 下的文章'),
|
||||
'search' => _t('包含关键字 %s 的文章'),
|
||||
'tag' => _t('标签 %s 下的文章'),
|
||||
'author' => _t('%s 发布的文章')
|
||||
], '', ''); ?>
|
||||
</h4>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php while ($this->next()): ?>
|
||||
<article class="post" itemscope itemtype="http://schema.org/BlogPosting">
|
||||
<?php postMeta($this); ?>
|
||||
|
||||
<div class="entry-content fmt" itemprop="articleBody">
|
||||
<?php $this->content(_t('阅读全文')); ?>
|
||||
</div>
|
||||
</article>
|
||||
<hr class="post-separator">
|
||||
<?php endwhile; ?>
|
||||
</div>
|
||||
|
||||
<!-- <div class="text-center">
|
||||
<a href="#">« Older Posts</a>
|
||||
<span class="mx-2 text-muted">·</span>
|
||||
<a href="#">Newer Posts »</a>
|
||||
</div> -->
|
||||
<?php $this->pageNav('← ' . _t('前一页'), _t('后一页') . ' →'); ?>
|
||||
</main>
|
||||
|
||||
<?php $this->need('footer.php'); ?>
|
20
usr/themes/classic-22/page.php
Normal file
20
usr/themes/classic-22/page.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
|
||||
<?php $this->need('header.php'); ?>
|
||||
|
||||
<main class="container">
|
||||
<div class="container-thin">
|
||||
<article class="post" itemscope itemtype="http://schema.org/BlogPosting">
|
||||
<?php postMeta($this, 'page'); ?>
|
||||
|
||||
<div class="entry-content fmt" itemprop="articleBody">
|
||||
<?php $this->content(); ?>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<hr class="post-separator">
|
||||
|
||||
<?php $this->need('comments.php'); ?>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php $this->need('footer.php'); ?>
|
28
usr/themes/classic-22/post.php
Normal file
28
usr/themes/classic-22/post.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
|
||||
<?php $this->need('header.php'); ?>
|
||||
|
||||
<main class="container">
|
||||
<div class="container-thin">
|
||||
<article class="post" itemscope itemtype="http://schema.org/BlogPosting">
|
||||
<?php postMeta($this, 'post'); ?>
|
||||
|
||||
<div class="entry-content fmt" itemprop="articleBody">
|
||||
<?php $this->content(); ?>
|
||||
<p itemprop="keywords"><?php _e('标签'); ?>:<?php $this->tags(', ', true, _t('无')); ?></p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<div class="grid post-next">
|
||||
<div>
|
||||
← <?php $this->thePrev('%s', _t('没有了')); ?>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<?php $this->theNext('%s', _t('没有了')); ?> →
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php $this->need('comments.php'); ?>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php $this->need('footer.php'); ?>
|
BIN
usr/themes/classic-22/screenshot.png
Normal file
BIN
usr/themes/classic-22/screenshot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.4 KiB |
45
usr/themes/classic-22/search.php
Normal file
45
usr/themes/classic-22/search.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
|
||||
<?php $this->need('header.php'); ?>
|
||||
|
||||
<main class="container">
|
||||
<div class="container-thin">
|
||||
|
||||
<h1 class="text-center"><?php _e('搜索'); ?></h1>
|
||||
|
||||
<form method="post" action="<?php $this->options->siteUrl(); ?>">
|
||||
<input type="search" id="s" name="s" placeholder="<?php _e('搜索关键字'); ?>" value="<?php $this->archiveTitle('','',''); ?>">
|
||||
</form>
|
||||
|
||||
<div class="text-center">
|
||||
<?php \Widget\Metas\Category\Rows::alloc()->listCategories('wrapClass=list-inline'); ?>
|
||||
</div>
|
||||
|
||||
<?php while ($this->next()): ?>
|
||||
<hr class="post-separator">
|
||||
<article class="post" itemscope itemtype="http://schema.org/BlogPosting">
|
||||
<header class="entry-header text-center">
|
||||
<h1 class="entry-title" itemprop="name headline"><a href="<?php $this->permalink() ?>" itemprop="url"><?php $this->title() ?></a></h1>
|
||||
<ul class="entry-meta list-inline text-muted">
|
||||
<li><i data-feather="calendar" class="is-sm me-2"></i><time datetime="<?php $this->date('c'); ?>" itemprop="datePublished"><?php $this->date(); ?></time></li>
|
||||
<li><i data-feather="folder" class="is-sm me-2"></i><?php $this->category(', '); ?></li>
|
||||
<li><i data-feather="message-circle" class="is-sm me-2"></i><a href="<?php $this->permalink() ?>#comments" itemprop="discussionUrl"><?php $this->commentsNum('暂无评论', '1 条评论', '%d 条评论'); ?></a></li>
|
||||
</ul>
|
||||
</header>
|
||||
|
||||
<div class="entry-content fmt" itemprop="articleBody">
|
||||
<?php $this->content('阅读剩余部分'); ?>
|
||||
</div>
|
||||
</article>
|
||||
<hr class="post-separator">
|
||||
<?php endwhile; ?>
|
||||
</div>
|
||||
|
||||
<!-- <div class="text-center">
|
||||
<a href="#">« Older Posts</a>
|
||||
<span class="mx-2 text-muted">·</span>
|
||||
<a href="#">Newer Posts »</a>
|
||||
</div> -->
|
||||
<?php $this->pageNav('« 前一页', '后一页 »'); ?>
|
||||
</main>
|
||||
|
||||
<?php $this->need('footer.php'); ?>
|
5
usr/themes/classic-22/static/css/style.css
Normal file
5
usr/themes/classic-22/static/css/style.css
Normal file
File diff suppressed because one or more lines are too long
16
usr/themes/classic-22/static/img/calendar.svg
Normal file
16
usr/themes/classic-22/static/img/calendar.svg
Normal file
@ -0,0 +1,16 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="#64748b"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<rect x="3" y="4" width="18" height="18" rx="2" ry="2" />
|
||||
<line x1="16" y1="2" x2="16" y2="6" />
|
||||
<line x1="8" y1="2" x2="8" y2="6" />
|
||||
<line x1="3" y1="10" x2="21" y2="10" />
|
||||
</svg>
|
After Width: | Height: | Size: 385 B |
13
usr/themes/classic-22/static/img/folder.svg
Normal file
13
usr/themes/classic-22/static/img/folder.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="#64748b"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z" />
|
||||
</svg>
|
After Width: | Height: | Size: 294 B |
15
usr/themes/classic-22/static/img/menu.svg
Normal file
15
usr/themes/classic-22/static/img/menu.svg
Normal file
@ -0,0 +1,15 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="#fff"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<line x1="3" y1="12" x2="21" y2="12" />
|
||||
<line x1="3" y1="6" x2="21" y2="6" />
|
||||
<line x1="3" y1="18" x2="21" y2="18" />
|
||||
</svg>
|
After Width: | Height: | Size: 324 B |
13
usr/themes/classic-22/static/img/message-circle.svg
Normal file
13
usr/themes/classic-22/static/img/message-circle.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="#64748b"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" />
|
||||
</svg>
|
After Width: | Height: | Size: 403 B |
14
usr/themes/classic-22/static/img/search.svg
Normal file
14
usr/themes/classic-22/static/img/search.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="#fff"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<circle cx="11" cy="11" r="8" />
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65" />
|
||||
</svg>
|
After Width: | Height: | Size: 284 B |
105
usr/themes/classic-22/static/scss/_pico.scss
Normal file
105
usr/themes/classic-22/static/scss/_pico.scss
Normal file
@ -0,0 +1,105 @@
|
||||
/*!
|
||||
* Pico.css (https://picocss.com)
|
||||
* Licensed under MIT
|
||||
*/
|
||||
|
||||
// Config
|
||||
// $enable-responsive-typography: false;
|
||||
|
||||
// Grey
|
||||
$grey-50: #f8fafc;
|
||||
$grey-100: #f1f5f9;
|
||||
$grey-200: #e2e8f0;
|
||||
$grey-300: #cbd5e1;
|
||||
$grey-400: #94a3b8;
|
||||
$grey-500: #64748b;
|
||||
$grey-600: #475569;
|
||||
$grey-700: #334155;
|
||||
$grey-800: #1e293b;
|
||||
$grey-900: #0f172a;
|
||||
|
||||
// Blue
|
||||
$primary-50: #E9F2FC;
|
||||
$primary-100: #D1E5FB;
|
||||
$primary-200: #9BCCFD;
|
||||
$primary-300: #51B4FF;
|
||||
$primary-400: #029AE8;
|
||||
$primary-500: #017FC0;
|
||||
$primary-600: #02659A;
|
||||
$primary-700: #014C75;
|
||||
$primary-800: #033452;
|
||||
$primary-900: #061E2F;
|
||||
|
||||
// Amber
|
||||
$amber-50: #fffbeb;
|
||||
$amber-100: #fef3c7;
|
||||
$amber-200: #fde68a;
|
||||
$amber-300: #fcd34d;
|
||||
$amber-400: #fbbf24;
|
||||
$amber-500: #f59e0b;
|
||||
$amber-600: #d97706;
|
||||
$amber-700: #b45309;
|
||||
$amber-800: #92400e;
|
||||
$amber-900: #78350f;
|
||||
|
||||
// Green
|
||||
$green-50: #f0fdf4;
|
||||
$green-100: #dcfce7;
|
||||
$green-200: #bbf7d0;
|
||||
$green-300: #86efac;
|
||||
$green-400: #4ade80;
|
||||
$green-500: #22c55e;
|
||||
$green-600: #16a34a;
|
||||
$green-700: #15803d;
|
||||
$green-800: #166534;
|
||||
$green-900: #14532d;
|
||||
|
||||
// Red
|
||||
$red-50: #fef2f2;
|
||||
$red-100: #fee2e2;
|
||||
$red-200: #fecaca;
|
||||
$red-300: #fca5a5;
|
||||
$red-400: #f87171;
|
||||
$red-500: #ef4444;
|
||||
$red-600: #dc2626;
|
||||
$red-700: #b91c1c;
|
||||
$red-800: #991b1b;
|
||||
$red-900: #7f1d1d;
|
||||
|
||||
@import "../../../../../tools/node_modules/@picocss/pico/scss/variables";
|
||||
|
||||
// Theming
|
||||
@import "../../../../../tools/node_modules/@picocss/pico/scss/themes/default";
|
||||
|
||||
// Layout
|
||||
@import "../../../../../tools/node_modules/@picocss/pico/scss/layout/document"; // html
|
||||
@import "../../../../../tools/node_modules/@picocss/pico/scss/layout/sectioning"; // body, header, main, footer
|
||||
@import "../../../../../tools/node_modules/@picocss/pico/scss/layout/container"; // .container, .container-fluid
|
||||
@import "../../../../../tools/node_modules/@picocss/pico/scss/layout/section"; // section
|
||||
@import "../../../../../tools/node_modules/@picocss/pico/scss/layout/grid"; // .grid
|
||||
// @import "../../../../../tools/node_modules/@picocss/pico/scss/layout/scroller"; // figure
|
||||
|
||||
// Content
|
||||
@import "../../../../../tools/node_modules/@picocss/pico/scss/content/typography"; // a, headings, p, ul, blockquote, ...
|
||||
@import "../../../../../tools/node_modules/@picocss/pico/scss/content/embedded"; // audio, canvas, iframe, img, svg, video
|
||||
@import "../../../../../tools/node_modules/@picocss/pico/scss/content/button"; // button, a[role=button], type=button, type=submit ...
|
||||
@import "../../../../../tools/node_modules/@picocss/pico/scss/content/form"; // input, select, textarea, label, fieldset, legend
|
||||
// @import "../../../../../tools/node_modules/@picocss/pico/scss/content/form-checkbox-radio"; // type=checkbox, type=radio, role=switch
|
||||
@import "../../../../../tools/node_modules/@picocss/pico/scss/content/form-alt-input-types"; // type=color, type=date, type=file, type=search, ...
|
||||
@import "../../../../../tools/node_modules/@picocss/pico/scss/content/table"; // table, tr, td, ...
|
||||
@import "../../../../../tools/node_modules/@picocss/pico/scss/content/code"; // pre, code, ...
|
||||
@import "../../../../../tools/node_modules/@picocss/pico/scss/content/miscs"; // hr, template, [hidden], dialog, canvas
|
||||
|
||||
// Components
|
||||
// @import "../../../../../tools/node_modules/@picocss/pico/scss/components/accordion"; // details, summary
|
||||
// @import "../../../../../tools/node_modules/@picocss/pico/scss/components/card"; // article
|
||||
// @import "../../../../../tools/node_modules/@picocss/pico/scss/components/modal"; // dialog
|
||||
@import "../../../../../tools/node_modules/@picocss/pico/scss/components/nav"; // nav
|
||||
// @import "../../../../../tools/node_modules/@picocss/pico/scss/components/progress"; // progress
|
||||
// @import "../../../../../tools/node_modules/@picocss/pico/scss/components/dropdown"; // dropdown
|
||||
|
||||
// Utilities
|
||||
// @import "../../../../../tools/node_modules/@picocss/pico/scss/utilities/loading"; // aria-busy=true
|
||||
// @import "../../../../../tools/node_modules/@picocss/pico/scss/utilities/tooltip"; // data-tooltip
|
||||
@import "../../../../../tools/node_modules/@picocss/pico/scss/utilities/accessibility"; // -ms-touch-action, aria-*
|
||||
@import "../../../../../tools/node_modules/@picocss/pico/scss/utilities/reduce-motion"; // prefers-reduced-motion
|
406
usr/themes/classic-22/static/scss/style.scss
Normal file
406
usr/themes/classic-22/static/scss/style.scss
Normal file
@ -0,0 +1,406 @@
|
||||
@import "pico";
|
||||
|
||||
// Global Set
|
||||
:root {
|
||||
--border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
body {
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
// Theme
|
||||
[data-theme="light"],
|
||||
:root:not([data-theme="dark"]) {
|
||||
--primary: #{$primary-500};
|
||||
--primary-hover: #{$primary-600};
|
||||
--muted-border-color: #{$grey-200};
|
||||
}
|
||||
|
||||
[data-theme="dark"] {
|
||||
--primary: #{$primary-500};
|
||||
--primary-hover: #{$primary-400};
|
||||
--muted-border-color: #{$grey-800};
|
||||
|
||||
.site-navbar {
|
||||
background-color: #{$primary-600};
|
||||
}
|
||||
}
|
||||
|
||||
// Content
|
||||
h1, h2, h3, h4, h5 { line-height: 1.25; }
|
||||
// h1 { --font-size: 2.5rem; }
|
||||
// h2 { --font-size: 2rem; }
|
||||
// h3 { --font-size: 1.75rem; }
|
||||
// h4 { --font-size: 1.5rem; }
|
||||
// h5 { --font-size: 1.25rem; }
|
||||
|
||||
// Icon Size
|
||||
.is-sm {
|
||||
width: 1.25em;
|
||||
height: 1.25em;
|
||||
}
|
||||
|
||||
// Utilities
|
||||
.text-muted,
|
||||
.text-muted a {
|
||||
color: var(--muted-color);
|
||||
}
|
||||
|
||||
.text-muted a:hover {
|
||||
color: var(--secondary-hover);
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.text-end {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.ms-2 {
|
||||
margin-left: calc(var(--spacing) / 2);
|
||||
}
|
||||
|
||||
.me-2 {
|
||||
margin-right: calc(var(--spacing) / 2);
|
||||
}
|
||||
|
||||
.mx-2 {
|
||||
margin-left: calc(var(--spacing) / 2);
|
||||
margin-right: calc(var(--spacing) / 2);
|
||||
}
|
||||
|
||||
.list-inline {
|
||||
padding-left: 0;
|
||||
list-style: none;
|
||||
margin-bottom: 0;
|
||||
|
||||
li {
|
||||
display: inline-block;
|
||||
margin-bottom: 0;
|
||||
|
||||
&:not(:last-child) { margin-right: var(--spacing); }
|
||||
}
|
||||
|
||||
svg { vertical-align: text-bottom; }
|
||||
}
|
||||
|
||||
// Layout
|
||||
.container-inner {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 84rem;
|
||||
|
||||
@if map-get($breakpoints, "lg") {
|
||||
@media (min-width: map-get($breakpoints, "lg")) {
|
||||
padding-left: calc(var(--spacing) * 1.5);
|
||||
padding-right: calc(var(--spacing) * 1.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.container-thin {
|
||||
margin: 0 auto;
|
||||
max-width: 46rem;
|
||||
}
|
||||
|
||||
.d-flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.align-items-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.justify-content-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.justify-content-end {
|
||||
justify-content: end;
|
||||
}
|
||||
|
||||
.align-self-center {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
// Header & Navbar
|
||||
.site-navbar {
|
||||
padding-top: calc(var(--spacing) / 2);
|
||||
padding-bottom: calc(var(--spacing) / 2);
|
||||
background-color: var(--primary);
|
||||
|
||||
a {
|
||||
color: rgba(255, 255, 255, 1.0);
|
||||
// &:hover { text-decoration: underline; }
|
||||
}
|
||||
|
||||
.site-name {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.brand {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.desc {
|
||||
color: rgba(255, 255, 255, .75);
|
||||
display: none;
|
||||
@if map-get($breakpoints, "sm") {
|
||||
@media (min-width: map-get($breakpoints, "sm")) {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.site-nav {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#nav-toggler {
|
||||
display: none;
|
||||
|
||||
&:checked ~ .nav-menu {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
// Dropdown Menu
|
||||
.nav-menu {
|
||||
display: none;
|
||||
|
||||
li {
|
||||
display: block;
|
||||
padding: calc(var(--spacing) * .5);
|
||||
}
|
||||
|
||||
a {
|
||||
margin: calc(var(--spacing) * -.5);
|
||||
padding: calc(var(--spacing) * .5);
|
||||
}
|
||||
|
||||
form {
|
||||
margin-bottom: 0;
|
||||
|
||||
input[type=search] {
|
||||
height: 50px;
|
||||
background-image: url("../img/search.svg");
|
||||
background-size: auto;
|
||||
color: rgb(255,255,255);
|
||||
|
||||
&:focus {
|
||||
--form-element-focus-color: rgba(255,255,255,0.5);
|
||||
}
|
||||
|
||||
&:not(:focus) {
|
||||
padding: 0;
|
||||
border: none;
|
||||
width: 30px;
|
||||
padding-inline-start: 0;
|
||||
background-position: center center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nav-toggler-btn {
|
||||
margin: calc(var(--spacing) * -1) calc(var(--spacing) * -0.5);
|
||||
padding: var(--spacing) calc(var(--spacing) * 0.5);
|
||||
color: rgba(255, 255, 255, 1.0);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@if map-get($breakpoints, "lg") {
|
||||
@media (min-width: map-get($breakpoints, "lg")) {
|
||||
.site-navbar .container-inner,
|
||||
.site-nav { display: flex; }
|
||||
.site-navbar .container-inner nav:first-child { flex-grow: 1; }
|
||||
.nav-toggler-btn { display: none; }
|
||||
.nav-menu {
|
||||
display: flex !important;
|
||||
li:not(:last-child) { margin-right: calc(var(--spacing) / 2); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Posts
|
||||
.post-separator {
|
||||
margin: var(--block-spacing-vertical) 0;
|
||||
}
|
||||
|
||||
.entry-header {
|
||||
margin-bottom: calc(var(--spacing) * 2);
|
||||
}
|
||||
|
||||
.entry-title {
|
||||
margin-bottom: var(--spacing);
|
||||
|
||||
a { color: var(--h1-color); }
|
||||
}
|
||||
|
||||
.entry-meta {
|
||||
font-size: .875rem;
|
||||
}
|
||||
|
||||
.feather-calendar::before,
|
||||
.feather-folder::before,
|
||||
.feather-message::before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
margin-right: .25rem;
|
||||
background: url("../img/calendar.svg") no-repeat center center / contain;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
vertical-align: text-top;
|
||||
fill: #396;
|
||||
}
|
||||
.feather-folder::before {
|
||||
background-image: url("../img/folder.svg");
|
||||
}
|
||||
.feather-message::before {
|
||||
background-image: url("../img/message-circle.svg");
|
||||
}
|
||||
|
||||
|
||||
|
||||
.entry-content .more {
|
||||
text-align: center;
|
||||
a {
|
||||
display: inline-block;
|
||||
font-size: .875rem;
|
||||
padding: 6px 16px;
|
||||
border: 1px solid var(--muted-border-color);
|
||||
color: var(--muted-color);
|
||||
border-radius: 100px;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.post-next {
|
||||
border-top: 1px solid var(--muted-border-color);
|
||||
padding: calc(var(--spacing) * 1.5) 0;
|
||||
margin: var(--block-spacing-vertical) 0;
|
||||
|
||||
a {
|
||||
color: var(--h5-color);
|
||||
}
|
||||
}
|
||||
|
||||
// Format
|
||||
.fmt {
|
||||
line-height: 1.6;
|
||||
|
||||
pre, hr {
|
||||
margin-bottom: var(--typography-spacing-vertical);
|
||||
}
|
||||
}
|
||||
|
||||
// Footer
|
||||
.site-footer {
|
||||
padding-bottom: calc(var(--block-spacing-vertical) / 2);
|
||||
}
|
||||
|
||||
// Comments
|
||||
.comment-list {
|
||||
list-style: none;
|
||||
padding-left: calc(var(--spacing) * 4);
|
||||
}
|
||||
|
||||
.comment-body {
|
||||
margin: calc(var(--spacing) * 1.5) 0;
|
||||
}
|
||||
|
||||
.comment-by-author > .comment-author::after {
|
||||
content: "OP";
|
||||
margin-left: .25rem;
|
||||
color: var(--muted-color);
|
||||
padding: 0 .375rem;
|
||||
border: 1px solid var(--muted-color);
|
||||
font-size: .75rem;
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
.comment-author {
|
||||
position: relative;
|
||||
|
||||
.avatar {
|
||||
position: absolute;
|
||||
width: calc(var(--spacing) * 3);
|
||||
left: calc(var(--spacing) * -4);
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
cite {
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--h5-color);
|
||||
}
|
||||
}
|
||||
|
||||
.comment-meta a,
|
||||
.comment-reply a {
|
||||
font-size: .875em;
|
||||
color: var(--muted-color);
|
||||
|
||||
&:hover { color: var(--secondary-hover); }
|
||||
}
|
||||
|
||||
.comment-meta {
|
||||
margin-bottom: calc(var(--spacing) / 2);
|
||||
}
|
||||
|
||||
.comment-reply:blank {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.comment-awaiting-moderation {
|
||||
margin-left: calc(var(--spacing) / 2);
|
||||
font-size: .875em;
|
||||
color: var(--del-color);
|
||||
}
|
||||
|
||||
.comment-children {
|
||||
margin: calc(var(--spacing) * 1.5) 0;
|
||||
}
|
||||
|
||||
#response {
|
||||
margin-bottom: var(--spacing);
|
||||
}
|
||||
|
||||
#cancel-comment-reply-link {
|
||||
font-size: .875em;
|
||||
color: var(--del-color);
|
||||
}
|
||||
|
||||
.comment-body .respond {
|
||||
margin-top: var(--spacing);
|
||||
}
|
||||
|
||||
#comment-form textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
// page nav
|
||||
.page-navigator {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
|
||||
li {
|
||||
display: inline;
|
||||
margin-left: calc(var(--spacing) / 2);
|
||||
margin-right: calc(var(--spacing) / 2);
|
||||
|
||||
&.current a {
|
||||
font-weight: 700;
|
||||
color: var(--h5-color);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
<?php if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
|
||||
<?php $this->need('header.php'); ?>
|
||||
|
||||
<div class="col-mb-12 col-8" id="main" role="main">
|
||||
<h3 class="archive-title"><?php $this->archiveTitle([
|
||||
'category' => _t('分类 %s 下的文章'),
|
||||
'search' => _t('包含关键字 %s 的文章'),
|
||||
'tag' => _t('标签 %s 下的文章'),
|
||||
'author' => _t('%s 发布的文章')
|
||||
], '', ''); ?></h3>
|
||||
<?php if ($this->have()): ?>
|
||||
<?php while ($this->next()): ?>
|
||||
<article class="post" itemscope itemtype="http://schema.org/BlogPosting">
|
||||
<h2 class="post-title" itemprop="name headline"><a itemprop="url"
|
||||
href="<?php $this->permalink() ?>"><?php $this->title() ?></a>
|
||||
</h2>
|
||||
<ul class="post-meta">
|
||||
<li itemprop="author" itemscope itemtype="http://schema.org/Person"><?php _e('作者: '); ?><a
|
||||
itemprop="name" href="<?php $this->author->permalink(); ?>"
|
||||
rel="author"><?php $this->author(); ?></a></li>
|
||||
<li><?php _e('时间: '); ?>
|
||||
<time datetime="<?php $this->date('c'); ?>"
|
||||
itemprop="datePublished"><?php $this->date(); ?></time>
|
||||
</li>
|
||||
<li><?php _e('分类: '); ?><?php $this->category(','); ?></li>
|
||||
<li itemprop="interactionCount"><a
|
||||
href="<?php $this->permalink() ?>#comments"><?php $this->commentsNum('评论', '1 条评论', '%d 条评论'); ?></a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="post-content" itemprop="articleBody">
|
||||
<?php $this->content('- 阅读剩余部分 -'); ?>
|
||||
</div>
|
||||
</article>
|
||||
<?php endwhile; ?>
|
||||
<?php else: ?>
|
||||
<article class="post">
|
||||
<h2 class="post-title"><?php _e('没有找到内容'); ?></h2>
|
||||
</article>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php $this->pageNav('« 前一页', '后一页 »'); ?>
|
||||
</div><!-- end #main -->
|
||||
|
||||
<?php $this->need('sidebar.php'); ?>
|
||||
<?php $this->need('footer.php'); ?>
|
@ -6,7 +6,7 @@
|
||||
|
||||
<?php $comments->listComments(); ?>
|
||||
|
||||
<?php $comments->pageNav('« 前一页', '后一页 »'); ?>
|
||||
<?php $comments->pageNav(); ?>
|
||||
|
||||
<?php endif; ?>
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
<h3 id="response"><?php _e('添加新评论'); ?></h3>
|
||||
<form method="post" action="<?php $this->commentUrl() ?>" id="comment-form" role="form">
|
||||
<?php if ($this->user->hasLogin()): ?>
|
||||
<p><?php _e('登录身份: '); ?><a
|
||||
<p><?php _e('登录身份'); ?>: <a
|
||||
href="<?php $this->options->profileUrl(); ?>"><?php $this->user->screenName(); ?></a>. <a
|
||||
href="<?php $this->options->logoutUrl(); ?>" title="Logout"><?php _e('退出'); ?> »</a>
|
||||
</p>
|
||||
@ -37,9 +37,9 @@
|
||||
</p>
|
||||
<p>
|
||||
<label
|
||||
for="url"<?php if ($this->options->commentsRequireURL): ?> class="required"<?php endif; ?>><?php _e('网站'); ?></label>
|
||||
for="url"<?php if ($this->options->commentsRequireUrl): ?> class="required"<?php endif; ?>><?php _e('网站'); ?></label>
|
||||
<input type="url" name="url" id="url" class="text" placeholder="<?php _e('http://'); ?>"
|
||||
value="<?php $this->remember('url'); ?>"<?php if ($this->options->commentsRequireURL): ?> required<?php endif; ?> />
|
||||
value="<?php $this->remember('url'); ?>"<?php if ($this->options->commentsRequireUrl): ?> required<?php endif; ?> />
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
<p>
|
||||
|
@ -11,7 +11,7 @@ function themeConfig($form)
|
||||
_t('在这里填入一个图片 URL 地址, 以在网站标题前加上一个 LOGO')
|
||||
);
|
||||
|
||||
$form->addInput($logoUrl);
|
||||
$form->addInput($logoUrl->addRule('url', _t('请填写一个合法的URL地址')));
|
||||
|
||||
$sidebarBlock = new \Typecho\Widget\Helper\Form\Element\Checkbox(
|
||||
'sidebarBlock',
|
||||
@ -29,6 +29,39 @@ function themeConfig($form)
|
||||
$form->addInput($sidebarBlock->multiMode());
|
||||
}
|
||||
|
||||
function postMeta(
|
||||
\Widget\Archive $archive,
|
||||
string $metaType = 'archive'
|
||||
)
|
||||
{
|
||||
$titleTag = $metaType == 'archive' ? 'h2' : 'h1';
|
||||
?>
|
||||
<<?php echo $titleTag ?> class="post-title" itemprop="name headline">
|
||||
<a itemprop="url"
|
||||
href="<?php $archive->permalink() ?>"><?php $archive->title() ?></a>
|
||||
</<?php echo $titleTag ?>>
|
||||
<?php if ($metaType != 'page'): ?>
|
||||
<ul class="post-meta">
|
||||
<li itemprop="author" itemscope itemtype="http://schema.org/Person">
|
||||
<?php _e('作者'); ?>: <a itemprop="name"
|
||||
href="<?php $archive->author->permalink(); ?>"
|
||||
rel="author"><?php $archive->author(); ?></a>
|
||||
</li>
|
||||
<li><?php _e('时间'); ?>:
|
||||
<time datetime="<?php $archive->date('c'); ?>" itemprop="datePublished"><?php $archive->date(); ?></time>
|
||||
</li>
|
||||
<li><?php _e('分类'); ?>: <?php $archive->category(','); ?></li>
|
||||
<?php if ($metaType == 'archive'): ?>
|
||||
<li itemprop="interactionCount">
|
||||
<a itemprop="discussionUrl"
|
||||
href="<?php $archive->permalink() ?>#comments"><?php $archive->commentsNum('评论', '1 条评论', '%d 条评论'); ?></a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
<?php endif; ?>
|
||||
<?php
|
||||
}
|
||||
|
||||
/*
|
||||
function themeFields($layout)
|
||||
{
|
||||
|
@ -13,32 +13,30 @@ $this->need('header.php');
|
||||
?>
|
||||
|
||||
<div class="col-mb-12 col-8" id="main" role="main">
|
||||
<?php if (!($this->is('index')) and !($this->is('post'))): ?>
|
||||
<h3 class="archive-title"><?php $this->archiveTitle([
|
||||
'category' => _t('分类 %s 下的文章'),
|
||||
'search' => _t('包含关键字 %s 的文章'),
|
||||
'tag' => _t('标签 %s 下的文章'),
|
||||
'author' => _t('%s 发布的文章')
|
||||
], '', ''); ?></h3>
|
||||
<?php endif; ?>
|
||||
<?php if ($this->have()): ?>
|
||||
<?php while ($this->next()): ?>
|
||||
<article class="post" itemscope itemtype="http://schema.org/BlogPosting">
|
||||
<h2 class="post-title" itemprop="name headline">
|
||||
<a itemprop="url"
|
||||
href="<?php $this->permalink() ?>"><?php $this->title() ?></a>
|
||||
</h2>
|
||||
<ul class="post-meta">
|
||||
<li itemprop="author" itemscope itemtype="http://schema.org/Person"><?php _e('作者: '); ?><a
|
||||
itemprop="name" href="<?php $this->author->permalink(); ?>"
|
||||
rel="author"><?php $this->author(); ?></a></li>
|
||||
<li><?php _e('时间: '); ?>
|
||||
<time datetime="<?php $this->date('c'); ?>" itemprop="datePublished"><?php $this->date(); ?></time>
|
||||
</li>
|
||||
<li><?php _e('分类: '); ?><?php $this->category(','); ?></li>
|
||||
<li itemprop="interactionCount">
|
||||
<a itemprop="discussionUrl"
|
||||
href="<?php $this->permalink() ?>#comments"><?php $this->commentsNum('评论', '1 条评论', '%d 条评论'); ?></a>
|
||||
</li>
|
||||
</ul>
|
||||
<?php postMeta($this); ?>
|
||||
<div class="post-content" itemprop="articleBody">
|
||||
<?php $this->content('- 阅读剩余部分 -'); ?>
|
||||
<?php $this->content(_t('阅读剩余部分')); ?>
|
||||
</div>
|
||||
</article>
|
||||
<?php endwhile; ?>
|
||||
<?php else: ?>
|
||||
<article class="post">
|
||||
<h2 class="post-title"><?php _e('没有找到内容'); ?></h2>
|
||||
</article>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php $this->pageNav('« 前一页', '后一页 »'); ?>
|
||||
<?php $this->pageNav('« ' . _t('前一页'), _t('后一页') . ' »'); ?>
|
||||
</div><!-- end #main-->
|
||||
|
||||
<?php $this->need('sidebar.php'); ?>
|
||||
|
@ -3,10 +3,7 @@
|
||||
|
||||
<div class="col-mb-12 col-8" id="main" role="main">
|
||||
<article class="post" itemscope itemtype="http://schema.org/BlogPosting">
|
||||
<h1 class="post-title" itemprop="name headline">
|
||||
<a itemprop="url"
|
||||
href="<?php $this->permalink() ?>"><?php $this->title() ?></a>
|
||||
</h1>
|
||||
<?php postMeta($this, 'page'); ?>
|
||||
<div class="post-content" itemprop="articleBody">
|
||||
<?php $this->content(); ?>
|
||||
</div>
|
||||
|
@ -3,32 +3,18 @@
|
||||
|
||||
<div class="col-mb-12 col-8" id="main" role="main">
|
||||
<article class="post" itemscope itemtype="http://schema.org/BlogPosting">
|
||||
<h1 class="post-title" itemprop="name headline">
|
||||
<a itemprop="url"
|
||||
href="<?php $this->permalink() ?>"><?php $this->title() ?></a>
|
||||
</h1>
|
||||
<ul class="post-meta">
|
||||
<li itemprop="author" itemscope itemtype="http://schema.org/Person">
|
||||
<?php _e('作者: '); ?><a itemprop="name"
|
||||
href="<?php $this->author->permalink(); ?>"
|
||||
rel="author"><?php $this->author(); ?></a>
|
||||
</li>
|
||||
<li><?php _e('时间: '); ?>
|
||||
<time datetime="<?php $this->date('c'); ?>" itemprop="datePublished"><?php $this->date(); ?></time>
|
||||
</li>
|
||||
<li><?php _e('分类: '); ?><?php $this->category(','); ?></li>
|
||||
</ul>
|
||||
<?php postMeta($this, 'post'); ?>
|
||||
<div class="post-content" itemprop="articleBody">
|
||||
<?php $this->content(); ?>
|
||||
</div>
|
||||
<p itemprop="keywords" class="tags"><?php _e('标签: '); ?><?php $this->tags(', ', true, 'none'); ?></p>
|
||||
<p itemprop="keywords" class="tags"><?php _e('标签'); ?>: <?php $this->tags(', ', true, 'none'); ?></p>
|
||||
</article>
|
||||
|
||||
<?php $this->need('comments.php'); ?>
|
||||
|
||||
<ul class="post-near">
|
||||
<li>上一篇: <?php $this->thePrev('%s', '没有了'); ?></li>
|
||||
<li>下一篇: <?php $this->theNext('%s', '没有了'); ?></li>
|
||||
<li>上一篇: <?php $this->thePrev('%s', _t('没有了')); ?></li>
|
||||
<li>下一篇: <?php $this->theNext('%s', _t('没有了')); ?></li>
|
||||
</ul>
|
||||
</div><!-- end #main-->
|
||||
|
||||
|
@ -14,7 +14,7 @@ class Base64
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $data;
|
||||
private string $data;
|
||||
|
||||
/**
|
||||
* 初始化数据
|
||||
|
@ -20,33 +20,33 @@ class Client
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $url;
|
||||
private string $url;
|
||||
|
||||
/**
|
||||
* 消息体
|
||||
*
|
||||
* @var Message
|
||||
*/
|
||||
private $message;
|
||||
private Message $message;
|
||||
|
||||
/**
|
||||
* 调试开关
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private $debug = false;
|
||||
private bool $debug = false;
|
||||
|
||||
/**
|
||||
* 请求前缀
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
private $prefix;
|
||||
private ?string $prefix;
|
||||
|
||||
/**
|
||||
* @var Error
|
||||
*/
|
||||
private $error;
|
||||
private Error $error;
|
||||
|
||||
/**
|
||||
* 客户端构造函数
|
||||
|
@ -9,17 +9,17 @@ namespace IXR;
|
||||
*/
|
||||
class Date
|
||||
{
|
||||
private $year;
|
||||
private int $year;
|
||||
|
||||
private $month;
|
||||
private int $month;
|
||||
|
||||
private $day;
|
||||
private int $day;
|
||||
|
||||
private $hour;
|
||||
private int $hour;
|
||||
|
||||
private $minute;
|
||||
private int $minute;
|
||||
|
||||
private $second;
|
||||
private int $second;
|
||||
|
||||
/**
|
||||
* @param int|string $time
|
||||
@ -39,12 +39,12 @@ class Date
|
||||
*/
|
||||
private function parseTimestamp(int $timestamp)
|
||||
{
|
||||
$this->year = date('Y', $timestamp);
|
||||
$this->month = date('m', $timestamp);
|
||||
$this->day = date('d', $timestamp);
|
||||
$this->hour = date('H', $timestamp);
|
||||
$this->minute = date('i', $timestamp);
|
||||
$this->second = date('s', $timestamp);
|
||||
$this->year = intval(date('Y', $timestamp));
|
||||
$this->month = intval(date('m', $timestamp));
|
||||
$this->day = intval(date('d', $timestamp));
|
||||
$this->hour = intval(date('H', $timestamp));
|
||||
$this->minute = intval(date('i', $timestamp));
|
||||
$this->second = intval(date('s', $timestamp));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -15,7 +15,7 @@ class Error
|
||||
* @access public
|
||||
* @var integer
|
||||
*/
|
||||
public $code;
|
||||
public int $code;
|
||||
|
||||
/**
|
||||
* 错误消息
|
||||
@ -23,7 +23,7 @@ class Error
|
||||
* @access public
|
||||
* @var string|null
|
||||
*/
|
||||
public $message;
|
||||
public ?string $message;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
|
@ -12,35 +12,35 @@ class Message
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $message;
|
||||
public string $message;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $messageType; // methodCall / methodResponse / fault
|
||||
public string $messageType; // methodCall / methodResponse / fault
|
||||
|
||||
public $faultCode;
|
||||
public int $faultCode;
|
||||
|
||||
public $faultString;
|
||||
public string $faultString;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $methodName;
|
||||
public string $methodName;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $params = [];
|
||||
public array $params = [];
|
||||
|
||||
// Current variable stacks
|
||||
private $arrayStructs = []; // The stack used to keep track of the current array/struct
|
||||
private array $arrayStructs = []; // The stack used to keep track of the current array/struct
|
||||
|
||||
private $arrayStructsTypes = []; // Stack keeping track of if things are structs or array
|
||||
private array $arrayStructsTypes = []; // Stack keeping track of if things are structs or array
|
||||
|
||||
private $currentStructName = []; // A stack as well
|
||||
private array $currentStructName = []; // A stack as well
|
||||
|
||||
private $currentTagContents;
|
||||
private string $currentTagContents;
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
@ -76,7 +76,7 @@ class Message
|
||||
xml_parser_free($parser);
|
||||
// Grab the error messages, if any
|
||||
if ($this->messageType == 'fault') {
|
||||
$this->faultCode = $this->params[0]['faultCode'];
|
||||
$this->faultCode = intval($this->params[0]['faultCode']);
|
||||
$this->faultString = $this->params[0]['faultString'];
|
||||
}
|
||||
return true;
|
||||
@ -84,7 +84,7 @@ class Message
|
||||
|
||||
/**
|
||||
* @param $parser
|
||||
* @param $tag
|
||||
* @param string $tag
|
||||
* @param $attr
|
||||
*/
|
||||
private function tagOpen($parser, string $tag, $attr)
|
||||
@ -133,7 +133,7 @@ class Message
|
||||
$this->currentTagContents = '';
|
||||
break;
|
||||
case 'string':
|
||||
$value = (string)trim($this->currentTagContents);
|
||||
$value = trim($this->currentTagContents);
|
||||
$this->currentTagContents = '';
|
||||
break;
|
||||
case 'dateTime.iso8601':
|
||||
@ -144,7 +144,7 @@ class Message
|
||||
case 'value':
|
||||
// "If no type is indicated, the type is string."
|
||||
if (trim($this->currentTagContents) != '') {
|
||||
$value = (string) $this->currentTagContents;
|
||||
$value = $this->currentTagContents;
|
||||
$this->currentTagContents = '';
|
||||
}
|
||||
break;
|
||||
|
@ -14,12 +14,12 @@ class Pingback
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $html;
|
||||
private string $html;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $target;
|
||||
private string $target;
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
@ -73,7 +73,7 @@ class Pingback
|
||||
*/
|
||||
public function getTitle(): string
|
||||
{
|
||||
if (preg_match("/\<title\>([^<]*?)\<\/title\\>/is", $this->html, $matchTitle)) {
|
||||
if (preg_match("/<title>([^<]*?)<\/title>/is", $this->html, $matchTitle)) {
|
||||
return Common::subStr(Common::removeXSS(trim(strip_tags($matchTitle[1]))), 0, 150, '...');
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@ class Request
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $xml;
|
||||
private string $xml;
|
||||
|
||||
/**
|
||||
* @param string $method
|
||||
|
@ -16,19 +16,19 @@ class Server
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $callbacks;
|
||||
private array $callbacks;
|
||||
|
||||
/**
|
||||
* 默认参数
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $capabilities;
|
||||
private array $capabilities;
|
||||
|
||||
/**
|
||||
* @var Hook
|
||||
*/
|
||||
private $hook;
|
||||
private Hook $hook;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
@ -248,9 +248,9 @@ class Server
|
||||
*
|
||||
* @access private
|
||||
* @param string $method 方法名
|
||||
* @return mixed
|
||||
* @return bool
|
||||
*/
|
||||
private function hasMethod(string $method)
|
||||
private function hasMethod(string $method): bool
|
||||
{
|
||||
return in_array($method, array_keys($this->callbacks));
|
||||
}
|
||||
|
@ -11,13 +11,13 @@ class Value
|
||||
{
|
||||
private $data;
|
||||
|
||||
private $type;
|
||||
private ?string $type;
|
||||
|
||||
/**
|
||||
* @param mixed $data
|
||||
* @param bool|string $type
|
||||
* @param string|null $type
|
||||
*/
|
||||
public function __construct($data, $type = false)
|
||||
public function __construct($data, ?string $type)
|
||||
{
|
||||
$this->data = $data;
|
||||
if (!$type) {
|
||||
|
@ -168,7 +168,7 @@ namespace Typecho {
|
||||
class Common
|
||||
{
|
||||
/** 程序版本 */
|
||||
public const VERSION = '1.2.1';
|
||||
public const VERSION = '1.3.0';
|
||||
|
||||
/**
|
||||
* 将路径转化为链接
|
||||
@ -236,6 +236,8 @@ namespace Typecho {
|
||||
} elseif ($exception instanceof \Typecho\Db\Adapter\SQLException) {
|
||||
$message = 'Database Query Error';
|
||||
}
|
||||
} else {
|
||||
$message = 'Server Error';
|
||||
}
|
||||
|
||||
/** 设置http code */
|
||||
@ -378,8 +380,8 @@ EOF;
|
||||
}
|
||||
|
||||
//非自闭合html标签列表
|
||||
preg_match_all("/<([_0-9a-zA-Z-\:]+)\s*([^>]*)>/is", $string, $startTags);
|
||||
preg_match_all("/<\/([_0-9a-zA-Z-\:]+)>/is", $string, $closeTags);
|
||||
preg_match_all("/<([_0-9a-zA-Z-:]+)\s*([^>]*)>/is", $string, $startTags);
|
||||
preg_match_all("/<\/([_0-9a-zA-Z-:]+)>/is", $string, $closeTags);
|
||||
|
||||
if (!empty($startTags[1]) && is_array($startTags[1])) {
|
||||
krsort($startTags[1]);
|
||||
@ -410,7 +412,7 @@ EOF;
|
||||
}
|
||||
}
|
||||
|
||||
return preg_replace("/\<br\s*\/\>\s*\<\/p\>/is", '</p>', $string);
|
||||
return preg_replace("/<br\s*\/>\s*<\/p>/is", '</p>', $string);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -432,7 +434,7 @@ EOF;
|
||||
$normalizeTags = '';
|
||||
$allowableAttributes = [];
|
||||
|
||||
if (!empty($allowableTags) && preg_match_all("/\<([_a-z0-9-]+)([^>]*)\>/is", $allowableTags, $tags)) {
|
||||
if (!empty($allowableTags) && preg_match_all("/<([_a-z0-9-]+)([^>]*)>/is", $allowableTags, $tags)) {
|
||||
$normalizeTags = '<' . implode('><', array_map('strtolower', $tags[1])) . '>';
|
||||
$attributes = array_map('trim', $tags[2]);
|
||||
foreach ($attributes as $key => $val) {
|
||||
@ -546,8 +548,8 @@ EOF;
|
||||
$params = array_map(function ($string) {
|
||||
$string = str_replace(['%0d', '%0a'], '', strip_tags($string));
|
||||
return preg_replace([
|
||||
"/\(\s*(\"|')/i", //函数开头
|
||||
"/(\"|')\s*\)/i", //函数结尾
|
||||
"/\(\s*([\"'])/i", //函数开头
|
||||
"/([\"'])\s*\)/i", //函数结尾
|
||||
], '', $string);
|
||||
}, $params);
|
||||
|
||||
@ -1477,5 +1479,21 @@ EOF;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* IDN转UTF8
|
||||
*
|
||||
* @param string $url
|
||||
* @return string
|
||||
*/
|
||||
public static function idnToUtf8(string $url): string
|
||||
{
|
||||
if (function_exists('idn_to_utf8') && !empty($url)) {
|
||||
$host = parse_url($url, PHP_URL_HOST);
|
||||
$url = str_replace($host, idn_to_utf8($host), $url);
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ namespace Typecho;
|
||||
* @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org)
|
||||
* @license GNU General Public License 2.0
|
||||
*/
|
||||
class Config implements \Iterator, \ArrayAccess
|
||||
class Config extends \stdClass implements \Iterator, \ArrayAccess
|
||||
{
|
||||
/**
|
||||
* 当前配置
|
||||
@ -18,7 +18,7 @@ class Config implements \Iterator, \ArrayAccess
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $currentConfig = [];
|
||||
private array $currentConfig = [];
|
||||
|
||||
/**
|
||||
* 实例化一个当前配置
|
||||
@ -200,7 +200,7 @@ class Config implements \Iterator, \ArrayAccess
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return serialize($this->currentConfig);
|
||||
return json_encode($this->currentConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -17,7 +17,7 @@ class Cookie
|
||||
* @var string
|
||||
* @access private
|
||||
*/
|
||||
private static $prefix = '';
|
||||
private static string $prefix = '';
|
||||
|
||||
/**
|
||||
* 路径
|
||||
@ -25,25 +25,25 @@ class Cookie
|
||||
* @var string
|
||||
* @access private
|
||||
*/
|
||||
private static $path = '/';
|
||||
private static string $path = '/';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @access private
|
||||
*/
|
||||
private static $domain = '';
|
||||
private static string $domain = '';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
* @access private
|
||||
*/
|
||||
private static $secure = false;
|
||||
private static bool $secure = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
* @access private
|
||||
*/
|
||||
private static $httponly = false;
|
||||
private static bool $httponly = false;
|
||||
|
||||
/**
|
||||
* 获取前缀
|
||||
@ -112,8 +112,8 @@ class Cookie
|
||||
public static function setOptions(array $options)
|
||||
{
|
||||
self::$domain = $options['domain'] ?: self::$domain;
|
||||
self::$secure = $options['secure'] ? (bool) $options['secure'] : false;
|
||||
self::$httponly = $options['httponly'] ? (bool) $options['httponly'] : false;
|
||||
self::$secure = !!$options['secure'];
|
||||
self::$httponly = !!$options['httponly'];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -141,7 +141,15 @@ class Cookie
|
||||
{
|
||||
$key = self::$prefix . $key;
|
||||
$_COOKIE[$key] = $value;
|
||||
Response::getInstance()->setCookie($key, $value, $expire, self::$path, self::$domain, self::$secure, self::$httponly);
|
||||
Response::getInstance()->setCookie(
|
||||
$key,
|
||||
$value,
|
||||
$expire,
|
||||
self::$path,
|
||||
self::$domain,
|
||||
self::$secure,
|
||||
self::$httponly
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -160,4 +168,3 @@ class Cookie
|
||||
unset($_COOKIE[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@ class Date
|
||||
* @access public
|
||||
* @var integer
|
||||
*/
|
||||
public static $timezoneOffset = 0;
|
||||
public static int $timezoneOffset = 0;
|
||||
|
||||
/**
|
||||
* 服务器时区偏移
|
||||
@ -25,7 +25,7 @@ class Date
|
||||
* @access public
|
||||
* @var integer
|
||||
*/
|
||||
public static $serverTimezoneOffset = 0;
|
||||
public static int $serverTimezoneOffset = 0;
|
||||
|
||||
/**
|
||||
* 当前的服务器时间戳
|
||||
@ -33,7 +33,7 @@ class Date
|
||||
* @access public
|
||||
* @var integer
|
||||
*/
|
||||
public static $serverTimeStamp;
|
||||
public static int $serverTimeStamp = 0;
|
||||
|
||||
/**
|
||||
* 可以被直接转换的时间戳
|
||||
@ -41,22 +41,22 @@ class Date
|
||||
* @access public
|
||||
* @var integer
|
||||
*/
|
||||
public $timeStamp = 0;
|
||||
public int $timeStamp = 0;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $year;
|
||||
public string $year;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $month;
|
||||
public string $month;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $day;
|
||||
public string $day;
|
||||
|
||||
/**
|
||||
* 初始化参数
|
||||
|
@ -55,14 +55,14 @@ class Db
|
||||
* 数据库适配器
|
||||
* @var Adapter
|
||||
*/
|
||||
private $adapter;
|
||||
private Adapter $adapter;
|
||||
|
||||
/**
|
||||
* 默认配置
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $config;
|
||||
private array $config;
|
||||
|
||||
/**
|
||||
* 已经连接
|
||||
@ -70,7 +70,7 @@ class Db
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $connectedPool;
|
||||
private array $connectedPool;
|
||||
|
||||
/**
|
||||
* 前缀
|
||||
@ -78,7 +78,7 @@ class Db
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $prefix;
|
||||
private string $prefix;
|
||||
|
||||
/**
|
||||
* 适配器名称
|
||||
@ -86,13 +86,13 @@ class Db
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $adapterName;
|
||||
private string $adapterName;
|
||||
|
||||
/**
|
||||
* 实例化的数据库对象
|
||||
* @var Db
|
||||
*/
|
||||
private static $instance;
|
||||
private static Db $instance;
|
||||
|
||||
/**
|
||||
* 数据库类构造函数
|
||||
@ -387,9 +387,11 @@ class Db
|
||||
/** 选择连接池 */
|
||||
$handle = $this->selectDb($op);
|
||||
|
||||
/** 如果是查询对象,则将其转换为查询语句 */
|
||||
$sql = $query instanceof Query ? $query->prepare($query) : $query;
|
||||
|
||||
/** 提交查询 */
|
||||
$resource = $this->adapter->query($query instanceof Query ?
|
||||
$query->prepare($query) : $query, $handle, $op, $action, $table);
|
||||
$resource = $this->adapter->query($sql, $handle, $op, $action, $table);
|
||||
|
||||
if ($action) {
|
||||
//根据查询动作返回相应资源
|
||||
|
@ -25,7 +25,7 @@ class Mysqli implements Adapter
|
||||
* @access private
|
||||
* @var \mysqli
|
||||
*/
|
||||
private $dbLink;
|
||||
private \mysqli $dbLink;
|
||||
|
||||
/**
|
||||
* 判断适配器是否可用
|
||||
|
@ -23,15 +23,15 @@ abstract class Pdo implements Adapter
|
||||
* @access protected
|
||||
* @var \PDO
|
||||
*/
|
||||
protected $object;
|
||||
protected \PDO $object;
|
||||
|
||||
/**
|
||||
* 最后一次操作的数据表
|
||||
*
|
||||
* @access protected
|
||||
* @var string
|
||||
* @var string|null
|
||||
*/
|
||||
protected $lastTable;
|
||||
protected ?string $lastTable;
|
||||
|
||||
/**
|
||||
* 判断适配器是否可用
|
||||
|
@ -62,8 +62,14 @@ class Pgsql extends Pdo
|
||||
*/
|
||||
public function init(Config $config): \PDO
|
||||
{
|
||||
$dsn = "pgsql:dbname={$config->database};host={$config->host};port={$config->port}";
|
||||
|
||||
if ($config->sslVerify) {
|
||||
$dsn .= ';sslmode=require';
|
||||
}
|
||||
|
||||
$pdo = new \PDO(
|
||||
"pgsql:dbname={$config->database};host={$config->host};port={$config->port}",
|
||||
$dsn,
|
||||
$config->user,
|
||||
$config->password
|
||||
);
|
||||
|
@ -42,6 +42,10 @@ class Pgsql implements Adapter
|
||||
$dsn = "host={$config->host} port={$config->port}"
|
||||
. " dbname={$config->database} user={$config->user} password={$config->password}";
|
||||
|
||||
if ($config->sslVerify) {
|
||||
$dsn .= ' sslmode=require';
|
||||
}
|
||||
|
||||
if ($config->charset) {
|
||||
$dsn .= " options='--client_encoding={$config->charset}'";
|
||||
}
|
||||
|
@ -11,17 +11,17 @@ trait PgsqlTrait
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $pk = [];
|
||||
private array $pk = [];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $compatibleInsert = false;
|
||||
private bool $compatibleInsert = false;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $lastInsertTable = null;
|
||||
private ?string $lastInsertTable = null;
|
||||
|
||||
/**
|
||||
* 清空数据表
|
||||
|
@ -9,7 +9,7 @@ trait SQLiteTrait
|
||||
{
|
||||
use QueryTrait;
|
||||
|
||||
private $isSQLite2 = false;
|
||||
private bool $isSQLite2 = false;
|
||||
|
||||
/**
|
||||
* 清空数据表
|
||||
|
@ -29,7 +29,7 @@ class Query
|
||||
* @var array
|
||||
* @access private
|
||||
*/
|
||||
private static $default = [
|
||||
private static array $default = [
|
||||
'action' => null,
|
||||
'table' => null,
|
||||
'fields' => '*',
|
||||
@ -48,14 +48,14 @@ class Query
|
||||
*
|
||||
* @var Adapter
|
||||
*/
|
||||
private $adapter;
|
||||
private Adapter $adapter;
|
||||
|
||||
/**
|
||||
* 查询语句预结构,由数组构成,方便组合为SQL查询字符串
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $sqlPreBuild;
|
||||
private array $sqlPreBuild;
|
||||
|
||||
/**
|
||||
* 前缀
|
||||
@ -63,12 +63,12 @@ class Query
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $prefix;
|
||||
private string $prefix;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $params = [];
|
||||
private array $params = [];
|
||||
|
||||
/**
|
||||
* 构造函数,引用数据库适配器作为内部数据
|
||||
@ -211,7 +211,6 @@ class Query
|
||||
$split .= $cha;
|
||||
$lastIsAlnum = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
@ -33,7 +33,7 @@ class Feed
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $type;
|
||||
private string $type;
|
||||
|
||||
/**
|
||||
* 字符集编码
|
||||
@ -41,7 +41,7 @@ class Feed
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $charset;
|
||||
private string $charset;
|
||||
|
||||
/**
|
||||
* 语言状态
|
||||
@ -49,7 +49,7 @@ class Feed
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $lang;
|
||||
private string $lang;
|
||||
|
||||
/**
|
||||
* 聚合地址
|
||||
@ -57,7 +57,7 @@ class Feed
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $feedUrl;
|
||||
private string $feedUrl;
|
||||
|
||||
/**
|
||||
* 基本地址
|
||||
@ -65,7 +65,7 @@ class Feed
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $baseUrl;
|
||||
private string $baseUrl;
|
||||
|
||||
/**
|
||||
* 聚合标题
|
||||
@ -73,7 +73,7 @@ class Feed
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $title;
|
||||
private string $title;
|
||||
|
||||
/**
|
||||
* 聚合副标题
|
||||
@ -81,7 +81,7 @@ class Feed
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $subTitle;
|
||||
private string $subTitle;
|
||||
|
||||
/**
|
||||
* 版本信息
|
||||
@ -89,7 +89,7 @@ class Feed
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $version;
|
||||
private string $version;
|
||||
|
||||
/**
|
||||
* 所有的items
|
||||
@ -97,7 +97,7 @@ class Feed
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $items = [];
|
||||
private array $items = [];
|
||||
|
||||
/**
|
||||
* 创建Feed对象
|
||||
@ -115,6 +115,14 @@ class Feed
|
||||
$this->lang = $lang;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置标题
|
||||
*
|
||||
@ -145,6 +153,14 @@ class Feed
|
||||
$this->feedUrl = $feedUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getFeedUrl(): string
|
||||
{
|
||||
return $this->feedUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置主页
|
||||
*
|
||||
@ -366,7 +382,7 @@ xml:base="' . $this->baseUrl . '"
|
||||
$result .= '<title type="text">' . htmlspecialchars($this->title) . '</title>
|
||||
<subtitle type="text">' . htmlspecialchars($this->subTitle ?? '') . '</subtitle>
|
||||
<updated>' . $this->dateFormat($lastUpdate) . '</updated>
|
||||
<generator uri="http://typecho.org/" version="' . $this->version . '">Typecho</generator>
|
||||
<generator uri="https://typecho.org/" version="' . $this->version . '">Typecho</generator>
|
||||
<link rel="alternate" type="text/html" href="' . $this->baseUrl . '" />
|
||||
<id>' . $this->feedUrl . '</id>
|
||||
<link rel="self" type="application/atom+xml" href="' . $this->feedUrl . '" />
|
||||
|
@ -30,33 +30,33 @@ class Client
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $method = self::METHOD_GET;
|
||||
private string $method = self::METHOD_GET;
|
||||
|
||||
/**
|
||||
* User-Agent
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public string $agent;
|
||||
|
||||
/**
|
||||
* 传递参数
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $query;
|
||||
|
||||
/**
|
||||
* User Agent
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $agent;
|
||||
private string $query;
|
||||
|
||||
/**
|
||||
* 设置超时
|
||||
*
|
||||
* @var string
|
||||
* @var integer
|
||||
*/
|
||||
private $timeout = 3;
|
||||
private int $timeout = 3;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $multipart = true;
|
||||
private bool $multipart = true;
|
||||
|
||||
/**
|
||||
* 需要在body中传递的值
|
||||
@ -71,40 +71,40 @@ class Client
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $headers = [];
|
||||
private array $headers = [];
|
||||
|
||||
/**
|
||||
* cookies
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $cookies = [];
|
||||
private array $cookies = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $options = [];
|
||||
private array $options = [];
|
||||
|
||||
/**
|
||||
* 回执头部信息
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $responseHeader = [];
|
||||
private array $responseHeader = [];
|
||||
|
||||
/**
|
||||
* 回执代码
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $responseStatus;
|
||||
private int $responseStatus;
|
||||
|
||||
/**
|
||||
* 回执身体
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $responseBody;
|
||||
private string $responseBody;
|
||||
|
||||
/**
|
||||
* 设置指定的COOKIE值
|
||||
@ -116,6 +116,7 @@ class Client
|
||||
public function setCookie(string $key, $value): Client
|
||||
{
|
||||
$this->cookies[$key] = $value;
|
||||
$this->setHeader('Cookie', str_replace('&', '; ', http_build_query($this->cookies)));
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -304,10 +305,6 @@ class Client
|
||||
$headers[] = $key . ': ' . $val;
|
||||
}
|
||||
|
||||
if (!empty($this->cookies)) {
|
||||
$headers[] = 'Cookie: ' . str_replace('&', '; ', http_build_query($this->cookies));
|
||||
}
|
||||
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||
}
|
||||
|
||||
|
@ -15,17 +15,17 @@ class I18n
|
||||
* 是否已经载入的标志位
|
||||
*
|
||||
* @access private
|
||||
* @var GetTextMulti
|
||||
* @var GetTextMulti|null
|
||||
*/
|
||||
private static $loaded;
|
||||
private static ?GetTextMulti $loaded = null;
|
||||
|
||||
/**
|
||||
* 语言文件
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
* @var string|null
|
||||
*/
|
||||
private static $lang = null;
|
||||
private static ?string $lang = null;
|
||||
|
||||
/**
|
||||
* 翻译文字
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user