Commit da595c87 authored by Liang Ding's avatar Liang Ding

新增 Markdown zip 导入方式 #128

parent 3e786b85
...@@ -519,7 +519,8 @@ public final class Server extends BaseServer { ...@@ -519,7 +519,8 @@ public final class Server extends BaseServer {
get("/admin-preference.do", adminConsole::showAdminPreferenceFunction). get("/admin-preference.do", adminConsole::showAdminPreferenceFunction).
get("/console/export/sql", adminConsole::exportSQL). get("/console/export/sql", adminConsole::exportSQL).
get("/console/export/json", adminConsole::exportJSON). get("/console/export/json", adminConsole::exportJSON).
get("/console/export/hexo", adminConsole::exportHexo); get("/console/export/hexo", adminConsole::exportHexo).
post("/console/import/markdown-zip", adminConsole::importMarkdownZip);
adminConsoleGroup.router().get(new String[]{"/admin-article.do", adminConsoleGroup.router().get(new String[]{"/admin-article.do",
"/admin-article-list.do", "/admin-article-list.do",
"/admin-comment-list.do", "/admin-comment-list.do",
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
package org.b3log.solo.processor.console; package org.b3log.solo.processor.console;
import jodd.io.ZipUtil; import jodd.io.ZipUtil;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateFormatUtils; import org.apache.commons.lang.time.DateFormatUtils;
...@@ -22,6 +23,8 @@ import org.b3log.latke.Keys; ...@@ -22,6 +23,8 @@ import org.b3log.latke.Keys;
import org.b3log.latke.Latkes; import org.b3log.latke.Latkes;
import org.b3log.latke.event.Event; import org.b3log.latke.event.Event;
import org.b3log.latke.event.EventManager; import org.b3log.latke.event.EventManager;
import org.b3log.latke.http.FileUpload;
import org.b3log.latke.http.Request;
import org.b3log.latke.http.RequestContext; import org.b3log.latke.http.RequestContext;
import org.b3log.latke.http.Response; import org.b3log.latke.http.Response;
import org.b3log.latke.http.renderer.AbstractFreeMarkerRenderer; import org.b3log.latke.http.renderer.AbstractFreeMarkerRenderer;
...@@ -31,14 +34,12 @@ import org.b3log.latke.model.Plugin; ...@@ -31,14 +34,12 @@ import org.b3log.latke.model.Plugin;
import org.b3log.latke.model.User; import org.b3log.latke.model.User;
import org.b3log.latke.plugin.ViewLoadEventData; import org.b3log.latke.plugin.ViewLoadEventData;
import org.b3log.latke.service.LangPropsService; import org.b3log.latke.service.LangPropsService;
import org.b3log.latke.util.Strings;
import org.b3log.solo.Server; import org.b3log.solo.Server;
import org.b3log.solo.model.Common; import org.b3log.solo.model.Common;
import org.b3log.solo.model.Option; import org.b3log.solo.model.Option;
import org.b3log.solo.model.UserExt; import org.b3log.solo.model.UserExt;
import org.b3log.solo.service.DataModelService; import org.b3log.solo.service.*;
import org.b3log.solo.service.ExportService;
import org.b3log.solo.service.OptionQueryService;
import org.b3log.solo.service.UserQueryService;
import org.b3log.solo.util.Markdowns; import org.b3log.solo.util.Markdowns;
import org.b3log.solo.util.Solos; import org.b3log.solo.util.Solos;
import org.json.JSONObject; import org.json.JSONObject;
...@@ -101,6 +102,12 @@ public class AdminConsole { ...@@ -101,6 +102,12 @@ public class AdminConsole {
@Inject @Inject
private EventManager eventManager; private EventManager eventManager;
/**
* Import service.
*/
@Inject
private ImportService importService;
/** /**
* Shows administrator index with the specified context. * Shows administrator index with the specified context.
* *
...@@ -220,6 +227,45 @@ public class AdminConsole { ...@@ -220,6 +227,45 @@ public class AdminConsole {
fireFreeMarkerActionEvent(templateName, dataModel); fireFreeMarkerActionEvent(templateName, dataModel);
} }
/**
* Imports markdown zip.
*
* @param context the specified context
*/
public void importMarkdownZip(final RequestContext context) {
final Request request = context.getRequest();
final FileUpload file = request.getFileUpload("file");
final String[] allowedSuffixArray = new String[]{"zip"};
final String fileName = file.getFilename();
String suffix = StringUtils.substringAfterLast(fileName, ".");
if (StringUtils.isBlank(suffix)) {
// TODO
return;
}
if (!Strings.containsIgnoreCase(suffix, allowedSuffixArray)) {
// TODO
return;
}
try {
final byte[] bytes = file.getData();
final String tmpDir = System.getProperty("java.io.tmpdir");
final String date = DateFormatUtils.format(new Date(), "yyyyMMddHHmmss");
final String zipPath = tmpDir + File.separator + "solo-import-" + date + ".zip";
final File zipFile = new File(zipPath);
FileUtils.writeByteArrayToFile(zipFile, bytes);
final String unzipPath = tmpDir + File.separator + "solo-import-" + date;
final File unzipDir = new File(unzipPath);
ZipUtil.unzip(zipFile, unzipDir);
importService.importMarkdownDir(unzipDir);
FileUtils.deleteQuietly(zipFile);
FileUtils.deleteQuietly(unzipDir);
} catch (final Exception e) {
LOGGER.log(Level.ERROR, "Imports markdown file failed", e);
return;
}
}
/** /**
* Exports data as SQL zip file. * Exports data as SQL zip file.
* *
......
...@@ -260,6 +260,10 @@ public class ExportService { ...@@ -260,6 +260,10 @@ public class ExportService {
*/ */
public void exportGitHub() { public void exportGitHub() {
try { try {
if (Latkes.RuntimeMode.DEVELOPMENT == Latkes.getRuntimeMode()) {
return;
}
final JSONObject preference = optionQueryService.getPreference(); final JSONObject preference = optionQueryService.getPreference();
if (null == preference) { if (null == preference) {
return; return;
......
...@@ -35,7 +35,7 @@ import java.util.*; ...@@ -35,7 +35,7 @@ import java.util.*;
* Import service. * Import service.
* *
* @author <a href="http://88250.b3log.org">Liang Ding</a> * @author <a href="http://88250.b3log.org">Liang Ding</a>
* @version 1.0.1.6, Apr 23, 2020 * @version 1.0.1.7, May 21, 2020
* @since 2.2.0 * @since 2.2.0
*/ */
@Service @Service
...@@ -64,8 +64,7 @@ public class ImportService { ...@@ -64,8 +64,7 @@ public class ImportService {
private UserQueryService userQueryService; private UserQueryService userQueryService;
/** /**
* Imports markdowns files as articles. See <a href="https://hacpai.com/article/1498490209748">Solo 支持 Hexo/Jekyll 数据导入</a> for * Imports markdown files as articles. See <a href="https://hacpai.com/article/1498490209748">Solo 支持 Hexo/Jekyll 数据导入</a> for more details.
* more details.
*/ */
public void importMarkdowns() { public void importMarkdowns() {
new Thread(() -> { new Thread(() -> {
...@@ -80,65 +79,74 @@ public class ImportService { ...@@ -80,65 +79,74 @@ public class ImportService {
} }
final File markdownsPath = Latkes.getFile("/markdowns"); final File markdownsPath = Latkes.getFile("/markdowns");
LOGGER.debug("Import directory [" + markdownsPath.getPath() + "]"); importMarkdownDir(markdownsPath);
}).start();
}
final JSONObject admin = userQueryService.getAdmin(); /**
if (null == admin) { // Not init yet * Imports markdown files under the specified markdown files dir.
return; *
} * @param markdownsDir the specified markdown files dir
*/
public void importMarkdownDir(final File markdownsDir) {
LOGGER.debug("Import directory [" + markdownsDir.getPath() + "]");
final String adminId = admin.optString(Keys.OBJECT_ID); final JSONObject admin = userQueryService.getAdmin();
if (null == admin) { // Not init yet
return;
}
int succCnt = 0, failCnt = 0; final String adminId = admin.optString(Keys.OBJECT_ID);
final Set<String> failSet = new TreeSet<>();
final Collection<File> mds = FileUtils.listFiles(markdownsPath, new String[]{"md"}, true);
if (mds.isEmpty()) {
return;
}
for (final File md : mds) { int succCnt = 0, failCnt = 0;
final String fileName = md.getName(); final Set<String> failSet = new TreeSet<>();
if (StringUtils.equalsIgnoreCase(fileName, "README.md")) { final Collection<File> mds = FileUtils.listFiles(markdownsDir, new String[]{"md"}, true);
continue; if (mds.isEmpty()) {
} return;
}
for (final File md : mds) {
final String fileName = md.getName();
if (StringUtils.equalsIgnoreCase(fileName, "README.md")) {
continue;
}
try { try {
final String fileContent = FileUtils.readFileToString(md, "UTF-8"); final String fileContent = FileUtils.readFileToString(md, "UTF-8");
final JSONObject article = parseArticle(fileName, fileContent); final JSONObject article = parseArticle(fileName, fileContent);
article.put(Article.ARTICLE_AUTHOR_ID, adminId); article.put(Article.ARTICLE_AUTHOR_ID, adminId);
final JSONObject request = new JSONObject(); final JSONObject request = new JSONObject();
request.put(Article.ARTICLE, article); request.put(Article.ARTICLE, article);
final String id = articleMgmtService.addArticle(request); final String id = articleMgmtService.addArticle(request);
FileUtils.moveFile(md, new File(md.getPath() + "." + id)); FileUtils.moveFile(md, new File(md.getPath() + "." + id));
LOGGER.info("Imported article [" + article.optString(Article.ARTICLE_TITLE) + "]"); LOGGER.info("Imported article [" + article.optString(Article.ARTICLE_TITLE) + "]");
succCnt++; succCnt++;
} catch (final Exception e) { } catch (final Exception e) {
LOGGER.log(Level.ERROR, "Import file [" + fileName + "] failed", e); LOGGER.log(Level.ERROR, "Import file [" + fileName + "] failed", e);
failCnt++; failCnt++;
failSet.add(fileName); failSet.add(fileName);
}
} }
}
if (0 == succCnt && 0 == failCnt) { if (0 == succCnt && 0 == failCnt) {
return; return;
} }
final StringBuilder logBuilder = new StringBuilder(); final StringBuilder logBuilder = new StringBuilder();
logBuilder.append("[").append(succCnt).append("] imported, [").append(failCnt).append("] failed"); logBuilder.append("[").append(succCnt).append("] imported, [").append(failCnt).append("] failed");
if (failCnt > 0) { if (failCnt > 0) {
logBuilder.append(": ").append(Strings.LINE_SEPARATOR); logBuilder.append(": ").append(Strings.LINE_SEPARATOR);
for (final String fail : failSet) { for (final String fail : failSet) {
logBuilder.append(" ").append(fail).append(Strings.LINE_SEPARATOR); logBuilder.append(" ").append(fail).append(Strings.LINE_SEPARATOR);
}
} else {
logBuilder.append(" :p");
} }
LOGGER.info(logBuilder.toString()); } else {
}).start(); logBuilder.append(" :p");
}
LOGGER.info(logBuilder.toString());
} }
private JSONObject parseArticle(final String fileName, String fileContent) { private JSONObject parseArticle(final String fileName, String fileContent) {
......
...@@ -23,6 +23,11 @@ ...@@ -23,6 +23,11 @@
<a href="#tools/others/data">${exportDataLabel}</a> <a href="#tools/others/data">${exportDataLabel}</a>
</div> </div>
</li> </li>
<li>
<div id="tabOthers_data">
<a href="#tools/others/import-data">${importDataLabel}</a>
</div>
</li>
<li> <li>
<div id="tabOthers_log"> <div id="tabOthers_log">
<a href="#tools/others/log">${viewLogLabel}</a> <a href="#tools/others/log">${viewLogLabel}</a>
...@@ -42,6 +47,10 @@ ...@@ -42,6 +47,10 @@
<button class="fn__margin12" onclick="admin.others.exportJSON();">${exportJSONLabel}</button> <button class="fn__margin12" onclick="admin.others.exportJSON();">${exportJSONLabel}</button>
<button class="fn__margin12" onclick="admin.others.exportHexo();">${exportHexoLabel}</button> <button class="fn__margin12" onclick="admin.others.exportHexo();">${exportHexoLabel}</button>
</div> </div>
<div id="tabOthersPanel_import-data" class="fn__none">
TODO
<button class="fn__margin12" onclick="">${uploadMarkdownZipLabel}</button>
</div>
<div id="tabOthersPanel_log" class="fn__none form"> <div id="tabOthersPanel_log" class="fn__none form">
<textarea rows="32" readonly></textarea> <textarea rows="32" readonly></textarea>
</div> </div>
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
# Author: Dongxu Wang # Author: Dongxu Wang
# #
uploadMarkdownZipLabel=Upload Markdown Zip
editorModeSVLabel=Split View editorModeSVLabel=Split View
editorModeIRLabel=Instant Rendering editorModeIRLabel=Instant Rendering
editorModeWYSIWYGLabel=WYSIWYG editorModeWYSIWYGLabel=WYSIWYG
...@@ -55,6 +56,7 @@ getUploadTokenErrLabel=Get community file storage service upload token failed ...@@ -55,6 +56,7 @@ getUploadTokenErrLabel=Get community file storage service upload token failed
startToUseLabel=Start startToUseLabel=Start
clearDataLabel=Clear data clearDataLabel=Clear data
exportDataLabel=Export data exportDataLabel=Export data
importDataLabel=Import data
syncToCommunityLabel=Sync to community syncToCommunityLabel=Sync to community
cntLabel= cntLabel=
reply1Label=Reply reply1Label=Reply
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
# Author: Dongxu Wang # Author: Dongxu Wang
# #
uploadMarkdownZipLabel=\u4E0A\u4F20 Markdown Zip \u5305
editorModeSVLabel=\u5206\u5C4F\u9884\u89C8 editorModeSVLabel=\u5206\u5C4F\u9884\u89C8
editorModeIRLabel=\u5373\u65F6\u6E32\u67D3 editorModeIRLabel=\u5373\u65F6\u6E32\u67D3
editorModeWYSIWYGLabel=\u6240\u89C1\u5373\u6240\u5F97 editorModeWYSIWYGLabel=\u6240\u89C1\u5373\u6240\u5F97
...@@ -55,6 +56,7 @@ getUploadTokenErrLabel=\u83B7\u53D6\u793E\u533A\u6587\u4EF6\u5B58\u50A8\u670D\u5 ...@@ -55,6 +56,7 @@ getUploadTokenErrLabel=\u83B7\u53D6\u793E\u533A\u6587\u4EF6\u5B58\u50A8\u670D\u5
startToUseLabel=\u5F00\u59CB\u4F7F\u7528 startToUseLabel=\u5F00\u59CB\u4F7F\u7528
clearDataLabel=\u6570\u636E\u6E05\u7406 clearDataLabel=\u6570\u636E\u6E05\u7406
exportDataLabel=\u6570\u636E\u5BFC\u51FA exportDataLabel=\u6570\u636E\u5BFC\u51FA
importDataLabel=\u6570\u636E\u5BFC\u5165
syncToCommunityLabel=\u540C\u6B65\u5230\u793E\u533A syncToCommunityLabel=\u540C\u6B65\u5230\u793E\u533A
cntLabel=\u4E2A cntLabel=\u4E2A
reply1Label=\u56DE\u590D reply1Label=\u56DE\u590D
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment