21.12.06 리팩토링, 버그 수정
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
package myblog.blog.article.controller;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import myblog.blog.article.domain.Article;
|
||||
import myblog.blog.article.dto.*;
|
||||
import myblog.blog.article.service.ArticleService;
|
||||
@@ -18,15 +17,15 @@ import myblog.blog.tags.service.TagsService;
|
||||
import org.commonmark.parser.Parser;
|
||||
import org.commonmark.renderer.html.HtmlRenderer;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.modelmapper.ModelMapper;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
@@ -36,210 +35,222 @@ import java.util.stream.Collectors;
|
||||
|
||||
@Controller
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class ArticleController {
|
||||
|
||||
private final ModelMapper modelMapper;
|
||||
private final ArticleService articleService;
|
||||
private final TagsService tagsService;
|
||||
private final CategoryService categoryService;
|
||||
private final CommentService commentService;
|
||||
private final Parser parser;
|
||||
private final HtmlRenderer htmlRenderer;
|
||||
private final TempArticleService tempArticleService;
|
||||
|
||||
private final ModelMapper modelMapper;
|
||||
private final Parser parser;
|
||||
private final HtmlRenderer htmlRenderer;
|
||||
|
||||
/*
|
||||
- 아티클 작성 폼 조회
|
||||
*/
|
||||
@GetMapping("article/write")
|
||||
public String writeArticleForm(ArticleForm articleForm, Model model) {
|
||||
public String writeArticleForm(Model model) {
|
||||
|
||||
List<CategoryNormalDto> categoryForInput =
|
||||
categoryService
|
||||
.findCategoryByTier(2)
|
||||
.stream()
|
||||
.map(category -> modelMapper.map(category, CategoryNormalDto.class))
|
||||
.collect(Collectors.toList());
|
||||
model.addAttribute("categoryInput", categoryForInput);
|
||||
|
||||
List<TagsDto> tagsForInput =
|
||||
tagsService
|
||||
.findAllTags()
|
||||
.stream()
|
||||
.map(tag -> new TagsDto(tag.getName()))
|
||||
.collect(Collectors.toList());
|
||||
model.addAttribute("tagsInput", tagsForInput);
|
||||
|
||||
CategoryForView categoryForView = CategoryForView.createCategory(categoryService.getCategoryForView());
|
||||
model.addAttribute("category", categoryForView);
|
||||
|
||||
List<CommentDtoForSide> comments = commentService.recentCommentList()
|
||||
.stream()
|
||||
.map(comment ->
|
||||
new CommentDtoForSide(comment.getId(), comment.getArticle().getId(), comment.getContent(), comment.isSecret() ))
|
||||
.collect(Collectors.toList());
|
||||
model.addAttribute("commentsList", comments);
|
||||
|
||||
model.addAttribute("articleDto", articleForm);
|
||||
modelsForArticleForm(model);
|
||||
modelsForLayout(model);
|
||||
model.addAttribute("articleDto", new ArticleForm());
|
||||
|
||||
return "article/articleWriteForm";
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
- 넘어온 articleForm을 저장
|
||||
- 아티클 작성 post 요청
|
||||
*/
|
||||
@PostMapping("article/write")
|
||||
@Transactional
|
||||
public String writeArticle(ArticleForm articleForm, @AuthenticationPrincipal PrincipalDetails principal) {
|
||||
public String writeArticle(@Validated ArticleForm articleForm,
|
||||
Errors errors,
|
||||
@AuthenticationPrincipal PrincipalDetails principal,
|
||||
Model model) {
|
||||
|
||||
Article article = articleService.writeArticle(articleForm, principal.getMember());
|
||||
if (errors.hasErrors()) {
|
||||
modelsForArticleForm(model);
|
||||
modelsForLayout(model);
|
||||
model.addAttribute("articleDto", new ArticleForm());
|
||||
return "article/articleWriteForm";
|
||||
}
|
||||
|
||||
Long articleId = articleService.writeArticle(articleForm, principal.getMember());
|
||||
// articleService.pushArticleToGithub(article);
|
||||
tempArticleService.deleteTemp();
|
||||
|
||||
return "redirect:/article/view?articleId=" + article.getId();
|
||||
return "redirect:/article/view?articleId=" + articleId;
|
||||
}
|
||||
|
||||
/*
|
||||
- 아티클 수정 폼 조회
|
||||
*/
|
||||
@GetMapping("/article/edit")
|
||||
public String updateArticle(@RequestParam Long articleId,
|
||||
Model model) {
|
||||
|
||||
// 기존 아티클 DTO 전처리
|
||||
Article article = articleService.readArticle(articleId);
|
||||
|
||||
ArticleDtoForEdit articleDto = modelMapper.map(article, ArticleDtoForEdit.class);
|
||||
articleDto.setArticleTagList(article.getArticleTagLists()
|
||||
.stream()
|
||||
.map(articleTag -> articleTag.getTags().getName())
|
||||
.collect(Collectors.toList()));
|
||||
//
|
||||
|
||||
modelsForArticleForm(model);
|
||||
modelsForLayout(model);
|
||||
model.addAttribute("articleDto", articleDto);
|
||||
|
||||
return "article/articleEditForm";
|
||||
}
|
||||
|
||||
/*
|
||||
- 아티클 수정 요청
|
||||
*/
|
||||
@PostMapping("/article/edit")
|
||||
@Transactional
|
||||
public String editArticle(@RequestParam Long articleId,
|
||||
@ModelAttribute ArticleForm articleForm) {
|
||||
|
||||
articleService.editArticle(articleId, articleForm);
|
||||
|
||||
return "redirect:/article/view?articleId=" + articleId;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
- 아티클 삭제 요청
|
||||
*/
|
||||
@PostMapping("/article/delete")
|
||||
@Transactional
|
||||
public String deleteArticle(@RequestParam Long articleId) {
|
||||
|
||||
articleService.deleteArticle(articleId);
|
||||
|
||||
return "redirect:/";
|
||||
}
|
||||
|
||||
/*
|
||||
- 카테고리별 게시물 조회하기
|
||||
*/
|
||||
@Transactional
|
||||
@GetMapping("article/list")
|
||||
public String getArticlesList(@RequestParam String category,
|
||||
@RequestParam Integer tier,
|
||||
@RequestParam Integer page,
|
||||
Model model) {
|
||||
public String getArticlesListByCategory(@RequestParam String category,
|
||||
@RequestParam Integer tier,
|
||||
@RequestParam Integer page,
|
||||
Model model) {
|
||||
|
||||
CategoryForView categoryForView = CategoryForView.createCategory(categoryService.getCategoryForView());
|
||||
model.addAttribute("category", categoryForView);
|
||||
List<CommentDtoForSide> comments = commentService.recentCommentList()
|
||||
.stream()
|
||||
.map(comment ->
|
||||
new CommentDtoForSide(comment.getId(), comment.getArticle().getId(), comment.getContent(), comment.isSecret()))
|
||||
.collect(Collectors.toList());
|
||||
model.addAttribute("commentsList", comments);
|
||||
// DTO 매핑 전처리
|
||||
CategoryForView categoryForView = modelsForLayout(model);
|
||||
|
||||
PagingBoxDto pagingBoxDto = PagingBoxDto
|
||||
.createOf(page, getTotalArticleCntByCategory(category, categoryForView));
|
||||
|
||||
Slice<ArticleDtoForMain> articleList = articleService.getArticlesByCategory(category, tier, pagingBoxDto.getCurPageNum())
|
||||
.map(article ->
|
||||
modelMapper.map(article, ArticleDtoForMain.class));
|
||||
//
|
||||
|
||||
PagingBoxDto pagingBoxDto = PagingBoxDto.createOf(page, articleService.getTotalArticleCntByCategory(category, categoryForView));
|
||||
model.addAttribute("pagingBox", pagingBoxDto);
|
||||
|
||||
Slice<ArticleDtoForMain> articleList = articleService.getArticlesByCategory(category, tier, pagingBoxDto.getCurPageNum());
|
||||
model.addAttribute("articleList", articleList);
|
||||
|
||||
|
||||
return "article/articleList";
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
- 태그별 게시물 조회하기
|
||||
*/
|
||||
@Transactional
|
||||
@GetMapping("article/list/tag/")
|
||||
public String getArticlesListByTag(@RequestParam Integer page,
|
||||
@RequestParam String tagName,
|
||||
Model model) {
|
||||
|
||||
CategoryForView categoryForView = CategoryForView.createCategory(categoryService.getCategoryForView());
|
||||
model.addAttribute("category", categoryForView);
|
||||
List<CommentDtoForSide> comments = commentService.recentCommentList()
|
||||
.stream()
|
||||
.map(comment ->
|
||||
new CommentDtoForSide(comment.getId(), comment.getArticle().getId(), comment.getContent(), comment.isSecret()))
|
||||
.collect(Collectors.toList());
|
||||
model.addAttribute("commentsList", comments);
|
||||
|
||||
// DTO 매핑 전처리
|
||||
|
||||
Page<ArticleDtoForMain> articleList =
|
||||
articleService.getArticlesByTag(tagName, page)
|
||||
.map(article ->
|
||||
modelMapper.map(article, ArticleDtoForMain.class));
|
||||
model.addAttribute("articleList", articleList);
|
||||
|
||||
PagingBoxDto pagingBoxDto = PagingBoxDto.createOf(page, articleList.getTotalPages());
|
||||
|
||||
modelsForLayout(model);
|
||||
//
|
||||
|
||||
model.addAttribute("articleList", articleList);
|
||||
model.addAttribute("pagingBox", pagingBoxDto);
|
||||
|
||||
return "article/articleListByTag";
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
- 검색어별 게시물 조회하기
|
||||
*/
|
||||
@Transactional
|
||||
@GetMapping("article/list/search/")
|
||||
public String getArticlesListByKeyword(@RequestParam Integer page,
|
||||
@RequestParam String keyword,
|
||||
Model model) {
|
||||
|
||||
CategoryForView categoryForView = CategoryForView.createCategory(categoryService.getCategoryForView());
|
||||
model.addAttribute("category", categoryForView);
|
||||
List<CommentDtoForSide> comments = commentService.recentCommentList()
|
||||
.stream()
|
||||
.map(comment ->
|
||||
new CommentDtoForSide(comment.getId(), comment.getArticle().getId(), comment.getContent(),comment.isSecret()))
|
||||
.collect(Collectors.toList());
|
||||
model.addAttribute("commentsList", comments);
|
||||
// DTO 매핑 전처리
|
||||
|
||||
Page<ArticleDtoForMain> articleList =
|
||||
articleService.getArticlesByKeyword(keyword, page)
|
||||
.map(article ->
|
||||
modelMapper.map(article, ArticleDtoForMain.class));
|
||||
model.addAttribute("articleList", articleList);
|
||||
|
||||
PagingBoxDto pagingBoxDto = PagingBoxDto.createOf(page, articleList.getTotalPages());
|
||||
|
||||
modelsForLayout(model);
|
||||
//
|
||||
|
||||
model.addAttribute("articleList", articleList);
|
||||
model.addAttribute("pagingBox", pagingBoxDto);
|
||||
|
||||
return "article/articleListByKeyword";
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
- 아티클 상세 조회
|
||||
1. 로그인여부 검토
|
||||
2. 게시물 상세조회에 필요한 Dto 전처리
|
||||
3. 메타태그 작성위한 Dto 전처리
|
||||
4. Dto 담기
|
||||
5. 조회수 증가 검토
|
||||
*/
|
||||
@GetMapping("/article/view")
|
||||
public String readArticle(@RequestParam Long articleId,
|
||||
Authentication authentication,
|
||||
@CookieValue(required = false, name = "view") String cookie, HttpServletResponse response,
|
||||
@AuthenticationPrincipal PrincipalDetails principal,
|
||||
@CookieValue(required = false, name = "view") String cookie,
|
||||
HttpServletResponse response,
|
||||
Model model) {
|
||||
|
||||
if (authentication != null) {
|
||||
PrincipalDetails principal = (PrincipalDetails) authentication.getPrincipal();
|
||||
MemberDto memberDto = modelMapper.map(principal.getMember(), MemberDto.class);
|
||||
model.addAttribute("member", memberDto);
|
||||
// 1. 로그인 여부에 따라 뷰단에 출력 여부 결정
|
||||
if (principal != null) {
|
||||
model.addAttribute("member", modelMapper.map(principal.getMember(), MemberDto.class));
|
||||
} else {
|
||||
model.addAttribute("member", null);
|
||||
}
|
||||
|
||||
CategoryForView categoryForView = CategoryForView.createCategory(categoryService.getCategoryForView());
|
||||
model.addAttribute("category", categoryForView);
|
||||
List<CommentDtoForSide> comments = commentService.recentCommentList()
|
||||
.stream()
|
||||
.map(comment ->
|
||||
new CommentDtoForSide(comment.getId(), comment.getArticle().getId(), comment.getContent(),comment.isSecret()))
|
||||
.collect(Collectors.toList());
|
||||
model.addAttribute("commentsList", comments);
|
||||
|
||||
/*
|
||||
DTO 매핑 전처리
|
||||
2. 게시물 상세조회용
|
||||
*/
|
||||
Article article = articleService.readArticle(articleId);
|
||||
ArticleDtoForDetail articleDtoForDetail = modelMapper.map(article, ArticleDtoForDetail.class);
|
||||
|
||||
ArticleDtoForDetail articleDtoForDetail =
|
||||
modelMapper.map(article, ArticleDtoForDetail.class);
|
||||
|
||||
List<String> tags = article.getArticleTagLists()
|
||||
.stream()
|
||||
.map(tag -> tag.getTags().getName())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
articleDtoForDetail
|
||||
.setTags(tags);
|
||||
|
||||
articleDtoForDetail
|
||||
.setContent(
|
||||
htmlRenderer.render(parser.parse(article.getContent()))
|
||||
);
|
||||
|
||||
model.addAttribute("article", articleDtoForDetail);
|
||||
|
||||
// 메타태그 삽입
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String tag : tags) {
|
||||
sb.append(tag).append(", ");
|
||||
}
|
||||
model.addAttribute("metaTags",sb);
|
||||
|
||||
String substringContents = null;
|
||||
if(articleDtoForDetail.getContent().length()>200) {
|
||||
substringContents = articleDtoForDetail.getContent().substring(0, 200);
|
||||
}
|
||||
else substringContents = articleDtoForDetail.getContent();
|
||||
|
||||
model.addAttribute("metaContents",Jsoup.parse(substringContents).text());
|
||||
//
|
||||
articleDtoForDetail.setTags(tags);
|
||||
articleDtoForDetail.setContent(htmlRenderer.render(parser.parse(article.getContent())));
|
||||
|
||||
List<ArticleDtoByCategory> articleTitlesSortByCategory =
|
||||
articleService
|
||||
@@ -247,96 +258,80 @@ public class ArticleController {
|
||||
.stream()
|
||||
.map(article1 -> modelMapper.map(article1, ArticleDtoByCategory.class))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 3. 메타 태그용 Dto 전처리
|
||||
StringBuilder metaTags = new StringBuilder();
|
||||
for (String tag : tags) {
|
||||
metaTags.append(tag).append(", ");
|
||||
}
|
||||
|
||||
String substringContents = null;
|
||||
if(articleDtoForDetail.getContent().length()>200) {
|
||||
substringContents = articleDtoForDetail.getContent().substring(0, 200);
|
||||
}
|
||||
else substringContents = articleDtoForDetail.getContent();
|
||||
|
||||
// 4. 모델 담기
|
||||
modelsForLayout(model);
|
||||
model.addAttribute("article", articleDtoForDetail);
|
||||
model.addAttribute("metaTags",metaTags);
|
||||
model.addAttribute("metaContents",Jsoup.parse(substringContents).text());
|
||||
model.addAttribute("articlesSortBycategory", articleTitlesSortByCategory);
|
||||
|
||||
// 5. 조회수 증가 검토
|
||||
addHitWithCookie(article, cookie, response);
|
||||
|
||||
return "article/articleView";
|
||||
}
|
||||
|
||||
@GetMapping("/article/edit")
|
||||
public String updateArticle(@RequestParam Long articleId,
|
||||
Authentication authentication,
|
||||
Model model) {
|
||||
|
||||
List<CategoryNormalDto> categoryForInput =
|
||||
/*
|
||||
- 아티클 폼에 필요한 모델 담기
|
||||
*/
|
||||
private void modelsForArticleForm(Model model) {
|
||||
List<CategoryNormalDto> categoryForForm =
|
||||
categoryService
|
||||
.findCategoryByTier(2)
|
||||
.stream()
|
||||
.map(c -> modelMapper.map(c, CategoryNormalDto.class))
|
||||
.map(category -> modelMapper.map(category, CategoryNormalDto.class))
|
||||
.collect(Collectors.toList());
|
||||
model.addAttribute("categoryInput", categoryForInput);
|
||||
model.addAttribute("categoryInput", categoryForForm);
|
||||
|
||||
List<TagsDto> tagsForInput =
|
||||
List<TagsDto> tagsForForm =
|
||||
tagsService
|
||||
.findAllTags()
|
||||
.stream()
|
||||
.map(c -> modelMapper.map(c, TagsDto.class))
|
||||
.map(tag -> new TagsDto(tag.getName()))
|
||||
.collect(Collectors.toList());
|
||||
model.addAttribute("tagsInput", tagsForInput);
|
||||
|
||||
|
||||
Article article = articleService.getArticleForEdit(articleId);
|
||||
ArticleDtoForEdit articleDto = modelMapper.map(article, ArticleDtoForEdit.class);
|
||||
|
||||
List<String> tagList = article.getArticleTagLists()
|
||||
.stream()
|
||||
.map(articleTag -> articleTag.getTags().getName())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
articleDto.setArticleTagList(tagList);
|
||||
|
||||
model.addAttribute("articleDto", articleDto);
|
||||
|
||||
model.addAttribute("tagsInput", tagsForForm);
|
||||
}
|
||||
/*
|
||||
- 레이아웃에 필요한 모델 담기
|
||||
*/
|
||||
private CategoryForView modelsForLayout(Model model) {
|
||||
CategoryForView categoryForView = CategoryForView.createCategory(categoryService.getCategoryForView());
|
||||
model.addAttribute("category", categoryForView);
|
||||
|
||||
List<CommentDtoForSide> comments = commentService.recentCommentList()
|
||||
.stream()
|
||||
.map(comment ->
|
||||
new CommentDtoForSide(comment.getId(), comment.getArticle().getId(), comment.getContent(),comment.isSecret()))
|
||||
new CommentDtoForSide(comment.getId(), comment.getArticle().getId(), comment.getContent(), comment.isSecret()))
|
||||
.collect(Collectors.toList());
|
||||
model.addAttribute("commentsList", comments);
|
||||
|
||||
return "article/articleEditForm";
|
||||
}
|
||||
|
||||
@PostMapping("/article/delete")
|
||||
@Transactional
|
||||
public String deleteArticle(@RequestParam Long articleId,
|
||||
Authentication authentication) {
|
||||
|
||||
articleService.deleteArticle(articleId);
|
||||
|
||||
return "redirect:/";
|
||||
return categoryForView;
|
||||
|
||||
}
|
||||
|
||||
@PostMapping("/article/edit")
|
||||
@Transactional
|
||||
public String editArticle(@RequestParam Long articleId,
|
||||
@ModelAttribute ArticleForm articleForm, @AuthenticationPrincipal PrincipalDetails principal) {
|
||||
|
||||
articleService.editArticle(articleId, articleForm);
|
||||
|
||||
|
||||
return "redirect:/article/view?articleId=" + articleId;
|
||||
|
||||
}
|
||||
|
||||
@GetMapping("/main/article/{pageNum}")
|
||||
public @ResponseBody
|
||||
List<ArticleDtoForMain> mainNextPage(@PathVariable int pageNum) {
|
||||
|
||||
return articleService.getRecentArticles(pageNum).getContent();
|
||||
}
|
||||
|
||||
/*
|
||||
- 쿠키 추가 검토
|
||||
*/
|
||||
private void addHitWithCookie(Article article, String cookie, HttpServletResponse response) {
|
||||
Long articleId = article.getId();
|
||||
if (cookie == null) {
|
||||
Cookie viewCookie = new Cookie("view", articleId + "/");
|
||||
viewCookie.setComment("게시물 조회 확인용");
|
||||
viewCookie.setMaxAge(60 * 60);
|
||||
articleService.addHit(article);
|
||||
article.addHit();
|
||||
response.addCookie(viewCookie);
|
||||
} else {
|
||||
boolean isRead = false;
|
||||
@@ -346,15 +341,33 @@ public class ArticleController {
|
||||
isRead = true;
|
||||
break;
|
||||
}
|
||||
;
|
||||
}
|
||||
if (!isRead) {
|
||||
cookie += articleId + "/";
|
||||
articleService.addHit(article);
|
||||
article.addHit();
|
||||
}
|
||||
response.addCookie(new Cookie("view", cookie));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
- 카테고리별 아티클 갯수 구하기
|
||||
*/
|
||||
private int getTotalArticleCntByCategory(String category, CategoryForView categorys) {
|
||||
|
||||
if (categorys.getTitle().equals(category)) {
|
||||
return categorys.getCount();
|
||||
} else {
|
||||
for (CategoryForView categoryCnt :
|
||||
categorys.getCategoryTCountList()) {
|
||||
if (categoryCnt.getTitle().equals(category))
|
||||
return categoryCnt.getCount();
|
||||
for (CategoryForView categoryCntSub : categoryCnt.getCategoryTCountList()) {
|
||||
if (categoryCntSub.getTitle().equals(category))
|
||||
return categoryCntSub.getCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("카테고리별 아티클 수 에러");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,21 +8,29 @@ import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/*
|
||||
- 임시 게시물 조회, 저장을 위한 rest 컨트롤러
|
||||
*/
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class TempArticleController {
|
||||
|
||||
private final TempArticleService tempArticleService;
|
||||
|
||||
/*
|
||||
- 임시 아티클 저장 요청
|
||||
*/
|
||||
@PostMapping("/article/temp/autoSave")
|
||||
public String autoSaveTemp(@RequestBody TempArticleDto tempArticleDto){
|
||||
|
||||
tempArticleService.saveTemp(tempArticleDto);
|
||||
|
||||
return "OK";
|
||||
tempArticleService.saveTemp(new TempArticle(tempArticleDto.getContent()));
|
||||
|
||||
return "저장성공";
|
||||
}
|
||||
|
||||
/*
|
||||
- 임시 아티클 조회
|
||||
*/
|
||||
@GetMapping("/article/temp/getTemp")
|
||||
public @ResponseBody TempArticleDto getTempArticle(){
|
||||
|
||||
@@ -32,7 +40,5 @@ public class TempArticleController {
|
||||
tempArticleDto.setContent(tempArticle.orElse(new TempArticle()).getContent());
|
||||
|
||||
return tempArticleDto;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,11 @@ import javax.persistence.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/*
|
||||
- 아티클 Entity
|
||||
- toc 추후 개발 예정
|
||||
*/
|
||||
@Entity
|
||||
@Getter
|
||||
@SequenceGenerator(
|
||||
@@ -37,6 +42,7 @@ public class Article extends BasicEntity {
|
||||
@Column(columnDefinition = "bigint default 0",nullable = false)
|
||||
private Long hit;
|
||||
|
||||
// 추후 개발 예정
|
||||
private String toc;
|
||||
|
||||
@Column(nullable = false)
|
||||
@@ -67,23 +73,42 @@ public class Article extends BasicEntity {
|
||||
this.content = content;
|
||||
this.toc = toc;
|
||||
this.member = member;
|
||||
this.thumbnailUrl = thumbnailUrl;
|
||||
this.thumbnailUrl = makeDefaultThumb(thumbnailUrl);
|
||||
this.category = category;
|
||||
this.hit = 0L;
|
||||
}
|
||||
|
||||
// 비지니스 로직 //
|
||||
|
||||
/*
|
||||
- 아티클 수정을 위한 로직
|
||||
*/
|
||||
public void editArticle(ArticleForm articleForm, Category category){
|
||||
this.content = articleForm.getContent();
|
||||
this.title = articleForm.getTitle();
|
||||
this.toc = articleForm.getToc();
|
||||
this.category = category;
|
||||
|
||||
if(articleForm.getThumbnailUrl() != null){
|
||||
this.thumbnailUrl = articleForm.getThumbnailUrl();
|
||||
}
|
||||
}
|
||||
/*
|
||||
- 아티클 조회수 증가
|
||||
*/
|
||||
public void addHit(){
|
||||
this.hit++;
|
||||
}
|
||||
|
||||
public void editArticle(ArticleForm articleForm, Category category){
|
||||
this.content = articleForm.getContent();
|
||||
this.title = articleForm.getTitle();
|
||||
this.thumbnailUrl = articleForm.getThumbnailUrl();
|
||||
this.toc = articleForm.getToc();
|
||||
this.category = category;
|
||||
}
|
||||
/*
|
||||
- 썸네일 기본 작성
|
||||
*/
|
||||
private String makeDefaultThumb(String thumbnailUrl) {
|
||||
String defaultThumbUrl = "https://cdn.pixabay.com/photo/2020/11/08/13/28/tree-5723734_1280.jpg";
|
||||
|
||||
if (thumbnailUrl == null || thumbnailUrl.equals("")) {
|
||||
thumbnailUrl = defaultThumbUrl;
|
||||
}
|
||||
return thumbnailUrl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,14 @@
|
||||
package myblog.blog.article.domain;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import myblog.blog.article.dto.ArticleForm;
|
||||
import myblog.blog.base.domain.BasicEntity;
|
||||
import myblog.blog.category.domain.Category;
|
||||
import myblog.blog.comment.domain.Comment;
|
||||
import myblog.blog.member.doamin.Member;
|
||||
import myblog.blog.tags.domain.ArticleTagList;
|
||||
import org.hibernate.annotations.OnDelete;
|
||||
import org.hibernate.annotations.OnDeleteAction;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
- 임시 아티클 저장 Entity
|
||||
- 임시 아티클은 한개만 유지할 예정
|
||||
*/
|
||||
@Entity
|
||||
@Getter
|
||||
public class TempArticle extends BasicEntity {
|
||||
|
||||
@@ -3,6 +3,9 @@ package myblog.blog.article.dto;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/*
|
||||
- 카테고리별 게시물 표시용 DTO
|
||||
*/
|
||||
@Getter @Setter
|
||||
public class ArticleDtoByCategory {
|
||||
|
||||
|
||||
@@ -7,6 +7,9 @@ import javax.validation.constraints.NotBlank;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
- 아티클 상세조회용 DTO
|
||||
*/
|
||||
@Getter @Setter
|
||||
public class ArticleDtoForDetail {
|
||||
|
||||
@@ -19,6 +22,4 @@ public class ArticleDtoForDetail {
|
||||
private String category;
|
||||
private List<String> tags;
|
||||
private LocalDateTime createdDate;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -9,6 +9,9 @@ import myblog.blog.tags.dto.TagsDto;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
- 아티클 수정 폼을 위한 DTO
|
||||
*/
|
||||
@Getter @Setter
|
||||
public class ArticleDtoForEdit {
|
||||
|
||||
@@ -20,6 +23,4 @@ public class ArticleDtoForEdit {
|
||||
|
||||
private List<String> articleTagList = new ArrayList<>();
|
||||
private Category category;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,9 @@ import lombok.Setter;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/*
|
||||
- 메인 화면 출력용 아티클 DTO
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class ArticleDtoForMain {
|
||||
|
||||
@@ -11,17 +11,17 @@ import java.util.Objects;
|
||||
@Getter
|
||||
public class ArticleForm {
|
||||
|
||||
@NotBlank
|
||||
@NotBlank(message = "제목을 입력해주세요")
|
||||
private String title;
|
||||
@NotBlank
|
||||
@NotBlank(message = "내용을 입력해주세요")
|
||||
private String content;
|
||||
|
||||
private String toc;
|
||||
|
||||
private String thumbnailUrl;
|
||||
|
||||
@NotBlank
|
||||
@NotBlank(message = "카테고리를 입력해주세요")
|
||||
private String category;
|
||||
@NotBlank
|
||||
@NotBlank(message = "태그를 하나이상 입력해주세요")
|
||||
private String tags;
|
||||
|
||||
}
|
||||
|
||||
@@ -4,7 +4,9 @@ import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.data.domain.Page;
|
||||
|
||||
|
||||
/*
|
||||
- 뷰단 페이징 박스 처리를 위한 핸들러
|
||||
*/
|
||||
@Getter @Setter
|
||||
public class PagingBoxDto {
|
||||
|
||||
@@ -18,6 +20,7 @@ public class PagingBoxDto {
|
||||
private final int displayPageBoxCnt = 5;
|
||||
private final int displayArticlePerPage = 5;
|
||||
|
||||
// 스태틱 생성 메소드
|
||||
public static PagingBoxDto createOf(int page, int totalArticles) {
|
||||
|
||||
PagingBoxDto box = new PagingBoxDto();
|
||||
@@ -39,7 +42,6 @@ public class PagingBoxDto {
|
||||
box.boxStartNum = (box.curPageNum / box.displayPageBoxCnt) * box.displayPageBoxCnt +1;
|
||||
}
|
||||
|
||||
|
||||
// 페이징 박스 끝번호 계산
|
||||
box.boxEndNum = (int) (Math.ceil(box.curPageNum / (double) box.displayPageBoxCnt) * box.displayPageBoxCnt);
|
||||
|
||||
@@ -51,12 +53,9 @@ public class PagingBoxDto {
|
||||
box.boxEndNum = 1;
|
||||
}
|
||||
|
||||
|
||||
// 페이징박스 다음버튼 만드는 마지막 페이지 번호
|
||||
box.pNumForNextBtn =box.boxEndNum - (box.boxEndNum % box.displayPageBoxCnt);
|
||||
|
||||
return box;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,10 +2,11 @@ package myblog.blog.article.dto;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/*
|
||||
- 임시 저장 아티클 조회용 DTO
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class TempArticleDto {
|
||||
|
||||
private String content;
|
||||
}
|
||||
|
||||
@@ -2,30 +2,24 @@ package myblog.blog.article.service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import myblog.blog.article.domain.Article;
|
||||
import myblog.blog.article.dto.ArticleDtoForMain;
|
||||
import myblog.blog.category.domain.Category;
|
||||
import myblog.blog.member.doamin.Member;
|
||||
import myblog.blog.article.dto.ArticleForm;
|
||||
import myblog.blog.article.repository.ArticleRepository;
|
||||
import myblog.blog.article.repository.NaArticleRepository;
|
||||
import myblog.blog.category.domain.Category;
|
||||
import myblog.blog.category.dto.CategoryForView;
|
||||
import myblog.blog.category.service.CategoryService;
|
||||
import myblog.blog.member.doamin.Member;
|
||||
import myblog.blog.member.repository.MemberRepository;
|
||||
import myblog.blog.tags.service.TagsService;
|
||||
import org.kohsuke.github.GHRepository;
|
||||
import org.kohsuke.github.GitHub;
|
||||
import org.kohsuke.github.GitHubBuilder;
|
||||
import org.modelmapper.ModelMapper;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
@@ -41,18 +35,17 @@ public class ArticleService {
|
||||
private final CategoryService categoryService;
|
||||
private final ArticleRepository articleRepository;
|
||||
private final NaArticleRepository naArticleRepository;
|
||||
private final ModelMapper modelMapper;
|
||||
|
||||
/*
|
||||
- 아티클 작성 로직
|
||||
*/
|
||||
public Article writeArticle(ArticleForm articleDto, Member writer) {
|
||||
public Long writeArticle(ArticleForm articleDto, Member writer) {
|
||||
|
||||
Article newArticle = articleFrom(articleDto, writer);
|
||||
articleRepository.save(newArticle);
|
||||
tagsService.createNewTagsAndArticleTagList(articleDto.getTags(), newArticle);
|
||||
|
||||
return newArticle;
|
||||
return newArticle.getId();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -64,7 +57,6 @@ public class ArticleService {
|
||||
Category category = categoryService.findCategory(articleForm.getCategory());
|
||||
tagsService.deleteArticleTags(article);
|
||||
tagsService.createNewTagsAndArticleTagList(articleForm.getTags(), article);
|
||||
articleForm.setThumbnailUrl(makeDefaultThumb(articleForm.getThumbnailUrl()));
|
||||
|
||||
// 더티 체킹으로 업데이트
|
||||
article.editArticle(articleForm,category);
|
||||
@@ -73,46 +65,24 @@ public class ArticleService {
|
||||
/*
|
||||
- 메인화면 위한 인기 아티클 6개 목록 가져오기
|
||||
*/
|
||||
public List<ArticleDtoForMain> getPopularArticles() {
|
||||
List<Article> top6ByOrderByHitDesc = articleRepository.findTop6ByOrderByHitDesc();
|
||||
public List<Article> getPopularArticles() {
|
||||
|
||||
List<ArticleDtoForMain> articles = top6ByOrderByHitDesc.stream()
|
||||
.map(article -> modelMapper.map(article, ArticleDtoForMain.class))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return articles;
|
||||
return articleRepository.findTop6ByOrderByHitDesc();
|
||||
|
||||
}
|
||||
|
||||
public Slice<ArticleDtoForMain> getRecentArticles(int page) {
|
||||
|
||||
Slice<ArticleDtoForMain> articles = articleRepository
|
||||
.findByOrderByIdDesc(PageRequest.of(page, 5))
|
||||
.map(article -> modelMapper
|
||||
.map(article, ArticleDtoForMain.class));
|
||||
return articles;
|
||||
|
||||
/*
|
||||
- 메인화면 위한 최신 아티클 페이징처리해서 가져오기
|
||||
*/
|
||||
public Slice<Article> getRecentArticles(int page) {
|
||||
return articleRepository
|
||||
.findByOrderByIdDesc(PageRequest.of(page, 5));
|
||||
}
|
||||
|
||||
public int getTotalArticleCntByCategory(String category, CategoryForView categorys) {
|
||||
|
||||
if (categorys.getTitle().equals(category)) {
|
||||
return categorys.getCount();
|
||||
} else {
|
||||
for (CategoryForView categoryCnt :
|
||||
categorys.getCategoryTCountList()) {
|
||||
if (categoryCnt.getTitle().equals(category))
|
||||
return categoryCnt.getCount();
|
||||
for (CategoryForView categoryCntSub : categoryCnt.getCategoryTCountList()) {
|
||||
if (categoryCntSub.getTitle().equals(category))
|
||||
return categoryCntSub.getCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("카테고리별 아티클 수 에러");
|
||||
}
|
||||
|
||||
public Slice<ArticleDtoForMain> getArticlesByCategory(String category, Integer tier, Integer page) {
|
||||
/*
|
||||
- 카테고리별 게시물 페이징 처리해서 가져오기
|
||||
*/
|
||||
public Slice<Article> getArticlesByCategory(String category, Integer tier, Integer page) {
|
||||
|
||||
Slice<Article> articles = null;
|
||||
|
||||
@@ -131,57 +101,54 @@ public class ArticleService {
|
||||
PageRequest.of(pageResolve(page), 5), category);
|
||||
}
|
||||
|
||||
return articles.map(article -> modelMapper
|
||||
.map(article, ArticleDtoForMain.class));
|
||||
return articles;
|
||||
}
|
||||
|
||||
/*
|
||||
- 아티클 읽기 위한 페치로 전체 가져오기
|
||||
*/
|
||||
public Article readArticle(Long id){
|
||||
return articleRepository.findArticleByIdFetchCategoryAndTags(id);
|
||||
}
|
||||
|
||||
public void addHit(Article article) {
|
||||
|
||||
article.addHit();
|
||||
|
||||
}
|
||||
|
||||
public Article getArticleForEdit(Long articleId){
|
||||
|
||||
return articleRepository.findArticleByIdFetchCategoryAndTags(articleId);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
- 아티클 삭제 로직
|
||||
*/
|
||||
public void deleteArticle(Long articleId) {
|
||||
|
||||
naArticleRepository.deleteArticle(articleId);
|
||||
}
|
||||
|
||||
/*
|
||||
- 카테고리별 최신게시물 6개만 아티클 상세뷰 위해 가져오는로직
|
||||
*/
|
||||
public List<Article> getArticlesByCategoryForDetailView(Category category){
|
||||
|
||||
return articleRepository.findTop6ByCategoryOrderByIdDesc(category);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
- 태그별 게시물 페이징 처리해서 가져오기
|
||||
*/
|
||||
public Page<Article> getArticlesByTag(String tag, Integer page) {
|
||||
|
||||
Page<Article> articles =
|
||||
articleRepository
|
||||
.findAllByArticleTagsOrderById(PageRequest.of(pageResolve(page), 5), tag);
|
||||
|
||||
return articles;
|
||||
return articleRepository
|
||||
.findAllByArticleTagsOrderById(PageRequest.of(pageResolve(page), 5), tag);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
- 검색어별 게시물 페이징 처리해서 가져오기
|
||||
*/
|
||||
public Page<Article> getArticlesByKeyword(String keyword, Integer page) {
|
||||
|
||||
Page<Article> articles =
|
||||
articleRepository
|
||||
.findAllByKeywordOrderById(PageRequest.of(pageResolve(page),5), keyword);
|
||||
|
||||
return articles;
|
||||
return articleRepository
|
||||
.findAllByKeywordOrderById(PageRequest.of(pageResolve(page),5), keyword);
|
||||
}
|
||||
|
||||
/*
|
||||
- 깃헙에 아티클 푸시하기
|
||||
*/
|
||||
public void pushArticleToGithub(Article article) {
|
||||
try {
|
||||
GitHub gitHub = new GitHubBuilder().withOAuthToken(gitToken).build();
|
||||
@@ -198,22 +165,18 @@ public class ArticleService {
|
||||
|
||||
}
|
||||
|
||||
private String makeDefaultThumb(String thumbnailUrl) {
|
||||
// 메시지로 올리기
|
||||
String defaultThumbUrl = "https://cdn.pixabay.com/photo/2020/11/08/13/28/tree-5723734_1280.jpg";
|
||||
|
||||
if (thumbnailUrl == null || thumbnailUrl.equals("")) {
|
||||
thumbnailUrl = defaultThumbUrl;
|
||||
}
|
||||
return thumbnailUrl;
|
||||
}
|
||||
|
||||
/*
|
||||
- 페이지 시작점 0~1변경 메서드
|
||||
*/
|
||||
private int pageResolve(Integer rawPage) {
|
||||
if (rawPage == null || rawPage == 1) {
|
||||
return 0;
|
||||
} else return rawPage - 1;
|
||||
}
|
||||
|
||||
/*
|
||||
- 새로운 아티클 도메인 생성 메서드
|
||||
*/
|
||||
private Article articleFrom(ArticleForm articleDto, Member writer) {
|
||||
|
||||
return Article.builder()
|
||||
@@ -221,7 +184,7 @@ public class ArticleService {
|
||||
.content(articleDto.getContent())
|
||||
.toc(articleDto.getToc())
|
||||
.member(writer)
|
||||
.thumbnailUrl(makeDefaultThumb(articleDto.getThumbnailUrl()))
|
||||
.thumbnailUrl(articleDto.getThumbnailUrl())
|
||||
.category(categoryService.findCategory(articleDto.getCategory()))
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package myblog.blog.article.service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import myblog.blog.article.domain.TempArticle;
|
||||
import myblog.blog.article.dto.TempArticleDto;
|
||||
import myblog.blog.article.repository.TempArticleRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
import java.util.Optional;
|
||||
@@ -17,18 +16,14 @@ public class TempArticleService {
|
||||
- 자동 저장 로직
|
||||
- ID값 고정으로 머지를 작동시켜 임시글 DB에 1개 유지
|
||||
*/
|
||||
public void saveTemp(TempArticleDto tempArticleDto){
|
||||
|
||||
TempArticle tempArticle = new TempArticle(tempArticleDto.getContent());
|
||||
public void saveTemp(TempArticle tempArticle){
|
||||
tempArticleRepository.save(tempArticle);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
- 임시글 가져오기
|
||||
*/
|
||||
public Optional<TempArticle> getTempArticle(){
|
||||
|
||||
return tempArticleRepository.findById(1L);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,22 +8,39 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@Configuration
|
||||
public class AppConfig {
|
||||
|
||||
/*
|
||||
- DTO <-> 엔티티 매퍼 빈등록
|
||||
*/
|
||||
@Bean
|
||||
public ModelMapper modelMapper(){ return new ModelMapper();}
|
||||
public ModelMapper modelMapper(){
|
||||
ModelMapper modelMapper = new ModelMapper();
|
||||
modelMapper.getConfiguration()
|
||||
.setFieldAccessLevel(org.modelmapper.config.Configuration.AccessLevel.PRIVATE)
|
||||
.setFieldMatchingEnabled(true);
|
||||
return modelMapper;
|
||||
}
|
||||
|
||||
/*
|
||||
- HTML -> 마크다운 파싱 & 렌더러 빈등록
|
||||
*/
|
||||
@Bean
|
||||
public Parser parser(){
|
||||
return Parser.builder()
|
||||
.extensions(List.of(TablesExtension.create()))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Parser parser(){return Parser.builder()
|
||||
.extensions(Arrays.asList(TablesExtension.create()))
|
||||
.build();}
|
||||
|
||||
@Bean
|
||||
public HtmlRenderer htmlRenderer(){return HtmlRenderer.builder()
|
||||
.extensions(Arrays.asList(TablesExtension.create()))
|
||||
.build();}
|
||||
public HtmlRenderer htmlRenderer(){
|
||||
return HtmlRenderer.builder()
|
||||
.extensions(List.of(TablesExtension.create()))
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -26,6 +26,9 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
private final LoginFailHandler loginFailHandler;
|
||||
private final DataSource dataSource;
|
||||
|
||||
/*
|
||||
- 인가 절차 제외 리소스
|
||||
*/
|
||||
@Override
|
||||
public void configure(WebSecurity web) throws Exception {
|
||||
web
|
||||
@@ -37,43 +40,29 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
// 인가
|
||||
.authorizeRequests()
|
||||
.antMatchers("/article/write").hasRole(Role.ADMIN.name())
|
||||
.antMatchers("/article/write", "/article/edit","/article/delete","/edit/category", "/category/edit").hasRole(Role.ADMIN.name())
|
||||
.anyRequest().permitAll()
|
||||
|
||||
.and()
|
||||
.formLogin()
|
||||
.loginPage("/login")
|
||||
|
||||
// 로그아웃
|
||||
.and()
|
||||
.logout()
|
||||
.logoutSuccessUrl("/")
|
||||
.deleteCookies("JSESSIONID","remember-me")
|
||||
|
||||
//csrf
|
||||
.and()
|
||||
.csrf()
|
||||
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
|
||||
|
||||
|
||||
|
||||
// oauth2 로그인 인증
|
||||
.and()
|
||||
.oauth2Login()
|
||||
.loginPage("/login")
|
||||
.failureHandler(loginFailHandler)
|
||||
.userInfoEndpoint()
|
||||
.userService(oauth2MemberService)
|
||||
|
||||
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
public PersistentTokenRepository tokenRepository() {
|
||||
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
|
||||
jdbcTokenRepository.setDataSource(dataSource);
|
||||
return jdbcTokenRepository;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -9,7 +9,9 @@ import javax.persistence.Column;
|
||||
import javax.persistence.EntityListeners;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/*
|
||||
- auditing 용 추상 엔티티
|
||||
*/
|
||||
@EntityListeners(AuditingEntityListener.class)
|
||||
@MappedSuperclass
|
||||
@Getter
|
||||
|
||||
@@ -16,7 +16,6 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@Controller
|
||||
@RequiredArgsConstructor
|
||||
@@ -26,41 +25,49 @@ public class CategoryController {
|
||||
private final CommentService commentService;
|
||||
private final ModelMapper modelMapper;
|
||||
|
||||
/*
|
||||
- 카테고리 수정폼 조회
|
||||
*/
|
||||
@GetMapping("/edit/category")
|
||||
public String editCategoryForm(Model model) {
|
||||
|
||||
// DTO 매핑 전처리
|
||||
List<CategoryNormalDto> categoryList = categoryService.getCategoryForView();
|
||||
List<CategoryNormalDto> copyList = categoryList
|
||||
.stream()
|
||||
.map(categoryNormalDto ->
|
||||
modelMapper.map(categoryNormalDto, CategoryNormalDto.class))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<CategoryNormalDto> copyList = cloneList(categoryList);
|
||||
copyList.remove(0);
|
||||
|
||||
model.addAttribute("categoryForEdit", copyList);
|
||||
|
||||
CategoryForView categoryForView = CategoryForView.createCategory(categoryList);
|
||||
model.addAttribute("category", categoryForView);
|
||||
|
||||
List<CommentDtoForSide> comments = commentService.recentCommentList()
|
||||
.stream()
|
||||
.map(comment ->
|
||||
new CommentDtoForSide(comment.getId(), comment.getArticle().getId(), comment.getContent(),comment.isSecret()))
|
||||
.collect(Collectors.toList());
|
||||
//
|
||||
|
||||
model.addAttribute("categoryForEdit", copyList);
|
||||
model.addAttribute("category", categoryForView);
|
||||
model.addAttribute("commentsList", comments);
|
||||
|
||||
return "admin/categoryEdit";
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
- 카테고리 수정 요청
|
||||
*/
|
||||
@PostMapping("/category/edit")
|
||||
public @ResponseBody String editCategory(@RequestBody List<CategoryNormalDto> categoryList){
|
||||
|
||||
categoryService.changeCategory(categoryList);
|
||||
|
||||
return "ok";
|
||||
|
||||
return "변경 성공";
|
||||
}
|
||||
|
||||
|
||||
private List<CategoryNormalDto> cloneList(List<CategoryNormalDto> categoryList) {
|
||||
return categoryList
|
||||
.stream()
|
||||
.map(categoryNormalDto ->
|
||||
modelMapper.map(categoryNormalDto, CategoryNormalDto.class))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,9 @@ import javax.persistence.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
- 카테고리 엔티티
|
||||
*/
|
||||
@Entity
|
||||
@Getter
|
||||
@SequenceGenerator(
|
||||
@@ -23,6 +26,7 @@ public class Category extends BasicEntity {
|
||||
@Column(name = "category_id")
|
||||
private Long id;
|
||||
|
||||
@Column(nullable = false, unique = true)
|
||||
private String title;
|
||||
|
||||
@OneToMany(mappedBy = "category")
|
||||
@@ -59,14 +63,17 @@ public class Category extends BasicEntity {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void updateCategory(String title, int tier, int pSortNum, int cSortNum, Category parents){
|
||||
// 도메인 비지니스 로직
|
||||
|
||||
/*
|
||||
- 카테고리 더티체킹 업데이트
|
||||
*/
|
||||
public void updateCategory(String title, int tier, int pSortNum, int cSortNum, Category parents){
|
||||
this.title = title;
|
||||
this.tier = tier;
|
||||
this.pSortNum = pSortNum;
|
||||
this.cSortNum = cSortNum;
|
||||
this.parents = parents;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,6 +7,9 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
- 레이아웃용 트리구조 카테고리 리스트
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class CategoryForView {
|
||||
@@ -16,24 +19,30 @@ public class CategoryForView {
|
||||
private Long id;
|
||||
private int pOrder;
|
||||
private int cOrder;
|
||||
// 트리구조를 갖기 위한 리스트
|
||||
private List<CategoryForView> categoryTCountList = new ArrayList<>();
|
||||
|
||||
/*
|
||||
- 스태틱 생성 메서드
|
||||
*/
|
||||
public static CategoryForView createCategory(List<CategoryNormalDto> crList) {
|
||||
|
||||
return recursiveBuildFromCategoryDto(0, crList);
|
||||
|
||||
}
|
||||
|
||||
private CategoryForView() {
|
||||
}
|
||||
private static CategoryForView recursiveBuildFromCategoryDto(int d, List<CategoryNormalDto> crList) {
|
||||
/*
|
||||
- 재귀호출로 트리구조 생성
|
||||
1. DTO객체 생성후 소스를 큐처리로 순차적 매핑
|
||||
2. Depth 변화시 재귀 호출 / 재귀 탈출
|
||||
3. 탈출시 상위 카테고리 list로 삽입하여 트리구조 작성
|
||||
*/
|
||||
private static CategoryForView recursiveBuildFromCategoryDto(int tier, List<CategoryNormalDto> source) {
|
||||
|
||||
CategoryForView categoryForView = new CategoryForView();
|
||||
|
||||
while (!crList.isEmpty()) {
|
||||
CategoryNormalDto cSource = crList.get(0);
|
||||
while (!source.isEmpty()) {
|
||||
CategoryNormalDto cSource = source.get(0);
|
||||
|
||||
if (cSource.getTier() == d) {
|
||||
if (cSource.getTier() == tier) {
|
||||
if(categoryForView.getTitle() != null
|
||||
&& !categoryForView.getTitle().equals(cSource.getTitle())){
|
||||
return categoryForView;
|
||||
@@ -43,17 +52,18 @@ public class CategoryForView {
|
||||
categoryForView.setId(cSource.getId());
|
||||
categoryForView.setCOrder(cSource.getCOrder());
|
||||
categoryForView.setPOrder(cSource.getPOrder());
|
||||
crList.remove(0);
|
||||
} else if (cSource.getTier() > d) {
|
||||
CategoryForView sub = recursiveBuildFromCategoryDto(d + 1, crList);
|
||||
source.remove(0);
|
||||
} else if (cSource.getTier() > tier) {
|
||||
CategoryForView sub = recursiveBuildFromCategoryDto(tier + 1, source);
|
||||
categoryForView.getCategoryTCountList().add(sub);
|
||||
} else {
|
||||
return categoryForView;
|
||||
}
|
||||
|
||||
}
|
||||
return categoryForView;
|
||||
}
|
||||
|
||||
private CategoryForView() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@ package myblog.blog.category.dto;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
/*
|
||||
- 범용 카테고리 DTO
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
|
||||
@@ -8,10 +8,19 @@ import java.util.List;
|
||||
|
||||
public interface CategoryRepository extends JpaRepository<Category, Long> {
|
||||
|
||||
/*
|
||||
- 카테고리 이름으로 카테고리 찾기
|
||||
*/
|
||||
Category findByTitle(String title);
|
||||
|
||||
/*
|
||||
- 티어별 카테고리들 가져오기
|
||||
*/
|
||||
List<Category> findAllByTierIs(int tier);
|
||||
|
||||
|
||||
/*
|
||||
- ID == 0 인 더미카테고리를 제외한 모든 카테고리 불러오기
|
||||
*/
|
||||
@Query("select c " +
|
||||
"from Category c " +
|
||||
"where c.id >0 ")
|
||||
|
||||
@@ -11,6 +11,9 @@ import java.util.List;
|
||||
@Repository
|
||||
public interface NaCategoryRepository {
|
||||
|
||||
/*
|
||||
- 카테고리별 아티클 갯수 통계 쿼리
|
||||
*/
|
||||
@Select("select ifnull(f.title,'total') as title, ifnull(tier,0) as tier, ifnull(f.category_id, 0) as id, ifnull(count,0) as count, ifnull(f.p_sort_num, 0) as pOrder, ifnull(f.c_sort_num, 0) as cOrder\n" +
|
||||
" from \n" +
|
||||
" (select ifnull(ifnull(b.title, c.title),'total') as title, count(*) as 'count'\n" +
|
||||
|
||||
@@ -21,7 +21,11 @@ public class CategoryService {
|
||||
private final CategoryRepository categoryRepository;
|
||||
private final NaCategoryRepository naCategoryRepository;
|
||||
|
||||
public Long createNewCategory(String title, String parent, int pOrder, int cOrder, int tier) {
|
||||
/*
|
||||
- 새로운 카테고리 생성하기
|
||||
- 상위 카테고리 존재 유무 분기
|
||||
*/
|
||||
public Category createNewCategory(String title, String parent, int pOrder, int cOrder, int tier) {
|
||||
|
||||
Category parentCategory = null;
|
||||
if (parent != null) {
|
||||
@@ -38,44 +42,73 @@ public class CategoryService {
|
||||
|
||||
categoryRepository.save(category);
|
||||
|
||||
return category.getId();
|
||||
return category;
|
||||
}
|
||||
|
||||
/*
|
||||
- 카테고리 이름으로 카테고리 찾기
|
||||
*/
|
||||
public Category findCategory(String title) {
|
||||
return categoryRepository.findByTitle(title);
|
||||
}
|
||||
|
||||
/*
|
||||
- 카테고리와 카테고리별 아티클 수 찾기
|
||||
*/
|
||||
public List<CategoryNormalDto> getCategoryForView() {
|
||||
|
||||
return naCategoryRepository.getCategoryCount();
|
||||
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
- 티어별 카테고리 목록 찾기
|
||||
*/
|
||||
public List<Category> findCategoryByTier(int tier) {
|
||||
return categoryRepository.findAllByTierIs(tier);
|
||||
}
|
||||
|
||||
/*
|
||||
- 카테고리 변경 로직
|
||||
1. 카테고리 리스트의 순서 작성
|
||||
2. 입력받은 카테고리리스트와 DB의 전체카테고리 리스트 두개를 큐로 처리하여 비교대조
|
||||
3. 해당 카테고리 변경처리
|
||||
3-1 해당 카테고리 존재시 더티체킹으로 업데이트 처리
|
||||
3-2 DB에 존재하지 않는경우 새로 생성
|
||||
3-3 DB에만 존재하는 카테고리는 삭제처리
|
||||
*/
|
||||
@Transactional
|
||||
public void changeCategory(List<CategoryNormalDto> categoryList) {
|
||||
|
||||
// 1.카테고리 리스트 순서 작성
|
||||
sortingOrder(categoryList);
|
||||
|
||||
// 2. 기존 DB 저장된 카테고리 리스트 불러오기
|
||||
List<Category> allWithoutDummy = categoryRepository.findAllWithoutDummy();
|
||||
|
||||
// 3. 카테고리 변경 루프
|
||||
while (!categoryList.isEmpty()) {
|
||||
|
||||
CategoryNormalDto categoryNormalDto = categoryList.get(0);
|
||||
categoryList.remove(0);
|
||||
|
||||
// 부모카테고리인경우
|
||||
if (categoryNormalDto.getTier() == 1) {
|
||||
|
||||
Category pCategory = null;
|
||||
if (categoryNormalDto.getId() == null) {
|
||||
Long newCategoryId = createNewCategory(categoryNormalDto.getTitle(), null, categoryNormalDto.getPOrder(), categoryNormalDto.getCOrder(), categoryNormalDto.getTier());
|
||||
pCategory = categoryRepository.findById(newCategoryId).get();
|
||||
|
||||
} else {
|
||||
// 부모카테고리가 기존에 존재 x
|
||||
if (categoryNormalDto.getId() == null) {
|
||||
pCategory
|
||||
= createNewCategory(categoryNormalDto.getTitle(),
|
||||
null,
|
||||
categoryNormalDto.getPOrder(),
|
||||
categoryNormalDto.getCOrder(),
|
||||
categoryNormalDto.getTier());
|
||||
|
||||
}
|
||||
// 부모카테고리가 기존에 존재 o
|
||||
else {
|
||||
for (int i = 0; i < allWithoutDummy.size(); i++) {
|
||||
if (allWithoutDummy.get(i).getId().equals(categoryNormalDto.getId())) {
|
||||
pCategory = allWithoutDummy.get(i);
|
||||
@@ -83,7 +116,6 @@ public class CategoryService {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pCategory.updateCategory(categoryNormalDto.getTitle(),
|
||||
categoryNormalDto.getTier(),
|
||||
categoryNormalDto.getPOrder(),
|
||||
@@ -95,18 +127,21 @@ public class CategoryService {
|
||||
|
||||
CategoryNormalDto subCategoryDto = categoryList.get(0);
|
||||
if (subCategoryDto.getTier() == 1) break;
|
||||
|
||||
categoryList.remove(0);
|
||||
|
||||
// 자식 카테고리인경우
|
||||
Category cCategory = null;
|
||||
// 카테고리가 기존에 존재 x
|
||||
if (subCategoryDto.getId() == null) {
|
||||
Long newCategoryId = createNewCategory(subCategoryDto.getTitle(),
|
||||
cCategory = createNewCategory(subCategoryDto.getTitle(),
|
||||
pCategory.getTitle(),
|
||||
subCategoryDto.getPOrder(),
|
||||
subCategoryDto.getCOrder(), subCategoryDto.getTier());
|
||||
cCategory = categoryRepository.findById(newCategoryId).get();
|
||||
subCategoryDto.getCOrder(),
|
||||
subCategoryDto.getTier());
|
||||
|
||||
} else {
|
||||
}
|
||||
// 카테고리가 기존에 존재 o
|
||||
else {
|
||||
for (int i = 0; i < allWithoutDummy.size(); i++) {
|
||||
if (allWithoutDummy.get(i).getId().equals(subCategoryDto.getId())) {
|
||||
cCategory = allWithoutDummy.get(i);
|
||||
@@ -120,32 +155,22 @@ public class CategoryService {
|
||||
subCategoryDto.getCOrder(),
|
||||
pCategory);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 3-3 불일치 카테고리 전부 삭제
|
||||
categoryRepository.deleteAll(allWithoutDummy);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
- 카테고리 변경을 위해 카테고리의 순번을 작성하는 로직
|
||||
*/
|
||||
private void sortingOrder(List<CategoryNormalDto> categoryList) {
|
||||
int pOrderIndex = 0;
|
||||
int cOrderIndex = 0;
|
||||
boolean isTier1 = false;
|
||||
for (CategoryNormalDto categoryNormalDto : categoryList) {
|
||||
|
||||
System.out.println("categoryNormalDto = " + categoryNormalDto);
|
||||
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i < categoryList.size(); i++) {
|
||||
|
||||
CategoryNormalDto categoryDto = categoryList.get(i);
|
||||
//티어별 트리구조로 순서 작성 로직
|
||||
for (CategoryNormalDto categoryDto : categoryList) {
|
||||
|
||||
if (categoryDto.getTier() == 1) {
|
||||
cOrderIndex = 0;
|
||||
@@ -155,21 +180,12 @@ public class CategoryService {
|
||||
categoryDto.setPOrder(pOrderIndex);
|
||||
categoryDto.setCOrder(++cOrderIndex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (CategoryNormalDto categoryNormalDto : categoryList) {
|
||||
|
||||
System.out.println("categoryNormalDto = " + categoryNormalDto);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
- 최초 더미 카테고리 추가 코드
|
||||
*/
|
||||
@PostConstruct
|
||||
public void insertCategory() {
|
||||
|
||||
|
||||
@@ -8,10 +8,13 @@ import myblog.blog.category.dto.CategoryForView;
|
||||
import myblog.blog.category.service.CategoryService;
|
||||
import myblog.blog.comment.dto.CommentDtoForSide;
|
||||
import myblog.blog.comment.service.CommentService;
|
||||
import org.modelmapper.ModelMapper;
|
||||
import org.springframework.data.domain.Slice;
|
||||
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.ResponseBody;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -24,6 +27,7 @@ public class MainController {
|
||||
private final ArticleService articleService;
|
||||
private final CategoryService categoryService;
|
||||
private final CommentService commentService;
|
||||
private final ModelMapper modelMapper;
|
||||
|
||||
@GetMapping("/")
|
||||
public String main(Model model) {
|
||||
@@ -38,16 +42,29 @@ public class MainController {
|
||||
.collect(Collectors.toList());
|
||||
model.addAttribute("commentsList", comments);
|
||||
|
||||
List<ArticleDtoForMain> popularArticles = articleService.getPopularArticles();
|
||||
List<ArticleDtoForMain> popularArticles = articleService.getPopularArticles()
|
||||
.stream()
|
||||
.map(article -> modelMapper.map(article, ArticleDtoForMain.class))
|
||||
.collect(Collectors.toList());
|
||||
model.addAttribute("popularArticles", popularArticles);
|
||||
|
||||
Slice<ArticleDtoForMain> recentArticles = articleService.getRecentArticles(0);
|
||||
Slice<ArticleDtoForMain> recentArticles = articleService.getRecentArticles(0)
|
||||
.map(article -> modelMapper.map(article, ArticleDtoForMain.class));
|
||||
model.addAttribute("recentArticles",recentArticles);
|
||||
|
||||
return "index";
|
||||
|
||||
}
|
||||
|
||||
@GetMapping("/main/article/{pageNum}")
|
||||
public @ResponseBody
|
||||
List<ArticleDtoForMain> mainNextPage(@PathVariable int pageNum) {
|
||||
|
||||
return articleService.getRecentArticles(pageNum).getContent()
|
||||
.stream()
|
||||
.map(article -> modelMapper.map(article, ArticleDtoForMain.class))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -6,40 +6,21 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Jinia's Log - 카테고리 편집</title>
|
||||
<!-- SEO -->
|
||||
<meta name="description" content=""/>
|
||||
<meta name="keyword" content=""/>
|
||||
<meta name="author" content="jinia"/>
|
||||
<title>카테고리 편집 - Jinia's Log</title>
|
||||
<meta name="viewport" content="width=device-width, user-scalable = no, initial-scale=1.0"/>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
|
||||
|
||||
<!-- OPEN GRAPH(FACEBOOK, LINKEDIN) -->
|
||||
<meta property="og:type" content=""/>
|
||||
<meta property="og:description" content=""/>
|
||||
<meta property="og:title" content=""/>
|
||||
<meta property="og:image" content=""/>
|
||||
<meta property="og:url" content=""/>
|
||||
<meta property="og:site_name" content=""/>
|
||||
|
||||
<!-- twitter -->
|
||||
<meta property="twitter:card" content=""/>
|
||||
<meta property="twitter:title" content=""/>
|
||||
<meta property="twitter:description" content=""/>
|
||||
<meta property="twitter:image" content=""/>
|
||||
<meta property="twitter:url" content=""/>
|
||||
<meta property="twitter:creator" content=""/>
|
||||
|
||||
<link rel="icon" href=""/>
|
||||
<link rel="apple-touch-icon" href=""/>
|
||||
<link rel="short icon" type="image/x-icon" href=""/>
|
||||
<link rel="icon" href="/img/favicon-16x16.png"/>
|
||||
<link rel="apple-touch-icon" href="/img/favicon-16x16.png"/>
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/img/favicon-16x16.png">
|
||||
<meta name="msapplication-TileColor" content="#ffffff">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<!-- CSS RESET -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css"/>
|
||||
<link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.min.css"/>
|
||||
<link rel="stylesheet" href="/css/mainCss.css"/>
|
||||
<link rel="stylesheet" href="/css/login.css"/>
|
||||
|
||||
<script src="https://kit.fontawesome.com/233840a552.js" crossorigin="anonymous"></script>
|
||||
</head>
|
||||
|
||||
@@ -65,7 +46,6 @@
|
||||
th:id="|subCategory-${subCategory.getId()}|"
|
||||
onclick="clickCategory(this)"></button></th:block></th:block></div>
|
||||
|
||||
|
||||
<div class="col-sm g-0 ms-sm-5">
|
||||
<div class="mb-5 ">
|
||||
<label for="categoryName">카테고리 이름 변경</label>
|
||||
@@ -107,54 +87,72 @@
|
||||
let idxNewCategoryNum = 0;
|
||||
const categoryName = document.getElementById("categoryName");
|
||||
|
||||
// 셀렉터
|
||||
let seclector;
|
||||
function clickCategory(div) {
|
||||
selector = div;
|
||||
console.log(categorysList);
|
||||
categoryName.value = selector.innerText;
|
||||
// DIV 셀렉터
|
||||
let selector;
|
||||
// DTO 카테고리 셀렉터
|
||||
let category;
|
||||
|
||||
// 카테고리명 중복 검사
|
||||
function isDuplicatedCategoryName(){
|
||||
const categoryList = document.getElementById("categoryBox").childNodes;
|
||||
for (let i = 0; i<categoryList.length; i++) {
|
||||
const curNode = categoryList[i];
|
||||
|
||||
for(let j = i+1; j<categoryList.length; j++){
|
||||
if(curNode.textContent === categoryList[j].textContent){
|
||||
alert("카테고리 명이 중복되면 안됩니다.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function clickCategory(div) {
|
||||
|
||||
if(isDuplicatedCategoryName()){
|
||||
return;
|
||||
}
|
||||
|
||||
selector = div;
|
||||
categoryName.value = selector.innerText;
|
||||
category = categorysList.find(x => x.title === selector.innerText);
|
||||
const childNodes = selector.parentNode.childNodes;
|
||||
|
||||
for (const childNode of childNodes) {
|
||||
childNode.classList.remove("active");
|
||||
}
|
||||
|
||||
selector.classList.add("active");
|
||||
|
||||
console.log(selector);
|
||||
}
|
||||
|
||||
categoryName.addEventListener("keyup", () => {
|
||||
const category = categorysList.find(x => x.title == selector.innerText);
|
||||
selector.innerText = categoryName.value;
|
||||
category.title = selector.innerText;
|
||||
|
||||
})
|
||||
|
||||
function swapToPrevious(title){
|
||||
|
||||
const index = categorysList.findIndex(x=>x.title == title);
|
||||
const index = categorysList.findIndex(x=>x.title === title);
|
||||
|
||||
let tmp = categorysList[index-1];
|
||||
categorysList[index-1] = categorysList[index];
|
||||
categorysList[index] = tmp;
|
||||
}
|
||||
|
||||
|
||||
function swapToNextInDto(title){
|
||||
|
||||
const index = categorysList.findIndex(x=>x.title == title);
|
||||
const index = categorysList.findIndex(x=>x.title === title);
|
||||
|
||||
let tmp = categorysList[index+1];
|
||||
categorysList[index+1] = categorysList[index];
|
||||
categorysList[index] = tmp;
|
||||
}
|
||||
|
||||
|
||||
function categoryUp() {
|
||||
|
||||
const categoryTitle = selector.innerText;
|
||||
const category = categorysList.find(x => x.title == selector.innerText);
|
||||
|
||||
// 선택 카테고리가 부모일때
|
||||
if (selector.previousSibling != null) {
|
||||
@@ -168,7 +166,6 @@
|
||||
function categoryDown() {
|
||||
|
||||
const categoryTitle = selector.innerText;
|
||||
const category = categorysList.find(x => x.title == selector.innerText);
|
||||
|
||||
// 선택 카테고리가 부모일때
|
||||
if (selector.nextSibling.nextSibling != null) {
|
||||
@@ -183,25 +180,22 @@
|
||||
}
|
||||
|
||||
function tierUp(){
|
||||
const category = categorysList.find(x => x.title == selector.innerText);
|
||||
category.tier = 1;
|
||||
selector.classList.add("fw-bold");
|
||||
|
||||
}
|
||||
|
||||
function tierDown(){
|
||||
const category = categorysList.find(x => x.title == selector.innerText);
|
||||
category.tier = 2;
|
||||
selector.classList.remove("fw-bold");
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
function addCategory(){
|
||||
const box = document.getElementById("categoryBox");
|
||||
box.innerHTML +=`<button class="list-group-item-action list-group-item-light list-group-item" onclick="clickCategory(this)">새 카테고리${idxNewCategoryNum}</button>`
|
||||
|
||||
let newCategory = new Object();
|
||||
let newCategory = {};
|
||||
newCategory.id = null;
|
||||
newCategory.title = '새 카테고리'+idxNewCategoryNum++;
|
||||
newCategory.tier = 2;
|
||||
@@ -212,17 +206,19 @@
|
||||
categorysList.push(newCategory);
|
||||
|
||||
}
|
||||
function deleteCategory(){
|
||||
const categoryIndex = categorysList.findIndex(x=>x.title == selector.innerText);
|
||||
|
||||
function deleteCategory(){
|
||||
const categoryIndex = categorysList.findIndex(x=>x.title === selector.innerText);
|
||||
categorysList.splice(categoryIndex,1);
|
||||
selector.remove();
|
||||
}
|
||||
|
||||
function changeCategory(){
|
||||
if(isDuplicatedCategoryName()){
|
||||
return;
|
||||
}
|
||||
|
||||
let token = getCsrfToken();
|
||||
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open("POST", "/category/edit");
|
||||
xhr.setRequestHeader("content-type", "application/json");
|
||||
@@ -231,7 +227,6 @@
|
||||
|
||||
xhr.onload = () => {
|
||||
if (xhr.status === 200 || xhr.status === 201 || xhr.status === 202) {
|
||||
|
||||
location.href='/';
|
||||
}
|
||||
else{
|
||||
@@ -242,7 +237,6 @@
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
</body>
|
||||
|
||||
@@ -6,33 +6,15 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Jinia's Log - 글 수정</title>
|
||||
<!-- SEO -->
|
||||
<meta name="description" content=""/>
|
||||
<meta name="keyword" content=""/>
|
||||
<meta name="author" content="jinia"/>
|
||||
<title>글 수정 - Jinia's Log</title>
|
||||
<meta name="viewport" content="width=device-width, user-scalable = no, initial-scale=1.0"/>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
|
||||
|
||||
<!-- OPEN GRAPH(FACEBOOK, LINKEDIN) -->
|
||||
<meta property="og:type" content=""/>
|
||||
<meta property="og:description" content=""/>
|
||||
<meta property="og:title" content=""/>
|
||||
<meta property="og:image" content=""/>
|
||||
<meta property="og:url" content=""/>
|
||||
<meta property="og:site_name" content=""/>
|
||||
|
||||
<!-- twitter -->
|
||||
<meta property="twitter:card" content=""/>
|
||||
<meta property="twitter:title" content=""/>
|
||||
<meta property="twitter:description" content=""/>
|
||||
<meta property="twitter:image" content=""/>
|
||||
<meta property="twitter:url" content=""/>
|
||||
<meta property="twitter:creator" content=""/>
|
||||
|
||||
<link rel="icon" href=""/>
|
||||
<link rel="apple-touch-icon" href=""/>
|
||||
<link rel="short icon" type="image/x-icon" href=""/>
|
||||
<link rel="icon" href="/img/favicon-16x16.png"/>
|
||||
<link rel="apple-touch-icon" href="/img/favicon-16x16.png"/>
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/img/favicon-16x16.png">
|
||||
<meta name="msapplication-TileColor" content="#ffffff">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<!-- CSS RESET -->
|
||||
<link rel="stylesheet" href="/node_modules/@toast-ui/editor/dist/toastui-editor.css"/>
|
||||
|
||||
@@ -6,33 +6,16 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Jinia's Log - 글 작성</title>
|
||||
<!-- SEO -->
|
||||
<meta name="description" content=""/>
|
||||
<meta name="keyword" content=""/>
|
||||
<meta name="author" content="jinia"/>
|
||||
<title>글 작성 - Jinia's Log</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, user-scalable = no, initial-scale=1.0"/>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
|
||||
|
||||
<!-- OPEN GRAPH(FACEBOOK, LINKEDIN) -->
|
||||
<meta property="og:type" content=""/>
|
||||
<meta property="og:description" content=""/>
|
||||
<meta property="og:title" content=""/>
|
||||
<meta property="og:image" content=""/>
|
||||
<meta property="og:url" content=""/>
|
||||
<meta property="og:site_name" content=""/>
|
||||
|
||||
<!-- twitter -->
|
||||
<meta property="twitter:card" content=""/>
|
||||
<meta property="twitter:title" content=""/>
|
||||
<meta property="twitter:description" content=""/>
|
||||
<meta property="twitter:image" content=""/>
|
||||
<meta property="twitter:url" content=""/>
|
||||
<meta property="twitter:creator" content=""/>
|
||||
|
||||
<link rel="icon" href=""/>
|
||||
<link rel="apple-touch-icon" href=""/>
|
||||
<link rel="short icon" type="image/x-icon" href=""/>
|
||||
<link rel="icon" href="/img/favicon-16x16.png"/>
|
||||
<link rel="apple-touch-icon" href="/img/favicon-16x16.png"/>
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/img/favicon-16x16.png">
|
||||
<meta name="msapplication-TileColor" content="#ffffff">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<!-- CSS RESET -->
|
||||
<link rel="stylesheet" href="/node_modules/@toast-ui/editor/dist/toastui-editor.css"/>
|
||||
|
||||
@@ -356,34 +356,48 @@
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// 유효성 검사
|
||||
function checkTitle() {
|
||||
|
||||
let title = document.getElementById("title");
|
||||
|
||||
if (title.value === "") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return document.getElementById("title").value !== "";
|
||||
}
|
||||
|
||||
function checkContent() {
|
||||
return contents.value !== "";
|
||||
}
|
||||
|
||||
function checkCategory() {
|
||||
return document.getElementById("category").value !== "카테고리를 선택해주세요";
|
||||
}
|
||||
|
||||
function checkTags() {
|
||||
return document.getElementById("tags").value !== "";
|
||||
}
|
||||
|
||||
// 전송
|
||||
function post() {
|
||||
|
||||
contents.value = editor.getMarkdown();
|
||||
|
||||
if (!checkTitle()) {
|
||||
alert("제목을 입력해주세요")
|
||||
return;
|
||||
}
|
||||
else if (!checkContent()) {
|
||||
alert("내용을 입력해주세요")
|
||||
return;
|
||||
}else if (!checkCategory()) {
|
||||
alert("카테고리를 입력해주세요")
|
||||
return;
|
||||
}else if (!checkTags()) {
|
||||
alert("태그를 입력해주세요")
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(thumbUrl.value + "????????")
|
||||
contents.value = editor.getMarkdown();
|
||||
document.getElementById("writeArticleForm").submit();
|
||||
|
||||
}
|
||||
|
||||
|
||||
editor.setMarkdown([[${articleDto.getContent()}]]);
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
@@ -396,10 +410,6 @@
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script th:fragment="articleWrite" th:inline="javascript" type="application/javascript">
|
||||
|
||||
const contents = document.getElementById("content");
|
||||
@@ -476,7 +486,6 @@
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function uploadImage(blob) {
|
||||
let token = getCsrfToken();
|
||||
let formData = new FormData();
|
||||
@@ -496,30 +505,46 @@
|
||||
|
||||
}
|
||||
|
||||
// 유효성 검사
|
||||
function checkTitle() {
|
||||
|
||||
let title = document.getElementById("title");
|
||||
|
||||
if (title.value === "") {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return document.getElementById("title").value !== "";
|
||||
}
|
||||
|
||||
function checkContent() {
|
||||
return contents.value !== "";
|
||||
}
|
||||
|
||||
function checkCategory() {
|
||||
return document.getElementById("category").value !== "카테고리를 선택해주세요";
|
||||
}
|
||||
|
||||
function checkTags() {
|
||||
return document.getElementById("tags").value !== "";
|
||||
}
|
||||
|
||||
// 전송
|
||||
function post() {
|
||||
|
||||
contents.value = editor.getMarkdown();
|
||||
|
||||
if (!checkTitle()) {
|
||||
alert("제목을 입력해주세요")
|
||||
return;
|
||||
}
|
||||
else if (!checkContent()) {
|
||||
alert("내용을 입력해주세요")
|
||||
return;
|
||||
}else if (!checkCategory()) {
|
||||
alert("카테고리를 입력해주세요")
|
||||
return;
|
||||
}else if (!checkTags()) {
|
||||
alert("태그를 입력해주세요")
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
contents.value = editor.getMarkdown();
|
||||
document.getElementById("writeArticleForm").submit();
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 자동 저장 기능
|
||||
function autoSave(){
|
||||
|
||||
@@ -557,7 +582,7 @@
|
||||
|
||||
let tmp = JSON.parse(xhr.response);
|
||||
|
||||
if(tmp.content != null&& tmp.content != ''){
|
||||
if(tmp.content != null&& tmp.content !== ''){
|
||||
let isLoadTmp = confirm("이전에 작성하던 글이 있습니다. 이어서 작성하시겠습니까?");
|
||||
if(isLoadTmp){
|
||||
editor.setMarkdown(tmp.content);
|
||||
|
||||
Reference in New Issue
Block a user