mirror of https://github.com/halo-dev/halo
Refactor theme configuration and theme property resolvers
parent
eeeee26b7c
commit
dda7225b2d
|
@ -0,0 +1,18 @@
|
|||
package run.halo.app.exception;
|
||||
|
||||
/**
|
||||
* Theme configuration missing exception.
|
||||
*
|
||||
* @author johnniang
|
||||
* @date 4/11/19
|
||||
*/
|
||||
public class ThemeConfigMissingException extends BadRequestException {
|
||||
|
||||
public ThemeConfigMissingException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ThemeConfigMissingException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package run.halo.app.exception;
|
||||
|
||||
/**
|
||||
* Theme property missing exception.
|
||||
*
|
||||
* @author johnniang
|
||||
* @date 4/11/19
|
||||
*/
|
||||
public class ThemePropertyMissingException extends BadRequestException {
|
||||
|
||||
public ThemePropertyMissingException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ThemePropertyMissingException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package run.halo.app.handler.theme;
|
||||
|
||||
import org.springframework.lang.NonNull;
|
||||
import run.halo.app.handler.theme.support.Group;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
@ -22,4 +23,5 @@ public interface ThemeConfigResolver {
|
|||
*/
|
||||
@NonNull
|
||||
List<Group> resolve(@NonNull String content) throws IOException;
|
||||
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import org.springframework.lang.NonNull;
|
|||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.stereotype.Component;
|
||||
import run.halo.app.handler.theme.impl.YamlThemeConfigResolverImpl;
|
||||
import run.halo.app.handler.theme.support.Group;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
package run.halo.app.handler.theme;
|
||||
|
||||
import org.springframework.lang.NonNull;
|
||||
import run.halo.app.handler.theme.support.ThemeProperty;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Theme file resolver.
|
||||
*
|
||||
* @author johnniang
|
||||
* @date 4/11/19
|
||||
*/
|
||||
public interface ThemePropertyResolver {
|
||||
|
||||
/**
|
||||
* Resolves the theme file.
|
||||
*
|
||||
* @param content file content must not be null
|
||||
* @return theme file
|
||||
*/
|
||||
@NonNull
|
||||
ThemeProperty resolve(@NonNull String content) throws IOException;
|
||||
}
|
|
@ -3,10 +3,11 @@ package run.halo.app.handler.theme.impl;
|
|||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
|
||||
import org.springframework.lang.Nullable;
|
||||
import run.halo.app.handler.theme.Group;
|
||||
import run.halo.app.handler.theme.Item;
|
||||
import run.halo.app.handler.theme.Option;
|
||||
import org.springframework.stereotype.Service;
|
||||
import run.halo.app.handler.theme.ThemeConfigResolver;
|
||||
import run.halo.app.handler.theme.support.Group;
|
||||
import run.halo.app.handler.theme.support.Item;
|
||||
import run.halo.app.handler.theme.support.Option;
|
||||
import run.halo.app.model.enums.DataType;
|
||||
import run.halo.app.model.enums.InputType;
|
||||
|
||||
|
@ -22,6 +23,7 @@ import java.util.Map;
|
|||
* @author johnniang
|
||||
* @date 4/10/19
|
||||
*/
|
||||
@Service
|
||||
public class YamlThemeConfigResolverImpl implements ThemeConfigResolver {
|
||||
|
||||
private final ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory());
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
package run.halo.app.handler.theme.impl;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.Assert;
|
||||
import run.halo.app.handler.theme.ThemePropertyResolver;
|
||||
import run.halo.app.handler.theme.support.ThemeProperty;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Yaml theme file resolver.
|
||||
*
|
||||
* @author johnniang
|
||||
* @date 4/11/19
|
||||
*/
|
||||
@Service
|
||||
public class YamlThemePropertyResolver implements ThemePropertyResolver {
|
||||
|
||||
private final ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory());
|
||||
|
||||
@Override
|
||||
public ThemeProperty resolve(String content) throws IOException {
|
||||
Assert.hasText(content, "Theme file content must not be null");
|
||||
|
||||
return yamlMapper.readValue(content, ThemeProperty.class);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package run.halo.app.handler.theme;
|
||||
package run.halo.app.handler.theme.support;
|
||||
|
||||
import lombok.Data;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package run.halo.app.handler.theme;
|
||||
package run.halo.app.handler.theme.support;
|
||||
|
||||
import lombok.Data;
|
||||
import run.halo.app.model.enums.DataType;
|
|
@ -1,4 +1,4 @@
|
|||
package run.halo.app.handler.theme;
|
||||
package run.halo.app.handler.theme.support;
|
||||
|
||||
import lombok.Data;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package run.halo.app.model.support;
|
||||
package run.halo.app.handler.theme.support;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
|
@ -44,12 +44,7 @@ public class ThemeProperty {
|
|||
/**
|
||||
* Theme author.
|
||||
*/
|
||||
private String author;
|
||||
|
||||
/**
|
||||
* Theme author website.
|
||||
*/
|
||||
private String authorWebsite;
|
||||
private Author author;
|
||||
|
||||
/**
|
||||
* Folder name.
|
||||
|
@ -70,4 +65,18 @@ public class ThemeProperty {
|
|||
* Screenshots url.
|
||||
*/
|
||||
private String screenshots;
|
||||
|
||||
@Data
|
||||
public static class Author {
|
||||
|
||||
/**
|
||||
* Author name.
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* Author website.
|
||||
*/
|
||||
private String website;
|
||||
}
|
||||
}
|
|
@ -44,11 +44,6 @@ public class HaloConst {
|
|||
*/
|
||||
public static Map<String, String> OWO_MAP = Collections.emptyMap();
|
||||
|
||||
/**
|
||||
* All of the themes
|
||||
*/
|
||||
public static List<Theme> THEMES;
|
||||
|
||||
/**
|
||||
* user_session
|
||||
*/
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
package run.halo.app.model.support;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Theme DTO
|
||||
*
|
||||
* @author : RYAN0UP
|
||||
* @date : 2018/1/3
|
||||
*/
|
||||
@Data
|
||||
@Deprecated
|
||||
public class Theme implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Theme key,is theme folder name.
|
||||
*/
|
||||
private String key;
|
||||
|
||||
/**
|
||||
* Is support setting options
|
||||
*/
|
||||
private boolean hasOptions;
|
||||
|
||||
/**
|
||||
* Theme properties
|
||||
*/
|
||||
private ThemeProperty properties;
|
||||
}
|
|
@ -1,10 +1,9 @@
|
|||
package run.halo.app.service;
|
||||
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import run.halo.app.handler.theme.Group;
|
||||
import run.halo.app.handler.theme.support.Group;
|
||||
import run.halo.app.handler.theme.support.ThemeProperty;
|
||||
import run.halo.app.model.support.ThemeFile;
|
||||
import run.halo.app.model.support.ThemeProperty;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
|
@ -20,7 +19,12 @@ public interface ThemeService {
|
|||
/**
|
||||
* Theme property file name.
|
||||
*/
|
||||
String THEME_PROPERTY_FILE_NAME = "theme.properties";
|
||||
String THEME_PROPERTY_FILE_NAME = "theme.yaml";
|
||||
|
||||
/**
|
||||
* Theme property file name.
|
||||
*/
|
||||
String[] THEME_PROPERTY_FILE_NAMES = {"theme.yaml", "theme.yml"};
|
||||
|
||||
|
||||
/**
|
||||
|
@ -36,7 +40,7 @@ public interface ThemeService {
|
|||
/**
|
||||
* These file names cannot be displayed.
|
||||
*/
|
||||
String[] FILTER_FILES = {".git", ".DS_Store", THEME_PROPERTY_FILE_NAME, "options.yaml", "option.yml"};
|
||||
String[] FILTER_FILES = {".git", ".DS_Store", "theme.yaml", "theme.yml", "options.yaml", "option.yml"};
|
||||
|
||||
/**
|
||||
* Theme folder location.
|
||||
|
@ -82,6 +86,7 @@ public interface ThemeService {
|
|||
*
|
||||
* @return list of themes
|
||||
*/
|
||||
@NonNull
|
||||
List<ThemeProperty> getThemes();
|
||||
|
||||
/**
|
||||
|
|
|
@ -15,16 +15,14 @@ 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.BadRequestException;
|
||||
import run.halo.app.exception.ForbiddenException;
|
||||
import run.halo.app.exception.NotFoundException;
|
||||
import run.halo.app.exception.ServiceException;
|
||||
import run.halo.app.handler.theme.Group;
|
||||
import run.halo.app.handler.theme.ThemeConfigResolvers;
|
||||
import run.halo.app.exception.*;
|
||||
import run.halo.app.handler.theme.ThemeConfigResolver;
|
||||
import run.halo.app.handler.theme.ThemePropertyResolver;
|
||||
import run.halo.app.handler.theme.support.Group;
|
||||
import run.halo.app.handler.theme.support.ThemeProperty;
|
||||
import run.halo.app.model.properties.PrimaryProperties;
|
||||
import run.halo.app.model.support.HaloConst;
|
||||
import run.halo.app.model.support.ThemeFile;
|
||||
import run.halo.app.model.support.ThemeProperty;
|
||||
import run.halo.app.service.OptionService;
|
||||
import run.halo.app.service.ThemeService;
|
||||
import run.halo.app.utils.FilenameUtils;
|
||||
|
@ -32,8 +30,6 @@ 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;
|
||||
|
@ -54,10 +50,16 @@ public class ThemeServiceImpl implements ThemeService {
|
|||
* Theme work directory.
|
||||
*/
|
||||
private final Path workDir;
|
||||
|
||||
private final OptionService optionService;
|
||||
|
||||
private final StringCacheStore cacheStore;
|
||||
|
||||
private final Configuration configuration;
|
||||
private final ThemeConfigResolvers resolvers;
|
||||
|
||||
private final ThemeConfigResolver themeConfigResolver;
|
||||
|
||||
private final ThemePropertyResolver themePropertyResolver;
|
||||
/**
|
||||
* Activated theme id.
|
||||
*/
|
||||
|
@ -67,12 +69,14 @@ public class ThemeServiceImpl implements ThemeService {
|
|||
OptionService optionService,
|
||||
StringCacheStore cacheStore,
|
||||
Configuration configuration,
|
||||
ThemeConfigResolvers resolvers) {
|
||||
ThemeConfigResolver themeConfigResolver,
|
||||
ThemePropertyResolver themePropertyResolver) {
|
||||
this.optionService = optionService;
|
||||
this.cacheStore = cacheStore;
|
||||
this.configuration = configuration;
|
||||
this.resolvers = resolvers;
|
||||
this.themeConfigResolver = themeConfigResolver;
|
||||
workDir = Paths.get(haloProperties.getWorkDir(), THEME_FOLDER);
|
||||
this.themePropertyResolver = themePropertyResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -89,13 +93,13 @@ public class ThemeServiceImpl implements ThemeService {
|
|||
log.debug("Themes type: [{}]", themes.getClass());
|
||||
log.debug("Themes: [{}]", themes);
|
||||
|
||||
return themes.stream().filter(themeProperty -> themeProperty.getId().equals(themeId)).findFirst();
|
||||
return themes.stream().filter(themeProperty -> StringUtils.equals(themeProperty.getId(), themeId)).findFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ThemeProperty> getThemes() {
|
||||
// Fetch themes from cache
|
||||
return cacheStore.get(THEMES_CACHE_KEY).map(themesCache -> {
|
||||
List<ThemeProperty> result = cacheStore.get(THEMES_CACHE_KEY).map(themesCache -> {
|
||||
try {
|
||||
// Convert to theme properties
|
||||
ThemeProperty[] themeProperties = JsonUtils.jsonToObject(themesCache, ThemeProperty[].class);
|
||||
|
@ -123,6 +127,8 @@ public class ThemeServiceImpl implements ThemeService {
|
|||
throw new ServiceException("Themes scan failed", e);
|
||||
}
|
||||
});
|
||||
|
||||
return CollectionUtils.isEmpty(result) ? Collections.emptyList() : result;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -141,10 +147,9 @@ public class ThemeServiceImpl implements ThemeService {
|
|||
|
||||
@Override
|
||||
public List<String> getCustomTpl(String themeId) {
|
||||
// TODO 这里的参数是有问题的,等待修复。
|
||||
final List<String> templates = new ArrayList<>();
|
||||
final File themePath = new File(getThemeBasePath(), themeId);
|
||||
final File[] themeFiles = themePath.listFiles();
|
||||
List<String> templates = new ArrayList<>();
|
||||
File themePath = new File(getThemeBasePath(), themeId);
|
||||
File[] themeFiles = themePath.listFiles();
|
||||
if (null != themeFiles && themeFiles.length > 0) {
|
||||
for (File file : themeFiles) {
|
||||
String[] split = StrUtil.removeSuffix(file.getName(), HaloConst.SUFFIX_FTL).split("_");
|
||||
|
@ -245,8 +250,9 @@ public class ThemeServiceImpl implements ThemeService {
|
|||
|
||||
// Read the yaml file
|
||||
String optionContent = new String(Files.readAllBytes(optionsPath));
|
||||
|
||||
// Resolve it
|
||||
return resolvers.resolve(optionContent);
|
||||
return themeConfigResolver.resolve(optionContent);
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
|
@ -394,35 +400,47 @@ public class ThemeServiceImpl implements ThemeService {
|
|||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Path getPropertyPath(@NonNull Path themePath) {
|
||||
Assert.notNull(themePath, "Theme path must not be null");
|
||||
|
||||
for (String propertyPathName : THEME_PROPERTY_FILE_NAMES) {
|
||||
Path propertyPath = themePath.resolve(propertyPathName);
|
||||
|
||||
log.debug("Attempting to find property file: [{}]", propertyPath);
|
||||
if (Files.exists(propertyPath) && Files.isReadable(propertyPath)) {
|
||||
log.debug("Found property file: [{}]", propertyPath);
|
||||
return propertyPath;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets theme property.
|
||||
*
|
||||
* @param themePath must not be null
|
||||
* @return theme property
|
||||
*/
|
||||
@NonNull
|
||||
private ThemeProperty getProperty(@NonNull Path themePath) {
|
||||
Assert.notNull(themePath, "Theme path must not be null");
|
||||
|
||||
Path propertyPath = themePath.resolve(THEME_PROPERTY_FILE_NAME);
|
||||
if (!Files.exists(propertyPath)) {
|
||||
return null;
|
||||
Path propertyPath = getPropertyPath(themePath);
|
||||
|
||||
if (propertyPath == null) {
|
||||
throw new ThemePropertyMissingException(themePath + " dose not exist any theme property file").setErrorData(themePath);
|
||||
}
|
||||
|
||||
try {
|
||||
Properties properties = new Properties();
|
||||
// Load properties
|
||||
properties.load(new InputStreamReader(Files.newInputStream(propertyPath), StandardCharsets.UTF_8));
|
||||
// Get property content
|
||||
String propertyContent = new String(Files.readAllBytes(propertyPath));
|
||||
|
||||
ThemeProperty themeProperty = new ThemeProperty();
|
||||
themeProperty.setId(properties.getProperty("theme.id"));
|
||||
themeProperty.setName(properties.getProperty("theme.name"));
|
||||
themeProperty.setWebsite(properties.getProperty("theme.website"));
|
||||
themeProperty.setDescription(properties.getProperty("theme.description"));
|
||||
themeProperty.setLogo(properties.getProperty("theme.logo"));
|
||||
themeProperty.setVersion(properties.getProperty("theme.version"));
|
||||
themeProperty.setAuthor(properties.getProperty("theme.author"));
|
||||
themeProperty.setAuthorWebsite(properties.getProperty("theme.author.website"));
|
||||
// Resolve the base properties
|
||||
ThemeProperty themeProperty = themePropertyResolver.resolve(propertyContent);
|
||||
|
||||
// Resolve additional properties
|
||||
themeProperty.setThemePath(themePath.toString());
|
||||
themeProperty.setHasOptions(hasOptions(themePath));
|
||||
themeProperty.setActivated(false);
|
||||
|
@ -435,15 +453,14 @@ public class ThemeServiceImpl implements ThemeService {
|
|||
"/",
|
||||
screenshotsName)));
|
||||
|
||||
if (themeProperty.getId().equals(getActivatedThemeId())) {
|
||||
if (StringUtils.equals(themeProperty.getId(), getActivatedThemeId())) {
|
||||
// Set activation
|
||||
themeProperty.setActivated(true);
|
||||
}
|
||||
|
||||
return themeProperty;
|
||||
} catch (IOException e) {
|
||||
// TODO Consider to ignore this error, then return null
|
||||
throw new ServiceException("Failed to load: " + themePath.toString(), e);
|
||||
throw new ThemePropertyMissingException("Cannot resolve theme property", e).setErrorData(propertyPath.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,10 +2,10 @@ package run.halo.app.web.controller.admin.api;
|
|||
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import run.halo.app.handler.theme.Group;
|
||||
import run.halo.app.handler.theme.support.Group;
|
||||
import run.halo.app.model.support.BaseResponse;
|
||||
import run.halo.app.model.support.ThemeFile;
|
||||
import run.halo.app.model.support.ThemeProperty;
|
||||
import run.halo.app.handler.theme.support.ThemeProperty;
|
||||
import run.halo.app.service.ThemeService;
|
||||
import run.halo.app.service.ThemeSettingService;
|
||||
|
||||
|
|
|
@ -23,10 +23,10 @@ spring:
|
|||
password: 123456
|
||||
|
||||
# MySql 配置
|
||||
# driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
# url: jdbc:mysql://127.0.0.1:3306/halodb?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
|
||||
# username: root
|
||||
# password: 123456
|
||||
# driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
# url: jdbc:mysql://127.0.0.1:3306/halodb?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
|
||||
# username: root
|
||||
# password: 123456
|
||||
|
||||
h2:
|
||||
console:
|
||||
|
@ -49,8 +49,8 @@ logging:
|
|||
level:
|
||||
run.halo.app: DEBUG
|
||||
org.hibernate: INFO
|
||||
org.hibernate.type.descriptor.sql.BasicBinder: TRACE
|
||||
org.hibernate.type.descriptor.sql.BasicExtractor: TRACE
|
||||
org.hibernate.type.descriptor.sql.BasicBinder: INFO
|
||||
org.hibernate.type.descriptor.sql.BasicExtractor: INFO
|
||||
file: ./logs/log.log
|
||||
|
||||
halo:
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
package run.halo.app.handler.theme.impl;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
|
||||
import org.junit.Test;
|
||||
import run.halo.app.handler.theme.support.ThemeProperty;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author johnniang
|
||||
* @date 4/11/19
|
||||
*/
|
||||
public class YamlThemePropertyResolverTest {
|
||||
|
||||
private final ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory());
|
||||
|
||||
@Test
|
||||
public void directResolveTest() throws IOException {
|
||||
String yaml = "id: viosey_material\n" +
|
||||
"name: Material\n" +
|
||||
"author:\n" +
|
||||
" name: Viosey\n" +
|
||||
" website: https://viosey.com\n" +
|
||||
"description: Nature, Pure | 原质,纯粹\n" +
|
||||
"logo: https://avatars0.githubusercontent.com/u/8141232?s=460&v=4\n" +
|
||||
"website: https://github.com/viosey/hexo-theme-material\n" +
|
||||
"version: 1.0";
|
||||
|
||||
ThemeProperty themeProperty = yamlMapper.readValue(yaml, ThemeProperty.class);
|
||||
|
||||
System.out.println(themeProperty);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
package run.halo.app.utils;
|
||||
|
||||
import org.junit.Test;
|
||||
import run.halo.app.handler.theme.Group;
|
||||
import run.halo.app.handler.theme.support.Group;
|
||||
import run.halo.app.handler.theme.impl.YamlThemeConfigResolverImpl;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -70,7 +70,6 @@ public class YamlTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void readAnotherYamlTest() throws IOException {
|
||||
String yaml = "sns:\n" +
|
||||
" label: 社交资料设置\n" +
|
||||
|
|
Loading…
Reference in New Issue