본문 바로가기
재밌는 IT 개발/그누보드 테마 제작기(記)

그누보드 테마 제작 18 - 쪽지 내역 및 전송 기능 개발

by 만수킴 2020. 7. 3.

오늘은 쪽지 관련 기능을 개발합니다.

그누보드 기본 테마의 쪽지함 모습

받은쪽지, 보낸쪽지, 쪽지쓰기의 총 3페이지로 되어 있군요.
제가 만들고 있는 테마에서는 어떻게 보일런지 궁금하네요.

현재 제작중인 테마에서 보여지는 쪽지함의 모습

관련 파일은, memo.php와 memo_form.php 그리고 memo_view.php 네요.
당연히 bbs 폴더 안에 있는 것들일거고...
제가 작업해야 할 파일은
/theme/제작중인테마/skin/member/basic 폴더에 있는
memo.skin.php와 memo_form.skin.php, memo_view.skin.php가 되겠네요.
(그동안 작업 좀 했다고, 술술 나오네요 ㅎㅎ)

Metronic Admin Templat에서 어떤 화면을 이용해야 할지 살펴볼게 꽤 있네요.
테이블로 하면 편할 것 같긴 한데, 지양해야 하는 것 같기도 하고...
받은 쪽지나 보낸 쪽지에서 확인하지 않은 항목들은
사용자 프로파일 이미지에 표시도 해주고 있네요.
(걍 안읽음이라고 표시할 수도 있겠지만...)

우선 상단 보기 탭은 이걸 이용하구요.
https://keenthemes.com/metronic/preview/demo1/features/bootstrap/navs.html

받은쪽지, 보낸쪽지, 쪽지보내기, 쪽지보기의 구분을 위해 사용할 Metronic Admin 예제

받은쪽지와 보낸쪽지의 리스트는 아래 두가지를 섞어야겠어요.
https://keenthemes.com/metronic/preview/demo1/features/widgets/lists.html

받은쪽지와 보낸쪽지의 리스트 표현을 위해 사용할 Metroinc Admin 예제

쪽지보내기와 쪽지보기에서 사용할 화면은 다음과 정했습니다.
https://keenthemes.com/metronic/preview/demo1/features/widgets/forms.html

쪽지전송과 쪽지보기에 사용할 Metronic Admin 예제

 

창이 4개이다 보니, 해야 할 일이 많은 느낌이네요.
하나 하나 코드를 만들어보겠습니다.
(확실히 저는 화면을 만드는 부분이 시간이 제일 오래 걸리네요. ㅠㅠ)

받은/보낸 쪽지에 사용할 UI와 쪽지보내기에 사용하기 위해 만든 UI

받은 쪽지함은 일단 받은 쪽지와 보낸 쪽지를 완료한 후에 만들기 위해 보류합니다.
(시간을 너무 끌어서, 작업의 효율이 떨어지니까요.)

 

 

"안읽은쪽지"에 깜빡거리는 파란점이 어려울 거라고 예상했는데,
그누의 CSS를 그대로 가져와 넣었더니 의외로 쉽게 해결되었습니다.

<style type="text/css">
.no_read {
    position: absolute;
    bottom: 0;
    right: 0;
    display: inline-block;
    background: #3a8afd;
    border: 1px solid #e1edff;
    text-indent: -9999px;
    border-radius: 10px;
    width: 10px;
    height: 10px;
    box-shadow: 0 0 10px 3px #9ec3f9;
}
.no_read {
    animation: blinker 1s linear infinite;
}
@keyframes blinker {
    50% {opacity:0}
}
</style>

                    <!--begin::Symbol-->
                    <div class="symbol symbol-50 symbol-2by3 flex-shrink-0 mr-4">
                        <div class="symbol-label" style="background-image: url('/theme/mt703/assets/media/stock-600x400/img-1.jpg');border-radius:50%;width:50px;"></div>
                        <span class="no_read">안 읽은 쪽지</span>
                    </div>
                    <!--end::Symbol-->

 

$list의 내용을 살펴봅니다.
name 즉 사용자의 아이콘과 이름을 눌렀을 경우 나오는 툴팁도 수정이 필요하겠군요.
bootstrap의 툴팁도 있지만,  기존걸 사용하는것이 더 예쁠 것 같다는 생각이 드네요.
(절대 귀찮아서는 아닙니다. 아니에..요... 그럴거에요...)

쪽지 리스트를 가지고 있는 $list 변수의 내용 확인

 

큰 어려움 없이 받은 편지함과 보낸 편지함을 완료하였습니다.

사용자 ID 영역을 제외하고 받은쪽지함과 보낸쪽지함의 모습

사용자의 ID를 어떻게 처리할지...
위에서 말한데로 기본 테마의 CSS를 몽땅 가져와서 처리해보겠습니다.
그누 기본 테마를 띄워놓고, 요소검사 요소검사 요소검사... 해서 모았습니다.

.sv_wrap {
    position: relative;
    font-weight: bold;
}
.sv_member {
    color: #333;
    text-decoration: none;
}
.sv_wrap .sv {
    z-index: 1000;
    display: none;
    margin: 5px 0 0;
    font-size: 0.92em;
    background: #333;
    -webkit-box-shadow: 2px 2px 3px 0px rgba(0,0,0,0.2);
    -moz-box-shadow: 2px 2px 3px 0px rgba(0,0,0,0.2);
    box-shadow: 2px 2px 3px 0px rgba(0,0,0,0.2);
}
.sv_wrap .sv:before {
    content: "";
    position: absolute;
    top: -6px;
    left: 15px;
    width: 0;
    height: 0;
    border-style: solid;
    border-width: 0 6px 6px 6px;
    border-color: transparent transparent #333 transparent;
}
.sv_on {
    display: block !important;
    position: absolute;
    top: 23px;
    left: 0px;
    width: auto;
    height: auto;
}
.sv_wrap .sv a {
    display: inline-block;
    margin: 0;
    padding: 0 10px;
    line-height: 30px;
    width: 100px;
    font-weight: bold;
    color: #bbb;
}

그 결과... 잘 되네요 ^^

사용자 ID의 오른쪽 클릭 완료된 모습

무언가 찜찜하더라니, 페이징 작업이 남아있었네요.
샘플이 몇개 안되니, /bbs/memo.php에 가서
페이지당 리스트의 개수를 2개로 바꿉니다.

페이징 개발을 위해 페이지 당 데이터 건수를 2개로 수정한 후 나타난 페이징 모습.

스크랩때 했던것과 유사하게 진행이 될 것 같네요.
관련 코드를 찾아봅니다.
이런... /bbs/memo.php에서 페이징 html을 만들고 있네요.

/bbs/memo.php 에서 paging 관련 Html을 만들고 있는 모습

가능하면 테마폴더 이외의 파일을 만지면 안된기에,
(꼭 필요한 경우라면 IF문을 사용하자!)
memo.skin.php에서 테마 전용 페이징을 만드는 함수를 호출합니다.

memo.skin.php에서 페이징 함수를 호출하여 $write_page 변수에 새로 할당함.

이제 예쁘게 나옵니다. ^^

페이징 처리까지 완료된 모습

보낸쪽지와 받은쪽지의 소스는 다음과 같습니다.
/theme/테마폴더/skin/member/skin/memo.skin.php

<?php
if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가

// add_stylesheet('css 구문', 출력순서); 숫자가 작을 수록 먼저 출력됨
//add_stylesheet('<link rel="stylesheet" href="'.$member_skin_url.'/style.css">', 0);

$write_pages = get_paging_mt703(G5_IS_MOBILE ? $config['cf_mobile_pages'] : $config['cf_write_pages'], $page, $total_page, "./memo.php?kind=$kind".$qstr."&amp;page=");
?>

