Enhance ThemeService

pull/146/head
johnniang 2019-04-09 10:18:39 +08:00
parent 9b3bdc8d5e
commit e098e44d58
4 changed files with 96 additions and 43 deletions

View File

@ -3,6 +3,7 @@ package run.halo.app.cache;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import javax.annotation.PreDestroy;
import java.util.Optional; import java.util.Optional;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
@ -28,6 +29,8 @@ public class InMemoryCacheStore extends StringCacheStore {
*/ */
private final static ConcurrentHashMap<String, CacheWrapper<String>> cacheContainer = new ConcurrentHashMap<>(); private final static ConcurrentHashMap<String, CacheWrapper<String>> cacheContainer = new ConcurrentHashMap<>();
private final Timer timer;
/** /**
* Lock. * Lock.
*/ */
@ -35,7 +38,8 @@ public class InMemoryCacheStore extends StringCacheStore {
public InMemoryCacheStore() { public InMemoryCacheStore() {
// Run a cache store cleaner // Run a cache store cleaner
new Timer().scheduleAtFixedRate(new CacheExpiryCleaner(), 0, PERIOD); timer = new Timer();
timer.scheduleAtFixedRate(new CacheExpiryCleaner(), 0, PERIOD);
} }
@Override @Override
@ -106,4 +110,10 @@ public class InMemoryCacheStore extends StringCacheStore {
}); });
} }
} }
@PreDestroy
public void preDestroy() {
log.debug("Cancelling all timer tasks");
timer.cancel();
}
} }

View File

@ -8,6 +8,7 @@ import run.halo.app.model.support.ThemeProperty;
import java.io.File; import java.io.File;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.List; import java.util.List;
import java.util.Optional;
/** /**
* @author : RYAN0UP * @author : RYAN0UP
@ -15,6 +16,24 @@ import java.util.List;
*/ */
public interface ThemeService { 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<ThemeProperty> getThemeBy(@NonNull String themeId);
/** /**
* Gets all themes * Gets all themes
* *
@ -58,10 +77,10 @@ public interface ThemeService {
/** /**
* Judging whether theme exists under template path * Judging whether theme exists under template path
* *
* @param theme theme name * @param themeId theme name
* @return boolean * @return boolean
*/ */
boolean isThemeExist(@NonNull String theme); boolean isThemeExist(@NonNull String themeId);
/** /**
* Gets theme base path. * Gets theme base path.
@ -83,7 +102,6 @@ public interface ThemeService {
* @param absolutePath absolute path * @param absolutePath absolute path
* @return template content * @return template content
*/ */
@Deprecated
String getTemplateContent(@NonNull String absolutePath); String getTemplateContent(@NonNull String absolutePath);
/** /**

View File

@ -5,7 +5,6 @@ import cn.hutool.core.io.file.FileReader;
import cn.hutool.core.io.file.FileWriter; import cn.hutool.core.io.file.FileWriter;
import cn.hutool.core.text.StrBuilder; import cn.hutool.core.text.StrBuilder;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.setting.dialect.Props;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -15,6 +14,7 @@ import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import run.halo.app.cache.StringCacheStore; import run.halo.app.cache.StringCacheStore;
import run.halo.app.config.properties.HaloProperties; import run.halo.app.config.properties.HaloProperties;
import run.halo.app.exception.ForbiddenException;
import run.halo.app.exception.NotFoundException; import run.halo.app.exception.NotFoundException;
import run.halo.app.exception.ServiceException; import run.halo.app.exception.ServiceException;
import run.halo.app.model.properties.PrimaryProperties; import run.halo.app.model.properties.PrimaryProperties;
@ -28,6 +28,8 @@ import run.halo.app.utils.JsonUtils;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
@ -78,6 +80,9 @@ public class ThemeServiceImpl implements ThemeService {
private final static String THEMES_CACHE_KEY = "themes"; private final static String THEMES_CACHE_KEY = "themes";
/**
* Theme work directory.
*/
private final Path workDir; private final Path workDir;
private final ObjectMapper yamlMapper; private final ObjectMapper yamlMapper;
@ -95,6 +100,23 @@ public class ThemeServiceImpl implements ThemeService {
workDir = Paths.get(haloProperties.getWorkDir(), THEME_FOLDER); 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<ThemeProperty> getThemeBy(String themeId) {
Assert.hasText(themeId, "Theme id must not be blank");
List<ThemeProperty> 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 * Gets all themes
* *
@ -105,15 +127,14 @@ public class ThemeServiceImpl implements ThemeService {
// Fetch themes from cache // Fetch themes from cache
return cacheStore.get(THEMES_CACHE_KEY).map(themesCache -> { return cacheStore.get(THEMES_CACHE_KEY).map(themesCache -> {
try { try {
@SuppressWarnings("unchecked") // Convert to theme properties
List<ThemeProperty> themes = JsonUtils.jsonToObject(themesCache, LinkedList.class); ThemeProperty[] themeProperties = JsonUtils.jsonToObject(themesCache, ThemeProperty[].class);
return themes; return Arrays.asList(themeProperties);
} catch (IOException e) { } catch (IOException e) {
throw new ServiceException("Failed to parse json", e); throw new ServiceException("Failed to parse json", e);
} }
}).orElseGet(() -> { }).orElseGet(() -> {
try { try {
// List and filter sub folders // List and filter sub folders
List<Path> themePaths = Files.list(getBasePath()).filter(path -> Files.isDirectory(path)).collect(Collectors.toList()); List<Path> themePaths = Files.list(getBasePath()).filter(path -> Files.isDirectory(path)).collect(Collectors.toList());
@ -142,6 +163,9 @@ public class ThemeServiceImpl implements ThemeService {
*/ */
@Override @Override
public List<ThemeFile> listThemeFolder(String absolutePath) { public List<ThemeFile> listThemeFolder(String absolutePath) {
// Check this path
checkDirectory(absolutePath);
final List<ThemeFile> templates = new ArrayList<>(); final List<ThemeFile> templates = new ArrayList<>();
try { try {
File absolutePathFile = new File(absolutePath); File absolutePathFile = new File(absolutePath);
@ -235,12 +259,13 @@ public class ThemeServiceImpl implements ThemeService {
/** /**
* Judging whether theme exists under template path * Judging whether theme exists under template path
* *
* @param theme theme name * @param themeId theme name
* @return boolean * @return boolean
*/ */
@Override @Override
public boolean isThemeExist(String theme) { public boolean isThemeExist(String themeId) {
File file = new File(getThemeBasePath(), theme); // TODO Get theme folder name by theme id
File file = new File(getThemeBasePath(), themeId);
return file.exists(); return file.exists();
} }
@ -267,8 +292,10 @@ public class ThemeServiceImpl implements ThemeService {
*/ */
@Override @Override
public String getTemplateContent(String absolutePath) { public String getTemplateContent(String absolutePath) {
final FileReader fileReader = new FileReader(absolutePath); // Check the path
return fileReader.readString(); checkDirectory(absolutePath);
return new FileReader(absolutePath).readString();
} }
/** /**
@ -341,6 +368,22 @@ public class ThemeServiceImpl implements ThemeService {
// TODO Check existence of the theme // 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. * Gets theme property.
* *
@ -355,11 +398,10 @@ public class ThemeServiceImpl implements ThemeService {
return null; return null;
} }
File propertyFile = propertyPath.toFile();
try { try {
Properties properties = new Properties(); 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 themeProperty = new ThemeProperty();
themeProperty.setId(properties.getProperty("theme.id")); themeProperty.setId(properties.getProperty("theme.id"));
@ -370,40 +412,16 @@ public class ThemeServiceImpl implements ThemeService {
themeProperty.setVersion(properties.getProperty("theme.version")); themeProperty.setVersion(properties.getProperty("theme.version"));
themeProperty.setAuthor(properties.getProperty("theme.author")); themeProperty.setAuthor(properties.getProperty("theme.author"));
themeProperty.setAuthorWebsite(properties.getProperty("theme.author.website")); themeProperty.setAuthorWebsite(properties.getProperty("theme.author.website"));
themeProperty.setFolderName(propertyFile.getName()); themeProperty.setFolderName(themePath.getFileName().toString());
themeProperty.setHasOptions(hasOptions(propertyPath)); themeProperty.setHasOptions(hasOptions(propertyPath));
return themeProperty; return themeProperty;
} catch (IOException e) { } 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); 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. * Check existence of the options.
* *
@ -421,6 +439,7 @@ public class ThemeServiceImpl implements ThemeService {
return true; return true;
} }
} }
return false; return false;
} }
} }

View File

@ -37,6 +37,12 @@ public class ThemeController {
this.themeService = themeService; 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 * List all themes
* *