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.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import run.halo.app.handler.theme.config.ThemeConfigResolver; import run.halo.app.handler.theme.config.ThemeConfigResolver;
@ -23,6 +24,7 @@ import java.util.Map;
* @author johnniang * @author johnniang
* @date 4/10/19 * @date 4/10/19
*/ */
@Slf4j
@Service @Service
public class YamlThemeConfigResolverImpl implements ThemeConfigResolver { public class YamlThemeConfigResolverImpl implements ThemeConfigResolver {
@ -109,7 +111,8 @@ public class YamlThemeConfigResolverImpl implements ThemeConfigResolver {
item.setName(itemMap.get("name").toString()); item.setName(itemMap.get("name").toString());
item.setLabel(itemMap.getOrDefault("label", item.getName()).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.setType(InputType.typeOf(itemMap.get("type")));
item.setDefaultValue(itemMap.get("default")); item.setDefaultValue(itemMap.get("default"));
@ -133,7 +136,8 @@ public class YamlThemeConfigResolverImpl implements ThemeConfigResolver {
Item item = new Item(); Item item = new Item();
item.setName(key.toString()); item.setName(key.toString());
item.setLabel(itemMap.getOrDefault("label", item.getName()).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.setType(InputType.typeOf(itemMap.get("type")));
item.setDefaultValue(itemMap.get("default")); 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 run.halo.app.model.enums.InputType;
import java.util.List; import java.util.List;
import java.util.Objects;
/** /**
* Theme configuration: item entity * Theme configuration: item entity
@ -44,4 +45,17 @@ public class Item {
* Item's options, default is empty list * Item's options, default is empty list
*/ */
private List<Option> options; 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 lombok.ToString;
import org.hibernate.annotations.SQLDelete; import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where; import org.hibernate.annotations.Where;
import run.halo.app.model.enums.DataType;
import javax.persistence.*; import javax.persistence.*;
@ -46,17 +45,10 @@ public class ThemeSetting extends BaseEntity {
@Column(name = "theme_id", columnDefinition = "varchar(255) not null") @Column(name = "theme_id", columnDefinition = "varchar(255) not null")
private String themeId; private String themeId;
@Column(name = "data_type", columnDefinition = "int default 0")
private DataType type;
@Override @Override
protected void prePersist() { protected void prePersist() {
super.prePersist(); super.prePersist();
id = null; id = null;
if (type == null) {
type = DataType.STRING;
}
} }
} }

View File

@ -1,7 +1,10 @@
package run.halo.app.model.enums; package run.halo.app.model.enums;
import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/** /**
* Data type enum. * Data type enum.
@ -9,6 +12,7 @@ import org.springframework.lang.Nullable;
* @author johnniang * @author johnniang
* @date 4/9/19 * @date 4/9/19
*/ */
@Slf4j
public enum DataType implements ValueEnum<Integer> { public enum DataType implements ValueEnum<Integer> {
STRING(0), STRING(0),
@ -48,4 +52,34 @@ public enum DataType implements ValueEnum<Integer> {
return STRING; 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 @Override
public List<Group> fetchConfig(String themeId) { 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 // Get theme property
ThemeProperty themeProperty = getThemeOfNonNullBy(themeId); ThemeProperty themeProperty = getThemeOfNonNullBy(themeId);

View File

@ -2,17 +2,20 @@ package run.halo.app.service.impl;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils; 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.model.entity.ThemeSetting;
import run.halo.app.repository.ThemeSettingRepository; import run.halo.app.repository.ThemeSettingRepository;
import run.halo.app.service.ThemeService;
import run.halo.app.service.ThemeSettingService; import run.halo.app.service.ThemeSettingService;
import run.halo.app.service.base.AbstractCrudService; import run.halo.app.service.base.AbstractCrudService;
import run.halo.app.utils.ServiceUtils; import run.halo.app.utils.ServiceUtils;
import java.util.List; import java.util.*;
import java.util.Map;
/** /**
* Theme setting service implementation. * Theme setting service implementation.
@ -26,9 +29,13 @@ public class ThemeSettingServiceImpl extends AbstractCrudService<ThemeSetting, I
private final ThemeSettingRepository themeSettingRepository; private final ThemeSettingRepository themeSettingRepository;
public ThemeSettingServiceImpl(ThemeSettingRepository themeSettingRepository) { private final ThemeService themeService;
public ThemeSettingServiceImpl(ThemeSettingRepository themeSettingRepository,
ThemeService themeService) {
super(themeSettingRepository); super(themeSettingRepository);
this.themeSettingRepository = themeSettingRepository; this.themeSettingRepository = themeSettingRepository;
this.themeService = themeService;
} }
@Override @Override
@ -36,8 +43,14 @@ public class ThemeSettingServiceImpl extends AbstractCrudService<ThemeSetting, I
Assert.notNull(key, "Setting key must not be null"); Assert.notNull(key, "Setting key must not be null");
assertThemeIdHasText(themeId); 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)) { if (StringUtils.isBlank(value)) {
return themeSettingRepository.findByThemeIdAndKey(themeId, key) // Delete it
return themeSettingOptional
.map(setting -> { .map(setting -> {
themeSettingRepository.delete(setting); themeSettingRepository.delete(setting);
log.debug("Removed theme setting: [{}]", setting); log.debug("Removed theme setting: [{}]", setting);
@ -45,15 +58,25 @@ public class ThemeSettingServiceImpl extends AbstractCrudService<ThemeSetting, I
}).orElse(null); }).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 -> { .map(setting -> {
log.debug("Updating theme setting: [{}]", setting);
setting.setValue(value); setting.setValue(value);
log.debug("Updated theme setting: [{}]", setting);
return setting; return setting;
}).orElseGet(() -> { }).orElseGet(() -> {
ThemeSetting setting = new ThemeSetting(); ThemeSetting setting = new ThemeSetting();
setting.setKey(key); setting.setKey(key);
setting.setValue(value); setting.setValue(value);
setting.setThemeId(themeId); setting.setThemeId(themeId);
log.debug("Creating theme setting: [{}]", setting);
return setting; return setting;
}); });
@ -82,11 +105,66 @@ public class ThemeSettingServiceImpl extends AbstractCrudService<ThemeSetting, I
@Override @Override
public Map<String, Object> listAsMapBy(String themeId) { 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); List<ThemeSetting> themeSettings = listBy(themeId);
// TODO Convert to corresponding data type Map<String, Object> result = new HashMap<>();
return ServiceUtils.convertToMap(themeSettings, ThemeSetting::getKey, ThemeSetting::getValue);
// 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) { private void assertThemeIdHasText(String themeId) {
Assert.hasText(themeId, "Theme id must not be null"); 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())); 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") @GetMapping("activation/settings")
@ApiOperation("Lists activated theme settings") @ApiOperation("Lists activated theme settings")
public Map<String, Object> listSettingsBy() { public Map<String, Object> listSettingsBy() {
return themeSettingService.listAsMapBy(themeService.getActivatedThemeId()); 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") @PostMapping("activation/settings")
@ApiOperation("Saves theme settings") @ApiOperation("Saves theme settings")
public void saveSettingsBy(@RequestBody Map<String, Object> settings) { public void saveSettingsBy(@RequestBody Map<String, Object> settings) {
themeSettingService.save(settings, themeService.getActivatedThemeId()); 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") @PostMapping("{themeId}/settings")
@ApiOperation("Saves theme settings") @ApiOperation("Saves theme settings")
public void saveSettingsBy(@PathVariable("themeId") String themeId, public void saveSettingsBy(@PathVariable("themeId") String themeId,

View File

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

View File

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