Merge remote-tracking branch 'origin/v1' into v1

pull/137/head
johnniang 2019-03-17 23:27:44 +08:00
commit 0e45f06a50
29 changed files with 1366 additions and 329 deletions

View File

@ -1,8 +1,19 @@
package cc.ryanc.halo.config; package cc.ryanc.halo.config;
import cc.ryanc.halo.model.freemarker.method.RandomMethod;
import cc.ryanc.halo.model.freemarker.method.RecentCommentsMethod;
import cc.ryanc.halo.model.freemarker.method.RecentPostsMethod;
import cc.ryanc.halo.model.freemarker.tag.ArticleTagDirective;
import cc.ryanc.halo.model.freemarker.tag.CommonTagDirective;
import cc.ryanc.halo.service.OptionService;
import cc.ryanc.halo.service.UserService;
import freemarker.template.TemplateModelException;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
/** /**
* <pre> * <pre>
* FreeMarker * FreeMarker
@ -15,43 +26,43 @@ import org.springframework.context.annotation.Configuration;
@Configuration @Configuration
public class FreeMarkerAutoConfiguration { public class FreeMarkerAutoConfiguration {
// @Autowired @Autowired
// private freemarker.template.Configuration configuration; private freemarker.template.Configuration configuration;
//
// @Autowired @Autowired
// private OptionsService optionsService; private OptionService optionsService;
//
// @Autowired @Autowired
// private UserService userService; private UserService userService;
//
// @Autowired @Autowired
// private CommonTagDirective commonTagDirective; private CommonTagDirective commonTagDirective;
//
// @Autowired @Autowired
// private ArticleTagDirective articleTagDirective; private ArticleTagDirective articleTagDirective;
//
// @Autowired @Autowired
// private RandomMethod randomMethod; private RandomMethod randomMethod;
//
// @Autowired @Autowired
// private RecentPostsMethod recentPostsMethod; private RecentPostsMethod recentPostsMethod;
//
// @Autowired @Autowired
// private RecentCommentsMethod recentCommentsMethod; private RecentCommentsMethod recentCommentsMethod;
//
// @PostConstruct @PostConstruct
// public void setSharedVariable() { public void setSharedVariable() {
// try { try {
// //自定义标签 //自定义标签
// configuration.setSharedVariable("commonTag", commonTagDirective); configuration.setSharedVariable("commonTag", commonTagDirective);
// configuration.setSharedVariable("articleTag", articleTagDirective); configuration.setSharedVariable("articleTag", articleTagDirective);
// configuration.setSharedVariable("options", optionsService.findAllOptions()); configuration.setSharedVariable("options", optionsService.listOptions());
// configuration.setSharedVariable("user", userService.findUser()); // configuration.setSharedVariable("user", userService.findUser());
// configuration.setSharedVariable("randomMethod", randomMethod); configuration.setSharedVariable("randomMethod", randomMethod);
// configuration.setSharedVariable("recentPostsMethod", recentPostsMethod); configuration.setSharedVariable("recentPostsMethod", recentPostsMethod);
// configuration.setSharedVariable("recentCommentsMethod", recentCommentsMethod); configuration.setSharedVariable("recentCommentsMethod", recentCommentsMethod);
// } catch (TemplateModelException e) { } catch (TemplateModelException e) {
// log.error("Custom tags failed to load{}", e.getMessage()); log.error("Custom tags failed to load{}", e.getMessage());
// } }
// } }
} }

View File

@ -54,7 +54,8 @@ public class WebMvcAutoConfiguration implements WebMvcConfigurer {
registry.addResourceHandler("/static/**") registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/"); .addResourceLocations("classpath:/static/");
registry.addResourceHandler("/**") registry.addResourceHandler("/**")
.addResourceLocations("classpath:/templates/themes/"); .addResourceLocations("classpath:/templates/themes/")
.addResourceLocations("file:///" + System.getProperties().getProperty("user.home") + "/halo/templates/themes/");
registry.addResourceHandler("/upload/**") registry.addResourceHandler("/upload/**")
.addResourceLocations("file:///" + System.getProperties().getProperty("user.home") + "/halo/upload/"); .addResourceLocations("file:///" + System.getProperties().getProperty("user.home") + "/halo/upload/");
registry.addResourceHandler("/favicon.ico") registry.addResourceHandler("/favicon.ico")

View File

@ -1,14 +1,27 @@
package cc.ryanc.halo.listener; package cc.ryanc.halo.listener;
import cc.ryanc.halo.config.properties.HaloProperties; import cc.ryanc.halo.config.properties.HaloProperties;
import cc.ryanc.halo.model.support.HaloConst;
import cc.ryanc.halo.model.support.Theme;
import cc.ryanc.halo.service.OptionService;
import cc.ryanc.halo.utils.HaloUtils; import cc.ryanc.halo.utils.HaloUtils;
import cc.ryanc.halo.utils.ThemeUtils;
import cc.ryanc.halo.web.controller.content.base.BaseContentController;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import freemarker.template.TemplateModelException;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.event.ApplicationStartedEvent; import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.util.ResourceUtils;
import java.io.File;
import java.io.FileNotFoundException;
import java.nio.charset.Charset;
import java.util.List;
/** /**
* <pre> * <pre>
@ -31,18 +44,54 @@ public class StartedListener implements ApplicationListener<ApplicationStartedEv
@Autowired @Autowired
private HaloProperties haloProperties; private HaloProperties haloProperties;
@Autowired
private OptionService optionService;
@Override @Override
public void onApplicationEvent(ApplicationStartedEvent event) { public void onApplicationEvent(ApplicationStartedEvent event) {
// save halo version to database // save halo version to database
// TODO Complete cache option value // TODO Complete cache option value
// TODO Complete load active theme this.cacheOptions();
// TODO Complete load option this.cacheThemes();
// TODO Load themes this.cacheOwo();
// TODO Load owo this.getActiveTheme();
this.printStartInfo(); this.printStartInfo();
} }
/**
* Cache options to map
*/
private void cacheOptions() {
HaloConst.OPTIONS = optionService.listOptions();
}
/**
* Cache themes to map
*/
private void cacheThemes() {
final List<Theme> themes = ThemeUtils.getThemes();
if (null != themes) {
HaloConst.THEMES = themes;
}
}
/**
* Get active theme
*/
private void getActiveTheme() {
final String themeValue = optionService.getByKey("theme");
if (StrUtil.isNotEmpty(themeValue) && !StrUtil.equals(themeValue, null)) {
BaseContentController.THEME = themeValue;
} else {
BaseContentController.THEME = "anatole";
}
try {
configuration.setSharedVariable("themeName", BaseContentController.THEME);
} catch (TemplateModelException e) {
e.printStackTrace();
}
}
private void printStartInfo() { private void printStartInfo() {
// Get server port // Get server port
String serverPort = applicationContext.getEnvironment().getProperty("server.port"); String serverPort = applicationContext.getEnvironment().getProperty("server.port");
@ -75,4 +124,16 @@ public class StartedListener implements ApplicationListener<ApplicationStartedEv
return blogUrl; return blogUrl;
} }
/**
* Cache Owo
*/
private void cacheOwo() {
try {
File file = new File(ResourceUtils.getURL("classpath:").getPath(), "static/halo-common/OwO/OwO.path.json");
HaloConst.OWO = JSONUtil.readJSONObject(file, Charset.forName("UTF-8"));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
} }

View File

@ -46,6 +46,12 @@ public class Comment {
@Column(name = "ip_address", columnDefinition = "varchar(127) default ''") @Column(name = "ip_address", columnDefinition = "varchar(127) default ''")
private String ipAddress; private String ipAddress;
/**
*
*/
@Column(name = "author_url",columnDefinition = "varchar(512) default ''")
private String authorUrl;
/** /**
* Gavatar md5 * Gavatar md5
*/ */
@ -55,7 +61,7 @@ public class Comment {
/** /**
* *
*/ */
@Column(name = "content", columnDefinition = "varchar(1024) not null") @Column(name = "content", columnDefinition = "varchar(1023) not null")
private String content; private String content;
/** /**

View File

@ -1,10 +1,8 @@
package cc.ryanc.halo.model.freemarker.tag; package cc.ryanc.halo.model.freemarker.tag;
import cc.ryanc.halo.service.*;
import freemarker.core.Environment; import freemarker.core.Environment;
import freemarker.template.TemplateDirectiveBody; import freemarker.template.*;
import freemarker.template.TemplateDirectiveModel;
import freemarker.template.TemplateException;
import freemarker.template.TemplateModel;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.io.IOException; import java.io.IOException;
@ -23,9 +21,55 @@ public class CommonTagDirective implements TemplateDirectiveModel {
private static final String METHOD_KEY = "method"; private static final String METHOD_KEY = "method";
private final MenuService menuService;
private final CategoryService categoryService;
private final TagService tagService;
private final LinkService linkService;
private final CommentService commentService;
public CommonTagDirective(MenuService menuService,
CategoryService categoryService,
TagService tagService,
LinkService linkService,
CommentService commentService) {
this.menuService = menuService;
this.categoryService = categoryService;
this.tagService = tagService;
this.linkService = linkService;
this.commentService = commentService;
}
@Override @Override
public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body) throws TemplateException, IOException { public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body) throws TemplateException, IOException {
// TODO Complete comment tag directive. final DefaultObjectWrapperBuilder builder = new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_25);
if (params.containsKey(METHOD_KEY)) {
String method = params.get(METHOD_KEY).toString();
switch (method) {
case "menus":
env.setVariable("menus", builder.build().wrap(menuService.listAll()));
break;
case "categories":
env.setVariable("categories", builder.build().wrap(categoryService.listAll()));
break;
case "tags":
env.setVariable("tags", builder.build().wrap(tagService.listAll()));
break;
case "links":
env.setVariable("links", builder.build().wrap(linkService.listAll()));
break;
case "newComments":
env.setVariable("newComments", builder.build().wrap(commentService.listAll()));
break;
default:
break;
}
}
body.render(env.getOut());
} }
} }

View File

@ -1,5 +1,7 @@
package cc.ryanc.halo.model.support; package cc.ryanc.halo.model.support;
import cn.hutool.json.JSONObject;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -21,22 +23,22 @@ public class HaloConst {
public static final String HALO_VERSION = "0.4.2"; public static final String HALO_VERSION = "0.4.2";
/** /**
* Token key * Token of header param
*/ */
public static final String TOKEN_HEADER = "token"; public static final String TOKEN_HEADER = "token";
/** /**
* key,value * All of the options
*/ */
public static Map<String, String> OPTIONS = new HashMap<>(); public static Map<String, String> OPTIONS = new HashMap<>();
/** /**
* OwO * All of the Owo
*/ */
public static Map<String, String> OWO = new HashMap<>(); public static JSONObject OWO;
/** /**
* * All of the themes
*/ */
public static List<Theme> THEMES = new ArrayList<>(); public static List<Theme> THEMES = new ArrayList<>();

View File

@ -5,9 +5,7 @@ import lombok.Data;
import java.io.Serializable; import java.io.Serializable;
/** /**
* <pre> * Theme DTO
*
* </pre>
* *
* @author : RYAN0UP * @author : RYAN0UP
* @date : 2018/1/3 * @date : 2018/1/3
@ -18,17 +16,22 @@ public class Theme implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* * theme name
*/ */
private String themeName; private String themeName;
/** /**
* * is support setting options
*/ */
private boolean hasOptions; private boolean hasOptions;
/** /**
* * is support update
*/ */
private boolean hasUpdate; private boolean hasUpdate;
/**
* is internal theme
*/
private boolean isInternal;
} }

