mirror of https://github.com/halo-dev/halo
Merge branch 'static-page' into dev
commit
f975a20fea
|
@ -33,6 +33,7 @@ public class Application extends SpringBootServletInitializer {
|
|||
|
||||
// Run application
|
||||
context = SpringApplication.run(Application.class, args);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
package run.halo.app.controller.admin.api;
|
||||
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import run.halo.app.service.DataProcessService;
|
||||
import run.halo.app.service.ThemeSettingService;
|
||||
|
||||
/**
|
||||
* @author ryanwang
|
||||
* @date 2019-12-29
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/admin/data/process")
|
||||
public class DataProcessController {
|
||||
|
||||
private final DataProcessService dataProcessService;
|
||||
|
||||
private final ThemeSettingService themeSettingService;
|
||||
|
||||
public DataProcessController(DataProcessService dataProcessService,
|
||||
ThemeSettingService themeSettingService) {
|
||||
this.dataProcessService = dataProcessService;
|
||||
this.themeSettingService = themeSettingService;
|
||||
}
|
||||
|
||||
@PutMapping("url/replace")
|
||||
@ApiOperation("Replace url in all table.")
|
||||
public void replaceUrl(@RequestParam("oldUrl") String oldUrl,
|
||||
@RequestParam("newUrl") String newUrl) {
|
||||
dataProcessService.replaceAllUrl(oldUrl, newUrl);
|
||||
}
|
||||
|
||||
@DeleteMapping("themes/settings/inactivated")
|
||||
@ApiOperation("Delete inactivated theme settings.")
|
||||
public void deleteInactivatedThemeSettings() {
|
||||
themeSettingService.deleteInactivated();
|
||||
}
|
||||
|
||||
@DeleteMapping("tags/unused")
|
||||
@ApiOperation("Delete unused tags")
|
||||
public void deleteUnusedTags() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@DeleteMapping("categories/unused")
|
||||
@ApiOperation("Delete unused categories")
|
||||
public void deleteUnusedCategories() {
|
||||
// TODO
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
package run.halo.app.controller.admin.api;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import run.halo.app.model.properties.NetlifyStaticDeployProperties;
|
||||
import run.halo.app.model.support.StaticPageFile;
|
||||
import run.halo.app.service.OptionService;
|
||||
import run.halo.app.service.StaticPageService;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Static page controller.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @date 2019-12-25
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/admin/static_page")
|
||||
public class StaticPageController {
|
||||
|
||||
private final static String DEPLOY_API = "https://api.netlify.com/api/v1/sites/%s/deploys";
|
||||
|
||||
private final OptionService optionService;
|
||||
|
||||
private final RestTemplate httpsRestTemplate;
|
||||
|
||||
private final StaticPageService staticPageService;
|
||||
|
||||
public StaticPageController(StaticPageService staticPageService,
|
||||
OptionService optionService,
|
||||
RestTemplate httpsRestTemplate) {
|
||||
this.staticPageService = staticPageService;
|
||||
this.optionService = optionService;
|
||||
this.httpsRestTemplate = httpsRestTemplate;
|
||||
|
||||
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
|
||||
mappingJackson2HttpMessageConverter.setSupportedMediaTypes(Collections.singletonList(MediaType.ALL));
|
||||
this.httpsRestTemplate.getMessageConverters().add(mappingJackson2HttpMessageConverter);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
@ApiOperation("List static page files.")
|
||||
public List<StaticPageFile> list() {
|
||||
return staticPageService.listFile();
|
||||
}
|
||||
|
||||
@GetMapping("generate")
|
||||
@ApiOperation("Generate static page files.")
|
||||
public void generate() {
|
||||
staticPageService.generate();
|
||||
}
|
||||
|
||||
@PostMapping("deploy")
|
||||
@ApiOperation("Deploy static page to remove platform")
|
||||
public void deploy() {
|
||||
staticPageService.deploy();
|
||||
}
|
||||
|
||||
@GetMapping("netlify")
|
||||
public void testNetlify() throws FileNotFoundException {
|
||||
String domain = optionService.getByPropertyOfNonNull(NetlifyStaticDeployProperties.NETLIFY_DOMAIN).toString();
|
||||
String siteId = optionService.getByPropertyOfNonNull(NetlifyStaticDeployProperties.NETLIFY_SITE_ID).toString();
|
||||
String token = optionService.getByPropertyOfNonNull(NetlifyStaticDeployProperties.NETLIFY_TOKEN).toString();
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
headers.set("Content-Type", "application/zip");
|
||||
headers.set(HttpHeaders.AUTHORIZATION, "Bearer " + token);
|
||||
|
||||
Path path = staticPageService.zipStaticPagesDirectory();
|
||||
|
||||
byte[] bytes = FileUtil.readBytes(path.toFile());
|
||||
|
||||
HttpEntity<byte[]> httpEntity = new HttpEntity<>(bytes, headers);
|
||||
|
||||
ResponseEntity<Object> responseEntity = httpsRestTemplate.postForEntity(String.format(DEPLOY_API, siteId), httpEntity, Object.class);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
package run.halo.app.handler.migrate;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import run.halo.app.model.enums.MigrateType;
|
||||
|
||||
|
@ -9,6 +11,8 @@ import run.halo.app.model.enums.MigrateType;
|
|||
* @author ryanwang
|
||||
* @date 2019-10-30
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class CnBlogsMigrateHandler implements MigrateHandler {
|
||||
|
||||
@Override
|
||||
|
|
|
@ -31,7 +31,7 @@ public class MigrateHandlers {
|
|||
|
||||
public MigrateHandlers(ApplicationContext applicationContext) {
|
||||
// Add all migrate handler
|
||||
addFileHandlers(applicationContext.getBeansOfType(MigrateHandler.class).values());
|
||||
addMigrateHandlers(applicationContext.getBeansOfType(MigrateHandler.class).values());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
@ -56,7 +56,7 @@ public class MigrateHandlers {
|
|||
* @return current migrate handlers
|
||||
*/
|
||||
@NonNull
|
||||
private MigrateHandlers addFileHandlers(@Nullable Collection<MigrateHandler> migrateHandlers) {
|
||||
private MigrateHandlers addMigrateHandlers(@Nullable Collection<MigrateHandler> migrateHandlers) {
|
||||
if (!CollectionUtils.isEmpty(migrateHandlers)) {
|
||||
this.migrateHandlers.addAll(migrateHandlers);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
package run.halo.app.handler.staticdeploy;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import run.halo.app.model.enums.StaticDeployType;
|
||||
import run.halo.app.service.OptionService;
|
||||
|
||||
/**
|
||||
* Git deploy handler.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @date 2019-12-26
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class GitStaticDeployHandler implements StaticDeployHandler {
|
||||
|
||||
private final OptionService optionService;
|
||||
|
||||
public GitStaticDeployHandler(OptionService optionService) {
|
||||
this.optionService = optionService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deploy() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportType(StaticDeployType type) {
|
||||
return StaticDeployType.GIT.equals(type);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package run.halo.app.handler.staticdeploy;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import run.halo.app.model.enums.StaticDeployType;
|
||||
import run.halo.app.model.properties.NetlifyStaticDeployProperties;
|
||||
import run.halo.app.service.OptionService;
|
||||
import run.halo.app.service.StaticPageService;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* Netlify deploy handler.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @date 2019-12-26
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class NetlifyStaticDeployHandler implements StaticDeployHandler {
|
||||
|
||||
private final static String DEPLOY_API = "https://api.netlify.com/api/v1/sites/%s/deploys";
|
||||
|
||||
private final OptionService optionService;
|
||||
|
||||
private final RestTemplate httpsRestTemplate;
|
||||
|
||||
private final StaticPageService staticPageService;
|
||||
|
||||
public NetlifyStaticDeployHandler(OptionService optionService,
|
||||
RestTemplate httpsRestTemplate,
|
||||
StaticPageService staticPageService) {
|
||||
this.optionService = optionService;
|
||||
this.httpsRestTemplate = httpsRestTemplate;
|
||||
this.staticPageService = staticPageService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deploy() {
|
||||
String domain = optionService.getByPropertyOfNonNull(NetlifyStaticDeployProperties.NETLIFY_DOMAIN).toString();
|
||||
String siteId = optionService.getByPropertyOfNonNull(NetlifyStaticDeployProperties.NETLIFY_SITE_ID).toString();
|
||||
String token = optionService.getByPropertyOfNonNull(NetlifyStaticDeployProperties.NETLIFY_TOKEN).toString();
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
headers.set("Content-Type", "application/zip");
|
||||
headers.set(HttpHeaders.AUTHORIZATION, "Bearer " + token);
|
||||
|
||||
Path path = staticPageService.zipStaticPagesDirectory();
|
||||
|
||||
byte[] bytes = FileUtil.readBytes(path.toFile());
|
||||
|
||||
HttpEntity<byte[]> httpEntity = new HttpEntity<>(bytes, headers);
|
||||
|
||||
ResponseEntity<Object> responseEntity = httpsRestTemplate.postForEntity(String.format(DEPLOY_API, siteId), httpEntity, Object.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportType(StaticDeployType type) {
|
||||
return StaticDeployType.NETLIFY.equals(type);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package run.halo.app.handler.staticdeploy;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import run.halo.app.model.enums.StaticDeployType;
|
||||
|
||||
/**
|
||||
* Static deploy handler interface class.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @date 2019-12-26
|
||||
*/
|
||||
public interface StaticDeployHandler {
|
||||
|
||||
/**
|
||||
* do deploy.
|
||||
*/
|
||||
void deploy();
|
||||
|
||||
/**
|
||||
* Checks if the given type is supported.
|
||||
*
|
||||
* @param type deploy type
|
||||
* @return true if supported; false or else
|
||||
*/
|
||||
boolean supportType(@Nullable StaticDeployType type);
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package run.halo.app.handler.staticdeploy;
|
||||
|
||||
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 run.halo.app.exception.FileOperationException;
|
||||
import run.halo.app.model.enums.StaticDeployType;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* Static deploy handlers.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @date 2019-12-26
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class StaticDeployHandlers {
|
||||
|
||||
private final Collection<StaticDeployHandler> staticDeployHandlers = new LinkedList<>();
|
||||
|
||||
public StaticDeployHandlers(ApplicationContext applicationContext) {
|
||||
// Add all static deploy handler
|
||||
addStaticDeployHandlers(applicationContext.getBeansOfType(StaticDeployHandler.class).values());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* do deploy.
|
||||
*
|
||||
* @param staticDeployType static deploy type
|
||||
*/
|
||||
public void deploy(@NonNull StaticDeployType staticDeployType) {
|
||||
Assert.notNull(staticDeployType, "Static deploy type must not be null");
|
||||
|
||||
for (StaticDeployHandler staticDeployHandler : staticDeployHandlers) {
|
||||
if (staticDeployHandler.supportType(staticDeployType)) {
|
||||
staticDeployHandler.deploy();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new FileOperationException("No available static deploy handler to deploy static pages").setErrorData(staticDeployType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds static deploy handlers.
|
||||
*
|
||||
* @param staticDeployHandlers static deploy handler collection
|
||||
* @return current file handlers
|
||||
*/
|
||||
@NonNull
|
||||
public StaticDeployHandlers addStaticDeployHandlers(@Nullable Collection<StaticDeployHandler> staticDeployHandlers) {
|
||||
if (!CollectionUtils.isEmpty(staticDeployHandlers)) {
|
||||
this.staticDeployHandlers.addAll(staticDeployHandlers);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package run.halo.app.model.enums;
|
||||
|
||||
/**
|
||||
* Static deploy type.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @date 2019-12-26
|
||||
*/
|
||||
public enum StaticDeployType implements ValueEnum<Integer> {
|
||||
|
||||
/**
|
||||
* Deploy static pages in remote git repository, such as github pages,gitee pages,coding pages.etc.
|
||||
*/
|
||||
GIT(0),
|
||||
|
||||
/**
|
||||
* Deploy static pages in netlify.
|
||||
*/
|
||||
NETLIFY(1);
|
||||
|
||||
private Integer value;
|
||||
|
||||
StaticDeployType(Integer value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get enum value.
|
||||
*
|
||||
* @return enum value
|
||||
*/
|
||||
@Override
|
||||
public Integer getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package run.halo.app.model.properties;
|
||||
|
||||
/**
|
||||
* Git static deploy properties.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @date 2019-12-26
|
||||
*/
|
||||
public enum GitStaticDeployProperties implements PropertyEnum {
|
||||
|
||||
/**
|
||||
* Git static deploy domain.
|
||||
*/
|
||||
GIT_DOMAIN("git_static_deploy_domain", String.class, ""),
|
||||
|
||||
/**
|
||||
* Git static deploy repository.
|
||||
*/
|
||||
GIT_REPOSITORY("git_static_deploy_repository", String.class, ""),
|
||||
|
||||
/**
|
||||
* Git static deploy branch.
|
||||
*/
|
||||
GIT_BRANCH("git_static_deploy_branch", String.class, "master"),
|
||||
|
||||
/**
|
||||
* Git static deploy username.
|
||||
*/
|
||||
GIT_USERNAME("git_static_deploy_username", String.class, ""),
|
||||
|
||||
/**
|
||||
* Git static deploy email.
|
||||
*/
|
||||
GIT_EMAIL("git_static_deploy_email", String.class, ""),
|
||||
|
||||
/**
|
||||
* Git static deploy token.
|
||||
*/
|
||||
GIT_TOKEN("git_static_deploy_token", String.class, ""),
|
||||
|
||||
/**
|
||||
* Git static deploy cname.
|
||||
*/
|
||||
GIT_CNAME("git_static_deploy_cname", String.class, "");
|
||||
|
||||
private final String value;
|
||||
|
||||
private final Class<?> type;
|
||||
|
||||
private final String defaultValue;
|
||||
|
||||
GitStaticDeployProperties(String value, Class<?> type, String defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
if (!PropertyEnum.isSupportedType(type)) {
|
||||
throw new IllegalArgumentException("Unsupported blog property type: " + type);
|
||||
}
|
||||
|
||||
this.value = value;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String defaultValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package run.halo.app.model.properties;
|
||||
|
||||
/**
|
||||
* Netlify static deploy properties.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @date 2019-12-26
|
||||
*/
|
||||
public enum NetlifyStaticDeployProperties implements PropertyEnum {
|
||||
|
||||
/**
|
||||
* Netlify static deploy domain.
|
||||
*/
|
||||
NETLIFY_DOMAIN("netlify_static_deploy_domain", String.class, ""),
|
||||
|
||||
/**
|
||||
* Netlify static deploy site id.
|
||||
*/
|
||||
NETLIFY_SITE_ID("netlify_static_deploy_site_id", String.class, ""),
|
||||
|
||||
/**
|
||||
* Netlify static deploy token.
|
||||
*/
|
||||
NETLIFY_TOKEN("netlify_static_deploy_token", String.class, "");
|
||||
|
||||
private final String value;
|
||||
|
||||
private final Class<?> type;
|
||||
|
||||
private final String defaultValue;
|
||||
|
||||
NetlifyStaticDeployProperties(String value, Class<?> type, String defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
if (!PropertyEnum.isSupportedType(type)) {
|
||||
throw new IllegalArgumentException("Unsupported blog property type: " + type);
|
||||
}
|
||||
|
||||
this.value = value;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String defaultValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
|
@ -155,6 +155,9 @@ public interface PropertyEnum extends ValueEnum<String> {
|
|||
propertyEnumClasses.add(SeoProperties.class);
|
||||
propertyEnumClasses.add(UpOssProperties.class);
|
||||
propertyEnumClasses.add(ApiProperties.class);
|
||||
propertyEnumClasses.add(StaticDeployProperties.class);
|
||||
propertyEnumClasses.add(GitStaticDeployProperties.class);
|
||||
propertyEnumClasses.add(NetlifyStaticDeployProperties.class);
|
||||
|
||||
Map<String, PropertyEnum> result = new HashMap<>();
|
||||
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
package run.halo.app.model.properties;
|
||||
|
||||
import run.halo.app.model.enums.StaticDeployType;
|
||||
|
||||
/**
|
||||
* Static deploy properties.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @date 2019-12-26
|
||||
*/
|
||||
public enum StaticDeployProperties implements PropertyEnum {
|
||||
|
||||
/**
|
||||
* static deploy type
|
||||
*/
|
||||
DEPLOY_TYPE("static_deploy_type", StaticDeployType.class, StaticDeployType.GIT.name());
|
||||
|
||||
private final String value;
|
||||
|
||||
private final Class<?> type;
|
||||
|
||||
private final String defaultValue;
|
||||
|
||||
|
||||
StaticDeployProperties(String value, Class<?> type, String defaultValue) {
|
||||
this.value = value;
|
||||
this.type = type;
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String defaultValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
|
@ -30,6 +30,11 @@ public class HaloConst {
|
|||
*/
|
||||
public final static String HALO_BACKUP_PREFIX = "halo-backup-";
|
||||
|
||||
/**
|
||||
* Static pages pack prefix.
|
||||
*/
|
||||
public final static String STATIC_PAGE_PACK_PREFIX = "static-pages-";
|
||||
|
||||
/**
|
||||
* Default theme name.
|
||||
*/
|
||||
|
|
|
@ -16,6 +16,8 @@ import java.util.List;
|
|||
@ToString
|
||||
public class StaticFile implements Comparator<StaticFile> {
|
||||
|
||||
private String id;
|
||||
|
||||
private String name;
|
||||
|
||||
private String path;
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
package run.halo.app.model.support;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Static page dto.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @date 2019-12-26
|
||||
*/
|
||||
@Data
|
||||
public class StaticPageFile implements Comparator<StaticPageFile> {
|
||||
|
||||
private String id;
|
||||
|
||||
private String name;
|
||||
|
||||
private Boolean isFile;
|
||||
|
||||
private List<StaticPageFile> children;
|
||||
|
||||
@Override
|
||||
public int compare(StaticPageFile leftFile, StaticPageFile rightFile) {
|
||||
if (leftFile.isFile && !rightFile.isFile) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!leftFile.isFile && rightFile.isFile) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return leftFile.getName().compareTo(rightFile.getName());
|
||||
}
|
||||
}
|
|
@ -42,4 +42,11 @@ public interface ThemeSettingRepository extends BaseRepository<ThemeSetting, Int
|
|||
*/
|
||||
@NonNull
|
||||
Optional<ThemeSetting> findByThemeIdAndKey(@NonNull String themeId, @NonNull String key);
|
||||
|
||||
/**
|
||||
* Deletes inactivated theme settings.
|
||||
*
|
||||
* @param activatedThemeId activated theme id.
|
||||
*/
|
||||
void deleteByThemeIdIsNot(@NonNull String activatedThemeId);
|
||||
}
|
||||
|
|
|
@ -83,4 +83,13 @@ public interface AttachmentService extends CrudService<Attachment, Integer> {
|
|||
* @return list of type.
|
||||
*/
|
||||
List<AttachmentType> listAllType();
|
||||
|
||||
/**
|
||||
* Replace attachment url in batch.
|
||||
*
|
||||
* @param oldUrl old blog url.
|
||||
* @param newUrl new blog url.
|
||||
* @return replaced attachments.
|
||||
*/
|
||||
List<Attachment> replaceUrl(@NonNull String oldUrl, @NonNull String newUrl);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package run.halo.app.service;
|
||||
|
||||
import org.springframework.lang.NonNull;
|
||||
|
||||
/**
|
||||
* Data process service interface.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @date 2019-12-29
|
||||
*/
|
||||
public interface DataProcessService {
|
||||
|
||||
/**
|
||||
* Replace all url.
|
||||
*
|
||||
* @param oldUrl old url must not be null.
|
||||
* @param newUrl new url must not be null.
|
||||
*/
|
||||
void replaceAllUrl(@NonNull String oldUrl, @NonNull String newUrl);
|
||||
}
|
|
@ -338,6 +338,15 @@ public interface OptionService extends CrudService<Option, Integer> {
|
|||
*/
|
||||
long getBirthday();
|
||||
|
||||
/**
|
||||
* Replace option url in batch.
|
||||
*
|
||||
* @param oldUrl old blog url.
|
||||
* @param newUrl new blog url.
|
||||
* @return replaced options.
|
||||
*/
|
||||
List<OptionDTO> replaceUrl(@NonNull String oldUrl, @NonNull String newUrl);
|
||||
|
||||
/**
|
||||
* Converts to option output dto.
|
||||
*
|
||||
|
|
|
@ -80,4 +80,13 @@ public interface PhotoService extends CrudService<Photo, Integer> {
|
|||
* @return list of teams
|
||||
*/
|
||||
List<String> listAllTeams();
|
||||
|
||||
/**
|
||||
* Replace photo url in batch.
|
||||
*
|
||||
* @param oldUrl old blog url.
|
||||
* @param newUrl new blog url.
|
||||
* @return replaced photos.
|
||||
*/
|
||||
List<PhotoDTO> replaceUrl(@NonNull String oldUrl, @NonNull String newUrl);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
package run.halo.app.service;
|
||||
|
||||
import run.halo.app.model.support.StaticPageFile;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import static run.halo.app.model.support.HaloConst.FILE_SEPARATOR;
|
||||
import static run.halo.app.model.support.HaloConst.TEMP_DIR;
|
||||
import static run.halo.app.utils.HaloUtils.ensureSuffix;
|
||||
|
||||
/**
|
||||
* Static Page service interface.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @date 2019-12-25
|
||||
*/
|
||||
public interface StaticPageService {
|
||||
|
||||
/**
|
||||
* Static page folder location.
|
||||
*/
|
||||
String PAGES_FOLDER = "static_pages";
|
||||
|
||||
|
||||
String STATIC_PAGE_PACK_DIR = ensureSuffix(TEMP_DIR, FILE_SEPARATOR) + "static-pages-pack" + FILE_SEPARATOR;
|
||||
|
||||
String[] USELESS_FILE_SUFFIX = {"ftl", "md", "yaml", "yml", "gitignore"};
|
||||
|
||||
/**
|
||||
* Generate pages.
|
||||
*/
|
||||
void generate();
|
||||
|
||||
/**
|
||||
* Deploy static pages.
|
||||
*/
|
||||
void deploy();
|
||||
|
||||
/**
|
||||
* Zip static pages directory.
|
||||
*
|
||||
* @return zip path
|
||||
*/
|
||||
Path zipStaticPagesDirectory();
|
||||
|
||||
/**
|
||||
* List file of generated static page.
|
||||
*
|
||||
* @return a list of generated static page.
|
||||
*/
|
||||
List<StaticPageFile> listFile();
|
||||
}
|
|
@ -64,6 +64,11 @@ public interface ThemeService {
|
|||
*/
|
||||
String RENDER_TEMPLATE = "themes/%s/%s";
|
||||
|
||||
/**
|
||||
* Render template with suffix.
|
||||
*/
|
||||
String RENDER_TEMPLATE_SUFFIX = "themes/%s/%s.ftl";
|
||||
|
||||
/**
|
||||
* Theme cache key.
|
||||
*/
|
||||
|
@ -231,6 +236,15 @@ public interface ThemeService {
|
|||
@NonNull
|
||||
String render(@NonNull String pageName);
|
||||
|
||||
/**
|
||||
* Renders a theme page.
|
||||
*
|
||||
* @param pageName must not be blank
|
||||
* @return full path of the theme page
|
||||
*/
|
||||
@NonNull
|
||||
String renderWithSuffix(@NonNull String pageName);
|
||||
|
||||
/**
|
||||
* Gets current theme id.
|
||||
*
|
||||
|
|
|
@ -55,4 +55,18 @@ public interface ThemeSettingService {
|
|||
*/
|
||||
@NonNull
|
||||
Map<String, Object> listAsMapBy(@NonNull String themeId);
|
||||
|
||||
/**
|
||||
* Replace theme setting url in batch.
|
||||
*
|
||||
* @param oldUrl old blog url.
|
||||
* @param newUrl new blog url.
|
||||
* @return replaced theme settings.
|
||||
*/
|
||||
List<ThemeSetting> replaceUrl(@NonNull String oldUrl, @NonNull String newUrl);
|
||||
|
||||
/**
|
||||
* Delete unused theme setting.
|
||||
*/
|
||||
void deleteInactivated();
|
||||
}
|
||||
|
|
|
@ -304,4 +304,13 @@ public interface BaseCommentService<COMMENT extends BaseComment> extends CrudSer
|
|||
*/
|
||||
<T extends BaseCommentDTO> Page<T> filterIpAddress(@NonNull Page<T> commentPage);
|
||||
|
||||
/**
|
||||
* Replace comment url in batch.
|
||||
*
|
||||
* @param oldUrl old blog url.
|
||||
* @param newUrl new blog url.
|
||||
* @return replaced comments.
|
||||
*/
|
||||
List<BaseCommentDTO> replaceUrl(@NonNull String oldUrl, @NonNull String newUrl);
|
||||
|
||||
}
|
||||
|
|
|
@ -287,4 +287,14 @@ public interface BasePostService<POST extends BasePost> extends CrudService<POST
|
|||
*/
|
||||
@NonNull
|
||||
List<POST> updateStatusByIds(@NonNull List<Integer> ids, @NonNull PostStatus status);
|
||||
|
||||
/**
|
||||
* Replace post blog url in batch.
|
||||
*
|
||||
* @param oldUrl old blog url.
|
||||
* @param newUrl new blog url.
|
||||
* @return replaced posts.
|
||||
*/
|
||||
@NonNull
|
||||
List<BasePostDetailDTO> replaceUrl(@NonNull String oldUrl, @NonNull String newUrl);
|
||||
}
|
||||
|
|
|
@ -183,6 +183,22 @@ public class AttachmentServiceImpl extends AbstractCrudService<Attachment, Integ
|
|||
return attachmentRepository.findAllType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Attachment> replaceUrl(String oldUrl, String newUrl) {
|
||||
List<Attachment> attachments = listAll();
|
||||
List<Attachment> replaced = new ArrayList<>();
|
||||
attachments.forEach(attachment -> {
|
||||
if (StringUtils.isNotEmpty(attachment.getPath())) {
|
||||
attachment.setPath(attachment.getPath().replaceAll(oldUrl, newUrl));
|
||||
}
|
||||
if (StringUtils.isNotEmpty(attachment.getThumbPath())) {
|
||||
attachment.setThumbPath(attachment.getThumbPath().replaceAll(oldUrl, newUrl));
|
||||
}
|
||||
replaced.add(attachment);
|
||||
});
|
||||
return updateInBatch(replaced);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Attachment create(Attachment attachment) {
|
||||
Assert.notNull(attachment, "Attachment must not be null");
|
||||
|
|
|
@ -610,6 +610,20 @@ public abstract class BaseCommentServiceImpl<COMMENT extends BaseComment> extend
|
|||
return commentPage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BaseCommentDTO> replaceUrl(String oldUrl, String newUrl) {
|
||||
List<COMMENT> comments = listAll();
|
||||
List<COMMENT> replaced = new ArrayList<>();
|
||||
comments.forEach(comment -> {
|
||||
if (StringUtils.isNotEmpty(comment.getAuthorUrl())) {
|
||||
comment.setAuthorUrl(comment.getAuthorUrl().replaceAll(oldUrl, newUrl));
|
||||
}
|
||||
replaced.add(comment);
|
||||
});
|
||||
List<COMMENT> updated = updateInBatch(replaced);
|
||||
return convertTo(updated);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get children comments recursively.
|
||||
*
|
||||
|
|
|
@ -29,10 +29,7 @@ import run.halo.app.utils.HaloUtils;
|
|||
import run.halo.app.utils.MarkdownUtils;
|
||||
import run.halo.app.utils.ServiceUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -395,6 +392,26 @@ public abstract class BasePostServiceImpl<POST extends BasePost> extends Abstrac
|
|||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BasePostDetailDTO> replaceUrl(String oldUrl, String newUrl) {
|
||||
List<POST> posts = listAll();
|
||||
List<POST> replaced = new ArrayList<>();
|
||||
posts.forEach(post -> {
|
||||
if (StringUtils.isNotEmpty(post.getThumbnail())) {
|
||||
post.setThumbnail(post.getThumbnail().replaceAll(oldUrl, newUrl));
|
||||
}
|
||||
if (StringUtils.isNotEmpty(post.getOriginalContent())) {
|
||||
post.setOriginalContent(post.getOriginalContent().replaceAll(oldUrl, newUrl));
|
||||
}
|
||||
if (StringUtils.isNotEmpty(post.getFormatContent())) {
|
||||
post.setFormatContent(post.getFormatContent().replaceAll(oldUrl, newUrl));
|
||||
}
|
||||
replaced.add(post);
|
||||
});
|
||||
List<POST> updated = updateInBatch(replaced);
|
||||
return updated.stream().map(this::convertToDetail).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public POST create(POST post) {
|
||||
// Check title
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
package run.halo.app.service.impl;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import run.halo.app.service.*;
|
||||
|
||||
/**
|
||||
* DataProcessService implementation.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @date 2019-12-29
|
||||
*/
|
||||
@Service
|
||||
public class DataProcessServiceImpl implements DataProcessService {
|
||||
|
||||
private final PostService postService;
|
||||
|
||||
private final SheetService sheetService;
|
||||
|
||||
private final PostCommentService postCommentService;
|
||||
|
||||
private final SheetCommentService sheetCommentService;
|
||||
|
||||
private final JournalCommentService journalCommentService;
|
||||
|
||||
private final AttachmentService attachmentService;
|
||||
|
||||
private final OptionService optionService;
|
||||
|
||||
private final PhotoService photoService;
|
||||
|
||||
private final ThemeSettingService themeSettingService;
|
||||
|
||||
public DataProcessServiceImpl(PostService postService,
|
||||
SheetService sheetService,
|
||||
PostCommentService postCommentService,
|
||||
SheetCommentService sheetCommentService,
|
||||
JournalCommentService journalCommentService,
|
||||
AttachmentService attachmentService,
|
||||
OptionService optionService,
|
||||
PhotoService photoService,
|
||||
ThemeSettingService themeSettingService) {
|
||||
this.postService = postService;
|
||||
this.sheetService = sheetService;
|
||||
this.postCommentService = postCommentService;
|
||||
this.sheetCommentService = sheetCommentService;
|
||||
this.journalCommentService = journalCommentService;
|
||||
this.attachmentService = attachmentService;
|
||||
this.optionService = optionService;
|
||||
this.photoService = photoService;
|
||||
this.themeSettingService = themeSettingService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replaceAllUrl(String oldUrl, String newUrl) {
|
||||
postService.replaceUrl(oldUrl, newUrl);
|
||||
sheetService.replaceUrl(oldUrl, newUrl);
|
||||
postCommentService.replaceUrl(oldUrl, newUrl);
|
||||
sheetCommentService.replaceUrl(oldUrl, newUrl);
|
||||
journalCommentService.replaceUrl(oldUrl, newUrl);
|
||||
attachmentService.replaceUrl(oldUrl, newUrl);
|
||||
optionService.replaceUrl(oldUrl, newUrl);
|
||||
photoService.replaceUrl(oldUrl, newUrl);
|
||||
themeSettingService.replaceUrl(oldUrl, newUrl);
|
||||
}
|
||||
}
|
|
@ -36,6 +36,7 @@ import run.halo.app.utils.ValidationUtils;
|
|||
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* OptionService implementation class
|
||||
|
@ -460,6 +461,21 @@ public class OptionServiceImpl extends AbstractCrudService<Option, Integer> impl
|
|||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OptionDTO> replaceUrl(String oldUrl, String newUrl) {
|
||||
List<Option> options = listAll();
|
||||
List<Option> replaced = new ArrayList<>();
|
||||
options.forEach(option -> {
|
||||
if (StringUtils.isNotEmpty(option.getValue())) {
|
||||
option.setValue(option.getValue().replaceAll(oldUrl, newUrl));
|
||||
}
|
||||
replaced.add(option);
|
||||
});
|
||||
List<Option> updated = updateInBatch(replaced);
|
||||
publishOptionUpdatedEvent();
|
||||
return updated.stream().map(this::convertToDto).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public OptionSimpleDTO convertToDto(Option option) {
|
||||
Assert.notNull(option, "Option must not be null");
|
||||
|
|
|
@ -19,10 +19,7 @@ import run.halo.app.service.base.AbstractCrudService;
|
|||
import run.halo.app.utils.ServiceUtils;
|
||||
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
|
@ -114,6 +111,23 @@ public class PhotoServiceImpl extends AbstractCrudService<Photo, Integer> implem
|
|||
return photoRepository.findAllTeams();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PhotoDTO> replaceUrl(String oldUrl, String newUrl) {
|
||||
List<Photo> photos = listAll();
|
||||
List<Photo> replaced = new ArrayList<>();
|
||||
photos.forEach(photo -> {
|
||||
if (StringUtils.isNotEmpty(photo.getThumbnail())) {
|
||||
photo.setThumbnail(photo.getThumbnail().replace(oldUrl, newUrl));
|
||||
}
|
||||
if (StringUtils.isNotEmpty(photo.getUrl())) {
|
||||
photo.setUrl(photo.getUrl().replaceAll(oldUrl, newUrl));
|
||||
}
|
||||
replaced.add(photo);
|
||||
});
|
||||
List<Photo> updated = updateInBatch(replaced);
|
||||
return updated.stream().map(photo -> (PhotoDTO) new PhotoDTO().convertFrom(photo)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Specification<Photo> buildSpecByQuery(@NonNull PhotoQuery photoQuery) {
|
||||
Assert.notNull(photoQuery, "Photo query must not be null");
|
||||
|
|
|
@ -0,0 +1,835 @@
|
|||
package run.halo.app.service.impl;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.file.FileWriter;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.PageUtil;
|
||||
import freemarker.template.Template;
|
||||
import freemarker.template.TemplateException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
|
||||
import run.halo.app.config.properties.HaloProperties;
|
||||
import run.halo.app.exception.ServiceException;
|
||||
import run.halo.app.handler.staticdeploy.StaticDeployHandlers;
|
||||
import run.halo.app.handler.theme.config.support.ThemeProperty;
|
||||
import run.halo.app.model.dto.PhotoDTO;
|
||||
import run.halo.app.model.entity.*;
|
||||
import run.halo.app.model.enums.PostStatus;
|
||||
import run.halo.app.model.enums.StaticDeployType;
|
||||
import run.halo.app.model.properties.PostProperties;
|
||||
import run.halo.app.model.properties.StaticDeployProperties;
|
||||
import run.halo.app.model.support.HaloConst;
|
||||
import run.halo.app.model.support.StaticPageFile;
|
||||
import run.halo.app.model.vo.PostDetailVO;
|
||||
import run.halo.app.model.vo.PostListVO;
|
||||
import run.halo.app.model.vo.SheetDetailVO;
|
||||
import run.halo.app.service.*;
|
||||
import run.halo.app.utils.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.springframework.data.domain.Sort.Direction.DESC;
|
||||
|
||||
/**
|
||||
* Static Page service implementation.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @date 2019-12-25
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class StaticPageServiceImpl implements StaticPageService {
|
||||
|
||||
private final Path pagesDir;
|
||||
|
||||
private final PostService postService;
|
||||
|
||||
private final PostCategoryService postCategoryService;
|
||||
|
||||
private final PostTagService postTagService;
|
||||
|
||||
private final PostMetaService postMetaService;
|
||||
|
||||
private final SheetService sheetService;
|
||||
|
||||
private final CategoryService categoryService;
|
||||
|
||||
private final TagService tagService;
|
||||
|
||||
private final LinkService linkService;
|
||||
|
||||
private final PhotoService photoService;
|
||||
|
||||
private final JournalService journalService;
|
||||
|
||||
private final ThemeService themeService;
|
||||
|
||||
private final HaloProperties haloProperties;
|
||||
|
||||
private final OptionService optionService;
|
||||
|
||||
private final FreeMarkerConfigurer freeMarkerConfigurer;
|
||||
|
||||
private final StaticDeployHandlers staticDeployHandlers;
|
||||
|
||||
public StaticPageServiceImpl(PostService postService,
|
||||
PostCategoryService postCategoryService,
|
||||
PostTagService postTagService,
|
||||
PostMetaService postMetaService,
|
||||
SheetService sheetService,
|
||||
CategoryService categoryService,
|
||||
TagService tagService,
|
||||
LinkService linkService,
|
||||
PhotoService photoService,
|
||||
JournalService journalService,
|
||||
ThemeService themeService,
|
||||
HaloProperties haloProperties,
|
||||
OptionService optionService,
|
||||
FreeMarkerConfigurer freeMarkerConfigurer,
|
||||
StaticDeployHandlers staticDeployHandlers) throws IOException {
|
||||
this.postService = postService;
|
||||
this.postCategoryService = postCategoryService;
|
||||
this.postTagService = postTagService;
|
||||
this.postMetaService = postMetaService;
|
||||
this.sheetService = sheetService;
|
||||
this.categoryService = categoryService;
|
||||
this.tagService = tagService;
|
||||
this.linkService = linkService;
|
||||
this.photoService = photoService;
|
||||
this.journalService = journalService;
|
||||
this.themeService = themeService;
|
||||
this.haloProperties = haloProperties;
|
||||
this.optionService = optionService;
|
||||
this.freeMarkerConfigurer = freeMarkerConfigurer;
|
||||
this.staticDeployHandlers = staticDeployHandlers;
|
||||
|
||||
pagesDir = Paths.get(haloProperties.getWorkDir(), PAGES_FOLDER);
|
||||
FileUtils.createIfAbsent(pagesDir);
|
||||
Files.createDirectories(Paths.get(STATIC_PAGE_PACK_DIR));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate() {
|
||||
try {
|
||||
this.cleanFolder();
|
||||
this.generateIndex(1);
|
||||
this.generatePost();
|
||||
this.generateArchives(1);
|
||||
this.generateSheet();
|
||||
this.generateLink();
|
||||
this.generatePhoto(1);
|
||||
this.generateCategories();
|
||||
this.generateTags();
|
||||
this.generateRss();
|
||||
this.generateAtom();
|
||||
this.generateSiteMapHtml();
|
||||
this.generateSiteMapXml();
|
||||
this.generateRobots();
|
||||
this.generateReadme();
|
||||
this.copyThemeFolder();
|
||||
this.copyUpload();
|
||||
this.copyStatic();
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException("生成静态页面失败!", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deploy() {
|
||||
StaticDeployType type = getStaticDeployType();
|
||||
|
||||
staticDeployHandlers.deploy(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path zipStaticPagesDirectory() {
|
||||
try {
|
||||
String staticPagePackName = HaloConst.STATIC_PAGE_PACK_PREFIX +
|
||||
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss-")) +
|
||||
IdUtil.simpleUUID().hashCode() + ".zip";
|
||||
Path staticPageZipPath = Files.createFile(Paths.get(STATIC_PAGE_PACK_DIR, staticPagePackName));
|
||||
|
||||
FileUtils.zip(pagesDir, staticPageZipPath);
|
||||
|
||||
return staticPageZipPath;
|
||||
} catch (IOException e) {
|
||||
throw new ServiceException("Failed to zip static pages directory", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StaticPageFile> listFile() {
|
||||
return listStaticPageFileTree(pagesDir);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private List<StaticPageFile> listStaticPageFileTree(@NonNull Path topPath) {
|
||||
Assert.notNull(topPath, "Top path must not be null");
|
||||
|
||||
if (!Files.isDirectory(topPath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try (Stream<Path> pathStream = Files.list(topPath)) {
|
||||
List<StaticPageFile> staticPageFiles = new LinkedList<>();
|
||||
|
||||
pathStream.forEach(path -> {
|
||||
StaticPageFile staticPageFile = new StaticPageFile();
|
||||
staticPageFile.setId(IdUtil.fastSimpleUUID());
|
||||
staticPageFile.setName(path.getFileName().toString());
|
||||
staticPageFile.setIsFile(Files.isRegularFile(path));
|
||||
if (Files.isDirectory(path)) {
|
||||
staticPageFile.setChildren(listStaticPageFileTree(path));
|
||||
}
|
||||
|
||||
staticPageFiles.add(staticPageFile);
|
||||
});
|
||||
|
||||
staticPageFiles.sort(new StaticPageFile());
|
||||
return staticPageFiles;
|
||||
} catch (IOException e) {
|
||||
throw new ServiceException("Failed to list sub files", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean static pages folder
|
||||
*/
|
||||
private void cleanFolder() {
|
||||
FileUtils.deleteFolderQuietly(pagesDir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate index.html and page/{page}/index.html.
|
||||
*
|
||||
* @param page current page.
|
||||
* @throws IOException IOException
|
||||
* @throws TemplateException TemplateException
|
||||
*/
|
||||
private void generateIndex(int page) throws IOException, TemplateException {
|
||||
if (!themeService.templateExists("index.ftl")) {
|
||||
log.warn("index.ftl not found,skip!");
|
||||
return;
|
||||
}
|
||||
|
||||
ModelMap model = new ModelMap();
|
||||
|
||||
String indexSort = optionService.getByPropertyOfNonNull(PostProperties.INDEX_SORT).toString();
|
||||
int pageSize = optionService.getPostPageSize();
|
||||
Pageable pageable = PageRequest.of(page >= 1 ? page - 1 : page, pageSize, Sort.by(DESC, "topPriority").and(Sort.by(DESC, indexSort)));
|
||||
Page<Post> postPage = postService.pageBy(PostStatus.PUBLISHED, pageable);
|
||||
Page<PostListVO> posts = postService.convertToListVo(postPage);
|
||||
int[] rainbow = PageUtil.rainbow(page, posts.getTotalPages(), 3);
|
||||
|
||||
model.addAttribute("is_index", true);
|
||||
model.addAttribute("posts", posts);
|
||||
model.addAttribute("rainbow", rainbow);
|
||||
|
||||
Template template = freeMarkerConfigurer.getConfiguration().getTemplate(themeService.renderWithSuffix("index"));
|
||||
String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
||||
|
||||
FileWriter fileWriter;
|
||||
|
||||
if (page == 1) {
|
||||
fileWriter = new FileWriter(getPageFile("index.html"), "UTF-8");
|
||||
} else {
|
||||
fileWriter = new FileWriter(getPageFile("page/" + page + "/index.html"), "UTF-8");
|
||||
}
|
||||
|
||||
fileWriter.write(html);
|
||||
|
||||
if (postPage.hasNext()) {
|
||||
generateIndex(postPage.getNumber() + 2);
|
||||
log.info("Generate page/{}/index.html", postPage.getNumber() + 2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate archives/index.html and archives/page/{page}/index.html.
|
||||
*
|
||||
* @param page current page
|
||||
* @throws IOException IOException
|
||||
* @throws TemplateException TemplateException
|
||||
*/
|
||||
private void generateArchives(int page) throws IOException, TemplateException {
|
||||
|
||||
if (!themeService.templateExists("archives.ftl")) {
|
||||
log.warn("archives.ftl not found,skip!");
|
||||
return;
|
||||
}
|
||||
|
||||
ModelMap model = new ModelMap();
|
||||
|
||||
Pageable pageable = PageRequest.of(page - 1, optionService.getPostPageSize(), Sort.by(DESC, "topPriority"));
|
||||
|
||||
Page<Post> postPage = postService.pageBy(PostStatus.PUBLISHED, pageable);
|
||||
Page<PostListVO> postListVos = postService.convertToListVo(postPage);
|
||||
int[] pageRainbow = PageUtil.rainbow(page, postListVos.getTotalPages(), 3);
|
||||
|
||||
model.addAttribute("is_archives", true);
|
||||
model.addAttribute("pageRainbow", pageRainbow);
|
||||
model.addAttribute("posts", postListVos);
|
||||
|
||||
Template template = freeMarkerConfigurer.getConfiguration().getTemplate(themeService.renderWithSuffix("archives"));
|
||||
String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
||||
|
||||
FileWriter fileWriter;
|
||||
|
||||
if (page == 1) {
|
||||
fileWriter = new FileWriter(getPageFile("archives/index.html"), "UTF-8");
|
||||
} else {
|
||||
fileWriter = new FileWriter(getPageFile("archives/page/" + page + "/index.html"), "UTF-8");
|
||||
}
|
||||
|
||||
fileWriter.write(html);
|
||||
|
||||
if (postPage.hasNext()) {
|
||||
generateArchives(postPage.getNumber() + 2);
|
||||
log.info("Generate page/{}/index.html", postPage.getNumber() + 2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate archives/{url}/index.html.
|
||||
*
|
||||
* @throws IOException IOException
|
||||
* @throws TemplateException TemplateException
|
||||
*/
|
||||
private void generatePost() throws IOException, TemplateException {
|
||||
|
||||
if (!themeService.templateExists("post.ftl")) {
|
||||
log.warn("post.ftl not found,skip!");
|
||||
return;
|
||||
}
|
||||
|
||||
List<Post> posts = postService.listAllBy(PostStatus.PUBLISHED);
|
||||
|
||||
for (Post post : posts) {
|
||||
log.info("Generate archives/{}/index.html", post.getUrl());
|
||||
ModelMap model = new ModelMap();
|
||||
postService.getNextPost(post.getCreateTime()).ifPresent(nextPost -> model.addAttribute("nextPost", nextPost));
|
||||
postService.getPrePost(post.getCreateTime()).ifPresent(prePost -> model.addAttribute("prePost", prePost));
|
||||
|
||||
List<Category> categories = postCategoryService.listCategoriesBy(post.getId());
|
||||
List<Tag> tags = postTagService.listTagsBy(post.getId());
|
||||
List<PostMeta> metas = postMetaService.listBy(post.getId());
|
||||
|
||||
model.addAttribute("is_post", true);
|
||||
model.addAttribute("post", postService.convertToDetailVo(post));
|
||||
model.addAttribute("categories", categories);
|
||||
model.addAttribute("tags", tags);
|
||||
model.addAttribute("metas", postMetaService.convertToMap(metas));
|
||||
|
||||
Template template = freeMarkerConfigurer.getConfiguration().getTemplate(themeService.renderWithSuffix("post"));
|
||||
String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
||||
|
||||
FileWriter fileWriter = new FileWriter(getPageFile("archives/" + post.getUrl() + "/index.html"), "UTF-8");
|
||||
fileWriter.write(html);
|
||||
log.info("Generate archives/{}/index.html succeed.", post.getUrl());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate s/{url}/index.html.
|
||||
*
|
||||
* @throws IOException IOException
|
||||
* @throws TemplateException TemplateException
|
||||
*/
|
||||
private void generateSheet() throws IOException, TemplateException {
|
||||
if (!themeService.templateExists("sheet.ftl")) {
|
||||
log.warn("sheet.ftl not found,skip!");
|
||||
return;
|
||||
}
|
||||
|
||||
List<Sheet> sheets = sheetService.listAllBy(PostStatus.PUBLISHED);
|
||||
for (Sheet sheet : sheets) {
|
||||
log.info("Generate s/{}/index.html", sheet.getUrl());
|
||||
ModelMap model = new ModelMap();
|
||||
|
||||
SheetDetailVO sheetDetailVO = sheetService.convertToDetailVo(sheet);
|
||||
model.addAttribute("sheet", sheetDetailVO);
|
||||
model.addAttribute("post", sheetDetailVO);
|
||||
model.addAttribute("is_sheet", true);
|
||||
|
||||
String templateName = "sheet";
|
||||
|
||||
if (themeService.templateExists(ThemeService.CUSTOM_SHEET_PREFIX + sheet.getTemplate() + HaloConst.SUFFIX_FTL)) {
|
||||
templateName = ThemeService.CUSTOM_SHEET_PREFIX + sheet.getTemplate();
|
||||
}
|
||||
|
||||
Template template = freeMarkerConfigurer.getConfiguration().getTemplate(themeService.renderWithSuffix(templateName));
|
||||
String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
||||
|
||||
FileWriter fileWriter = new FileWriter(getPageFile("s/" + sheet.getUrl() + "/index.html"), "UTF-8");
|
||||
fileWriter.write(html);
|
||||
|
||||
log.info("Generate s/{}/index.html succeed.", sheet.getUrl());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate links/index.html.
|
||||
*
|
||||
* @throws IOException IOException
|
||||
* @throws TemplateException TemplateException
|
||||
*/
|
||||
private void generateLink() throws IOException, TemplateException {
|
||||
log.info("Generate links.html");
|
||||
|
||||
if (!themeService.templateExists("links.ftl")) {
|
||||
log.warn("links.ftl not found,skip!");
|
||||
return;
|
||||
}
|
||||
|
||||
Template template = freeMarkerConfigurer.getConfiguration().getTemplate(themeService.renderWithSuffix("links"));
|
||||
String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, null);
|
||||
FileWriter fileWriter = new FileWriter(getPageFile("links/index.html"), "UTF-8");
|
||||
fileWriter.write(html);
|
||||
|
||||
log.info("Generate links.html succeed.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate photos/index.html and photos/page/{page}/index.html.
|
||||
*
|
||||
* @throws IOException IOException
|
||||
* @throws TemplateException TemplateException
|
||||
*/
|
||||
private void generatePhoto(int page) throws IOException, TemplateException {
|
||||
log.info("Generate photos.html");
|
||||
|
||||
if (!themeService.templateExists("photos.ftl")) {
|
||||
log.warn("photos.ftl not found,skip!");
|
||||
return;
|
||||
}
|
||||
|
||||
ModelMap model = new ModelMap();
|
||||
|
||||
Pageable pageable = PageRequest.of(page >= 1 ? page - 1 : page, 10, Sort.by(DESC, "createTime"));
|
||||
Page<PhotoDTO> photos = photoService.pageBy(pageable);
|
||||
|
||||
model.addAttribute("photos", photos);
|
||||
|
||||
FileWriter fileWriter;
|
||||
|
||||
if (page == 1) {
|
||||
fileWriter = new FileWriter(getPageFile("photos/index.html"), "UTF-8");
|
||||
} else {
|
||||
fileWriter = new FileWriter(getPageFile("photos/page/" + page + "/photos.html"), "UTF-8");
|
||||
}
|
||||
|
||||
Template template = freeMarkerConfigurer.getConfiguration().getTemplate(themeService.renderWithSuffix("photos"));
|
||||
String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
||||
fileWriter.write(html);
|
||||
|
||||
log.info("Generate photos.html succeed.");
|
||||
|
||||
if (photos.hasNext()) {
|
||||
generatePhoto(photos.getNumber() + 2);
|
||||
log.info("Generate page/{}/index.html", photos.getNumber() + 2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate categories/index.html.
|
||||
*
|
||||
* @throws IOException IOException
|
||||
* @throws TemplateException TemplateException
|
||||
*/
|
||||
private void generateCategories() throws IOException, TemplateException {
|
||||
log.info("Generate categories.html");
|
||||
|
||||
ModelMap model = new ModelMap();
|
||||
|
||||
if (!themeService.templateExists("categories.ftl")) {
|
||||
log.warn("categories.ftl not found,skip!");
|
||||
return;
|
||||
}
|
||||
|
||||
model.addAttribute("is_categories", true);
|
||||
Template template = freeMarkerConfigurer.getConfiguration().getTemplate(themeService.renderWithSuffix("categories"));
|
||||
String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
||||
FileWriter fileWriter = new FileWriter(getPageFile("categories/index.html"), "UTF-8");
|
||||
fileWriter.write(html);
|
||||
|
||||
List<Category> categories = categoryService.listAll();
|
||||
for (Category category : categories) {
|
||||
generateCategory(1, category);
|
||||
}
|
||||
|
||||
log.info("Generate categories.html succeed.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate categories/{slugName}/index.html and categories/{slugName}/{page}/index.html.
|
||||
*
|
||||
* @param page current page
|
||||
* @param category current category
|
||||
* @throws IOException IOException
|
||||
* @throws TemplateException TemplateException
|
||||
*/
|
||||
private void generateCategory(int page, Category category) throws IOException, TemplateException {
|
||||
if (!themeService.templateExists("category.ftl")) {
|
||||
log.warn("category.ftl not found,skip!");
|
||||
return;
|
||||
}
|
||||
|
||||
ModelMap model = new ModelMap();
|
||||
|
||||
final Pageable pageable = PageRequest.of(page - 1, optionService.getPostPageSize(), Sort.by(DESC, "createTime"));
|
||||
Page<Post> postPage = postCategoryService.pagePostBy(category.getId(), PostStatus.PUBLISHED, pageable);
|
||||
Page<PostListVO> posts = postService.convertToListVo(postPage);
|
||||
final int[] rainbow = PageUtil.rainbow(page, posts.getTotalPages(), 3);
|
||||
|
||||
model.addAttribute("is_category", true);
|
||||
model.addAttribute("posts", posts);
|
||||
model.addAttribute("rainbow", rainbow);
|
||||
model.addAttribute("category", category);
|
||||
|
||||
Template template = freeMarkerConfigurer.getConfiguration().getTemplate(themeService.renderWithSuffix("category"));
|
||||
String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
||||
|
||||
FileWriter fileWriter;
|
||||
|
||||
if (page == 1) {
|
||||
fileWriter = new FileWriter(getPageFile("categories/" + category.getSlugName() + "/index.html"), "UTF-8");
|
||||
} else {
|
||||
fileWriter = new FileWriter(getPageFile("categories/" + category.getSlugName() + "/page/" + page + "/index.html"), "UTF-8");
|
||||
}
|
||||
|
||||
fileWriter.write(html);
|
||||
|
||||
if (postPage.hasNext()) {
|
||||
generateCategory(postPage.getNumber() + 2, category);
|
||||
log.info("Generate categories/{}/page/{}/index.html", category.getSlugName(), postPage.getNumber() + 2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate tags/{slugName}/index.html and tags/{slugName}/{page}/index.html.
|
||||
*
|
||||
* @param page current page
|
||||
* @param category current category
|
||||
* @throws IOException IOException
|
||||
* @throws TemplateException TemplateException
|
||||
*/
|
||||
private void generateTag(int page, Tag tag) throws IOException, TemplateException {
|
||||
if (!themeService.templateExists("tag.ftl")) {
|
||||
log.warn("tag.ftl not found,skip!");
|
||||
return;
|
||||
}
|
||||
|
||||
ModelMap model = new ModelMap();
|
||||
|
||||
final Pageable pageable = PageRequest.of(page - 1, optionService.getPostPageSize(), Sort.by(DESC, "createTime"));
|
||||
Page<Post> postPage = postTagService.pagePostsBy(tag.getId(), PostStatus.PUBLISHED, pageable);
|
||||
Page<PostListVO> posts = postService.convertToListVo(postPage);
|
||||
final int[] rainbow = PageUtil.rainbow(page, posts.getTotalPages(), 3);
|
||||
|
||||
model.addAttribute("is_tag", true);
|
||||
model.addAttribute("posts", posts);
|
||||
model.addAttribute("rainbow", rainbow);
|
||||
model.addAttribute("tag", tag);
|
||||
|
||||
Template template = freeMarkerConfigurer.getConfiguration().getTemplate(themeService.renderWithSuffix("tag"));
|
||||
String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
||||
|
||||
FileWriter fileWriter;
|
||||
|
||||
if (page == 1) {
|
||||
fileWriter = new FileWriter(getPageFile("tags/" + tag.getSlugName() + "/index.html"), "UTF-8");
|
||||
} else {
|
||||
fileWriter = new FileWriter(getPageFile("tags/" + tag.getSlugName() + "/page/" + page + "/index.html"), "UTF-8");
|
||||
}
|
||||
|
||||
fileWriter.write(html);
|
||||
|
||||
if (postPage.hasNext()) {
|
||||
generateTag(postPage.getNumber() + 2, tag);
|
||||
log.info("Generate tags/{}/page/{}/index.html", tag.getSlugName(), postPage.getNumber() + 2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate tags/index.html.
|
||||
*
|
||||
* @throws IOException IOException
|
||||
* @throws TemplateException TemplateException
|
||||
*/
|
||||
private void generateTags() throws IOException, TemplateException {
|
||||
log.info("Generate tags.html");
|
||||
|
||||
ModelMap model = new ModelMap();
|
||||
|
||||
if (!themeService.templateExists("tags.ftl")) {
|
||||
log.warn("tags.ftl not found,skip!");
|
||||
return;
|
||||
}
|
||||
|
||||
model.addAttribute("is_tags", true);
|
||||
Template template = freeMarkerConfigurer.getConfiguration().getTemplate(themeService.renderWithSuffix("tags"));
|
||||
String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
||||
FileWriter fileWriter = new FileWriter(getPageFile("tags/index.html"), "UTF-8");
|
||||
fileWriter.write(html);
|
||||
|
||||
log.info("Generate tags.html succeed.");
|
||||
|
||||
List<Tag> tags = tagService.listAll();
|
||||
for (Tag tag : tags) {
|
||||
generateTag(1, tag);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate rss.xml and feed.xml
|
||||
*
|
||||
* @throws IOException IOException
|
||||
* @throws TemplateException TemplateException
|
||||
*/
|
||||
private void generateRss() throws IOException, TemplateException {
|
||||
log.info("Generate rss.xml/feed.xml");
|
||||
|
||||
ModelMap model = new ModelMap();
|
||||
|
||||
model.addAttribute("posts", buildPosts(buildPostPageable(optionService.getRssPageSize())));
|
||||
|
||||
Template template = freeMarkerConfigurer.getConfiguration().getTemplate("common/web/rss.ftl");
|
||||
String xml = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
||||
FileWriter rssWriter = new FileWriter(getPageFile("rss.xml"), "UTF-8");
|
||||
rssWriter.write(xml);
|
||||
|
||||
FileWriter feedWriter = new FileWriter(getPageFile("feed.xml"), "UTF-8");
|
||||
feedWriter.write(xml);
|
||||
|
||||
log.info("Generate rss.xml/feed.xml succeed.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate atom.xml
|
||||
*
|
||||
* @throws IOException IOException
|
||||
* @throws TemplateException TemplateException
|
||||
*/
|
||||
private void generateAtom() throws IOException, TemplateException {
|
||||
log.info("Generate atom.xml");
|
||||
|
||||
ModelMap model = new ModelMap();
|
||||
|
||||
model.addAttribute("posts", buildPosts(buildPostPageable(optionService.getRssPageSize())));
|
||||
|
||||
Template template = freeMarkerConfigurer.getConfiguration().getTemplate("common/web/atom.ftl");
|
||||
String xml = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
||||
FileWriter fileWriter = new FileWriter(getPageFile("atom.xml"), "UTF-8");
|
||||
fileWriter.write(xml);
|
||||
|
||||
log.info("Generate atom.xml succeed.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate sitemap.html
|
||||
*
|
||||
* @throws IOException IOException
|
||||
* @throws TemplateException TemplateException
|
||||
*/
|
||||
private void generateSiteMapHtml() throws IOException, TemplateException {
|
||||
log.info("Generate sitemap.html");
|
||||
|
||||
ModelMap model = new ModelMap();
|
||||
|
||||
model.addAttribute("posts", buildPosts(buildPostPageable(optionService.getRssPageSize())));
|
||||
|
||||
Template template = freeMarkerConfigurer.getConfiguration().getTemplate("common/web/sitemap_html.ftl");
|
||||
String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
||||
FileWriter fileWriter = new FileWriter(getPageFile("sitemap.html"), "UTF-8");
|
||||
fileWriter.write(html);
|
||||
|
||||
log.info("Generate sitemap.html succeed.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate sitemap.xml
|
||||
*
|
||||
* @throws IOException IOException
|
||||
* @throws TemplateException TemplateException
|
||||
*/
|
||||
private void generateSiteMapXml() throws IOException, TemplateException {
|
||||
log.info("Generate sitemap.xml");
|
||||
|
||||
ModelMap model = new ModelMap();
|
||||
|
||||
model.addAttribute("posts", buildPosts(buildPostPageable(optionService.getRssPageSize())));
|
||||
|
||||
Template template = freeMarkerConfigurer.getConfiguration().getTemplate("common/web/sitemap_xml.ftl");
|
||||
String xml = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
||||
FileWriter fileWriter = new FileWriter(getPageFile("sitemap.xml"), "UTF-8");
|
||||
fileWriter.write(xml);
|
||||
|
||||
log.info("Generate sitemap.xml succeed.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate robots.txt
|
||||
*
|
||||
* @throws IOException IOException
|
||||
* @throws TemplateException TemplateException
|
||||
*/
|
||||
private void generateRobots() throws IOException, TemplateException {
|
||||
log.info("Generate robots.txt");
|
||||
|
||||
Template template = freeMarkerConfigurer.getConfiguration().getTemplate("common/web/robots.ftl");
|
||||
String txt = FreeMarkerTemplateUtils.processTemplateIntoString(template, null);
|
||||
FileWriter fileWriter = new FileWriter(getPageFile("robots.txt"), "UTF-8");
|
||||
fileWriter.write(txt);
|
||||
|
||||
log.info("Generate robots.txt succeed.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate README.md.
|
||||
*
|
||||
* @throws IOException IOException
|
||||
* @throws TemplateException TemplateException
|
||||
*/
|
||||
private void generateReadme() throws IOException, TemplateException {
|
||||
log.info("Generate readme.md");
|
||||
|
||||
Template template = freeMarkerConfigurer.getConfiguration().getTemplate("common/web/readme.ftl");
|
||||
String txt = FreeMarkerTemplateUtils.processTemplateIntoString(template, null);
|
||||
FileWriter fileWriter = new FileWriter(getPageFile("README.md"), "UTF-8");
|
||||
fileWriter.write(txt);
|
||||
|
||||
log.info("Generate readme.md succeed.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy current theme folder.
|
||||
*
|
||||
* @throws IOException IOException
|
||||
*/
|
||||
private void copyThemeFolder() throws IOException {
|
||||
ThemeProperty activatedTheme = themeService.getActivatedTheme();
|
||||
Path path = Paths.get(pagesDir.toString(), activatedTheme.getFolderName());
|
||||
FileUtils.createIfAbsent(path);
|
||||
FileUtils.copyFolder(Paths.get(activatedTheme.getThemePath()), path);
|
||||
cleanThemeFolder(Paths.get(pagesDir.toString(), activatedTheme.getFolderName()));
|
||||
}
|
||||
|
||||
private void cleanThemeFolder(Path themePath) {
|
||||
if (!Files.isDirectory(themePath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try (Stream<Path> pathStream = Files.list(themePath)) {
|
||||
|
||||
pathStream.forEach(path -> {
|
||||
if (!Files.isDirectory(path)) {
|
||||
for (String suffix : USELESS_FILE_SUFFIX) {
|
||||
if (suffix.contains(FileUtil.extName(path.toFile()))) {
|
||||
try {
|
||||
Files.delete(path);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (path.getFileName().toString().contains(".git")) {
|
||||
FileUtils.deleteFolderQuietly(path);
|
||||
} else {
|
||||
cleanThemeFolder(path);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
throw new ServiceException("Failed to list sub files", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy upload folder.
|
||||
*
|
||||
* @throws IOException IOException
|
||||
*/
|
||||
private void copyUpload() throws IOException {
|
||||
Path path = Paths.get(pagesDir.toString(), "upload");
|
||||
FileUtils.createIfAbsent(path);
|
||||
FileUtils.copyFolder(Paths.get(haloProperties.getWorkDir(), "upload"), path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy static folder.
|
||||
*
|
||||
* @throws IOException IOException
|
||||
*/
|
||||
private void copyStatic() throws IOException {
|
||||
FileUtils.copyFolder(Paths.get(haloProperties.getWorkDir(), "static"), pagesDir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build posts for feed
|
||||
*
|
||||
* @param pageable pageable
|
||||
* @return List<Post>
|
||||
*/
|
||||
private List<PostDetailVO> buildPosts(@NonNull Pageable pageable) {
|
||||
Page<Post> postPage = postService.pageBy(PostStatus.PUBLISHED, pageable);
|
||||
Page<PostDetailVO> posts = postService.convertToDetailVo(postPage);
|
||||
posts.getContent().forEach(postListVO -> {
|
||||
try {
|
||||
// Encode post url
|
||||
postListVO.setUrl(URLEncoder.encode(postListVO.getUrl(), StandardCharsets.UTF_8.name()));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
log.warn("Failed to encode url: " + postListVO.getUrl(), e);
|
||||
}
|
||||
});
|
||||
return posts.getContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds page info for post.
|
||||
*
|
||||
* @param size page size
|
||||
* @return page info
|
||||
*/
|
||||
@NonNull
|
||||
private Pageable buildPostPageable(int size) {
|
||||
return PageRequest.of(0, size, Sort.by(DESC, "createTime"));
|
||||
}
|
||||
|
||||
private File getPageFile(String subPath) {
|
||||
Path path = Paths.get(pagesDir.toString(), subPath);
|
||||
return path.toFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get static deploy type from options.
|
||||
*
|
||||
* @return static deploy type
|
||||
*/
|
||||
@NonNull
|
||||
private StaticDeployType getStaticDeployType() {
|
||||
return optionService.getEnumByPropertyOrDefault(StaticDeployProperties.DEPLOY_TYPE, StaticDeployType.class, StaticDeployType.GIT);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package run.halo.app.service.impl;
|
||||
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
@ -59,6 +60,7 @@ public class StaticStorageServiceImpl implements StaticStorageService {
|
|||
|
||||
pathStream.forEach(path -> {
|
||||
StaticFile staticFile = new StaticFile();
|
||||
staticFile.setId(IdUtil.fastSimpleUUID());
|
||||
staticFile.setName(path.getFileName().toString());
|
||||
staticFile.setPath(path.toString());
|
||||
staticFile.setRelativePath(StringUtils.removeStart(path.toString(), staticDir.toString()));
|
||||
|
|
|
@ -352,6 +352,14 @@ public class ThemeServiceImpl implements ThemeService {
|
|||
return String.format(RENDER_TEMPLATE, activatedTheme.getFolderName(), pageName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String renderWithSuffix(String pageName) {
|
||||
// Get activated theme
|
||||
ThemeProperty activatedTheme = getActivatedTheme();
|
||||
// Build render url
|
||||
return String.format(RENDER_TEMPLATE_SUFFIX, activatedTheme.getFolderName(), pageName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getActivatedThemeId() {
|
||||
if (activatedThemeId == null) {
|
||||
|
|
|
@ -6,6 +6,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import run.halo.app.exception.ServiceException;
|
||||
|
@ -160,6 +161,25 @@ public class ThemeSettingServiceImpl extends AbstractCrudService<ThemeSetting, I
|
|||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ThemeSetting> replaceUrl(String oldUrl, String newUrl) {
|
||||
List<ThemeSetting> themeSettings = listAll();
|
||||
List<ThemeSetting> replaced = new ArrayList<>();
|
||||
themeSettings.forEach(themeSetting -> {
|
||||
if (StringUtils.isNotEmpty(themeSetting.getValue())) {
|
||||
themeSetting.setValue(themeSetting.getValue().replaceAll(oldUrl, newUrl));
|
||||
}
|
||||
replaced.add(themeSetting);
|
||||
});
|
||||
return updateInBatch(replaced);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void deleteInactivated() {
|
||||
themeSettingRepository.deleteByThemeIdIsNot(themeService.getActivatedThemeId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets config item map. (key: item name, value: item)
|
||||
*
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
<div style="text-align:center">
|
||||
<img src="${user.avatar!}" width="100" height="100" alt="${user.nickname!}">
|
||||
<h3>${options.blog_title!}</h3>
|
||||
<h4>
|
||||
<a href="${context!}" target="_blank">${context!}</a>
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
<@postTag method="archiveYear">
|
||||
<#list archives as archive>
|
||||
## ${archive.year?c}
|
||||
<#list archive.posts?sort_by("createTime")?reverse as post>
|
||||
- <a href="${context!}/archives/${post.url!}" title="${post.title!}" target="_blank">${post.title!}</a>
|
||||
</#list>
|
||||
</#list>
|
||||
</@postTag>
|
||||
|
||||
## 分类目录
|
||||
<@categoryTag method="list">
|
||||
<#list categories as category>
|
||||
- <a href="${context!}/categories/${category.slugName!}" target="_blank">${category.name!}</a>
|
||||
</#list>
|
||||
</@categoryTag>
|
||||
|
||||
## 标签
|
||||
<@tagTag method="list">
|
||||
<#list tags as tag>
|
||||
- <a href="${context!}/tags/${tag.slugName!}" target="_blank">${tag.name!}</a>
|
||||
</#list>
|
||||
</@tagTag>
|
Loading…
Reference in New Issue