<style type="text/css">
.no_read {
    position: absolute;
    bottom: 0;
    right: 0;
    display: inline-block;
    background: #3a8afd;
    border: 1px solid #e1edff;
    text-indent: -9999px;
    border-radius: 10px;
    width: 10px;
    height: 10px;
    box-shadow: 0 0 10px 3px #9ec3f9;
}
.no_read {
    animation: blinker 1s linear infinite;
}
@keyframes blinker {
    50% {opacity:0}
}

.sv_wrap {
    position: relative;
    font-weight: bold;
}
.sv_member {
    color: #333;
    text-decoration: none;
}
.sv_wrap .sv {
    z-index: 1000;
    display: none;
    margin: 5px 0 0;
    font-size: 0.92em;
    background: #333;
    -webkit-box-shadow: 2px 2px 3px 0px rgba(0,0,0,0.2);
    -moz-box-shadow: 2px 2px 3px 0px rgba(0,0,0,0.2);
    box-shadow: 2px 2px 3px 0px rgba(0,0,0,0.2);
}
.sv_wrap .sv:before {
    content: "";
    position: absolute;
    top: -6px;
    left: 15px;
    width: 0;
    height: 0;
    border-style: solid;
    border-width: 0 6px 6px 6px;
    border-color: transparent transparent #333 transparent;
}
.sv_on {
    display: block !important;
    position: absolute;
    top: 23px;
    left: 0px;
    width: auto;
    height: auto;
}
.sv_wrap .sv a {
    display: inline-block;
    margin: 0;
    padding: 0 10px;
    line-height: 30px;
    width: 100px;
    font-weight: bold;
    color: #bbb;
}
</style>

<?php //tLog("list", $list); ?>
<!-- 쪽지 목록 시작 { -->
<div class="card card-custom card-stretch gutter-b">
    <div class="card-header">
        <div class="card-title">
            <h3 class="card-label"><?php echo $g5['title'] ?></h3>
        </div>
        <div class="card-toolbar">
            <div class="d-flex align-items-center">
                <a href="javascript:;" class="btn btn-clean btn-secondary text-weight-bold text-primary">
                    전체 <?php echo $kind_title ?>쪽지 <?php echo $total_count ?>통
                </a>
            </div>
        </div>
    </div>
    <div class="card-body">
        <ul class="nav nav-tabs" id="myTab" role="tablist">
            <li class="nav-item">
                <a class="nav-link <?php echo ($kind == 'recv') ? "active" : "" ; ?>" id="home-tab" href="./memo.php?kind=recv" aria-controls="recv">
                    <span class="nav-icon">
                        <i class="flaticon2-email"></i>
                    </span>
                    <span class="nav-text">받은쪽지</span>
                </a>
            </li>
            <li class="nav-item">
                <a class="nav-link <?php echo ($kind == 'send') ? "active" : "" ; ?>" id="profile-tab" href="./memo.php?kind=send" aria-controls="send">
                    <span class="nav-icon">
                        <i class="flaticon2-send"></i>
                    </span>
                    <span class="nav-text">보낸쪽지</span>
                </a>
            </li>
            <li class="nav-item">
                <a class="nav-link" id="contact-tab" href="./memo_form.php" aria-controls="write">
                    <span class="nav-icon">
                        <i class="flaticon2-pen"></i>
                    </span>
                    <span class="nav-text">쪽지쓰기</span>
                </a>
            </li>
        </ul>
        <div class="tab-content mt-5" id="myTabContent">
            <div class="tab-pane fade show active" id="<?php echo ($kind == 'recv') ? "recv" : "send" ; ?>" role="tabpanel" aria-labelledby="<?php echo ($kind == 'recv') ? "recv" : "send" ; ?>-tab">

                <?php
                for ($i=0; $i<count($list); $i++) {
                $readed = (substr($list[$i]['me_read_datetime'],0,1) == 0) ? '' : 'background-color: #F3F6F9;';
                $memo_preview = utf8_strcut(strip_tags($list[$i]['me_memo']), 30, '..');
                ?>
                <!--begin::Item-->
                <div class="d-flex flex-wrap align-items-center mb-6" style="<?php echo $readed; ?>">
                    <!--begin::Symbol-->
                    <div class="symbol symbol-50 symbol-2by3 flex-shrink-0 mr-4">
                        <div class="symbol-label" style="background-image: url('<?php echo get_profile_img($list[$i]['mb_id']); ?>');border-radius:50%;width:50px;"></div>
                        <?php if (! $readed){ ?><span class="no_read">안 읽은 쪽지</span><?php } ?>
                    </div>
                    <!--end::Symbol-->
                    <!--begin::Title-->
                    <div class="d-flex flex-column flex-grow-1 w-75 mx-5">
                        <!--begin::Desc-->
                        <span class="text-dark text-hover-primary mb-1 font-size-lg">
                            <?php echo $list[$i]['name']; ?>
                            <span class="text-dark-75 text-muted"><i class="fa fa-clock-o" aria-hidden="true"></i> <?php echo $list[$i]['send_datetime']; ?></span>
                            <!-- a href="http://hunnov5.tank.com/bbs/profile.php?mb_id=hunnovsi" class="text-dark" title="최고관리자 자기소개" target="_blank" rel="nofollow" onclick="return false;">
                                <span class="profile_img"><img src="http://hunnov5.tank.com/data/member/hu/hunnovsi.gif" width="22" height="22" alt="" title=""></span> 최고관리자
                            </a -->
                        </span>
                        <!--begin::Desc-->
                        <!--begin::Title-->
                        <a href="<?php echo $list[$i]['view_href']; ?>" class="text-dark font-size-lg text-hover-primary mb-1"><?php echo $memo_preview; ?></a>
                        <!--end::Title-->
                    </div>
                    <!--end::Title-->
                    <!--begin::Btn-->
                    <a href="<?php echo $list[$i]['del_href']; ?>" onclick="del(this.href); return false;" class="btn btn-icon btn-light btn-sm">
                        <span class="svg-icon svg-icon-success">
                            <span class="svg-icon svg-icon-danger svg-icon-2x"><!--begin::Svg Icon | path:C:\wamp64\www\keenthemes\themes\metronic\theme\html\demo1\dist/../src/media/svg/icons\General\Trash.svg--><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24px" height="24px" viewBox="0 0 24 24" version="1.1">
                                <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
                                    <rect x="0" y="0" width="24" height="24"/>
                                    <path d="M6,8 L6,20.5 C6,21.3284271 6.67157288,22 7.5,22 L16.5,22 C17.3284271,22 18,21.3284271 18,20.5 L18,8 L6,8 Z" fill="#000000" fill-rule="nonzero"/>
                                    <path d="M14,4.5 L14,4 C14,3.44771525 13.5522847,3 13,3 L11,3 C10.4477153,3 10,3.44771525 10,4 L10,4.5 L5.5,4.5 C5.22385763,4.5 5,4.72385763 5,5 L5,5.5 C5,5.77614237 5.22385763,6 5.5,6 L18.5,6 C18.7761424,6 19,5.77614237 19,5.5 L19,5 C19,4.72385763 18.7761424,4.5 18.5,4.5 L14,4.5 Z" fill="#000000" opacity="0.3"/>
                                </g>
                            </svg><!--end::Svg Icon--></span>
                        </span>
                    </a>
                    <!--end::Btn-->
                </div>
                <!--end::Item-->

                <?php } ?>
                <?php if ($i==0) { ?>
                <!--begin::Item-->
                <div class="d-flex flex-wrap align-items-center mb-6">
                    <!--begin::Symbol-->
                    <div class="symbol symbol-50 symbol-2by3 flex-shrink-0 mr-4">
                        <div class="symbol-label" style="background-image: url('no_img');border-radius:50%;width:50px;"></div>
                    </div>
                    <!--end::Symbol-->
                    <!--begin::Title-->
                    <div class="d-flex flex-column flex-grow-1 w-75 mx-5">
                        <!--begin::Desc-->
                        <span class="text-dark text-hover-primary mb-1 font-size-h5">자료가 없습니다.</span>
                        <!--begin::Desc-->
                    </div>
                    <!--end::Title-->
                </div>
                <!--end::Item-->
                <?php }  ?>
                <div class="alert alert-custom alert-notice alert-light-danger fade show py-2" role="alert">
                    <div class="alert-icon">
                        <i class="flaticon-warning"></i>
                    </div>
                    <div class="alert-text"> 쪽지 보관일수는 최장 <strong><?php echo $config['cf_memo_del'] ?></strong>일 입니다.</div>
                </div>
                <div class="mt-10 text-center">
                    <?php echo $write_pages; ?><!-- 페이지 -->
                </div>

            </div>
            <div class="tab-pane fade" id="<?php echo ($kind == 'send') ? "send" : "recv"; ?>" role="tabpanel" aria-labelledby="<?php echo ($kind == 'send') ? "send" : "recv"; ?>-tab">
                보낸/받은 쪽지 (<?php echo $kind; ?>)
            </div>
            <div class="tab-pane fade" id="write" role="tabpanel" aria-labelledby="write-tab">
                쪽지쓰기
            </div>
        </div>


    </div>
    <div class="card-footer text-center">
        <button onclick="window.close();" class="btn btn-secondary font-weight-bold">닫기</button>
    </div>
</div>
<!--end::Card-->
<!-- } 쪽지 목록 끝 -->

 

 

이제 쪽지 전송부터 작업을 진행하겠습니다.
(아직 쪽지보기 페이지는 화면을 안만들었으니까요?)

쉽게 끝날 줄 알았는데, 문제가 생겼네요.
<form>과 </form>사이에 submit 버튼이 있어야 하는데, 
그렇지 못한 상황입니다.
form이 submit을 포함하게 코딩하면, 화면이 확 깨져버리네요. ㅠㅠ
그래서 이런 꼼수를 활용하여 해결합니다.

submit버튼을 form 사이에 두고 히든 처리, 바깥쪽 버튼을 클릭하면 히든 서브밋 버튼을 클릭하도록 한다.


여자 저차 완료하였는데...
포인트가 모자라네요. 쿨럭... 관리자에게 가서 삥 좀 뜯어와야겠습니다.

포인트 부족으로 테스트 진행 불가 ㅠㅠ
이제 저는 부자인가요? 흠냐...

이제 테스트만 끝나면 되는군요.
정상적으로 전송 완료!!!

전송 테스트가 완료되고, 보낸쪽지함에 정상적으로 보여지는 모습

쪽지보내기의 소스는 다음과 같습니다.
/theme/테마폴더/skin/member/skin/memo_form.skin.php

<?php
if (!defined('_GNUBOARD_')) exit; // 개별 페이지 접근 불가

// add_stylesheet('css 구문', 출력순서); 숫자가 작을 수록 먼저 출력됨
add_stylesheet('<link rel="stylesheet" href="'.$member_skin_url.'/style.css">', 0);
?>

