mirror of https://github.com/halo-dev/halo
commit
c17448046e
|
@ -38,7 +38,7 @@ wget https://github.com/halo-dev/halo/releases/download/v1.0.1/halo-1.0.1.jar -O
|
|||
### 启动 Halo
|
||||
|
||||
```bash
|
||||
nohup java -jar halo-latest.jar &
|
||||
nohup java -jar halo-latest.jar >/dev/null 2>&1&
|
||||
```
|
||||
|
||||
详细文档请移步:<https://halo.run/docs>
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
plugins {
|
||||
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 'war'
|
||||
}
|
||||
|
||||
apply plugin: 'io.spring.dependency-management'
|
||||
|
|
|
@ -2,6 +2,8 @@ package run.halo.app;
|
|||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
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.EnableJpaRepositories;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
|
@ -21,7 +23,7 @@ import run.halo.app.repository.base.BaseRepositoryImpl;
|
|||
@EnableScheduling
|
||||
@EnableAsync
|
||||
@EnableJpaRepositories(basePackages = "run.halo.app.repository", repositoryBaseClass = BaseRepositoryImpl.class)
|
||||
public class Application {
|
||||
public class Application extends SpringBootServletInitializer {
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Customize the spring config location
|
||||
|
@ -30,4 +32,10 @@ public class Application {
|
|||
// Run application
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import run.halo.app.security.resolver.AuthenticationArgumentResolver;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Mvc configuration.
|
||||
|
@ -120,6 +121,11 @@ public class WebMvcAutoConfiguration implements WebMvcConfigurer {
|
|||
configurer.setTemplateLoaderPaths(FILE_PROTOCOL + haloProperties.getWorkDir() + "templates/", "classpath:/templates/");
|
||||
configurer.setDefaultEncoding("UTF-8");
|
||||
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty("auto_import","/common/macro/common_macro.ftl as common");
|
||||
|
||||
configurer.setFreemarkerSettings(properties);
|
||||
|
||||
// Predefine configuration
|
||||
freemarker.template.Configuration configuration = configurer.createConfiguration();
|
||||
if (haloProperties.isProductionEnv()) {
|
||||
|
|
|
@ -27,7 +27,7 @@ import java.util.Map;
|
|||
*
|
||||
* @author johnniang
|
||||
*/
|
||||
@RestControllerAdvice
|
||||
@RestControllerAdvice({"run.halo.app.controller.admin.api", "run.halo.app.controller.content.api"})
|
||||
@Slf4j
|
||||
public class ControllerExceptionHandler {
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ public class ContentArchiveController {
|
|||
* Render post archives page.
|
||||
*
|
||||
* @param model model
|
||||
* @return template path : theme/{theme}/archives.ftl
|
||||
* @return template path : themes/{theme}/archives.ftl
|
||||
*/
|
||||
@GetMapping
|
||||
public String archives(Model model) {
|
||||
|
@ -77,7 +77,7 @@ public class ContentArchiveController {
|
|||
* Render post archives page.
|
||||
*
|
||||
* @param model model
|
||||
* @return template path : theme/{theme}/archives.ftl
|
||||
* @return template path : themes/{theme}/archives.ftl
|
||||
*/
|
||||
@GetMapping(value = "page/{page}")
|
||||
public String archives(Model model,
|
||||
|
@ -102,7 +102,7 @@ public class ContentArchiveController {
|
|||
* @param url post slug url.
|
||||
* @param cp comment page number
|
||||
* @param model model
|
||||
* @return template path: theme/{theme}/post.ftl
|
||||
* @return template path: themes/{theme}/post.ftl
|
||||
*/
|
||||
@GetMapping("{url}")
|
||||
public String post(@PathVariable("url") String url,
|
||||
|
|
|
@ -13,10 +13,8 @@ import org.springframework.web.bind.annotation.PathVariable;
|
|||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
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.OptionService;
|
||||
import run.halo.app.service.PostCategoryService;
|
||||
import run.halo.app.service.ThemeService;
|
||||
import run.halo.app.model.vo.PostListVO;
|
||||
import run.halo.app.service.*;
|
||||
|
||||
import static org.springframework.data.domain.Sort.Direction.DESC;
|
||||
|
||||
|
@ -34,25 +32,29 @@ public class ContentCategoryController {
|
|||
|
||||
private final PostCategoryService postCategoryService;
|
||||
|
||||
private final PostService postService;
|
||||
|
||||
private final OptionService optionService;
|
||||
|
||||
public ContentCategoryController(CategoryService categoryService,
|
||||
ThemeService themeService,
|
||||
PostCategoryService postCategoryService,
|
||||
OptionService optionService) {
|
||||
PostService postService, OptionService optionService) {
|
||||
this.categoryService = categoryService;
|
||||
this.themeService = themeService;
|
||||
this.postCategoryService = postCategoryService;
|
||||
this.postService = postService;
|
||||
this.optionService = optionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render category list page
|
||||
*
|
||||
* @return template path: theme/{theme}/categories.ftl
|
||||
* @return template path: themes/{theme}/categories.ftl
|
||||
*/
|
||||
@GetMapping
|
||||
public String categories() {
|
||||
public String categories(Model model) {
|
||||
model.addAttribute("is_categories", true);
|
||||
return themeService.render("categories");
|
||||
}
|
||||
|
||||
|
@ -61,7 +63,7 @@ public class ContentCategoryController {
|
|||
*
|
||||
* @param model model
|
||||
* @param slugName slugName
|
||||
* @return template path: theme/{theme}/category.ftl
|
||||
* @return template path: themes/{theme}/category.ftl
|
||||
*/
|
||||
@GetMapping(value = "{slugName}")
|
||||
public String categories(Model model,
|
||||
|
@ -75,7 +77,7 @@ public class ContentCategoryController {
|
|||
* @param model model
|
||||
* @param slugName slugName
|
||||
* @param page current page number
|
||||
* @return template path: theme/{theme}/category.ftl
|
||||
* @return template path: themes/{theme}/category.ftl
|
||||
*/
|
||||
@GetMapping("{slugName}/page/{page}")
|
||||
public String categories(Model model,
|
||||
|
@ -86,10 +88,11 @@ public class ContentCategoryController {
|
|||
final Category category = categoryService.getBySlugName(slugName);
|
||||
|
||||
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);
|
||||
|
||||
model.addAttribute("is_categories", true);
|
||||
model.addAttribute("is_category", true);
|
||||
model.addAttribute("posts", posts);
|
||||
model.addAttribute("rainbow", rainbow);
|
||||
model.addAttribute("category", category);
|
||||
|
|
|
@ -51,7 +51,7 @@ public class ContentIndexController {
|
|||
* Render blog index
|
||||
*
|
||||
* @param model model
|
||||
* @return template path: /{theme}/post.ftl
|
||||
* @return template path: themes/{theme}/index.ftl
|
||||
*/
|
||||
@GetMapping
|
||||
public String index(Model model) {
|
||||
|
@ -63,7 +63,7 @@ public class ContentIndexController {
|
|||
*
|
||||
* @param model model
|
||||
* @param page current page number
|
||||
* @return template path: /{theme}/post.ftl
|
||||
* @return template path: themes/{theme}/index.ftl
|
||||
*/
|
||||
@GetMapping(value = "page/{page}")
|
||||
public String index(Model model,
|
||||
|
|
|
@ -53,7 +53,7 @@ public class ContentJournalController {
|
|||
* Render journal page.
|
||||
*
|
||||
* @param model model
|
||||
* @return template path: theme/{theme}/journal.ftl
|
||||
* @return template path: themes/{theme}/journals.ftl
|
||||
*/
|
||||
@GetMapping
|
||||
public String journals(Model model) {
|
||||
|
@ -66,7 +66,7 @@ public class ContentJournalController {
|
|||
*
|
||||
* @param model model
|
||||
* @param page current page number
|
||||
* @return template path: theme/{theme}/journal.ftl
|
||||
* @return template path: themes/{theme}/journals.ftl
|
||||
*/
|
||||
@GetMapping(value = "page/{page}")
|
||||
public String journals(Model model,
|
||||
|
|
|
@ -48,7 +48,7 @@ public class ContentSearchController {
|
|||
*
|
||||
* @param model model
|
||||
* @param keyword keyword
|
||||
* @return template path : themes/{theme}/search
|
||||
* @return template path : themes/{theme}/search.ftl
|
||||
*/
|
||||
@GetMapping
|
||||
public String search(Model model,
|
||||
|
@ -61,7 +61,7 @@ public class ContentSearchController {
|
|||
*
|
||||
* @param model model
|
||||
* @param keyword keyword
|
||||
* @return template path :themes/{theme}/search
|
||||
* @return template path :themes/{theme}/search.ftl
|
||||
*/
|
||||
@GetMapping(value = "page/{page}")
|
||||
public String search(Model model,
|
||||
|
@ -69,14 +69,14 @@ public class ContentSearchController {
|
|||
@PathVariable(value = "page") Integer page,
|
||||
@SortDefault(sort = "createTime", direction = DESC) Sort 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("keyword", keyword);
|
||||
model.addAttribute("posts", postPage);
|
||||
model.addAttribute("posts", posts);
|
||||
model.addAttribute("rainbow", rainbow);
|
||||
return themeService.render("search");
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import org.springframework.stereotype.Controller;
|
|||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
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.enums.PostStatus;
|
||||
import run.halo.app.model.support.HaloConst;
|
||||
|
@ -34,7 +33,7 @@ public class ContentSheetController {
|
|||
/**
|
||||
* Render photo page
|
||||
*
|
||||
* @return template path: themes/{theme}/gallery.ftl
|
||||
* @return template path: themes/{theme}/photos.ftl
|
||||
*/
|
||||
@GetMapping(value = "/photos")
|
||||
public String photos() {
|
||||
|
@ -60,7 +59,6 @@ public class ContentSheetController {
|
|||
*/
|
||||
@GetMapping(value = "/s/{url}")
|
||||
public String sheet(@PathVariable(value = "url") String url,
|
||||
@RequestParam(value = "cp", defaultValue = "1") Integer cp,
|
||||
Model model) {
|
||||
Sheet sheet = sheetService.getBy(PostStatus.PUBLISHED, url);
|
||||
|
||||
|
|
|
@ -53,10 +53,11 @@ public class ContentTagController {
|
|||
/**
|
||||
* All of tags
|
||||
*
|
||||
* @return template path: themes/{theme}/tags
|
||||
* @return template path: themes/{theme}/tags.ftl
|
||||
*/
|
||||
@GetMapping
|
||||
public String tags() {
|
||||
public String tags(Model model) {
|
||||
model.addAttribute("is_tags", true);
|
||||
return themeService.render("tags");
|
||||
}
|
||||
|
||||
|
@ -65,7 +66,7 @@ public class ContentTagController {
|
|||
*
|
||||
* @param model model
|
||||
* @param slugName slug name
|
||||
* @return template path: themes/{theme}/tag
|
||||
* @return template path: themes/{theme}/tag.ftl
|
||||
*/
|
||||
@GetMapping(value = "{slugName}")
|
||||
public String tags(Model model,
|
||||
|
@ -79,21 +80,22 @@ public class ContentTagController {
|
|||
* @param model model
|
||||
* @param slugName slug name
|
||||
* @param page current page
|
||||
* @return template path: themes/{theme}/tag
|
||||
* @return template path: themes/{theme}/tag.ftl
|
||||
*/
|
||||
@GetMapping(value = "{slugName}/page/{page}")
|
||||
public String tags(Model model,
|
||||
@PathVariable("slugName") String slugName,
|
||||
@PathVariable("page") Integer page,
|
||||
@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);
|
||||
Page<Post> postPage = postTagService.pagePostsBy(tag.getId(), pageable);
|
||||
Page<PostListVO> posts = postService.convertToListVo(postPage);
|
||||
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("rainbow", rainbow);
|
||||
model.addAttribute("tag", tag);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -1,18 +1,24 @@
|
|||
package run.halo.app.controller.content.api;
|
||||
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
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.RequestMapping;
|
||||
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.service.LinkService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.springframework.data.domain.Sort.Direction.DESC;
|
||||
|
||||
/**
|
||||
* Portal link controller.
|
||||
*
|
||||
* @author johnniang
|
||||
* @author ryanwang
|
||||
* @date 4/3/19
|
||||
*/
|
||||
@RestController("ApiContentLinkController")
|
||||
|
@ -25,7 +31,14 @@ public class LinkController {
|
|||
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")
|
||||
@ApiOperation("List all links with team view")
|
||||
public List<LinkTeamVO> listTeamVos(Sort sort) {
|
||||
return linkService.listTeamVos(sort);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import org.springframework.web.bind.annotation.GetMapping;
|
|||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import run.halo.app.model.dto.MenuDTO;
|
||||
import run.halo.app.model.vo.MenuVO;
|
||||
import run.halo.app.service.MenuService;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -17,6 +18,7 @@ import static org.springframework.data.domain.Sort.Direction.DESC;
|
|||
* Portal menu controller.
|
||||
*
|
||||
* @author johnniang
|
||||
* @author ryanwang
|
||||
* @date 4/3/19
|
||||
*/
|
||||
@RestController("ApiContentMenuController")
|
||||
|
@ -34,4 +36,10 @@ public class MenuController {
|
|||
public List<MenuDTO> listAll(@SortDefault(sort = "priority", direction = DESC) Sort 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,6 +59,14 @@ public class PostController {
|
|||
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+}")
|
||||
@ApiOperation("Gets a post")
|
||||
public BasePostDetailDTO getBy(@PathVariable("postId") Integer postId,
|
||||
|
|
|
@ -53,11 +53,11 @@ public class CommonController implements ErrorController {
|
|||
if (StringUtils.startsWithIgnoreCase(throwable.getMessage(), "Could not resolve view with name '")) {
|
||||
// TODO May cause unknown-reason problem
|
||||
// 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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -12,6 +12,7 @@ import run.halo.app.event.options.OptionUpdatedEvent;
|
|||
import run.halo.app.event.theme.ThemeActivatedEvent;
|
||||
import run.halo.app.event.user.UserUpdatedEvent;
|
||||
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.ThemeService;
|
||||
import run.halo.app.service.ThemeSettingService;
|
||||
|
@ -90,6 +91,7 @@ public class FreemarkerConfigAwareListener {
|
|||
private void loadOptionsConfig() throws TemplateModelException {
|
||||
configuration.setSharedVariable("options", optionService.listOptions());
|
||||
configuration.setSharedVariable("context", optionService.getBlogBaseUrl());
|
||||
configuration.setSharedVariable("version", HaloConst.HALO_VERSION);
|
||||
log.debug("Loaded options");
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package run.halo.app.model.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import run.halo.app.model.enums.Mode;
|
||||
|
||||
/**
|
||||
* Theme controller.
|
||||
|
@ -16,4 +17,6 @@ public class EnvironmentDTO {
|
|||
private long startTime;
|
||||
|
||||
private String version;
|
||||
|
||||
private Mode mode;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -34,7 +34,7 @@ public class CommentTagDirective implements TemplateDirectiveModel {
|
|||
switch (method) {
|
||||
case "latest":
|
||||
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;
|
||||
case "count":
|
||||
env.setVariable("count", builder.build().wrap(postCommentService.count()));
|
||||
|
|
|
@ -39,7 +39,7 @@ public class PhotoTagDirective implements TemplateDirectiveModel {
|
|||
env.setVariable("photos", builder.build().wrap(photoService.listAll()));
|
||||
break;
|
||||
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;
|
||||
case "listByTeam":
|
||||
String team = params.get("team").toString();
|
||||
|
|
|
@ -2,6 +2,7 @@ package run.halo.app.model.params;
|
|||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.Email;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
|
@ -14,6 +15,7 @@ import javax.validation.constraints.NotBlank;
|
|||
public class MailParam {
|
||||
|
||||
@NotBlank(message = "收件人不能为空")
|
||||
@Email(message = "邮箱格式错误")
|
||||
private String to;
|
||||
|
||||
@NotBlank(message = "主题不能为空")
|
||||
|
|
|
@ -12,7 +12,9 @@ public enum OtherProperties implements PropertyEnum {
|
|||
|
||||
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, ""),
|
||||
|
||||
/**
|
||||
* 是否禁止爬虫
|
||||
|
|
|
@ -23,7 +23,7 @@ public class HaloConst {
|
|||
public final static String DEFAULT_THEME_ID = "caicai_anatole";
|
||||
|
||||
/**
|
||||
* version constant
|
||||
* Version constant. (Available in production environment)
|
||||
*/
|
||||
public static final String HALO_VERSION;
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ public interface CategoryService extends CrudService<Category, Integer> {
|
|||
* @param name name
|
||||
* @return Category
|
||||
*/
|
||||
@Nullable
|
||||
Category getByName(@NonNull String name);
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,6 +2,7 @@ package run.halo.app.service.impl;
|
|||
|
||||
import cn.hutool.core.lang.Validator;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
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.entity.User;
|
||||
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.params.LoginParam;
|
||||
import run.halo.app.model.support.HaloConst;
|
||||
|
@ -63,6 +65,8 @@ public class AdminServiceImpl implements AdminService {
|
|||
|
||||
private final String driverClassName;
|
||||
|
||||
private final String mode;
|
||||
|
||||
public AdminServiceImpl(PostService postService,
|
||||
SheetService sheetService,
|
||||
AttachmentService attachmentService,
|
||||
|
@ -74,7 +78,8 @@ public class AdminServiceImpl implements AdminService {
|
|||
LinkService linkService,
|
||||
StringCacheStore cacheStore,
|
||||
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.sheetService = sheetService;
|
||||
this.attachmentService = attachmentService;
|
||||
|
@ -87,6 +92,7 @@ public class AdminServiceImpl implements AdminService {
|
|||
this.cacheStore = cacheStore;
|
||||
this.eventPublisher = eventPublisher;
|
||||
this.driverClassName = driverClassName;
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -189,6 +195,8 @@ public class AdminServiceImpl implements AdminService {
|
|||
|
||||
environmentDTO.setVersion(HaloConst.HALO_VERSION);
|
||||
|
||||
environmentDTO.setMode(Mode.valueFrom(this.mode));
|
||||
|
||||
return environmentDTO;
|
||||
}
|
||||
|
||||
|
@ -203,9 +211,8 @@ public class AdminServiceImpl implements AdminService {
|
|||
User user = userService.getById(userId);
|
||||
|
||||
// Remove all token
|
||||
cacheStore.getAny(SecurityUtils.buildAccessTokenKey(user), String.class).ifPresent(accessToken -> {
|
||||
cacheStore.delete(SecurityUtils.buildTokenAccessKey(accessToken));
|
||||
});
|
||||
cacheStore.getAny(SecurityUtils.buildAccessTokenKey(user), String.class)
|
||||
.ifPresent(accessToken -> cacheStore.delete(SecurityUtils.buildTokenAccessKey(accessToken)));
|
||||
cacheStore.delete(SecurityUtils.buildTokenRefreshKey(refreshToken));
|
||||
cacheStore.delete(SecurityUtils.buildAccessTokenKey(user));
|
||||
cacheStore.delete(SecurityUtils.buildRefreshTokenKey(user));
|
||||
|
|
|
@ -8,6 +8,7 @@ import run.halo.app.service.BackupService;
|
|||
import run.halo.app.service.PostService;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* Backup service implementation.
|
||||
|
@ -28,7 +29,7 @@ public class BackupServiceImpl implements BackupService {
|
|||
public BasePostDetailDTO importMarkdowns(MultipartFile file) throws IOException {
|
||||
|
||||
// Read markdown content.
|
||||
String markdown = IoUtil.read(file.getInputStream(), "UTF-8");
|
||||
String markdown = IoUtil.read(file.getInputStream(), StandardCharsets.UTF_8);
|
||||
|
||||
// TODO sheet import
|
||||
|
||||
|
|
|
@ -29,10 +29,7 @@ import run.halo.app.model.vo.PostDetailVO;
|
|||
import run.halo.app.model.vo.PostListVO;
|
||||
import run.halo.app.repository.PostRepository;
|
||||
import run.halo.app.service.*;
|
||||
import run.halo.app.utils.DateUtils;
|
||||
import run.halo.app.utils.MarkdownUtils;
|
||||
import run.halo.app.utils.ServiceUtils;
|
||||
import run.halo.app.utils.SlugUtils;
|
||||
import run.halo.app.utils.*;
|
||||
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
|
@ -326,20 +323,24 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
|
|||
if (null == tag) {
|
||||
tag = new Tag();
|
||||
tag.setName(ele);
|
||||
tag.setSlugName(SlugUtils.slugify(ele));
|
||||
String slugName = SlugUtils.slugify(ele);
|
||||
tag.setSlugName(HaloUtils.initializeUrlIfBlank(slugName));
|
||||
tag = tagService.create(tag);
|
||||
}
|
||||
tagIds.add(tag.getId());
|
||||
break;
|
||||
case "categories":
|
||||
Category category = categoryService.getByName(ele);
|
||||
if (null == category) {
|
||||
category = new Category();
|
||||
category.setName(ele);
|
||||
category.setSlugName(SlugUtils.slugify(ele));
|
||||
String slugName = SlugUtils.slugify(ele);
|
||||
category.setSlugName(HaloUtils.initializeUrlIfBlank(slugName));
|
||||
category.setDescription(ele);
|
||||
category = categoryService.create(category);
|
||||
}
|
||||
categoryIds.add(category.getId());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -124,7 +124,7 @@ public class UserServiceImpl extends AbstractCrudService<User, Integer> implemen
|
|||
User updatedUser = update(user);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,44 @@ import static run.halo.app.model.support.HaloConst.FILE_SEPARATOR;
|
|||
@Slf4j
|
||||
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.
|
||||
*
|
||||
|
|
|
@ -10,9 +10,15 @@
|
|||
${options.blog_footer_info!}
|
||||
</#macro>
|
||||
|
||||
<#macro custom_head>
|
||||
${options.blog_custom_head!}
|
||||
</#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>
|
||||
|
||||
<#-- 站点验证代码 -->
|
||||
|
@ -46,11 +52,13 @@
|
|||
</#macro>
|
||||
|
||||
<#macro globalHeader>
|
||||
<@favicon />
|
||||
<meta name="generator" content="Halo ${version!}" />
|
||||
<@custom_head />
|
||||
<@verification />
|
||||
<@favicon />
|
||||
</#macro>
|
||||
|
||||
<#macro globalFooter>
|
||||
<@statistics />
|
||||
<@footer_info />
|
||||
<@statistics />
|
||||
</#macro>
|
|
@ -1,7 +1,7 @@
|
|||
<#macro comment post,type>
|
||||
<#if !post.disallowComment!false>
|
||||
<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}"/>
|
||||
</#if>
|
||||
</#macro>
|
|
@ -1,4 +1,4 @@
|
|||
<#include "/common/macro/common_macro.ftl">
|
||||
<#import "/common/macro/common_macro.ftl" as common>
|
||||
<#macro head title,keywords,description>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
@ -14,8 +14,7 @@
|
|||
<meta name="author" content="${user.nickname!}" />
|
||||
<meta name="keywords" content="${keywords!}"/>
|
||||
<meta name="description" content="${description!}" />
|
||||
<@verification />
|
||||
<@favicon />
|
||||
<@common.globalHeader />
|
||||
<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 href="${static!}/source/css/style.min.css" type="text/css" rel="stylesheet" />
|
||||
|
@ -77,7 +76,7 @@
|
|||
xhr.send();
|
||||
</#if>
|
||||
</script>
|
||||
<@statistics />
|
||||
<@common.statistics />
|
||||
</body>
|
||||
</html>
|
||||
</#macro>
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
<a href="https://github.com/halo-dev/halo" target="_blank">Proudly published with Halo!</a>
|
||||
</div>
|
||||
<div class="footer_text">
|
||||
<@footer_info></@footer_info>
|
||||
<@common.footer_info />
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package run.halo.app.utils;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.RandomUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -14,6 +15,7 @@ import static org.junit.Assert.assertThat;
|
|||
* @author johnniang
|
||||
* @date 3/29/19
|
||||
*/
|
||||
@Slf4j
|
||||
public class HaloUtilsTest {
|
||||
|
||||
@Test
|
||||
|
@ -92,4 +94,33 @@ public class HaloUtilsTest {
|
|||
public void pluralizeLabelExceptionTest() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue