게시글 제목, 내용에 대한 특수문자(태그포함) 및 XSS 처리 통합자료관과 통일성있게 변경 처리

This commit is contained in:
KNKIM 2021-12-22 10:07:27 +09:00
parent 22f543a91a
commit 3c29a91a59
8 changed files with 1342 additions and 1278 deletions

View File

@ -1,384 +1,395 @@
package nlib.bbs.service;
import java.util.List;
import org.apache.commons.lang.StringEscapeUtils;
import com.fasterxml.jackson.annotation.JsonIgnore;
import nlib.cmm.service.PagingVO;
import nlib.util.StringUtil;
/**
* <pre>
* @Class Name : ArticleVO.java
*
* @Description : 게시물 공통 VO
*
*
* @프로젝트명: 지방문화원 통합자료관리시스템 구축사업 (2021)
*
* </pre>
*
* @ ------------ -------- ---------------------------
* @ 수정일 수정자 수정내용
* @ ------------ -------- ---------------------------
* @ 2021. 09. 13. KNKIM 최초 생성
*
*
* @author 이씨플라자 * DIGITALSHIP KNKIM
* @since 2021. 09. 13.
* @version 1.0
*
*/
public class ArticleVO extends PagingVO {
private String articleId; /* 게시글ID */
private String mngOrgCd; /* 관리문화원코드 */
private String mngOrgNm; /* 관리문화원명 */
@JsonIgnore
private String bdType; /* 게시판유형 */
@JsonIgnore
private String bdTypeName; /* 게시판유형명 */
private String title; /* 제목 */
private String content; /* 내용 */
// 공지사항관련
private String notiYn; /* 공지여부 */
private String openYn; /* 공개여부 */
@JsonIgnore
private String postStartDate; /* 게시시작일자 */
@JsonIgnore
private String postEndDate; /* 게시종료일자 */
// FAQ, QNA관련
private String answer; /* 답변 */
private String answerYn; /* 답변여부 */
@JsonIgnore
private String useYn; /* 사용여부 */
private String questionType; /* 질문유형 */
private String questionTypeName; /* 질문유형명 */
private String secretYn; /* 비밀글여부 */
private String emailRecvYn; /* 답변이미엘수신여부 */
private String email; /* 이메일 */
private int viewCnt; /* 조회수 */
private String bdAttachFileId; /* 첨부파일아이디 */
private String attachYn; /* 첨부파일존재여부 */
private List<AttachFileVO> attachFiles; /* 첨부파일목록 */
private List<AttachFileVO> removedAttachFiles; /* 삭제첨부파일목록 */
@JsonIgnore
private String regId; /* 등록자아이디 */
private String regNm; /* 등록자명 */
private String regDd; /* 등록일자 */
@JsonIgnore
private String modId; /* 등록자아이디 */
private String modDd; /* 등록일자 */
private int rno; /* 글번호 */
@JsonIgnore
private String loginedMbInfoId; /* 로그인 사용자 ID */
private String loginedName; /* 로그인 사용자명 */
private String myQnaYn; /* 내가한 질문인지 여부 */
// 검색관련
private String searchType; /* 검색대상구분 */
private String searchKeyword; /* 검색어 */
private String searchQuestionType; /* 검색FAQ유형 */
@JsonIgnore
private String searchMbInfoId; /* 로그인한 사용자ID */
// 처리결과 관련
private String resultMessage = null; /* 처리결과메시지 */
private String resultCode = null; /* 처리결과코드 */
public int getAttachFileCnt() {
if(attachFiles == null) return 0;
return attachFiles.size();
}
public String getUnescapedContent() {
return StringEscapeUtils.unescapeHtml(content);
}
// SETTER & GETTER
public String getArticleId() {
return articleId;
}
public void setArticleId(String articleId) {
// 쿼리 조건으로 사용되는 항목에 대하여 SQL 인젝션으로 처리될만한 문자 강체 치환 처리
this.articleId = StringUtil.getValidCodeString(articleId);
}
public String getMngOrgCd() {
return mngOrgCd;
}
public void setMngOrgCd(String mngOrgCd) {
// 쿼리 조건으로 사용되는 항목에 대하여 SQL 인젝션으로 처리될만한 문자 강체 치환 처리
this.mngOrgCd = StringUtil.getValidCodeString(mngOrgCd);
}
public String getBdType() {
return bdType;
}
public void setBdType(String bdType) {
this.bdType = bdType;
}
public String getUnescapeTitle() {
return StringEscapeUtils.unescapeHtml(title);
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getNotiYn() {
return notiYn;
}
public void setNotiYn(String notiYn) {
this.notiYn = notiYn;
}
public String getOpenYn() {
return openYn;
}
public void setOpenYn(String openYn) {
this.openYn = openYn;
}
public String getPostStartDate() {
return postStartDate;
}
public void setPostStartDate(String postStartDate) {
this.postStartDate = postStartDate;
}
public String getPostEndDate() {
return postEndDate;
}
public void setPostEndDate(String postEndDate) {
this.postEndDate = postEndDate;
}
public int getViewCnt() {
return viewCnt;
}
public void setViewCnt(int viewCnt) {
this.viewCnt = viewCnt;
}
public String getBdAttachFileId() {
return bdAttachFileId;
}
public void setBdAttachFileId(String bdAttachFileId) {
this.bdAttachFileId = bdAttachFileId;
}
public String getRegId() {
return regId;
}
public void setRegId(String regId) {
this.regId = regId;
}
public String getRegDd() {
return regDd;
}
public void setRegDd(String regDd) {
this.regDd = regDd;
}
public String getModId() {
return modId;
}
public void setModId(String modId) {
this.modId = modId;
}
public String getModDd() {
return modDd;
}
public void setModDd(String modDd) {
this.modDd = modDd;
}
public String getRegNm() {
return regNm;
}
public String getRegNmSec() {
if(StringUtil.isEmpty(regNm)) return null;
return StringUtil.maskName(regNm);
}
public void setRegNm(String regNm) {
this.regNm = regNm;
}
public int getRno() {
return rno;
}
public void setRno(int rno) {
this.rno = rno;
}
public String getSearchType() {
return searchType;
}
public void setSearchType(String searchType) {
this.searchType = searchType;
}
public String getSearchKeyword() {
return searchKeyword;
}
public String getEscapeSearchKeyword() {
return StringUtil.getSqlSearchKeyword(searchKeyword);
}
public void setSearchKeyword(String searchKeyword) {
this.searchKeyword = searchKeyword;
}
public String getAttachYn() {
return attachYn;
}
public void setAttachYn(String attachYn) {
this.attachYn = attachYn;
}
public String getMngOrgNm() {
return mngOrgNm;
}
public void setMngOrgNm(String mngOrgNm) {
this.mngOrgNm = mngOrgNm;
}
public String getBdTypeName() {
return bdTypeName;
}
public void setBdTypeName(String bdTypeName) {
this.bdTypeName = bdTypeName;
}
public List<AttachFileVO> getAttachFiles() {
return attachFiles;
}
public void setAttachFiles(List<AttachFileVO> attachFiles) {
if(attachFiles == null || attachFiles.size() < 1) this.attachFiles = null;
this.attachFiles = attachFiles;
}
public String getAnswerYn() {
return StringUtil.isEmpty(answerYn) ? "N" : answerYn;
}
public void setAnswerYn(String answerYn) {
this.answerYn = answerYn;
}
public String getUseYn() {
return useYn;
}
public void setUseYn(String useYn) {
this.useYn = useYn;
}
public String getQuestionType() {
return questionType;
}
public void setQuestionType(String questionType) {
this.questionType = questionType;
}
public String getQuestionTypeName() {
return questionTypeName;
}
public void setQuestionTypeName(String questionTypeName) {
this.questionTypeName = questionTypeName;
}
public String getAnswer() {
return answer;
}
public void setAnswer(String answer) {
this.answer = answer;
}
public String getSearchQuestionType() {
return searchQuestionType;
}
public void setSearchQuestionType(String searchQuestionType) {
this.searchQuestionType = searchQuestionType;
}
public String getSearchMbInfoId() {
return searchMbInfoId;
}
public void setSearchMbInfoId(String searchMbInfoId) {
// 쿼리 조건으로 사용되는 항목에 대하여 SQL 인젝션으로 처리될만한 문자 강체 치환 처리
this.searchMbInfoId = StringUtil.getValidCodeString(searchMbInfoId);
}
public String getSecretYn() {
return secretYn;
}
public void setSecretYn(String secretYn) {
this.secretYn = secretYn;
}
public String getLoginedMbInfoId() {
return loginedMbInfoId;
}
public void setLoginedMbInfoId(String loginedMbInfoId) {
// 쿼리 조건으로 사용되는 항목에 대하여 SQL 인젝션으로 처리될만한 문자 강체 치환 처리
this.loginedMbInfoId = StringUtil.getValidCodeString(loginedMbInfoId);
}
public String getMyQnaYn() {
return StringUtil.isEmpty(myQnaYn) ? "N" : myQnaYn;
}
public void setMyQnaYn(String myQnaYn) {
this.myQnaYn = myQnaYn;
}
public String getLoginedName() {
return loginedName;
}
public void setLoginedName(String loginedName) {
this.loginedName = loginedName;
}
public String getEmailRecvYn() {
return StringUtil.isEmpty(emailRecvYn) ? "N" : emailRecvYn;
}
public void setEmailRecvYn(String emailRecvYn) {
this.emailRecvYn = emailRecvYn;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getResultMessage() {
return resultMessage;
}
public void setResultMessage(String resultMessage) {
this.resultMessage = resultMessage;
}
public String getResultCode() {
return resultCode;
}
public void setResultCode(String resultCode) {
this.resultCode = resultCode;
}
public List<AttachFileVO> getRemovedAttachFiles() {
return removedAttachFiles;
}
public void setRemovedAttachFiles(List<AttachFileVO> removedAttachFiles) {
this.removedAttachFiles = removedAttachFiles;
}
}
package nlib.bbs.service;
import java.util.List;
import org.apache.commons.lang.StringEscapeUtils;
import com.fasterxml.jackson.annotation.JsonIgnore;
import nlib.cmm.service.PagingVO;
import nlib.util.StringUtil;
/**
* <pre>
* @Class Name : ArticleVO.java
*
* @Description : 게시물 공통 VO
*
*
* @프로젝트명: 지방문화원 통합자료관리시스템 구축사업 (2021)
*
* </pre>
*
* @ ------------ -------- ---------------------------
* @ 수정일 수정자 수정내용
* @ ------------ -------- ---------------------------
* @ 2021. 09. 13. KNKIM 최초 생성
*
*
* @author 이씨플라자 * DIGITALSHIP KNKIM
* @since 2021. 09. 13.
* @version 1.0
*
*/
public class ArticleVO extends PagingVO {
private String articleId; /* 게시글ID */
private String mngOrgCd; /* 관리문화원코드 */
private String mngOrgNm; /* 관리문화원명 */
@JsonIgnore
private String bdType; /* 게시판유형 */
@JsonIgnore
private String bdTypeName; /* 게시판유형명 */
private String title; /* 제목 */
private String content; /* 내용 */
// 공지사항관련
private String notiYn; /* 공지여부 */
private String openYn; /* 공개여부 */
@JsonIgnore
private String postStartDate; /* 게시시작일자 */
@JsonIgnore
private String postEndDate; /* 게시종료일자 */
// FAQ, QNA관련
private String answer; /* 답변 */
private String answerYn; /* 답변여부 */
@JsonIgnore
private String useYn; /* 사용여부 */
private String questionType; /* 질문유형 */
private String questionTypeName; /* 질문유형명 */
private String secretYn; /* 비밀글여부 */
private String emailRecvYn; /* 답변이미엘수신여부 */
private String email; /* 이메일 */
private int viewCnt; /* 조회수 */
private String bdAttachFileId; /* 첨부파일아이디 */
private String attachYn; /* 첨부파일존재여부 */
private List<AttachFileVO> attachFiles; /* 첨부파일목록 */
private List<AttachFileVO> removedAttachFiles; /* 삭제첨부파일목록 */
@JsonIgnore
private String regId; /* 등록자아이디 */
private String regNm; /* 등록자명 */
private String regDd; /* 등록일자 */
@JsonIgnore
private String modId; /* 등록자아이디 */
private String modDd; /* 등록일자 */
private int rno; /* 글번호 */
@JsonIgnore
private String loginedMbInfoId; /* 로그인 사용자 ID */
private String loginedName; /* 로그인 사용자명 */
private String myQnaYn; /* 내가한 질문인지 여부 */
// 검색관련
private String searchType; /* 검색대상구분 */
private String searchKeyword; /* 검색어 */
private String searchQuestionType; /* 검색FAQ유형 */
@JsonIgnore
private String searchMbInfoId; /* 로그인한 사용자ID */
// 처리결과 관련
private String resultMessage = null; /* 처리결과메시지 */
private String resultCode = null; /* 처리결과코드 */
public int getAttachFileCnt() {
if(attachFiles == null) return 0;
return attachFiles.size();
}
public String getSanitizedContent() {
return StringUtil.sanitizeHtml(content);
}
// SETTER & GETTER
public String getArticleId() {
return articleId;
}
public void setArticleId(String articleId) {
// 쿼리 조건으로 사용되는 항목에 대하여 SQL 인젝션으로 처리될만한 문자 강체 치환 처리
this.articleId = StringUtil.getValidCodeString(articleId);
}
public String getMngOrgCd() {
return mngOrgCd;
}
public void setMngOrgCd(String mngOrgCd) {
// 쿼리 조건으로 사용되는 항목에 대하여 SQL 인젝션으로 처리될만한 문자 강체 치환 처리
this.mngOrgCd = StringUtil.getValidCodeString(mngOrgCd);
}
public String getBdType() {
return bdType;
}
public void setBdType(String bdType) {
this.bdType = bdType;
}
public String getUnescapeTitle() {
return StringEscapeUtils.unescapeHtml(title);
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
/*
* 통합자료관관리시스템과 동일하게 게시글 저장 처리를 수행하며,
* 제목의 경우, DB에 일부 특수문자에 대하여 치환되어 저장되고,
* 내용의 경우, HTML 요청된 그대로 저장하되 표출할 HTML Sanitizing하여 표출토록 처리하기로 협의됨에 따라
* 제목 설정시 치환되어 저장토록 (2021.12.21, 이규모차장님)
*/
this.title = StringUtil.getRemovedQuotesStr(title);
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getNotiYn() {
return notiYn;
}
public void setNotiYn(String notiYn) {
this.notiYn = notiYn;
}
public String getOpenYn() {
return openYn;
}
public void setOpenYn(String openYn) {
this.openYn = openYn;
}
public String getPostStartDate() {
return postStartDate;
}
public void setPostStartDate(String postStartDate) {
this.postStartDate = postStartDate;
}
public String getPostEndDate() {
return postEndDate;
}
public void setPostEndDate(String postEndDate) {
this.postEndDate = postEndDate;
}
public int getViewCnt() {
return viewCnt;
}
public void setViewCnt(int viewCnt) {
this.viewCnt = viewCnt;
}
public String getBdAttachFileId() {
return bdAttachFileId;
}
public void setBdAttachFileId(String bdAttachFileId) {
this.bdAttachFileId = bdAttachFileId;
}
public String getRegId() {
return regId;
}
public void setRegId(String regId) {
this.regId = regId;
}
public String getRegDd() {
return regDd;
}
public void setRegDd(String regDd) {
this.regDd = regDd;
}
public String getModId() {
return modId;
}
public void setModId(String modId) {
this.modId = modId;
}
public String getModDd() {
return modDd;
}
public void setModDd(String modDd) {
this.modDd = modDd;
}
public String getRegNm() {
return regNm;
}
public String getRegNmSec() {
if(StringUtil.isEmpty(regNm)) return null;
return StringUtil.maskName(regNm);
}
public void setRegNm(String regNm) {
this.regNm = regNm;
}
public int getRno() {
return rno;
}
public void setRno(int rno) {
this.rno = rno;
}
public String getSearchType() {
return searchType;
}
public void setSearchType(String searchType) {
this.searchType = searchType;
}
public String getSearchKeyword() {
return searchKeyword;
}
public String getEscapeSearchKeyword() {
return StringUtil.getSqlSearchKeyword(searchKeyword);
}
public void setSearchKeyword(String searchKeyword) {
this.searchKeyword = searchKeyword;
}
public String getAttachYn() {
return attachYn;
}
public void setAttachYn(String attachYn) {
this.attachYn = attachYn;
}
public String getMngOrgNm() {
return mngOrgNm;
}
public void setMngOrgNm(String mngOrgNm) {
this.mngOrgNm = mngOrgNm;
}
public String getBdTypeName() {
return bdTypeName;
}
public void setBdTypeName(String bdTypeName) {
this.bdTypeName = bdTypeName;
}
public List<AttachFileVO> getAttachFiles() {
return attachFiles;
}
public void setAttachFiles(List<AttachFileVO> attachFiles) {
if(attachFiles == null || attachFiles.size() < 1) this.attachFiles = null;
this.attachFiles = attachFiles;
}
public String getAnswerYn() {
return StringUtil.isEmpty(answerYn) ? "N" : answerYn;
}
public void setAnswerYn(String answerYn) {
this.answerYn = answerYn;
}
public String getUseYn() {
return useYn;
}
public void setUseYn(String useYn) {
this.useYn = useYn;
}
public String getQuestionType() {
return questionType;
}
public void setQuestionType(String questionType) {
this.questionType = questionType;
}
public String getQuestionTypeName() {
return questionTypeName;
}
public void setQuestionTypeName(String questionTypeName) {
this.questionTypeName = questionTypeName;
}
public String getAnswer() {
return answer;
}
public String getSanitizedAnswer() {
return StringUtil.sanitizeHtml(answer);
}
public void setAnswer(String answer) {
this.answer = answer;
}
public String getSearchQuestionType() {
return searchQuestionType;
}
public void setSearchQuestionType(String searchQuestionType) {
this.searchQuestionType = searchQuestionType;
}
public String getSearchMbInfoId() {
return searchMbInfoId;
}
public void setSearchMbInfoId(String searchMbInfoId) {
// 쿼리 조건으로 사용되는 항목에 대하여 SQL 인젝션으로 처리될만한 문자 강체 치환 처리
this.searchMbInfoId = StringUtil.getValidCodeString(searchMbInfoId);
}
public String getSecretYn() {
return secretYn;
}
public void setSecretYn(String secretYn) {
this.secretYn = secretYn;
}
public String getLoginedMbInfoId() {
return loginedMbInfoId;
}
public void setLoginedMbInfoId(String loginedMbInfoId) {
// 쿼리 조건으로 사용되는 항목에 대하여 SQL 인젝션으로 처리될만한 문자 강체 치환 처리
this.loginedMbInfoId = StringUtil.getValidCodeString(loginedMbInfoId);
}
public String getMyQnaYn() {
return StringUtil.isEmpty(myQnaYn) ? "N" : myQnaYn;
}
public void setMyQnaYn(String myQnaYn) {
this.myQnaYn = myQnaYn;
}
public String getLoginedName() {
return loginedName;
}
public void setLoginedName(String loginedName) {
this.loginedName = loginedName;
}
public String getEmailRecvYn() {
return StringUtil.isEmpty(emailRecvYn) ? "N" : emailRecvYn;
}
public void setEmailRecvYn(String emailRecvYn) {
this.emailRecvYn = emailRecvYn;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getResultMessage() {
return resultMessage;
}
public void setResultMessage(String resultMessage) {
this.resultMessage = resultMessage;
}
public String getResultCode() {
return resultCode;
}
public void setResultCode(String resultCode) {
this.resultCode = resultCode;
}
public List<AttachFileVO> getRemovedAttachFiles() {
return removedAttachFiles;
}
public void setRemovedAttachFiles(List<AttachFileVO> removedAttachFiles) {
this.removedAttachFiles = removedAttachFiles;
}
}

View File

@ -1,152 +1,158 @@
package nlib.bbs.web;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import nlib.bbs.service.ArticleVO;
import nlib.bbs.service.BoardService;
import nlib.cmm.NlibCommonController;
import nlib.cmm.service.CodeService;
import nlib.cmm.service.NlibProperty;
import nlib.cmm.service.PagingVO;
/**
* <pre>
* @Class Name : FaqController.java
*
* @Description : FAQ 컨트롤러 클래스
*
*
* @프로젝트명: 지방문화원 통합자료관리시스템 구축사업 (2021)
*
* </pre>
*
* @ ------------ -------- ---------------------------
* @ 수정일 수정자 수정내용
* @ ------------ -------- ---------------------------
* @ 2021. 09. 13. KNKIM 최초 생성
*
*
* @author 이씨플라자 * DIGITALSHIP KNKIM
* @since 2021. 09. 13.
* @version 1.0
*
*/
@Controller
public class FaqController extends NlibCommonController {
private static final Logger log = LoggerFactory.getLogger(FaqController.class);
static final int DEFUALT_PAGE_SIZE = NlibProperty.getInt("list.paging.page.size", 10);
@Resource(name = "faqService")
private BoardService faqService;
@Resource(name="codeService")
private CodeService codeService;
/**
* 목록 화면을 표시한다.
*
* @param req
* @return
*/
@RequestMapping("/bbs/listFaqs.do")
public String listFaqs(
HttpServletRequest req,
@RequestParam Map<String, String> paramMap,
ArticleVO searchArticleVO,
ModelMap model
) throws Exception {
List<Map<String, String>> questionTypeList = codeService.listCodes("NLIB_FAQ_TYPE_CD");
model.addAttribute("questionTypeList", questionTypeList);
model.addAttribute("searchArticle", searchArticleVO);
return "nlib/bbs/listFaqs";
}
/**
* 목록 조회한다.
*
* @param req
* @return
*/
@RequestMapping(value="/bbs/listFaqsAjax.do")
public ResponseEntity<String> listFaqsAjax(
HttpServletRequest req,
Authentication authentication,
@RequestBody ArticleVO searchArticleVO) throws Exception {
if(searchArticleVO.getPageIndex() < 1) searchArticleVO.setPageIndex(1);
if(searchArticleVO.getPageSize() < 1) searchArticleVO.setPageSize(DEFUALT_PAGE_SIZE);
searchArticleVO.setMngOrgCd(getCurCouncilCd(req));
List<ArticleVO> list = faqService.listArticles(searchArticleVO);
int totRecordCount = faqService.countArticles(searchArticleVO);
//-------------------------------
// JSON변환 응답 처리
//-------------------------------
// JS-GRID 페이징 처리를 포함한 응답값 처리
// {data: [{...}],
// itemsCount: 255
// }
HashMap<String, Object> retMap = new HashMap<String, Object>();
retMap.put("data", list);
retMap.put("itemsCount", totRecordCount);
PagingVO pageVO = new PagingVO();
pageVO.setPagingVO(totRecordCount, searchArticleVO.getPageIndex(), searchArticleVO.getPageSize());
retMap.put("pagingPageIndex" , pageVO.getPageIndex());
retMap.put("pagingTotRecordCount", pageVO.getTotRecordCount());
retMap.put("pagingStartPage" , pageVO.getStartPage());
retMap.put("pagingEndPage" , pageVO.getEndPage());
retMap.put("pagingLastPage" , pageVO.getLastPage());
return makeResponseEntityJson(retMap);
}
/**
* 알림 상세 내용 조회한다.
*
* @param req
* @return
*/
@RequestMapping("/bbs/selectFaqArticleAjax.do")
public ResponseEntity<String> selectFaqArticleAjax(HttpServletRequest req, Authentication authentication, @RequestBody ArticleVO searchArticleVO) throws Exception {
// 읽음처리 상세내용 조회
ArticleVO articleVO = faqService.selectArticle(searchArticleVO);
//-------------------------------
// JSON변환 응답 처리
//-------------------------------
// JS-GRID 페이징 처리를 포함한 응답값 처리
// {data: [{...}],
// itemsCount: 255
// }
HashMap<String, Object> retMap = new HashMap<String, Object>();
retMap.put("data", articleVO);
return makeResponseEntityJson(retMap);
}
package nlib.bbs.web;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import nlib.bbs.service.ArticleVO;
import nlib.bbs.service.BoardService;
import nlib.cmm.NlibCommonController;
import nlib.cmm.service.CodeService;
import nlib.cmm.service.NlibProperty;
import nlib.cmm.service.PagingVO;
/**
* <pre>
* @Class Name : FaqController.java
*
* @Description : FAQ 컨트롤러 클래스
*
*
* @프로젝트명: 지방문화원 통합자료관리시스템 구축사업 (2021)
*
* </pre>
*
* @ ------------ -------- ---------------------------
* @ 수정일 수정자 수정내용
* @ ------------ -------- ---------------------------
* @ 2021. 09. 13. KNKIM 최초 생성
*
*
* @author 이씨플라자 * DIGITALSHIP KNKIM
* @since 2021. 09. 13.
* @version 1.0
*
*/
@Controller
public class FaqController extends NlibCommonController {
private static final Logger log = LoggerFactory.getLogger(FaqController.class);
static final int DEFUALT_PAGE_SIZE = NlibProperty.getInt("list.paging.page.size", 10);
@Resource(name = "faqService")
private BoardService faqService;
@Resource(name="codeService")
private CodeService codeService;
/**
* 목록 화면을 표시한다.
*
* @param req
* @return
*/
@RequestMapping("/bbs/listFaqs.do")
public String listFaqs(
HttpServletRequest req,
@RequestParam Map<String, String> paramMap,
ArticleVO searchArticleVO,
ModelMap model
) throws Exception {
List<Map<String, String>> questionTypeList = codeService.listCodes("NLIB_FAQ_TYPE_CD");
model.addAttribute("questionTypeList", questionTypeList);
model.addAttribute("searchArticle", searchArticleVO);
return "nlib/bbs/listFaqs";
}
/**
* 목록 조회한다.
*
* @param req
* @return
*/
@RequestMapping(value="/bbs/listFaqsAjax.do")
public ResponseEntity<String> listFaqsAjax(
HttpServletRequest req,
Authentication authentication,
@RequestBody ArticleVO searchArticleVO) throws Exception {
if(searchArticleVO.getPageIndex() < 1) searchArticleVO.setPageIndex(1);
if(searchArticleVO.getPageSize() < 1) searchArticleVO.setPageSize(DEFUALT_PAGE_SIZE);
searchArticleVO.setMngOrgCd(getCurCouncilCd(req));
List<ArticleVO> list = faqService.listArticles(searchArticleVO);
int totRecordCount = faqService.countArticles(searchArticleVO);
//-------------------------------
// JSON변환 응답 처리
//-------------------------------
// JS-GRID 페이징 처리를 포함한 응답값 처리
// {data: [{...}],
// itemsCount: 255
// }
HashMap<String, Object> retMap = new HashMap<String, Object>();
retMap.put("data", list);
retMap.put("itemsCount", totRecordCount);
PagingVO pageVO = new PagingVO();
pageVO.setPagingVO(totRecordCount, searchArticleVO.getPageIndex(), searchArticleVO.getPageSize());
retMap.put("pagingPageIndex" , pageVO.getPageIndex());
retMap.put("pagingTotRecordCount", pageVO.getTotRecordCount());
retMap.put("pagingStartPage" , pageVO.getStartPage());
retMap.put("pagingEndPage" , pageVO.getEndPage());
retMap.put("pagingLastPage" , pageVO.getLastPage());
return makeResponseEntityJson(retMap);
}
/**
* 알림 상세 내용 조회한다.
*
* @param req
* @return
*/
@RequestMapping("/bbs/selectFaqArticleAjax.do")
public ResponseEntity<String> selectFaqArticleAjax(HttpServletRequest req, Authentication authentication, @RequestBody ArticleVO searchArticleVO) throws Exception {
// 읽음처리 상세내용 조회
ArticleVO articleVO = faqService.selectArticle(searchArticleVO);
// AJAX OUT에 XSS처리되지 않은 HTML 전송 차단위한 변경 처리
if(articleVO != null) {
articleVO.setAnswer(articleVO.getSanitizedAnswer());
articleVO.setContent(articleVO.getSanitizedContent());
}
//-------------------------------
// JSON변환 응답 처리
//-------------------------------
// JS-GRID 페이징 처리를 포함한 응답값 처리
// {data: [{...}],
// itemsCount: 255
// }
HashMap<String, Object> retMap = new HashMap<String, Object>();
retMap.put("data", articleVO);
return makeResponseEntityJson(retMap);
}
}

View File

@ -273,6 +273,45 @@ public class StringUtil extends StringUtils {
return str;
}
/**
* 게시글 제목 허용되지 말아야하는 일부 특수문자에 대한 치환 처리하여 리턴
*
* @param val
* @return
*/
public static String getRemovedQuotesStr(String val) {
if (isEmpty(val)) return val;
StringBuffer strBuff = new StringBuffer();
for (int j = 0; j < val.length(); j++) {
char c = val.charAt(j);
switch (c) {
case '<':
strBuff.append("");
break;
case '>':
strBuff.append("");
break;
case '&':
strBuff.append("");
break;
case '"':
strBuff.append("");
break;
case '\'':
strBuff.append("");
break;
default:
strBuff.append(c);
break;
}
}
return strBuff.toString();
}
/**
* 날짜 문자열(YYYYMMDD) 받아서 화면 출력용 날짜 형식 문자열(YYYY-MM-DD) 리턴한다.
*
@ -391,7 +430,9 @@ public class StringUtil extends StringUtils {
* @param html
* @return
*/
private static String sanitizeHtml(String html) {
public static String sanitizeHtml(String html) {
if(isEmpty(html)) return html;
PolicyFactory policy = new HtmlPolicyBuilder()
.allowAttributes("src", "align", "title").onElements("img")

View File

@ -1,394 +1,394 @@
<%
/**
* <pre>
* @Class Name : insertQnaForm.jsp
*
* @Description : 묻고답하기 등록화면을 표출한다.
*
*
* @프로젝트명: 지방문화원 통합자료관리시스템 구축사업 (2021)
*
* </pre>
*
* @ ------------ -------- ---------------------------
* @ 수정일 수정자 수정내용
* @ ------------ -------- ---------------------------
* @ 2021. 7. 13. KNKIM 최초 생성
*
*
* @author 이씨플라자 * DIGITALSHIP KNKIM
* @since 2021. 7. 13.
* @version 1.0
*
*/
%>
<%@ page language="java" contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="ui" uri="http://egovframework.gov/ctl/ui" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="validator" uri="http://www.springmodules.org/tags/commons-validator" %>
<c:set var="pageTitle">Q&A</c:set>
<c:if test='${searchArticle.articleId != null and searchArticle.articleId != ""}'>
<c:set var="pageTitleSub">수정</c:set>
</c:if>
<c:if test='${searchArticle.articleId == null or searchArticle.articleId == ""}'>
<c:set var="pageTitleSub">등록</c:set>
</c:if>
<!-- File Upload : 시작-->
<script src="<c:out value='${pageContext.request.contextPath}' />/js/fileupload/dropzone-nlib.js"></script>
<!-- File Upload : 종료 -->
<!-- SmartEditor : 시작 -->
<script src="<c:out value='${pageContext.request.contextPath}' />/js/smartEditor/js/service/HuskyEZCreator.js"></script>
<!-- SmartEditor : 종료 -->
<script>
var myDropzone = null; <% // 파일드롭다운 객체 %>
var fileList = new Array(); <% // 업로드된 파일 정보 %>
var delFileList = new Array(); <% // 삭제된 파일 정보 %>
var IN_PROC = false; <% // 현재 상태가 업로드처리중에 있는지 여부 %>
var UPLOADING_FILE_CNT = 0; <% // 업로드할 파일 수 %>
var isModified = false;
var oEditors = [];
$(window.document).ready(function() {
fn_setPageTitle("<c:out value='${pageTitle}' escapeXml='false' />");
if(!gfn_isEmpty('<c:out value="${message}"/>')) {
alert("<c:out value='${message}'/>");
}
/**
* 스마트 에디터
*/
nhn.husky.EZCreator.createInIFrame({
oAppRef: oEditors,
elPlaceHolder: "content",
sSkinURI: "<c:out value='${pageContext.request.contextPath}' />/js/smartEditor/SmartEditor2Skin.html",
fCreator: "createSEditor2"
});
//--------------------------------------------------------------------
// 파일 업로드 처리 : 시작
//--------------------------------------------------------------------
// 파일업로드 객체 생성
myDropzone = new Dropzone("form#myDropzone", {
url: "<c:out value='${pageContext.request.contextPath}' />/fileupload/uploadFilesAjax.do?subPathKey=fileupload.bbs.qna.subpath&attachFileId=<c:out value='${searchArticle.bdAttachFileId}'/>",
maxFilesize: <c:out value="${maxFilesize}" />,
maxFiles: <c:out value="${maxFiles}" />,
parallelUploads: <c:out value="${maxFiles}" />,
acceptedFiles: "<c:out value="${acceptedFiles}" />"
});
// 기존 첨부파일 출력
<c:if test="${searchArticle.attachFileCnt > 0}">
var re = /(?:\.([^.]+))?$/;
<c:forEach var="attachFile" items="${searchArticle.attachFiles }" varStatus="status">
myDropzone.addFile({
name: '<c:out value="${attachFile.orignlFileNm}" />',
size: <c:out value="${attachFile.fileSize}" />,
type:re.exec('<c:out value="${attachFile.orignlFileNm}" />')[1],
attachFileId: '<c:out value="${attachFile.attachFileId}" />',
streFileNm: '<c:out value="${attachFile.streFileNm}" />',
fileSn: '<c:out value="${attachFile.fileSn}" />',
fileUploadType: 'uploaded'
});
</c:forEach>
</c:if>
// 각 파일별 업로드 성공시 호출됨 (1th called)
myDropzone.on("success", function(file, responseText) {
var jobj = JSON.parse(responseText);
//console.log("file upload success : responseText = " + responseText);
//console.log("fileUpDone count : " + fileList.length);
if(jobj.length < 1) {
alert("[" + file.name + "] 첨부파일 업로드 처리가 취소되었습니다. \n\n다음에 해당하는 경우, 업로드가 취소됩니다.\n- 최대 파일수 초과 \n- 파일용량 초과 \n- 첨부가능한 파일 종류가 아닌 경우");
return;
}
var idx = fileList.length;
fileList[idx++] = {
"attachFileId" : jobj[0].attachFileId,
"streFileNm" : jobj[0].streFileNm,
"orignlFileNm" : jobj[0].orignlFileNm,
"fileExt" : jobj[0].fileExt,
"fileSn" : jobj[0].fileSn,
"fileSize" : jobj[0].fileSize
};
console.log("success : " + file.name + " -> " + "data > " + jobj[0].streFileNm);
});
myDropzone.on("error", function(file, message) {
myDropzone.removeFile(file);
alert(message);
});
myDropzone.on("removedfile", function(file) {
var idx = delFileList.length;
if(!gfn_isEmpty(file.streFileNm) && !gfn_isEmpty(file.fileSn)) {
delFileList[idx++] = {
"attachFileId" : file.attachFileId,
"streFileNm" : file.streFileNm,
"fileSn" : file.fileSn
};
}
});
// 각 파일별 업로드 처리 완료 시 호출됨 (2th called)
myDropzone.on("complete", function(file) {
//alert("개별 파일 처리 완료 > file = " + JSON.stringify(file));
});
// 모든 업로드 처리 완료 시 호출됨
myDropzone.on("queuecomplete", function() {
var uploadCnt = fileList.length;
if(uploadCnt > 0) {
$("#fileList").val(JSON.stringify(JSON.stringify(fileList)));
}
if(IN_PROC) {
if(UPLOADING_FILE_CNT > 0 && UPLOADING_FILE_CNT != uploadCnt) {
alert("일부 첨부파일 등록이 실패하였습니다.\n총 " + UPLOADING_FILE_CNT + "건의 업로드할 첨부파일 중 " + uploadCnt + "건이 정상적으로 업로드 되었습니다.\n글 등록(수정)을 계속 진행합니다.");
}
fn_saveSubmit();
}
});
//--------------------------------------------------------------------
// 파일 업로드 처리 : 종료
//--------------------------------------------------------------------
});
function fn_getContent() {
oEditors.getById["content"].exec("UPDATE_CONTENTS_FIELD");
}
// 목록으로 이동
function fn_list() {
if(!gfn_isEmpty($("#articleForm #title").val()) || !gfn_isEmpty($("#articleForm #content").val())) {
if(!confirm("작성중인 글이 있습니다. 취소하시겠습니까?")) return;
}
$('#articleForm').removeAttr('onsubmit');
$("#articleForm").attr("action", "<c:out value='${pageContext.request.contextPath}' />/bbs/listQnas.do");
$("#articleForm").submit();
}
var MAX_LEN_OF_CONTENT = 65000; // 내용 최대 길이
//등록버튼 클릭
function fn_pressSaveBtn() {
$("#articleForm").submit();
}
// 등록 처리
function fn_save() {
// 스마트에디터의 내용 적용
fn_getContent();
// 내용 작성 유무
if(gfn_isEmpty($("#title").val())) {
alert("제목을 입력하여 주시기 바랍니다.");
$("#title").focus();
return false;
}
// 내용 작성 유무
var ctext = gfn_extractContentText($("#content").val());
if(ctext.trim() == "") {
alert("내용을 입력하여 주시기 바랍니다.");
oEditors.getById["content"].exec("FOCUS");
return false;
}
// 내용 길이 체크
var contentLen = gfn_lengthBytes($("#content").val());
if(contentLen > MAX_LEN_OF_CONTENT) {
alert("내용의 길이는 " + MAX_LEN_OF_CONTENT + " bytes를 초과할 수 없습니다.");
oEditors.getById["content"].exec("FOCUS");
return false;
}
if(!confirm("저장하시겠습니까?")) return false;
<c:if test='${searchArticle.articleId != null and searchArticle.articleId != ""}'>
// 수정
$("#articleForm").attr("action", "${pageContext.request.contextPath}/bbs/updateQnaArticle.do");
</c:if>
<c:if test='${searchArticle.articleId == null or searchArticle.articleId == ""}'>
// 등록
$("#articleForm").attr("action", "${pageContext.request.contextPath}/bbs/insertQnaArticle.do");
</c:if>
IN_PROC = true;
$("#delFileList").val(JSON.stringify(JSON.stringify(delFileList)));
// 파일 업로드 처리
UPLOADING_FILE_CNT = myDropzone.countAcceptedUploadingFiles();
if(UPLOADING_FILE_CNT > 0) {
myDropzone.processQueue();
return false;
}
return true;
}
//등록 처리
function fn_saveSubmit() {
$('#articleForm').removeAttr('onsubmit');
$("#articleForm").submit();
}
//파일 다운로드
function fn_downloadFile(attachFileId, fileSn) {
if(attachFileId == null || attachFileId.trim().length < 1) {
alert("파일정보를 확인해 주시기 바랍니다.[1]");
return;
}
if(fileSn < 1) {
alert("파일정보를 확인해 주시기 바랍니다.[3]");
return;
}
$("#attachFileId").val(attachFileId);
$("#fileSn").val(fileSn);
$("#downloadFileForm").submit();
}
</script>
<!-- Q&amp;A 섹션 -->
<div class="location qna-data">
<h2 class="blind">Q&amp;A 섹션영역</h2>
<div class="inner">
<div class="row-top">
<div class="location-box">
<ul class="loc">
<li>HOME</li>
<li>고객지원</li>
<li class="on"><c:out value='${pageTitle }'/></li>
</ul>
<div class="tit">
<h2><span><c:out value='${pageTitle }'/> <c:out value='${pageTitleSub }'/></span></h2>
</div>
</div>
<div class="arr"><img src="/images/icon/icon-join-arr2.png" alt=""></div>
</div>
<div class="contents row-bottom">
<div class="community qna">
<div class="contents-frame inner-center">
<div class="community-write">
<p class="required-text"><span>*</span> 표시는 필수 입력 항목입니다.</p>
<form name="articleForm" id="articleForm" method="post" onsubmit="return fn_save();">
<fieldset>
<legend>Q&amp;A 작성</legend>
<input type="hidden" name="pageIndex" id="pageIndex" title="페이지번호" value="<c:out value='${searchArticle.pageIndex}'/>" />
<input type="hidden" name="pageSize" id="pageSize" title="목록에 보여줄 글개수" value="<c:out value='${searchArticle.pageSize}'/>" />
<input type="hidden" name="searchType" id="searchType" title="검색구분" value="<c:out value='${searchArticle.searchType}'/>" />
<input type="hidden" name="searchKeyword" id="searchKeyword" title="검색어" value="<c:out value='${searchArticle.searchKeyword}'/>" />
<input type="hidden" name="articleId" id="articleId" title="선택글번호" value="<c:out value='${searchArticle.articleId}'/>" />
<input type="hidden" name="fileList" id="fileList" title="첨부파일정보" />
<input type="hidden" name="delFileList" id="delFileList" title="삭제첨부파일정보" />
<input type="hidden" name="bdAttachFileId" id="bdAttachFileId" value="<c:out value='${searchArticle.bdAttachFileId}'/>" />
<div class="qna-write-table">
<ul>
<li class="wt-title fl">
<div class="w-tit">제목 <span class="required">*</span></div>
<div class="w-form">
<input type="text" name="title" id="title" title="제목" value="<c:out value='${searchArticle.title}' escapeXml='false' />" size="100" maxlength="200" class="grid-1" placeholder="제목을 입력하세요." required />
</div>
</li>
<li class="wt-secret fl check">
<div class="w-tit">
<input type="checkbox" name="secretYn" id="secretYn" value="Y"
<c:if test='${searchArticle.secretYn == "Y" }'> checked </c:if> />
<label for="secretYn">비밀글</label>
</div>
</li>
<li class="wt-mail check">
<div class="w-tit">답변메일 수신여부</div>
<div class="w-form">
<input type="checkbox" name="emailRecvYn" id="emailRecvYn" value="Y" <c:if test='${searchArticle.emailRecvYn == "Y" }'> checked </c:if> />
<label for="emailRecvYn"> (답변 수신메일 주소는 내 정보에서 수정하실 수 있습니다.)</label>
</div>
</li>
<li class="wt-question">
<div class="w-tit">내용 <span class="required">*</span></div>
<div class="w-form">
<textarea name="content" id="content" cols="80" rows="10" placeholder="질문하실 내용을 입력하세요." style="width:100%;min-width:260px;"><c:out value='${searchArticle.unescapedContent}' escapeXml='false' /></textarea>
</div>
</li>
</ul>
</div>
</fieldset>
</form>
<div class="qna-write-table">
<ul>
<li class="wt-attachments">
<div class="w-tit">첨부파일</div>
<div id="dropzone" class="w-form">
<form id="myDropzone" name="myDropzone"
action="${pageContext.request.contextPath}/fileupload/uploadFile.do"
class="dropzone needsclick" >
<div class="dz-message needsclick" style="cursor:pointer">
<button type="button" id="attachFileBtn" name="attachFileBtn" class="attachments-btn">파일첨부</button>
<div class="mb">최대 5개까지 (개당 10MB 이내)</div>
<label for="attachFileBtn">※ 이곳에 파일을 끌어다 놓거나(Drag&Drop), 클릭하여 파일을 선택하세요.</label>
</div>
</form>
</div>
</li>
</ul>
</div>
<div class="btn-wrap">
<button type="button" name="btnSave" id="btnSave" title="저장버튼" class="btn btn-color" onclick="fn_pressSaveBtn()">저장</button>
<button type="button" name="btnCancel" id="btnCancel" title="취소버튼" class="btn btn-gray" onclick="fn_list()">취소</button>
</div>
</div>
</div><!-- //contents-frame -->
</div>
</div>
</div>
</div>
<!-- 첨부파일 다운로드용 폼 -->
<form name="downloadFileForm" id="downloadFileForm"
action="${pageContext.request.contextPath}/fileupload/downloadAttachFile.do"
method="post">
<input type="hidden" name="bdType" id="bdType" value="QNA" />
<input type="hidden" name="attachFileId" id="attachFileId" value="" />
<input type="hidden" name="streFileNm" id="streFileNm" value="" />
<input type="hidden" name="fileSn" id="fileSn" value="" />
<input type="hidden" name="orignlFileNm" id="orignlFileNm" value="" />
</form>
<%
/**
* <pre>
* @Class Name : insertQnaForm.jsp
*
* @Description : 묻고답하기 등록화면을 표출한다.
*
*
* @프로젝트명: 지방문화원 통합자료관리시스템 구축사업 (2021)
*
* </pre>
*
* @ ------------ -------- ---------------------------
* @ 수정일 수정자 수정내용
* @ ------------ -------- ---------------------------
* @ 2021. 7. 13. KNKIM 최초 생성
*
*
* @author 이씨플라자 * DIGITALSHIP KNKIM
* @since 2021. 7. 13.
* @version 1.0
*
*/
%>
<%@ page language="java" contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="ui" uri="http://egovframework.gov/ctl/ui" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="validator" uri="http://www.springmodules.org/tags/commons-validator" %>
<c:set var="pageTitle">Q&A</c:set>
<c:if test='${searchArticle.articleId != null and searchArticle.articleId != ""}'>
<c:set var="pageTitleSub">수정</c:set>
</c:if>
<c:if test='${searchArticle.articleId == null or searchArticle.articleId == ""}'>
<c:set var="pageTitleSub">등록</c:set>
</c:if>
<!-- File Upload : 시작-->
<script src="<c:out value='${pageContext.request.contextPath}' />/js/fileupload/dropzone-nlib.js"></script>
<!-- File Upload : 종료 -->
<!-- SmartEditor : 시작 -->
<script src="<c:out value='${pageContext.request.contextPath}' />/js/smartEditor/js/service/HuskyEZCreator.js"></script>
<!-- SmartEditor : 종료 -->
<script>
var myDropzone = null; <% // 파일드롭다운 객체 %>
var fileList = new Array(); <% // 업로드된 파일 정보 %>
var delFileList = new Array(); <% // 삭제된 파일 정보 %>
var IN_PROC = false; <% // 현재 상태가 업로드처리중에 있는지 여부 %>
var UPLOADING_FILE_CNT = 0; <% // 업로드할 파일 수 %>
var isModified = false;
var oEditors = [];
$(window.document).ready(function() {
fn_setPageTitle("<c:out value='${pageTitle}' escapeXml='false' />");
if(!gfn_isEmpty('<c:out value="${message}"/>')) {
alert("<c:out value='${message}'/>");
}
/**
* 스마트 에디터
*/
nhn.husky.EZCreator.createInIFrame({
oAppRef: oEditors,
elPlaceHolder: "content",
sSkinURI: "<c:out value='${pageContext.request.contextPath}' />/js/smartEditor/SmartEditor2Skin.html",
fCreator: "createSEditor2"
});
//--------------------------------------------------------------------
// 파일 업로드 처리 : 시작
//--------------------------------------------------------------------
// 파일업로드 객체 생성
myDropzone = new Dropzone("form#myDropzone", {
url: "<c:out value='${pageContext.request.contextPath}' />/fileupload/uploadFilesAjax.do?subPathKey=fileupload.bbs.qna.subpath&attachFileId=<c:out value='${searchArticle.bdAttachFileId}'/>",
maxFilesize: <c:out value="${maxFilesize}" />,
maxFiles: <c:out value="${maxFiles}" />,
parallelUploads: <c:out value="${maxFiles}" />,
acceptedFiles: "<c:out value="${acceptedFiles}" />"
});
// 기존 첨부파일 출력
<c:if test="${searchArticle.attachFileCnt > 0}">
var re = /(?:\.([^.]+))?$/;
<c:forEach var="attachFile" items="${searchArticle.attachFiles }" varStatus="status">
myDropzone.addFile({
name: '<c:out value="${attachFile.orignlFileNm}" />',
size: <c:out value="${attachFile.fileSize}" />,
type:re.exec('<c:out value="${attachFile.orignlFileNm}" />')[1],
attachFileId: '<c:out value="${attachFile.attachFileId}" />',
streFileNm: '<c:out value="${attachFile.streFileNm}" />',
fileSn: '<c:out value="${attachFile.fileSn}" />',
fileUploadType: 'uploaded'
});
</c:forEach>
</c:if>
// 각 파일별 업로드 성공시 호출됨 (1th called)
myDropzone.on("success", function(file, responseText) {
var jobj = JSON.parse(responseText);
//console.log("file upload success : responseText = " + responseText);
//console.log("fileUpDone count : " + fileList.length);
if(jobj.length < 1) {
alert("[" + file.name + "] 첨부파일 업로드 처리가 취소되었습니다. \n\n다음에 해당하는 경우, 업로드가 취소됩니다.\n- 최대 파일수 초과 \n- 파일용량 초과 \n- 첨부가능한 파일 종류가 아닌 경우");
return;
}
var idx = fileList.length;
fileList[idx++] = {
"attachFileId" : jobj[0].attachFileId,
"streFileNm" : jobj[0].streFileNm,
"orignlFileNm" : jobj[0].orignlFileNm,
"fileExt" : jobj[0].fileExt,
"fileSn" : jobj[0].fileSn,
"fileSize" : jobj[0].fileSize
};
console.log("success : " + file.name + " -> " + "data > " + jobj[0].streFileNm);
});
myDropzone.on("error", function(file, message) {
myDropzone.removeFile(file);
alert(message);
});
myDropzone.on("removedfile", function(file) {
var idx = delFileList.length;
if(!gfn_isEmpty(file.streFileNm) && !gfn_isEmpty(file.fileSn)) {
delFileList[idx++] = {
"attachFileId" : file.attachFileId,
"streFileNm" : file.streFileNm,
"fileSn" : file.fileSn
};
}
});
// 각 파일별 업로드 처리 완료 시 호출됨 (2th called)
myDropzone.on("complete", function(file) {
//alert("개별 파일 처리 완료 > file = " + JSON.stringify(file));
});
// 모든 업로드 처리 완료 시 호출됨
myDropzone.on("queuecomplete", function() {
var uploadCnt = fileList.length;
if(uploadCnt > 0) {
$("#fileList").val(JSON.stringify(JSON.stringify(fileList)));
}
if(IN_PROC) {
if(UPLOADING_FILE_CNT > 0 && UPLOADING_FILE_CNT != uploadCnt) {
alert("일부 첨부파일 등록이 실패하였습니다.\n총 " + UPLOADING_FILE_CNT + "건의 업로드할 첨부파일 중 " + uploadCnt + "건이 정상적으로 업로드 되었습니다.\n글 등록(수정)을 계속 진행합니다.");
}
fn_saveSubmit();
}
});
//--------------------------------------------------------------------
// 파일 업로드 처리 : 종료
//--------------------------------------------------------------------
});
function fn_getContent() {
oEditors.getById["content"].exec("UPDATE_CONTENTS_FIELD");
}
// 목록으로 이동
function fn_list() {
if(!gfn_isEmpty($("#articleForm #title").val()) || !gfn_isEmpty($("#articleForm #content").val())) {
if(!confirm("작성중인 글이 있습니다. 취소하시겠습니까?")) return;
}
$('#articleForm').removeAttr('onsubmit');
$("#articleForm").attr("action", "<c:out value='${pageContext.request.contextPath}' />/bbs/listQnas.do");
$("#articleForm").submit();
}
var MAX_LEN_OF_CONTENT = 65000; // 내용 최대 길이
//등록버튼 클릭
function fn_pressSaveBtn() {
$("#articleForm").submit();
}
// 등록 처리
function fn_save() {
// 스마트에디터의 내용 적용
fn_getContent();
// 내용 작성 유무
if(gfn_isEmpty($("#title").val())) {
alert("제목을 입력하여 주시기 바랍니다.");
$("#title").focus();
return false;
}
// 내용 작성 유무
var ctext = gfn_extractContentText($("#content").val());
if(ctext.trim() == "") {
alert("내용을 입력하여 주시기 바랍니다.");
oEditors.getById["content"].exec("FOCUS");
return false;
}
// 내용 길이 체크
var contentLen = gfn_lengthBytes($("#content").val());
if(contentLen > MAX_LEN_OF_CONTENT) {
alert("내용의 길이는 " + MAX_LEN_OF_CONTENT + " bytes를 초과할 수 없습니다.");
oEditors.getById["content"].exec("FOCUS");
return false;
}
if(!confirm("저장하시겠습니까?")) return false;
<c:if test='${searchArticle.articleId != null and searchArticle.articleId != ""}'>
// 수정
$("#articleForm").attr("action", "${pageContext.request.contextPath}/bbs/updateQnaArticle.do");
</c:if>
<c:if test='${searchArticle.articleId == null or searchArticle.articleId == ""}'>
// 등록
$("#articleForm").attr("action", "${pageContext.request.contextPath}/bbs/insertQnaArticle.do");
</c:if>
IN_PROC = true;
$("#delFileList").val(JSON.stringify(JSON.stringify(delFileList)));
// 파일 업로드 처리
UPLOADING_FILE_CNT = myDropzone.countAcceptedUploadingFiles();
if(UPLOADING_FILE_CNT > 0) {
myDropzone.processQueue();
return false;
}
return true;
}
//등록 처리
function fn_saveSubmit() {
$('#articleForm').removeAttr('onsubmit');
$("#articleForm").submit();
}
//파일 다운로드
function fn_downloadFile(attachFileId, fileSn) {
if(attachFileId == null || attachFileId.trim().length < 1) {
alert("파일정보를 확인해 주시기 바랍니다.[1]");
return;
}
if(fileSn < 1) {
alert("파일정보를 확인해 주시기 바랍니다.[3]");
return;
}
$("#attachFileId").val(attachFileId);
$("#fileSn").val(fileSn);
$("#downloadFileForm").submit();
}
</script>
<!-- Q&amp;A 섹션 -->
<div class="location qna-data">
<h2 class="blind">Q&amp;A 섹션영역</h2>
<div class="inner">
<div class="row-top">
<div class="location-box">
<ul class="loc">
<li>HOME</li>
<li>고객지원</li>
<li class="on"><c:out value='${pageTitle }'/></li>
</ul>
<div class="tit">
<h2><span><c:out value='${pageTitle }'/> <c:out value='${pageTitleSub }'/></span></h2>
</div>
</div>
<div class="arr"><img src="/images/icon/icon-join-arr2.png" alt=""></div>
</div>
<div class="contents row-bottom">
<div class="community qna">
<div class="contents-frame inner-center">
<div class="community-write">
<p class="required-text"><span>*</span> 표시는 필수 입력 항목입니다.</p>
<form name="articleForm" id="articleForm" method="post" onsubmit="return fn_save();">
<fieldset>
<legend>Q&amp;A 작성</legend>
<input type="hidden" name="pageIndex" id="pageIndex" title="페이지번호" value="<c:out value='${searchArticle.pageIndex}'/>" />
<input type="hidden" name="pageSize" id="pageSize" title="목록에 보여줄 글개수" value="<c:out value='${searchArticle.pageSize}'/>" />
<input type="hidden" name="searchType" id="searchType" title="검색구분" value="<c:out value='${searchArticle.searchType}'/>" />
<input type="hidden" name="searchKeyword" id="searchKeyword" title="검색어" value="<c:out value='${searchArticle.searchKeyword}'/>" />
<input type="hidden" name="articleId" id="articleId" title="선택글번호" value="<c:out value='${searchArticle.articleId}'/>" />
<input type="hidden" name="fileList" id="fileList" title="첨부파일정보" />
<input type="hidden" name="delFileList" id="delFileList" title="삭제첨부파일정보" />
<input type="hidden" name="bdAttachFileId" id="bdAttachFileId" value="<c:out value='${searchArticle.bdAttachFileId}'/>" />
<div class="qna-write-table">
<ul>
<li class="wt-title fl">
<div class="w-tit">제목 <span class="required">*</span></div>
<div class="w-form">
<input type="text" name="title" id="title" title="제목" value="<c:out value='${searchArticle.title}' escapeXml='false' />" size="100" maxlength="200" class="grid-1" placeholder="제목을 입력하세요." required />
</div>
</li>
<li class="wt-secret fl check">
<div class="w-tit">
<input type="checkbox" name="secretYn" id="secretYn" value="Y"
<c:if test='${searchArticle.secretYn == "Y" }'> checked </c:if> />
<label for="secretYn">비밀글</label>
</div>
</li>
<li class="wt-mail check">
<div class="w-tit">답변메일 수신여부</div>
<div class="w-form">
<input type="checkbox" name="emailRecvYn" id="emailRecvYn" value="Y" <c:if test='${searchArticle.emailRecvYn == "Y" }'> checked </c:if> />
<label for="emailRecvYn"> (답변 수신메일 주소는 내 정보에서 수정하실 수 있습니다.)</label>
</div>
</li>
<li class="wt-question">
<div class="w-tit">내용 <span class="required">*</span></div>
<div class="w-form">
<textarea name="content" id="content" cols="80" rows="10" placeholder="질문하실 내용을 입력하세요." style="width:100%;min-width:260px;"><c:out value='${searchArticle.sanitizedContent}' escapeXml='false' /></textarea>
</div>
</li>
</ul>
</div>
</fieldset>
</form>
<div class="qna-write-table">
<ul>
<li class="wt-attachments">
<div class="w-tit">첨부파일</div>
<div id="dropzone" class="w-form">
<form id="myDropzone" name="myDropzone"
action="${pageContext.request.contextPath}/fileupload/uploadFile.do"
class="dropzone needsclick" >
<div class="dz-message needsclick" style="cursor:pointer">
<button type="button" id="attachFileBtn" name="attachFileBtn" class="attachments-btn">파일첨부</button>
<div class="mb">최대 5개까지 (개당 10MB 이내)</div>
<label for="attachFileBtn">※ 이곳에 파일을 끌어다 놓거나(Drag&Drop), 클릭하여 파일을 선택하세요.</label>
</div>
</form>
</div>
</li>
</ul>
</div>
<div class="btn-wrap">
<button type="button" name="btnSave" id="btnSave" title="저장버튼" class="btn btn-color" onclick="fn_pressSaveBtn()">저장</button>
<button type="button" name="btnCancel" id="btnCancel" title="취소버튼" class="btn btn-gray" onclick="fn_list()">취소</button>
</div>
</div>
</div><!-- //contents-frame -->
</div>
</div>
</div>
</div>
<!-- 첨부파일 다운로드용 폼 -->
<form name="downloadFileForm" id="downloadFileForm"
action="${pageContext.request.contextPath}/fileupload/downloadAttachFile.do"
method="post">
<input type="hidden" name="bdType" id="bdType" value="QNA" />
<input type="hidden" name="attachFileId" id="attachFileId" value="" />
<input type="hidden" name="streFileNm" id="streFileNm" value="" />
<input type="hidden" name="fileSn" id="fileSn" value="" />
<input type="hidden" name="orignlFileNm" id="orignlFileNm" value="" />
</form>

View File

@ -121,7 +121,7 @@ function fn_downloadFile(attachFileId, fileSn) {
<div class="view-body">
<div class="view-con">
<div class="view-txt">
<c:out value='${article.content}' escapeXml='false' />
<c:out value='${article.sanitizedContent}' escapeXml='false' />
</div>
</div>
<ul class="attachments">

View File

@ -145,7 +145,7 @@ function fn_downloadFile(attachFileId, fileSn) {
<div class="view-con">
<div class="view-txt">
<p>
<c:out value='${article.unescapedContent}' escapeXml="false" />
<c:out value='${article.sanitizedContent}' escapeXml='false' />
</p>
</div>
</div>
@ -156,7 +156,7 @@ function fn_downloadFile(attachFileId, fileSn) {
<span class="right"><c:out value='${article.modDd }'/></span>
</div>
<div class="answer-con">
<p><c:out value='${article.answer}' escapeXml = 'false' /></p>
<p><c:out value='${article.sanitizedAnswer}' escapeXml = 'false' /></p>
</div>
</div>
</c:if>

View File

@ -1,225 +1,228 @@
<%
/**
* <pre>
* @Class Name : lisNotifications.jsp
*
* @Description : 알림 목록을 조회한다.
*
*
* @프로젝트명: 지방문화원 통합자료관리시스템 구축사업 (2021)
*
* </pre>
*
* @ ------------ -------- ---------------------------
* @ 수정일 수정자 수정내용
* @ ------------ -------- ---------------------------
* @ 2021. 9. 6. KNKIM 최초 생성
*
*
* @author 이씨플라자 * DIGITALSHIP KNKIM
* @since 2021. 9. 6.
* @version 1.0
*
*/
%>
<%@ page language="java" contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="ui" uri="http://egovframework.gov/ctl/ui" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="validator" uri="http://www.springmodules.org/tags/commons-validator" %>
<c:set var="pageTitle">알림</c:set>
<script>
$( document ).ready(function() {
fn_setPageTitle("<c:out value='${pageTitle}'/>");
// 메시지 표출
if(!gfn_isEmpty("<c:out value='${message}'/>")) {
alert("<c:out value='${message}'/>");
}
// 검색 수행 이벤트
$("#btnSearch").on("click", function(e) {
fn_searchArticle(1);
});
$("#searchKeyword").on("keydown", function(e) {
if(e.keyCode == 13) {
fn_searchArticle(1);
}
});
//초기 자료 조회
fn_searchArticle();
}); // document ready
// 선택한 글 상세 보기로 이동
function fn_viewDetail(qObj, notiId) {
var $qObj = $(qObj);
if($qObj.hasClass('on')) {
return;
}
var reqUrl = "${pageContext.request.contextPath}/cmm/selectNotificationAjax.do";
var inputData = { "notiId" : notiId };
//--------------------------------------
// 요청 처리
//--------------------------------------
$.ajax({
type: "post",
contentType: "application/json; charset=utf-8",
url: reqUrl,
dataType: "json",
data: JSON.stringify(inputData),
}).done(function(response){
var jsonObj = JSON.parse(response);
var answerHtml = jsonObj.data.content;
$('.list-accordion .answer').html("");
$qObj.next(".answer").html("<p>" + answerHtml + "<p>&nbsp;</p>");
// 펼치기
$('.list-accordion .answer').stop().slideUp(300);
$qObj.next('.answer').stop().slideDown(300);
$('.list-accordion .question').removeClass('on');
$qObj.addClass('on');
$qObj.find("span unread").remove();
});
}
// 그리드 데이터 조회 호출
function fn_searchArticle(pageIndex) {
if(gfn_isEmpty(pageIndex)) pageIndex = $("#pageIndex").val();
if(gfn_isEmpty(pageIndex)) pageIndex = "1";
$("#pageIndex").val(pageIndex);
var reqUrl = "${pageContext.request.contextPath}/cmm/listNotificationsAjax.do";
var inputData = {
"pageIndex" : $("#pageIndex").val()
, "pageSize" : $("#pageSize").val()
};
//--------------------------------------
// 요청 처리
//--------------------------------------
$.ajax({
type: "post",
contentType: "application/json; charset=utf-8",
url: reqUrl,
dataType: "json",
data: JSON.stringify(inputData),
}).done(function(data) {
<%
// 그리드 데이터 예시 :
// (1) 페이징 클라이언트에서 수행하는 경우(pageloading = false) : [{ "articleNo": 1, "articleType": "01" }, { "articleNo": 2, "articleType": "02" }]
// (2) 페이징 서버에서 수행하는 경우(pageloading = true) : {data: [{...}], itemsCount: 255}
%>
var jsonObj = JSON.parse(data);
console.log(jsonObj.itemsCount);
// 건수
if(jsonObj.itemsCount == undefined) {
$("#itemsCount").text("0");
}
else {
$("#itemsCount").text(jsonObj.itemsCount);
}
// 기존 데이터 CLEAR
$( "ul.list-faq li").not('.no-result').remove();
if(!jsonObj.data || jsonObj.data.length <= 0 ) {
$("ul.list-faq li.no-result").show();
} else {
$("ul.list-faq li.no-result").hide();
// 출력
$.each(jsonObj.data,function(key,obj) {
var li = $(document.createElement('li'));
li.attr("articleId", obj.articleId);
li.append($('<button type="button"></button>').attr('class', 'question').on("click", function() {
fn_viewDetail(this, obj.notiId);
}));
if(!gfn_isEmpty(obj.mngOrgNm)) li.find(".question").append($('<span />').attr("class", "type").html("[" + obj.mngOrgNm + "]"));
else li.find(".question").append($('<span />').attr("class", "type").html("[소장자료관]"));
var unread = "";
if(obj.readYn != "Y") unread = " <unread>미확인</unread>";
li.find(".question").append($('<span />').html(obj.title + unread));
li.find(".question").append($('<span />').attr('class', 'date').html(obj.regDd));
li.append($('<div></div>').attr('class', 'answer').attr('style', 'display: none;'));
$( "ul.list-faq").append(li);
});
}
gfn_makePaging("fn_searchArticle", jsonObj.pagingPageIndex, jsonObj.pagingTotRecordCount, jsonObj.pagingStartPage, jsonObj.pagingEndPage, jsonObj.pagingLastPage);
}).fail(function (request, textStatus, errorThrown) {
alert("조회에 실패하였습니다. \n" + gfn_removeQuotes(request.responseText));
});
}
</script>
<!-- 알림 섹션 -->
<div class="location noti-data">
<h2 class="blind">알림 섹션영역</h2>
<div class="inner">
<div class="row-top">
<div class="location-box">
<ul class="loc">
<li>HOME</li>
<li class="on"><c:out value='${pageTitle}'/></li>
</ul>
<div class="tit">
<h2><span><c:out value='${pageTitle}'/></span></h2>
</div>
</div>
<div class="arr"><img src="/images/icon/icon-join-arr2.png" alt=""></div>
</div>
<div class="contents row-bottom">
<div class="list-box noti-box">
<div class="title">
<span class="count">총 <em id="itemsCount">0</em>건</span>
<div class="text">
<p>- 최근 한달동안 발송된 알림 내역만 보관됩니다.</p>
</div>
</div>
<div class="notifi">
<div class="list-accordion">
<ul class="list-faq">
<li class="no-result" style="display:none;">
<span>자료가 없습니다.</span>
</li>
</ul>
</div>
<ul id="paging" class="paging"></ul>
</div>
</div>
</div>
</div>
</div>
<form name="articleForm" id="articleForm" method="post" onsubmit="return false;">
<input type="hidden" name="pageSize" id="pageSize" value="<c:out value='${pageSize }'/>" />
<input type="hidden" name="pageIndex" id="pageIndex" title="페이지번호" value="<c:out value='${pageIndex }'/>" />
<input type="hidden" name="articleId" id="articleId" title="게시글번호" value="" />
</form>
<%
/**
* <pre>
* @Class Name : lisNotifications.jsp
*
* @Description : 알림 목록을 조회한다.
*
*
* @프로젝트명: 지방문화원 통합자료관리시스템 구축사업 (2021)
*
* </pre>
*
* @ ------------ -------- ---------------------------
* @ 수정일 수정자 수정내용
* @ ------------ -------- ---------------------------
* @ 2021. 9. 6. KNKIM 최초 생성
*
*
* @author 이씨플라자 * DIGITALSHIP KNKIM
* @since 2021. 9. 6.
* @version 1.0
*
*/
%>
<%@ page language="java" contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="ui" uri="http://egovframework.gov/ctl/ui" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="validator" uri="http://www.springmodules.org/tags/commons-validator" %>
<c:set var="pageTitle">알림</c:set>
<script>
$( document ).ready(function() {
fn_setPageTitle("<c:out value='${pageTitle}'/>");
// 메시지 표출
if(!gfn_isEmpty("<c:out value='${message}'/>")) {
alert("<c:out value='${message}'/>");
}
// 검색 수행 이벤트
$("#btnSearch").on("click", function(e) {
fn_searchArticle(1);
});
$("#searchKeyword").on("keydown", function(e) {
if(e.keyCode == 13) {
fn_searchArticle(1);
}
});
//초기 자료 조회
fn_searchArticle();
}); // document ready
// 선택한 글 상세 보기로 이동
function fn_viewDetail(qObj, notiId) {
var $qObj = $(qObj);
if($qObj.hasClass('on')) {
$qObj.removeClass("on");
$qObj.next(".answer").stop().slideUp(300);
$qObj.next(".answer").html("");
return;
}
var reqUrl = "${pageContext.request.contextPath}/cmm/selectNotificationAjax.do";
var inputData = { "notiId" : notiId };
//--------------------------------------
// 요청 처리
//--------------------------------------
$.ajax({
type: "post",
contentType: "application/json; charset=utf-8",
url: reqUrl,
dataType: "json",
data: JSON.stringify(inputData),
}).done(function(response){
var jsonObj = JSON.parse(response);
var answerHtml = jsonObj.data.content;
$('.list-accordion .answer').html("");
$qObj.next(".answer").html("<p>" + answerHtml + "<p>&nbsp;</p>");
// 펼치기
$('.list-accordion .answer').stop().slideUp(300);
$qObj.next('.answer').stop().slideDown(300);
$('.list-accordion .question').removeClass('on');
$qObj.addClass('on');
$qObj.find("span unread").remove();
});
}
// 그리드 데이터 조회 호출
function fn_searchArticle(pageIndex) {
if(gfn_isEmpty(pageIndex)) pageIndex = $("#pageIndex").val();
if(gfn_isEmpty(pageIndex)) pageIndex = "1";
$("#pageIndex").val(pageIndex);
var reqUrl = "${pageContext.request.contextPath}/cmm/listNotificationsAjax.do";
var inputData = {
"pageIndex" : $("#pageIndex").val()
, "pageSize" : $("#pageSize").val()
};
//--------------------------------------
// 요청 처리
//--------------------------------------
$.ajax({
type: "post",
contentType: "application/json; charset=utf-8",
url: reqUrl,
dataType: "json",
data: JSON.stringify(inputData),
}).done(function(data) {
<%
// 그리드 데이터 예시 :
// (1) 페이징 클라이언트에서 수행하는 경우(pageloading = false) : [{ "articleNo": 1, "articleType": "01" }, { "articleNo": 2, "articleType": "02" }]
// (2) 페이징 서버에서 수행하는 경우(pageloading = true) : {data: [{...}], itemsCount: 255}
%>
var jsonObj = JSON.parse(data);
console.log(jsonObj.itemsCount);
// 건수
if(jsonObj.itemsCount == undefined) {
$("#itemsCount").text("0");
}
else {
$("#itemsCount").text(jsonObj.itemsCount);
}
// 기존 데이터 CLEAR
$( "ul.list-faq li").not('.no-result').remove();
if(!jsonObj.data || jsonObj.data.length <= 0 ) {
$("ul.list-faq li.no-result").show();
} else {
$("ul.list-faq li.no-result").hide();
// 출력
$.each(jsonObj.data,function(key,obj) {
var li = $(document.createElement('li'));
li.attr("articleId", obj.articleId);
li.append($('<button type="button"></button>').attr('class', 'question').on("click", function() {
fn_viewDetail(this, obj.notiId);
}));
if(!gfn_isEmpty(obj.mngOrgNm)) li.find(".question").append($('<span />').attr("class", "type").html("[" + obj.mngOrgNm + "]"));
else li.find(".question").append($('<span />').attr("class", "type").html("[소장자료관]"));
var unread = "";
if(obj.readYn != "Y") unread = " <unread>미확인</unread>";
li.find(".question").append($('<span />').html(obj.title + unread));
li.find(".question").append($('<span />').attr('class', 'date').html(obj.regDd));
li.append($('<div></div>').attr('class', 'answer').attr('style', 'display: none;'));
$( "ul.list-faq").append(li);
});
}
gfn_makePaging("fn_searchArticle", jsonObj.pagingPageIndex, jsonObj.pagingTotRecordCount, jsonObj.pagingStartPage, jsonObj.pagingEndPage, jsonObj.pagingLastPage);
}).fail(function (request, textStatus, errorThrown) {
alert("조회에 실패하였습니다. \n" + gfn_removeQuotes(request.responseText));
});
}
</script>
<!-- 알림 섹션 -->
<div class="location noti-data">
<h2 class="blind">알림 섹션영역</h2>
<div class="inner">
<div class="row-top">
<div class="location-box">
<ul class="loc">
<li>HOME</li>
<li class="on"><c:out value='${pageTitle}'/></li>
</ul>
<div class="tit">
<h2><span><c:out value='${pageTitle}'/></span></h2>
</div>
</div>
<div class="arr"><img src="/images/icon/icon-join-arr2.png" alt=""></div>
</div>
<div class="contents row-bottom">
<div class="list-box noti-box">
<div class="title">
<span class="count">총 <em id="itemsCount">0</em>건</span>
<div class="text">
<p>- 최근 한달동안 발송된 알림 내역만 보관됩니다.</p>
</div>
</div>
<div class="notifi">
<div class="list-accordion">
<ul class="list-faq">
<li class="no-result" style="display:none;">
<span>자료가 없습니다.</span>
</li>
</ul>
</div>
<ul id="paging" class="paging"></ul>
</div>
</div>
</div>
</div>
</div>
<form name="articleForm" id="articleForm" method="post" onsubmit="return false;">
<input type="hidden" name="pageSize" id="pageSize" value="<c:out value='${pageSize }'/>" />
<input type="hidden" name="pageIndex" id="pageIndex" title="페이지번호" value="<c:out value='${pageIndex }'/>" />
<input type="hidden" name="articleId" id="articleId" title="게시글번호" value="" />
</form>

View File

@ -1,120 +1,123 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<display-name>nlib</display-name>
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
<filter>
<filter-name>HTMLTagFilter</filter-name>
<filter-class>egovframework.rte.ptl.mvc.filter.HTMLTagFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HTMLTagFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
<!-- Spring Security Filter : DIGITALSHIP 2021.07.02 -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:egovframework/spring/context-*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 지방문화원 세션 처리를 위한 세션 리스너 : 2021.08.23 KKN -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/config/egovframework/springmvc/dispatcher-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.ajax</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<login-config>
<auth-method>BASIC</auth-method>
</login-config>
<error-page>
<exception-type>java.lang.Throwable</exception-type>
<location>/common/error-throw.jsp</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/common/error404.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/common/error500.jsp</location>
</error-page>
<security-constraint>
<web-resource-collection>
<web-resource-name>Protected Resource</web-resource-name>
<url-pattern>/*</url-pattern>
<http-method>OPTIONS</http-method>
<http-method>HEAD</http-method>
<http-method>TRACE</http-method>
<http-method>PUT</http-method>
<http-method>DELETE</http-method>
<http-method>PATCH</http-method>
<http-method>SEARCH</http-method>
<http-method>CONNECT</http-method>
<http-method>PROPFIND</http-method>
<http-method>PROPPATCH</http-method>
<http-method>MKCOL</http-method>
<http-method>COPY</http-method>
<http-method>MOVE</http-method>
<http-method>LOCK</http-method>
<http-method>UNLOCK</http-method>
</web-resource-collection>
<auth-constraint>
<role-name></role-name>
</auth-constraint>
</security-constraint>
</web-app>
<?xml version="1.0" encoding="UTF-8"?>
<web-app
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<display-name>nlib</display-name>
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
<!-- 별도 OWASP HTML Sanitizer 사용으로 대체 -->
<!--
<filter>
<filter-name>HTMLTagFilter</filter-name>
<filter-class>egovframework.rte.ptl.mvc.filter.HTMLTagFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HTMLTagFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
-->
<!-- Spring Security Filter : DIGITALSHIP 2021.07.02 -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:egovframework/spring/context-*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 지방문화원 세션 처리를 위한 세션 리스너 : 2021.08.23 KKN -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/config/egovframework/springmvc/dispatcher-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.ajax</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<login-config>
<auth-method>BASIC</auth-method>
</login-config>
<error-page>
<exception-type>java.lang.Throwable</exception-type>
<location>/common/error-throw.jsp</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/common/error404.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/common/error500.jsp</location>
</error-page>
<security-constraint>
<web-resource-collection>
<web-resource-name>Protected Resource</web-resource-name>
<url-pattern>/*</url-pattern>
<http-method>OPTIONS</http-method>
<http-method>HEAD</http-method>
<http-method>TRACE</http-method>
<http-method>PUT</http-method>
<http-method>DELETE</http-method>
<http-method>PATCH</http-method>
<http-method>SEARCH</http-method>
<http-method>CONNECT</http-method>
<http-method>PROPFIND</http-method>
<http-method>PROPPATCH</http-method>
<http-method>MKCOL</http-method>
<http-method>COPY</http-method>
<http-method>MOVE</http-method>
<http-method>LOCK</http-method>
<http-method>UNLOCK</http-method>
</web-resource-collection>
<auth-constraint>
<role-name></role-name>
</auth-constraint>
</security-constraint>
</web-app>