View File

@ -3,6 +3,8 @@ package cc.ryanc.halo.repository;
import cc.ryanc.halo.model.entity.Option; import cc.ryanc.halo.model.entity.Option;
import cc.ryanc.halo.repository.base.BaseRepository; import cc.ryanc.halo.repository.base.BaseRepository;
import java.util.Optional;
/** /**
* Option repository. * Option repository.
* *
@ -10,4 +12,18 @@ import cc.ryanc.halo.repository.base.BaseRepository;
*/ */
public interface OptionRepository extends BaseRepository<Option, Integer> { public interface OptionRepository extends BaseRepository<Option, Integer> {
/**
* Query option by key
*
* @param key key
* @return Option
*/
Optional<Option> findByOptionKey(String key);
/**
* Delete option by key
*
* @param key key
*/
void removeByOptionKey(String key);
} }

View File

@ -3,6 +3,8 @@ package cc.ryanc.halo.service;
import cc.ryanc.halo.model.entity.Option; import cc.ryanc.halo.model.entity.Option;
import cc.ryanc.halo.service.base.CrudService; import cc.ryanc.halo.service.base.CrudService;
import java.util.Map;
/** /**
* Option service. * Option service.
* *
@ -10,4 +12,33 @@ import cc.ryanc.halo.service.base.CrudService;
*/ */
public interface OptionService extends CrudService<Option, Integer> { public interface OptionService extends CrudService<Option, Integer> {
/**
* Save one option
*
* @param key key
* @param value value
*/
void saveOption(String key, String value);
/**
* Save multiple options
*
* @param options options
*/
void saveOptions(Map<String, String> options);
/**
* Get all options
*
* @return Map
*/
Map<String, String> listOptions();
/**
* Get option by key
*
* @param key key
* @return String
*/
String getByKey(String key);
} }

View File

@ -4,7 +4,12 @@ import cc.ryanc.halo.model.entity.Option;
import cc.ryanc.halo.repository.OptionRepository; import cc.ryanc.halo.repository.OptionRepository;
import cc.ryanc.halo.service.OptionService; import cc.ryanc.halo.service.OptionService;
import cc.ryanc.halo.service.base.AbstractCrudService; import cc.ryanc.halo.service.base.AbstractCrudService;
import cc.ryanc.halo.utils.ServiceUtils;
import cn.hutool.core.util.StrUtil;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.Map;
/** /**
* OptionService implementation class * OptionService implementation class
@ -21,4 +26,65 @@ public class OptionServiceImpl extends AbstractCrudService<Option, Integer> impl
super(optionRepository); super(optionRepository);
this.optionRepository = optionRepository; this.optionRepository = optionRepository;
} }
/**
* Save one option
*
* @param key key
* @param value value
*/
@Override
public void saveOption(String key, String value) {
if (StrUtil.equals(value, "")) {
optionRepository.removeByOptionKey(key);
} else if (StrUtil.isNotEmpty(key)) {
Option options = optionRepository.findByOptionKey(key).map(option -> {
// Exist
option.setOptionValue(value);
return option;
}).orElseGet(() -> {
// Not exist
Option option = new Option();
option.setOptionKey(key);
option.setOptionValue(value);
return option;
});
// Save or update the options
optionRepository.save(options);
}
}
/**
* Save multiple options
*
* @param options options
*/
@Override
public void saveOptions(Map<String, String> options) {
if (!CollectionUtils.isEmpty(options)) {
options.forEach(this::saveOption);
}
}
/**
* Get all options
*
* @return Map
*/
@Override
public Map<String, String> listOptions() {
return ServiceUtils.convertToMap(listAll(), Option::getOptionKey, Option::getOptionValue);
}
/**
* Get option by key
*
* @param key key
* @return String
*/
@Override
public String getByKey(String key) {
return optionRepository.findByOptionKey(key).map(Option::getOptionValue).orElse(null);
}
} }

View File

@ -1,8 +1,5 @@
package cc.ryanc.halo.utils; package cc.ryanc.halo.utils;
import cc.ryanc.halo.model.support.BackupDto;
import cc.ryanc.halo.model.support.Theme;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.text.StrBuilder; import cn.hutool.core.text.StrBuilder;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.qiniu.common.Zone; import com.qiniu.common.Zone;
@ -10,7 +7,6 @@ import io.github.biezhi.ome.OhMyEmail;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.NonNull; import org.springframework.lang.NonNull;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.ResourceUtils;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
@ -25,7 +21,9 @@ import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributeView; import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
import java.util.*; import java.util.Calendar;
import java.util.Date;
import java.util.Properties;
import static cc.ryanc.halo.model.support.HaloConst.OPTIONS; import static cc.ryanc.halo.model.support.HaloConst.OPTIONS;
@ -207,113 +205,6 @@ public class HaloUtils {
return ""; return "";
} }
/**
*
*
* @return List
*/
public static List<Theme> getThemes() {
final List<Theme> themes = new ArrayList<>();
try {
// 获取项目根路径
final File basePath = new File(ResourceUtils.getURL("classpath:").getPath());
// 获取主题路径
final File themesPath = new File(basePath.getAbsolutePath(), "templates/themes");
final File[] files = themesPath.listFiles();
if (null != files) {
Theme theme;
for (File file : files) {
if (file.isDirectory()) {
if (StrUtil.equals("__MACOSX", file.getName())) {
continue;
}
theme = new Theme();
theme.setThemeName(file.getName());
File optionsPath = new File(themesPath.getAbsolutePath(),
file.getName() + "/module/options.ftl");
if (optionsPath.exists()) {
theme.setHasOptions(true);
} else {
theme.setHasOptions(false);
}
File gitPath = new File(themesPath.getAbsolutePath(), file.getName() + "/.git");
if (gitPath.exists()) {
theme.setHasUpdate(true);
} else {
theme.setHasUpdate(false);
}
themes.add(theme);
}
}
}
} catch (Exception e) {
log.error("Themes scan failed", e);
}
return themes;
}
/**
*
*
* @param theme theme
* @return List
*/
public static List<String> getTplName(String theme) {
final List<String> tpls = new ArrayList<>();
try {
// 获取项目根路径
final File basePath = new File(ResourceUtils.getURL("classpath:").getPath());
// 获取主题路径
final File themesPath = new File(basePath.getAbsolutePath(), "templates/themes/" + theme);
final File modulePath = new File(themesPath.getAbsolutePath(), "module");
final File[] baseFiles = themesPath.listFiles();
final File[] moduleFiles = modulePath.listFiles();
if (null != moduleFiles) {
for (File file : moduleFiles) {
if (file.isFile() && file.getName().endsWith(".ftl")) {
tpls.add("module/" + file.getName());
}
}
}
if (null != baseFiles) {
for (File file : baseFiles) {
if (file.isFile() && file.getName().endsWith(".ftl")) {
tpls.add(file.getName());
}
}
}
} catch (Exception e) {
log.error("Failed to get theme template", e);
}
return tpls;
}
/**
* page_xxx
*
* @return List
*/
public static List<String> getCustomTpl(String theme) {
final List<String> tpls = new ArrayList<>();
try {
final File basePath = new File(ResourceUtils.getURL("classpath:").getPath());
// 获取主题路径
final File themePath = new File(basePath.getAbsolutePath(), "templates/themes/" + theme);
final File[] themeFiles = themePath.listFiles();
if (null != themeFiles && themeFiles.length > 0) {
for (File file : themeFiles) {
String[] split = StrUtil.removeSuffix(file.getName(), ".ftl").split("_");
if (split.length == 2 && "page".equals(split[0])) {
tpls.add(StrUtil.removeSuffix(file.getName(), ".ftl"));
}
}
}
} catch (FileNotFoundException e) {
log.error("File not found", e);
}
return tpls;
}
/** /**
* *
* *

View File

@ -15,33 +15,35 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
/** /**
* Markdown utils
*
* @author : RYAN0UP * @author : RYAN0UP
* @date : 2018/11/14 * @date : 2018/11/14
*/ */
public class MarkdownUtils { public class MarkdownUtils {
/** /**
* Front-matter * Front-matter extension
*/ */
private static final Set<Extension> EXTENSIONS_YAML = Collections.singleton(YamlFrontMatterExtension.create()); private static final Set<Extension> EXTENSIONS_YAML = Collections.singleton(YamlFrontMatterExtension.create());
/** /**
* Table * Table extension
*/ */
private static final Set<Extension> EXTENSIONS_TABLE = Collections.singleton(TablesExtension.create()); private static final Set<Extension> EXTENSIONS_TABLE = Collections.singleton(TablesExtension.create());
/** /**
* Markdown * Parse Markdown content
*/ */
private static final Parser PARSER = Parser.builder().extensions(EXTENSIONS_YAML).extensions(EXTENSIONS_TABLE).build(); private static final Parser PARSER = Parser.builder().extensions(EXTENSIONS_YAML).extensions(EXTENSIONS_TABLE).build();
/** /**
* HTML * Render HTML content
*/ */
private static final HtmlRenderer RENDERER = HtmlRenderer.builder().extensions(EXTENSIONS_YAML).extensions(EXTENSIONS_TABLE).build(); private static final HtmlRenderer RENDERER = HtmlRenderer.builder().extensions(EXTENSIONS_YAML).extensions(EXTENSIONS_TABLE).build();
/** /**
* Markdown * Render Markdown content
* *
* @param content content * @param content content
* @return String * @return String
@ -66,7 +68,7 @@ public class MarkdownUtils {
} }
/** /**
* * Get front-matter
* *
* @param content content * @param content content
* @return Map * @return Map

View File

@ -5,9 +5,7 @@ import lombok.extern.slf4j.Slf4j;
import static cc.ryanc.halo.model.support.HaloConst.OWO; import static cc.ryanc.halo.model.support.HaloConst.OWO;
/** /**
* <pre> * Owo util
* OwO
* </pre>
* *
* @author : RYAN0UP * @author : RYAN0UP
* @date : 2017/12/22 * @date : 2017/12/22
@ -16,15 +14,15 @@ import static cc.ryanc.halo.model.support.HaloConst.OWO;
public class OwoUtil { public class OwoUtil {
/** /**
* * Owo mark converted into a picture address
* *
* @param mark * @param content content
* @return * @return picture address
*/ */
public static String markToImg(String mark) { public static String parseOwo(String content) {
for (String key : OWO.keySet()) { for (String key : OWO.keySet()) {
mark = mark.replace(key, OWO.get(key)); content = content.replace(key, OWO.get(key).toString());
} }
return mark; return content;
} }
} }

