728x90
반응형
728x170
▶ Properties/launchSettings.json
{
"iisSettings" :
{
"windowsAuthentication" : false,
"anonymousAuthentication" : true,
"iisExpress" :
{
"applicationUrl" : "http://localhost:1795",
"sslPort" : 44306
}
},
"profiles" :
{
"IIS Express" :
{
"commandName" : "IISExpress",
"launchBrowser" : true,
"launchUrl" : "NoticeBoard",
"environmentVariables" :
{
"ASPNETCORE_ENVIRONMENT" : "Development"
}
},
"TestProject" :
{
"commandName" : "Project",
"launchBrowser" : true,
"launchUrl" : "NoticeBoard",
"applicationUrl" : "https://localhost:5001;http://localhost:5000",
"environmentVariables" :
{
"ASPNETCORE_ENVIRONMENT" : "Development"
}
}
}
}
728x90
▶ appsettings.json
{
"ConnectionStrings" :
{
"DefaultConnection" : "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=TestDB;Integrated Security=True;"
},
"Logging" :
{
"LogLevel" :
{
"Default" : "Information",
"Microsoft" : "Warning",
"Microsoft.Hosting.Lifetime" : "Information"
}
},
"AllowedHosts" : "*",
"MainSettings" :
{
"SiteAdministrator" : "admin"
}
}
300x250
▶ Database/TestDB.sql
CREATE TABLE dbo.Notice
(
ID INT IDENTITY(1, 1) NOT NULL PRIMARY KEY -- ID
,[Name] NVARCHAR(25) NOT NULL -- 작성자명
,MailAddress NVARCHAR(100) NULL -- 메일 주소
,Title NVARCHAR(150) NOT NULL -- 제목
,WriteDate DATETIME DEFAULT GETDATE() NOT NULL -- 작성일
,WriteIP NVARCHAR(15) NULL -- 작성 IP
,Content NTEXT NOT NULL -- 내용
,[Password] NVARCHAR(20) NULL -- 패스워드
,ReadCount INT DEFAULT 0 -- 조회 수
,[Encoding] NVARCHAR(10) NOT NULL -- 인코딩(HTML/TEXT)
,Homepage NVARCHAR(100) NULL -- 홈페이지
,UpdateDate DATETIME NULL -- 수정일
,UpdateIP NVARCHAR(15) NULL -- 수정 IP
,[FileName] NVARCHAR(255) NULL -- 파일명
,FileSize INT DEFAULT 0 -- 파일 크기
,DownloadCount INT DEFAULT 0 -- 다운로드 수
,ReferenceID INT NOT NULL -- 참조 ID
,ReplyLevel INT DEFAULT 0 -- 답변 레벨
,ReplyOrder INT DEFAULT 0 -- 답변 순서
,ReplyCount INT DEFAULT 0 -- 답변 수
,ParentID INT DEFAULT 0 -- 부모 ID
,CommentCount INT DEFAULT 0 -- 댓글 수
,Category NVARCHAR(50) Default('FREE') Null -- 카테고리
)
GO
CREATE TABLE dbo.NoticeComment
(
ID INT IDENTITY(1, 1) NOT NULL PRIMARY KEY -- ID
,NoticeID INT NOT NULL -- 게시판 ID
,[Name] NVARCHAR(25) NOT NULL -- 작성자명
,Comment NVARCHAR(4000) NOT NULL -- 내용
,WriteDate SMALLDATETIME DEFAULT(GETDATE()) -- 작성일
,[Password] NVARCHAR(20) NOT NULL -- 패스워드
)
GO
CREATE PROCEDURE dbo.WriteNotice
@Name NVARCHAR(25)
,@MailAddress NVARCHAR(100)
,@Title NVARCHAR(150)
,@WriteIP NVARCHAR(15)
,@Content NTEXT
,@Password NVARCHAR(20)
,@Encoding NVARCHAR(10)
,@Homepage NVARCHAR(100)
,@FileName NVARCHAR(255)
,@FileSize INT
AS
DECLARE @MaximumReferenceID INT;
SELECT @MaximumReferenceID = MAX(ReferenceID) FROM dbo.Notice;
IF @MaximumReferenceID IS NULL
BEGIN
SET @MaximumReferenceID = 1;
END
ELSE
BEGIN
SET @MaximumReferenceID = @MaximumReferenceID + 1;
END
INSERT INTO dbo.Notice
(
[Name]
,MailAddress
,Title
,WriteIP
,Content
,[Password]
,[Encoding]
,Homepage
,[ReferenceID]
,[FileName]
,[FileSize]
)
Values
(
@Name
,@MailAddress
,@Title
,@WriteIP
,@Content
,@Password
,@Encoding
,@Homepage
,@MaximumReferenceID
,@FileName
,@FileSize
);
GO
CREATE PROCEDURE dbo.ListNotice
@Page INT
AS
WITH OrderedNoticeBoard
AS
(
SELECT
ID
,[Name]
,MailAddress
,Title
,WriteDate
,ReadCount
,ReferenceID
,ReplyLevel
,ReplyOrder
,ReplyCount
,ParentID
,CommentCount
,[FileName]
,FileSize
,DownloadCount
,ROW_NUMBER() OVER (ORDER BY ReferenceID DESC, ReplyOrder ASC) AS 'RowNumber'
FROM dbo.Notice
)
SELECT *
FROM OrderedNoticeBoard
WHERE RowNumber BETWEEN @Page * 10 + 1 AND (@Page + 1) * 10;
GO
CREATE PROCEDURE dbo.ViewNotice
@ID Int
As
UPDATE dbo.Notice
SET ReadCount = ReadCount + 1
WHERE ID = @ID;
SELECT *
FROM dbo.Notice
WHERE ID = @ID;
GO
CREATE PROCEDURE dbo.ReplyNotice
@Name NVARCHAR(25)
,@MailAddress NVARCHAR(100)
,@Title NVARCHAR(150)
,@WriteIP NVARCHAR(15)
,@Content NTEXT
,@Password NVARCHAR(20)
,@Encoding NVARCHAR(10)
,@Homepage NVARCHAR(100)
,@ParentID INT
,@FileName NVARCHAR(255)
,@FileSize INT
AS
DECLARE @MaximumReplyOrder INT;
DECLARE @MaximumReplyCount INT;
DECLARE @ParentReferenceID INT;
DECLARE @ParentReplyLevel INT;
DECLARE @ParentReplyOrder INT;
UPDATE dbo.Notice
Set ReplyCount = ReplyCount + 1
WHERE ID = @ParentID;
SELECT
@MaximumReplyOrder = ReplyOrder
,@MaximumReplyCount = ReplyCount
FROM dbo.Notice
WHERE ParentID = @ParentID
AND ReplyOrder = (SELECT MAX(ReplyOrder) FROM dbo.Notice WHERE ParentID = @ParentID);
IF @MaximumReplyOrder IS NULL
BEGIN
SELECT @MaximumReplyOrder = ReplyOrder
FROM dbo.Notice
WHERE ID = @ParentID;
SET @MaximumReplyCount = 0;
END
SELECT
@ParentReferenceID = ReferenceID
,@ParentReplyLevel = ReplyLevel
FROM dbo.Notice
WHERE ID = @ParentID;
UPDATE dbo.Notice
SET ReplyOrder = ReplyOrder + 1
WHERE ReferenceID = @ParentReferenceID And ReplyOrder > (@MaximumReplyOrder + @MaximumReplyCount);
INSERT INTO dbo.Notice
(
[Name]
,MailAddress
,Title
,WriteIP
,Content
,[Password]
,[Encoding]
,Homepage
,ReferenceID
,ReplyLevel
,ReplyOrder
,ParentID
,[FileName]
,FileSize
)
VALUES
(
@Name
,@MailAddress
,@Title
,@WriteIP
,@Content
,@Password
,@Encoding
,@Homepage
,@ParentReferenceID
,@ParentReplyLevel + 1
,@MaximumReplyOrder + @MaximumReplyCount + 1
,@ParentID
,@FileName
,@FileSize
);
GO
CREATE PROCEDURE dbo.GetNoticeCount
As
Select Count(*) From dbo.Notice;
GO
CREATE PROCEDURE dbo.SearchNoticeCount
@SearchField NVARCHAR(25)
,@SearchQuery NVARCHAR(25)
AS
SET @SearchQuery = '%' + @SearchQuery + '%';
SELECT COUNT(*)
FROM dbo.Notice
WHERE
(
CASE @SearchField
WHEN 'Name' THEN [Name]
WHEN 'Title' THEN Title
WHEN 'Content' THEN Content
ELSE @SearchQuery
END
)
LIKE @SearchQuery;
GO
CREATE PROCEDURE dbo.DeleteNotice
@ID INT
,@Password NVARCHAR(30)
AS
DECLARE @Count INT;
SELECT @Count = COUNT(*)
FROM dbo.Notice
WHERE ID = @ID
AND [Password] = @Password;
IF @Count = 0
BEGIN
Return 0;
END
DECLARE @ReplyCount INT;
DECLARE @ReplyOrder INT;
DECLARE @ReferenceID INT;
DECLARE @ParentID INT;
SELECT
@ReplyCount = ReplyCount
,@ReplyOrder = ReplyOrder
,@ReferenceID = ReferenceID
,@ParentID = ParentID
FROM dbo.Notice
WHERE ID = @ID;
IF @ReplyCount = 0
BEGIN
IF @ReplyOrder > 0
BEGIN
UPDATE dbo.Notice
SET ReplyOrder = ReplyOrder - 1
WHERE ReferenceID = @ReferenceID
AND ReplyOrder > @ReplyOrder;
UPDATE Notice
SET ReplyCount = ReplyCount - 1
WHERE ID = @ParentID;
END
DELETE FROM dbo.Notice
WHERE ID = @ID;
DELETE FROM dbo.Notice
WHERE ID = @ParentID
AND UpdateIP = N'((DELETED))'
AND ReplyCount = 0;
END
ELSE
BEGIN
UPDATE dbo.Notice
SET
[Name] = N'(Unknown)',
MailAddress = '',
[Password] = '',
Title = N'(삭제된 글입니다.)',
Content = N'(삭제된 글입니다. 현재 답변이 포함되어 있기 때문에 내용만 삭제되었습니다.)',
UpdateIP = N'((DELETED))',
[FileName] = '',
FileSize = 0,
CommentCount = 0
WHERE ID = @ID;
END
GO
CREATE PROCEDURE dbo.UpdateNotice
@Name NVARCHAR(25)
,@MailAddress NVARCHAR(100)
,@Title NVARCHAR(150)
,@UpdateIP NVARCHAR(15)
,@Content NTEXT
,@Password NVARCHAR(30)
,@Encoding NVARCHAR(10)
,@Homepage NVARCHAR(100)
,@FileName NVARCHAR(255)
,@FileSize INT
,@ID INT
AS
DECLARE @Count INT
SELECT @Count = Count(*)
FROM dbo.Notice
WHERE ID = @ID
AND [Password] = @Password;
IF @Count > 0
BEGIN
UPDATE dbo.Notice
SET
[Name] = @Name
,MailAddress = @MailAddress
,Title = @Title
,UpdateIP = @UpdateIP
,UpdateDate = GETDATE()
,Content = @Content
,[Encoding] = @Encoding
,Homepage = @Homepage
,[FileName] = @FileName
,FileSize = @FileSize
Where ID = @ID;
SELECT '1';
END
ELSE
SELECT '0';
GO
CREATE PROCEDURE dbo.SearchNotice
@Page INT
,@SearchField NVARCHAR(25)
,@SearchQuery NVARCHAR(25)
AS
WITH OrderedNoticeBoard
AS
(
SELECT
ID
,[Name]
,MailAddress
,Title
,WriteDate
,ReadCount
,ReferenceID
,ReplyLevel
,ReplyOrder
,ReplyCount
,ParentID
,CommentCount
,[FileName]
,FileSize
,DownloadCount
,ROW_NUMBER() OVER (ORDER BY ReferenceID DESC, ReplyOrder ASC) AS 'RowNumber'
FROM dbo.Notice
WHERE
(
CASE @SearchField
WHEN 'Name' THEN [Name]
WHEN 'Title' THEN Title
WHEN 'Content' THEN Content
ELSE @SearchQuery
END
)
LIKE '%' + @SearchQuery + '%'
)
SELECT
ID
,[Name]
,MailAddress
,Title
,WriteDate
,ReadCount
,ReferenceID
,ReplyLevel
,ReplyOrder
,ReplyCount
,ParentID
,CommentCount
,[FileName]
,FileSize
,DownloadCount
,RowNumber
FROM OrderedNoticeBoard
WHERE RowNumber BETWEEN @Page * 10 + 1 AND (@Page + 1) * 10
ORDER BY ID DESC;
GO
▶ Setings/MainSettings.cs
namespace TestProject.Settings
{
/// <summary>
/// 메인 설정
/// </summary>
public class MainSettings
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 사이트 관리자 - SiteAdministrator
/// <summary>
/// 사이트 관리자
/// </summary>
public string SiteAdministrator { get; set; }
#endregion
}
}
▶ FileHelper.cs
using System.IO;
namespace TestProject
{
/// <summary>
/// 파일 헬퍼
/// </summary>
public class FileHelper
{
////////////////////////////////////////////////////////////////////////////////////////// Method
//////////////////////////////////////////////////////////////////////////////// Static
////////////////////////////////////////////////////////////////////// Public
#region 유니크 파일명 구하기 - GetUniqueFileName(sourceDirectoryPath, fileName)
/// <summary>
/// 유니크 파일명 구하기
/// </summary>
/// <param name="sourceDirectoryPath">소스 디렉토리 경로</param>
/// <param name="fileName">파일명</param>
/// <returns>유니크 파일명</returns>
public static string GetUniqueFileName(string sourceDirectoryPath, string fileName)
{
string temporaryName = Path.GetFileNameWithoutExtension(fileName);
string fileExtension = Path.GetExtension(fileName);
bool exist = true;
int i = 0;
while(exist)
{
if(File.Exists(Path.Combine(sourceDirectoryPath, fileName)))
{
fileName = temporaryName + "(" + ++i + ")" + fileExtension;
}
else
{
exist = false;
}
}
return fileName;
}
#endregion
}
}
▶ HTMLHelper.cs
namespace TestProject
{
/// <summary>
/// HTML 헬퍼
/// </summary>
public class HTMLHelper
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Public
#region 인코딩하기 - Encode(source)
/// <summary>
/// 인코딩하기
/// </summary>
/// <param name="source">소스 문자열</param>
/// <returns>인코딩 문자열</returns>
public static string Encode(string source)
{
string target = string.Empty;
if(string.IsNullOrEmpty(source))
{
target = string.Empty;
}
else
{
target = source;
target = target.Replace("&" , "&" );
target = target.Replace(">" , ">" );
target = target.Replace("<" , "<" );
target = target.Replace("\r\n", "<br />");
target = target.Replace("\"" , """ );
}
return target;
}
#endregion
#region 탭/공백 포함 인코딩하기 - EncodeIncludingTabAndSpace(source)
/// <summary>
/// 탭/공백 포함 인코딩하기
/// </summary>
/// <param name="source">소스 문자열</param>
/// <returns>인코딩 문자열</returns>
public static string EncodeIncludingTabAndSpace(string source)
{
return Encode(source)
.Replace("\t" , " ")
.Replace(" " + " ", " ");
}
#endregion
}
}
▶ NoticeHelper.cs
using System;
using System.IO;
namespace TestProject
{
/// <summary>
/// 게시판 헬퍼
/// </summary>
public class NoticeHelper
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Public
#region REPLY 이미지 HTML 구하기 - GetReplyImageHTML(stepObject)
/// <summary>
/// REPLY 이미지 HTML 구하기
/// </summary>
/// <param name="stepObject">단계 객체</param>
/// <returns>REPLY 이미지 HTML</returns>
public static string GetReplyImageHTML(object stepObject)
{
int step = Convert.ToInt32(stepObject);
string target = string.Empty;
if(step == 0)
{
target = string.Empty;
}
else
{
for(int i = 0; i < step; i++)
{
target = string.Format
(
"<img src=\"{0}\" height=\"{1}\" width=\"{2}\">",
"/Image/NoticeBoard/blank.gif",
"0",
(step * 15)
);
}
target += "<img src=\"/Image/NoticeBoard/reply.gif\">";
}
return target;
}
#endregion
#region 댓글 수 HTML 구하기 - GetCommentCountHTML(commentCountObject)
/// <summary>
/// 댓글 수 HTML 구하기
/// </summary>
/// <param name="commentCountObject">댓글 수 객체</param>
/// <returns>댓글 수 HTML</returns>
public static string GetCommentCountHTML(object commentCountObject)
{
string target = string.Empty;
try
{
if(Convert.ToInt32(commentCountObject) > 0)
{
target = "<img src=\"/Image/NoticeBoard/comment.gif\" />";
target += " (" + commentCountObject.ToString() + ")";
}
}
catch(Exception)
{
target = string.Empty;
}
return target;
}
#endregion
#region 신규 이미지 HTML 구하기 - GetNewImageHTML(dateObject)
/// <summary>
/// 신규 이미지 HTML 구하기
/// </summary>
/// <param name="dateObject">날짜 객체</param>
/// <returns>신규 이미지 HTML</returns>
public static string GetNewImageHTML(object dateObject)
{
if(dateObject != null)
{
if(!string.IsNullOrEmpty(dateObject.ToString()))
{
DateTime date = Convert.ToDateTime(dateObject);
TimeSpan timeSpan = DateTime.Now - date;
string target = string.Empty;
if(timeSpan.TotalMinutes < 1440)
{
target = "<img src=\"/Image/NoticeBoard/new.gif\">";
}
return target;
}
}
return string.Empty;
}
#endregion
#region 날짜/시간 HTML 구하기 - GetDateTimeHTML(dateTimeObject)
/// <summary>
/// 날짜/시간 HTML 구하기
/// </summary>
/// <param name="dateTimeObject">날짜/시간 객체</param>
/// <returns>날짜/시간 HTML</returns>
public static string GetDateTimeHTML(object dateTimeObject)
{
if(dateTimeObject != null)
{
if(!string.IsNullOrEmpty(dateTimeObject.ToString()))
{
string writeDateString = Convert.ToDateTime(dateTimeObject).ToString("yyyy-MM-dd");
if(writeDateString == DateTime.Now.ToString("yyyy-MM-dd"))
{
return Convert.ToDateTime(dateTimeObject).ToString("hh:mm:ss");
}
else
{
return writeDateString;
}
}
}
return "-";
}
#endregion
#region 파일 크기 문자열 구하기 - GetFileSizeString(fileSize)
/// <summary>
/// 파일 크기 문자열 구하기
/// </summary>
/// <param name="fileSize">파일 크기</param>
/// <returns>파일 크기 문자열</returns>
public static string GetFileSizeString(int fileSize)
{
string target = string.Empty;
if(fileSize >= 1048576)
{
target = string.Format("{0:F} MB", (fileSize / 1048576));
}
else
{
if(fileSize >= 1024)
{
target = string.Format("{0} KB", (fileSize / 1024));
}
else
{
target = fileSize + " Byte(s)";
}
}
return target;
}
#endregion
#region 이미지 파일 여부 구하기 - IsImageFile(filePath)
/// <summary>
/// 이미지 파일 여부 구하기
/// </summary>
/// <param name="filePath">파일 경로</param>
/// <returns>이미지 파일 여부</returns>
public static bool IsImageFile(string filePath)
{
string fileExtension = Path.GetExtension(filePath).Replace(".", "").ToLower();
bool result = false;
if(fileExtension == "gif" || fileExtension == "jpg" || fileExtension == "jpeg" || fileExtension == "png")
{
result = true;
}
else
{
result = false;
}
return result;
}
#endregion
#region 파일 다운로드 링크 HTML 구하기 - GetFileDownloadLinkHTML(id, filePath, fileSizeString)
/// <summary>
/// 파일 다운로드 링크 HTML 구하기
/// </summary>
/// <param name="id">ID</param>
/// <param name="filePath">파일 경로</param>
/// <param name="fileSizeString">파일 크기 문자열</param>
/// <returns>파일 다운로드 링크 HTML 구하기</returns>
public static string GetFileDownloadLinkHTML(int id, string filePath, string fileSizeString)
{
if(filePath.Length > 0)
{
return string.Format
(
"<a href=\"/NoticeBoard/Download?id={0}\">{1}</a>",
id.ToString(),
GetFileExtensionImageHTML(filePath, filePath + "(" + GetFileSizeString(Convert.ToInt32(fileSizeString)) + ")")
);
}
else
{
return "-";
}
}
#endregion
#region 파일 확장자 이미지 HTML 구하기 - GetFileExtensionImageHTML(filePath, alternateString)
/// <summary>
/// 파일 확장자 이미지 HTML 구하기
/// </summary>
/// <param name="filePath">파일 이름</param>
/// <param name="alternateString">대체 문자열</param>
/// <returns>파일 확장자 이미지 HTML</returns>
public static string GetFileExtensionImageHTML(string filePath, string alternateString)
{
string format = "<img src='{0}' border='0' alt='{1}'>";
string fileExtension = Path.GetExtension(filePath).Replace(".", "").ToLower();
string target = string.Empty;
switch(fileExtension)
{
case "ace" : target = string.Format(format, "/Image/FileExtension/ext_ace.gif" , alternateString); break;
case "ai" : target = string.Format(format, "/Image/FileExtension/ext_ai.gif" , alternateString); break;
case "alz" : target = string.Format(format, "/Image/FileExtension/ext_alz.gif" , alternateString); break;
case "arj" : target = string.Format(format, "/Image/FileExtension/ext_arj.gif" , alternateString); break;
case "asa" : target = string.Format(format, "/Image/FileExtension/ext_asa.gif" , alternateString); break;
case "asax" : target = string.Format(format, "/Image/FileExtension/ext_asax.gif" , alternateString); break;
case "ascx" : target = string.Format(format, "/Image/FileExtension/ext_ascx.gif" , alternateString); break;
case "asf" : target = string.Format(format, "/Image/FileExtension/ext_asf.gif" , alternateString); break;
case "asmx" : target = string.Format(format, "/Image/FileExtension/ext_asmx.gif" , alternateString); break;
case "asp" : target = string.Format(format, "/Image/FileExtension/ext_asp.gif" , alternateString); break;
case "aspx" : target = string.Format(format, "/Image/FileExtension/ext_aspx.gif" , alternateString); break;
case "asx" : target = string.Format(format, "/Image/FileExtension/ext_asx.gif" , alternateString); break;
case "au" : target = string.Format(format, "/Image/FileExtension/ext_au.gif" , alternateString); break;
case "avi" : target = string.Format(format, "/Image/FileExtension/ext_avi.gif" , alternateString); break;
case "bat" : target = string.Format(format, "/Image/FileExtension/ext_bat.gif" , alternateString); break;
case "bmp" : target = string.Format(format, "/Image/FileExtension/ext_bmp.gif" , alternateString); break;
case "c" : target = string.Format(format, "/Image/FileExtension/ext_c.gif" , alternateString); break;
case "cs" : target = string.Format(format, "/Image/FileExtension/ext_cs.gif" , alternateString); break;
case "csproj" : target = string.Format(format, "/Image/FileExtension/ext_csproj.gif" , alternateString); break;
case "cab" : target = string.Format(format, "/Image/FileExtension/ext_cab.gif" , alternateString); break;
case "chm" : target = string.Format(format, "/Image/FileExtension/ext_chm.gif" , alternateString); break;
case "com" : target = string.Format(format, "/Image/FileExtension/ext_com.gif" , alternateString); break;
case "config" : target = string.Format(format, "/Image/FileExtension/ext_config.gif" , alternateString); break;
case "cpp" : target = string.Format(format, "/Image/FileExtension/ext_cpp.gif" , alternateString); break;
case "css" : target = string.Format(format, "/Image/FileExtension/ext_css.gif" , alternateString); break;
case "csv" : target = string.Format(format, "/Image/FileExtension/ext_csv.gif" , alternateString); break;
case "disco" : target = string.Format(format, "/Image/FileExtension/ext_disco.gif" , alternateString); break;
case "dll" : target = string.Format(format, "/Image/FileExtension/ext_dll.gif" , alternateString); break;
case "doc" : target = string.Format(format, "/Image/FileExtension/ext_doc.gif" , alternateString); break;
case "eml" : target = string.Format(format, "/Image/FileExtension/ext_eml.gif" , alternateString); break;
case "exe" : target = string.Format(format, "/Image/FileExtension/ext_exe.gif" , alternateString); break;
case "gif" : target = string.Format(format, "/Image/FileExtension/ext_gif.gif" , alternateString); break;
case "gz" : target = string.Format(format, "/Image/FileExtension/ext_gz.gif" , alternateString); break;
case "h" : target = string.Format(format, "/Image/FileExtension/ext_h.gif" , alternateString); break;
case "hlp" : target = string.Format(format, "/Image/FileExtension/ext_hlp.gif" , alternateString); break;
case "htm" : target = string.Format(format, "/Image/FileExtension/ext_htm.gif" , alternateString); break;
case "html" : target = string.Format(format, "/Image/FileExtension/ext_html.gif" , alternateString); break;
case "hwp" : target = string.Format(format, "/Image/FileExtension/ext_hwp.gif" , alternateString); break;
case "hwt" : target = string.Format(format, "/Image/FileExtension/ext_hwt.gif" , alternateString); break;
case "inc" : target = string.Format(format, "/Image/FileExtension/ext_inc.gif" , alternateString); break;
case "ini" : target = string.Format(format, "/Image/FileExtension/ext_ini.gif" , alternateString); break;
case "jpg" : target = string.Format(format, "/Image/FileExtension/ext_jpg.gif" , alternateString); break;
case "jpeg" : target = string.Format(format, "/Image/FileExtension/ext_jpeg.gif" , alternateString); break;
case "js" : target = string.Format(format, "/Image/FileExtension/ext_js.gif" , alternateString); break;
case "lzh" : target = string.Format(format, "/Image/FileExtension/ext_lzh.gif" , alternateString); break;
case "m3u" : target = string.Format(format, "/Image/FileExtension/ext_m3u.gif" , alternateString); break;
case "max" : target = string.Format(format, "/Image/FileExtension/ext_max.gif" , alternateString); break;
case "mdb" : target = string.Format(format, "/Image/FileExtension/ext_mdb.gif" , alternateString); break;
case "mid" : target = string.Format(format, "/Image/FileExtension/ext_mid.gif" , alternateString); break;
case "mov" : target = string.Format(format, "/Image/FileExtension/ext_mov.gif" , alternateString); break;
case "mp2" : target = string.Format(format, "/Image/FileExtension/ext_mp2.gif" , alternateString); break;
case "mp3" : target = string.Format(format, "/Image/FileExtension/ext_mp3.gif" , alternateString); break;
case "mpe" : target = string.Format(format, "/Image/FileExtension/ext_mpe.gif" , alternateString); break;
case "mpeg" : target = string.Format(format, "/Image/FileExtension/ext_mpeg.gif" , alternateString); break;
case "mpg" : target = string.Format(format, "/Image/FileExtension/ext_mpg.gif" , alternateString); break;
case "msi" : target = string.Format(format, "/Image/FileExtension/ext_exe.gif" , alternateString); break;
case "" : target = string.Format(format, "/Image/FileExtension/ext_none.gif" , alternateString); break;
case "pcx" : target = string.Format(format, "/Image/FileExtension/ext_pcx.gif" , alternateString); break;
case "pdb" : target = string.Format(format, "/Image/FileExtension/ext_pdb.gif" , alternateString); break;
case "pdf" : target = string.Format(format, "/Image/FileExtension/ext_pdf.gif" , alternateString); break;
case "pls" : target = string.Format(format, "/Image/FileExtension/ext_pls.gif" , alternateString); break;
case "png" : target = string.Format(format, "/Image/FileExtension/ext_png.gif" , alternateString); break;
case "ppt" : target = string.Format(format, "/Image/FileExtension/ext_ppt.gif" , alternateString); break;
case "psd" : target = string.Format(format, "/Image/FileExtension/ext_psd.gif" , alternateString); break;
case "ra" : target = string.Format(format, "/Image/FileExtension/ext_ra.gif" , alternateString); break;
case "ram" : target = string.Format(format, "/Image/FileExtension/ext_ram.gif" , alternateString); break;
case "rar" : target = string.Format(format, "/Image/FileExtension/ext_rar.gif" , alternateString); break;
case "reg" : target = string.Format(format, "/Image/FileExtension/ext_reg.gif" , alternateString); break;
case "resx" : target = string.Format(format, "/Image/FileExtension/ext_resx.gif" , alternateString); break;
case "rm" : target = string.Format(format, "/Image/FileExtension/ext_rm.gif" , alternateString); break;
case "rmi" : target = string.Format(format, "/Image/FileExtension/ext_rmi.gif" , alternateString); break;
case "rtf" : target = string.Format(format, "/Image/FileExtension/ext_rtf.gif" , alternateString); break;
case "sql" : target = string.Format(format, "/Image/FileExtension/ext_sql.gif" , alternateString); break;
case "swf" : target = string.Format(format, "/Image/FileExtension/ext_swf.gif" , alternateString); break;
case "sys" : target = string.Format(format, "/Image/FileExtension/ext_sys.gif" , alternateString); break;
case "tar" : target = string.Format(format, "/Image/FileExtension/ext_tar.gif" , alternateString); break;
case "tga" : target = string.Format(format, "/Image/FileExtension/ext_tga.gif" , alternateString); break;
case "tif" : target = string.Format(format, "/Image/FileExtension/ext_tif.gif" , alternateString); break;
case "ttf" : target = string.Format(format, "/Image/FileExtension/ext_ttf.gif" , alternateString); break;
case "txt" : target = string.Format(format, "/Image/FileExtension/ext_txt.gif" , alternateString); break;
case "vb" : target = string.Format(format, "/Image/FileExtension/ext_vb.gif" , alternateString); break;
case "vbs" : target = string.Format(format, "/Image/FileExtension/ext_vbs.gif" , alternateString); break;
case "vbdisco" : target = string.Format(format, "/Image/FileExtension/ext_vbdisco.gif", alternateString); break;
case "wav" : target = string.Format(format, "/Image/FileExtension/ext_wav.gif" , alternateString); break;
case "wax" : target = string.Format(format, "/Image/FileExtension/ext_wax.gif" , alternateString); break;
case "webinfo" : target = string.Format(format, "/Image/FileExtension/ext_webinfo.gif", alternateString); break;
case "wma" : target = string.Format(format, "/Image/FileExtension/ext_wma.gif" , alternateString); break;
case "wmv" : target = string.Format(format, "/Image/FileExtension/ext_wmv.gif" , alternateString); break;
case "wmx" : target = string.Format(format, "/Image/FileExtension/ext_wmx.gif" , alternateString); break;
case "wri" : target = string.Format(format, "/Image/FileExtension/ext_wri.gif" , alternateString); break;
case "wvx" : target = string.Format(format, "/Image/FileExtension/ext_wvx.gif" , alternateString); break;
case "xls" : target = string.Format(format, "/Image/FileExtension/ext_xls.gif" , alternateString); break;
case "xml" : target = string.Format(format, "/Image/FileExtension/ext_xml.gif" , alternateString); break;
case "zip" : target = string.Format(format, "/Image/FileExtension/ext_zip.gif" , alternateString); break;
default : target = string.Format(format, "/Image/FileExtension/ext_unknown.gif", alternateString); break;
}
return target;
}
#endregion
}
}
▶ StringHelper.cs
using System.Globalization;
namespace TestProject
{
/// <summary>
/// 문자열 헬퍼
/// </summary>
public static class StringHelper
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Public
#region 문자열 잘라내기 - CutString(source, length)
/// <summary>
/// 문자열 잘라내기
/// </summary>
/// <param name="source">소스 문자열</param>
/// <param name="length">길이</param>
/// <returns>잘라낸 문자열</returns>
public static string CutString(this string source, int length)
{
if(source.Length > (length - 3))
{
return source.Substring(0, length - 3) + "...";
}
return source;
}
#endregion
#region 유니코드 문자열 잘라내기 - CutUnicodeString(source, length)
/// <summary>
/// 유니코드 문자열 잘라내기
/// </summary>
/// <param name="source">소스 문자열</param>
/// <param name="length">길이</param>
/// <returns>잘라낸 유니코드 문자열</returns>
public static string CutUnicodeString(this string source, int length)
{
string target = source;
StringInfo stringInfo = new StringInfo(source);
if(stringInfo.LengthInTextElements > (length - 3))
{
target = stringInfo.SubstringByTextElements(0, length - 3) + "...";
}
return target;
}
#endregion
}
}
▶ Models/BoardWriteFormType.cs
namespace TestProject.Models
{
/// <summary>
/// 게시판 쓰기 폼 타입
/// </summary>
public enum BoardWriteFormType
{
/// <summary>
/// 글 쓰기 페이지
/// </summary>
Write,
/// <summary>
/// 글 수정 페이지
/// </summary>
Modify,
/// <summary>
/// 글 답변 페이지
/// </summary>
Reply
}
}
▶ ContentEncodingType.cs
namespace TestProject.Models
{
/// <summary>
/// 내용 인코딩 타입
/// </summary>
public enum ContentEncodingType
{
/// <summary>
/// 텍스트
/// </summary>
Text,
/// <summary>
/// HTML로 실행
/// </summary>
HTML,
/// <summary>
/// HTML + 개행(\r\n) 적용
/// </summary>
Mixed
}
}
▶ Models/NoticeModel.cs
using System;
using System.ComponentModel.DataAnnotations;
namespace TestProject.Models
{
/// <summary>
/// 게시글 모델
/// </summary>
public class NoticeModel
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Public
#region ID - ID
/// <summary>
/// ID
/// </summary>
[Display(Name = "ID")]
public int ID { get; set; }
#endregion
#region 작성자명 - Name
/// <summary>
/// 작성자명
/// </summary>
[Display(Name = "작성자명")]
[Required(ErrorMessage = "작성자명을 입력해 주시기 바랍니다.")]
public string Name { get; set; }
#endregion
#region 메일 주소 - MailAddress
/// <summary>
/// 메일 주소
/// </summary>
[EmailAddress(ErrorMessage = "메일 주소를 입력해 주시기 바랍니다.")]
public string MailAddress { get; set; }
#endregion
#region 제목 - Title
/// <summary>
/// 제목
/// </summary>
[Display(Name = "제목")]
[Required(ErrorMessage = "제목을 입력해 주시기 바랍니다.")]
public string Title { get; set; }
#endregion
#region 작성일 - WriteDate
/// <summary>
/// 작성일
/// </summary>
[Display(Name = "작성일")]
public DateTime WriteDate { get; set; }
#endregion
#region 작성 IP - WriteIP
/// <summary>
/// 작성 IP
/// </summary>
public string WriteIP { get; set; }
#endregion
#region 내용 - Content
/// <summary>
/// 내용
/// </summary>
[Display(Name = "내용")]
[Required(ErrorMessage = "내용을 입력해 주시기 바랍니다.")]
public string Content { get; set; }
#endregion
#region 패스워드 - Password
/// <summary>
/// 패스워드
/// </summary>
[Display(Name = "패스워드")]
[Required(ErrorMessage = "패스워드를 입력해 주시기 바랍니다.")]
public string Password { get; set; }
#endregion
#region 조회 수 - ReadCount
/// <summary>
/// 조회 수
/// </summary>
[Display(Name = "조회 수")]
public int ReadCount { get; set; }
#endregion
#region 인코딩 - Encoding
/// <summary>
/// 인코딩
/// </summary>
[Display(Name = "인코딩")]
public string Encoding { get; set; } = "Text";
#endregion
#region 홈페이지 - Homepage
/// <summary>
/// 홈페이지
/// </summary>
public string Homepage { get; set; }
#endregion
#region 수정일 - UpdateDate
/// <summary>
/// 수정일
/// </summary>
public DateTime UpdateDate { get; set; }
#endregion
#region 수정 IP - UpdateIP
/// <summary>
/// 수정 IP
/// </summary>
public string UpdateIP { get; set; }
#endregion
#region 파일명 - FileName
/// <summary>
/// 파일명
/// </summary>
[Display(Name = "파일명")]
public string FileName { get; set; }
#endregion
#region 파일 크기 - FileSize
/// <summary>
/// 파일 크기
/// </summary>
public int FileSize { get; set; }
#endregion
#region 다운로드 수 - DownloadCount
/// <summary>
/// 다운로드 수
/// </summary>
public int DownloadCount { get; set; }
#endregion
#region 참조 ID - ReferenceID
/// <summary>
/// 참조 ID
/// </summary>
public int ReferenceID { get; set; }
#endregion
#region 답변 레벨 - ReplyLevel
/// <summary>
/// 답변 레벨
/// </summary>
public int ReplyLevel { get; set; }
#endregion
#region 답변 순서 - ReplyOrder
/// <summary>
/// 답변 순서
/// </summary>
public int ReplyOrder { get; set; }
#endregion
#region 답변 수 - ReplyCount
/// <summary>
/// 답변 수
/// </summary>
public int ReplyCount { get; set; }
#endregion
#region 부모 ID - ParentID
/// <summary>
/// 부모 ID
/// </summary>
public int ParentID { get; set; }
#endregion
#region 댓글 수 - CommentCount
/// <summary>
/// 댓글 수
/// </summary>
public int CommentCount { get; set; }
#endregion
#region 카테고리 - Category
/// <summary>
/// 카테고리
/// </summary>
public string Category { get; set; } = "FREE";
#endregion
}
}
▶ Models/CommentModel.cs
using System;
using System.ComponentModel.DataAnnotations;
namespace TestProject.Models
{
/// <summary>
/// 댓글 모델
/// </summary>
public class CommentModel
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Public
#region ID - ID
/// <summary>
/// ID
/// </summary>
public int ID { get; set; }
#endregion
#region 게시판 ID - NoticeID
/// <summary>
/// 게시판 ID
/// </summary>
public int NoticeID { get; set; }
#endregion
#region 작성자명 - Name
/// <summary>
/// 작성자명
/// </summary>
[Required(ErrorMessage = "작성자명을 입력해 주시기 바랍니다.")]
public string Name { get; set; }
#endregion
#region 댓글 - Comment
/// <summary>
/// 댓글
/// </summary>
[Required(ErrorMessage = "댓글을 입력해 주시기 바랍니다.")]
public string Comment { get; set; }
#endregion
#region 작성일 - WriteDate
/// <summary>
/// 작성일
/// </summary>
public DateTime WriteDate { get; set; }
#endregion
#region 패스워드 - Password
/// <summary>
/// 패스워드
/// </summary>
[Required(ErrorMessage = "패스워드를 입력해 주시기 바랍니다.")]
public string Password { get; set; }
#endregion
}
}
▶ Models/NoticeCommentModel.cs
using System.Collections.Generic;
namespace TestProject.Models
{
/// <summary>
/// 게시판 댓글 모델
/// </summary>
public class NoticeCommentModel
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 게시판 ID - NoticeID
/// <summary>
/// 게시판 ID
/// </summary>
public int NoticeID { get; set; }
#endregion
#region 댓글 리스트 - CommentList
/// <summary>
/// 댓글 리스트
/// </summary>
public List<CommentModel> CommentList { get; set; }
#endregion
}
}
▶ Models/INoticeRepository.cs
using System.Collections.Generic;
namespace TestProject.Models
{
/// <summary>
/// 게시판 저장소 인터페이스
/// </summary>
public interface INoticeRepository
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
#region 게시판 저장하기 - SaveNotice(notice, formType)
/// <summary>
/// 게시판 저장하기
/// </summary>
/// <param name="notice">게시판</param>
/// <param name="formType">폼 타입</param>
/// <returns>처리 레코드 수</returns>
int SaveNotice(NoticeModel notice, BoardWriteFormType formType);
#endregion
#region 게시판 쓰기 - WriteNotice(notice)
/// <summary>
/// 게시판 쓰기
/// </summary>
/// <param name="notice">게시판</param>
void WriteNotice(NoticeModel notice);
#endregion
#region 게시판 수정하기 - UpdateNotice(notice)
/// <summary>
/// 게시판 수정하기
/// </summary>
/// <param name="notice">게시판</param>
/// <returns>처리 레코드 수</returns>
int UpdateNotice(NoticeModel notice);
#endregion
#region 게시판 답변하기 - ReplyNotice(notice)
/// <summary>
/// 게시판 답변하기
/// </summary>
/// <param name="notice">게시판</param>
void ReplyNotice(NoticeModel notice);
#endregion
#region 게시판 삭제하기 - DeleteNotice(id, password)
/// <summary>
/// 게시판 삭제하기
/// </summary>
/// <param name="id">ID</param>
/// <param name="password">패스워드</param>
/// <returns>처리 레코드 수</returns>
int DeleteNotice(int id, string password);
#endregion
#region 카운트 구하기 - GetCount()
/// <summary>
/// 카운트 구하기
/// </summary>
int GetCount();
#endregion
#region 카운트 구하기 - GetCount(searchField, searchQuery)
/// <summary>
/// 카운트 구하기
/// </summary>
/// <param name="searchField">검색 필드</param>
/// <param name="searchQuery">검색 쿼리</param>
/// <returns>카운트</returns>
int GetCount(string searchField, string searchQuery);
#endregion
#region 게시판 리스트 구하기 - GetNoticeList(int pageIndex)
/// <summary>
/// 게시판 리스트 구하기
/// </summary>
/// <param name="pageIndex">페이지 인덱스</param>
List<NoticeModel> GetNoticeList(int pageIndex);
#endregion
#region 게시판 리스트 구하기 - GetNoticeList(pageIndex, searchField, searchQuery)
/// <summary>
/// 게시판 리스트 구하기
/// </summary>
/// <param name="pageIndex">페이지 인덱스</param>
/// <param name="searchField">검색 필드</param>
/// <param name="searchQuery">검색 쿼리</param>
/// <returns>게시판 리스트</returns>
List<NoticeModel> GetNoticeList(int pageIndex, string searchField, string searchQuery);
#endregion
#region 게시판 보기 - GetNotice(id)
/// <summary>
/// 게시판 보기
/// </summary>
NoticeModel GetNotice(int id);
#endregion
#region 파일명 구하기 - GetFileName(id)
/// <summary>
/// 파일명 구하기
/// </summary>
/// <param name="id">ID</param>
/// <returns>파일명</returns>
string GetFileName(int id);
#endregion
#region 다운로드 카운트 수정하기 - UpdateDownloadCount(id)
/// <summary>
/// 다운로드 카운트 수정하기
/// </summary>
/// <param name="id">ID</param>
void UpdateDownloadCount(int id);
#endregion
#region 다운로드 카운트 수정하기 - UpdateDownloadCount(fileName)
/// <summary>
/// 다운로드 카운트 수정하기
/// </summary>
/// <param name="fileName">파일명</param>
void UpdateDownloadCount(string fileName);
#endregion
#region 사진이 있는 최근 게시판 리스트 구하기 - GetRecentPhotoNoticeList()
/// <summary>
/// 사진이 있는 최근 게시판 리스트 구하기
/// </summary>
/// <returns>사진이 있는 최근 게시판 리스트</returns>
List<NoticeModel> GetRecentPhotoNoticeList();
#endregion
#region 특정 카테고리 최근 게시판 리스트 구하기 - GetRecentCategoryNoticeList(category)
/// <summary>
/// 특정 카테고리 최근 게시판 리스트 구하기
/// </summary>
/// <returns>특정 카테고리 최근 게시판 리스트</returns>
List<NoticeModel> GetRecentCategoryNoticeList(string category);
#endregion
#region 최근 게시판 리스트 구하기 - GetRecentNoticeList()
/// <summary>
/// 최근 게시판 리스트 구하기
/// </summary>
/// <returns>최근 게시판 리스트</returns>
List<NoticeModel> GetRecentNoticeList();
#endregion
#region 최근 게시판 리스트 구하기 - GetRecentNoticeList(noticeCount)
/// <summary>
/// 최근 게시판 리스트 구하기
/// </summary>
/// <param name="noticeCount">게시판 수</param>
/// <returns>최근 게시판 리스트</returns>
List<NoticeModel> GetRecentNoticeList(int noticeCount);
#endregion
#region 게시판 고정하기 - PinNotice(id)
/// <summary>
/// 게시판 고정하기
/// </summary>
/// <param name="id">ID</param>
/// <remarks>특정 게시글을 공지사항으로 올리기</remarks>
void PinNotice(int id);
#endregion
}
}
▶ Models/NoticeRepository.cs
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using Dapper;
namespace TestProject.Models
{
/// <summary>
/// 게시판 저장소
/// </summary>
public class NoticeRepository : INoticeRepository
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 구성
/// </summary>
private IConfiguration configuration;
/// <summary>
/// 로그 기록기
/// </summary>
private ILogger<NoticeRepository> logger;
/// <summary>
/// 연결
/// </summary>
private SqlConnection connection;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - NoticeRepository(config, logger)
/// <summary>
/// 생성자
/// </summary>
public NoticeRepository(IConfiguration config, ILogger<NoticeRepository> logger)
{
this.configuration = config;
this.connection = new SqlConnection
(
this.configuration.GetSection("ConnectionStrings").GetSection("DefaultConnection").Value
);
this.logger = logger;
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 게시판 저장하기 - SaveNotice(notice, formType)
/// <summary>
/// 게시판 저장하기
/// </summary>
/// <param name="notice">게시판</param>
/// <param name="formType">폼 타입</param>
/// <returns>처리 레코드 수</returns>
public int SaveNotice(NoticeModel notice, BoardWriteFormType formType)
{
int recordCount = 0;
DynamicParameters dynamicParameters = new DynamicParameters();
dynamicParameters.Add("@Name" , value : notice.Name , dbType : DbType.String);
dynamicParameters.Add("@MailAddress", value : notice.MailAddress, dbType : DbType.String);
dynamicParameters.Add("@Title" , value : notice.Title , dbType : DbType.String);
dynamicParameters.Add("@Content" , value : notice.Content , dbType : DbType.String);
dynamicParameters.Add("@Password" , value : notice.Password , dbType : DbType.String);
dynamicParameters.Add("@Encoding" , value : notice.Encoding , dbType : DbType.String);
dynamicParameters.Add("@Homepage" , value : notice.Homepage , dbType : DbType.String);
dynamicParameters.Add("@FileName" , value : notice.FileName , dbType : DbType.String);
dynamicParameters.Add("@FileSize" , value : notice.FileSize , dbType : DbType.Int32 );
switch(formType)
{
case BoardWriteFormType.Write :
dynamicParameters.Add("@WriteIP", value: notice.WriteIP, dbType : DbType.String);
recordCount = this.connection.Execute
(
"WriteNotice",
dynamicParameters,
commandType : CommandType.StoredProcedure
);
break;
case BoardWriteFormType.Modify :
dynamicParameters.Add("@UpdateIP", value : notice.UpdateIP, dbType : DbType.String);
dynamicParameters.Add("@ID" , value : notice.ID , dbType : DbType.Int32 );
recordCount = this.connection.Execute
(
"UpdateNotice",
dynamicParameters,
commandType : CommandType.StoredProcedure
);
break;
case BoardWriteFormType.Reply :
dynamicParameters.Add("@WriteIP" , value : notice.WriteIP , dbType : DbType.String);
dynamicParameters.Add("@ParentID", value : notice.ParentID, dbType : DbType.Int32 );
recordCount = this.connection.Execute
(
"ReplyNotice",
dynamicParameters,
commandType : CommandType.StoredProcedure
);
break;
}
return recordCount;
}
#endregion
#region 게시판 쓰기 - WriteNotice(notice)
/// <summary>
/// 게시판 쓰기
/// </summary>
/// <param name="notice">게시판</param>
public void WriteNotice(NoticeModel notice)
{
this.logger.LogInformation("EXECITE WRITE NOTICE");
try
{
SaveNotice(notice, BoardWriteFormType.Write);
}
catch(Exception exception)
{
this.logger.LogError($"ERROR WRITE NOTICE : {exception}");
}
}
#endregion
#region 게시판 수정하기 - UpdateNotice(notice)
/// <summary>
/// 게시판 수정하기
/// </summary>
/// <param name="notice">게시판</param>
/// <returns>처리 레코드 수</returns>
public int UpdateNotice(NoticeModel notice)
{
int recordCount = 0;
this.logger.LogInformation("EXECUTE UPDATE NOTICE");
try
{
recordCount = SaveNotice(notice, BoardWriteFormType.Modify);
}
catch(Exception exception)
{
this.logger.LogError($"ERROR EXECUTE UPDATE NOTICE {exception}");
}
return recordCount;
}
#endregion
#region 게시판 답변하기 - ReplyNotice(notice)
/// <summary>
/// 게시판 답변하기
/// </summary>
/// <param name="notice">게시판</param>
public void ReplyNotice(NoticeModel notice)
{
this.logger.LogInformation("EXECUTE REPLY NOTICE");
try
{
SaveNotice(notice, BoardWriteFormType.Reply);
}
catch(Exception exception)
{
this.logger.LogError($"ERROR REPLY NOTICE {exception}");
}
}
#endregion
#region 게시판 삭제하기 - DeleteNotice(id, password)
/// <summary>
/// 게시판 삭제하기
/// </summary>
/// <param name="id">ID</param>
/// <param name="password">패스워드</param>
/// <returns>처리 레코드 수</returns>
public int DeleteNotice(int id, string password)
{
return this.connection.Execute
(
"DeleteNotice",
new { ID = id, Password = password },
commandType : CommandType.StoredProcedure
);
}
#endregion
#region 카운트 구하기 - GetCount()
/// <summary>
/// 카운트 구하기
/// </summary>
public int GetCount()
{
this.logger.LogInformation("EXECUTE GET COUNT");
try
{
return this.connection.Query<int>("SELECT COUNT(*) FROM dbo.Notice").SingleOrDefault();
}
catch(Exception exception)
{
this.logger.LogError($"ERROR GET COUNT : {exception}");
return -1;
}
}
#endregion
#region 카운트 구하기 - GetCount(searchField, searchQuery)
/// <summary>
/// 카운트 구하기
/// </summary>
/// <param name="searchField">검색 필드</param>
/// <param name="searchQuery">검색 쿼리</param>
/// <returns>카운트</returns>
public int GetCount(string searchField, string searchQuery)
{
this.logger.LogInformation("EXECUTE GET COUNT (SEARCH FIELD, SEARCH QUERY)");
try
{
return this.connection.Query<int>
(
"SearchNoticeCount",
new { SearchField = searchField, SearchQuery = searchQuery },
commandType : CommandType.StoredProcedure
)
.SingleOrDefault();
}
catch(Exception exception)
{
this.logger.LogError($"ERROR GET COUNT (SEARCH FIELD, SEARCH QUERY) : {exception}");
return -1;
}
}
#endregion
#region 게시판 리스트 구하기 - GetNoticeList(int pageIndex)
/// <summary>
/// 게시판 리스트 구하기
/// </summary>
/// <param name="pageIndex">페이지 인덱스</param>
public List<NoticeModel> GetNoticeList(int pageIndex)
{
this.logger.LogInformation("EXECUTE GET NOTICE LIST");
try
{
DynamicParameters dynamicParameters = new DynamicParameters(new { Page = pageIndex });
return this.connection.Query<NoticeModel>
(
"ListNotice",
dynamicParameters,
commandType : CommandType.StoredProcedure
)
.ToList();
}
catch(Exception exception)
{
this.logger.LogError($"ERROR GET NOTICE LIST : {exception}");
return null;
}
}
#endregion
#region 게시판 리스트 구하기 - GetNoticeList(pageIndex, searchField, searchQuery)
/// <summary>
/// 게시판 리스트 구하기
/// </summary>
/// <param name="pageIndex">페이지 인덱스</param>
/// <param name="searchField">검색 필드</param>
/// <param name="searchQuery">검색 쿼리</param>
/// <returns>게시판 리스트</returns>
public List<NoticeModel> GetNoticeList(int pageIndex, string searchField, string searchQuery)
{
DynamicParameters dynamicParameters = new DynamicParameters
(
new
{
Page = pageIndex,
SearchField = searchField,
SearchQuery = searchQuery
}
);
return this.connection.Query<NoticeModel>
(
"SearchNotice",
dynamicParameters, commandType: CommandType.StoredProcedure).ToList();
}
#endregion
#region 게시판 보기 - GetNotice(id)
/// <summary>
/// 게시판 보기
/// </summary>
public NoticeModel GetNotice(int id)
{
DynamicParameters dynamicParameters = new DynamicParameters(new { ID = id });
return this.connection.Query<NoticeModel>
(
"ViewNotice",
dynamicParameters,
commandType : CommandType.StoredProcedure
)
.SingleOrDefault();
}
#endregion
#region 파일명 구하기 - GetFileName(id)
/// <summary>
/// 파일명 구하기
/// </summary>
/// <param name="id">ID</param>
/// <returns>파일명</returns>
public string GetFileName(int id)
{
return this.connection.Query<string>
(
"SELECT FileName FROM dbo.Notice WHERE ID = @ID",
new { ID = id }
)
.SingleOrDefault();
}
#endregion
#region 다운로드 카운트 수정하기 - UpdateDownloadCount(id)
/// <summary>
/// 다운로드 카운트 수정하기
/// </summary>
/// <param name="id">ID</param>
public void UpdateDownloadCount(int id)
{
DynamicParameters dynamicParameters = new DynamicParameters(new { ID = id });
this.connection.Execute
(
"UPDATE dbo.Notice SET DownloadCount = DownloadCount + 1 WHERE ID = @ID",
dynamicParameters,
commandType : CommandType.Text
);
}
#endregion
#region 다운로드 카운트 수정하기 - UpdateDownloadCount(fileName)
/// <summary>
/// 다운로드 카운트 수정하기
/// </summary>
/// <param name="fileName">파일명</param>
public void UpdateDownloadCount(string fileName)
{
this.connection.Execute
(
"UPDATE dbo.Notice SET DownloadCount = DownloadCount + 1 WHERE FileName = @FileName",
new { FileName = fileName }
);
}
#endregion
#region 사진이 있는 최근 게시판 리스트 구하기 - GetRecentPhotoNoticeList()
/// <summary>
/// 사진이 있는 최근 게시판 리스트 구하기
/// </summary>
/// <returns>사진이 있는 최근 게시판 리스트</returns>
public List<NoticeModel> GetRecentPhotoNoticeList()
{
string sql = @"
SELECT TOP 4
ID
,Title
,FileName
,FileSize
FROM dbo.Notice
WHERE FileName LIKE '%.png'
OR FileName LIKE '%.jpg'
OR FileName LIKE '%.jpeg'
OR FileName LIKE '%.gif'
ORDER BY ID DESC
";
return this.connection.Query<NoticeModel>(sql).ToList();
}
#endregion
#region 특정 카테고리 최근 게시판 리스트 구하기 - GetRecentCategoryNoticeList(category)
/// <summary>
/// 특정 카테고리 최근 게시판 리스트 구하기
/// </summary>
/// <returns>특정 카테고리 최근 게시판 리스트</returns>
public List<NoticeModel> GetRecentCategoryNoticeList(string category)
{
string sql = @"
SELECT TOP 3
ID
,Title
,Name
,WriteDate
,FileName
,FileSize
,ReadCount
,CommentCount
,ReplyLevel
FROM dbo.Notice
WHERE Category = @Category
ORDER BY ID DESC
";
return this.connection.Query<NoticeModel>(sql, new { Category = category }).ToList();
}
#endregion
#region 최근 게시판 리스트 구하기 - GetRecentNoticeList()
/// <summary>
/// 최근 게시판 리스트 구하기
/// </summary>
/// <returns>최근 게시판 리스트</returns>
public List<NoticeModel> GetRecentNoticeList()
{
string sql = "SELECT TOP 3 ID, Title, Name, WriteDate FROM dbo.Notice ORDER BY ID DESC";
return this.connection.Query<NoticeModel>(sql).ToList();
}
#endregion
#region 최근 게시판 리스트 구하기 - GetRecentNoticeList(noticeCount)
/// <summary>
/// 최근 게시판 리스트 구하기
/// </summary>
/// <param name="noticeCount">게시판 수</param>
/// <returns>최근 게시판 리스트</returns>
public List<NoticeModel> GetRecentNoticeList(int noticeCount)
{
string sql = $"SELECT TOP {noticeCount} ID, Title, Name, WriteDate FROM dbo.Notice ORDER BY ID DESC";
return this.connection.Query<NoticeModel>(sql).ToList();
}
#endregion
#region 게시판 고정하기 - PinNotice(id)
/// <summary>
/// 게시판 고정하기
/// </summary>
/// <param name="id">ID</param>
/// <remarks>특정 게시글을 공지사항으로 올리기</remarks>
public void PinNotice(int id)
{
this.connection.Execute
(
"UPDATE dbo.Notice SET CATEGORY = 'Notice' WHERE ID = @ID",
new { ID = id }
);
}
#endregion
}
}
▶ Models/ICommentRepository.cs
using System.Collections.Generic;
namespace TestProject.Models
{
/// <summary>
/// 댓글 저장소
/// </summary>
public interface ICommentRepository
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
#region 댓글 추가하기 - WriteComment(comment)
/// <summary>
/// 댓글 추가하기
/// </summary>
/// <param name="comment">댓글</param>
void WriteComment(CommentModel comment);
#endregion
#region 댓글 리스트 구하기 - GetCommentList(noticeID)
/// <summary>
/// 댓글 리스트 구하기
/// </summary>
/// <param name="noticeID">게시판 ID</param>
/// <returns>댓글 리스트</returns>
List<CommentModel> GetCommentList(int noticeID);
#endregion
#region 카운트 구하기 - GetCount(noticeID, id, password)
/// <summary>
/// 카운트 구하기
/// </summary>
/// <param name="noticeID">게시판 ID</param>
/// <param name="id">ID</param>
/// <param name="password">패스워드</param>
/// <returns>카운트</returns>
int GetCount(int noticeID, int id, string password);
#endregion
#region 댓글 삭제하기 - DeleteComment(noticeID, id, password)
/// <summary>
/// 댓글 삭제하기
/// </summary>
/// <param name="noticeID">게시판 ID</param>
/// <param name="id">ID</param>
/// <param name="password">패스워드</param>
int DeleteComment(int noticeID, int id, string password);
#endregion
#region 최근 댓글 리스트 구하기 - GetRecentCommentList()
/// <summary>
/// 최근 댓글 리스트 구하기
/// </summary>
/// <returns>최근 댓글 리스트</returns>
List<CommentModel> GetRecentCommentList();
#endregion
}
}
▶ Models/CommentRepository.cs
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using Dapper;
namespace TestProject.Models
{
/// <summary>
/// 댓글 저장소
/// </summary>
public class CommentRepository : ICommentRepository
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 연결
/// </summary>
private SqlConnection connection;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - CommentRepository(connectionString)
/// <summary>
/// 생성자
/// </summary>
/// <param name="connectionString">연결 문자열</param>
public CommentRepository(string connectionString)
{
this.connection = new SqlConnection(connectionString);
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 댓글 추가하기 - WriteComment(comment)
/// <summary>
/// 댓글 추가하기
/// </summary>
/// <param name="comment">댓글</param>
public void WriteComment(CommentModel comment)
{
DynamicParameters dynamicParameters = new DynamicParameters();
dynamicParameters.Add("@NoticeID", value : comment.NoticeID, dbType : DbType.Int32 );
dynamicParameters.Add("@Name" , value : comment.Name , dbType : DbType.String);
dynamicParameters.Add("@Comment" , value : comment.Comment , dbType : DbType.String);
dynamicParameters.Add("@Password", value : comment.Password, dbType : DbType.String);
string sql = @"
INSERT INTO dbo.NoticeComment
(
NoticeID,
[Name],
Comment,
Password
)
Values
(
@NoticeID,
@Name,
@Comment,
@Password
);
UPDATE dbo.Notice
SET CommentCount = CommentCount + 1
Where ID = @NoticeID
";
this.connection.Execute(sql, dynamicParameters, commandType : CommandType.Text);
}
#endregion
#region 댓글 리스트 구하기 - GetCommentList(noticeID)
/// <summary>
/// 댓글 리스트 구하기
/// </summary>
/// <param name="noticeID">게시판 ID</param>
/// <returns>댓글 리스트</returns>
public List<CommentModel> GetCommentList(int noticeID)
{
return this.connection.Query<CommentModel>
(
"SELECT * FROM dbo.NoticeComment WHERE NoticeID = @NoticeID",
new { NoticeID = noticeID },
commandType : CommandType.Text
)
.ToList();
}
#endregion
#region 카운트 구하기 - GetCount(noticeID, id, password)
/// <summary>
/// 카운트 구하기
/// </summary>
/// <param name="noticeID">게시판 ID</param>
/// <param name="id">ID</param>
/// <param name="password">패스워드</param>
/// <returns>카운트</returns>
public int GetCount(int noticeID, int id, string password)
{
return this.connection.Query<int>
(
"SELECT COUNT(*) FROM dbo.NoticeComment WHERE NoticeID = @NoticeID AND ID = @ID AND Password = @Password",
new { NoticeID = noticeID, ID = id, Password = password },
commandType : CommandType.Text
)
.SingleOrDefault();
}
#endregion
#region 댓글 삭제하기 - DeleteComment(noticeID, id, password)
/// <summary>
/// 댓글 삭제하기
/// </summary>
/// <param name="noticeID">게시판 ID</param>
/// <param name="id">ID</param>
/// <param name="password">패스워드</param>
public int DeleteComment(int noticeID, int id, string password)
{
return this.connection.Execute
(
@"
DELETE dbo.NoticeComment
WHERE NoticeID = @NoticeID
And ID = @ID
And Password = @Password;
UPDATE dbo.Notice
SET CommentCount = CommentCount - 1
WHERE ID = @NoticeID",
new { NoticeID = noticeID, Id = id, Password = password },
commandType : CommandType.Text
);
}
#endregion
#region 최근 댓글 리스트 구하기 - GetRecentCommentList()
/// <summary>
/// 최근 댓글 리스트 구하기
/// </summary>
/// <returns>최근 댓글 리스트</returns>
public List<CommentModel> GetRecentCommentList()
{
string sql = "SELECT TOP 3 ID, NoticeID, Comment, WriteDate FROM dbo.NoticeComment ORDER BY ID DESC";
return this.connection.Query<CommentModel>(sql).ToList();
}
#endregion
}
}
▶ Controllers/NoticeBoardController.cs
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using TestProject.Models;
namespace TestProject.Controllers
{
/// <summary>
/// 게시판 컨트롤러
/// </summary>
public class NoticeBoardController : Controller
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 환경 변수
/// </summary>
private IWebHostEnvironment environment;
/// <summary>
/// 게시판 저장소
/// </summary>
private INoticeRepository noticeRepository;
/// <summary>
/// 댓글 저장소
/// </summary>
private ICommentRepository commentRepository;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 검색 모드 - SearchMode
/// <summary>
/// 검색 모드
/// </summary>
public bool SearchMode { get; set; } = false;
#endregion
#region 검색 필드 - SearchField
/// <summary>
/// 검색 필드
/// </summary>
public string SearchField { get; set; }
#endregion
#region 검색 쿼리 - SearchQuery
/// <summary>
/// 검색 쿼리
/// </summary>
public string SearchQuery { get; set; }
#endregion
#region 페이지 인덱스 - PageIndex
/// <summary>
/// 페이지 인덱스
/// </summary>
public int PageIndex { get; set; } = 0;
#endregion
#region 레코드 카운트 - RecordCount
/// <summary>
/// 레코드 카운트
/// </summary>
public int RecordCount { get; set; } = 0;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - NoticeBoardController(environment, noticeRepository, commentRepository)
/// <summary>
/// 생성자
/// </summary>
/// <param name="environment">환경</param>
/// <param name="noticeRepository">게시판 저장소</param>
/// <param name="commentRepository">댓글 저장소</param>
public NoticeBoardController(IWebHostEnvironment environment, INoticeRepository noticeRepository, ICommentRepository commentRepository)
{
this.environment = environment;
this.noticeRepository = noticeRepository;
this.commentRepository = commentRepository;
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 인덱스 페이지 처리하기 - Index()
/// <summary>
/// 인덱스 페이지 처리하기
/// </summary>
/// <returns>액션 결과</returns>
public IActionResult Index()
{
SearchMode = (!string.IsNullOrEmpty(Request.Query["SearchField"]) && !string.IsNullOrEmpty(Request.Query["SearchQuery"]));
if(SearchMode)
{
SearchField = Request.Query["SearchField"].ToString();
SearchQuery = Request.Query["SearchQuery"].ToString();
}
if(!string.IsNullOrEmpty(Request.Query["Page"].ToString()))
{
PageIndex = Convert.ToInt32(Request.Query["Page"]) - 1;
}
if(!string.IsNullOrEmpty(Request.Cookies["TestProjectPageNumber"]))
{
if(!String.IsNullOrEmpty(Request.Cookies["TestProjectPageNumber"]))
{
PageIndex = Convert.ToInt32(Request.Cookies["TestProjectPageNumber"]);
}
else
{
PageIndex = 0;
}
}
IEnumerable<NoticeModel> noticeEnumerable;
if(!SearchMode)
{
RecordCount = this.noticeRepository.GetCount();
noticeEnumerable = this.noticeRepository.GetNoticeList(PageIndex);
}
else
{
RecordCount = this.noticeRepository.GetCount(SearchField, SearchQuery);
noticeEnumerable = this.noticeRepository.GetNoticeList(PageIndex, SearchField, SearchQuery);
}
ViewBag.RecordCount = RecordCount;
ViewBag.SearchMode = SearchMode;
ViewBag.SearchField = SearchField;
ViewBag.SearchQuery = SearchQuery;
return View(noticeEnumerable);
}
#endregion
#region 생성 페이지 처리하기 - Create()
/// <summary>
/// 생성 페이지 처리하기
/// </summary>
/// <returns>액션 결과</returns>
[HttpGet]
public IActionResult Create()
{
ViewBag.FormType = BoardWriteFormType.Write;
ViewBag.TitleDescription = "게시글 쓰기 - 다음 항목들을 입력해 주시기 바랍니다.";
ViewBag.SaveButtonText = "저장";
return View();
}
#endregion
#region 생성 페이지 처리하기 - Create(sourceNotice, formFileCollection)
/// <summary>
/// 생성 페이지 처리하기
/// </summary>
/// <param name="sourceNotice">소스 게시글</param>
/// <param name="formFileCollection">폼 파일 컬렉션</param>
/// <returns>액션 결과</returns>
[HttpPost]
public async Task<IActionResult> Create(NoticeModel sourceNotice, ICollection<IFormFile> formFileCollection)
{
string fileName = string.Empty;
int fileSize = 0;
string uploadDirectoryPath = Path.Combine(this.environment.WebRootPath, "upload");
foreach(IFormFile formFile in formFileCollection)
{
if(formFile.Length > 0)
{
fileSize = Convert.ToInt32(formFile.Length);
fileName = FileHelper.GetUniqueFileName
(
uploadDirectoryPath,
Path.GetFileName(ContentDispositionHeaderValue.Parse(formFile.ContentDisposition).FileName.Trim('"'))
);
using(FileStream fileStream = new FileStream(Path.Combine(uploadDirectoryPath, fileName), FileMode.Create))
{
await formFile.CopyToAsync(fileStream);
}
}
}
NoticeModel targetNotice = new NoticeModel();
targetNotice.Name = sourceNotice.Name;
targetNotice.MailAddress = HTMLHelper.Encode(sourceNotice.MailAddress);
targetNotice.Homepage = sourceNotice.Homepage;
targetNotice.Title = HTMLHelper.Encode(sourceNotice.Title);
targetNotice.Content = sourceNotice.Content;
targetNotice.FileName = fileName;
targetNotice.FileSize = fileSize;
targetNotice.Password = sourceNotice.Password;
targetNotice.WriteIP = HttpContext.Connection.RemoteIpAddress.ToString();
targetNotice.Encoding = sourceNotice.Encoding;
this.noticeRepository.WriteNotice(targetNotice);
TempData["Message"] = "게시글이 생성되었습니다.";
return RedirectToAction("Index");
}
#endregion
#region 수정 페이지 처리하기 - Update(id)
/// <summary>
/// 수정 페이지 처리하기
/// </summary>
/// <param name="id">ID</param>
/// <returns>액션 결과</returns>
[HttpGet]
public IActionResult Update(int id)
{
ViewBag.FormType = BoardWriteFormType.Modify;
ViewBag.TitleDescription = "게시글 수정 - 다음 항목들을 수정해 주시기 바랍니다.";
ViewBag.SaveButtonText = "수정";
NoticeModel notice = this.noticeRepository.GetNotice(id);
if(notice.FileName.Length > 1)
{
ViewBag.FileName = notice.FileName;
ViewBag.FileSize = notice.FileSize;
ViewBag.FileNamePrevious = $"기존 업로드 파일명 : {notice.FileName}";
}
else
{
ViewBag.FileName = string.Empty;
ViewBag.FileSize = 0;
}
return View(notice);
}
#endregion
#region 수정 페이지 처리하기 - Update(sourceNotice, formFileCollection, id, previousFileName, previousFileSize)
/// <summary>
/// 수정 페이지 처리하기
/// </summary>
/// <param name="sourceNotice">소스 게시글</param>
/// <param name="formFileCollection">폼 파일 컬렉션</param>
/// <param name="id">ID</param>
/// <param name="previousFileName">이전 파일명</param>
/// <param name="previousFileSize">이전 파일 크기</param>
/// <returns></returns>
[HttpPost]
public async Task<IActionResult> Update
(
NoticeModel sourceNotice,
ICollection<IFormFile> formFileCollection,
int id,
string previousFileName = "",
int previousFileSize = 0
)
{
ViewBag.FormType = BoardWriteFormType.Modify;
ViewBag.TitleDescription = "게시글 수정 - 아래 항목을 수정해 주시기 바랍니다.";
ViewBag.SaveButtonText = "수정";
string fileName = string.Empty;
int fileSize = 0;
if(previousFileName != null)
{
fileName = previousFileName;
fileSize = previousFileSize;
}
string uploadDirectoryPath = Path.Combine(this.environment.WebRootPath, "upload");
foreach(IFormFile formFile in formFileCollection)
{
if(formFile.Length > 0)
{
fileSize = Convert.ToInt32(formFile.Length);
fileName = FileHelper.GetUniqueFileName
(
uploadDirectoryPath,
Path.GetFileName(ContentDispositionHeaderValue.Parse(formFile.ContentDisposition).FileName.Trim('"'))
);
using(FileStream fileStream = new FileStream(Path.Combine(uploadDirectoryPath, fileName), FileMode.Create))
{
await formFile.CopyToAsync(fileStream);
}
}
}
NoticeModel targetNotice = new NoticeModel();
targetNotice.ID = id;
targetNotice.Name = sourceNotice.Name;
targetNotice.MailAddress = HTMLHelper.Encode(sourceNotice.MailAddress);
targetNotice.Homepage = sourceNotice.Homepage;
targetNotice.Title = HTMLHelper.Encode(sourceNotice.Title);
targetNotice.Content = sourceNotice.Content;
targetNotice.FileName = fileName;
targetNotice.FileSize = fileSize;
targetNotice.Password = sourceNotice.Password;
targetNotice.UpdateIP = HttpContext.Connection.RemoteIpAddress.ToString();
targetNotice.Encoding = sourceNotice.Encoding;
int recordCount = this.noticeRepository.UpdateNotice(targetNotice);
if(recordCount > 0)
{
TempData["Message"] = "게시글 수정이 완료되었습니다.";
return RedirectToAction("Details", new { ID = id });
}
else
{
ViewBag.ErrorMessage = "게시글 수정을 실패했습니다. 패스워드를 확인해 주시기 바랍니다.";
return View(targetNotice);
}
}
#endregion
#region 삭제 페이지 처리하기 - Delete(id)
/// <summary>
/// 삭제 페이지 처리하기
/// </summary>
/// <param name="id">ID</param>
/// <returns>액션 결과</returns>
[HttpGet]
public IActionResult Delete(int id)
{
ViewBag.ID = id;
return View();
}
#endregion
#region 삭제 페이지 처리하기 - Delete(id, password)
/// <summary>
/// 삭제 페이지 처리하기
/// </summary>
/// <param name="id">ID</param>
/// <param name="password">패스워드</param>
/// <returns>액션 결과</returns>
[HttpPost]
public IActionResult Delete(int id, string password)
{
if(this.noticeRepository.DeleteNotice(id, password) > 0)
{
TempData["Message"] = "게시글이 삭제되었습니다.";
//return RedirectToAction("DeleteCompleted");
return RedirectToAction("Index");
}
else
{
ViewBag.Message = "게시글 삭제가 실패했습니다. 패스워드를 확인해 주시기 바랍니다.";
}
ViewBag.ID = id;
return View();
}
#endregion
#region 삭제 완료시 페이지 처리하기 - DeleteCompleted()
/// <summary>
/// 삭제 완료시 페이지 처리하기
/// </summary>
/// <returns>액션 결과</returns>
public IActionResult DeleteCompleted()
{
return View();
}
#endregion
#region 상세 페이지 처리하기 - Details(id)
/// <summary>
/// 상세 페이지 처리하기
/// </summary>
/// <param name="id">ID</param>
/// <returns>액션 결과</returns>
public IActionResult Details(int id)
{
NoticeModel notice = this.noticeRepository.GetNotice(id);
ContentEncodingType encodingType = (ContentEncodingType)Enum.Parse(typeof(ContentEncodingType), notice.Encoding);
string targetContent = string.Empty;
switch(encodingType)
{
case ContentEncodingType.Text :
targetContent = HTMLHelper.EncodeIncludingTabAndSpace(notice.Content);
break;
case ContentEncodingType.HTML :
targetContent = notice.Content;
break;
case ContentEncodingType.Mixed :
targetContent = notice.Content.Replace("\r\n", "<br />");
break;
default :
targetContent = notice.Content;
break;
}
ViewBag.Content = targetContent;
if(notice.FileName.Length > 1)
{
ViewBag.FileName = String.Format
(
"<a href='/NoticeBoard/Download?id={0}'>{1} {2} / 전송 수 : {3}</a>",
notice.ID,
"<img src=\"/Image/FileExtension/ext_zip.gif\" border=\"0\">",
notice.FileName,
notice.DownloadCount
);
if(NoticeHelper.IsImageFile(notice.FileName))
{
ViewBag.ImageDownloadURL = $"<img src=\'/NoticeBoard/DownloadImage/{notice.ID}\'><br />";
}
}
else
{
ViewBag.FileName = "(업로드된 파일이 없습니다.)";
}
NoticeCommentModel noticeComment = new NoticeCommentModel();
noticeComment.CommentList = this.commentRepository.GetCommentList(notice.ID);
noticeComment.NoticeID = notice.ID;
ViewBag.NoticeComment = noticeComment;
return View(notice);
}
#endregion
#region 다운로드 페이지 처리하기 - Download(id)
/// <summary>
/// 다운로드 페이지 처리하기
/// </summary>
/// <param name="id">ID</param>
/// <returns>파일 결과</returns>
public FileResult Download(int id)
{
string fileName = string.Empty;
fileName = this.noticeRepository.GetFileName(id);
if(fileName == null)
{
return null;
}
else
{
this.noticeRepository.UpdateDownloadCount(id);
byte[] fileByteArray = System.IO.File.ReadAllBytes
(
Path.Combine
(
this.environment.WebRootPath,
"upload",
fileName
)
);
return File(fileByteArray, "application/octet-stream", fileName);
}
}
#endregion
#region 이미지 다운로드 페이지 처리하기 - DownloadImage(id)
/// <summary>
/// 이미지 다운로드 페이지 처리하기
/// </summary>
/// <param name="id">ID</param>
/// <returns>파일 결과</returns>
public FileResult DownloadImage(int id)
{
string fileName = string.Empty;
fileName = this.noticeRepository.GetFileName(id);
if(fileName == null)
{
return null;
}
else
{
string fileExtension = Path.GetExtension(fileName);
string contentType = string.Empty;
if(fileExtension == ".gif" || fileExtension == ".jpg" || fileExtension == ".jpeg" || fileExtension == ".png")
{
switch(fileExtension)
{
case ".gif" : contentType = "image/gif"; break;
case ".jpg" : contentType = "image/jpeg"; break;
case ".jpeg" : contentType = "image/jpeg"; break;
case ".png" : contentType = "image/png"; break;
}
}
this.noticeRepository.UpdateDownloadCount(fileName);
byte[] fileByteArray = System.IO.File.ReadAllBytes(Path.Combine(this.environment.WebRootPath, "upload") + "\\" + fileName);
return File(fileByteArray, contentType, fileName);
}
}
#endregion
#region 답변 페이지 처리하기 - Reply(id)
/// <summary>
/// 답변 페이지 처리하기
/// </summary>
/// <param name="id">ID</param>
/// <returns>액션 결과</returns>
[HttpGet]
public IActionResult Reply(int id)
{
ViewBag.FormType = BoardWriteFormType.Reply;
ViewBag.TitleDescription = "글 답변 - 다음 항목을 입력해 주시기 바랍니다.";
ViewBag.SaveButtonText = "답변";
NoticeModel notice = this.noticeRepository.GetNotice(id);
NoticeModel newNotice = new NoticeModel();
newNotice.Title = $"Re : {notice.Title}";
newNotice.Content = $"\n\nOn {notice.WriteDate}, '{notice.Name}' wrote:\n----------\n>" +
$"{notice.Content.Replace("\n", "\n>")}\n---------";
return View(newNotice);
}
#endregion
#region 답변 페이지 처리하기 - Reply(sourceNotice, formFileCollection, id)
/// <summary>
/// 답변 페이지 처리하기
/// </summary>
/// <param name="sourceNotice">게시글</param>
/// <param name="formFileCollection">폼 파일 컬렉션</param>
/// <param name="id">ID</param>
/// <returns>액션 결과</returns>
[HttpPost]
public async Task<IActionResult> Reply(NoticeModel sourceNotice, ICollection<IFormFile> formFileCollection, int id)
{
string fileName = string.Empty;
int fileSize = 0;
string uploadDicrectoryPath = Path.Combine(this.environment.WebRootPath, "upload");
foreach(IFormFile formFile in formFileCollection)
{
if(formFile.Length > 0)
{
fileSize = Convert.ToInt32(formFile.Length);
fileName = FileHelper.GetUniqueFileName
(
uploadDicrectoryPath,
Path.GetFileName(ContentDispositionHeaderValue.Parse(formFile.ContentDisposition).FileName.Trim('"'))
);
using(FileStream fileStream = new FileStream(Path.Combine(uploadDicrectoryPath, fileName), FileMode.Create))
{
await formFile.CopyToAsync(fileStream);
}
}
}
NoticeModel targetNotice = new NoticeModel();
targetNotice.ID = targetNotice.ParentID = Convert.ToInt32(id);
targetNotice.Name = sourceNotice.Name;
targetNotice.MailAddress = HTMLHelper.Encode(sourceNotice.MailAddress);
targetNotice.Homepage = sourceNotice.Homepage;
targetNotice.Title = HTMLHelper.Encode(sourceNotice.Title);
targetNotice.Content = sourceNotice.Content;
targetNotice.FileName = fileName;
targetNotice.FileSize = fileSize;
targetNotice.Password = sourceNotice.Password;
targetNotice.WriteIP = HttpContext.Connection.RemoteIpAddress.ToString();
targetNotice.Encoding = sourceNotice.Encoding;
this.noticeRepository.ReplyNotice(targetNotice);
TempData["Message"] = "답변 글이 저장되었습니다.";
return RedirectToAction("Index");
}
#endregion
#region 댓글 추가 페이지 처리하기 - AddComment(noticeID, name, password, comment)
/// <summary>
/// 댓글 추가 페이지 처리하기
/// </summary>
/// <param name="noticeID">게시글 ID</param>
/// <param name="name">성명</param>
/// <param name="password">패스워드</param>
/// <param name="comment">댓글</param>
/// <returns>액션 결과</returns>
[HttpPost]
public IActionResult AddComment(int noticeID, string name, string password, string comment)
{
CommentModel newComment = new CommentModel();
newComment.NoticeID = noticeID;
newComment.Name = name;
newComment.Password = password;
newComment.Comment = comment;
this.commentRepository.WriteComment(newComment);
return RedirectToAction("Details", new { ID = noticeID });
}
#endregion
#region 댓글 삭제 페이지 처리하기 - DeleteComment(noticeID, id)
/// <summary>
/// 댓글 삭제 페이지 처리하기
/// </summary>
/// <param name="noticeID">게시글 ID</param>
/// <param name="id">ID</param>
/// <returns>액션 결과</returns>
[HttpGet]
public IActionResult DeleteComment(string noticeID, string id)
{
ViewBag.NoticeID = noticeID;
ViewBag.ID = id;
return View();
}
#endregion
#region 댓글 삭제 페이지 처리하기 - DeleteComment(noticeID, id, password)
/// <summary>
/// 댓글 삭제 페이지 처리하기
/// </summary>
/// <param name="noticeID">게시글 ID</param>
/// <param name="id">ID</param>
/// <param name="password">패스워드</param>
/// <returns>액션 결과</returns>
[HttpPost]
public IActionResult DeleteComment(string noticeID, string id, string password)
{
if(this.commentRepository.GetCount(Convert.ToInt32(noticeID), Convert.ToInt32(id), password) > 0)
{
this.commentRepository.DeleteComment(Convert.ToInt32(noticeID), Convert.ToInt32(id), password);
return RedirectToAction("Details", new { ID = noticeID });
}
ViewBag.NoticeID = noticeID;
ViewBag.ID = id;
ViewBag.ErrorMessage = "패스워드가 틀립니다. 다시 입력해 주시기 바랍니다.";
return View();
}
#endregion
#region 게시글 고정 페이지 처리하기 - Pin(id)
/// <summary>
/// 게시글 고정 페이지 처리하기
/// </summary>
/// <param name="id">ID</param>
/// <returns>액션 결과</returns>
[HttpGet]
[Authorize("Administrators")]
public IActionResult Pin(int id)
{
this.noticeRepository.PinNotice(id);
return RedirectToAction("Details", new { ID = id });
}
#endregion
#region 최근 게시글 리스트 페이지 처리하기 - ListRecentNotice()
/// <summary>
/// 최근 게시글 리스트 페이지 처리하기
/// </summary>
/// <returns>액션 결과</returns>
public IActionResult ListRecentNotice()
{
return View();
}
#endregion
#region 최근 댓글 리스트 페이지 처리하기 - ListRecentComment()
/// <summary>
/// 최근 댓글 리스트 페이지 처리하기
/// </summary>
/// <returns>액션 결과</returns>
public IActionResult ListRecentComment()
{
return View();
}
#endregion
}
}
▶ Views/NoticeBoard/_SearchForm.cshtml
<form asp-controller="NoticeBoard" asp-action="Index" method="get">
<select name="searchField" id="searchField"
class="form-control"
style="width:80px;display:inline-block;">
<option value="Name">성명</option>
<option value="Title">제목</option>
<option value="Content">내용</option>
</select>
<input type="text" name="searchQuery" id="searchQuery"
class="form-control"
style="width:200px;display:inline-block;"
value="" />
<input type="submit" name="searchButton"
class="form-control"
style="width:100px;display:inline-block;"
value="검색" />
</form>
▶ Views/NoticeBoard/_BoardEditorForm.cshtml
@model NoticeModel
<div class="form-horizontal">
@if(ViewBag.FormType == TestProject.Models.BoardWriteFormType.Modify)
{
<div class="form-group">
<label asp-for="ID" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="ID"
class="form-control"
style="border:0;width:100px;"
maxlength="10"
readonly />
<input type="hidden" name="ID" value="@Model.ID" />
</div>
</div>
}
<div class="form-group">
<label asp-for="Name" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Name"
class="form-control"
style="width:150px;"
maxlength="10" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<label asp-for="MailAddress" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="MailAddress"
class="form-control"
style="width:200px;display:inline-block;"
maxlength="80" />
<span style="color:#aaaaaa;font-style:italic"> (선택)</span>
<span asp-validation-for="MailAddress" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<label asp-for="Homepage" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Homepage"
class="form-control"
style="width:300px;display:inline-block;"
maxlength="80" />
<span style="color:#aaaaaa;font-style:italic"> (선택)</span>
<span asp-validation-for="Homepage" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<label asp-for="Title" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Title"
class="form-control"
style="width:480px;" />
<span asp-validation-for="Title" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<label asp-for="Content" class="col-md-2 control-label"></label>
<div class="col-md-10">
<textarea asp-for="Content"
class="form-control"
style="width:480px;height:100px;"
rows="5"
cols="80">
</textarea>
<span asp-validation-for="Content" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<label asp-for="FileName" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input type="file" name="formFileCollection"
style="width:290px;display:inline-block;"
value="" />
<span style="color:#aaaaaa;font-style:italic"> (선택)</span>
<span asp-validation-for="FileName" class="text-danger"></span>
@if(ViewBag.FileSize > 0)
{
<br />
@ViewBag.FileNamePrevious
}
<input type="hidden" name="previousFileName" value="@ViewBag.FileName" />
<input type="hidden" name="previousFileSize" value="@ViewBag.FileSize" />
</div>
</div>
<div class="form-group">
<label asp-for="Encoding" class="col-md-2 control-label"></label>
<div class="col-md-10">
<select asp-for="Encoding" class="form-control" style="width:100px;">
<option value="Text" selected>Text</option>
<option value="Html">HTML</option>
<option value="Mixed">Mixed</option>
</select>
</div>
</div>
<div class="form-group">
<label asp-for="Password" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input type="password" asp-for="Password"
class="form-control"
style="width:150px;display:inline-block;"
maxlength="20" />
<span style="color:#aaaaaa;"> (수정/삭제시 필요)</span><br />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
</div>
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-primary" value="@ViewBag.SaveButtonText" />
<a asp-action="Index" class="btn btn-default">리스트</a>
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<span class="text-danger">@ViewBag.ErrorMessage</span>
</div>
</div>
<input type="hidden" asp-for="Category" value="FREE" />
▶ Views/NoticeBoard/_BoardCommentControl.cshtml
@model NoticeCommentModel
<table style="padding:10px;margin-left:20px;margin-right:20px;width:95%;">
@foreach(CommentModel comment in Model.CommentList)
{
<tr style="border-bottom:1px dotted silver;">
<td style="width:100px;">
@comment.Name
</td>
<td style="width:400px;">
@Html.Raw(comment.Comment.Replace("\r\n", "<br />"))
</td>
<td style="width:200px;">
@comment.WriteDate
</td>
<td style="width:10px;text-align:center;">
<a href="/NoticeBoard/DeleteComment?noticeID=@Model.NoticeID&id=@comment.ID"
title="댓글 삭제">
<img border="0" src="/Image/NoticeBoard/delete.gif" />
</a>
</td>
</tr>
}
</table>
<br />
<script>
function CheckForm()
{
var name = document.getElementById("name");
var password = document.getElementById("password");
var comment = document.getElementById("comment");
if(name.value.length < 1 || comment.value.length < 1 || password.value.length < 1)
{
window.alert("성명, 패스워드 및 댓들을 입력해 주시기 바랍니다.");
name.focus();
return false;
}
return true;
}
</script>
<form asp-controller="NoticeBoard" asp-action="AddComment" method="post" onsubmit="return CheckForm();">
<input type="hidden" name="noticeID" value="@Model.NoticeID" />
<table style="width:520px;margin-left:auto;">
<tr>
<td style="width:60px;text-align:right;">성명 </td>
<td style="width:100px;">
<input type="text" name="name" id="name"
class="form-control"
style="width:100px;display:inline-block;" />
</td>
<td style="width:80px;text-align:right;">패스워드 </td>
<td style="width:100px;">
<input type="password" name="password" id="password"
class="form-control"
style="width:100px;display:inline-block;" />
</td>
<td style="width:180px;text-align:right;">
<input type="submit"
class="form-control btn btn-primary"
style="width:120px;display:inline-block;"
value="댓글 남기기" />
</td>
</tr>
<tr>
<td style="width:60px;text-align:right;">댓글 </td>
<td colspan="4" style="width:460px;">
<textarea name="comment" id="comment"
class="form-control"
style="width:460px;display:inline-block;"
rows="3"
cols="70">
</textarea>
</td>
</tr>
</table>
</form>
<hr />
▶ Views/NoticeBoard/Index.cshtml
@using TestProject;
@model IEnumerable<NoticeModel>
@inject INoticeRepository NoticeRepository
@{
ViewData["Title"] = "게시판 리스트";
}
<h2 style="text-align:center;">게시판</h2>
<span style="color:#ff0000">
글 목록 - ASP.NET CORE MVC 게시판 입니다.
</span>
<hr />
<table style="width:700px;margin-left:auto;margin-right:auto;">
<tr>
<td>
<div style="font-style:italic;text-align:right;font-size:8pt;">
Total Record: @ViewBag.RecordCount
</div>
<table class="table table-bordered table-hover table-condensed table-striped table-responsive">
<tr style="background-color:rgb(222,222,222)">
<th style="width:50px;text-align:center;">
@Html.DisplayNameFor(model => model.ID)
</th>
<th style="width:300px;text-align:center;">
@Html.DisplayNameFor(model => model.Title)
</th>
<th style="width:80px;text-align:center;">
@Html.DisplayNameFor(model => model.FileName)
</th>
<th style="width:90px;text-align:center;">
@Html.DisplayNameFor(model => model.Name)
</th>
<th style="width:90px;text-align:center;">
@Html.DisplayNameFor(model => model.WriteDate)
</th>
<th style="width:90px;text-align:center;">
@Html.DisplayNameFor(model => model.ReadCount)
</th>
</tr>
<cache>
@foreach(NoticeModel notice in NoticeRepository.GetRecentCategoryNoticeList("Notice").Take(2))
{
<tr>
<td style="text-align:center;">
<span class="label label-success" style="font-size:xx-small">공지 사항</span>
</td>
<td>
@Html.Raw(NoticeHelper.GetReplyImageHTML(notice.ReplyLevel))
<a asp-controller="NoticeBoard" asp-action="Details" asp-route-id="@notice.ID">
<strong>
@StringHelper.CutUnicodeString(notice.Title, 30)
</strong>
</a>
<small>
@Html.Raw(NoticeHelper.GetCommentCountHTML(notice.CommentCount))
</small>
@Html.Raw(NoticeHelper.GetNewImageHTML(notice.WriteDate))
</td>
<td style="text-align:center;">
@Html.Raw(NoticeHelper.GetFileDownloadLinkHTML(
notice.ID,
notice.FileName,
notice.FileSize.ToString()
))
</td>
<td style="text-align:center;">
@Html.DisplayFor(modelItem => notice.Name)
</td>
<td style="text-align:center;">
@NoticeHelper.GetDateTimeHTML(notice.WriteDate)
</td>
<td style="text-align:right;">
@Html.DisplayFor(modelItem => notice.ReadCount)
</td>
</tr>
}
</cache>
@foreach(NoticeModel notice in Model)
{
<tr>
<td style="text-align:center;">
@if(notice.ReplyLevel == 0)
{
@Html.DisplayFor(modelItem => notice.ID)
}
else
{
<text> </text>
}
</td>
<td>
@Html.Raw(NoticeHelper.GetReplyImageHTML(notice.ReplyLevel))
<a asp-controller="NoticeBoard" asp-action="Details" asp-route-id="@notice.ID">
@StringHelper.CutUnicodeString(notice.Title, 30)
</a>
<small>
@Html.Raw(NoticeHelper.GetCommentCountHTML(notice.CommentCount))
</small>
@Html.Raw(NoticeHelper.GetNewImageHTML(notice.WriteDate))
</td>
<td style="text-align:center;">
@Html.Raw(NoticeHelper.GetFileDownloadLinkHTML(
notice.ID,
notice.FileName,
notice.FileSize.ToString()
))
</td>
<td style="text-align:center;">
@Html.DisplayFor(modelItem => notice.Name)
</td>
<td style="text-align:center;">
@NoticeHelper.GetDateTimeHTML(notice.WriteDate)
</td>
<td style="text-align:right;">
@Html.DisplayFor(modelItem => notice.ReadCount)
</td>
</tr>
}
</table>
</td>
</tr>
<tr>
<td style="text-align:center;">
<paging-helper
url="@Context.Request.Path"
page-index='Convert.ToInt32(Context.Request.Query["Page"])'
page-size="10"
record-count="@ViewBag.RecordCount"
search-mode='Convert.ToBoolean(@ViewBag.SearchMode)'
search-field="@ViewBag.SearchField"
search-query="@ViewBag.SearchQuery">
</paging-helper>
</td>
</tr>
<tr>
<td style="text-align:right;">
<a asp-action="Create" class="btn btn-primary">글쓰기</a>
</td>
</tr>
<tr>
<td style="text-align:center;">
@await Html.PartialAsync("_SearchForm")
<br />
@if(ViewBag.SearchMode)
{
<a asp-controller="NoticeBoard" asp-action="Index"
class="btn btn-success">
검색 완료
</a>
}
</td>
</tr>
</table>
@section Scripts {
@if(TempData["Message"] != null)
{
<link href="~/lib/toastr.js/toastr.css" rel="stylesheet" />
<script src="~/lib/toastr.js/toastr.min.js"></script>
<script>
$(function () {
toastr.info('@TempData["Message"]');
});
</script>
}
}
▶ Views/NoticeBoard/Create.cshtml
@model NoticeModel
<h2 style="text-align:center;">게시판</h2>
@ViewBag.TitleDescription
<hr />
<div class="row">
<div class="col-md-offset-2 col-md-8 col-md-offset-2">
<form asp-controller="NoticeBoard" asp-action="Create"
enctype="multipart/form-data"
method="post">
@await Html.PartialAsync("_BoardEditorForm")
</form>
</div>
</div>
@section Scripts {
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
}
▶ Views/NoticeBoard/Update.cshtml
@model NoticeModel
<h2 style="text-align:center;">게시판</h2>
@ViewBag.TitleDescription
<hr />
<div class="row">
<div class="col-md-offset-2 col-md-8 col-md-offset-2">
<form asp-controller="NoticeBoard" asp-action="Update" method="post"
enctype="multipart/form-data">
@await Html.PartialAsync("_BoardEditorForm")
</form>
</div>
</div>
@section Scripts {
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
}
▶ Views/NoticeBoard/Reply.cshtml
@model NoticeModel
<h2 style="text-align:center;">게시판</h2>
@ViewBag.TitleDescription
<hr />
<div class="row">
<div class="col-md-offset-2 col-md-8 col-md-offset-2">
<form asp-controller="NoticeBoard" asp-action="Reply" method="post"
enctype="multipart/form-data">
@await Html.PartialAsync("_BoardEditorForm")
</form>
</div>
</div>
@section Scripts {
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
}
▶ Views/NoticeBoard/Delete.cshtml
@section Scripts {
<script src="~/lib/bootbox.js/bootbox.js"></script>
}
<script>
function ConfirmDelete()
{
var flag = false;
var password = document.getElementById("Password");
if(password.value.length == 0)
{
bootbox.alert("패스워드를 입력해 주시기 바랍니다.", function() { });
password.focus();
return false;
}
flag = false;
bootbox.confirm
(
"현재 글을 삭제하시겠습니까?",
function(result)
{
if(result == true)
{
document.forms[0].submit();
}
}
);
return flag;
}
</script>
<h2 style="text-align:center;">게시판</h2>
<span style="color:#ff0000;">
게시글 삭제 - 글을 삭제하려면 글 작성시에 기록하였던 패스워드가 필요합니다.
</span>
<hr />
<div style="text-align:center;">
<form asp-action="Delete" method="post" onsubmit="return ConfirmDelete();">
<span class="text-danger">@ViewBag.ID</span>번 글을 지우시겠습니까?
<input type="hidden" name="id" id="id" value="@ViewBag.ID" />
<br />
패스워드
<input type="password" name="password" id="password"
style="width:120px;display:inline-block;"
class="form-control" />
<input type="submit" value="지우기" class="btn btn-danger"
style="display:inline-block;" />
<a asp-action="Details" asp-route-id="@ViewBag.ID"
class="btn btn-default">취소</a>
<br />
<span class="text-danger">@ViewBag.Message</span>
<br />
</form>
</div>
▶ Views/NoticeBoard/DeleteCompleted.cshtml
@{ Layout = null; }
<script>
window.alert("게시글이 삭제되었습니다.");
window.location.href = "/NoticeBoard/Index";
</script>
▶ Views/NoticeBoard/Details.cshtml
@model NoticeModel
@inject Microsoft.Extensions.Options.IOptions<TestProject.Settings.MainSettings> settings
<h2 style="text-align:center;">게시판</h2>
<span style="color:#ff0000">
게시글 보기 - 현재 글에 대해서 수정 및 삭제를 할 수 있습니다.
</span>
<hr />
<table style="width:700px;margin-left:auto;margin-right:auto;">
<tbody>
<tr style="color:white;background-color:#46698c;">
<td style="width:80px;text-align:right;height:35px;">
<b style="font-size:18px">제목</b> :
</td>
<td colspan="3">
<span style="font-weight:bold;font-size:18px;">
@Model.Title
</span>
</td>
</tr>
<tr style="background-color:#efefef;">
<td class="text-right" style="font-weight:bold;">
번호
</td>
<td>
<span>@Model.ID</span>
</td>
<td class="text-right" style="font-weight:bold;">
메일 주소
</td>
<td>
<a href="mailto:@Model.MailAddress">@Model.MailAddress</a>
</td>
</tr>
<tr style="background-color:#efefef;">
<td class="text-right" style="font-weight:bold;">
성명
</td>
<td>
<span>@Model.Name</span>
</td>
<td class="text-right" style="font-weight:bold;">
홈페이지
</td>
<td>
<span>
@Html.Raw(string.Format(
"<a href=\"{0}\" target=\"_blank\">{0}</a>",
Model.Homepage))
</span>
</td>
</tr>
<tr style="background-color:#efefef;">
<td class="text-right" style="font-weight:bold;">
작성일
</td>
<td>
<span>@Model.WriteDate</span>
</td>
<td class="text-right" style="font-weight:bold;">
IP 주소
</td>
<td>
<span>@Model.WriteIP</span>
</td>
</tr>
<tr style="background-color:#efefef;">
<td class="text-right" style="font-weight:bold;">
조회 수
</td>
<td>
<span>@Model.ReadCount</span>
</td>
<td class="text-right" style="font-weight:bold;">
파일
</td>
<td>
@Html.Raw(ViewBag.FileName)
</td>
</tr>
<tr>
<td colspan="4" style="padding:10px;height:100px;vertical-align:top;">
@Html.Raw(ViewBag.ImageDownloadURL)
@Html.Raw(ViewBag.Content)
</td>
</tr>
<tr>
<td colspan="4">
<hr />
</td>
</tr>
<tr>
<td colspan="4">
@await Html.PartialAsync(
"_BoardCommentControl", (NoticeCommentModel)ViewBag.NoticeComment
)
</td>
</tr>
</tbody>
</table>
<div style="text-align:center;">
@if(Model.Category != "Notice" && User.IsInRole("Users") &&
User.FindFirst("UserID").Value == settings.Value.SiteAdministrator)
{
<a asp-action="Pin" asp-route-id="@Model.ID" class="btn btn-danger">
공지로 올리기
</a>
}
<a asp-action="Delete" asp-route-id="@Model.ID" class="btn btn-default">삭제</a>
<a asp-action="Update" asp-route-id="@Model.ID" class="btn btn-default">수정</a>
<a asp-action="Reply" asp-route-id="@Model.ID" class="btn btn-default">답변</a>
<a asp-action="Index" class="btn btn-default">목록</a>
</div>
<br />
@section Scripts {
@if(TempData["Message"] != null)
{
<link href="~/lib/toastr.js/toastr.css" rel="stylesheet" />
<script src="~/lib/toastr.js/toastr.min.js"></script>
<script>
$(function () {
toastr.info('@TempData["Message"]');
});
</script>
}
}
▶ Views/NoticeBoard/DeleteComment.cshtml
<h2 style="text-align:center;">게시판</h2>
<span style="color: #ff0000">
댓글 삭제 - 패스워드를 입력하시면 댓글을 삭제할 수 있습니다.
</span>
<hr />
<form asp-controller="NoticeBoard" asp-action="DeleteComment" method="post">
<input type="hidden" name="noticeID" value="@ViewBag.NoticeID" />
<input type="hidden" name="id" value="@ViewBag.ID" />
<table style="width:500px;margin-left:auto;margin-right:auto;">
<tr>
<td class="text-center">
<i class="glyphicon glyphicon-lock"></i>
<span style="font-size:12pt;">댓글 삭제</span>
</td>
</tr>
<tr>
<td class="text-center">
<span>
해당 댓글를 삭제하려면 패스워드를 입력해 주시기 바랍니다.
</span>
<br />
패스워드(<u>P</u>)
<input type="password" name="password" maxlength="50"
class="form-control"
style="width:250px;display:inline-block;"
tabindex="2"
accesskey="P" />
</td>
</tr>
<tr>
<td style="text-align: center;">
<input type="submit" value="삭제" class="btn btn-danger" />
<input type="button" value="뒤로" onclick="history.go(-1);"
class="btn btn-default"><br />
<span class="text-danger">@ViewBag.ErrorMessage</span>
</td>
</tr>
</table>
</form>
▶ Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using TestProject.Models;
using TestProject.Settings;
namespace TestProject
{
/// <summary>
/// 시작
/// </summary>
public class Startup
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 구성 - Configuration
/// <summary>
/// 구성
/// </summary>
public IConfiguration Configuration { get; }
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - Startup(configuration)
/// <summary>
/// 생성자
/// </summary>
/// <param name="configuration">구성</param>
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 서비스 컬렉션 구성하기 - ConfigureServices(services)
/// <summary>
/// 서비스 컬렉션 구성하기
/// </summary>
/// <param name="services">서비스 컬렉션</param>
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddSingleton<IConfiguration>(Configuration);
services.Configure<MainSettings>(Configuration.GetSection(nameof(MainSettings)));
services.AddTransient<INoticeRepository, NoticeRepository>();
services.AddSingleton<ICommentRepository>(new CommentRepository(Configuration["ConnectionStrings:DefaultConnection"]));
}
#endregion
#region 구성하기 - Configure(app, environment)
/// <summary>
/// 구성하기
/// </summary>
/// <param name="app">애플리케이션 빌더</param>
/// <param name="environment">웹 호스트 환경</param>
public void Configure(IApplicationBuilder app, IWebHostEnvironment environment)
{
if(environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints
(
endpoints =>
{
endpoints.MapControllerRoute
(
name : "default",
pattern : "{controller=Home}/{action=Index}/{id?}"
);
}
);
}
#endregion
}
}
▶ Program.cs
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
namespace TestProject
{
/// <summary>
/// 프로그램
/// </summary>
public class Program
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Public
#region 프로그램 시작하기 - Main(argumentArray)
/// <summary>
/// 프로그램 시작하기
/// </summary>
/// <param name="argumentArray">인자 배열</param>
public static void Main(string[] argumentArray)
{
CreateHostBuilder(argumentArray).Build().Run();
}
#endregion
#region 호스트 빌더 생성하기 - CreateHostBuilder(argumentArray)
/// <summary>
/// 호스트 빌더 생성하기
/// </summary>
/// <param name="argumentArray">인자 배열</param>
/// <returns>호스트 빌더</returns>
public static IHostBuilder CreateHostBuilder(string[] argumentArray) =>
Host.CreateDefaultBuilder(argumentArray)
.ConfigureWebHostDefaults
(
builder =>
{
builder.UseStartup<Startup>();
}
);
#endregion
}
}
728x90
반응형
그리드형(광고전용)
'C# > ASP.NET MVC' 카테고리의 다른 글
[C#/ASP.NET MVC/.NETCORE] 스캐폴딩에 필요한 패키지 (0) | 2020.10.17 |
---|---|
[C#/ASP.NET MVC/.NETCORE] 누겟 설치 : Microsoft.VisualStudio.Web.CodeGeneration.Design (0) | 2020.10.17 |
[C#/ASP.NET MVC/.NETCORE] dotnet tool install 명령 : 스캐폴딩 도구 설치하기 (0) | 2020.10.17 |
[C#/ASP.NET MVC/.NETCORE] dotnet dev-certs https 명령 : 개발 인증서 신뢰하기 (0) | 2020.10.17 |
[C#/ASP.NET MVC/.NETCORE] dotnet new webapp 명령 : RAZOR 페이지 웹앱 만들기 (0) | 2020.10.17 |
[C#/ASP.NET MVC/.NETCORE] 누겟 설치 : Microsoft.Extensions.Caching.Memory (0) | 2020.10.14 |
[C#/ASP.NET MVC/.NETCORE] 누겟 설치 : Microsoft.AspNetCore.Session (0) | 2020.10.14 |
[C#/ASP.NET MVC/.NETCORE] Kestrel 사용시 업로드 파일 크기 설정하기 (2) (0) | 2020.10.14 |
[C#/ASP.NET MVC/.NETCORE] Kestrel 사용시 업로드 파일 크기 설정하기 (1) (0) | 2020.10.14 |
[C#/ASP.NET MVC/.NETCORE] IIS Express 사용시 업로드 파일 크기 설정하기 (0) | 2020.10.14 |
댓글을 달아 주세요