mirror of https://github.com/halo-dev/halo
commit
c640afc3a0
|
@ -43,7 +43,7 @@ bootJar {
|
|||
|
||||
ext {
|
||||
ohMyEmailVersion = '0.0.4'
|
||||
hutoolVersion = '4.6.3'
|
||||
hutoolVersion = '5.0.3'
|
||||
upyunSdkVersion = '4.0.1'
|
||||
qiniuSdkVersion = '7.2.18'
|
||||
aliyunSdkVersion = '3.4.2'
|
||||
|
|
|
@ -151,6 +151,7 @@ public class HaloConfiguration {
|
|||
"/api/admin/refresh/*",
|
||||
"/api/admin/installations",
|
||||
"/api/admin/recoveries/migrations/*",
|
||||
"/api/admin/migrations/*",
|
||||
"/api/admin/is_installed",
|
||||
"/api/admin/password/code",
|
||||
"/api/admin/password/reset"
|
||||
|
|
|
@ -179,7 +179,7 @@ public class InstallController {
|
|||
@Nullable
|
||||
private Category createDefaultCategoryIfAbsent() {
|
||||
long categoryCount = categoryService.count();
|
||||
if (categoryCount == 0) {
|
||||
if (categoryCount > 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
package run.halo.app.controller.admin.api;
|
||||
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestPart;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import run.halo.app.model.enums.MigrateType;
|
||||
import run.halo.app.service.MigrateService;
|
||||
|
||||
/**
|
||||
* Migrate controller
|
||||
*
|
||||
* @author ryanwang
|
||||
* @date 2019-10-29
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/admin/migrations")
|
||||
public class MigrateController {
|
||||
|
||||
private final MigrateService migrateService;
|
||||
|
||||
public MigrateController(MigrateService migrateService) {
|
||||
this.migrateService = migrateService;
|
||||
}
|
||||
|
||||
@PostMapping("halo_v0_4_4")
|
||||
@ApiOperation("Migrate from Halo 0.4.4")
|
||||
public void migrateHaloOldVersion(@RequestPart("file") MultipartFile file) {
|
||||
migrateService.migrate(file, MigrateType.OLD_VERSION);
|
||||
}
|
||||
|
||||
@PostMapping("wordpress")
|
||||
@ApiOperation("Migrate from WordPress")
|
||||
public void migrateWordPress(@RequestPart("file") MultipartFile file) {
|
||||
migrateService.migrate(file, MigrateType.WORDPRESS);
|
||||
}
|
||||
|
||||
@PostMapping("cnblogs")
|
||||
@ApiOperation("Migrate from cnblogs")
|
||||
public void migrateCnBlogs(@RequestPart("file") MultipartFile file) {
|
||||
migrateService.migrate(file, MigrateType.CNBLOGS);
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ import run.halo.app.service.RecoveryService;
|
|||
* @author johnniang
|
||||
* @date 19-4-26
|
||||
*/
|
||||
@Deprecated
|
||||
@RestController
|
||||
@RequestMapping("/api/admin/recoveries")
|
||||
public class RecoveryController {
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package run.halo.app.handler.migrate;
|
||||
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import run.halo.app.model.enums.MigrateType;
|
||||
|
||||
/**
|
||||
* Cnblogs(https://cnblogs.com) migrate handler.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @date 2019-10-30
|
||||
*/
|
||||
public class CnBlogsMigrateHandler implements MigrateHandler {
|
||||
|
||||
@Override
|
||||
public void migrate(MultipartFile file) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportType(MigrateType type) {
|
||||
return MigrateType.CNBLOGS.equals(type);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package run.halo.app.handler.migrate;
|
||||
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import run.halo.app.model.enums.MigrateType;
|
||||
|
||||
/**
|
||||
* Migrate handler interface.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @date 2019-10-28
|
||||
*/
|
||||
public interface MigrateHandler {
|
||||
|
||||
/**
|
||||
* Migrate
|
||||
*
|
||||
* @param file multipart file must not be null
|
||||
*/
|
||||
void migrate(@NonNull MultipartFile file);
|
||||
|
||||
/**
|
||||
* Checks if the given type is supported.
|
||||
*
|
||||
* @param type migrate type
|
||||
* @return true if supported; false or else
|
||||
*/
|
||||
boolean supportType(@Nullable MigrateType type);
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package run.halo.app.handler.migrate;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import run.halo.app.exception.FileOperationException;
|
||||
import run.halo.app.model.enums.MigrateType;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* Migrate handler manager.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @date 2019-10-28
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class MigrateHandlers {
|
||||
|
||||
/**
|
||||
* Migrate handler container.
|
||||
*/
|
||||
private final Collection<MigrateHandler> migrateHandlers = new LinkedList<>();
|
||||
|
||||
public MigrateHandlers(ApplicationContext applicationContext) {
|
||||
// Add all migrate handler
|
||||
addFileHandlers(applicationContext.getBeansOfType(MigrateHandler.class).values());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public void upload(@NonNull MultipartFile file, @NonNull MigrateType migrateType) {
|
||||
Assert.notNull(file, "Multipart file must not be null");
|
||||
Assert.notNull(migrateType, "Migrate type must not be null");
|
||||
|
||||
for (MigrateHandler migrateHandler : migrateHandlers) {
|
||||
if (migrateHandler.supportType(migrateType)) {
|
||||
migrateHandler.migrate(file);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new FileOperationException("No available migrate handler to migrate the file").setErrorData(migrateType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds migrate handlers.
|
||||
*
|
||||
* @param migrateHandlers migrate handler collection
|
||||
* @return current migrate handlers
|
||||
*/
|
||||
@NonNull
|
||||
private MigrateHandlers addFileHandlers(@Nullable Collection<MigrateHandler> migrateHandlers) {
|
||||
if (!CollectionUtils.isEmpty(migrateHandlers)) {
|
||||
this.migrateHandlers.addAll(migrateHandlers);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,693 @@
|
|||
package run.halo.app.handler.migrate;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import run.halo.app.exception.ServiceException;
|
||||
import run.halo.app.model.entity.*;
|
||||
import run.halo.app.model.enums.AttachmentType;
|
||||
import run.halo.app.model.enums.CommentStatus;
|
||||
import run.halo.app.model.enums.MigrateType;
|
||||
import run.halo.app.model.enums.PostStatus;
|
||||
import run.halo.app.service.*;
|
||||
import run.halo.app.utils.BeanUtils;
|
||||
import run.halo.app.utils.JsonUtils;
|
||||
import run.halo.app.utils.ServiceUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Old version(0.4.4) migrate handler
|
||||
*
|
||||
* @author ryanwang
|
||||
* @author johnniang
|
||||
* @date 2019-10-28
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@SuppressWarnings("unchecked")
|
||||
public class OldVersionMigrateHandler implements MigrateHandler {
|
||||
|
||||
private final AttachmentService attachmentService;
|
||||
|
||||
private final PostService postService;
|
||||
|
||||
private final LinkService linkService;
|
||||
|
||||
private final MenuService menuService;
|
||||
|
||||
private final CategoryService categoryService;
|
||||
|
||||
private final TagService tagService;
|
||||
|
||||
private final PostCommentService postCommentService;
|
||||
|
||||
private final SheetCommentService sheetCommentService;
|
||||
|
||||
private final SheetService sheetService;
|
||||
|
||||
private final PhotoService photoService;
|
||||
|
||||
private final PostCategoryService postCategoryService;
|
||||
|
||||
private final PostTagService postTagService;
|
||||
|
||||
public OldVersionMigrateHandler(AttachmentService attachmentService,
|
||||
PostService postService,
|
||||
LinkService linkService,
|
||||
MenuService menuService,
|
||||
CategoryService categoryService,
|
||||
TagService tagService,
|
||||
PostCommentService postCommentService,
|
||||
SheetCommentService sheetCommentService,
|
||||
SheetService sheetService,
|
||||
PhotoService photoService,
|
||||
PostCategoryService postCategoryService,
|
||||
PostTagService postTagService) {
|
||||
this.attachmentService = attachmentService;
|
||||
this.postService = postService;
|
||||
this.linkService = linkService;
|
||||
this.menuService = menuService;
|
||||
this.categoryService = categoryService;
|
||||
this.tagService = tagService;
|
||||
this.postCommentService = postCommentService;
|
||||
this.sheetCommentService = sheetCommentService;
|
||||
this.sheetService = sheetService;
|
||||
this.photoService = photoService;
|
||||
this.postCategoryService = postCategoryService;
|
||||
this.postTagService = postTagService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void migrate(MultipartFile file) {
|
||||
// Get migration content
|
||||
try {
|
||||
String migrationContent = FileCopyUtils.copyToString(new InputStreamReader(file.getInputStream(), StandardCharsets.UTF_8));
|
||||
|
||||
Object migrationObject = JsonUtils.jsonToObject(migrationContent, Object.class);
|
||||
|
||||
if (migrationObject instanceof Map) {
|
||||
Map<String, Object> migrationMap = (Map<String, Object>) migrationObject;
|
||||
|
||||
// Handle attachments
|
||||
List<Attachment> attachments = handleAttachments(migrationMap.get("attachments"));
|
||||
|
||||
log.debug("Migrated attachments: [{}]", attachments);
|
||||
|
||||
// Handle links
|
||||
List<Link> links = handleLinks(migrationMap.get("links"));
|
||||
|
||||
log.debug("Migrated links: [{}]", links);
|
||||
|
||||
// Handle galleries
|
||||
List<Photo> photos = handleGalleries(migrationMap.get("galleries"));
|
||||
|
||||
log.debug("Migrated photos: [{}]", photos);
|
||||
|
||||
// Handle menus
|
||||
List<Menu> menus = handleMenus(migrationMap.get("menus"));
|
||||
|
||||
log.debug("Migrated menus: [{}]", menus);
|
||||
|
||||
// Handle posts
|
||||
List<BasePost> posts = handleBasePosts(migrationMap.get("posts"));
|
||||
|
||||
log.debug("Migrated posts: [{}]", posts);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new ServiceException("备份文件 " + file.getOriginalFilename() + " 读取失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
private List<BasePost> handleBasePosts(@Nullable Object postsObject) {
|
||||
if (!(postsObject instanceof List)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Object> postObjectList = (List<Object>) postsObject;
|
||||
|
||||
List<BasePost> result = new LinkedList<>();
|
||||
|
||||
postObjectList.forEach(postObject -> {
|
||||
if (!(postObject instanceof Map)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> postMap = (Map<String, Object>) postObject;
|
||||
|
||||
BasePost post = new BasePost();
|
||||
post.setTitle(postMap.getOrDefault("postTitle", "").toString());
|
||||
post.setUrl(postMap.getOrDefault("postUrl", "").toString());
|
||||
post.setOriginalContent(postMap.getOrDefault("postContentMd", "").toString());
|
||||
post.setFormatContent(postMap.getOrDefault("postContent", "").toString());
|
||||
post.setSummary(postMap.getOrDefault("postSummary", "").toString());
|
||||
post.setThumbnail(postMap.getOrDefault("postThumbnail", "").toString());
|
||||
post.setVisits(getLongOrDefault(postMap.getOrDefault("postViews", "").toString(), 0L));
|
||||
post.setDisallowComment(false);
|
||||
post.setTemplate(postMap.getOrDefault("customTpl", "").toString());
|
||||
|
||||
// Set disallow comment
|
||||
Integer allowComment = getIntegerOrDefault(postMap.getOrDefault("allowComment", "1").toString(), 1);
|
||||
if (allowComment != 1) {
|
||||
post.setDisallowComment(true);
|
||||
}
|
||||
|
||||
// Set create time
|
||||
Long createTime = getLongOrDefault(postMap.getOrDefault("postDate", "").toString(), 0L);
|
||||
if (createTime != 0L) {
|
||||
post.setCreateTime(new Date(createTime));
|
||||
}
|
||||
|
||||
// Set update time
|
||||
Long updateTime = getLongOrDefault(postMap.getOrDefault("postUpdate", "").toString(), 0L);
|
||||
if (updateTime != 0L) {
|
||||
post.setUpdateTime(new Date(updateTime));
|
||||
}
|
||||
|
||||
// Set status (default draft)
|
||||
Integer postStatus = getIntegerOrDefault(postMap.getOrDefault("postStatus", "").toString(), 1);
|
||||
if (postStatus == 0) {
|
||||
post.setStatus(PostStatus.PUBLISHED);
|
||||
} else if (postStatus == 1) {
|
||||
post.setStatus(PostStatus.DRAFT);
|
||||
} else {
|
||||
post.setStatus(PostStatus.RECYCLE);
|
||||
}
|
||||
|
||||
String postType = postMap.getOrDefault("postType", "post").toString();
|
||||
|
||||
try {
|
||||
if ("post".equalsIgnoreCase(postType)) {
|
||||
// Handle post
|
||||
result.add(handlePost(post, postMap));
|
||||
} else {
|
||||
// Handle page
|
||||
result.add(handleSheet(post, postMap));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to migrate a post or sheet", e);
|
||||
// Ignore this exception
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Post handlePost(@NonNull BasePost basePost, @NonNull Map<String, Object> postMap) {
|
||||
Post post = BeanUtils.transformFrom(basePost, Post.class);
|
||||
|
||||
// Create it
|
||||
Post createdPost = postService.createOrUpdateBy(post);
|
||||
|
||||
Object commentsObject = postMap.get("comments");
|
||||
Object categoriesObject = postMap.get("categories");
|
||||
Object tagsObject = postMap.get("tags");
|
||||
// Handle comments
|
||||
List<BaseComment> baseComments = handleComment(commentsObject, createdPost.getId());
|
||||
|
||||
// Handle categories
|
||||
List<Category> categories = handleCategories(categoriesObject, createdPost.getId());
|
||||
|
||||
log.debug("Migrated categories of post [{}]: [{}]", categories, createdPost.getId());
|
||||
|
||||
// Handle tags
|
||||
List<Tag> tags = handleTags(tagsObject, createdPost.getId());
|
||||
|
||||
log.debug("Migrated tags of post [{}]: [{}]", tags, createdPost.getId());
|
||||
|
||||
List<PostComment> postComments = baseComments.stream()
|
||||
.map(baseComment -> BeanUtils.transformFrom(baseComment, PostComment.class))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
try {
|
||||
// Build virtual comment
|
||||
PostComment virtualPostComment = new PostComment();
|
||||
virtualPostComment.setId(0L);
|
||||
// Create comments
|
||||
createPostCommentRecursively(virtualPostComment, postComments);
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to create post comments for post with id " + createdPost.getId(), e);
|
||||
// Ignore this exception
|
||||
}
|
||||
|
||||
return createdPost;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Sheet handleSheet(@NonNull BasePost basePost, @NonNull Map<String, Object> postMap) {
|
||||
Sheet sheet = BeanUtils.transformFrom(basePost, Sheet.class);
|
||||
|
||||
// Create it
|
||||
Sheet createdSheet = sheetService.createOrUpdateBy(sheet);
|
||||
|
||||
Object commentsObject = postMap.get("comments");
|
||||
// Handle comments
|
||||
List<BaseComment> baseComments = handleComment(commentsObject, createdSheet.getId());
|
||||
|
||||
List<SheetComment> sheetComments = baseComments.stream()
|
||||
.map(baseComment -> BeanUtils.transformFrom(baseComment, SheetComment.class))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Create comments
|
||||
try {
|
||||
// Build virtual comment
|
||||
SheetComment virtualSheetComment = new SheetComment();
|
||||
virtualSheetComment.setId(0L);
|
||||
// Create comments
|
||||
createSheetCommentRecursively(virtualSheetComment, sheetComments);
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to create sheet comments for sheet with id " + createdSheet.getId(), e);
|
||||
// Ignore this exception
|
||||
}
|
||||
|
||||
return createdSheet;
|
||||
}
|
||||
|
||||
|
||||
private void createPostCommentRecursively(@NonNull final PostComment parentComment, List<PostComment> postComments) {
|
||||
Long oldParentId = parentComment.getId();
|
||||
|
||||
// Create parent
|
||||
if (!ServiceUtils.isEmptyId(parentComment.getId())) {
|
||||
PostComment createdComment = postCommentService.create(parentComment);
|
||||
log.debug("Created post comment: [{}]", createdComment);
|
||||
parentComment.setId(createdComment.getId());
|
||||
}
|
||||
|
||||
if (CollectionUtils.isEmpty(postComments)) {
|
||||
return;
|
||||
}
|
||||
// Get all children
|
||||
List<PostComment> children = postComments.stream()
|
||||
.filter(postComment -> Objects.equals(oldParentId, postComment.getParentId()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
|
||||
// Set parent id again
|
||||
children.forEach(postComment -> postComment.setParentId(parentComment.getId()));
|
||||
|
||||
// Remove children
|
||||
postComments.removeAll(children);
|
||||
|
||||
// Create children recursively
|
||||
children.forEach(childComment -> createPostCommentRecursively(childComment, postComments));
|
||||
}
|
||||
|
||||
private void createSheetCommentRecursively(@NonNull final SheetComment parentComment, List<SheetComment> sheetComments) {
|
||||
Long oldParentId = parentComment.getId();
|
||||
// Create parent
|
||||
if (!ServiceUtils.isEmptyId(parentComment.getId())) {
|
||||
SheetComment createComment = sheetCommentService.create(parentComment);
|
||||
parentComment.setId(createComment.getId());
|
||||
}
|
||||
|
||||
if (CollectionUtils.isEmpty(sheetComments)) {
|
||||
return;
|
||||
}
|
||||
// Get all children
|
||||
List<SheetComment> children = sheetComments.stream()
|
||||
.filter(sheetComment -> Objects.equals(oldParentId, sheetComment.getParentId()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Set parent id again
|
||||
children.forEach(postComment -> postComment.setParentId(parentComment.getId()));
|
||||
|
||||
// Remove children
|
||||
sheetComments.removeAll(children);
|
||||
|
||||
// Create children recursively
|
||||
children.forEach(childComment -> createSheetCommentRecursively(childComment, sheetComments));
|
||||
}
|
||||
|
||||
private List<BaseComment> handleComment(@Nullable Object commentsObject, @NonNull Integer postId) {
|
||||
Assert.notNull(postId, "Post id must not be null");
|
||||
|
||||
if (!(commentsObject instanceof List)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Object> commentObjectList = (List<Object>) commentsObject;
|
||||
|
||||
List<BaseComment> result = new LinkedList<>();
|
||||
|
||||
commentObjectList.forEach(commentObject -> {
|
||||
if (!(commentObject instanceof Map)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> commentMap = (Map<String, Object>) commentObject;
|
||||
|
||||
BaseComment baseComment = new BaseComment();
|
||||
baseComment.setId(getLongOrDefault(commentMap.getOrDefault("commentId", "").toString(), null));
|
||||
baseComment.setAuthor(commentMap.getOrDefault("commentAuthor", "").toString());
|
||||
baseComment.setEmail(commentMap.getOrDefault("commentAuthorEmail", "").toString());
|
||||
baseComment.setIpAddress(commentMap.getOrDefault("commentAuthorIp", "").toString());
|
||||
baseComment.setAuthorUrl(commentMap.getOrDefault("commentAuthorUrl", "").toString());
|
||||
baseComment.setGravatarMd5(commentMap.getOrDefault("commentAuthorAvatarMd5", "").toString());
|
||||
baseComment.setContent(commentMap.getOrDefault("commentContent", "").toString());
|
||||
baseComment.setUserAgent(commentMap.getOrDefault("commentAgent", "").toString());
|
||||
baseComment.setIsAdmin(getBooleanOrDefault(commentMap.getOrDefault("isAdmin", "").toString(), false));
|
||||
baseComment.setPostId(postId);
|
||||
baseComment.setParentId(getLongOrDefault(commentMap.getOrDefault("commentParent", "").toString(), 0L));
|
||||
|
||||
// Set create date
|
||||
Long createTimestamp = getLongOrDefault(commentMap.getOrDefault("createDate", "").toString(), System.currentTimeMillis());
|
||||
baseComment.setCreateTime(new Date(createTimestamp));
|
||||
|
||||
Integer commentStatus = getIntegerOrDefault(commentMap.getOrDefault("commentStatus", "").toString(), 1);
|
||||
if (commentStatus == 0) {
|
||||
baseComment.setStatus(CommentStatus.PUBLISHED);
|
||||
} else if (commentStatus == 1) {
|
||||
baseComment.setStatus(CommentStatus.AUDITING);
|
||||
} else {
|
||||
baseComment.setStatus(CommentStatus.RECYCLE);
|
||||
}
|
||||
|
||||
result.add(baseComment);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private List<Category> handleCategories(@Nullable Object categoriesObject, @NonNull Integer postId) {
|
||||
Assert.notNull(postId, "Post id must not be null");
|
||||
|
||||
if (!(categoriesObject instanceof List)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Object> categoryObjectList = (List<Object>) categoriesObject;
|
||||
|
||||
List<Category> result = new LinkedList<>();
|
||||
|
||||
categoryObjectList.forEach(categoryObject -> {
|
||||
if (!(categoryObject instanceof Map)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> categoryMap = (Map<String, Object>) categoryObject;
|
||||
|
||||
String slugName = categoryMap.getOrDefault("cateUrl", "").toString();
|
||||
|
||||
Category category = categoryService.getBySlugName(slugName);
|
||||
|
||||
if (null == category) {
|
||||
category = new Category();
|
||||
category.setName(categoryMap.getOrDefault("cateName", "").toString());
|
||||
category.setSlugName(slugName);
|
||||
category.setDescription(categoryMap.getOrDefault("cateDesc", "").toString());
|
||||
category = categoryService.create(category);
|
||||
}
|
||||
|
||||
PostCategory postCategory = new PostCategory();
|
||||
postCategory.setCategoryId(category.getId());
|
||||
postCategory.setPostId(postId);
|
||||
postCategoryService.create(postCategory);
|
||||
|
||||
try {
|
||||
result.add(category);
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to migrate a category", e);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private List<Tag> handleTags(@Nullable Object tagsObject, @NonNull Integer postId) {
|
||||
Assert.notNull(postId, "Post id must not be null");
|
||||
|
||||
if (!(tagsObject instanceof List)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Object> tagObjectList = (List<Object>) tagsObject;
|
||||
|
||||
List<Tag> result = new LinkedList<>();
|
||||
|
||||
tagObjectList.forEach(tagObject -> {
|
||||
if (!(tagObject instanceof Map)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> tagMap = (Map<String, Object>) tagObject;
|
||||
|
||||
String slugName = tagMap.getOrDefault("tagUrl", "").toString();
|
||||
|
||||
Tag tag = tagService.getBySlugName(slugName);
|
||||
|
||||
if (null == tag) {
|
||||
tag = new Tag();
|
||||
tag.setName(tagMap.getOrDefault("tagName", "").toString());
|
||||
tag.setSlugName(slugName);
|
||||
tag = tagService.create(tag);
|
||||
}
|
||||
|
||||
PostTag postTag = new PostTag();
|
||||
postTag.setTagId(tag.getId());
|
||||
postTag.setPostId(postId);
|
||||
postTagService.create(postTag);
|
||||
|
||||
try {
|
||||
result.add(tag);
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to migrate a tag", e);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private List<Menu> handleMenus(@Nullable Object menusObject) {
|
||||
if (!(menusObject instanceof List)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Object> menuObjectList = (List<Object>) menusObject;
|
||||
|
||||
List<Menu> result = new LinkedList<>();
|
||||
|
||||
menuObjectList.forEach(menuObject -> {
|
||||
if (!(menuObject instanceof Map)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> menuMap = (Map<String, Object>) menuObject;
|
||||
|
||||
Menu menu = new Menu();
|
||||
|
||||
menu.setName(menuMap.getOrDefault("menuName", "").toString());
|
||||
menu.setUrl(menuMap.getOrDefault("menuUrl", "").toString());
|
||||
// Set priority
|
||||
String sortString = menuMap.getOrDefault("menuSort", "0").toString();
|
||||
menu.setPriority(getIntegerOrDefault(sortString, 0));
|
||||
menu.setTarget(menuMap.getOrDefault("menuTarget", "_self").toString());
|
||||
menu.setIcon(menuMap.getOrDefault("menuIcon", "").toString());
|
||||
|
||||
try {
|
||||
// Create menu
|
||||
result.add(menuService.create(menu));
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to migrate a menu", e);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private List<Photo> handleGalleries(@Nullable Object galleriesObject) {
|
||||
if (!(galleriesObject instanceof List)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Object> galleryObjectList = (List<Object>) galleriesObject;
|
||||
|
||||
List<Photo> result = new LinkedList<>();
|
||||
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
||||
|
||||
galleryObjectList.forEach(galleryObject -> {
|
||||
if (!(galleriesObject instanceof Map)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> galleryMap = (Map<String, Object>) galleryObject;
|
||||
|
||||
Photo photo = new Photo();
|
||||
photo.setName(galleryMap.getOrDefault("galleryName", "").toString());
|
||||
photo.setDescription(galleryMap.getOrDefault("galleryDesc", "").toString());
|
||||
photo.setLocation(galleryMap.getOrDefault("galleryLocation", "").toString());
|
||||
photo.setThumbnail(galleryMap.getOrDefault("galleryThumbnailUrl", "").toString());
|
||||
photo.setUrl(galleryMap.getOrDefault("galleryUrl", "").toString());
|
||||
|
||||
Object galleryDate = galleryMap.get("galleryDate");
|
||||
|
||||
try {
|
||||
if (galleryDate != null) {
|
||||
photo.setTakeTime(dateFormat.parse(galleryDate.toString()));
|
||||
}
|
||||
|
||||
// Create it
|
||||
result.add(photoService.create(photo));
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to create a photo", e);
|
||||
// Ignore this exception
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private List<Link> handleLinks(@Nullable Object linksObject) {
|
||||
if (!(linksObject instanceof List)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Object> linkObjectList = (List<Object>) linksObject;
|
||||
|
||||
List<Link> result = new LinkedList<>();
|
||||
|
||||
linkObjectList.forEach(linkObject -> {
|
||||
if (!(linkObject instanceof Map)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> linkMap = (Map<String, Object>) linkObject;
|
||||
|
||||
Link link = new Link();
|
||||
|
||||
link.setName(linkMap.getOrDefault("linkName", "").toString());
|
||||
link.setUrl(linkMap.getOrDefault("linkUrl", "").toString());
|
||||
link.setLogo(linkMap.getOrDefault("linkPic", "").toString());
|
||||
link.setDescription(linkMap.getOrDefault("linkDesc", "").toString());
|
||||
try {
|
||||
result.add(linkService.create(link));
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to migrate a link", e);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private List<Attachment> handleAttachments(@Nullable Object attachmentsObject) {
|
||||
if (!(attachmentsObject instanceof List)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Object> attachmentObjectList = (List<Object>) attachmentsObject;
|
||||
|
||||
List<Attachment> result = new LinkedList<>();
|
||||
|
||||
attachmentObjectList.forEach(attachmentObject -> {
|
||||
if (!(attachmentObject instanceof Map)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> attachmentMap = (Map<String, Object>) attachmentObject;
|
||||
// Convert to attachment param
|
||||
Attachment attachment = new Attachment();
|
||||
|
||||
attachment.setName(attachmentMap.getOrDefault("attachName", "").toString());
|
||||
attachment.setPath(StringUtils.removeStart(attachmentMap.getOrDefault("attachPath", "").toString(), "/"));
|
||||
attachment.setThumbPath(attachmentMap.getOrDefault("attachSmallPath", "").toString());
|
||||
attachment.setMediaType(attachmentMap.getOrDefault("attachType", "").toString());
|
||||
attachment.setSuffix(StringUtils.removeStart(attachmentMap.getOrDefault("attachSuffix", "").toString(), "."));
|
||||
attachment.setSize(0L);
|
||||
|
||||
if (StringUtils.startsWith(attachment.getPath(), "/upload")) {
|
||||
// Set this key
|
||||
attachment.setFileKey(attachment.getPath());
|
||||
}
|
||||
|
||||
// Set location
|
||||
String attachLocation = attachmentMap.getOrDefault("attachLocation", "").toString();
|
||||
if (StringUtils.equalsIgnoreCase(attachLocation, "qiniu")) {
|
||||
attachment.setType(AttachmentType.QNYUN);
|
||||
} else if (StringUtils.equalsIgnoreCase(attachLocation, "upyun")) {
|
||||
attachment.setType(AttachmentType.UPYUN);
|
||||
} else {
|
||||
attachment.setType(AttachmentType.LOCAL);
|
||||
}
|
||||
|
||||
try {
|
||||
// Save to db
|
||||
Attachment createdAttachment = attachmentService.create(attachment);
|
||||
|
||||
result.add(createdAttachment);
|
||||
|
||||
} catch (Exception e) {
|
||||
// Ignore this exception
|
||||
log.warn("Failed to migrate an attachment " + attachment.getPath(), e);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Integer getIntegerOrDefault(@Nullable String numberString, @Nullable Integer defaultNumber) {
|
||||
try {
|
||||
return Integer.valueOf(numberString);
|
||||
} catch (Exception e) {
|
||||
// Ignore this exception
|
||||
return defaultNumber;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Long getLongOrDefault(@Nullable String numberString, @Nullable Long defaultNumber) {
|
||||
try {
|
||||
return Long.valueOf(numberString);
|
||||
} catch (Exception e) {
|
||||
// Ignore this exception
|
||||
return defaultNumber;
|
||||
}
|
||||
}
|
||||
|
||||
private Boolean getBooleanOrDefault(@Nullable String boolString, @Nullable Boolean defaultValue) {
|
||||
if (StringUtils.equalsIgnoreCase(boolString, "0")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (StringUtils.equalsIgnoreCase(boolString, "1")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (StringUtils.equalsIgnoreCase(boolString, "true")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (StringUtils.equalsIgnoreCase(boolString, "false")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportType(MigrateType type) {
|
||||
return MigrateType.OLD_VERSION.equals(type);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,229 @@
|
|||
package run.halo.app.handler.migrate;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dom4j.Element;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import run.halo.app.exception.ServiceException;
|
||||
import run.halo.app.model.entity.BasePost;
|
||||
import run.halo.app.model.entity.Category;
|
||||
import run.halo.app.model.entity.Post;
|
||||
import run.halo.app.model.entity.Tag;
|
||||
import run.halo.app.model.enums.MigrateType;
|
||||
import run.halo.app.service.*;
|
||||
import run.halo.app.utils.MarkdownUtils;
|
||||
import run.halo.app.utils.WordPressMigrateUtils;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* WordPress migrate handler
|
||||
*
|
||||
* @author ryanwang
|
||||
* @date 2019-10-28
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@SuppressWarnings("unchecked")
|
||||
public class WordPressMigrateHandler implements MigrateHandler {
|
||||
|
||||
private final AttachmentService attachmentService;
|
||||
|
||||
private final PostService postService;
|
||||
|
||||
private final LinkService linkService;
|
||||
|
||||
private final MenuService menuService;
|
||||
|
||||
private final CategoryService categoryService;
|
||||
|
||||
private final TagService tagService;
|
||||
|
||||
private final PostCommentService postCommentService;
|
||||
|
||||
private final SheetCommentService sheetCommentService;
|
||||
|
||||
private final SheetService sheetService;
|
||||
|
||||
private final PhotoService photoService;
|
||||
|
||||
private final PostCategoryService postCategoryService;
|
||||
|
||||
private final PostTagService postTagService;
|
||||
|
||||
public WordPressMigrateHandler(AttachmentService attachmentService,
|
||||
PostService postService,
|
||||
LinkService linkService,
|
||||
MenuService menuService,
|
||||
CategoryService categoryService,
|
||||
TagService tagService,
|
||||
PostCommentService postCommentService,
|
||||
SheetCommentService sheetCommentService,
|
||||
SheetService sheetService,
|
||||
PhotoService photoService,
|
||||
PostCategoryService postCategoryService,
|
||||
PostTagService postTagService) {
|
||||
this.attachmentService = attachmentService;
|
||||
this.postService = postService;
|
||||
this.linkService = linkService;
|
||||
this.menuService = menuService;
|
||||
this.categoryService = categoryService;
|
||||
this.tagService = tagService;
|
||||
this.postCommentService = postCommentService;
|
||||
this.sheetCommentService = sheetCommentService;
|
||||
this.sheetService = sheetService;
|
||||
this.photoService = photoService;
|
||||
this.postCategoryService = postCategoryService;
|
||||
this.postTagService = postTagService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void migrate(MultipartFile file) {
|
||||
try {
|
||||
String migrationContent = FileCopyUtils.copyToString(new InputStreamReader(file.getInputStream(), StandardCharsets.UTF_8));
|
||||
Element rootElement = WordPressMigrateUtils.getRootElement(new FileInputStream(migrationContent));
|
||||
Map<String, Object> resultSetMapping = WordPressMigrateUtils.getResultSetMapping(rootElement);
|
||||
|
||||
// Handle categories
|
||||
List<Category> categories = handleCategories(resultSetMapping.get("wp:category"));
|
||||
|
||||
// Handle tags
|
||||
List<Tag> tags = handleTags(resultSetMapping.get("wp:tag"));
|
||||
|
||||
// Handle posts
|
||||
List<BasePost> posts = handlePosts(resultSetMapping.get("item"));
|
||||
|
||||
log.debug("Migrated posts: [{}]", posts);
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException("WordPress 导出文件 " + file.getOriginalFilename() + " 读取失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Category> handleCategories(@Nullable Object categoriesObject) {
|
||||
|
||||
if (!(categoriesObject instanceof List)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Object> categoryObjectList = (List<Object>) categoriesObject;
|
||||
|
||||
List<Category> result = new LinkedList<>();
|
||||
|
||||
categoryObjectList.forEach(categoryObject -> {
|
||||
|
||||
if (!(categoryObject instanceof Map)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> categoryMap = (Map<String, Object>) categoryObject;
|
||||
|
||||
String slugName = categoryMap.getOrDefault("wp:category_nicename", "").toString();
|
||||
|
||||
Category category = categoryService.getBySlugName(slugName);
|
||||
|
||||
if (null == category) {
|
||||
category = new Category();
|
||||
category.setName(categoryMap.getOrDefault("wp:cat_name", "").toString());
|
||||
category.setSlugName(slugName);
|
||||
category = categoryService.create(category);
|
||||
}
|
||||
|
||||
try {
|
||||
result.add(category);
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to migrate a category", e);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Tag> handleTags(@Nullable Object tagsObject) {
|
||||
|
||||
if (!(tagsObject instanceof List)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Object> tagObjectList = (List<Object>) tagsObject;
|
||||
|
||||
List<Tag> result = new LinkedList<>();
|
||||
|
||||
tagObjectList.forEach(tagObject -> {
|
||||
if (!(tagObject instanceof Map)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> tagMap = (Map<String, Object>) tagObject;
|
||||
|
||||
String slugName = tagMap.getOrDefault("wp:tag_slug", "").toString();
|
||||
|
||||
Tag tag = tagService.getBySlugName(slugName);
|
||||
|
||||
if (null == tag) {
|
||||
tag = new Tag();
|
||||
tag.setName(tagMap.getOrDefault("wp:tag_name", "").toString());
|
||||
tag.setSlugName(slugName);
|
||||
tag = tagService.create(tag);
|
||||
}
|
||||
|
||||
try {
|
||||
result.add(tag);
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to migrate a tag", e);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private List<BasePost> handlePosts(@Nullable Object postsObject) {
|
||||
if (!(postsObject instanceof List)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Object> postObjectList = (List<Object>) postsObject;
|
||||
|
||||
List<BasePost> result = new LinkedList<>();
|
||||
|
||||
postObjectList.forEach(postObject -> {
|
||||
if (!(postObject instanceof Map)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> postMap = (Map<String, Object>) postObject;
|
||||
|
||||
BasePost post = new BasePost();
|
||||
post.setTitle(postMap.getOrDefault("title", "").toString());
|
||||
post.setUrl(postMap.getOrDefault("wp:post_name", "").toString());
|
||||
post.setOriginalContent(MarkdownUtils.renderMarkdown(postMap.getOrDefault("content:encoded", "").toString()));
|
||||
post.setFormatContent(postMap.getOrDefault("content:encoded", "").toString());
|
||||
post.setSummary(postMap.getOrDefault("excerpt:encoded", "").toString());
|
||||
|
||||
String url = postMap.getOrDefault("wp:post_name", "").toString();
|
||||
|
||||
Post temp = postService.getByUrl(url);
|
||||
|
||||
if (temp != null) {
|
||||
post.setUrl(post.getUrl() + "_1");
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportType(MigrateType type) {
|
||||
return MigrateType.WORDPRESS.equals(type);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package run.halo.app.model.enums;
|
||||
|
||||
/**
|
||||
* Migrate type.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @date : 2019-03-12
|
||||
*/
|
||||
public enum MigrateType implements ValueEnum<Integer> {
|
||||
|
||||
/**
|
||||
* Halo version 0.4.4
|
||||
*/
|
||||
OLD_VERSION(0),
|
||||
|
||||
/**
|
||||
* WordPress
|
||||
*/
|
||||
WORDPRESS(1),
|
||||
|
||||
/**
|
||||
* cnblogs.com
|
||||
*/
|
||||
CNBLOGS(2);
|
||||
|
||||
private Integer value;
|
||||
|
||||
MigrateType(Integer value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get enum value.
|
||||
*
|
||||
* @return enum value
|
||||
*/
|
||||
@Override
|
||||
public Integer getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@ import run.halo.app.model.enums.PostStatus;
|
|||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Date;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -62,7 +63,7 @@ public class PostParam implements InputConverter<Post> {
|
|||
@Override
|
||||
public Post convertTo() {
|
||||
if (StringUtils.isBlank(url)) {
|
||||
url = title.replace(".","");
|
||||
url = URLEncoder.encode(title.replace(".",""));
|
||||
}
|
||||
if (null == thumbnail) {
|
||||
thumbnail = "";
|
||||
|
@ -74,7 +75,7 @@ public class PostParam implements InputConverter<Post> {
|
|||
@Override
|
||||
public void update(Post post) {
|
||||
if (StringUtils.isBlank(url)) {
|
||||
url = title.replace(".","");
|
||||
url = URLEncoder.encode(title.replace(".",""));
|
||||
}
|
||||
if (null == thumbnail) {
|
||||
thumbnail = "";
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
package run.halo.app.service;
|
||||
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import run.halo.app.model.enums.MigrateType;
|
||||
|
||||
/**
|
||||
* Migrate service interface.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @date 2019-10-29
|
||||
*/
|
||||
public interface MigrateService {
|
||||
|
||||
/**
|
||||
* Migrate.
|
||||
*
|
||||
* @param file multipart file must not be null
|
||||
* @param migrateType migrate type
|
||||
*/
|
||||
void migrate(@NonNull MultipartFile file,@NonNull MigrateType migrateType);
|
||||
}
|
|
@ -9,6 +9,7 @@ import org.springframework.web.multipart.MultipartFile;
|
|||
* @author johnniang
|
||||
* @date 2019-04-26
|
||||
*/
|
||||
@Deprecated
|
||||
public interface RecoveryService {
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
package run.halo.app.service.impl;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import run.halo.app.handler.migrate.MigrateHandlers;
|
||||
import run.halo.app.model.enums.MigrateType;
|
||||
import run.halo.app.service.MigrateService;
|
||||
|
||||
/**
|
||||
* Migrate service implementation.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @date 2019-10-29
|
||||
*/
|
||||
@Service
|
||||
public class MigrateServiceImpl implements MigrateService {
|
||||
|
||||
private final MigrateHandlers migrateHandlers;
|
||||
|
||||
public MigrateServiceImpl(MigrateHandlers migrateHandlers) {
|
||||
this.migrateHandlers = migrateHandlers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void migrate(MultipartFile file, MigrateType migrateType) {
|
||||
Assert.notNull(file, "Multipart file must not be null");
|
||||
Assert.notNull(migrateType, "Migrate type must not be null");
|
||||
|
||||
migrateHandlers.upload(file, migrateType);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
package run.halo.app.utils;
|
||||
|
||||
import org.dom4j.Document;
|
||||
import org.dom4j.Element;
|
||||
import org.dom4j.io.SAXReader;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 解析 WordPress 导出的 XML 数据为 Map 结果集
|
||||
*
|
||||
* @author guqing
|
||||
* @date 2019-10-29 14:49
|
||||
*/
|
||||
public class WordPressMigrateUtils {
|
||||
|
||||
/**
|
||||
* 存储在此集合中的节点名称都会被解析为一个List存储
|
||||
*/
|
||||
private static final List<String> ARRAY_PROPERTY = Arrays.asList("channel", "item", "wp:category", "wp:tag", "wp:term", "wp:postmeta", "wp:comment");
|
||||
|
||||
/**
|
||||
* 根据xml文件对象获取xml的根节点rootElement对象
|
||||
*
|
||||
* @param file xml的文件对象
|
||||
* @return 返回根节点元素
|
||||
*/
|
||||
public static Element getRootElement(File file) {
|
||||
try {
|
||||
SAXReader saxReader = new SAXReader();
|
||||
FileInputStream fileInputStream = new FileInputStream(file);
|
||||
Document document = saxReader.read(fileInputStream);
|
||||
|
||||
return document.getRootElement();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("can not get root element");
|
||||
}
|
||||
}
|
||||
|
||||
public static Element getRootElement(FileInputStream fileInputStream) {
|
||||
try {
|
||||
SAXReader saxReader = new SAXReader();
|
||||
Document document = saxReader.read(fileInputStream);
|
||||
return document.getRootElement();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("can not get root element");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 xml的 映射结果集对象
|
||||
*
|
||||
* @return 返回 xml 解析得到的 Map 映射结果集
|
||||
*/
|
||||
public static Map<String, Object> getResultSetMapping(File file) {
|
||||
Element rootElement = getRootElement(file);
|
||||
return getResultSetMapping(rootElement);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据根节点获取子节点元素集合递归遍历得到 Map 结果集
|
||||
*
|
||||
* @param root xml 的根节点元素对象
|
||||
* @return 返回解析 xml 得到的Map结果集
|
||||
*/
|
||||
public static Map<String, Object> getResultSetMapping(Element root) {
|
||||
Map<String, Object> result = new HashMap<String, Object>();
|
||||
|
||||
try {
|
||||
// 获取根元素的所有子元素
|
||||
List<Element> children = root.elements();
|
||||
|
||||
// 递归遍历将 xml 节点数据解析为 Map 结果集
|
||||
result = transfer2Map(children, null);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("can not transfer xml file to map." + e.getMessage());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归解析 xml,实现 N 层解析
|
||||
*
|
||||
* @param elements 所有子节点元素集,随着递归遍历而改变
|
||||
* @param list 存储中间遍历结果的容器
|
||||
* @return 返回递归完成后的Map结果集映射
|
||||
*/
|
||||
private static Map<String, Object> transfer2Map(List<Element> elements, List<Map<String, Object>> list) {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
for (Element element : elements) {
|
||||
// getName 获取到的节点名称不带名称空间例如 <wp:content/> 获取到 name 为 content
|
||||
String nameWithoutPrefix = element.getName();
|
||||
|
||||
// 需要使用的真是 name 默认等于不带名称空间的,如果名称存在空间则 name 的形式为: 名称空间:名称
|
||||
String name = nameWithoutPrefix;
|
||||
String prefix = element.getNamespace().getPrefix();
|
||||
if (isNotBlack(prefix)) {
|
||||
name = prefix + ":" + nameWithoutPrefix;
|
||||
}
|
||||
|
||||
// 判断节点是否在定义的数组中,如果存在将其创建成 List 集合,否则作为文本
|
||||
if (ARRAY_PROPERTY.contains(name)) {
|
||||
//继续递归循环
|
||||
List<Map<String, Object>> sublist = new ArrayList<Map<String, Object>>();
|
||||
|
||||
Map<String, Object> subMap = transfer2Map(element.elements(), sublist);
|
||||
|
||||
// 根据 key 获取是否已经存在
|
||||
Object object = map.get(name);
|
||||
// 如果存在,合并
|
||||
if (object != null) {
|
||||
List<Map<String, Object>> olist = (List<Map<String, Object>>) object;
|
||||
olist.add(subMap);
|
||||
map.put(name, olist);
|
||||
} else {
|
||||
// 否则直接存入 map
|
||||
map.put(name, sublist);
|
||||
}
|
||||
} else {
|
||||
map.put(name, element.getTextTrim());
|
||||
}
|
||||
}
|
||||
|
||||
// 存入 list 中
|
||||
if (list != null) {
|
||||
list.add(map);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断字符串是否为空,如果是空串返回 {@code true},否则 {@code false}
|
||||
*
|
||||
* @param str 需要进行空串判断的字符串
|
||||
* @return 如果是空串返回 true, 否则返回 false
|
||||
*/
|
||||
private static boolean isNotBlack(String str) {
|
||||
return str != null && str.length() > 0 && str.trim().length() > 0;
|
||||
}
|
||||
}
|
|
@ -1,144 +0,0 @@
|
|||
package run.halo.app.utils;
|
||||
|
||||
import org.dom4j.Document;
|
||||
import org.dom4j.Element;
|
||||
import org.dom4j.io.SAXReader;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 解析wordpress导出的xml文章数据为Map结果集
|
||||
* @author guqing
|
||||
* @date 2019-10-29 14:49
|
||||
*/
|
||||
public class XmlTransferMapUtils {
|
||||
|
||||
/**
|
||||
* 存储在此集合中的节点名称都会被解析为一个List存储
|
||||
*/
|
||||
private static final List<String> ARRAY_PROPERTY = Arrays.asList("channel", "item", "category", "postmeta", "comment");
|
||||
|
||||
/**
|
||||
* 需要解析成map集合的xml文件对象
|
||||
*/
|
||||
private File file;
|
||||
|
||||
public XmlTransferMapUtils(File file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据xml文件对象获取xml的根节点rootElement对象
|
||||
* @param file xml的文件对象
|
||||
* @return 返回根节点元素
|
||||
*/
|
||||
private Element getRootElement(File file) {
|
||||
try {
|
||||
SAXReader saxReader = new SAXReader();
|
||||
FileInputStream fileInputStream = new FileInputStream(file);
|
||||
Document document = saxReader.read(fileInputStream);
|
||||
|
||||
return document.getRootElement();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("can not get root element");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取xml的映射结果集对象
|
||||
* @return 返回xml解析得到的Map映射结果集
|
||||
*/
|
||||
public Map<String, Object> getResultSetMapping() {
|
||||
Element rootElement = getRootElement(file);
|
||||
return getResultSetMapping(rootElement);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据根节点获取子节点元素集合递归遍历得到Map结果集
|
||||
* @param root xml的根节点元素对象
|
||||
* @return 返回解析xml得到的Map结果集
|
||||
*/
|
||||
private Map<String, Object> getResultSetMapping(Element root) {
|
||||
Map<String, Object> result = new HashMap<String, Object>();
|
||||
|
||||
try {
|
||||
// 获取根元素的所有子元素
|
||||
List<Element> children = root.elements();
|
||||
|
||||
//递归遍历将xml节点数据解析为Map结果集
|
||||
result = transfer2Map(children,null);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("can not transfer xml file to map." + e.getMessage());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归解析xml,实现N层解析
|
||||
* @param elements 所有子节点元素集,随着递归遍历而改变
|
||||
* @param list 存储中间遍历结果的容器
|
||||
* @return 返回递归完成后的Map结果集映射
|
||||
*/
|
||||
private Map<String, Object> transfer2Map(List<Element> elements,List<Map<String,Object>> list){
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
for(Element element : elements){
|
||||
// getName获取到的节点名称不带名称空间例如<wp:content/>获取到name为content
|
||||
String nameWithoutPrefix = element.getName();
|
||||
|
||||
// 需要使用的真是name默认等于不带名称空间的,如果名称存在空间则name的形式为: 名称空间:名称
|
||||
String name = nameWithoutPrefix;
|
||||
String preifx = element.getNamespace().getPrefix();
|
||||
if(isNotBlack(preifx)) {
|
||||
name = preifx + ":" +nameWithoutPrefix;
|
||||
}
|
||||
|
||||
//判断节点是否在定义的数组中,如果存在将其创建成List集合,否则作为文本
|
||||
if(ARRAY_PROPERTY.contains(name)) {
|
||||
//继续递归循环
|
||||
List<Map<String,Object>> sublist = new ArrayList<Map<String,Object>>();
|
||||
|
||||
Map<String,Object> subMap = this.transfer2Map(element.elements(), sublist);
|
||||
|
||||
//根据key获取是否已经存在
|
||||
Object object = map.get(name);
|
||||
//如果存在,合并
|
||||
if(object !=null ){
|
||||
List<Map<String,Object>> olist = (List<Map<String,Object>>)object;
|
||||
olist.add(subMap);
|
||||
map.put(name, olist);
|
||||
}else{
|
||||
//否则直接存入map
|
||||
map.put(name, sublist);
|
||||
}
|
||||
}else {
|
||||
//单个值存入map
|
||||
map.put(name, element.getTextTrim());
|
||||
}
|
||||
}
|
||||
|
||||
//存入list中
|
||||
if(list != null) {
|
||||
list.add(map);
|
||||
}
|
||||
|
||||
//返回结果集合
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断字符串是否为空,如果是空串返回 {@code true},否则 {@code false}
|
||||
* @param str 需要进行空串判断的字符串
|
||||
* @return 如果是空串返回true,否则返回false
|
||||
*/
|
||||
private boolean isNotBlack(String str) {
|
||||
if(str != null && str.length() > 0 && str.trim().length() > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue