블로그 기능 구현

> 글쓰기, 수정, 삭제 구현
> 목록 페이지 벽돌 레이아웃 구성
> 로그인 기능 추가
> 컨텐츠 공개/숨김 기능 추가
This commit is contained in:
javamon117
2023-08-03 21:09:18 +09:00
parent 590beba676
commit 19359fa961
6 changed files with 340 additions and 50 deletions

View File

@@ -2,45 +2,61 @@
<html>
<head>
<title>Blog</title>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.6.16/css/uikit.min.css"/>
<style>
.blog-card {
width: 300px;
margin: 10px;
a:link {
color: inherit;
text-decoration: none;
}
.blog-card img {
width: 100%;
height: auto;
a:visited {
color: inherit;
text-decoration: none;
}
.grid-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
a:hover {
color: inherit;
text-decoration: underline;
}
::-webkit-scrollbar {
width: 10px;
}
::-webkit-scrollbar-track {
background: #f1f1f1;
}
::-webkit-scrollbar-thumb {
background: #888;
}
::-webkit-scrollbar-thumb:hover {
background: #555;
}
</style>
<!-- include libraries(jQuery, bootstrap) -->
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
<!-- include summernote css/js -->
<link href="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote.min.js"></script>
<script src="https://unpkg.com/masonry-layout@4/dist/masonry.pkgd.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.imagesloaded/4.1.4/imagesloaded.pkgd.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/masonry/4.2.2/masonry.pkgd.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
</head>
<body>
<nav class="uk-navbar-container" uk-navbar>
<div class="uk-navbar-left">
<ul class="uk-navbar-nav">
<li class="uk-active"><a href="#">
<li class="uk-active"><a href="/">
<img src="http://webmail.wixon.co.kr/user_img/logoImage.jpeg" alt="">
</a></li>
<li><a href="/">목록</a></li>
</ul>
</div>
@@ -54,6 +70,7 @@
<div class="uk-navbar-dropdown">
<ul class="uk-nav uk-navbar-dropdown-nav">
<li class="uk-active"><a href="/write">포스트 작성</a></li>
<li class="uk-active"><a target="_blank" href="http://www.wixon.co.kr/backS1te">관리자 페이지</a></li>
<li class="uk-active"><a href="/logout">로그아웃</a></li>
</ul>
</div>

73
templates/edit_post.html Normal file
View File

@@ -0,0 +1,73 @@
{% extends 'base.html' %}
{% block content %}
<div class="uk-container uk-margin-top">
<h1 class="uk-text-center"><span>포스트 수정</span></h1>
<form action="/edit_post/{{ post.id }}" method="post" class="uk-form-stacked">
<div class="uk-margin">
<label class="uk-form-label" for="title">제목</label>
<div class="uk-form-controls">
<input class="uk-input" id="title" type="text" name="title" value="{{ post.title }}"
placeholder="제목을 입력하세요..." required>
</div>
</div>
<div class="uk-margin">
<label class="uk-form-label" for="category">카테고리</label>
<div class="uk-form-controls">
<select class="uk-select" id="category" name="category" required>
<option value="">카테고리를 선택하세요...</option>
<option value="IT" {% if post.category == 'IT' %} selected {% endif %}>IT</option>
<option value="NEWS" {% if post.category == 'NEWS' %} selected {% endif %}>NEWS</option>
<option value="ETC" {% if post.category == 'ETC' %} selected {% endif %}>ETC</option>
</select>
</div>
</div>
<div class="uk-margin">
<label class="uk-form-label" for="public">공개 여부</label>
<div class="uk-form-controls">
<label><input class="uk-checkbox" id="public" type="checkbox" name="public" {% if post.is_public %}
checked {% endif %}> 이 글을 외부 공개합니다</label>
</div>
</div>
<div class="uk-margin">
<label class="uk-form-label" for="contents">내용</label>
<div class="uk-form-controls">
<textarea class="summernote" id="contents" name="contents" required>{{ post.contents }}</textarea>
</div>
</div>
<div class="uk-margin">
<a style="float: left;" href="{{ url_for('post', post_id=post.id) }}" class="uk-button uk-button-default">뒤로가기</a>
<button style="float: right;" type="submit" class="uk-button uk-button-primary">글 수정</button>
</div>
</form>
</div>
<script>
$(document).ready(function () {
$('.summernote').summernote({
height: 300,
callbacks: {
onImageUpload: function (files) {
var $editor = $(this);
var data = new FormData();
data.append("file", files[0]);
$.ajax({
url: "/upload_image",
method: "POST",
data: data,
processData: false,
contentType: false,
success: function (response) {
if (response.success) {
var imageUrl = response.fileUrl;
$editor.summernote("insertImage", imageUrl);
} else {
alert(response.message);
}
}
});
}
}
});
});
</script>
{% endblock %}

View File

@@ -1,32 +1,37 @@
{% extends 'base.html' %}
{% block content %}
<style>
.blog-card {
margin: 10px;
}
.blog-card img {
width: 100%;
height: auto;
}
</style>
<div class="uk-container">
<div class="grid-container masonry-grid">
{% for post in posts %}
<div class="blog-card uk-card uk-card-default">
<div class="uk-card-media-top">
<img src="{{ post.thumbnail_img }}" alt="{{ post.title }}">
<div class="uk-grid uk-grid-small uk-child-width-1-4@m uk-child-width-1-2@s" uk-grid>
{% if not posts %}
<h4 style="margin-top: 30px">작성된 포스트가 없습니다.</h4>
{% else %}
{% for post in posts %}
<div>
<div class="uk-card uk-card-default blog-card">
<div class="uk-card-media-top">
<img src="{{ post.thumbnail_img | default('https://via.placeholder.com/300x200', true) }}"
alt="{{ post.title }}" style="max-width: 100%;max-height: 500px;">
</div>
<div class="uk-card-body">
<h3 class="uk-card-title">{{ post.title }}</h3>
<a href="{{ url_for('post', post_id=post.id) }}" class="uk-button uk-button-text">Read
more</a>
</div>
</div>
</div>
<div class="uk-card-body">
<h3 class="uk-card-title">{{ post.title }}</h3>
<!-- Truncate the contents to 100 characters, removing any HTML tags -->
<p>{{ post.contents | striptags | truncate(100) }}</p>
<a href="#" class="uk-button uk-button-text">Read more</a>
</div>
</div>
{% endfor %}
{% endfor %}
{% endif %}
</div>
</div>
<script>
$(document).ready(function () {
$('.masonry-grid').masonry({
// options...
itemSelector: '.blog-card',
columnWidth: '.blog-card',
percentPosition: true
});
});
</script>
{% endblock %}

92
templates/post.html Normal file
View File

@@ -0,0 +1,92 @@
{% extends 'base.html' %}
{% block content %}
<div class="uk-container" style="margin-top: 30px">
<div class="uk-card uk-card-default uk-margin" style="padding: 10px 30px">
<div class="uk-card-body" style="padding: 10px 30px">
{% if g.is_login and (g.user_info.mb_id == post.mb_id or g.user_info.mb_id in ['admin', 'wixon']) %}
<div style="float: right;color: white">
<a href="{{ url_for('edit_post', post_id=post.id) }}" class="uk-button uk-button-primary">수정</a>
<a class="uk-button uk-button-danger" href="javascript:blogDelete('{{ post.id }}');">삭제</a>
</div>
{% endif %}
<h3 class="uk-card-title">[{{ post.category }}] {{ post.title }}</h3>
<p class="uk-text-meta" style="float: right">Posted on {{ post.add_date }}</p>
<div style="width: 100%;border-top: 1px solid #eee;padding: 15px">
{{ post.contents | safe }}
</div>
</div>
</div>
<div class="uk-card uk-card-default uk-margin custom-table">
<div class="uk-card-body" style="padding: 0px">
<table class="uk-table uk-table-divider">
<tbody>
{% if prev_post is not none %}
<tr>
<td style="width: 80px;text-align: center;padding: 8px 6px;">이전</td>
<td style="padding: 8px 6px;">
<a href="{{ url_for('post', post_id=prev_post.id) }}">{{ prev_post.title }}</a>
</td>
</tr>
{% endif %}
{% if next_post is not none %}
<tr>
<td style="width: 80px;text-align: center;padding: 8px 6px;">다음</td>
<td style="padding: 8px 6px;">
<a href="{{ url_for('post', post_id=next_post.id) }}">
<div style="width: 100%">
{{ next_post.title }}
</div>
</a>
</td>
</tr>
{% endif %}
</tbody>
</table>
</div>
<div style="text-align: right;padding: 15px;">
<a href="{{ url_for('index') }}" class="uk-button uk-button-default">목록</a>
</div>
</div>
</div>
<script>
function blogDelete(id) {
Swal.fire({
title: '정말로 삭제하시겠습니까?',
text: "이 작업은 되돌릴 수 없습니다!",
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: '네, 삭제합니다!'
}).then((result) => {
if (result.isConfirmed) {
$.ajax({
url: "/blog/" + id,
type: "DELETE",
success: function (result) {
Swal.fire(
'삭제 완료!',
'포스트가 성공적으로 삭제되었습니다.',
'success'
);
// 포스트를 UI에서 삭제하거나 페이지를 다시 로드합니다.
location.href = '/';
},
error: function (jqXHR, textStatus, errorThrown) {
Swal.fire(
'오류!',
'문제가 발생했습니다: ' + textStatus,
'error'
);
}
});
}
});
}
</script>
{% endblock %}

View File

@@ -21,6 +21,13 @@
</select>
</div>
</div>
<!-- Here is your new checkbox field -->
<div class="uk-margin">
<label class="uk-form-label" for="public">공개 여부</label>
<div class="uk-form-controls">
<label><input class="uk-checkbox" id="public" type="checkbox" name="public"> 이 글을 외부 공개합니다</label>
</div>
</div>
<div class="uk-margin">
<label class="uk-form-label" for="contents">내용</label>
<div class="uk-form-controls">
@@ -28,7 +35,8 @@
</div>
</div>
<div class="uk-margin">
<button type="submit" class="uk-button uk-button-default">글 작성</button>
<a href="{{ url_for('index') }}" class="uk-button uk-button-default">목록</a>
<button style="float: right" type="submit" class="uk-button uk-button-primary">글 작성</button>
</div>
</form>
</div>