Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 65 additions & 2 deletions functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,14 @@ function themeConfig($form) {
}
$backPath = $absUploadDir . '/BackupSetting_' . $theTheme . '.txt';
$ret = ['success'=>false, 'message'=>'未知错误'];
if ($_POST['action'] === 'oneblog_theme_backup') {
$action = $_POST['action'] ?? '';
if ($action === 'oneblog_theme_backup') {
$themeConfStr = $db->fetchRow($db->select()->from('table.options')->where('name = ?', 'theme:' . $theTheme))['value'];
$ok = file_put_contents($backPath, $themeConfStr);
$ret = $ok !== false
? ['success'=>true, 'message'=>'备份成功']
: ['success'=>false, 'message'=>'备份失败,uploads 目录不可写'];
} elseif ($_POST['action'] === 'oneblog_theme_restore') {
} elseif ($action === 'oneblog_theme_restore') {
if (file_exists($backPath)) {
$str = file_get_contents($backPath);
$updateThemeConQuery = $db->update('table.options')->rows(['value'=>$str])->where('name=?', 'theme:' . $theTheme);
Expand All @@ -47,6 +48,68 @@ function themeConfig($form) {
} else {
$ret = ['success'=>false, 'message'=>'未找到备份文件,无法恢复'];
}
} elseif ($action === 'oneblog_theme_upload_image') {
$user = Typecho_Widget::widget('Widget_User');
if (!$user || !$user->pass('administrator', true)) {
$ret = ['success'=>false, 'message'=>'无权限操作'];
} else {
$allowedKeys = ['logo','logoWhite','Favicon','Tagbg','Webthumb','Weixin'];
$key = trim((string)($_POST['key'] ?? ''));
if ($key === '' || !in_array($key, $allowedKeys, true)) {
$ret = ['success'=>false, 'message'=>'不支持的字段'];
} elseif (empty($_FILES['file']) || !is_array($_FILES['file'])) {
$ret = ['success'=>false, 'message'=>'未选择文件'];
} else {
$file = $_FILES['file'];
$error = $file['error'] ?? UPLOAD_ERR_NO_FILE;
if ($error !== UPLOAD_ERR_OK) {
$ret = ['success'=>false, 'message'=>'上传失败(错误码:' . $error . ')'];
} else {
$tmpName = $file['tmp_name'] ?? '';
if (!$tmpName || !is_uploaded_file($tmpName)) {
$ret = ['success'=>false, 'message'=>'无效的上传文件'];
} else {
$origName = (string)($file['name'] ?? '');
$ext = strtolower(pathinfo($origName, PATHINFO_EXTENSION));
$allowedExt = ['jpg','jpeg','png','gif','webp','svg','ico'];
if ($ext === '' || !in_array($ext, $allowedExt, true)) {
$ret = ['success'=>false, 'message'=>'仅支持:' . implode(' / ', $allowedExt)];
} else {
$nameMap = [
'logo' => 'logo',
'logoWhite' => 'logoWhite',
'Favicon' => 'favicon',
'Tagbg' => 'Tagbg',
'Webthumb' => 'Webthumb',
'Weixin' => 'Weixin',
];
$baseName = $nameMap[$key] ?? $key;
$baseName = preg_replace('/[^A-Za-z0-9_-]/', '', (string)$baseName);
if ($baseName === '') {
$ret = ['success'=>false, 'message'=>'文件名不合法'];
} else {
$themeDir = __DIR__;
if (!is_dir($themeDir) || !is_writable($themeDir)) {
$ret = ['success'=>false, 'message'=>'主题目录不可写:' . $themeDir];
} else {
$newName = $baseName . '.' . $ext;
$destPath = $themeDir . DIRECTORY_SEPARATOR . $newName;
if (file_exists($destPath) && !@unlink($destPath)) {
$ret = ['success'=>false, 'message'=>'无法覆盖旧文件:' . $newName];
} elseif (!move_uploaded_file($tmpName, $destPath)) {
$ret = ['success'=>false, 'message'=>'保存失败,请检查目录权限'];
} else {
@chmod($destPath, 0644);
$url = rtrim((string)Helper::options()->themeUrl, '/\\') . '/' . $newName;
$ret = ['success'=>true, 'message'=>'上传成功', 'url'=>$url, 'filename'=>$newName];
}
}
}
}
}
}
}
}
}
echo json_encode($ret);
exit;
Expand Down
75 changes: 72 additions & 3 deletions static/js/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ document.addEventListener('DOMContentLoaded', function(){

// 主题设置备份与恢复
jQuery(function($){
const ajaxUrl = location.href.split('#')[0];
function showResult(msg, icon, reload){
if (typeof layer !== 'undefined') {
layer.msg(msg, {icon: icon, time: 1200}, function(){
Expand All @@ -158,7 +159,7 @@ jQuery(function($){
if(confirm('确定要备份当前主题配置吗?')) doBackup();
}
function doBackup(){
$.post(location.href, {action:'oneblog_theme_backup', _ajax:1}, function(res){
$.post(ajaxUrl, {action:'oneblog_theme_backup', _ajax:1}, function(res){
if(res && res.success){
showResult(res.message, 1, true);
}else{
Expand All @@ -180,7 +181,7 @@ jQuery(function($){
if(confirm('确定要恢复主题配置吗?恢复操作不可逆,请谨慎!')) doRestore();
}
function doRestore(){
$.post(location.href, {action:'oneblog_theme_restore', _ajax:1}, function(res){
$.post(ajaxUrl, {action:'oneblog_theme_restore', _ajax:1}, function(res){
if(res && res.success){
showResult(res.message, 1, true);
}else{
Expand All @@ -191,5 +192,73 @@ jQuery(function($){
});
}
});

function ensureUploadStyles(){
if (document.getElementById('ob-upload-style')) return;
const style = document.createElement('style');
style.id = 'ob-upload-style';
style.textContent = `
.ob-upload-input-wrap{position:relative;display:block;width:100%;}
.ob-upload-input-wrap>input[type="text"]{padding-right:78px;box-sizing:border-box;}
.ob-upload-btn{position:absolute;right:6px;top:50%;transform:translateY(-50%);height:28px;padding:0 10px;border:1px solid #dcdcdc;background:#f3f3f3;color:#333;border-radius:3px;font-size:12px;line-height:26px;cursor:pointer;}
.ob-upload-btn:hover{background:#ededed;}
.ob-upload-btn:disabled{opacity:.6;cursor:not-allowed;}
`.trim();
document.head.appendChild(style);
}

function bindUpload(fieldName){
const $input = $(`input[name="${fieldName}"]`);
if (!$input.length) return;
if ($input.data('obUploadBind')) return;
$input.data('obUploadBind', 1);

ensureUploadStyles();

$input.wrap('<span class="ob-upload-input-wrap"></span>');
const $wrap = $input.parent();
const $btn = $('<button type="button" class="ob-upload-btn">上传</button>');
const $file = $('<input type="file" class="ob-upload-file" accept="image/*,.svg,.ico" style="display:none;">');
$wrap.append($btn, $file);

$btn.on('click', function(){
$file.trigger('click');
});

$file.on('change', function(){
const file = this.files && this.files[0];
if (!file) return;

const formData = new FormData();
formData.append('action', 'oneblog_theme_upload_image');
formData.append('_ajax', '1');
formData.append('key', fieldName);
formData.append('file', file);

$btn.prop('disabled', true).text('上传中');
$.ajax({
url: ajaxUrl,
type: 'POST',
data: formData,
processData: false,
contentType: false,
dataType: 'json'
}).done(function(res){
if (res && res.success && res.url) {
$input.val(res.url).trigger('change');
showResult(res.message || '上传成功', 1);
} else {
showResult((res && res.message) ? res.message : '上传失败', 2);
}
}).fail(function(){
showResult('请求失败,请检查网络', 2);
}).always(function(){
$btn.prop('disabled', false).text('上传');
$file.val('');
});
});
}

['logo','logoWhite','Favicon','Tagbg','Webthumb','Weixin'].forEach(bindUpload);
});