Refactor and beauty codes

pull/146/head
johnniang 2019-04-09 01:18:52 +08:00
parent f87dc413e3
commit 468a0323b4
14 changed files with 256 additions and 196 deletions

View File

@ -17,7 +17,6 @@ import run.halo.app.model.params.UserParam;
import run.halo.app.model.properties.BlogProperties;
import run.halo.app.model.properties.PrimaryProperties;
import run.halo.app.model.support.HaloConst;
import run.halo.app.model.support.Theme;
import run.halo.app.service.OptionService;
import run.halo.app.service.ThemeService;
import run.halo.app.service.UserService;
@ -63,7 +62,6 @@ public class StartedListener implements ApplicationListener<ApplicationStartedEv
@Override
public void onApplicationEvent(ApplicationStartedEvent event) {
// save halo version to database
this.cacheThemes();
this.cacheOwo();
this.cacheActiveTheme();
this.printStartInfo();
@ -96,22 +94,12 @@ public class StartedListener implements ApplicationListener<ApplicationStartedEv
}
}
/**
* Cache themes to map
*/
private void cacheThemes() {
final List<Theme> themes = themeService.getThemes();
if (null != themes) {
HaloConst.THEMES = themes;
}
}
/**
* Get active theme
*/
private void cacheActiveTheme() {
try {
configuration.setSharedVariable("themeName", themeService.getTheme());
configuration.setSharedVariable("themeName", themeService.getActivatedTheme());
} catch (TemplateModelException e) {
log.error("", e);
}

View File

@ -23,16 +23,23 @@ import javax.persistence.*;
@EqualsAndHashCode(callSuper = true)
public class ThemeSetting extends BaseEntity {
/**
* Theme id as id.
*/
@Id
@Column(name = "id", columnDefinition = "varchar(255) not null")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "theme", columnDefinition = "varchar(255) not null")
private String theme;
private String id;
/**
* Setting key.
*/
@Column(name = "setting_key", columnDefinition = "varchar(255) not null")
private String key;
/**
* Setting value
*/
@Column(name = "setting_value", columnDefinition = "varchar(10239) not null")
private String value;

View File

@ -1,25 +0,0 @@
package run.halo.app.model.enums;
/**
* Option source.
*
* @author johnniang
* @date 4/1/19
*/
@Deprecated
public enum OptionSource implements ValueEnum<Integer> {
SYSTEM(0),
THEME(1);
private final int value;
OptionSource(int value) {
this.value = value;
}
@Override
public Integer getValue() {
return null;
}
}

View File

@ -1,19 +0,0 @@
package run.halo.app.model.enums.converter;
import run.halo.app.model.enums.OptionSource;
import javax.persistence.Converter;
/**
* OptionSource converter.
*
* @author johnniang
* @date 4/1/19
*/
@Converter(autoApply = true)
public class OptionSourceConverter extends AbstractConverter<OptionSource, Integer> {
public OptionSourceConverter() {
super(OptionSource.class);
}
}

View File

@ -11,6 +11,7 @@ import java.io.Serializable;
* @date : 2018/1/3
*/
@Data
@Deprecated
public class Theme implements Serializable {
private static final long serialVersionUID = 1L;
@ -28,5 +29,5 @@ public class Theme implements Serializable {
/**
* Theme properties
*/
private ThemeProperties properties;
private ThemeProperty properties;
}

View File

@ -1,27 +0,0 @@
package run.halo.app.model.support;
import lombok.Data;
/**
* @author : RYAN0UP
* @date : 2019-03-22
*/
@Data
public class ThemeProperties {
private String id;
private String name;
private String website;
private String description;
private String logo;
private String version;
private String author;
private String authorWebsite;
}

View File

@ -0,0 +1,61 @@
package run.halo.app.model.support;
import lombok.Data;
/**
* @author : RYAN0UP
* @date : 2019-03-22
*/
@Data
public class ThemeProperty {
/**
* Theme id.
*/
private String id;
/**
* Theme name.
*/
private String name;
/**
* Theme website.
*/
private String website;
/**
* Theme description.
*/
private String description;
/**
* Theme logo.
*/
private String logo;
/**
* Theme version.
*/
private String version;
/**
* Theme author.
*/
private String author;
/**
* Theme author website.
*/
private String authorWebsite;
/**
* Folder name.
*/
private String folderName;
/**
* Has options.
*/
private Boolean hasOptions;
}

View File

@ -2,9 +2,8 @@ package run.halo.app.service;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import run.halo.app.model.support.Theme;
import run.halo.app.model.support.ThemeFile;
import run.halo.app.model.support.ThemeProperties;
import run.halo.app.model.support.ThemeProperty;
import java.io.File;
import java.nio.file.Path;
@ -21,7 +20,7 @@ public interface ThemeService {
*
* @return list of themes
*/
List<Theme> getThemes();
List<ThemeProperty> getThemes();
/**
* Lists theme folder by absolute path.
@ -78,14 +77,6 @@ public interface ThemeService {
*/
Path getBasePath();
/**
* Get theme Properties.
*
* @param path path
* @return ThemeProperties
*/
ThemeProperties getProperties(@NonNull File path);
/**
* Get template content by template absolute path.
*
@ -130,11 +121,17 @@ public interface ThemeService {
String render(@NonNull String pageName);
/**
* Gets current theme name.
* Gets current theme id.
*
* @return current theme name
* @return current theme id
*/
@NonNull
String getTheme();
String getActivatedTheme();
/**
* Actives a theme.
*
* @param themeId theme id must not be blank
*/
void activeTheme(@NonNull String themeId);
}

View File

@ -9,27 +9,30 @@ import cn.hutool.setting.dialect.Props;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service;
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.NotFoundException;
import run.halo.app.exception.ServiceException;
import run.halo.app.model.properties.PrimaryProperties;
import run.halo.app.model.support.HaloConst;
import run.halo.app.model.support.Theme;
import run.halo.app.model.support.ThemeFile;
import run.halo.app.model.support.ThemeProperties;
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;
import run.halo.app.utils.JsonUtils;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;
import static run.halo.app.model.support.HaloConst.DEFAULT_THEME_NAME;
@ -42,39 +45,52 @@ import static run.halo.app.model.support.HaloConst.DEFAULT_THEME_NAME;
public class ThemeServiceImpl implements ThemeService {
/**
* The type of file that can be modified.
* Theme property file name.
*/
private static String[] CAN_EDIT_SUFFIX = {"ftl", "css", "js"};
private final static String THEME_PROPERTY_FILE_NAME = "theme.properties";
/**
* These file names cannot be displayed.
*/
private static String[] FILTER_FILES = {".git", ".DS_Store", "theme.properties"};
/**
* Theme folder location.
*/
private final static String THEME_FOLDER = "templates/themes";
/**
* Configuration file name.
*/
private final static String[] OPTIONS_NAMES = {"options.yaml", "options.yml"};
/**
* The type of file that can be modified.
*/
private static String[] CAN_EDIT_SUFFIX = {"ftl", "css", "js"};
/**
* These file names cannot be displayed.
*/
private static String[] FILTER_FILES = {".git", ".DS_Store", THEME_PROPERTY_FILE_NAME, "options.yaml", "option.yml"};
/**
* Theme folder location.
*/
private final static String THEME_FOLDER = "templates/themes";
/**
* Render template.
*/
private final static String RENDER_TEMPLATE = "themes/%s/%s";
private final static String THEMES_CACHE_KEY = "themes";
private final Path workDir;
private final ObjectMapper yamlMapper;
private final OptionService optionService;
private final StringCacheStore cacheStore;
public ThemeServiceImpl(HaloProperties haloProperties,
OptionService optionService) {
OptionService optionService,
StringCacheStore cacheStore) {
this.optionService = optionService;
this.cacheStore = cacheStore;
yamlMapper = new ObjectMapper(new YAMLFactory());
workDir = Paths.get(haloProperties.getWorkDir(), THEME_FOLDER);
}
@ -85,36 +101,37 @@ public class ThemeServiceImpl implements ThemeService {
* @return list of themes
*/
@Override
public List<Theme> getThemes() {
final List<Theme> themes = new ArrayList<>();
final File[] files = getThemeBasePath().listFiles();
try {
if (null != files) {
Theme theme;
for (File file : files) {
if (!file.isDirectory()) {
continue;
}
theme = new Theme();
theme.setKey(file.getName());
theme.setHasOptions(false);
for (String optionsName : OPTIONS_NAMES) {
// Resolve the options path
Path optionsPath = workDir.resolve(file.getName()).resolve(optionsName);
if (!Files.exists(optionsPath)) {
continue;
}
theme.setHasOptions(true);
}
theme.setProperties(getProperties(new File(getThemeBasePath(), file.getName())));
themes.add(theme);
}
public List<ThemeProperty> getThemes() {
// Fetch themes from cache
return cacheStore.get(THEMES_CACHE_KEY).map(themesCache -> {
try {
@SuppressWarnings("unchecked")
List<ThemeProperty> themes = JsonUtils.jsonToObject(themesCache, LinkedList.class);
return themes;
} catch (IOException e) {
throw new ServiceException("Failed to parse json", e);
}
} catch (Exception e) {
throw new RuntimeException("Themes scan failed", e);
}
return themes;
}).orElseGet(() -> {
try {
// List and filter sub folders
List<Path> themePaths = Files.list(getBasePath()).filter(path -> Files.isDirectory(path)).collect(Collectors.toList());
if (CollectionUtils.isEmpty(themePaths)) {
return Collections.emptyList();
}
// Get theme properties
List<ThemeProperty> themes = themePaths.stream().map(this::getProperty).collect(Collectors.toList());
// Cache the themes
cacheStore.put(THEMES_CACHE_KEY, JsonUtils.objectToJson(themes));
return themes;
} catch (Exception e) {
throw new ServiceException("Themes scan failed", e);
}
});
}
/**
@ -208,7 +225,7 @@ public class ThemeServiceImpl implements ThemeService {
*/
@Override
public boolean isTemplateExist(String template) {
StrBuilder templatePath = new StrBuilder(getTheme());
StrBuilder templatePath = new StrBuilder(getActivatedTheme());
templatePath.append("/");
templatePath.append(template);
File file = new File(getThemeBasePath(), templatePath.toString());
@ -242,30 +259,6 @@ public class ThemeServiceImpl implements ThemeService {
return workDir;
}
/**
* Get theme Properties.
*
* @param path path
* @return ThemeProperties
*/
@Override
public ThemeProperties getProperties(File path) {
File propertiesFile = new File(path, "theme.properties");
ThemeProperties properties = new ThemeProperties();
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"));
}
return properties;
}
/**
* Get template content by template absolute path.
*
@ -297,12 +290,13 @@ public class ThemeServiceImpl implements ThemeService {
*/
@Override
public void deleteTheme(String key) {
if (!this.isThemeExist(key)) {
if (!isThemeExist(key)) {
throw new NotFoundException("该主题不存在!").setErrorData(key);
}
File file = new File(this.getThemeBasePath(), key);
FileUtil.del(file);
HaloConst.THEMES = this.getThemes();
cacheStore.delete(THEMES_CACHE_KEY);
}
@Override
@ -334,12 +328,99 @@ public class ThemeServiceImpl implements ThemeService {
@Override
public String render(String pageName) {
return String.format(RENDER_TEMPLATE, getTheme(), pageName);
return String.format(RENDER_TEMPLATE, getActivatedTheme(), pageName);
}
@Override
public String getTheme() {
public String getActivatedTheme() {
return optionService.getByProperty(PrimaryProperties.THEME).orElse(DEFAULT_THEME_NAME);
}
@Override
public void activeTheme(String themeId) {
// TODO Check existence of the theme
}
/**
* Gets theme property.
*
* @param themePath must not be null
* @return theme property
*/
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;
}
File propertyFile = propertyPath.toFile();
try {
Properties properties = new Properties();
properties.load(new java.io.FileReader(propertyFile));
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"));
themeProperty.setFolderName(propertyFile.getName());
themeProperty.setHasOptions(hasOptions(propertyPath));
return themeProperty;
} catch (IOException e) {
// TODO Consider 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.
*
* @param themePath theme path must not be null
* @return true if it has options; false otherwise
*/
private boolean hasOptions(@NonNull Path themePath) {
Assert.notNull(themePath, "Path must not be null");
for (String optionsName : OPTIONS_NAMES) {
// Resolve the options path
Path optionsPath = themePath.resolve(optionsName);
if (Files.exists(optionsPath)) {
return true;
}
}
return false;
}
}

View File

@ -22,5 +22,4 @@ public class ThemeSettingServiceImpl extends AbstractCrudService<ThemeSetting, I
this.themeSettingRepository = themeSettingRepository;
}
}

View File

@ -1,14 +1,10 @@
package run.halo.app.web.controller.admin.api;
import run.halo.app.model.dto.OptionOutputDTO;
import run.halo.app.model.enums.OptionSource;
import run.halo.app.model.params.OptionParam;
import run.halo.app.service.OptionService;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import run.halo.app.model.dto.OptionOutputDTO;
import run.halo.app.model.enums.OptionSource;
import run.halo.app.model.params.OptionParam;
import javax.validation.Valid;
import java.util.List;

View File

@ -6,8 +6,8 @@ import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import run.halo.app.model.properties.PrimaryProperties;
import run.halo.app.model.support.BaseResponse;
import run.halo.app.model.support.Theme;
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;
@ -44,7 +44,7 @@ public class ThemeController {
*/
@GetMapping
@ApiOperation("List all themes")
public List<Theme> listAll() {
public List<ThemeProperty> listAll() {
return themeService.getThemes();
}
@ -55,7 +55,7 @@ public class ThemeController {
*/
@GetMapping("files")
public List<ThemeFile> listFiles() {
return themeService.listThemeFolderBy(themeService.getTheme());
return themeService.listThemeFolderBy(themeService.getActivatedTheme());
}
@GetMapping("files/content")
@ -71,19 +71,21 @@ public class ThemeController {
@GetMapping("files/custom")
public List<String> customTemplate() {
return themeService.getCustomTpl(themeService.getTheme());
return themeService.getCustomTpl(themeService.getActivatedTheme());
}
@PostMapping("active")
@PostMapping("{themeId}/activate")
@ApiOperation("Active a theme")
public void active(String theme) throws TemplateModelException {
public void active(@RequestParam("themeId") String themeId) throws TemplateModelException {
themeService.activeTheme(themeId);
// TODO Check existence of the theme
optionService.saveProperty(PrimaryProperties.THEME, theme);
configuration.setSharedVariable("themeName", theme);
optionService.saveProperty(PrimaryProperties.THEME, themeId);
configuration.setSharedVariable("themeName", themeId);
configuration.setSharedVariable("options", optionService.listOptions());
}
@DeleteMapping("{key}")
@DeleteMapping("key/{key}")
@ApiOperation("Deletes a theme")
public void deleteBy(@PathVariable("key") String key) {
themeService.deleteTheme(key);
@ -92,6 +94,6 @@ public class ThemeController {
@GetMapping("configurations")
@ApiOperation("Fetches theme configuration")
public BaseResponse<Object> fetchConfig() {
return BaseResponse.ok(themeService.fetchConfig(themeService.getTheme()));
return BaseResponse.ok(themeService.fetchConfig(themeService.getActivatedTheme()));
}
}

View File

@ -114,7 +114,7 @@ public class CommonController implements ErrorController {
return "common/error/404";
}
StrBuilder path = new StrBuilder("themes/");
path.append(themeService.getTheme());
path.append(themeService.getActivatedTheme());
path.append("/404");
return path.toString();
}
@ -130,7 +130,7 @@ public class CommonController implements ErrorController {
return "common/error/500";
}
StrBuilder path = new StrBuilder("themes/");
path.append(themeService.getTheme());
path.append(themeService.getActivatedTheme());
path.append("/500");
return path.toString();
}

View File

@ -12,7 +12,6 @@ import org.springframework.web.bind.annotation.ResponseBody;
import run.halo.app.exception.BadRequestException;
import run.halo.app.model.entity.*;
import run.halo.app.model.enums.AttachmentType;
import run.halo.app.model.enums.OptionSource;
import run.halo.app.model.params.InstallParam;
import run.halo.app.model.properties.*;
import run.halo.app.model.support.BaseResponse;