게시물 삭제 기능 추가.
parent
93da80a1ff
commit
bde144db79
|
|
@ -8,22 +8,20 @@ import java.util.HashMap;
|
|||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum LogStatus {
|
||||
WRITE("WRITE", "작성"),
|
||||
MODIFY("MODIFY", "수정"),
|
||||
MOVE("MOVE", "이동"),
|
||||
DELETE("DELETE", "삭제"),
|
||||
FILE_ADD("FILE_ADD", "파일추가"),
|
||||
FILE_REMOVE("FILE_REMOVE", "파일삭제"),
|
||||
FILE_DOWN("FILE_DOWN", "파일다운로드");
|
||||
WRITE("작성"),
|
||||
MODIFY("수정"),
|
||||
MOVE("이동"),
|
||||
DELETE("삭제"),
|
||||
FILE_ADD("파일추가"),
|
||||
FILE_REMOVE("파일삭제"),
|
||||
FILE_DOWN("파일다운로드");
|
||||
|
||||
private String key;
|
||||
private String value;
|
||||
|
||||
|
||||
public static HashMap<String, String> getStatusMap(){
|
||||
HashMap<String, String> statusMap = new HashMap<>();
|
||||
for(LogStatus status: LogStatus.values()){
|
||||
statusMap.put(status.getKey(), status.getValue());
|
||||
statusMap.put(status.name(), status.getValue());
|
||||
}
|
||||
return statusMap;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,8 +33,8 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
|||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.authorizeRequests() // 페이지 권한 설정
|
||||
.antMatchers("/board/**").hasRole("MEMBER") // MEMBER, ADMIN만 접근 허용
|
||||
.antMatchers("/admin/**").hasRole("ADMIN") // ADMIN만 접근 허용
|
||||
.antMatchers("/board/**").hasRole(Role.USER.name()) // USER, ADMIN 접근 허용
|
||||
.antMatchers("/admin/**").hasRole(Role.ADMIN.name()) // ADMIN만 접근 허용
|
||||
.antMatchers("/user/login").permitAll() // 로그인 페이지는 권한 없이 접근 허용
|
||||
.and() // 로그인 설정
|
||||
.formLogin() .loginPage("/user/login") // Custom login form 사용
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import com.dbnt.kcgfilemanager.model.UserInfo;
|
|||
import com.dbnt.kcgfilemanager.service.BoardService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartHttpServletRequest;
|
||||
|
|
@ -62,10 +63,11 @@ public class BoardController {
|
|||
}
|
||||
|
||||
@GetMapping("/selectBoardContent")
|
||||
public ModelAndView selectBoardContent(Board content){
|
||||
public ModelAndView selectBoardContent(Board content, @AuthenticationPrincipal UserInfo loginUser){
|
||||
ModelAndView mav = new ModelAndView("board/contentDetail");
|
||||
Board target = boardService.selectContentByContentSeqAndViewCntUp(content.getContentSeq());
|
||||
mav.addObject("content", boardService.SelectContentForeignAttribute(target));
|
||||
mav.addObject("loginUser", loginUser);
|
||||
return mav;
|
||||
}
|
||||
@GetMapping("/selectBoardLog")
|
||||
|
|
@ -76,6 +78,10 @@ public class BoardController {
|
|||
mav.addObject("logList", boardService.selectContentLog(content.getContentSeq()));
|
||||
return mav;
|
||||
}
|
||||
@DeleteMapping("/deleteContent")
|
||||
public Integer deleteContent(Board content, @AuthenticationPrincipal UserInfo loginUser){
|
||||
return boardService.deleteContent(content, loginUser);
|
||||
}
|
||||
|
||||
@GetMapping("/fileDownload")
|
||||
public void fileDownload(Principal principal, Integer contentSeq, Integer fileSeq, HttpServletRequest request, HttpServletResponse response){
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ package com.dbnt.kcgfilemanager.repository;
|
|||
import com.dbnt.kcgfilemanager.model.HashTagLink;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface HashTagLinkRepository extends JpaRepository<HashTagLink, HashTagLink.HashTagLinkId> {
|
||||
import java.util.List;
|
||||
|
||||
public interface HashTagLinkRepository extends JpaRepository<HashTagLink, HashTagLink.HashTagLinkId> {
|
||||
List<HashTagLink> findByContentSeq(int contentSeq);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ public class BoardService {
|
|||
BoardLog log = new BoardLog();
|
||||
log.setContentSeq(contentSeq);
|
||||
log.setLogSeq(lastLog == null?1:(lastLog.getLogSeq()+1));
|
||||
log.setLogStatus(status.getKey());
|
||||
log.setLogStatus(status.name());
|
||||
log.setDescription(description);
|
||||
log.setCreateId(createId);
|
||||
boardLogRepository.save(log);
|
||||
|
|
@ -126,7 +126,9 @@ public class BoardService {
|
|||
@Transactional
|
||||
public Board selectContentByContentSeqAndViewCntUp(Integer contentSeq) {
|
||||
Board target = boardRepository.findById(contentSeq).orElse(null);
|
||||
target.setViewCnt(target.getViewCnt()+1);
|
||||
if(target.getStatus()==null){
|
||||
target.setViewCnt(target.getViewCnt()+1);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
|
|
@ -157,6 +159,32 @@ public class BoardService {
|
|||
return target;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Integer deleteContent(Board content, UserInfo user) {
|
||||
int contentSeq = content.getContentSeq();
|
||||
content = boardRepository.findById(contentSeq).orElse(null);
|
||||
content.setTitle("삭제된 게시물");
|
||||
content.setDescription(null);
|
||||
content.setStatus("D");
|
||||
saveBoardLog(contentSeq, LogStatus.DELETE, null, user.getUserId());
|
||||
deleteHashTagLink(contentSeq);
|
||||
deleteFileInfo(contentSeq);
|
||||
return contentSeq;
|
||||
}
|
||||
|
||||
private void deleteHashTagLink(int contentSeq){
|
||||
List<HashTagLink> tagLinkList = hashTagLinkRepository.findByContentSeq(contentSeq);
|
||||
hashTagLinkRepository.deleteAll(tagLinkList);
|
||||
}
|
||||
private void deleteFileInfo(int contentSeq){
|
||||
List<FileInfo> fileInfoList = fileInfoRepository.findByContentSeqOrderByFileSeqAsc(contentSeq);
|
||||
for(FileInfo fileInfo: fileInfoList){
|
||||
File file = new File(fileInfo.getSavePath(), fileInfo.getConversionName());
|
||||
file.delete();
|
||||
}
|
||||
fileInfoRepository.deleteAll(fileInfoList);
|
||||
}
|
||||
|
||||
private String makeFilePath(Integer categorySeq){
|
||||
BoardCategory category = boardCategoryRepository.findById(categorySeq).orElse(null);
|
||||
if(category.getParentSeq()==null){
|
||||
|
|
@ -169,5 +197,4 @@ public class BoardService {
|
|||
double unitSelector = Math.floor(Math.log(fileSize)/Math.log(1024));
|
||||
return Math.round((fileSize/Math.pow(1024, unitSelector))*100)/100d+" "+units[(int)unitSelector];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
FROM BOARD A
|
||||
INNER JOIN USER_INFO B
|
||||
ON A.CREATE_ID = B.USER_ID
|
||||
INNER JOIN (SELECT CONTENT_SEQ, MAX(FILE_SEQ) AS FILE_CNT
|
||||
LEFT OUTER JOIN (SELECT CONTENT_SEQ, MAX(FILE_SEQ) AS FILE_CNT
|
||||
FROM FILE_INFO
|
||||
GROUP BY CONTENT_SEQ ) C
|
||||
ON A.CONTENT_SEQ = C.CONTENT_SEQ
|
||||
|
|
@ -30,11 +30,11 @@
|
|||
FROM BOARD A
|
||||
INNER JOIN USER_INFO B
|
||||
ON A.CREATE_ID = B.USER_ID
|
||||
INNER JOIN (SELECT CONTENT_SEQ, MAX(FILE_SEQ) AS FILE_CNT
|
||||
LEFT OUTER JOIN (SELECT CONTENT_SEQ, MAX(FILE_SEQ) AS FILE_CNT
|
||||
FROM FILE_INFO
|
||||
GROUP BY CONTENT_SEQ ) C
|
||||
ON A.CONTENT_SEQ = C.CONTENT_SEQ
|
||||
WHERE CATEGORY_SEQ = ${categorySeq}
|
||||
WHERE A.CATEGORY_SEQ = ${categorySeq}
|
||||
</select>
|
||||
|
||||
<select id="selectHashTagListFromContentSeq" resultType="HashTag" parameterType="int">
|
||||
|
|
|
|||
|
|
@ -49,7 +49,28 @@ $(document).on('click', '#zipDownBtn', function (){
|
|||
alert("선택된 파일이 없습니다.")
|
||||
}
|
||||
})
|
||||
$(document).on('click', '#deleteBtn', function (){
|
||||
if(confirm("이 게시물을 삭제하시겠습니까?\n되돌릴 수 없습니다.")){
|
||||
$.ajax({
|
||||
type : 'DELETE',
|
||||
url : "/board/deleteContent",
|
||||
data : {contentSeq: Number($(this).attr("data-contentseq"))},
|
||||
beforeSend: function (xhr){
|
||||
xhr.setRequestHeader($("[name='_csrf_header']").val(), $("[name='_csrf']").val());
|
||||
},
|
||||
success : function(data) {
|
||||
alert("저장되었습니다.");
|
||||
location.reload();
|
||||
},
|
||||
error : function(xhr, status) {
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
$(document).on('click', '#modifyBtn', function (){
|
||||
|
||||
})
|
||||
function getContentSeq(){
|
||||
return $(".contentCheckBox:checked").val();
|
||||
}
|
||||
|
|
@ -65,8 +86,10 @@ function getBoardContent(contentSeq){
|
|||
dataType:"html",
|
||||
success: function(html){
|
||||
$("#contentDiv").empty().append(html)
|
||||
const viewCntTd = $(".contentCheckBox:checked").parents("tr").find(".viewCntTd");
|
||||
viewCntTd.text(Number(viewCntTd.text())+1);
|
||||
if($("#contentStatus").val() !== "D"){
|
||||
const viewCntTd = $(".contentCheckBox:checked").parents("tr").find(".viewCntTd");
|
||||
viewCntTd.text(Number(viewCntTd.text())+1);
|
||||
}
|
||||
},
|
||||
error:function(){
|
||||
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@
|
|||
<div class="mb-3 row">
|
||||
<div class="col-sm-4"></div>
|
||||
<div class="col-sm-auto form-check ms-3">
|
||||
<input type="hidden" name="userRole" value="ROLE_MEMBER">
|
||||
<input type="hidden" name="userRole" value="ROLE_USER">
|
||||
<input class="form-check-input" type="checkbox" value="ROLE_ADMIN" id="roleAdmin" name="userRole" th:checked="${#strings.contains(userInfo.userRole,'ADMIN')}">
|
||||
<label class="form-check-label" for="roleAdmin">관리자 계정</label>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -210,7 +210,7 @@
|
|||
<div class="mb-3 row">
|
||||
<div class="col-sm-4"></div>
|
||||
<div class="col-sm-auto form-check ms-3">
|
||||
<input type="hidden" name="userRole" value="ROLE_MEMBER">
|
||||
<input type="hidden" name="userRole" value="ROLE_USER">
|
||||
<input class="form-check-input" type="checkbox" value="ROLE_ADMIN" id="roleAdmin" name="userRole">
|
||||
<label class="form-check-label" for="roleAdmin">관리자 계정</label>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,8 +2,9 @@
|
|||
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
|
||||
<div class="p-3">
|
||||
<input type="hidden" id="detailViewContentSeq" th:value="${content.contentSeq}">
|
||||
<input type="hidden" id="contentStatus" th:value="${content.status}">
|
||||
<div class="row justify-content-between">
|
||||
<div class="col-auto"><h3 th:text="${content.title}"></h3></div>
|
||||
<div class="col-auto"><h5 class="fw-bold" th:text="${content.title}"></h5></div>
|
||||
<div class="col-auto" th:text="|조회수: ${content.viewCnt}|"></div>
|
||||
</div>
|
||||
<div class="row justify-content-between border-bottom pb-3">
|
||||
|
|
@ -11,7 +12,7 @@
|
|||
<div class="col-auto" th:text="${content.createId}"></div>
|
||||
</div>
|
||||
<div class="row border-bottom py-3">
|
||||
<div class="col-8">
|
||||
<div th:class="|col-${content.childFileList.size()>1?8:12}|">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
|
|
@ -31,7 +32,7 @@
|
|||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-4 my-auto">
|
||||
<div class="col-4 m-auto" th:if="${content.childFileList.size()>1}">
|
||||
<button type="button" class="btn btn-info m-1" id="zipDownBtn"><i class="bi bi-file-zip"></i> 선택된 파일<br>압축 다운로드</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -43,4 +44,14 @@
|
|||
<div class="col-auto" th:text="'#'+${hashTag.tagName}"></div>
|
||||
</th:block>
|
||||
</div>
|
||||
<th:block th:if="${content.createId == loginUser.userId || loginUser.userRole.contains('ADMIN')}">
|
||||
<div class="row justify-content-between py-3">
|
||||
<div class="col-auto">
|
||||
<button class="btn btn-danger" id="deleteBtn" th:data-contentseq="${content.contentSeq}"><i class="bi bi-trash"></i> 삭제</button>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button class="btn btn-warning" id="modifyBtn" th:data-contentseq="${content.contentSeq}"><i class="bi bi-pencil-square"></i> 수정</button>
|
||||
</div>
|
||||
</div>
|
||||
</th:block>
|
||||
</div>
|
||||
|
|
@ -9,6 +9,8 @@
|
|||
</th:block>
|
||||
<div layout:fragment="content">
|
||||
<main class="pt-3">
|
||||
<input type="hidden" name="_csrf_header" th:value="${_csrf.headerName}"/>
|
||||
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
|
||||
<h4 th:text="${pageTitle}"></h4>
|
||||
<div class="row mx-0">
|
||||
<div class="col-12 card">
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@
|
|||
<form th:action="@{/signup}" method="post">
|
||||
<input type="text" name="userId" placeholder="이메일 입력해주세요" />
|
||||
<input type="password" name="password" placeholder="비밀번호" />
|
||||
<input type="radio" name="userRole" value="ROLE_ADMIN,ROLE_MEMBER" /> admin
|
||||
<input type="radio" name="userRole" value="ROLE_MEMBER" checked="checked" />
|
||||
<input type="radio" name="userRole" value="ROLE_ADMIN,ROLE_USER" /> admin
|
||||
<input type="radio" name="userRole" value="ROLE_USER" checked="checked" />
|
||||
member <br />
|
||||
<button type="submit">가입하기</button>
|
||||
</form>
|
||||
|
|
|
|||
Loading…
Reference in New Issue