主题可在线安装以及更新(基于git)

pull/33/merge
ruibaby 2018-08-28 19:19:40 +08:00
parent 42ee51cfff
commit 8775511ed9
6 changed files with 360 additions and 74 deletions

View File

@ -64,10 +64,10 @@ public class ListPage<T> {
this.nowPage = nowPage;
this.totalCount = data.size();
this.totalPage = (totalCount + pageSize - 1) / pageSize;
this.prePage = nowPage-1>1? nowPage-1:1;
this.nextPage = nowPage>=totalPage? totalPage: nowPage + 1;
this.hasPrevious = nowPage!=prePage;
this.hasNext = nowPage!=nextPage;
this.prePage = nowPage - 1 > 1 ? nowPage - 1 : 1;
this.nextPage = nowPage >= totalPage ? totalPage : nowPage + 1;
this.hasPrevious = nowPage != prePage;
this.hasNext = nowPage != nextPage;
}
/**
@ -80,7 +80,7 @@ public class ListPage<T> {
if (fromIndex >= data.size()) {
return Collections.emptyList();
}
if(fromIndex<0){
if (fromIndex < 0) {
return Collections.emptyList();
}
int toIndex = nowPage * pageSize;

View File

@ -26,4 +26,9 @@ public class Theme implements Serializable {
*
*/
private boolean hasOptions;
/**
*
*/
private boolean hasUpdate;
}

View File

@ -170,6 +170,12 @@ public class HaloUtils {
} else {
theme.setHasOptions(false);
}
File gitPath = new File(themesPath.getAbsolutePath(), file.getName() + "/.git");
if (gitPath.exists()) {
theme.setHasUpdate(true);
} else {
theme.setHasUpdate(false);
}
themes.add(theme);
}
}

View File

