diff --git a/src/main/java/run/halo/app/model/enums/DataType.java b/src/main/java/run/halo/app/model/enums/DataType.java index a84748021..f2ab2e6fe 100644 --- a/src/main/java/run/halo/app/model/enums/DataType.java +++ b/src/main/java/run/halo/app/model/enums/DataType.java @@ -1,5 +1,8 @@ package run.halo.app.model.enums; + +import org.springframework.lang.Nullable; + /** * Data type enum. * @@ -27,4 +30,22 @@ public enum DataType implements ValueEnum { public Integer getValue() { return value; } + + /** + * Data type of string. + * + * @param type data type string + * @return corresponding data type, default is `STRING` if the type is missing + */ + public static DataType typeOf(@Nullable Object type) { + if (type != null) { + for (DataType datatype : values()) { + if (datatype.name().equalsIgnoreCase(type.toString())) { + return datatype; + } + } + } + + return STRING; + } } diff --git a/src/main/java/run/halo/app/model/enums/InputType.java b/src/main/java/run/halo/app/model/enums/InputType.java new file mode 100644 index 000000000..4dcbb1fd3 --- /dev/null +++ b/src/main/java/run/halo/app/model/enums/InputType.java @@ -0,0 +1,40 @@ +package run.halo.app.model.enums; + +import org.springframework.lang.Nullable; + +/** + * Input type enum. + * + * @author johnniang + * @date 4/10/19 + */ +public enum InputType { + + TEXT, + + NUMBER, + + RADIO, + + SELECT, + + TEXTAREA; + + /** + * Convert type to input type. + * + * @param type input type + * @return corresponding input type or TEXT if the given type is missing or mismatch + */ + public static InputType typeOf(@Nullable Object type) { + if (type != null) { + for (InputType inputType : values()) { + if (inputType.name().equalsIgnoreCase(type.toString())) { + return inputType; + } + } + } + return TEXT; + } + +} diff --git a/src/main/java/run/halo/app/service/ThemeSettingService.java b/src/main/java/run/halo/app/service/ThemeSettingService.java index d7e06bbee..7cdd35404 100644 --- a/src/main/java/run/halo/app/service/ThemeSettingService.java +++ b/src/main/java/run/halo/app/service/ThemeSettingService.java @@ -36,7 +36,7 @@ public interface ThemeSettingService { * @param themeId theme id must not be blank */ @Transactional - void save(@Nullable Map settings, @NonNull String themeId); + void save(@Nullable Map settings, @NonNull String themeId); /** * Lists theme settings by theme id. @@ -54,5 +54,5 @@ public interface ThemeSettingService { * @return theme setting map */ @NonNull - Map listAsMapBy(@NonNull String themeId); + Map listAsMapBy(@NonNull String themeId); } diff --git a/src/main/java/run/halo/app/service/impl/ThemeSettingServiceImpl.java b/src/main/java/run/halo/app/service/impl/ThemeSettingServiceImpl.java index 044e7e183..195f5db1d 100644 --- a/src/main/java/run/halo/app/service/impl/ThemeSettingServiceImpl.java +++ b/src/main/java/run/halo/app/service/impl/ThemeSettingServiceImpl.java @@ -62,7 +62,7 @@ public class ThemeSettingServiceImpl extends AbstractCrudService settings, String themeId) { + public void save(Map settings, String themeId) { assertThemeIdHasText(themeId); if (CollectionUtils.isEmpty(settings)) { @@ -70,7 +70,7 @@ public class ThemeSettingServiceImpl extends AbstractCrudService save(key, value, themeId)); + settings.forEach((key, value) -> save(key, value.toString(), themeId)); } @Override @@ -81,7 +81,9 @@ public class ThemeSettingServiceImpl extends AbstractCrudService listAsMapBy(String themeId) { + public Map listAsMapBy(String themeId) { + + // TODO Convert to corresponding data type return ServiceUtils.convertToMap(listBy(themeId), ThemeSetting::getKey, ThemeSetting::getValue); } diff --git a/src/main/java/run/halo/app/web/controller/admin/api/ThemeController.java b/src/main/java/run/halo/app/web/controller/admin/api/ThemeController.java index f37a959d7..40401e0fc 100644 --- a/src/main/java/run/halo/app/web/controller/admin/api/ThemeController.java +++ b/src/main/java/run/halo/app/web/controller/admin/api/ThemeController.java @@ -85,13 +85,13 @@ public class ThemeController { @GetMapping("activation/settings") @ApiOperation("Lists activated theme settings") - public Map listSettingsBy() { + public Map listSettingsBy() { return themeSettingService.listAsMapBy(themeService.getActivatedThemeId()); } @PostMapping("activation/settings") @ApiOperation("Saves theme settings") - public void saveSettingsBy(@RequestBody Map settings) { + public void saveSettingsBy(@RequestBody Map settings) { themeSettingService.save(settings, themeService.getActivatedThemeId()); } @@ -104,7 +104,7 @@ public class ThemeController { @PostMapping("{themeId}/settings") @ApiOperation("Saves theme settings") public void saveSettingsBy(@PathVariable("themeId") String themeId, - @RequestBody Map settings) { + @RequestBody Map settings) { themeSettingService.save(settings, themeId); } diff --git a/src/test/java/run/halo/app/utils/YamlTest.java b/src/test/java/run/halo/app/utils/YamlTest.java index d9c4e882c..8b211783e 100644 --- a/src/test/java/run/halo/app/utils/YamlTest.java +++ b/src/test/java/run/halo/app/utils/YamlTest.java @@ -2,9 +2,14 @@ package run.halo.app.utils; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import lombok.Data; import org.junit.Test; +import org.springframework.lang.Nullable; +import run.halo.app.model.enums.DataType; +import run.halo.app.model.enums.InputType; import java.io.IOException; +import java.util.*; /** * Yaml test. @@ -21,26 +26,358 @@ public class YamlTest { @Test public void readYamlTest() throws IOException { - String yaml = "style:\n" + - " name: Style settings\n" + + String yaml = "sns:\n" + + " label: 社交资料设置\n" + " items:\n" + - " post_title_lower:\n" + - " name: post_title_lower\n" + - " description: Post title lower\n" + + " rss:\n" + + " name: rss\n" + + " label: RSS\n" + " type: radio\n" + - " defaultValue: true\n" + + " default: true\n" + " options:\n" + " - value: true\n" + - " label: Enabled\n" + + " label: 开启\n" + " - value: false\n" + - " label: Disabled\n" + - " custom_style:\n" + - " name: custom_style\n" + - " description: Custom style\n" + - " type: textarea\n"; + " label: 关闭\n" + + " twitter:\n" + + " name: twitter\n" + + " label: Twitter\n" + + " type: text\n" + + " facebook:\n" + + " name: facebook\n" + + " label: Facebook\n" + + " type: text\n" + + "style:\n" + + " label: 样式设置\n" + + " items:\n" + + " icon:\n" + + " name: icon\n" + + " label: 右上角图标\n" + + " type: text\n" + + " post_title_uppper:\n" + + " name: post_title_uppper\n" + + " label: 文章标题大写\n" + + " type: radio\n" + + " default: true\n" + + " options:\n" + + " - value: true\n" + + " label: 开启\n" + + " - value: false\n" + + " label: 关闭"; - Object config = yamlMapper.readValue(yaml, Object.class); + LinkedHashMap config = yamlMapper.readValue(yaml, LinkedHashMap.class); + + System.out.println(config.getClass()); System.out.println(jsonMapper.writeValueAsString(config)); } + + @Test + @SuppressWarnings("unchecked") + public void readAnotherYamlTest() throws IOException { + String yaml = "sns:\n" + + " label: 社交资料设置\n" + + " items:\n" + + " - name: rss\n" + + " label: RSS\n" + + " type: radio\n" + + " default: true\n" + + " options:\n" + + " - value: true\n" + + " label: 开启\n" + + " - value: false\n" + + " label: 关闭\n" + + " - name: twitter\n" + + " label: Twitter\n" + + " type: text\n" + + " - name: facebook\n" + + " label: Facebook\n" + + " type: text\n" + + " - name: instagram\n" + + " label: Instagram\n" + + " type: text\n" + + "style:\n" + + " label: 样式设置\n" + + " items:\n" + + " - name: icon\n" + + " label: 右上角图标\n" + + " type: text\n" + + " - name: post_title_uppper\n" + + " label: 文章标题大写\n" + + " type: radio\n" + + " default: true\n" + + " options:\n" + + " - value: true\n" + + " label: 开启\n" + + " - value: false\n" + + " label: 关闭\n" + + " - name: blog_title_uppper\n" + + " label: 博客标题大写\n" + + " type: radio\n" + + " default: true\n" + + " options:\n" + + " - value: true\n" + + " label: 开启\n" + + " - value: false\n" + + " label: 关闭\n"; + + Object config = yamlMapper.readValue(yaml, Object.class); + + List tabs = handleTabs(config); + + System.out.println(config.getClass()); + + System.out.println(tabs); + + System.out.println(jsonMapper.writeValueAsString(config)); + } + + @Test + public void convertYamlTest() throws IOException { + String yaml = "- name: sns\n" + + " label: 社交资料设置\n" + + " items:\n" + + " - name: rss\n" + + " label: RSS\n" + + " type: radio\n" + + " default: true\n" + + " options:\n" + + " - value: true\n" + + " label: 开启\n" + + " - value: false\n" + + " label: 关闭\n" + + " - name: twitter\n" + + " label: Twitter\n" + + " type: text\n" + + " - name: facebook\n" + + " label: Facebook\n" + + " type: text\n" + + " - name: instagram\n" + + " label: Instagram\n" + + " type: text\n" + + "- name: style\n" + + " label: 样式设置\n" + + " items:\n" + + " - name: icon\n" + + " label: 右上角图标\n" + + " type: text\n" + + " - name: post_title_uppper\n" + + " label: 文章标题大写\n" + + " type: radio\n" + + " default: true\n" + + " options:\n" + + " - value: true\n" + + " label: 开启\n" + + " - value: false\n" + + " label: 关闭\n" + + " - name: blog_title_uppper\n" + + " label: 博客标题大写\n" + + " type: radio\n" + + " default: true\n" + + " options:\n" + + " - value: true\n" + + " label: 开启\n" + + " - value: false\n" + + " label: 关闭"; + + + Object config = yamlMapper.readValue(yaml, Object.class); + + List tabs = handleTabs(config); + + System.out.println(config.getClass()); + + System.out.print(tabs); + } + + @SuppressWarnings("unchecked") + private List handleTabs(@Nullable Object config) { + List tabs = new LinkedList<>(); + + if (config instanceof List) { + List configList = (List) config; + + // Resolve tab + + configList.forEach(tabYaml -> { + // tabYaml should be map + if (!(tabYaml instanceof Map)) { + return; + } + + Map tabMap = ((Map) tabYaml); + + Tab tab = new Tab(); + + tab.setName(tabMap.get("name").toString()); + tab.setLabel(tabMap.get("label").toString()); + + // Handle items + tab.setItems(handleItems(tabMap.get("items"))); + + tabs.add(tab); + }); + + } else if (config instanceof Map) { + Map configMap = (Map) config; + configMap.forEach((key, value) -> { + // key: tab name + // value: tab property, should be a map + if (!(value instanceof Map)) { + return; + } + + Map tabMap = (Map) value; + + Tab tab = new Tab(); + + tab.setName(key.toString()); + tab.setLabel(tabMap.get("label").toString()); + + // Handle items + tab.setItems(handleItems(tabMap.get("items"))); + + tabs.add(tab); + }); + } + + return tabs; + } + + @SuppressWarnings("unchecked") + private List handleItems(@Nullable Object items) { + + if (items == null) { + return Collections.emptyList(); + } + + List result = new LinkedList<>(); + + if (items instanceof List) { + ((List) items).forEach(itemYaml -> { + if (!(itemYaml instanceof Map)) { + return; + } + + // Should be Map + Map itemMap = (Map) itemYaml; + + // Build item + Item item = new Item(); + + item.setName(itemMap.get("name").toString()); + item.setLabel(itemMap.getOrDefault("label", item.getName()).toString()); + item.setDataType(DataType.typeOf(itemMap.get("data_type"))); + item.setType(InputType.typeOf(itemMap.get("type"))); + item.setDefaultValue(itemMap.get("default")); + + // Handle options + item.setOptions(handleOptions(itemMap.get("options"))); + + // Add item + result.add(item); + }); + } else if (items instanceof Map) { + Map itemsMap = (Map) items; + + itemsMap.forEach((key, value) -> { + if (!(value instanceof Map)) { + return; + } + + Map itemMap = (Map) value; + + // key: item name + Item item = new Item(); + item.setName(key.toString()); + item.setLabel(itemMap.getOrDefault("label", item.getName()).toString()); + item.setDataType(DataType.typeOf(itemMap.get("data_type"))); + item.setType(InputType.typeOf(itemMap.get("type"))); + item.setDefaultValue(itemMap.get("default")); + + // Handle options + item.setOptions(handleOptions(itemMap.get("options"))); + + // Add item + result.add(item); + }); + } + + return result; + } + + @SuppressWarnings("unchecked") + private List