<!-- 쪽지 보내기 시작 { -->
<div class="card card-custom gutter-b">
    <div class="card-header">
        <div class="card-title">
            <h3 class="card-label"><?php echo $g5['title'] ?></h3>
        </div>
    </div>
    <div class="card-body">
        <ul class="nav nav-tabs" id="myTab" role="tablist">
            <li class="nav-item">
                <a class="nav-link" id="home-tab" href="./memo.php?kind=recv">
                    <span class="nav-icon">
                        <i class="flaticon2-email"></i>
                    </span>
                    <span class="nav-text">받은쪽지</span>
                </a>
            </li>
            <li class="nav-item">
                <a class="nav-link" id="profile-tab" href="./memo.php?kind=send">
                    <span class="nav-icon">
                        <i class="flaticon2-send"></i>
                    </span>
                    <span class="nav-text">보낸쪽지</span>
                </a>
            </li>
            <li class="nav-item">
                <a class="nav-link active" id="contact-tab" href="./memo_form.php" aria-controls="write">
                    <span class="nav-icon">
                        <i class="flaticon2-pen"></i>
                    </span>
                    <span class="nav-text">쪽지쓰기</span>
                </a>
            </li>
        </ul>
        <div class="tab-content mt-5" id="myTabContent">
            <div class="tab-pane fade" id="recv" role="tabpanel" aria-labelledby="recv-tab">
                받은쪽지
            </div>
            <div class="tab-pane fade" id="send" role="tabpanel" aria-labelledby="send-tab">
                보낸쪽지
            </div>
            <div class="tab-pane fade show active px-5 py-5" id="write" role="tabpanel" aria-labelledby="write-tab" style="background-color: #f3f6f9!important;">
                <form name="fmemoform" action="<?php echo $memo_action_url; ?>" onsubmit="return fmemoform_submit(this);" method="post" autocomplete="off">
                    <div class="form-group">
                        <input type="text" name="me_recv_mb_id" value="<?php echo $me_recv_mb_id; ?>" id="me_recv_mb_id" required class="form-control border-0" size="47" placeholder="받는 회원 아이디">
                        <p class="text-dark-75 font-size-sm font-weight-normal pl-2 pt-2">
                            여러 회원에게 보낼때는 컴마(,)로 구분하세요.
                            <?php if ($config['cf_memo_send_point']) { ?>
                            <br>쪽지 보낼때 회원당 <?php echo number_format($config['cf_memo_send_point']); ?>점의 포인트를 차감합니다.
                            <?php } ?>
                        </p>
                    </div>
                    <div class="form-group">
                        <textarea class="form-control border-0" name="me_memo" id="me_memo" required rows="4" placeholder="내용" style="overflow: hidden; overflow-wrap: break-word; height: 88px;"><?php echo $content ?></textarea>
                    </div>
                    <div class="form-group">
                        <?php echo captcha_html_mt703(); ?>
                    </div>
                    <button type="submit" id="btn_submit" class="btn btn-primary font-weight-bold mr-5" style="display:none;">보내기</button>
                </form>
            </div>
        </div>

    </div>
    <div class="card-footer text-center">
        <button onclick="$('#btn_submit').click();" class="btn btn-primary font-weight-bold mr-5">보내기</button>
        <button onclick="window.close();" class="btn btn-secondary font-weight-bold">닫기</button>
    </div>
</div>
<!--end::Card-->
<!-- } 쪽지 보내기 끝 -->

<script>
function fmemoform_submit(f)
{
    <?php echo chk_captcha_js();  ?>
    return true;
}
</script>
<!-- } 쪽지 보내기 끝 -->

 

이제, 쪽지보기만 남았습니다.
아 젤 오래 걸리는 화면 그리는 작업을 해야 하네요. ㅠㅠ
(요 부분은 정말 실력이 늘지 않는군요...)
시간이 늦어서, 걍 바로 코딩으로 작업했네요.
다음 쪽지, 이전 쪽지에서 시간이 좀 걸렸지만, (걍 좌우에 놓기가 안되어서...)
card-footer를 이용하여 간단히 해결해버렸습니다.

쪽지 보기를 완료한 화면

 

글의 서두에 올렸던 깨졌단 화면과 완료된 화면을 비교해보고 싶어져 캡처를 뜹니다.
(사실 이 맛에 하는거죠~)

 

이렇게 또 하나의 단계를 넘었네요.

이제 남은건!!!
게시판!!!
이거 끝내면 나머진 별거 없으니까!!!
힘을 내서 더 달려보겠습니다!!!

긴 글 읽어주셔서 감사합니다.~~

댓글