Merge pull request #195 from halo-dev/dev

Dev
pull/286/head
John Niang 2019-06-11 15:03:57 +08:00 committed by GitHub
commit c17448046e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 320 additions and 65 deletions

View File

@ -38,7 +38,7 @@ wget https://github.com/halo-dev/halo/releases/download/v1.0.1/halo-1.0.1.jar -O
### 启动 Halo ### 启动 Halo
```bash ```bash
nohup java -jar halo-latest.jar & nohup java -jar halo-latest.jar >/dev/null 2>&1&
``` ```
详细文档请移步:<https://halo.run/docs> 详细文档请移步:<https://halo.run/docs>

View File

@ -1,7 +1,8 @@
plugins { plugins {
id 'org.springframework.boot' version '2.1.3.RELEASE' id 'org.springframework.boot' version '2.1.3.RELEASE'
id "io.freefair.lombok" version "3.1.4" id "io.freefair.lombok" version "3.6.6"
id 'java' id 'java'
id 'war'
} }
apply plugin: 'io.spring.dependency-management' apply plugin: 'io.spring.dependency-management'

View File

@ -2,6 +2,8 @@ package run.halo.app;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing; import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableAsync;
@ -21,7 +23,7 @@ import run.halo.app.repository.base.BaseRepositoryImpl;
@EnableScheduling @EnableScheduling
@EnableAsync @EnableAsync
@EnableJpaRepositories(basePackages = "run.halo.app.repository", repositoryBaseClass = BaseRepositoryImpl.class) @EnableJpaRepositories(basePackages = "run.halo.app.repository", repositoryBaseClass = BaseRepositoryImpl.class)
public class Application { public class Application extends SpringBootServletInitializer {
public static void main(String[] args) { public static void main(String[] args) {
// Customize the spring config location // Customize the spring config location
@ -30,4 +32,10 @@ public class Application {
// Run application // Run application
SpringApplication.run(Application.class, args); SpringApplication.run(Application.class, args);
} }
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
System.setProperty("spring.config.additional-location", "file:${user.home}/.halo/,file:${user.home}/halo-dev/");
return application.sources(Application.class);
}
} }

View File

