Convert theme setting with type

pull/146/head
johnniang 2019-04-21 00:52:43 +08:00
parent fe391c93e7
commit 5a7b02c4aa
10 changed files with 197 additions and 25 deletions

View File

@ -2,6 +2,7 @@ package run.halo.app.handler.theme.config.impl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import run.halo.app.handler.theme.config.ThemeConfigResolver;
@ -23,6 +24,7 @@ import java.util.Map;
* @author johnniang
* @date 4/10/19
*/
@Slf4j
@Service
public class YamlThemeConfigResolverImpl implements ThemeConfigResolver {
@ -109,7 +111,8 @@ public class YamlThemeConfigResolverImpl implements ThemeConfigResolver {
item.setName(itemMap.get("name").toString());
item.setLabel(itemMap.getOrDefault("label", item.getName()).toString());
item.setDataType(DataType.typeOf(itemMap.get("data_type")));
Object dataType = itemMap.getOrDefault("data-type", itemMap.get("dataType"));
item.setDataType(DataType.typeOf(dataType));
item.setType(InputType.typeOf(itemMap.get("type")));
item.setDefaultValue(itemMap.get("default"));
@ -133,7 +136,8 @@ public class YamlThemeConfigResolverImpl implements ThemeConfigResolver {
Item item = new Item();
item.setName(key.toString());
item.setLabel(itemMap.getOrDefault("label", item.getName()).toString());
item.setDataType(DataType.typeOf(itemMap.get("data_type")));
Object dataType = itemMap.getOrDefault("data-type", itemMap.get("dataType"));
item.setDataType(DataType.typeOf(dataType));
item.setType(InputType.typeOf(itemMap.get("type")));
item.setDefaultValue(itemMap.get("default"));

View File

@ -5,6 +5,7 @@ import run.halo.app.model.enums.DataType;
import run.halo.app.model.enums.InputType;
import java.util.List;
import java.util.Objects;
/**
* Theme configuration: item entity
@ -44,4 +45,17 @@ public class Item {
* Item's options, default is empty list
*/
private List<Option> options;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Item item = (Item) o;
return name.equals(item.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}

View File

@ -5,7 +5,6 @@ import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where;
import run.halo.app.model.enums.DataType;
import javax.persistence.*;
@ -46,17 +45,10 @@ public class ThemeSetting extends BaseEntity {
@Column(name = "theme_id", columnDefinition = "varchar(255) not null")
private String themeId;
@Column(name = "data_type", columnDefinition = "int default 0")
private DataType type;
@Override
protected void prePersist() {
super.prePersist();
id = null;
if (type == null) {
type = DataType.STRING;
}
}
}

View File

@ -1,7 +1,10 @@
package run.halo.app.model.enums;
import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Data type enum.
@ -9,6 +12,7 @@ import org.springframework.lang.Nullable;
* @author johnniang
* @date 4/9/19
*/
@Slf4j
public enum DataType implements ValueEnum<Integer> {
STRING(0),
@ -48,4 +52,34 @@ public enum DataType implements ValueEnum<Integer> {
return STRING;
}
/**
* Converts data to corresponding type.
*
* @param data data to be converted must not be null
* @return data with corresponding type
*/
@NonNull
public Object convertTo(@NonNull Object data) {
Assert.notNull(data, "Data must not be null");
try {
switch (this) {
case STRING:
return data.toString();
case BOOL:
return Boolean.valueOf(data.toString());
case LONG:
return Long.valueOf(data.toString());
case DOUBLE:
return Double.valueOf(data.toString());
default:
return data;
}
} catch (Exception e) {
log.warn("Failed to convert " + data + " to corresponding type: " + this.name(), e);
return data;
}
}
}

View File

@ -245,7 +245,9 @@ public class ThemeServiceImpl implements ThemeService {
@Override
public List<Group> fetchConfig(String themeId) {
Assert.hasText(themeId, "Theme name must not be blank");
Assert.hasText(themeId, "Theme id must not be blank");
// TODO Cache the config
// Get theme property
ThemeProperty themeProperty = getThemeOfNonNullBy(themeId);

View File

@ -2,17 +2,20 @@ package run.halo.app.service.impl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import run.halo.app.handler.theme.config.support.Group;
import run.halo.app.handler.theme.config.support.Item;
import run.halo.app.model.entity.ThemeSetting;
import run.halo.app.repository.ThemeSettingRepository;
import run.halo.app.service.ThemeService;
import run.halo.app.service.ThemeSettingService;
import run.halo.app.service.base.AbstractCrudService;
import run.halo.app.utils.ServiceUtils;
import java.util.List;
import java.util.Map;
import java.util.*;
/**
* Theme setting service implementation.
@ -26,9 +29,13 @@ public class ThemeSettingServiceImpl extends AbstractCrudService<ThemeSetting, I
private final ThemeSettingRepository themeSettingRepository;
public ThemeSettingServiceImpl(ThemeSettingRepository themeSettingRepository) {
private final ThemeService themeService;
public ThemeSettingServiceImpl(ThemeSettingRepository themeSettingRepository,
ThemeService themeService) {
super(themeSettingRepository);
this.themeSettingRepository = themeSettingRepository;
this.themeService = themeService;
}
@Override
@ -36,8 +43,14 @@ public class ThemeSettingServiceImpl extends AbstractCrudService<ThemeSetting, I
Assert.notNull(key, "Setting key must not be null");
assertThemeIdHasText(themeId);
log.debug("Starting saving theme setting key: [{}], value: [{}]", key, value);
// Find setting by key
Optional<ThemeSetting> themeSettingOptional = themeSettingRepository.findByThemeIdAndKey(themeId, key);
if (StringUtils.isBlank(value)) {
return themeSettingRepository.findByThemeIdAndKey(themeId, key)
// Delete it
return themeSettingOptional
.map(setting -> {
themeSettingRepository.delete(setting);
log.debug("Removed theme setting: [{}]", setting);
@ -45,15 +58,25 @@ public class ThemeSettingServiceImpl extends AbstractCrudService<ThemeSetting, I
}).orElse(null);
}
ThemeSetting themeSetting = themeSettingRepository.findByThemeIdAndKey(themeId, key)
// Get config item map
Map<String, Item> itemMap = getConfigItemMap(themeId);
// Get item info
Item item = itemMap.get(key);
// Update or create
ThemeSetting themeSetting = themeSettingOptional
.map(setting -> {
log.debug("Updating theme setting: [{}]", setting);
setting.setValue(value);
log.debug("Updated theme setting: [{}]", setting);
return setting;
}).orElseGet(() -> {
ThemeSetting setting = new ThemeSetting();
setting.setKey(key);
setting.setValue(value);
setting.setThemeId(themeId);
log.debug("Creating theme setting: [{}]", setting);
return setting;
});
@ -82,11 +105,66 @@ public class ThemeSettingServiceImpl extends AbstractCrudService<ThemeSetting, I
@Override
public Map<String, Object> listAsMapBy(String themeId) {
// Convert to item map(key: item name, value: item)
Map<String, Item> itemMap = getConfigItemMap(themeId);
// Get theme setting
List<ThemeSetting> themeSettings = listBy(themeId);
// TODO Convert to corresponding data type
return ServiceUtils.convertToMap(themeSettings, ThemeSetting::getKey, ThemeSetting::getValue);
Map<String, Object> result = new HashMap<>();
// Build settings from user-defined
themeSettings.forEach(themeSetting -> {
Item item = itemMap.get(themeSetting.getKey());
// Convert data to corresponding data type
String key = themeSetting.getKey();
Object convertedValue = themeSetting.getValue();
if (item != null) {
convertedValue = item.getDataType().convertTo(themeSetting.getValue());
log.debug("Converted user-defined data from [{}] to [{}], type: [{}]", themeSetting.getValue(), convertedValue, item.getDataType());
}
result.put(key, convertedValue);
});
// Build settings from pre-defined
itemMap.forEach((name, item) -> {
log.debug("Name: [{}], item: [{}]", name, item);
if (item.getDefaultValue() == null || result.containsKey(name)) {
return;
}
// Set default value
Object convertedDefaultValue = item.getDataType().convertTo(item.getDefaultValue());
log.debug("Converted pre-defined data from [{}] to [{}], type: [{}]", item.getDefaultValue(), convertedDefaultValue, item.getDataType());
result.put(name, convertedDefaultValue);
});
return result;
}
/**
* Gets config item map. (key: item name, value: item)
*
* @param themeId theme id must not be blank
* @return config item map
*/
private Map<String, Item> getConfigItemMap(@NonNull String themeId) {
// Get theme configuration
List<Group> groups = themeService.fetchConfig(themeId);
// Mix all items
Set<Item> items = new LinkedHashSet<>();
groups.forEach(group -> items.addAll(group.getItems()));
// Convert to item map(key: item name, value: item)
return ServiceUtils.convertToMap(items, Item::getName);
}
/**
@ -97,4 +175,5 @@ public class ThemeSettingServiceImpl extends AbstractCrudService<ThemeSetting, I
private void assertThemeIdHasText(String themeId) {
Assert.hasText(themeId, "Theme id must not be null");
}
}

View File

@ -86,24 +86,30 @@ public class ThemeController {
return BaseResponse.ok(themeService.fetchConfig(themeService.getActivatedThemeId()));
}
@GetMapping("{themeId}/configurations")
@ApiOperation("Fetches theme configuration by theme id")
public List<Group> fetchConfig(@PathVariable("themeId") String themeId) {
return themeService.fetchConfig(themeId);
}
@GetMapping("activation/settings")
@ApiOperation("Lists activated theme settings")
public Map<String, Object> listSettingsBy() {
return themeSettingService.listAsMapBy(themeService.getActivatedThemeId());
}
@GetMapping("{themeId}/settings")
@ApiOperation("Lists theme settings by theme id")
public Map<String, Object> listSettingsBy(@PathVariable("themeId") String themeId) {
return themeSettingService.listAsMapBy(themeId);
}
@PostMapping("activation/settings")
@ApiOperation("Saves theme settings")
public void saveSettingsBy(@RequestBody Map<String, Object> settings) {
themeSettingService.save(settings, themeService.getActivatedThemeId());
}
@GetMapping("{themeId}/configurations")
@ApiOperation("Fetches theme configuration by theme id")
public List<Group> fetchConfig(@PathVariable("themeId") String themeId) {
return themeService.fetchConfig(themeId);
}
@PostMapping("{themeId}/settings")
@ApiOperation("Saves theme settings")
public void saveSettingsBy(@PathVariable("themeId") String themeId,

View File

@ -5,6 +5,7 @@ sns:
name: rss
label: RSS
type: radio
data-type: bool
default: true
options:
- value: true
@ -58,6 +59,7 @@ style:
name: post_title_uppper
label: 文章标题大写
type: radio
data-type: bool
default: true
options:
- value: true
@ -68,6 +70,7 @@ style:
name: blog_title_uppper
label: 博客标题大写
type: radio
data-type: bool
default: true
options:
- value: true
@ -78,6 +81,7 @@ style:
name: avatar_circle
label: 圆形头像
type: radio
data-type: bool
default: true
options:
- value: true
@ -88,6 +92,7 @@ style:
name: hitokoto
label: 博客描述开启一言
type: radio
data-type: bool
default: false
options:
- value: true

View File

@ -119,6 +119,7 @@ style:
name: background_bing
label: Bing 背景
type: radio
data-type: bool
default: false
options:
- value: true
@ -130,7 +131,7 @@ style:
name: code_pretty
label: 代码高亮
type: select
default: false
default: Default
options:
- value: Default
label: Default
@ -222,6 +223,7 @@ sns-share:
name: sns_share_twitter
label: 分享到 Twitter
type: radio
data-type: bool
default: true
options:
- value: true
@ -233,6 +235,7 @@ sns-share:
name: sns_share_facebook
label: 分享到 Facebook
type: radio
data-type: bool
default: true
options:
- value: true
@ -244,6 +247,7 @@ sns-share:
name: sns_share_googleplus
label: 分享到 Google +
type: radio
data-type: bool
default: true
options:
- value: true
@ -255,6 +259,7 @@ sns-share:
name: sns_share_weibo
label: 分享到微博
type: radio
data-type: bool
default: true
options:
- value: true
@ -266,6 +271,7 @@ sns-share:
name: sns_share_linkedin
label: 分享到 Linkedin
type: radio
data-type: bool
default: true
options:
- value: true
@ -277,6 +283,7 @@ sns-share:
name: sns_share_qq
label: 分享到 QQ
type: radio
data-type: bool
default: true
options:
- value: true
@ -288,6 +295,7 @@ sns-share:
name: sns_share_telegram
label: 分享到 Telegram
type: radio
data-type: bool
default: true
options:
- value: true
@ -302,6 +310,7 @@ other:
name: other_js_fade
label: 渐显效果
type: radio
data-type: bool
default: true
options:
- value: true
@ -313,6 +322,7 @@ other:
name: other_js_smoothscroll
label: 平滑滚动效果
type: radio
data-type: bool
default: true
options:
- value: true
@ -324,6 +334,7 @@ other:
name: other_sidebar_archives
label: 侧边栏归档
type: radio
data-type: bool
default: true
options:
- value: true
@ -335,6 +346,7 @@ other:
name: other_sidebar_cates
label: 侧边栏分类
type: radio
data-type: bool
default: true
options:
- value: true
@ -346,6 +358,7 @@ other:
name: other_sidebar_postcount
label: 侧边栏文章总数
type: radio
data-type: bool
default: true
options:
- value: true
@ -357,6 +370,7 @@ other:
name: other_mathjax
label: MathJax 插件
type: radio
data-type: bool
default: true
options:
- value: true

View File

@ -0,0 +1,22 @@
package run.halo.app.model.enums;
import org.junit.Test;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.*;
/**
* Data type test.
*
* @author johnniang
* @date 19-4-21
*/
public class DataTypeTest {
@Test
public void typeOf() {
DataType type = DataType.typeOf("bool");
System.out.println(type);
assertThat(type, equalTo(DataType.BOOL));
}
}