View File

@ -0,0 +1,206 @@
package cc.ryanc.halo.utils;
import cc.ryanc.halo.model.support.HaloConst;
import cc.ryanc.halo.model.support.Theme;
import cc.ryanc.halo.web.controller.content.base.BaseContentController;
import cn.hutool.core.text.StrBuilder;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ResourceUtils;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
/**
* Theme utils
*
* @author : RYAN0UP
* @date : 2019/3/16
*/
@Slf4j
public class ThemeUtils {
/**
* Scan internal themes and user's themes
*
* @return List
*/
public static List<Theme> getThemes() {
final List<Theme> themes = new ArrayList<>();
try {
themes.addAll(getThemesByPath(getInternalThemesPath(), true));
themes.addAll(getThemesByPath(getUsersThemesPath(), false));
} catch (Exception e) {
log.error("Themes scan failed", e);
}
return themes;
}
/**
* Scan themes by directory
*
* @param file file
* @return List<Theme>
*/
private static List<Theme> getThemesByPath(File themesPath, boolean isInternal) {
final List<Theme> themes = new ArrayList<>();
try {
final File[] files = themesPath.listFiles();
if (null != files) {
Theme theme;
for (File file : files) {
if (file.isDirectory()) {
if (StrUtil.equals("__MACOSX", file.getName())) {
continue;
}
theme = new Theme();
theme.setThemeName(file.getName());
File optionsPath = new File(themesPath.getAbsolutePath(),
file.getName() + "/module/options.ftl");
if (optionsPath.exists()) {
theme.setHasOptions(true);
} else {
theme.setHasOptions(false);
}
File gitPath = new File(themesPath.getAbsolutePath(), file.getName() + "/.git");
if (gitPath.exists()) {
theme.setHasUpdate(true);
} else {
theme.setHasUpdate(false);
}
theme.setInternal(isInternal);
themes.add(theme);
}
}
}
} catch (Exception e) {
log.error("Themes scan failed", e);
}
return themes;
}
/**
* Get internal themes path
*
* @return File
* @throws FileNotFoundException FileNotFoundException
*/
public static File getInternalThemesPath() throws FileNotFoundException {
return new File(ResourceUtils.getURL("classpath:").getPath(), "templates/themes");
}
/**
* Get user's themes path
*
* @return File
*/
public static File getUsersThemesPath() {
return new File(System.getProperties().getProperty("user.home"), "halo/templates/themes");
}
/**
* Get themes path by theme name
*
* @param themeName themeName
* @return File
*/
public static File getThemesPath(String themeName) throws FileNotFoundException {
if (isInternal(themeName)) {
return getInternalThemesPath();
} else {
return getUsersThemesPath();
}
}
/**
* Get theme templates
*
* @param theme theme
* @return List<String>
*/
public static List<String> getTplName(String theme) {
final List<String> templates = new ArrayList<>();
try {
final File themesPath = new File(getThemesPath(theme), theme);
final File modulePath = new File(themesPath.getAbsolutePath(), "module");
final File[] baseFiles = themesPath.listFiles();
final File[] moduleFiles = modulePath.listFiles();
if (null != moduleFiles) {
for (File file : moduleFiles) {
if (file.isFile() && file.getName().endsWith(".ftl")) {
templates.add("module/" + file.getName());
}
}
}
if (null != baseFiles) {
for (File file : baseFiles) {
if (file.isFile() && file.getName().endsWith(".ftl")) {
templates.add(file.getName());
}
}
}
} catch (Exception e) {
log.error("Failed to get theme template", e);
}
return templates;
}
/**
* Get custom template, such as page_xxx.ftl, and xxx will be template name
*
* @return List
*/
public static List<String> getCustomTpl(String theme) throws FileNotFoundException {
final List<String> templates = new ArrayList<>();
final File themePath = new File(getThemesPath(theme), theme);
final File[] themeFiles = themePath.listFiles();
if (null != themeFiles && themeFiles.length > 0) {
for (File file : themeFiles) {
String[] split = StrUtil.removeSuffix(file.getName(), ".ftl").split("_");
if (split.length == 2 && "page".equals(split[0])) {
templates.add(StrUtil.removeSuffix(file.getName(), ".ftl"));
}
}
}
return templates;
}
/**
* Judging whether template exists under the specified theme
*
* @param template template
* @return boolean
*/
public static boolean isTemplateExist(String template) throws FileNotFoundException {
boolean result = false;
StrBuilder templatePath = new StrBuilder(BaseContentController.THEME);
templatePath.append("/");
templatePath.append(template);
File file = new File(getThemesPath(BaseContentController.THEME), templatePath.toString());
if (file.exists()) {
result = true;
}
return result;
}
/**
* Judging whether the theme is a internal theme or not
*
* @param themeName themeName
* @return boolean
*/
public static boolean isInternal(String themeName) {
boolean result = false;
List<Theme> themes = HaloConst.THEMES;
for (Theme theme : themes) {
if (theme.getThemeName().equals(themeName) && theme.isInternal()) {
result = true;
break;
}
}
return result;
}
}

View File