@ -29,6 +29,7 @@ import run.halo.app.security.resolver.AuthenticationArgumentResolver;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.Properties;
/** /**
* Mvc configuration. * Mvc configuration.
@ -120,6 +121,11 @@ public class WebMvcAutoConfiguration implements WebMvcConfigurer {
configurer.setTemplateLoaderPaths(FILE_PROTOCOL + haloProperties.getWorkDir() + "templates/", "classpath:/templates/"); configurer.setTemplateLoaderPaths(FILE_PROTOCOL + haloProperties.getWorkDir() + "templates/", "classpath:/templates/");
configurer.setDefaultEncoding("UTF-8"); configurer.setDefaultEncoding("UTF-8");
Properties properties = new Properties();
properties.setProperty("auto_import","/common/macro/common_macro.ftl as common");
configurer.setFreemarkerSettings(properties);
// Predefine configuration // Predefine configuration
freemarker.template.Configuration configuration = configurer.createConfiguration(); freemarker.template.Configuration configuration = configurer.createConfiguration();
if (haloProperties.isProductionEnv()) { if (haloProperties.isProductionEnv()) {

View File

@ -27,7 +27,7 @@ import java.util.Map;
* *
* @author johnniang * @author johnniang
*/ */
@RestControllerAdvice @RestControllerAdvice({"run.halo.app.controller.admin.api", "run.halo.app.controller.content.api"})
@Slf4j @Slf4j
public class ControllerExceptionHandler { public class ControllerExceptionHandler {

View File

@ -66,7 +66,7 @@ public class ContentArchiveController {
* Render post archives page. * Render post archives page.
* *
* @param model model * @param model model
* @return template path : theme/{theme}/archives.ftl * @return template path : themes/{theme}/archives.ftl
*/ */
@GetMapping @GetMapping
public String archives(Model model) { public String archives(Model model) {
@ -77,7 +77,7 @@ public class ContentArchiveController {
* Render post archives page. * Render post archives page.
* *
* @param model model * @param model model
* @return template path : theme/{theme}/archives.ftl * @return template path : themes/{theme}/archives.ftl
*/ */
@GetMapping(value = "page/{page}") @GetMapping(value = "page/{page}")
public String archives(Model model, public String archives(Model model,
@ -102,7 +102,7 @@ public class ContentArchiveController {
* @param url post slug url. * @param url post slug url.
* @param cp comment page number * @param cp comment page number
* @param model model * @param model model
* @return template path: theme/{theme}/post.ftl * @return template path: themes/{theme}/post.ftl
*/ */
@GetMapping("{url}") @GetMapping("{url}")
public String post(@PathVariable("url") String url, public String post(@PathVariable("url") String url,

View File

@ -13,10 +13,8 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import run.halo.app.model.entity.Category; import run.halo.app.model.entity.Category;
import run.halo.app.model.entity.Post; import run.halo.app.model.entity.Post;
import run.halo.app.service.CategoryService; import run.halo.app.model.vo.PostListVO;
import run.halo.app.service.OptionService; import run.halo.app.service.*;
import run.halo.app.service.PostCategoryService;
import run.halo.app.service.ThemeService;
import static org.springframework.data.domain.Sort.Direction.DESC; import static org.springframework.data.domain.Sort.Direction.DESC;
@ -34,25 +32,29 @@ public class ContentCategoryController {
private final PostCategoryService postCategoryService; private final PostCategoryService postCategoryService;
private final PostService postService;
private final OptionService optionService; private final OptionService optionService;
public ContentCategoryController(CategoryService categoryService, public ContentCategoryController(CategoryService categoryService,
ThemeService themeService, ThemeService themeService,
PostCategoryService postCategoryService, PostCategoryService postCategoryService,
OptionService optionService) { PostService postService, OptionService optionService) {
this.categoryService = categoryService; this.categoryService = categoryService;
this.themeService = themeService; this.themeService = themeService;
this.postCategoryService = postCategoryService; this.postCategoryService = postCategoryService;
this.postService = postService;
this.optionService = optionService; this.optionService = optionService;
} }
/** /**
* Render category list page * Render category list page
* *
* @return template path: theme/{theme}/categories.ftl * @return template path: themes/{theme}/categories.ftl
*/ */
@GetMapping @GetMapping
public String categories() { public String categories(Model model) {
model.addAttribute("is_categories", true);
return themeService.render("categories"); return themeService.render("categories");
} }
@ -61,7 +63,7 @@ public class ContentCategoryController {
* *
* @param model model * @param model model
* @param slugName slugName * @param slugName slugName
* @return template path: theme/{theme}/category.ftl * @return template path: themes/{theme}/category.ftl
*/ */
@GetMapping(value = "{slugName}") @GetMapping(value = "{slugName}")
public String categories(Model model, public String categories(Model model,
@ -75,7 +77,7 @@ public class ContentCategoryController {
* @param model model * @param model model
* @param slugName slugName * @param slugName slugName
* @param page current page number * @param page current page number
* @return template path: theme/{theme}/category.ftl * @return template path: themes/{theme}/category.ftl
*/ */
@GetMapping("{slugName}/page/{page}") @GetMapping("{slugName}/page/{page}")
public String categories(Model model, public String categories(Model model,
@ -86,10 +88,11 @@ public class ContentCategoryController {
final Category category = categoryService.getBySlugName(slugName); final Category category = categoryService.getBySlugName(slugName);
final Pageable pageable = PageRequest.of(page - 1, optionService.getPostPageSize(), sort); final Pageable pageable = PageRequest.of(page - 1, optionService.getPostPageSize(), sort);
Page<Post> posts = postCategoryService.pagePostBy(category.getId(), pageable); Page<Post> postPage = postCategoryService.pagePostBy(category.getId(), pageable);
Page<PostListVO> posts = postService.convertToListVo(postPage);
final int[] rainbow = PageUtil.rainbow(page, posts.getTotalPages(), 3); final int[] rainbow = PageUtil.rainbow(page, posts.getTotalPages(), 3);
model.addAttribute("is_categories", true); model.addAttribute("is_category", true);
model.addAttribute("posts", posts); model.addAttribute("posts", posts);
model.addAttribute("rainbow", rainbow); model.addAttribute("rainbow", rainbow);
model.addAttribute("category", category); model.addAttribute("category", category);

View File

@ -51,7 +51,7 @@ public class ContentIndexController {
* Render blog index * Render blog index
* *
* @param model model * @param model model
* @return template path: /{theme}/post.ftl * @return template path: themes/{theme}/index.ftl
*/ */
@GetMapping @GetMapping
public String index(Model model) { public String index(Model model) {
@ -63,7 +63,7 @@ public class ContentIndexController {
* *
* @param model model * @param model model
* @param page current page number * @param page current page number
* @return template path: /{theme}/post.ftl * @return template path: themes/{theme}/index.ftl
*/ */
@GetMapping(value = "page/{page}") @GetMapping(value = "page/{page}")
public String index(Model model, public String index(Model model,

View File

@ -53,7 +53,7 @@ public class ContentJournalController {
* Render journal page. * Render journal page.
* *
* @param model model * @param model model
* @return template path: theme/{theme}/journal.ftl * @return template path: themes/{theme}/journals.ftl
*/ */
@GetMapping @GetMapping
public String journals(Model model) { public String journals(Model model) {
@ -66,7 +66,7 @@ public class ContentJournalController {
* *
* @param model model * @param model model
* @param page current page number * @param page current page number
* @return template path: theme/{theme}/journal.ftl * @return template path: themes/{theme}/journals.ftl
*/ */
@GetMapping(value = "page/{page}") @GetMapping(value = "page/{page}")
public String journals(Model model, public String journals(Model model,

View File

@ -48,7 +48,7 @@ public class ContentSearchController {
* *
* @param model model * @param model model
* @param keyword keyword * @param keyword keyword
* @return template path : themes/{theme}/search * @return template path : themes/{theme}/search.ftl
*/ */
@GetMapping @GetMapping
public String search(Model model, public String search(Model model,
@ -61,7 +61,7 @@ public class ContentSearchController {
* *
* @param model model * @param model model
* @param keyword keyword * @param keyword keyword
* @return template path :themes/{theme}/search * @return template path :themes/{theme}/search.ftl
*/ */
@GetMapping(value = "page/{page}") @GetMapping(value = "page/{page}")
public String search(Model model, public String search(Model model,
@ -69,14 +69,14 @@ public class ContentSearchController {
@PathVariable(value = "page") Integer page, @PathVariable(value = "page") Integer page,
@SortDefault(sort = "createTime", direction = DESC) Sort sort) { @SortDefault(sort = "createTime", direction = DESC) Sort sort) {
final Pageable pageable = PageRequest.of(page - 1, optionService.getPostPageSize(), sort); final Pageable pageable = PageRequest.of(page - 1, optionService.getPostPageSize(), sort);
final Page<Post> posts = postService.pageBy(keyword, pageable); final Page<Post> postPage = postService.pageBy(keyword, pageable);
final Page<PostListVO> postPage = postService.convertToListVo(posts); final Page<PostListVO> posts = postService.convertToListVo(postPage);
final int[] rainbow = PageUtil.rainbow(page, postPage.getTotalPages(), 3); final int[] rainbow = PageUtil.rainbow(page, posts.getTotalPages(), 3);
model.addAttribute("is_search", true); model.addAttribute("is_search", true);
model.addAttribute("keyword", keyword); model.addAttribute("keyword", keyword);
model.addAttribute("posts", postPage); model.addAttribute("posts", posts);
model.addAttribute("rainbow", rainbow); model.addAttribute("rainbow", rainbow);
return themeService.render("search"); return themeService.render("search");
} }

View File

@ -4,7 +4,6 @@ import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import run.halo.app.model.entity.Sheet; import run.halo.app.model.entity.Sheet;
import run.halo.app.model.enums.PostStatus; import run.halo.app.model.enums.PostStatus;
import run.halo.app.model.support.HaloConst; import run.halo.app.model.support.HaloConst;
@ -34,7 +33,7 @@ public class ContentSheetController {
/** /**
* Render photo page * Render photo page
* *
* @return template path: themes/{theme}/gallery.ftl * @return template path: themes/{theme}/photos.ftl
*/ */
@GetMapping(value = "/photos") @GetMapping(value = "/photos")
public String photos() { public String photos() {
@ -60,7 +59,6 @@ public class ContentSheetController {
*/ */
@GetMapping(value = "/s/{url}") @GetMapping(value = "/s/{url}")
public String sheet(@PathVariable(value = "url") String url, public String sheet(@PathVariable(value = "url") String url,
@RequestParam(value = "cp", defaultValue = "1") Integer cp,
Model model) { Model model) {
Sheet sheet = sheetService.getBy(PostStatus.PUBLISHED, url); Sheet sheet = sheetService.getBy(PostStatus.PUBLISHED, url);

View File

@ -53,10 +53,11 @@ public class ContentTagController {
/** /**
* All of tags * All of tags
* *
* @return template path: themes/{theme}/tags * @return template path: themes/{theme}/tags.ftl
*/ */
@GetMapping @GetMapping
public String tags() { public String tags(Model model) {
model.addAttribute("is_tags", true);
return themeService.render("tags"); return themeService.render("tags");
} }
@ -65,7 +66,7 @@ public class ContentTagController {
* *
* @param model model * @param model model
* @param slugName slug name * @param slugName slug name
* @return template path: themes/{theme}/tag * @return template path: themes/{theme}/tag.ftl
*/ */
@GetMapping(value = "{slugName}") @GetMapping(value = "{slugName}")
public String tags(Model model, public String tags(Model model,
@ -79,21 +80,22 @@ public class ContentTagController {
* @param model model * @param model model
* @param slugName slug name * @param slugName slug name
* @param page current page * @param page current page
* @return template path: themes/{theme}/tag * @return template path: themes/{theme}/tag.ftl
*/ */
@GetMapping(value = "{slugName}/page/{page}") @GetMapping(value = "{slugName}/page/{page}")
public String tags(Model model, public String tags(Model model,
@PathVariable("slugName") String slugName, @PathVariable("slugName") String slugName,
@PathVariable("page") Integer page, @PathVariable("page") Integer page,
@SortDefault(sort = "createTime", direction = DESC) Sort sort) { @SortDefault(sort = "createTime", direction = DESC) Sort sort) {
Tag tag = tagService.getBySlugNameOfNonNull(slugName); // Get tag by slug name
final Tag tag = tagService.getBySlugNameOfNonNull(slugName);
final Pageable pageable = PageRequest.of(page - 1, optionService.getPostPageSize(), sort); final Pageable pageable = PageRequest.of(page - 1, optionService.getPostPageSize(), sort);
Page<Post> postPage = postTagService.pagePostsBy(tag.getId(), pageable); Page<Post> postPage = postTagService.pagePostsBy(tag.getId(), pageable);
Page<PostListVO> posts = postService.convertToListVo(postPage); Page<PostListVO> posts = postService.convertToListVo(postPage);
final int[] rainbow = PageUtil.rainbow(page, posts.getTotalPages(), 3); final int[] rainbow = PageUtil.rainbow(page, posts.getTotalPages(), 3);
model.addAttribute("is_tags", true); model.addAttribute("is_tag", true);
model.addAttribute("posts", posts); model.addAttribute("posts", posts);
model.addAttribute("rainbow", rainbow); model.addAttribute("rainbow", rainbow);
model.addAttribute("tag", tag); model.addAttribute("tag", tag);

View File

@ -0,0 +1,66 @@
package run.halo.app.controller.content.api;
import io.swagger.annotations.ApiOperation;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.data.web.SortDefault;
import org.springframework.web.bind.annotation.*;
import run.halo.app.model.dto.CategoryDTO;
import run.halo.app.model.dto.post.BasePostSimpleDTO;
import run.halo.app.model.entity.Category;
import run.halo.app.model.entity.Post;
import run.halo.app.service.CategoryService;
import run.halo.app.service.PostCategoryService;
import run.halo.app.service.PostService;
import java.util.List;
import static org.springframework.data.domain.Sort.Direction.DESC;
/**
* Category portal controller.
*
* @author ryanwang
* @date 6/9/19
*/
@RestController("ApiContentCategoryController")
@RequestMapping("/api/content/categories")
public class CategoryController {
private final CategoryService categoryService;
private final PostCategoryService postCategoryService;
private final PostService postService;
public CategoryController(CategoryService categoryService,
PostCategoryService postCategoryService,
PostService postService) {
this.categoryService = categoryService;
this.postCategoryService = postCategoryService;
this.postService = postService;
}
@GetMapping
@ApiOperation("Lists categories")
public List<? extends CategoryDTO> listCategories(@SortDefault(sort = "updateTime", direction = DESC) Sort sort,
@RequestParam(name = "more", required = false, defaultValue = "false") Boolean more) {
if (more) {
return postCategoryService.listCategoryWithPostCountDto(sort);
}
return categoryService.convertTo(categoryService.listAll(sort));
}
@GetMapping("{slugName}/posts")
@ApiOperation("Lists posts by category slug name")
public Page<BasePostSimpleDTO> listPostsBy(@PathVariable("slugName") String slugName,
@PageableDefault(sort = "updateTime", direction = DESC) Pageable pageable) {
// Get category by slug name
Category category = categoryService.getBySlugName(slugName);
Page<Post> postPage = postCategoryService.pagePostBy(category.getId(), pageable);
return postService.convertToSimple(postPage);
}
}

View File

@ -1,18 +1,24 @@
package run.halo.app.controller.content.api; package run.halo.app.controller.content.api;
import io.swagger.annotations.ApiOperation;
import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort;
import org.springframework.data.web.SortDefault;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import run.halo.app.model.dto.LinkDTO;
import run.halo.app.model.vo.LinkTeamVO; import run.halo.app.model.vo.LinkTeamVO;
import run.halo.app.service.LinkService; import run.halo.app.service.LinkService;
import java.util.List; import java.util.List;
import static org.springframework.data.domain.Sort.Direction.DESC;
/** /**
* Portal link controller. * Portal link controller.
* *
* @author johnniang * @author johnniang
* @author ryanwang
* @date 4/3/19 * @date 4/3/19
*/ */
@RestController("ApiContentLinkController") @RestController("ApiContentLinkController")
@ -25,7 +31,14 @@ public class LinkController {
this.linkService = linkService; this.linkService = linkService;
} }
@GetMapping
@ApiOperation("List all links")
public List<LinkDTO> listLinks(@SortDefault(sort = "createTime", direction = DESC) Sort sort) {
return linkService.listDtos(sort);
}
@GetMapping("team_view") @GetMapping("team_view")
@ApiOperation("List all links with team view")
public List<LinkTeamVO> listTeamVos(Sort sort) { public List<LinkTeamVO> listTeamVos(Sort sort) {
return linkService.listTeamVos(sort); return linkService.listTeamVos(sort);
} }

View File

@ -7,6 +7,7 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import run.halo.app.model.dto.MenuDTO; import run.halo.app.model.dto.MenuDTO;
import run.halo.app.model.vo.MenuVO;
import run.halo.app.service.MenuService; import run.halo.app.service.MenuService;
import java.util.List; import java.util.List;
@ -17,6 +18,7 @@ import static org.springframework.data.domain.Sort.Direction.DESC;
* Portal menu controller. * Portal menu controller.
* *
* @author johnniang * @author johnniang
* @author ryanwang
* @date 4/3/19 * @date 4/3/19
*/ */
@RestController("ApiContentMenuController") @RestController("ApiContentMenuController")
@ -34,4 +36,10 @@ public class MenuController {
public List<MenuDTO> listAll(@SortDefault(sort = "priority", direction = DESC) Sort sort) { public List<MenuDTO> listAll(@SortDefault(sort = "priority", direction = DESC) Sort sort) {
return menuService.listDtos(sort); return menuService.listDtos(sort);
} }
@GetMapping(value = "tree_view")
@ApiOperation("Lists menus with tree view")
public List<MenuVO> listMenusTree(@SortDefault(sort = "createTime", direction = DESC) Sort sort) {
return menuService.listAsTree(sort);
}
} }

View File

@ -59,6 +59,14 @@ public class PostController {
return postService.convertToSimple(postPage); return postService.convertToSimple(postPage);
} }
@PostMapping(value = "search")
@ApiOperation("Lists posts by keyword")
public Page<BasePostSimpleDTO> pageBy(@RequestParam(value = "keyword") String keyword,
@PageableDefault(sort = "createTime", direction = DESC) Pageable pageable) {
Page<Post> postPage = postService.pageBy(keyword, pageable);
return postService.convertToSimple(postPage);
}
@GetMapping("{postId:\\d+}") @GetMapping("{postId:\\d+}")
@ApiOperation("Gets a post") @ApiOperation("Gets a post")
public BasePostDetailDTO getBy(@PathVariable("postId") Integer postId, public BasePostDetailDTO getBy(@PathVariable("postId") Integer postId,

View File

@ -53,11 +53,11 @@ 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 unknown-reason problem // TODO May cause unknown-reason problem
// if Ftl was not found then redirect to /404 // if Ftl was not found then redirect to /404
return "redirect:/404"; return contentNotFround();
} }
} }
return statusCode == 500 ? "redirect:/500" : "redirect:/404"; return statusCode == 500 ? contentInternalError() : contentNotFround();
} }
/** /**

View File

@ -12,6 +12,7 @@ import run.halo.app.event.options.OptionUpdatedEvent;
import run.halo.app.event.theme.ThemeActivatedEvent; import run.halo.app.event.theme.ThemeActivatedEvent;
import run.halo.app.event.user.UserUpdatedEvent; import run.halo.app.event.user.UserUpdatedEvent;
import run.halo.app.handler.theme.config.support.ThemeProperty; import run.halo.app.handler.theme.config.support.ThemeProperty;
import run.halo.app.model.support.HaloConst;
import run.halo.app.service.OptionService; import run.halo.app.service.OptionService;
import run.halo.app.service.ThemeService; import run.halo.app.service.ThemeService;
import run.halo.app.service.ThemeSettingService; import run.halo.app.service.ThemeSettingService;
@ -90,6 +91,7 @@ public class FreemarkerConfigAwareListener {
private void loadOptionsConfig() throws TemplateModelException { private void loadOptionsConfig() throws TemplateModelException {
configuration.setSharedVariable("options", optionService.listOptions()); configuration.setSharedVariable("options", optionService.listOptions());
configuration.setSharedVariable("context", optionService.getBlogBaseUrl()); configuration.setSharedVariable("context", optionService.getBlogBaseUrl());
configuration.setSharedVariable("version", HaloConst.HALO_VERSION);
log.debug("Loaded options"); log.debug("Loaded options");
} }

View File

@ -1,6 +1,7 @@
package run.halo.app.model.dto; package run.halo.app.model.dto;
import lombok.Data; import lombok.Data;
import run.halo.app.model.enums.Mode;
/** /**
* Theme controller. * Theme controller.
@ -16,4 +17,6 @@ public class EnvironmentDTO {
private long startTime; private long startTime;
private String version; private String version;
private Mode mode;
} }

View File

@ -0,0 +1,47 @@
package run.halo.app.model.enums;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import org.apache.commons.lang3.StringUtils;
import org.springframework.lang.Nullable;
/**
* Halo runtime mode.
*
* @author johnniang
* @date 19-6-10
*/
public enum Mode {
PRODUCTION,
DEVELOPMENT,
TEST;
@JsonValue
String getValue() {
return this.name().toLowerCase();
}
/**
* Get mode from value.
*
* @param value mode value
* @return runtime mode or null if the value is mismatch
*/
@Nullable
@JsonCreator
public static Mode valueFrom(@Nullable String value) {
if (StringUtils.isBlank(value) || value.equalsIgnoreCase("prod")) {
return Mode.PRODUCTION;
}
if (value.equalsIgnoreCase("dev")) {
return Mode.DEVELOPMENT;
}
if (value.equalsIgnoreCase("test")) {
return Mode.TEST;
}
return null;
}
}

View File

@ -34,7 +34,7 @@ public class CommentTagDirective implements TemplateDirectiveModel {
switch (method) { switch (method) {
case "latest": case "latest":
int top = Integer.parseInt(params.get("top").toString()); int top = Integer.parseInt(params.get("top").toString());
env.setVariable("categories", builder.build().wrap(postCommentService.pageLatest(top))); env.setVariable("comments", builder.build().wrap(postCommentService.pageLatest(top)));
break; break;
case "count": case "count":
env.setVariable("count", builder.build().wrap(postCommentService.count())); env.setVariable("count", builder.build().wrap(postCommentService.count()));

View File

@ -39,7 +39,7 @@ public class PhotoTagDirective implements TemplateDirectiveModel {
env.setVariable("photos", builder.build().wrap(photoService.listAll())); env.setVariable("photos", builder.build().wrap(photoService.listAll()));
break; break;
case "listTeams": case "listTeams":
env.setVariable("teams", builder.build().wrap(photoService.listDtos(Sort.by(DESC, "createTime")))); env.setVariable("teams", builder.build().wrap(photoService.listTeamVos(Sort.by(DESC, "createTime"))));
break; break;
case "listByTeam": case "listByTeam":
String team = params.get("team").toString(); String team = params.get("team").toString();

View File

@ -2,6 +2,7 @@ package run.halo.app.model.params;
import lombok.Data; import lombok.Data;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotBlank;
/** /**
@ -14,6 +15,7 @@ import javax.validation.constraints.NotBlank;
public class MailParam { public class MailParam {
@NotBlank(message = "收件人不能为空") @NotBlank(message = "收件人不能为空")
@Email(message = "邮箱格式错误")
private String to; private String to;
@NotBlank(message = "主题不能为空") @NotBlank(message = "主题不能为空")

View File

@ -12,7 +12,9 @@ public enum OtherProperties implements PropertyEnum {
API_ACCESS_KEY("api_access_key", String.class, ""), API_ACCESS_KEY("api_access_key", String.class, ""),
STATISTICS_CODE("statistics_code", String.class, ""), CUSTOM_HEAD("blog_custom_head",String.class,""),
STATISTICS_CODE("blog_statistics_code", String.class, ""),
/** /**
* *

View File

@ -23,7 +23,7 @@ public class HaloConst {
public final static String DEFAULT_THEME_ID = "caicai_anatole"; public final static String DEFAULT_THEME_ID = "caicai_anatole";
/** /**
* version constant * Version constant. (Available in production environment)
*/ */
public static final String HALO_VERSION; public static final String HALO_VERSION;

View File

@ -42,6 +42,7 @@ public interface CategoryService extends CrudService<Category, Integer> {
* @param name name * @param name name
* @return Category * @return Category
*/ */
@Nullable
Category getByName(@NonNull String name); Category getByName(@NonNull String name);
/** /**

View File

@ -2,6 +2,7 @@ package run.halo.app.service.impl;
import cn.hutool.core.lang.Validator; import cn.hutool.core.lang.Validator;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisher;
import org.springframework.lang.NonNull; import org.springframework.lang.NonNull;
@ -14,6 +15,7 @@ import run.halo.app.model.dto.EnvironmentDTO;
import run.halo.app.model.dto.StatisticDTO; import run.halo.app.model.dto.StatisticDTO;
import run.halo.app.model.entity.User; import run.halo.app.model.entity.User;
import run.halo.app.model.enums.CommentStatus; import run.halo.app.model.enums.CommentStatus;
import run.halo.app.model.enums.Mode;
import run.halo.app.model.enums.PostStatus; import run.halo.app.model.enums.PostStatus;
import run.halo.app.model.params.LoginParam; import run.halo.app.model.params.LoginParam;
import run.halo.app.model.support.HaloConst; import run.halo.app.model.support.HaloConst;
@ -63,6 +65,8 @@ public class AdminServiceImpl implements AdminService {
private final String driverClassName; private final String driverClassName;
private final String mode;
public AdminServiceImpl(PostService postService, public AdminServiceImpl(PostService postService,
SheetService sheetService, SheetService sheetService,
AttachmentService attachmentService, AttachmentService attachmentService,
@ -74,7 +78,8 @@ public class AdminServiceImpl implements AdminService {
LinkService linkService, LinkService linkService,
StringCacheStore cacheStore, StringCacheStore cacheStore,
ApplicationEventPublisher eventPublisher, ApplicationEventPublisher eventPublisher,
@Value("${spring.datasource.driver-class-name}") String driverClassName) { @Value("${spring.datasource.driver-class-name}") String driverClassName,
@Value("${spring.profiles.active:prod}") String mode) {
this.postService = postService; this.postService = postService;
this.sheetService = sheetService; this.sheetService = sheetService;
this.attachmentService = attachmentService; this.attachmentService = attachmentService;
@ -87,6 +92,7 @@ public class AdminServiceImpl implements AdminService {
this.cacheStore = cacheStore; this.cacheStore = cacheStore;
this.eventPublisher = eventPublisher; this.eventPublisher = eventPublisher;
this.driverClassName = driverClassName; this.driverClassName = driverClassName;
this.mode = mode;
} }
@Override @Override
@ -189,6 +195,8 @@ public class AdminServiceImpl implements AdminService {
environmentDTO.setVersion(HaloConst.HALO_VERSION); environmentDTO.setVersion(HaloConst.HALO_VERSION);
environmentDTO.setMode(Mode.valueFrom(this.mode));
return environmentDTO; return environmentDTO;
} }
@ -203,9 +211,8 @@ public class AdminServiceImpl implements AdminService {
User user = userService.getById(userId); User user = userService.getById(userId);
// Remove all token // Remove all token
cacheStore.getAny(SecurityUtils.buildAccessTokenKey(user), String.class).ifPresent(accessToken -> { cacheStore.getAny(SecurityUtils.buildAccessTokenKey(user), String.class)
cacheStore.delete(SecurityUtils.buildTokenAccessKey(accessToken)); .ifPresent(accessToken -> cacheStore.delete(SecurityUtils.buildTokenAccessKey(accessToken)));
});
cacheStore.delete(SecurityUtils.buildTokenRefreshKey(refreshToken)); cacheStore.delete(SecurityUtils.buildTokenRefreshKey(refreshToken));
cacheStore.delete(SecurityUtils.buildAccessTokenKey(user)); cacheStore.delete(SecurityUtils.buildAccessTokenKey(user));
cacheStore.delete(SecurityUtils.buildRefreshTokenKey(user)); cacheStore.delete(SecurityUtils.buildRefreshTokenKey(user));

View File

@ -8,6 +8,7 @@ import run.halo.app.service.BackupService;
import run.halo.app.service.PostService; import run.halo.app.service.PostService;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets;
/** /**
* Backup service implementation. * Backup service implementation.
@ -28,7 +29,7 @@ public class BackupServiceImpl implements BackupService {
public BasePostDetailDTO importMarkdowns(MultipartFile file) throws IOException { public BasePostDetailDTO importMarkdowns(MultipartFile file) throws IOException {
// Read markdown content. // Read markdown content.
String markdown = IoUtil.read(file.getInputStream(), "UTF-8"); String markdown = IoUtil.read(file.getInputStream(), StandardCharsets.UTF_8);
// TODO sheet import // TODO sheet import

View File

@ -29,10 +29,7 @@ import run.halo.app.model.vo.PostDetailVO;
import run.halo.app.model.vo.PostListVO; import run.halo.app.model.vo.PostListVO;
import run.halo.app.repository.PostRepository; import run.halo.app.repository.PostRepository;
import run.halo.app.service.*; import run.halo.app.service.*;
import run.halo.app.utils.DateUtils; import run.halo.app.utils.*;
import run.halo.app.utils.MarkdownUtils;
import run.halo.app.utils.ServiceUtils;
import run.halo.app.utils.SlugUtils;
import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root; import javax.persistence.criteria.Root;
@ -326,20 +323,24 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
if (null == tag) { if (null == tag) {
tag = new Tag(); tag = new Tag();
tag.setName(ele); tag.setName(ele);
tag.setSlugName(SlugUtils.slugify(ele)); String slugName = SlugUtils.slugify(ele);
tag.setSlugName(HaloUtils.initializeUrlIfBlank(slugName));
tag = tagService.create(tag); tag = tagService.create(tag);
} }
tagIds.add(tag.getId()); tagIds.add(tag.getId());
break;
case "categories": case "categories":
Category category = categoryService.getByName(ele); Category category = categoryService.getByName(ele);
if (null == category) { if (null == category) {
category = new Category(); category = new Category();
category.setName(ele); category.setName(ele);
category.setSlugName(SlugUtils.slugify(ele)); String slugName = SlugUtils.slugify(ele);
category.setSlugName(HaloUtils.initializeUrlIfBlank(slugName));
category.setDescription(ele); category.setDescription(ele);
category = categoryService.create(category); category = categoryService.create(category);
} }
categoryIds.add(category.getId()); categoryIds.add(category.getId());
break;
default: default:
break; break;
} }

View File

@ -124,7 +124,7 @@ public class UserServiceImpl extends AbstractCrudService<User, Integer> implemen
User updatedUser = update(user); User updatedUser = update(user);
// Log it // Log it
eventPublisher.publishEvent(new LogEvent(this, updatedUser.getId().toString(), LogType.PASSWORD_UPDATED, oldPassword)); eventPublisher.publishEvent(new LogEvent(this, updatedUser.getId().toString(), LogType.PASSWORD_UPDATED, HaloUtils.desensitize(oldPassword, 2, 1)));
return updatedUser; return updatedUser;
} }

View File

@ -21,6 +21,44 @@ import static run.halo.app.model.support.HaloConst.FILE_SEPARATOR;
@Slf4j @Slf4j
public class HaloUtils { public class HaloUtils {
/**
* Desensitizes the plain text.
*
* @param plainText plain text must not be null
* @param leftSize left size
* @param rightSize right size
* @return desensitization
*/
public static String desensitize(@NonNull String plainText, int leftSize, int rightSize) {
Assert.hasText(plainText, "Plain text must not be blank");
if (leftSize < 0) {
leftSize = 0;
}
if (leftSize > plainText.length()) {
leftSize = plainText.length();
}
if (rightSize < 0) {
rightSize = 0;
}
if (rightSize > plainText.length()) {
rightSize = plainText.length();
}
if (plainText.length() < leftSize + rightSize) {
rightSize = plainText.length() - leftSize;
}
int remainSize = plainText.length() - rightSize - leftSize;
String left = StringUtils.left(plainText, leftSize);
String right = StringUtils.right(plainText, rightSize);
return StringUtils.rightPad(left, remainSize + leftSize, '*') + right;
}
/** /**
* Changes file separator to url separator. * Changes file separator to url separator.
* *

View File

@ -10,9 +10,15 @@
${options.blog_footer_info!} ${options.blog_footer_info!}
</#macro> </#macro>
<#macro custom_head>
${options.blog_custom_head!}
</#macro>
<#-- favicon --> <#-- favicon -->
<#macro favicon> <#macro favicon>
<link rel="shortcut icon" type="images/x-icon" href="${options.blog_favicon!}"> <#if options.blog_favicon?? && options.blog_favicon!=''>
<link rel="shortcut icon" type="images/x-icon" href="${options.blog_favicon!}">
</#if>
</#macro> </#macro>
<#-- 站点验证代码 --> <#-- 站点验证代码 -->
@ -46,11 +52,13 @@
</#macro> </#macro>
<#macro globalHeader> <#macro globalHeader>
<@favicon /> <meta name="generator" content="Halo ${version!}" />
<@custom_head />
<@verification /> <@verification />
<@favicon />
</#macro> </#macro>
<#macro globalFooter> <#macro globalFooter>
<@statistics />
<@footer_info /> <@footer_info />
<@statistics />
</#macro> </#macro>

View File

@ -1,7 +1,7 @@
<#macro comment post,type> <#macro comment post,type>
<#if !post.disallowComment!false> <#if !post.disallowComment!false>
<script src="//cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.min.js"></script> <script src="//cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.min.js"></script>
<script src="//cdn.jsdelivr.net/gh/halo-dev/halo-comment@1.0.0/dist/halo-comment.min.js"></script> <script src="//cdn.jsdelivr.net/gh/halo-dev/halo-comment@1.0.2/dist/halo-comment.min.js"></script>
<halo-comment id="${post.id}" type="${type}"/> <halo-comment id="${post.id}" type="${type}"/>
</#if> </#if>
</#macro> </#macro>

View File

@ -1,4 +1,4 @@
<#include "/common/macro/common_macro.ftl"> <#import "/common/macro/common_macro.ftl" as common>
<#macro head title,keywords,description> <#macro head title,keywords,description>
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
@ -14,8 +14,7 @@
<meta name="author" content="${user.nickname!}" /> <meta name="author" content="${user.nickname!}" />
<meta name="keywords" content="${keywords!}"/> <meta name="keywords" content="${keywords!}"/>
<meta name="description" content="${description!}" /> <meta name="description" content="${description!}" />
<@verification /> <@common.globalHeader />
<@favicon />
<link href="${static!}/source/css/font-awesome.min.css" type="text/css" rel="stylesheet"/> <link href="${static!}/source/css/font-awesome.min.css" type="text/css" rel="stylesheet"/>
<link rel="stylesheet" href="${static!}/source/css/blog_basic.min.css?version=88107691fe"> <link rel="stylesheet" href="${static!}/source/css/blog_basic.min.css?version=88107691fe">
<link href="${static!}/source/css/style.min.css" type="text/css" rel="stylesheet" /> <link href="${static!}/source/css/style.min.css" type="text/css" rel="stylesheet" />
@ -77,7 +76,7 @@
xhr.send(); xhr.send();
</#if> </#if>
</script> </script>
<@statistics /> <@common.statistics />
</body> </body>
</html> </html>
</#macro> </#macro>

View File

@ -24,7 +24,7 @@
<a href="https://github.com/halo-dev/halo" target="_blank">Proudly published with Halo&#65281;</a> <a href="https://github.com/halo-dev/halo" target="_blank">Proudly published with Halo&#65281;</a>
</div> </div>
<div class="footer_text"> <div class="footer_text">
<@footer_info></@footer_info> <@common.footer_info />
</div> </div>
</a> </a>
</div> </div>

View File

@ -7,4 +7,4 @@ description: A other Halo theme
logo: https://avatars1.githubusercontent.com/u/1811819?s=460&v=4 logo: https://avatars1.githubusercontent.com/u/1811819?s=460&v=4
website: https://github.com/halo-dev/halo-theme-anatole website: https://github.com/halo-dev/halo-theme-anatole
repo: https://github.com/halo-dev/halo-theme-anatole repo: https://github.com/halo-dev/halo-theme-anatole
version: 1.0 version: 1.0

View File

@ -1,5 +1,6 @@
package run.halo.app.utils; package run.halo.app.utils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomUtils; import org.apache.commons.lang3.RandomUtils;
import org.junit.Test; import org.junit.Test;
@ -14,6 +15,7 @@ import static org.junit.Assert.assertThat;
* @author johnniang * @author johnniang
* @date 3/29/19 * @date 3/29/19
*/ */
@Slf4j
public class HaloUtilsTest { public class HaloUtilsTest {
@Test @Test
@ -92,4 +94,33 @@ public class HaloUtilsTest {
public void pluralizeLabelExceptionTest() { public void pluralizeLabelExceptionTest() {
HaloUtils.pluralize(1, null, null); HaloUtils.pluralize(1, null, null);
} }
@Test
public void desensitizeSuccessTest() {
String plainText = "12345678";
String desensitization = HaloUtils.desensitize(plainText, 1, 1);
assertThat(desensitization, equalTo("1******8"));
desensitization = HaloUtils.desensitize(plainText, 2, 3);
assertThat(desensitization, equalTo("12***678"));
desensitization = HaloUtils.desensitize(plainText, 2, 6);
assertThat(desensitization, equalTo("12345678"));
desensitization = HaloUtils.desensitize(plainText, 2, 7);
assertThat(desensitization, equalTo("12345678"));
desensitization = HaloUtils.desensitize(plainText, 0, 0);
assertThat(desensitization, equalTo("********"));
desensitization = HaloUtils.desensitize(plainText, -1, -1);
assertThat(desensitization, equalTo("********"));
}
@Test(expected = IllegalArgumentException.class)
public void desensitizeFailureTest() {
String plainText = " ";
HaloUtils.desensitize(plainText, 1, 1);
}
} }