From e098e44d58153e9bc852fe9565d884a32bca67a6 Mon Sep 17 00:00:00 2001 From: johnniang Date: Tue, 9 Apr 2019 10:18:39 +0800 Subject: [PATCH] Enhance ThemeService --- .../halo/app/cache/InMemoryCacheStore.java | 12 ++- .../run/halo/app/service/ThemeService.java | 24 ++++- .../app/service/impl/ThemeServiceImpl.java | 97 +++++++++++-------- .../controller/admin/api/ThemeController.java | 6 ++ 4 files changed, 96 insertions(+), 43 deletions(-) diff --git a/src/main/java/run/halo/app/cache/InMemoryCacheStore.java b/src/main/java/run/halo/app/cache/InMemoryCacheStore.java index 1c3e97698..1d9c46cfc 100644 --- a/src/main/java/run/halo/app/cache/InMemoryCacheStore.java +++ b/src/main/java/run/halo/app/cache/InMemoryCacheStore.java @@ -3,6 +3,7 @@ package run.halo.app.cache; import lombok.extern.slf4j.Slf4j; import org.springframework.util.Assert; +import javax.annotation.PreDestroy; import java.util.Optional; import java.util.Timer; import java.util.TimerTask; @@ -28,6 +29,8 @@ public class InMemoryCacheStore extends StringCacheStore { */ private final static ConcurrentHashMap> cacheContainer = new ConcurrentHashMap<>(); + private final Timer timer; + /** * Lock. */ @@ -35,7 +38,8 @@ public class InMemoryCacheStore extends StringCacheStore { public InMemoryCacheStore() { // Run a cache store cleaner - new Timer().scheduleAtFixedRate(new CacheExpiryCleaner(), 0, PERIOD); + timer = new Timer(); + timer.scheduleAtFixedRate(new CacheExpiryCleaner(), 0, PERIOD); } @Override @@ -106,4 +110,10 @@ public class InMemoryCacheStore extends StringCacheStore { }); } } + + @PreDestroy + public void preDestroy() { + log.debug("Cancelling all timer tasks"); + timer.cancel(); + } } diff --git a/src/main/java/run/halo/app/service/ThemeService.java b/src/main/java/run/halo/app/service/ThemeService.java index 78b4d2656..39e781c79 100644 --- a/src/main/java/run/halo/app/service/ThemeService.java +++ b/src/main/java/run/halo/app/service/ThemeService.java @@ -8,6 +8,7 @@ import run.halo.app.model.support.ThemeProperty; import java.io.File; import java.nio.file.Path; import java.util.List; +import java.util.Optional; /** * @author : RYAN0UP @@ -15,6 +16,24 @@ import java.util.List; */ public interface ThemeService { + /** + * Get theme property by theme id. + * + * @param themeId must not be blank + * @return theme property + */ + @NonNull + ThemeProperty getThemeOfNonNullBy(@NonNull String themeId); + + /** + * Get theme property by theme id. + * + * @param themeId must not be blank + * @return a optional theme property + */ + @NonNull + Optional getThemeBy(@NonNull String themeId); + /** * Gets all themes * @@ -58,10 +77,10 @@ public interface ThemeService { /** * Judging whether theme exists under template path * - * @param theme theme name + * @param themeId theme name * @return boolean */ - boolean isThemeExist(@NonNull String theme); + boolean isThemeExist(@NonNull String themeId); /** * Gets theme base path. @@ -83,7 +102,6 @@ public interface ThemeService { * @param absolutePath absolute path * @return template content */ - @Deprecated String getTemplateContent(@NonNull String absolutePath); /** diff --git a/src/main/java/run/halo/app/service/impl/ThemeServiceImpl.java b/src/main/java/run/halo/app/service/impl/ThemeServiceImpl.java index c4577232e..b3490f225 100644 --- a/src/main/java/run/halo/app/service/impl/ThemeServiceImpl.java +++ b/src/main/java/run/halo/app/service/impl/ThemeServiceImpl.java @@ -5,7 +5,6 @@ import cn.hutool.core.io.file.FileReader; import cn.hutool.core.io.file.FileWriter; import cn.hutool.core.text.StrBuilder; import cn.hutool.core.util.StrUtil; -import cn.hutool.setting.dialect.Props; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import lombok.extern.slf4j.Slf4j; @@ -15,6 +14,7 @@ import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import run.halo.app.cache.StringCacheStore; import run.halo.app.config.properties.HaloProperties; +import run.halo.app.exception.ForbiddenException; import run.halo.app.exception.NotFoundException; import run.halo.app.exception.ServiceException; import run.halo.app.model.properties.PrimaryProperties; @@ -28,6 +28,8 @@ import run.halo.app.utils.JsonUtils; import java.io.File; import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -78,6 +80,9 @@ public class ThemeServiceImpl implements ThemeService { private final static String THEMES_CACHE_KEY = "themes"; + /** + * Theme work directory. + */ private final Path workDir; private final ObjectMapper yamlMapper; @@ -95,6 +100,23 @@ public class ThemeServiceImpl implements ThemeService { workDir = Paths.get(haloProperties.getWorkDir(), THEME_FOLDER); } + @Override + public ThemeProperty getThemeOfNonNullBy(String themeId) { + return getThemeBy(themeId).orElseThrow(() -> new NotFoundException("Theme with id: " + themeId + " was not found").setErrorData(themeId)); + } + + @Override + public Optional getThemeBy(String themeId) { + Assert.hasText(themeId, "Theme id must not be blank"); + + List themes = getThemes(); + + log.debug("Themes type: [{}]", themes.getClass()); + log.debug("Themes: [{}]", themes); + + return themes.stream().filter(themeProperty -> themeProperty.getId().equals(themeId)).findFirst(); + } + /** * Gets all themes * @@ -105,15 +127,14 @@ public class ThemeServiceImpl implements ThemeService { // Fetch themes from cache return cacheStore.get(THEMES_CACHE_KEY).map(themesCache -> { try { - @SuppressWarnings("unchecked") - List themes = JsonUtils.jsonToObject(themesCache, LinkedList.class); - return themes; + // Convert to theme properties + ThemeProperty[] themeProperties = JsonUtils.jsonToObject(themesCache, ThemeProperty[].class); + return Arrays.asList(themeProperties); } catch (IOException e) { throw new ServiceException("Failed to parse json", e); } }).orElseGet(() -> { try { - // List and filter sub folders List themePaths = Files.list(getBasePath()).filter(path -> Files.isDirectory(path)).collect(Collectors.toList()); @@ -142,6 +163,9 @@ public class ThemeServiceImpl implements ThemeService { */ @Override public List listThemeFolder(String absolutePath) { + // Check this path + checkDirectory(absolutePath); + final List templates = new ArrayList<>(); try { File absolutePathFile = new File(absolutePath); @@ -235,12 +259,13 @@ public class ThemeServiceImpl implements ThemeService { /** * Judging whether theme exists under template path * - * @param theme theme name + * @param themeId theme name * @return boolean */ @Override - public boolean isThemeExist(String theme) { - File file = new File(getThemeBasePath(), theme); + public boolean isThemeExist(String themeId) { + // TODO Get theme folder name by theme id + File file = new File(getThemeBasePath(), themeId); return file.exists(); } @@ -267,8 +292,10 @@ public class ThemeServiceImpl implements ThemeService { */ @Override public String getTemplateContent(String absolutePath) { - final FileReader fileReader = new FileReader(absolutePath); - return fileReader.readString(); + // Check the path + checkDirectory(absolutePath); + + return new FileReader(absolutePath).readString(); } /** @@ -341,6 +368,22 @@ public class ThemeServiceImpl implements ThemeService { // TODO Check existence of the theme } + /** + * Check if directory is valid or not. + * + * @param absoluteName must not be blank + * @throws ForbiddenException throws when the given absolute directory name is invalid + */ + private void checkDirectory(@NonNull String absoluteName) { + Assert.hasText(absoluteName, "Absolute name must not be blank"); + + boolean valid = Paths.get(absoluteName).startsWith(workDir); + + if (!valid) { + throw new ForbiddenException("You cannot access " + absoluteName).setErrorData(absoluteName); + } + } + /** * Gets theme property. * @@ -355,11 +398,10 @@ public class ThemeServiceImpl implements ThemeService { return null; } - File propertyFile = propertyPath.toFile(); - try { Properties properties = new Properties(); - properties.load(new java.io.FileReader(propertyFile)); + // Load properties + properties.load(new InputStreamReader(Files.newInputStream(propertyPath), StandardCharsets.UTF_8)); ThemeProperty themeProperty = new ThemeProperty(); themeProperty.setId(properties.getProperty("theme.id")); @@ -370,40 +412,16 @@ public class ThemeServiceImpl implements ThemeService { themeProperty.setVersion(properties.getProperty("theme.version")); themeProperty.setAuthor(properties.getProperty("theme.author")); themeProperty.setAuthorWebsite(properties.getProperty("theme.author.website")); - themeProperty.setFolderName(propertyFile.getName()); + themeProperty.setFolderName(themePath.getFileName().toString()); themeProperty.setHasOptions(hasOptions(propertyPath)); return themeProperty; } catch (IOException e) { - // TODO Consider Ignore this error, then return null + // TODO Consider to ignore this error, then return null throw new ServiceException("Failed to load: " + themePath.toString(), e); } } - /** - * Gets theme Properties. - * - * @param path path - * @return ThemeProperty - */ - private ThemeProperty getProperties(File path) { - File propertiesFile = new File(path, "theme.properties"); - ThemeProperty properties = new ThemeProperty(); - if (propertiesFile.exists()) { - Props props = new Props(propertiesFile); - properties.setId(props.getStr("theme.id")); - properties.setName(props.getStr("theme.name")); - properties.setWebsite(props.getStr("theme.website")); - properties.setDescription(props.getStr("theme.description")); - properties.setLogo(props.getStr("theme.logo")); - properties.setVersion(props.getStr("theme.version")); - properties.setAuthor(props.getStr("theme.author")); - properties.setAuthorWebsite(props.getStr("theme.author.website")); - properties.setFolderName(path.getName()); - } - return properties; - } - /** * Check existence of the options. * @@ -421,6 +439,7 @@ public class ThemeServiceImpl implements ThemeService { return true; } } + return false; } } diff --git a/src/main/java/run/halo/app/web/controller/admin/api/ThemeController.java b/src/main/java/run/halo/app/web/controller/admin/api/ThemeController.java index 2a8932586..a31193bbc 100644 --- a/src/main/java/run/halo/app/web/controller/admin/api/ThemeController.java +++ b/src/main/java/run/halo/app/web/controller/admin/api/ThemeController.java @@ -37,6 +37,12 @@ public class ThemeController { this.themeService = themeService; } + @GetMapping("{themeId}") + @ApiOperation("Gets theme property by theme id") + public ThemeProperty getBy(@PathVariable("themeId") String themeId) { + return themeService.getThemeOfNonNullBy(themeId); + } + /** * List all themes *