@ -1,6 +1,8 @@
package cc.ryanc.halo.web.controller.admin; package cc.ryanc.halo.web.controller.admin;
import cc.ryanc.halo.service.CommentService;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
/** /**
@ -12,4 +14,15 @@ import org.springframework.web.bind.annotation.RequestMapping;
@Controller @Controller
@RequestMapping(value = "/admin/comments") @RequestMapping(value = "/admin/comments")
public class CommentController { public class CommentController {
private final CommentService commentService;
public CommentController(CommentService commentService) {
this.commentService = commentService;
}
@GetMapping
public String comments(){
return "admin/admin_comment";
}
} }

View File

@ -1,6 +1,8 @@
package cc.ryanc.halo.web.controller.admin; package cc.ryanc.halo.web.controller.admin;
import cc.ryanc.halo.service.MenuService;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
/** /**
@ -12,4 +14,15 @@ import org.springframework.web.bind.annotation.RequestMapping;
@Controller @Controller
@RequestMapping(value = "/admin/menus") @RequestMapping(value = "/admin/menus")
public class MenuController { public class MenuController {
private final MenuService menuService;
public MenuController(MenuService menuService) {
this.menuService = menuService;
}
@GetMapping
public String menus(){
return "admin/admin_menu";
}
} }

View File

@ -1,6 +1,8 @@
package cc.ryanc.halo.web.controller.admin; package cc.ryanc.halo.web.controller.admin;
import cc.ryanc.halo.service.OptionService;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
/** /**
@ -12,4 +14,15 @@ import org.springframework.web.bind.annotation.RequestMapping;
@Controller @Controller
@RequestMapping(value = "/admin/options") @RequestMapping(value = "/admin/options")
public class OptionController { public class OptionController {
private final OptionService optionService;
public OptionController(OptionService optionService) {
this.optionService = optionService;
}
@GetMapping
public String options(){
return "admin/admin_option";
}
} }

View File

@ -1,6 +1,8 @@
package cc.ryanc.halo.web.controller.admin; package cc.ryanc.halo.web.controller.admin;
import cc.ryanc.halo.service.PostService;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
/** /**
@ -12,4 +14,15 @@ import org.springframework.web.bind.annotation.RequestMapping;
@Controller @Controller
@RequestMapping(value = "/admin/pages") @RequestMapping(value = "/admin/pages")
public class PageController { public class PageController {
private final PostService postService;
public PageController(PostService postService) {
this.postService = postService;
}
@GetMapping
public String pages(){
return "admin/admin_page";
}
} }

View File

@ -1,6 +1,8 @@
package cc.ryanc.halo.web.controller.admin; package cc.ryanc.halo.web.controller.admin;
import cc.ryanc.halo.service.TagService;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
/** /**
@ -12,4 +14,15 @@ import org.springframework.web.bind.annotation.RequestMapping;
@Controller @Controller
@RequestMapping(value = "/admin/tags") @RequestMapping(value = "/admin/tags")
public class TagController { public class TagController {
private final TagService tagService;
public TagController(TagService tagService) {
this.tagService = tagService;
}
@GetMapping
public String tags(){
return "admin/admin_tag";
}
} }

View File

@ -1,7 +1,32 @@
package cc.ryanc.halo.web.controller.admin; package cc.ryanc.halo.web.controller.admin;
import cc.ryanc.halo.logging.Logger;
import cc.ryanc.halo.model.support.JsonResult;
import cc.ryanc.halo.service.LogService;
import cc.ryanc.halo.service.OptionService;
import cc.ryanc.halo.utils.LocaleMessageUtil;
import cc.ryanc.halo.utils.ThemeUtils;
import cc.ryanc.halo.web.controller.admin.base.BaseController;
import cc.ryanc.halo.web.controller.content.base.BaseContentController;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.FileReader;
import cn.hutool.core.io.file.FileWriter;
import cn.hutool.core.text.StrBuilder;
import cn.hutool.core.util.RuntimeUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import freemarker.template.Configuration;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.util.List;
import static cc.ryanc.halo.model.support.HaloConst.THEMES;
/** /**
* Themes controller * Themes controller
@ -11,5 +36,253 @@ import org.springframework.web.bind.annotation.RequestMapping;
*/ */
@Controller @Controller
@RequestMapping(value = "/admin/themes") @RequestMapping(value = "/admin/themes")
public class ThemeController { public class ThemeController extends BaseController {
private final Logger log = Logger.getLogger(getClass());
private LogService logsService;
public ThemeController(Configuration configuration,
OptionService optionService,
LocaleMessageUtil localeMessageUtil,
LogService logsService) {
super(configuration, optionService, localeMessageUtil);
this.logsService = logsService;
}
/**
* Render theme manage
*
* @param model model
* @return template path: admin/admin_theme.ftl
*/
@GetMapping
public String themes(Model model) {
model.addAttribute("activeTheme", BaseContentController.THEME);
model.addAttribute("themes", THEMES);
return "admin/admin_theme";
}
/**
* Active theme
*
* @param themeName theme name
* @param request request
* @return JsonResult
*/
@GetMapping(value = "/set")
@ResponseBody
@CacheEvict(value = "posts", allEntries = true, beforeInvocation = true)
public JsonResult activeTheme(@RequestParam("themeName") String themeName,
HttpServletRequest request) {
try {
optionService.saveOption("theme", themeName);
BaseContentController.THEME = themeName;
configuration.setSharedVariable("themeName", themeName);
log.info("Changed theme to {}", themeName);
return new JsonResult(1, localeMessage("code.admin.theme.change-success", new Object[]{themeName}));
} catch (Exception e) {
return new JsonResult(0, localeMessage("code.admin.theme.change-failed"));
} finally {
refreshCache();
}
}
/**
* Upload theme
*
* @param file file
* @return JsonResult
*/
@RequestMapping(value = "/upload", method = RequestMethod.POST)
@ResponseBody
public JsonResult uploadTheme(@RequestParam("file") MultipartFile file,
HttpServletRequest request) {
try {
if (!file.isEmpty()) {
final File themePath = new File(ThemeUtils.getUsersThemesPath().getAbsolutePath(), file.getOriginalFilename());
file.transferTo(themePath);
ZipUtil.unzip(themePath, ThemeUtils.getUsersThemesPath());
FileUtil.del(themePath);
log.info("Upload topic success, path is " + themePath.getAbsolutePath());
// logsService.save(LogsRecord.UPLOAD_THEME, file.getOriginalFilename(), request);
} else {
log.error("Upload theme failed, no file selected");
return new JsonResult(0, localeMessage("code.admin.theme.upload-no-file"));
}
} catch (Exception e) {
log.error("Upload theme failed: {}", e.getMessage());
return new JsonResult(0, localeMessage("code.admin.theme.upload-failed"));
} finally {
refreshCache();
}
return new JsonResult(1, localeMessage("code.admin.theme.upload-success"));
}
/**
* Delete theme
*
* @param themeName theme name
* @return redirect to admin/themes
*/
@GetMapping(value = "/remove")
public String removeTheme(@RequestParam("themeName") String themeName) {
try {
final File themePath = new File(ThemeUtils.getThemesPath(themeName), themeName);
FileUtil.del(themePath);
} catch (Exception e) {
log.error("Delete theme failed: {}", e.getMessage());
} finally {
refreshCache();
}
return "redirect:/admin/themes";
}
/**
* Install theme page
*
* @return template path: admin/widget/_theme-install.ftl
*/
@GetMapping(value = "/install")
public String install() {
return "admin/widget/_theme-install";
}
/**
* Clone theme by git
*
* @param remoteAddr theme remote address
* @param themeName theme name
* @return JsonResult
*/
@PostMapping(value = "/clone")
@ResponseBody
public JsonResult cloneFromRemote(@RequestParam(value = "remoteAddr") String remoteAddr,
@RequestParam(value = "themeName") String themeName) {
if (StrUtil.isBlank(remoteAddr) || StrUtil.isBlank(themeName)) {
return new JsonResult(0, localeMessage("code.admin.common.info-no-complete"));
}
try {
final String cmdResult = RuntimeUtil.execForStr("git clone " + remoteAddr + " " + ThemeUtils.getUsersThemesPath().getAbsolutePath() + "/" + themeName);
if ("".equals(cmdResult)) {
return new JsonResult(0, localeMessage("code.admin.theme.no-git"));
}
} catch (Exception e) {
log.error("Cloning theme failed: {}", e.getMessage());
return new JsonResult(0, localeMessage("code.admin.theme.clone-theme-failed"));
} finally {
refreshCache();
}
return new JsonResult(1, localeMessage("code.admin.common.install-success"));
}
/**
* Update theme
*
* @param themeName theme name
* @return JsonResult
*/
@GetMapping(value = "/pull")
@ResponseBody
public JsonResult pullFromRemote(@RequestParam(value = "themeName") String themeName) {
try {
final String cmdResult = RuntimeUtil.execForStr("cd " + ThemeUtils.getUsersThemesPath().getAbsolutePath() + "/" + themeName + " && git pull");
if ("".equals(cmdResult)) {
return new JsonResult(0, localeMessage("code.admin.theme.no-git"));
}
} catch (Exception e) {
log.error("Update theme failed: {}", e.getMessage());
return new JsonResult(0, localeMessage("code.admin.theme.update-theme-failed"));
} finally {
refreshCache();
}
return new JsonResult(1, localeMessage("code.admin.common.update-success"));
}
/**
* Redirect to theme option page
*
* @param model model
* @param theme theme name
* @param hasUpdate hasUpdate
*/
@GetMapping(value = "/options")
public String setting(Model model,
@RequestParam("theme") String theme,
@RequestParam("hasUpdate") String hasUpdate) {
model.addAttribute("themeDir", theme);
if (StrUtil.equals(hasUpdate, "true")) {
model.addAttribute("hasUpdate", true);
} else {
model.addAttribute("hasUpdate", false);
}
return "themes/" + theme + "/module/options";
}
/**
* Edit theme template
*
* @param model model
* @return template path: admin/admin_theme-editor.ftl
*/
@GetMapping(value = "/editor")
public String editor(Model model) {
final List<String> templates = ThemeUtils.getTplName(BaseContentController.THEME);
model.addAttribute("tpls", templates);
return "admin/admin_theme-editor";
}
/**
* Get template content
*
* @param tplName template name
* @return template content
*/
@GetMapping(value = "/getTpl", produces = "text/text;charset=UTF-8")
@ResponseBody
public String getTplContent(@RequestParam("tplName") String tplName) {
String tplContent = "";
try {
final StrBuilder themePath = new StrBuilder(ThemeUtils.getThemesPath(BaseContentController.THEME).getAbsolutePath());
themePath.append(BaseContentController.THEME);
themePath.append("/");
themePath.append(tplName);
final File themesPath = new File(themePath.toString());
final FileReader fileReader = new FileReader(themesPath);
tplContent = fileReader.readString();
} catch (Exception e) {
log.error("Get template file error: {}", e.getMessage());
}
return tplContent;
}
/**
* Save template file
*
* @param tplName template name
* @param tplContent template content
* @return JsonResult
*/
@PostMapping(value = "/editor/save")
@ResponseBody
public JsonResult saveTpl(@RequestParam("tplName") String tplName,
@RequestParam("tplContent") String tplContent) {
if (StrUtil.isBlank(tplContent)) {
return new JsonResult(0, localeMessage("code.admin.theme.edit.no-content"));
}
try {
final StrBuilder themePath = new StrBuilder(ThemeUtils.getThemesPath(BaseContentController.THEME).getAbsolutePath());
themePath.append(BaseContentController.THEME);
themePath.append("/");
themePath.append(tplName);
final File tplPath = new File(themePath.toString());
final FileWriter fileWriter = new FileWriter(tplPath);
fileWriter.write(tplContent);
} catch (Exception e) {
log.error("Template save failed: {}", e.getMessage());
return new JsonResult(0, localeMessage("code.admin.common.save-failed"));
}
return new JsonResult(1, localeMessage("code.admin.common.save-success"));
}
} }

View File

@ -0,0 +1,69 @@
package cc.ryanc.halo.web.controller.admin.base;
import cc.ryanc.halo.service.OptionService;
import cc.ryanc.halo.utils.LocaleMessageUtil;
import cc.ryanc.halo.utils.ThemeUtils;
import freemarker.template.Configuration;
import freemarker.template.TemplateModelException;
import static cc.ryanc.halo.model.support.HaloConst.OPTIONS;
import static cc.ryanc.halo.model.support.HaloConst.THEMES;
/**
* Admin base Controller
*
* @author : RYAN0UP
* @date : 2019/3/16
*/
public abstract class BaseController {
public final Configuration configuration;
public final OptionService optionService;
public final LocaleMessageUtil localeMessageUtil;
public BaseController(Configuration configuration,
OptionService optionService,
LocaleMessageUtil localeMessageUtil) {
this.configuration = configuration;
this.optionService = optionService;
this.localeMessageUtil = localeMessageUtil;
}
/**
* Clear all caches
*/
public void refreshCache() {
try {
OPTIONS.clear();
THEMES.clear();
OPTIONS = optionService.listOptions();
THEMES = ThemeUtils.getThemes();
configuration.setSharedVariable("options", OPTIONS);
} catch (TemplateModelException e) {
e.printStackTrace();
}
}
/**
* Get message with code
*
* @param code code
* @return String
*/
public String localeMessage(String code) {
return localeMessageUtil.getMessage(code);
}
/**
* Get message with code and params
*
* @param code code
* @param args args
* @return String
*/
public String localeMessage(String code, Object[] args) {
return localeMessageUtil.getMessage(code, args);
}
}

View File

@ -0,0 +1,15 @@
package cc.ryanc.halo.web.controller.content;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* Blog index page controller
*
* @author : RYAN0UP
* @date : 2019-03-17
*/
@Controller
@RequestMapping
public class IndexController {
}

View File

@ -1,29 +1,28 @@
package cc.ryanc.halo.web.controller.core; package cc.ryanc.halo.web.controller.content.base;
import cc.ryanc.halo.logging.Logger; import cc.ryanc.halo.logging.Logger;
import cn.hutool.core.text.StrBuilder; import cn.hutool.core.text.StrBuilder;
/** /**
* <pre> * Content base Controller
* Controller
* </pre>
* *
* @author : RYAN0UP * @author : RYAN0UP
* @date : 2017/12/15 * @date : 2017/12/15
*/ */
public abstract class BaseController { public abstract class BaseContentController {
/** /**
* * Default theme
*/ */
public static String THEME = "anatole"; public static String THEME = "anatole";
protected Logger log = Logger.getLogger(getClass()); protected Logger log = Logger.getLogger(getClass());
/** /**
* * Render page by template name
* *
* @param pageName pageName * @param pageName pageName
* @return * @return template path
*/ */
public String render(String pageName) { public String render(String pageName) {
final StrBuilder themeStr = new StrBuilder("themes/"); final StrBuilder themeStr = new StrBuilder("themes/");
@ -34,7 +33,7 @@ public abstract class BaseController {
} }
/** /**
* 404 * Redirect to 404
* *
* @return redirect:/404 * @return redirect:/404
*/ */

View File

@ -1,17 +1,22 @@
package cc.ryanc.halo.web.controller.core; package cc.ryanc.halo.web.controller.core;
import cc.ryanc.halo.logging.Logger; import cc.ryanc.halo.logging.Logger;
import cc.ryanc.halo.model.entity.User;
import cc.ryanc.halo.model.support.HaloConst;
import cc.ryanc.halo.utils.ThemeUtils;
import cc.ryanc.halo.web.controller.content.base.BaseContentController;
import cn.hutool.core.text.StrBuilder;
import org.springframework.boot.web.servlet.error.ErrorController; import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.FileNotFoundException;
/** /**
* <pre> * Error page Controller
*
* </pre>
* *
* @author : RYAN0UP * @author : RYAN0UP
* @date : 2017/12/26 * @date : 2017/12/26
@ -20,18 +25,29 @@ import javax.servlet.http.HttpServletRequest;
public class CommonController implements ErrorController { public class CommonController implements ErrorController {
private static final String ERROR_PATH = "/error"; private static final String ERROR_PATH = "/error";
private static final String NOT_FROUND_TEMPLATE = "404.ftl";
private static final String INTERNAL_ERROR_TEMPLATE = "500.ftl";
private static final String ADMIN_URL = "/admin";
private final Logger log = Logger.getLogger(getClass()); private final Logger log = Logger.getLogger(getClass());
/** /**
* 404500 * Handle error
* *
* @param request request * @param request request
* @return String * @return String
*/ */
@GetMapping(value = ERROR_PATH) @GetMapping(value = ERROR_PATH)
public String handleError(HttpServletRequest request) { public String handleError(HttpServletRequest request, HttpSession session) {
final Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code"); final Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
final String requestURI = request.getRequestURI();
final User user = (User) session.getAttribute(HaloConst.USER_SESSION_KEY);
log.error("Error path: [{}], status: [{}]", request.getRequestURI(), statusCode); log.error("Error path: [{}], status: [{}]", request.getRequestURI(), statusCode);
// Get the exception // Get the exception
@ -43,37 +59,70 @@ public class CommonController implements ErrorController {
if (StringUtils.startsWithIgnoreCase(throwable.getMessage(), "Could not resolve view with name '")) { if (StringUtils.startsWithIgnoreCase(throwable.getMessage(), "Could not resolve view with name '")) {
// TODO May cause unreasoned problem // TODO May cause unreasoned problem
// if Ftl was not found then redirect to /404 // if Ftl was not found then redirect to /404
return "redirect:/404"; if (requestURI.contains(ADMIN_URL) && null != user) {
return "redirect:/admin/404";
} else {
return "redirect:/404";
}
} }
} }
if (requestURI.contains(ADMIN_URL) && null != user) {
// if (statusCode.equals(CommonParamsEnum.NOT_FOUND.getValue())) { return "redirect:/admin/500";
// return "redirect:/404"; } else {
// } else { return "redirect:/500";
// return "redirect:/500"; }
// }
// TODO Complete error handler
return "redirect:/500";
} }
/** /**
* 404 * Render 404 error page
* *
* @return String * @return template path:
*/ */
@GetMapping(value = "/404") @GetMapping(value = "/admin/404")
public String fourZeroFour() { public String adminNotFround() {
return "common/error/404"; return "common/error/404";
} }
/** /**
* 500 * Render 500 error page
*
* @return template path:
*/
@GetMapping(value = "/admin/500")
public String adminInternalError() {
return "common/error/500";
}
/**
* Render 404 error page
* *
* @return String * @return String
*/ */
@GetMapping(value = "/404")
public String contentNotFround() throws FileNotFoundException {
if (ThemeUtils.isTemplateExist(NOT_FROUND_TEMPLATE)) {
return "common/error/404";
}
StrBuilder path = new StrBuilder("themes/");
path.append(BaseContentController.THEME);
path.append("/404");
return path.toString();
}
/**
* Render 500 error page
*
* @return template path:
*/
@GetMapping(value = "/500") @GetMapping(value = "/500")
public String fiveZeroZero() { public String contentInternalError() throws FileNotFoundException {
return "common/error/500"; if (ThemeUtils.isTemplateExist(INTERNAL_ERROR_TEMPLATE)) {
return "common/error/404";
}
StrBuilder path = new StrBuilder("themes/");
path.append(BaseContentController.THEME);
path.append("/500");
return path.toString();
} }
/** /**

View File

@ -0,0 +1,194 @@
package cc.ryanc.halo.web.controller.core;
import cc.ryanc.halo.model.entity.*;
import cc.ryanc.halo.model.enums.CommentStatus;
import cc.ryanc.halo.model.enums.PostStatus;
import cc.ryanc.halo.model.support.JsonResult;
import cc.ryanc.halo.service.*;
import cc.ryanc.halo.utils.MarkdownUtils;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import freemarker.template.Configuration;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static cc.ryanc.halo.model.support.HaloConst.OPTIONS;
/**
* @author : RYAN0UP
* @date : 2019-03-17
*/
@Slf4j
@Controller
@RequestMapping("/install")
public class InstallController {
private final UserService userService;
private final CategoryService categoryService;
private final PostService postService;
private final CommentService commentService;
private final OptionService optionService;
private final MenuService menuService;
private final Configuration configuration;
public InstallController(UserService userService,
CategoryService categoryService,
PostService postService,
CommentService commentService,
OptionService optionService,
MenuService menuService,
Configuration configuration) {
this.userService = userService;
this.categoryService = categoryService;
this.postService = postService;
this.commentService = commentService;
this.optionService = optionService;
this.menuService = menuService;
this.configuration = configuration;
}
/**
* Render install page
*
* @param model model
* @return template path: common/install.ftl
*/
@GetMapping
public String install(Model model) {
try {
if (StrUtil.equals("true", OPTIONS.get("is_install"))) {
model.addAttribute("isInstall", true);
} else {
model.addAttribute("isInstall", false);
}
} catch (Exception e) {
log.error(e.getMessage());
}
return "common/install";
}
/**
* Do install
*
* @param blogLocale language
* @param blogTitle blog title
* @param blogUrl blog url
* @param userName user name
* @param nickName nick name
* @param userEmail user email
* @param userPwd user password
* @param request request
* @return JsonResult
*/
@PostMapping(value = "/do")
@ResponseBody
public JsonResult doInstall(@RequestParam("blogLocale") String blogLocale,
@RequestParam("blogTitle") String blogTitle,
@RequestParam("blogUrl") String blogUrl,
@RequestParam("userName") String userName,
@RequestParam("userDisplayName") String nickName,
@RequestParam("userEmail") String userEmail,
@RequestParam("userPwd") String userPwd,
HttpServletRequest request) {
try {
if (StrUtil.equals("true", OPTIONS.get("is_install"))) {
return new JsonResult(0, "该博客已初始化,不能再次安装!");
}
// Create new user
final User user = new User();
user.setUsername(userName);
user.setNickname(StrUtil.isBlank(nickName) ? userName : nickName);
user.setEmail(userEmail);
user.setPassword(SecureUtil.md5(userPwd));
userService.create(user);
//默认分类
Category category = new Category();
category.setName("未分类");
category.setSnakeName("default");
category.setDescription("未分类");
category = categoryService.create(category);
//第一篇文章
final Post post = new Post();
final List<Category> categories = new ArrayList<>(1);
categories.add(category);
post.setTitle("Hello Halo!");
post.setOriginalContent("# Hello Halo!\n" +
"欢迎使用Halo进行创作删除这篇文章后赶紧开始吧。");
post.setFormatContent(MarkdownUtils.renderMarkdown(post.getOriginalContent()));
post.setSummary("欢迎使用Halo进行创作删除这篇文章后赶紧开始吧。");
post.setStatus(PostStatus.PUBLISHED);
post.setUrl("hello-halo");
post.setDisallowComment(true);
post.setThumbnail("/static/halo-frontend/images/thumbnail/thumbnail-" + RandomUtil.randomInt(1, 11) + ".jpg");
postService.create(post);
//第一个评论
final Comment comment = new Comment();
comment.setAuthor("ruibaby");
comment.setEmail("i@ryanc.cc");
comment.setAuthorUrl("https://ryanc.cc");
comment.setIpAddress("127.0.0.1");
comment.setGavatarMd5(SecureUtil.md5("i@ryanc.cc"));
comment.setContent("欢迎,欢迎!");
comment.setStatus(CommentStatus.PUBLISHED);
comment.setUserAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.162 Safari/537.36");
comment.setIsAdmin(false);
commentService.create(comment);
final Map<String, String> options = new HashMap<>();
// options.put(BlogPropertiesEnum.IS_INSTALL.getProp(), TrueFalseEnum.TRUE.getDesc());
// options.put(BlogPropertiesEnum.BLOG_LOCALE.getProp(), blogLocale);
// options.put(BlogPropertiesEnum.BLOG_TITLE.getProp(), blogTitle);
// options.put(BlogPropertiesEnum.BLOG_URL.getProp(), blogUrl);
// options.put(BlogPropertiesEnum.THEME.getProp(), "anatole");
// options.put(BlogPropertiesEnum.BLOG_START.getProp(), DateUtil.format(DateUtil.date(), "yyyy-MM-dd"));
// options.put(BlogPropertiesEnum.SMTP_EMAIL_ENABLE.getProp(), TrueFalseEnum.FALSE.getDesc());
// options.put(BlogPropertiesEnum.NEW_COMMENT_NOTICE.getProp(), TrueFalseEnum.FALSE.getDesc());
// options.put(BlogPropertiesEnum.COMMENT_PASS_NOTICE.getProp(), TrueFalseEnum.FALSE.getDesc());
// options.put(BlogPropertiesEnum.COMMENT_REPLY_NOTICE.getProp(), TrueFalseEnum.FALSE.getDesc());
// options.put(BlogPropertiesEnum.ATTACH_LOC.getProp(), AttachLocationEnum.SERVER.getDesc());
optionService.saveOptions(options);
//更新日志
// logsService.save(LogsRecord.INSTALL, "安装成功欢迎使用Halo。", request);
final Menu menuIndex = new Menu();
menuIndex.setName("首页");
menuIndex.setUrl("/");
menuIndex.setSort(1);
menuService.create(menuIndex);
final Menu menuArchive = new Menu();
menuArchive.setName("归档");
menuArchive.setUrl("/archives");
menuArchive.setSort(2);
menuService.create(menuArchive);
OPTIONS.clear();
OPTIONS = optionService.listOptions();
configuration.setSharedVariable("options", OPTIONS);
// configuration.setSharedVariable("user", userService.findUser());
} catch (Exception e) {
log.error(e.getMessage());
return new JsonResult(0, e.getMessage());
}
return new JsonResult(1, "安装成功!");
}
}

View File

@ -0,0 +1,137 @@
{
"@[勉强]": "<img src='/static/halo-common/OwO/paopao/勉强.png' alt='勉强.png' style='vertical-align: middle;'>",
"@[乖]": "<img src='/static/halo-common/OwO/paopao/乖.png' alt='乖.png' style='vertical-align: middle;'>",
"@[黑线]": "<img src='/static/halo-common/OwO/paopao/黑线.png' alt='黑线.png' style='vertical-align: middle;'>",
"@(献花)": "<img src='/static/halo-common/OwO/alu/献花.png' alt='献花.png' style='vertical-align: middle;'>",
"@(想一想)": "<img src='/static/halo-common/OwO/alu/想一想.png' alt='想一想.png' style='vertical-align: middle;'>",
"@(蜡烛)": "<img src='/static/halo-common/OwO/alu/蜡烛.png' alt='蜡烛.png' style='vertical-align: middle;'>",
"@[滑稽]": "<img src='/static/halo-common/OwO/paopao/滑稽.png' alt='滑稽.png' style='vertical-align: middle;'>",
"@(黑线)": "<img src='/static/halo-common/OwO/alu/黑线.png' alt='黑线.png' style='vertical-align: middle;'>",
"@[小乖]": "<img src='/static/halo-common/OwO/paopao/小乖.png' alt='小乖.png' style='vertical-align: middle;'>",
"@(中枪)": "<img src='/static/halo-common/OwO/alu/中枪.png' alt='中枪.png' style='vertical-align: middle;'>",
"@(长草)": "<img src='/static/halo-common/OwO/alu/长草.png' alt='长草.png' style='vertical-align: middle;'>",
"@[捂嘴笑]": "<img src='/static/halo-common/OwO/paopao/捂嘴笑.png' alt='捂嘴笑.png' style='vertical-align: middle;'>",
"@[酷]": "<img src='/static/halo-common/OwO/paopao/酷.png' alt='酷.png' style='vertical-align: middle;'>",
"@(喷血)": "<img src='/static/halo-common/OwO/alu/喷血.png' alt='喷血.png' style='vertical-align: middle;'>",
"@[彩虹]": "<img src='/static/halo-common/OwO/paopao/彩虹.png' alt='彩虹.png' style='vertical-align: middle;'>",
"@[钱]": "<img src='/static/halo-common/OwO/paopao/钱.png' alt='钱.png' style='vertical-align: middle;'>",
"@(呲牙)": "<img src='/static/halo-common/OwO/alu/呲牙.png' alt='呲牙.png' style='vertical-align: middle;'>",
"@[手纸]": "<img src='/static/halo-common/OwO/paopao/手纸.png' alt='手纸.png' style='vertical-align: middle;'>",
"@[钱币]": "<img src='/static/halo-common/OwO/paopao/钱币.png' alt='钱币.png' style='vertical-align: middle;'>",
"@[太开心]": "<img src='/static/halo-common/OwO/paopao/太开心.png' alt='太开心.png' style='vertical-align: middle;'>",
"@(抠鼻)": "<img src='/static/halo-common/OwO/alu/抠鼻.png' alt='抠鼻.png' style='vertical-align: middle;'>",
"@[你懂的]": "<img src='/static/halo-common/OwO/paopao/你懂的.png' alt='你懂的.png' style='vertical-align: middle;'>",
"@[吐舌]": "<img src='/static/halo-common/OwO/paopao/吐舌.png' alt='吐舌.png' style='vertical-align: middle;'>",
"@[茶杯]": "<img src='/static/halo-common/OwO/paopao/茶杯.png' alt='茶杯.png' style='vertical-align: middle;'>",
"@[泪]": "<img src='/static/halo-common/OwO/paopao/泪.png' alt='泪.png' style='vertical-align: middle;'>",
"@(吐)": "<img src='/static/halo-common/OwO/alu/吐.png' alt='吐.png' style='vertical-align: middle;'>",
"@(吐舌)": "<img src='/static/halo-common/OwO/alu/吐舌.png' alt='吐舌.png' style='vertical-align: middle;'>",
"@[太阳]": "<img src='/static/halo-common/OwO/paopao/太阳.png' alt='太阳.png' style='vertical-align: middle;'>",
"@(不出所料)": "<img src='/static/halo-common/OwO/alu/不出所料.png' alt='不出所料.png' style='vertical-align: middle;'>",
"@[挖鼻]": "<img src='/static/halo-common/OwO/paopao/挖鼻.png' alt='挖鼻.png' style='vertical-align: middle;'>",
"@(观察)": "<img src='/static/halo-common/OwO/alu/观察.png' alt='观察.png' style='vertical-align: middle;'>",
"@(内伤)": "<img src='/static/halo-common/OwO/alu/内伤.png' alt='内伤.png' style='vertical-align: middle;'>",
"@[灯泡]": "<img src='/static/halo-common/OwO/paopao/灯泡.png' alt='灯泡.png' style='vertical-align: middle;'>",
"@[呵呵]": "<img src='/static/halo-common/OwO/paopao/呵呵.png' alt='呵呵.png' style='vertical-align: middle;'>",
"@(尴尬)": "<img src='/static/halo-common/OwO/alu/尴尬.png' alt='尴尬.png' style='vertical-align: middle;'>",
"@(深思)": "<img src='/static/halo-common/OwO/alu/深思.png' alt='深思.png' style='vertical-align: middle;'>",
"@(小怒)": "<img src='/static/halo-common/OwO/alu/小怒.png' alt='小怒.png' style='vertical-align: middle;'>",
"@[狂汗]": "<img src='/static/halo-common/OwO/paopao/狂汗.png' alt='狂汗.png' style='vertical-align: middle;'>",
"@[what]": "<img src='/static/halo-common/OwO/paopao/what.png' alt='what.png' style='vertical-align: middle;'>",
"@(口水)": "<img src='/static/halo-common/OwO/alu/口水.png' alt='口水.png' style='vertical-align: middle;'>",
"@(大囧)": "<img src='/static/halo-common/OwO/alu/大囧.png' alt='大囧.png' style='vertical-align: middle;'>",
"@[懒得理]": "<img src='/static/halo-common/OwO/paopao/懒得理.png' alt='懒得理.png' style='vertical-align: middle;'>",
"@[真棒]": "<img src='/static/halo-common/OwO/paopao/真棒.png' alt='真棒.png' style='vertical-align: middle;'>",
"@[三道杠]": "<img src='/static/halo-common/OwO/paopao/三道杠.png' alt='三道杠.png' style='vertical-align: middle;'>",
"@[睡觉]": "<img src='/static/halo-common/OwO/paopao/睡觉.png' alt='睡觉.png' style='vertical-align: middle;'>",
"@[沙发]": "<img src='/static/halo-common/OwO/paopao/沙发.png' alt='沙发.png' style='vertical-align: middle;'>",
"@(暗地观察)": "<img src='/static/halo-common/OwO/alu/暗地观察.png' alt='暗地观察.png' style='vertical-align: middle;'>",
"@[笑尿]": "<img src='/static/halo-common/OwO/paopao/笑尿.png' alt='笑尿.png' style='vertical-align: middle;'>",
"@[生气]": "<img src='/static/halo-common/OwO/paopao/生气.png' alt='生气.png' style='vertical-align: middle;'>",
"@(肿包)": "<img src='/static/halo-common/OwO/alu/肿包.png' alt='肿包.png' style='vertical-align: middle;'>",
"@(狂汗)": "<img src='/static/halo-common/OwO/alu/狂汗.png' alt='狂汗.png' style='vertical-align: middle;'>",
"@(喷水)": "<img src='/static/halo-common/OwO/alu/喷水.png' alt='喷水.png' style='vertical-align: middle;'>",
"@(惊喜)": "<img src='/static/halo-common/OwO/alu/惊喜.png' alt='惊喜.png' style='vertical-align: middle;'>",
"@[便便]": "<img src='/static/halo-common/OwO/paopao/便便.png' alt='便便.png' style='vertical-align: middle;'>",
"@(阴暗)": "<img src='/static/halo-common/OwO/alu/阴暗.png' alt='阴暗.png' style='vertical-align: middle;'>",
"@[汗]": "<img src='/static/halo-common/OwO/paopao/汗.png' alt='汗.png' style='vertical-align: middle;'>",
"@(喜极而泣)": "<img src='/static/halo-common/OwO/alu/喜极而泣.png' alt='喜极而泣.png' style='vertical-align: middle;'>",
"@[香蕉]": "<img src='/static/halo-common/OwO/paopao/香蕉.png' alt='香蕉.png' style='vertical-align: middle;'>",
"@[大拇指]": "<img src='/static/halo-common/OwO/paopao/大拇指.png' alt='大拇指.png' style='vertical-align: middle;'>",
"@[蜡烛]": "<img src='/static/halo-common/OwO/paopao/蜡烛.png' alt='蜡烛.png' style='vertical-align: middle;'>",
"@(鼓掌)": "<img src='/static/halo-common/OwO/alu/鼓掌.png' alt='鼓掌.png' style='vertical-align: middle;'>",
"@(邪恶)": "<img src='/static/halo-common/OwO/alu/邪恶.png' alt='邪恶.png' style='vertical-align: middle;'>",
"@[惊恐]": "<img src='/static/halo-common/OwO/paopao/惊恐.png' alt='惊恐.png' style='vertical-align: middle;'>",
"@[惊哭]": "<img src='/static/halo-common/OwO/paopao/惊哭.png' alt='惊哭.png' style='vertical-align: middle;'>",
"@[嘚瑟]": "<img src='/static/halo-common/OwO/paopao/嘚瑟.png' alt='嘚瑟.png' style='vertical-align: middle;'>",
"@[药丸]": "<img src='/static/halo-common/OwO/paopao/药丸.png' alt='药丸.png' style='vertical-align: middle;'>",
"@[吐]": "<img src='/static/halo-common/OwO/paopao/吐.png' alt='吐.png' style='vertical-align: middle;'>",
"@[蛋糕]": "<img src='/static/halo-common/OwO/paopao/蛋糕.png' alt='蛋糕.png' style='vertical-align: middle;'>",
"@(愤怒)": "<img src='/static/halo-common/OwO/alu/愤怒.png' alt='愤怒.png' style='vertical-align: middle;'>",
"@(无所谓)": "<img src='/static/halo-common/OwO/alu/无所谓.png' alt='无所谓.png' style='vertical-align: middle;'>",
"@[啊]": "<img src='/static/halo-common/OwO/paopao/啊.png' alt='啊.png' style='vertical-align: middle;'>",
"@(坐等)": "<img src='/static/halo-common/OwO/alu/坐等.png' alt='坐等.png' style='vertical-align: middle;'>",
"@[nico]": "<img src='/static/halo-common/OwO/paopao/nico.png' alt='nico.png' style='vertical-align: middle;'>",
"@[弱]": "<img src='/static/halo-common/OwO/paopao/弱.png' alt='弱.png' style='vertical-align: middle;'>",
"@[小红脸]": "<img src='/static/halo-common/OwO/paopao/小红脸.png' alt='小红脸.png' style='vertical-align: middle;'>",
"@(哭泣)": "<img src='/static/halo-common/OwO/alu/哭泣.png' alt='哭泣.png' style='vertical-align: middle;'>",
"@[呼]": "<img src='/static/halo-common/OwO/paopao/呼.png' alt='呼.png' style='vertical-align: middle;'>",
"@[哈哈]": "<img src='/static/halo-common/OwO/paopao/哈哈.png' alt='哈哈.png' style='vertical-align: middle;'>",
"@[爱心]": "<img src='/static/halo-common/OwO/paopao/爱心.png' alt='爱心.png' style='vertical-align: middle;'>",
"@(不说话)": "<img src='/static/halo-common/OwO/alu/不说话.png' alt='不说话.png' style='vertical-align: middle;'>",
"@(脸红)": "<img src='/static/halo-common/OwO/alu/脸红.png' alt='脸红.png' style='vertical-align: middle;'>",
"@[惊讶]": "<img src='/static/halo-common/OwO/paopao/惊讶.png' alt='惊讶.png' style='vertical-align: middle;'>",
"@(傻笑)": "<img src='/static/halo-common/OwO/alu/傻笑.png' alt='傻笑.png' style='vertical-align: middle;'>",
"@(期待)": "<img src='/static/halo-common/OwO/alu/期待.png' alt='期待.png' style='vertical-align: middle;'>",
"@(击掌)": "<img src='/static/halo-common/OwO/alu/击掌.png' alt='击掌.png' style='vertical-align: middle;'>",
"@(抽烟)": "<img src='/static/halo-common/OwO/alu/抽烟.png' alt='抽烟.png' style='vertical-align: middle;'>",
"@[胜利]": "<img src='/static/halo-common/OwO/paopao/胜利.png' alt='胜利.png' style='vertical-align: middle;'>",
"@(害羞)": "<img src='/static/halo-common/OwO/alu/害羞.png' alt='害羞.png' style='vertical-align: middle;'>",
"@[吃翔]": "<img src='/static/halo-common/OwO/paopao/吃翔.png' alt='吃翔.png' style='vertical-align: middle;'>",
"@(便便)": "<img src='/static/halo-common/OwO/alu/便便.png' alt='便便.png' style='vertical-align: middle;'>",
"@(献黄瓜)": "<img src='/static/halo-common/OwO/alu/献黄瓜.png' alt='献黄瓜.png' style='vertical-align: middle;'>",
"@[不高兴]": "<img src='/static/halo-common/OwO/paopao/不高兴.png' alt='不高兴.png' style='vertical-align: middle;'>",
"@(皱眉)": "<img src='/static/halo-common/OwO/alu/皱眉.png' alt='皱眉.png' style='vertical-align: middle;'>",
"@(汗)": "<img src='/static/halo-common/OwO/alu/汗.png' alt='汗.png' style='vertical-align: middle;'>",
"@[吃瓜]": "<img src='/static/halo-common/OwO/paopao/吃瓜.png' alt='吃瓜.png' style='vertical-align: middle;'>",
"@[心碎]": "<img src='/static/halo-common/OwO/paopao/心碎.png' alt='心碎.png' style='vertical-align: middle;'>",
"@[玫瑰]": "<img src='/static/halo-common/OwO/paopao/玫瑰.png' alt='玫瑰.png' style='vertical-align: middle;'>",
"@(扇耳光)": "<img src='/static/halo-common/OwO/alu/扇耳光.png' alt='扇耳光.png' style='vertical-align: middle;'>",
"@[OK]": "<img src='/static/halo-common/OwO/paopao/OK.png' alt='OK.png' style='vertical-align: middle;'>",
"@[音乐]": "<img src='/static/halo-common/OwO/paopao/音乐.png' alt='音乐.png' style='vertical-align: middle;'>",
"@[阴险]": "<img src='/static/halo-common/OwO/paopao/阴险.png' alt='阴险.png' style='vertical-align: middle;'>",
"@(看热闹)": "<img src='/static/halo-common/OwO/alu/看热闹.png' alt='看热闹.png' style='vertical-align: middle;'>",
"@[星星月亮]": "<img src='/static/halo-common/OwO/paopao/星星月亮.png' alt='星星月亮.png' style='vertical-align: middle;'>",
"@[犀利]": "<img src='/static/halo-common/OwO/paopao/犀利.png' alt='犀利.png' style='vertical-align: middle;'>",
"@(高兴)": "<img src='/static/halo-common/OwO/alu/高兴.png' alt='高兴.png' style='vertical-align: middle;'>",
"@(亲亲)": "<img src='/static/halo-common/OwO/alu/亲亲.png' alt='亲亲.png' style='vertical-align: middle;'>",
"@[冷]": "<img src='/static/halo-common/OwO/paopao/冷.png' alt='冷.png' style='vertical-align: middle;'>",
"@[喷]": "<img src='/static/halo-common/OwO/paopao/喷.png' alt='喷.png' style='vertical-align: middle;'>",
"@(咽气)": "<img src='/static/halo-common/OwO/alu/咽气.png' alt='咽气.png' style='vertical-align: middle;'>",
"@[怒]": "<img src='/static/halo-common/OwO/paopao/怒.png' alt='怒.png' style='vertical-align: middle;'>",
"@(锁眉)": "<img src='/static/halo-common/OwO/alu/锁眉.png' alt='锁眉.png' style='vertical-align: middle;'>",
"@[红领巾]": "<img src='/static/halo-common/OwO/paopao/红领巾.png' alt='红领巾.png' style='vertical-align: middle;'>",
"@(中指)": "<img src='/static/halo-common/OwO/alu/中指.png' alt='中指.png' style='vertical-align: middle;'>",
"@[礼物]": "<img src='/static/halo-common/OwO/paopao/礼物.png' alt='礼物.png' style='vertical-align: middle;'>",
"@(赞一个)": "<img src='/static/halo-common/OwO/alu/赞一个.png' alt='赞一个.png' style='vertical-align: middle;'>",
"@(欢呼)": "<img src='/static/halo-common/OwO/alu/欢呼.png' alt='欢呼.png' style='vertical-align: middle;'>",
"@[花心]": "<img src='/static/halo-common/OwO/paopao/花心.png' alt='花心.png' style='vertical-align: middle;'>",
"@[鄙视]": "<img src='/static/halo-common/OwO/paopao/鄙视.png' alt='鄙视.png' style='vertical-align: middle;'>",
"@[呀咩爹]": "<img src='/static/halo-common/OwO/paopao/呀咩爹.png' alt='呀咩爹.png' style='vertical-align: middle;'>",
"@[开心]": "<img src='/static/halo-common/OwO/paopao/开心.png' alt='开心.png' style='vertical-align: middle;'>",
"@[酸爽]": "<img src='/static/halo-common/OwO/paopao/酸爽.png' alt='酸爽.png' style='vertical-align: middle;'>",
"@(得意)": "<img src='/static/halo-common/OwO/alu/得意.png' alt='得意.png' style='vertical-align: middle;'>",
"@(装大款)": "<img src='/static/halo-common/OwO/alu/装大款.png' alt='装大款.png' style='vertical-align: middle;'>",
"@[委屈]": "<img src='/static/halo-common/OwO/paopao/委屈.png' alt='委屈.png' style='vertical-align: middle;'>",
"@(中刀)": "<img src='/static/halo-common/OwO/alu/中刀.png' alt='中刀.png' style='vertical-align: middle;'>",
"@(看不见)": "<img src='/static/halo-common/OwO/alu/看不见.png' alt='看不见.png' style='vertical-align: middle;'>",
"@(小眼睛)": "<img src='/static/halo-common/OwO/alu/小眼睛.png' alt='小眼睛.png' style='vertical-align: middle;'>",
"@(无语)": "<img src='/static/halo-common/OwO/alu/无语.png' alt='无语.png' style='vertical-align: middle;'>",
"@(投降)": "<img src='/static/halo-common/OwO/alu/投降.png' alt='投降.png' style='vertical-align: middle;'>",
"@(无奈)": "<img src='/static/halo-common/OwO/alu/无奈.png' alt='无奈.png' style='vertical-align: middle;'>",
"@[咦]": "<img src='/static/halo-common/OwO/paopao/咦.png' alt='咦.png' style='vertical-align: middle;'>",
"@(不高兴)": "<img src='/static/halo-common/OwO/alu/不高兴.png' alt='不高兴.png' style='vertical-align: middle;'>",
"@[笑眼]": "<img src='/static/halo-common/OwO/paopao/笑眼.png' alt='笑眼.png' style='vertical-align: middle;'>",
"@(吐血倒地)": "<img src='/static/halo-common/OwO/alu/吐血倒地.png' alt='吐血倒地.png' style='vertical-align: middle;'>",
"@[疑问]": "<img src='/static/halo-common/OwO/paopao/疑问.png' alt='疑问.png' style='vertical-align: middle;'>"
}

View File

@ -26,18 +26,18 @@
<ul class="menu"> <ul class="menu">
<#if newComments?size gt 0> <#if newComments?size gt 0>
<#assign x=0> <#assign x=0>
<#list newComments?sort_by("commentDate")?reverse as comment> <#list newComments?sort_by("createTime")?reverse as comment>
<#assign x = x+1> <#assign x = x+1>
<li> <li>
<a data-pjax="true" href="/admin/comments?status=1"> <a data-pjax="true" href="/admin/comments?status=1">
<div class="pull-left"> <div class="pull-left">
<img src="//gravatar.loli.net/avatar/${comment.commentAuthorAvatarMd5?default("hash")}?s=256&d=${options.native_comment_avatar?default("mm")}" class="img-circle" alt="User Image"> <img src="//gravatar.loli.net/avatar/${comment.gavatarMd5!'hash'}?s=256&d=${options.native_comment_avatar!'mm'}" class="img-circle" alt="User Image">
</div> </div>
<h4> <h4>
${comment.commentAuthor} ${comment.author!}
<small> <@common.timeline datetime="${comment.commentDate}"?datetime /></small> <small> <@common.timeline datetime="${comment.createTime}"?datetime /></small>
</h4> </h4>
<object>${comment.commentContent}</object> <object>${comment.content!}</object>
</a> </a>
</li> </li>
<#if x==10> <#if x==10>

View File

@ -1,52 +1 @@
<#compress> 404 Not Found
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
<meta name="renderer" content="webkit">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<title>404 Not Found</title>
<link href="//cdnjs.loli.net/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<link href="//cdnjs.loli.net/ajax/libs/animate.css/3.7.0/animate.min.css" rel="stylesheet">
<style type="text/css" rel="stylesheet">
body{margin:0}*{box-sizing:border-box}h1,h2{margin:0}a{color:#fff;text-decoration:none}body,html{font-family:-apple-system,system-ui,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif}.fullscreen{background-position:50% 50%;background-size:cover}.fullscreen,.fullscreen .backColor{position:absolute;top:0;left:0;width:100%;height:100%}.fullscreen .backColor{background-color:rgba(0,0,0,.1)}.infos{display:flex;text-align:center;align-items:center;justify-content:center}.infos,.main-content{position:absolute;top:0;left:0;width:100%;height:100%;color:#fff}.main-content{background: #833ab4;background: -webkit-linear-gradient(to right, #833ab4, #fd1d1d, #fcb045);background: linear-gradient(to right, #833ab4, #fd1d1d, #fcb045);}.errorPage{position:relative;width:100vw;height:100vh}.infos-h1{margin:0;font-size:5em;line-height:1}.infos-h1 h1{font-weight:200}.footer{position:absolute;right:1rem;bottom:1rem;left:1rem;z-index:9999;font-size:14px}.infos-h2{font-size:24px}.infos-h2 a:hover{color:#7a8d85}
</style>
</head>
<body>
<div class="container">
<div class="errorPage">
<div class="main-content ">
<div class="fullscreen">
<div class="backColor"></div>
</div>
<div class="infos">
<div class="infos-main">
<div class="infos-h1"><h1>404</h1></div>
<div class="infos-h2">
<a href="javascript:window.history.back()" title="返回上一页">
<i class="fa fa-chevron-left"></i>
</a>
<a href="/" title="返回到主页">
<i class="fa fa-home"></i>
</a>
</div>
</div>
</div>
<div class="footer">
<span>Copyright © 2018</span>
<a href="${options.blog_url!}">${options.blog_title!'Halo'}</a>
<span style="float: right">
Background image from <a href="https://cn.bing.com/" target="_blank">Bing</a>.
</span>
</div>
</div>
</div>
</div>
</body>
<script src="//cdnjs.loli.net/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script>
eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('$.o(\'m://q.8.n/?1=4%y%z%B.5.9%d.e%f%g%h%i%j%k\',l(b){6 a=b.p[0].1;6 c=$(\'.r\');c.t("u-v","1(4://s.w.5.x"+a+")");c.2(\'3 A\');$(\'.7-C\').2(\'3 D\');$(\'.7-E\').2(\'3 F\')});',42,42,'|url|addClass|animated|http|bing|var|infos|afeld|com||||2FHPImageArchive|aspx|3Fformat|3Djs|26idx|3D0|26n|3D1|function|https|me|get|images|jsonp|fullscreen||css|background|image|cn|net|3A|2F|fadeIn|2Fcn|h1|shake|h2|fadeInDown'.split('|'),0,{}))
</script>
</html>
</#compress>

View File

@ -1,52 +1 @@
<#compress> 500 Internal Error
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
<meta name="renderer" content="webkit">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<title>500 Error Page</title>
<link href="//cdnjs.loli.net/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
<link href="//cdnjs.loli.net/ajax/libs/animate.css/3.7.0/animate.min.css" rel="stylesheet">
<style type="text/css" rel="stylesheet">
body{margin:0}*{box-sizing:border-box}h1,h2{margin:0}a{color:#fff;text-decoration:none}body,html{font-family:-apple-system,system-ui,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif}.fullscreen{background-position:50% 50%;background-size:cover}.fullscreen,.fullscreen .backColor{position:absolute;top:0;left:0;width:100%;height:100%}.fullscreen .backColor{background-color:rgba(0,0,0,.1)}.infos{display:flex;text-align:center;align-items:center;justify-content:center}.infos,.main-content{position:absolute;top:0;left:0;width:100%;height:100%;color:#fff}.main-content{background: #833ab4;background: -webkit-linear-gradient(to right, #833ab4, #fd1d1d, #fcb045);background: linear-gradient(to right, #833ab4, #fd1d1d, #fcb045);}.errorPage{position:relative;width:100vw;height:100vh}.infos-h1{margin:0;font-size:5em;line-height:1}.infos-h1 h1{font-weight:200}.footer{position:absolute;right:1rem;bottom:1rem;left:1rem;z-index:9999;font-size:14px}.infos-h2{font-size:24px}.infos-h2 a:hover{color:#7a8d85}
</style>
</head>
<body>
<div class="container">
<div class="errorPage">
<div class="main-content ">
<div class="fullscreen">
<div class="backColor"></div>
</div>
<div class="infos">
<div class="infos-main">
<div class="infos-h1"><h1>500</h1></div>
<div class="infos-h2">
<a href="javascript:window.history.back()" title="返回上一页">
<i class="fa fa-chevron-left"></i>
</a>
<a href="/" title="返回到主页">
<i class="fa fa-home"></i>
</a>
</div>
</div>
</div>
<div class="footer">
<span>Copyright © 2018</span>
<a href="${options.blog_title!}">${options.blog_title!'Halo'}</a>
<span style="float: right">
Background image from <a href="https://cn.bing.com/" target="_blank">Bing</a>.
</span>
</div>
</div>
</div>
</div>
</body>
<script src="//cdnjs.loli.net/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script>
eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('$.o(\'m://q.8.n/?1=4%y%z%B.5.9%d.e%f%g%h%i%j%k\',l(b){6 a=b.p[0].1;6 c=$(\'.r\');c.t("u-v","1(4://s.w.5.x"+a+")");c.2(\'3 A\');$(\'.7-C\').2(\'3 D\');$(\'.7-E\').2(\'3 F\')});',42,42,'|url|addClass|animated|http|bing|var|infos|afeld|com||||2FHPImageArchive|aspx|3Fformat|3Djs|26idx|3D0|26n|3D1|function|https|me|get|images|jsonp|fullscreen||css|background|image|cn|net|3A|2F|fadeIn|2Fcn|h1|shake|h2|fadeInDown'.split('|'),0,{}))
</script>
</html>
</#compress>