@ -14,6 +14,7 @@ import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.FileReader;
import cn.hutool.core.io.file.FileWriter;
import cn.hutool.core.util.RuntimeUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.extra.servlet.ServletUtil;
import lombok.extern.slf4j.Slf4j;
@ -29,6 +30,7 @@ import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.websocket.server.PathParam;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.List;
/**
@ -50,6 +52,8 @@ public class ThemeController extends BaseController {
@Autowired
private LogsService logsService;
private static final String NOT_FOUND_GIT = "-bash: git: command not found";
/**
*
*
@ -149,6 +153,71 @@ public class ThemeController extends BaseController {
return "redirect:/admin/themes";
}
/**
*
*
* @return admin/widget/_theme-install
*/
@GetMapping(value = "/install")
public String install() {
return "admin/widget/_theme-install";
}
/**
* 线
*
* @param remoteAddr
* @param themeName
* @return JsonResult
*/
@PostMapping(value = "/clone")
@ResponseBody
public JsonResult cloneFromRemote(@RequestParam(value = "remoteAddr") String remoteAddr,
@RequestParam(value = "themeName") String themeName) {
if (StringUtils.isBlank(remoteAddr) || StringUtils.isBlank(themeName)) {
return new JsonResult(0, "请输入完整信息!");
}
try {
File basePath = new File(ResourceUtils.getURL("classpath:").getPath());
File themePath = new File(basePath.getAbsolutePath(), "templates/themes");
String cmdResult = RuntimeUtil.execForStr("git clone " + remoteAddr + " " + themePath.getAbsolutePath() + "/" + themeName);
if (NOT_FOUND_GIT.equals(cmdResult)) {
return new JsonResult(0, "没有安装Git");
}
HaloConst.THEMES.clear();
HaloConst.THEMES = HaloUtils.getThemes();
} catch (FileNotFoundException e) {
log.error("克隆主题失败:{}", e.getMessage());
return new JsonResult(0, "克隆主题失败:" + e.getMessage());
}
return new JsonResult(1, "安装成功!");
}
/**
*
*
* @param themeName
* @return JsonResult
*/
@GetMapping(value = "/pull")
@ResponseBody
public JsonResult pullFromRemote(@RequestParam(value = "themeName") String themeName) {
try {
File basePath = new File(ResourceUtils.getURL("classpath:").getPath());
File themePath = new File(basePath.getAbsolutePath(), "templates/themes");
String cmdResult = RuntimeUtil.execForStr("cd " + themePath.getAbsolutePath() + "/" + themeName + " && git pull");
if (NOT_FOUND_GIT.equals(cmdResult)) {
return new JsonResult(0, "没有安装Git");
}
HaloConst.THEMES.clear();
HaloConst.THEMES = HaloUtils.getThemes();
} catch (Exception e) {
log.error("更新主题失败:{}", e.getMessage());
return new JsonResult(0, "更新主题失败:" + e.getMessage());
}
return new JsonResult(1, "更新成功!");
}
/**
*
*

View File

@ -60,8 +60,8 @@
</style>
<section class="content-header">
<h1 style="display: inline-block;">主题管理</h1>
<a id="showForm" href="#">
<i class="fa fa-cloud-upload" aria-hidden="true"></i>上传
<a id="showForm" href="#" onclick="openThemeInstall()">
<i class="fa fa-cloud-upload" aria-hidden="true"></i>安装主题
</a>
<ol class="breadcrumb">
<li><a data-pjax="true" href="/admin"><i class="fa fa-dashboard"></i> 首页</a></li>
@ -70,15 +70,6 @@
</ol>
</section>
<section class="content container-fluid">
<div class="row" id="uploadForm">
<div class="col-md-12">
<div class="form-group">
<div class="file-loading">
<input id="uploadTheme" class="file-loading" type="file" name="file" multiple>
</div>
</div>
</div>
</div>
<div class="row">
<#if themes?? && (themes?size>0)>
<#list themes as theme>
@ -89,9 +80,12 @@
</div>
<div class="box-footer">
<span class="theme-title">${theme.themeName?if_exists?upper_case}</span>
<#if theme.hasOptions==true>
<#if theme.hasOptions>
<button class="btn btn-primary btn-sm pull-right btn-theme-setting" onclick="openSetting('${theme.themeName?if_exists}')" style="display: none">设置</button>
</#if>
<#if theme.hasUpdate>
<button class="btn btn-warning btn-sm pull-right btn-theme-update" data-loading-text="更新中..." onclick="updateTheme('${theme.themeName?if_exists}',this)" style="display: none;margin-right: 3px">更新</button>
</#if>
<#if activeTheme != "${theme.themeName}">
<button class="btn btn-default btn-sm pull-right btn-theme-enable" onclick="setTheme('${theme.themeName?if_exists}')" style="display: none;margin-right: 3px">启用</button>
<#else>
@ -133,63 +127,26 @@
</div>
</div>
<script type="application/javascript">
function loadFileInput() {
$('#uploadTheme').fileinput({
language: 'zh',
uploadUrl: '/admin/themes/upload',
allowedFileExtensions: ['zip','jpg'],
maxFileCount: 1,
enctype: 'multipart/form-data',
dropZoneTitle: '拖拽主题压缩包到这里 &hellip;<br>不支持多个主题同时上传',
showClose: false
}).on("fileuploaded",function (event,data,previewId,index) {
var data = data.jqXHR.responseJSON;
if(data.code==1){
$("#uploadForm").hide(400);
$.toast({
text: data.msg,
heading: '提示',
icon: 'success',
showHideTransition: 'fade',
allowToastClose: true,
hideAfter: 1000,
stack: 1,
position: 'top-center',
textAlign: 'left',
loader: true,
loaderBg: '#ffffff',
afterHidden: function () {
window.location.reload();
}
});
}else{
$.toast({
text: data.msg,
heading: '提示',
icon: 'error',
showHideTransition: 'fade',
allowToastClose: true,
hideAfter: 1000,
stack: 1,
position: 'top-center',
textAlign: 'left',
loader: true,
loaderBg: '#ffffff'
/**
* 打开安装主题的窗口
*/
function openThemeInstall() {
layer.open({
type: 2,
title: '安装主题',
shadeClose: true,
shade: 0.5,
maxmin: true,
area: ['90%', '90%'],
content: '/admin/themes/install',
scrollbar: false
});
}
});
}
$(document).ready(function () {
loadFileInput();
});
<#if options.admin_pjax?default("true") == "true">
$(document).on('pjax:complete',function () {
loadFileInput();
});
</#if>
$("#showForm").click(function(){
$("#uploadForm").slideToggle(400);
});
/**
* 设置主题
* @param site_theme 主题名
*/
function setTheme(site_theme) {
$.ajax({
type: 'get',
@ -233,6 +190,61 @@
}
});
}
/**
* 更新主题
*/
function updateTheme(theme,e) {
$(e).button('loading');
$.ajax({
type: 'get',
url: '/admin/themes/pull',
data: {
'themeName': theme
},
success: function (data) {
if(data.code==1){
$.toast({
text: data.msg,
heading: '提示',
icon: 'success',
showHideTransition: 'fade',
allowToastClose: true,
hideAfter: 1000,
stack: 1,
position: 'top-center',
textAlign: 'left',
loader: true,
loaderBg: '#ffffff',
afterHidden: function () {
window.location.reload();
}
});
}else{
$.toast({
text: data.msg,
heading: '提示',
icon: 'error',
showHideTransition: 'fade',
allowToastClose: true,
hideAfter: 2000,
stack: 1,
position: 'top-center',
textAlign: 'left',
loader: true,
loaderBg: '#ffffff'
});
$(e).button('reset');
}
}
});
}
/**
* 打开主题设置
*
* @param theme 主题名
*/
function openSetting(theme) {
layer.open({
type: 2,
@ -253,11 +265,11 @@
});
$('.theme-body').mouseover(function () {
$(this).find(".theme-thumbnail").css("opacity","0.8");
$(this).find(".btn-theme-setting,.btn-theme-enable").show();
$(this).find(".btn-theme-setting,.btn-theme-enable,.btn-theme-update").show();
});
$('.theme-body').mouseleave(function () {
$(this).find(".theme-thumbnail").css("opacity","1");
$(this).find(".btn-theme-setting,.btn-theme-enable").hide();
$(this).find(".btn-theme-setting,.btn-theme-enable,.btn-theme-update").hide();
});
function modelShow(url) {
$('#url').val(url);

View File

@ -0,0 +1,194 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<link rel="stylesheet" href="/static/plugins/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="/static/plugins/toast/css/jquery.toast.min.css">
<link rel="stylesheet" href="/static/plugins/fileinput/fileinput.min.css">
<link rel="stylesheet" href="/static/css/AdminLTE.min.css">
<style type="text/css" rel="stylesheet">
.form-horizontal .control-label{
text-align: left;
}
.alert-info{
color: #31708f!important;
background-color: #d9edf7!important;
border-color: #bce8f1!important;
}
</style>
</head>
<body>
<div class="container-fluid">
<section class="content">
<div class="nav-tabs-custom">
<ul class="nav nav-tabs">
<li class="active">
<a href="#upload" data-toggle="tab">本地上传</a>
</li>
<li>
<a href="#clone" data-toggle="tab">远程拉取</a>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="upload">
<div class="row" id="uploadForm">
<div class="col-md-12">
<div class="form-group">
<div class="file-loading">
<input id="uploadTheme" class="file-loading" type="file" name="file" multiple>
</div>
</div>
</div>
</div>
</div>
<div class="tab-pane" id="clone">
<form method="post" class="form-horizontal" id="pullForm">
<div class="box-body">
<div class="alert alert-info alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<strong>注意!</strong> 使用该功能必须安装Git否则无法使用。更多主题请点击<a href="https://gitee.com/babyrui" class="alert-link">https://gitee.com/babyrui</a>.
</div>
<div class="form-group">
<label for="remoteAddr" class="col-lg-2 col-sm-4 control-label">远程地址:</label>
<div class="col-lg-4 col-sm-8">
<input type="text" class="form-control" id="remoteAddr" name="remoteAddr">
</div>
</div>
<div class="form-group">
<label for="themeName" class="col-lg-2 col-sm-4 control-label">主题名称:</label>
<div class="col-lg-4 col-sm-8">
<input type="text" class="form-control" id="themeName" name="themeName">
</div>
</div>
</div>
<div class="box-footer">
<button type="button" data-loading-text="安装中..." class="btn btn-primary btn-sm" onclick="pullAction()" id="btnInstall">安装</button>
</div>
</form>
</div>
</div>
</div>
</section>
</div>
</body>
<script src="/static/plugins/jquery/jquery.min.js"></script>
<script src="/static/plugins/bootstrap/js/bootstrap.min.js"></script>
<script src="/static/plugins/fileinput/fileinput.min.js"></script>
<script src="/static/plugins/fileinput/zh.min.js"></script>
<script src="/static/plugins/toast/js/jquery.toast.min.js"></script>
<script src="/static/plugins/layer/layer.js"></script>
<script src="/static/js/app.js"></script>
<script>
$(document).ready(function () {
loadFileInput();
});
/**
* 初始化上传组件
*/
function loadFileInput() {
$('#uploadTheme').fileinput({
language: 'zh',
uploadUrl: '/admin/themes/upload',
allowedFileExtensions: ['zip'],
maxFileCount: 1,
enctype: 'multipart/form-data',
dropZoneTitle: '拖拽主题压缩包到这里 &hellip;<br>仅支持Zip格式',
showClose: false
}).on("fileuploaded",function (event,data,previewId,index) {
var data = data.jqXHR.responseJSON;
if(data.code==1){
$("#uploadForm").hide(400);
$.toast({
text: data.msg,
heading: '提示',
icon: 'success',
showHideTransition: 'fade',
allowToastClose: true,
hideAfter: 1000,
stack: 1,
position: 'top-center',
textAlign: 'left',
loader: true,
loaderBg: '#ffffff',
afterHidden: function () {
parent.location.reload();
}
});
}else{
$.toast({
text: data.msg,
heading: '提示',
icon: 'error',
showHideTransition: 'fade',
allowToastClose: true,
hideAfter: 1000,
stack: 1,
position: 'top-center',
textAlign: 'left',
loader: true,
loaderBg: '#ffffff'
});
}
});
}
/**
* 拉取主题
*/
function pullAction() {
var remoteAddr = $("#remoteAddr").val();
var themeName = $("#themeName").val();
if(remoteAddr==null || themeName==null){
$.toast({
text: "请输入完整信息!",
heading: '提示',
icon: 'error',
showHideTransition: 'fade',
allowToastClose: true,
hideAfter: 1000,
stack: 1,
position: 'top-center',
textAlign: 'left',
loader: true,
loaderBg: '#ffffff'
});
return;
}
$('#btnInstall').button('loading');
$.ajax({
type: 'post',
url: '/admin/themes/clone',
data: {
remoteAddr : remoteAddr,
themeName: themeName
},
success: function (data) {
if(data.code==1){
$.toast({
text: data.msg,
heading: '提示',
icon: 'success',
showHideTransition: 'fade',
allowToastClose: true,
hideAfter: 1000,
stack: 1,
position: 'top-center',
textAlign: 'left',
loader: true,
loaderBg: '#ffffff',
afterHidden: function () {
parent.location.reload();
}
});
}else {
showMsg(data.msg,"error",1000);
$('#btnInstall').button('reset');
}
}
});
}
</script>
</html>