이번에는 xe템플릿 코드로 게시판 스킨을 수정하는 방법을 공부 합니다.
수정할 페이지가 꽤 됩니다.(목록, 읽기, 쓰기, 댓글쓰기, 댓글삭제, 엮인글삭제, 권한안내페이지 등)
참고) XE는 게시판 모듈이 기본으로 설치 되어 있지 않기 때문에, 별도로 설치 해야 합니다.
저는 쉬원설치(설정한 웹FTP로 설치)를 사용하지 않고, 다운로드 사이트로 이동해서 게시판 모듈을 다운로드 후 호스팅서버에 개별 업로드 방식으로 설치 했습니다.(아래 참조)
보드모듈을 업로드 한 후 호스팅 폴더의 모습은 아래와 같습니다.
위 게시판 스킨폴더 내에서 새로 작성할 스킨폴더를 지정하거나, 업로드 한 후 파일을 살펴보면 아래와 같이 정리됩니다.
- skin.xml (게시판 스킨정보-스킨명,제작자,버전정보 등)
*참고) 여기서 잠깐 xml파일에서 변수를 지정하고, 아래 html파일에서 사용가능한 XE템플릿 형식을 알아보고 넘어 갑니다.
* xml파일에서 변수지정 : <var name="board_image" type="image"><title>내용</title></var>
* html에서 변수 사용 : {$module_info->board_image("title")} 변수->변수("title") 결과 적으론 내용 화면에 출력 됩니다.
- list.html (게시물 페이지 인클루드내용 - 아래참조
: 지난 레이아웃에서는 <load... /> 라는 태그를 사용했는데, 여긴 다릅니다. 조건문도<p cond="..></p> 를 사용하지 않고, 다른 방식입니다. )
<include target="_header.html" />
<!--@if($oDocument->isExists())-->
<include target="_read.html" />
<!--@else-->
<include target="_list.html" />
<!--@end-->
<include target="_footer.html" />
- _header.html (게시판 헤더 레이아웃 - 게시판 공통사용)
*참고) 게시판 헤더에 <div class="user_board"> 로 시작하고, 닫는 태그는 아래 푸터 파일에 존재 합니다.
- _footer.html (게시판 푸터 레이아웃 - 게시판 공통사용)
*참고) 게시판 푸터에 </div> 로 게시판 내용 끝을 지정 합니다.
- _list.html (게시물 목록 페이지 레이아웃)
- _read.html (게시물 읽기 레이아웃)
- _comment.html (댓글 쓰기 레이아웃)
- _trackback.html (트랙백 쓰기 레이아웃)
- wtire_form.html (게시물 쓰기폼 레이아웃)
- comment_form.html (댓글 쓰기폼 레이아웃)
- delete_form.html (게시물 삭제폼 레이아웃)
- delete_commnet_form.html ( 댓글 삭제폼 레이아웃)
- delete_trackback_form.html (엮인글 삭제폼 레이아웃)
- message.html (알림 메세지 레이아웃)
- 사용자지정파일명.css (게시판 스타일시트 파일)
만약 반응형으로 작업하게 되면 작업량이 상당 하게 됩니다.
*리스트 페이지 예) XE템플릿으로 만들기 전 퍼블리싱된 게시판 html은 아래와 같은 기본 골격을 같습니다.(너무 길어질것 같아서 헤더,푸터는 생략했습니다. 실제 퍼블리싱에서는 헤더,푸터를 포함한 풀코딩으로 만든 후 분해하는 절차를 거치게 됩니다. )
<include target="_header.html" />
<div class="list">
<form action="./" method="get" class="list-body">
<div class="list-container">
<ul><li>반복내용</li></ul>
</div>
</form>
<div class="fbox list-foot">
<form action="./" method="get" class="pagination">
<ul><li>페이징번호</li></ul>
</form>
</div>
<div class="list_footer">
<div class="btnArea"><a class="btn">버튼</a></div>
</div>
</div>
<include target="_footer.html" />
간단해 보이죠!!!, 하지만 실제로는 <div class="list"></div> 사이에 엄청나게 XE템플릿 코드가 많습니다. 퍼블리싱한 결과를 아래 형식에 맞게 분해 해야 합니다. 쉬운 작업은 아닙니다.(아래)
{@
$mi = $module_info;
if (!$mi->thumbnail_width) $mi->thumbnail_width = 130;
if (!$mi->thumbnail_height) $mi->thumbnail_height = 100;
if (!$mi->content_cut_size) $mi->content_cut_size = 200;
$list_idx = 1;
}
<load target="js/jquery.easing.1.3.js" />
<load target="js/list.xe.js" />
<div class="list">
<form action="./" method="get" class="list-body">
<fieldset>
<legend>List of Articles</legend>
<div class="list-container hide">
<ul>
<li class="fbox article">
<a href="#" class="title"><span class="iefix"></span></a>
<span class="thumb">
<img src="./img/noimage.gif" width="{$mi->thumbnail_width}" height="{$mi->thumbnail_height}" alt="" /></a>
</span>
<ul class="flat meta">
<li class="reply"></li>
<li class="author"><a href="#popup_menu_area" class="member_0" onclick="return false"></a></li>
<li class="date"></li>
<li class="summary"></li>
</ul>
</li>
</ul>
</div>
<div class="list-container">
<ul>
<li class="fbox article {($list_idx++%2)?'odd':'even'}" loop="$document_list=>$doc">
{@
$post_link = getUrl('document_srl',$doc->document_srl);
$perm_link = $doc->getPermanentUrl();
$comment_count = $doc->getCommentCount();
$has_thumbnail = $doc->thumbnailExists($mi->thumbnail_width, $mi->thumbnail_height, $mi->thumbnail_type);
}
<a href="{$post_link}" class="title">{$doc->getTitle($mi->subject_cut_size)} {$doc->printExtraImages(60*60*$module_info->duration_new)}<span class="iefix"></span></a>
<span class="thumb">
<!--@if($has_thumbnail)-->
<img src="{$doc->getThumbnail($mi->thumbnail_width, $mi->thumbnail_height, $mi->thumbnail_type)}" width="{$mi->thumbnail_width}" height="{$mi->thumbnail_height}" alt="" />
<!--@else-->
<img src="./img/noimage.gif" width="{$mi->thumbnail_width}" height="{$mi->thumbnail_height}" alt="" />
<!--@end-->
</span>
<ul class="flat meta">
<li class="reply">{$comment_count}</li>
<li class="author"><a href="#popup_menu_area" class="member_{$doc->get('member_srl')}" onclick="return false">{$doc->getNickName()}</a></li>
<li class="date">{$doc->getRegdate('Y.m.d')}</li>
<li class="summary">{$doc->getSummary($mi->content_cut_size)}</li>
</ul>
</li>
</ul>
</div>
</fieldset>
</form>
<div class="fbox list-foot">
<form action="./" method="get" class="pagination">
<fieldset>
<legend>Board Pagination</legend>
<input type="hidden" name="vid" value="{$vid}" />
<input type="hidden" name="mid" value="{$mid}" />
<input type="hidden" name="category" value="{$category}" />
<input type="hidden" name="search_keyword" value="{htmlspecialchars($search_keyword)}" />
<input type="hidden" name="search_target" value="{$search_target}" />
{@ $prev_page = max($page-1, 1)}
{@ $next_page = min($page+1, $page_navigation->last_page)}
<ul>
<li class="first"><a href="{getUrl('page','','document_srl','','division',$division,'last_division',$last_division)}" class="active"|cond="$page!=1"><span>{$lang->first_page}</span></a></li>
<li class="prev"><a href="{getUrl('page',$prev_page,'document_srl','','division',$division,'last_division',$last_division)}" class="active"|cond="$prev_page<$page"><span>PREV {$mi->list_count}</span></a></li>
<li class="pages"><span><input type="text" name="page" value="{$page}" /> of <em>{$total_page}</em></span></li>
<li class="next"><a href="{getUrl('page',$next_page,'document_srl','','division',$division,'last_division',$last_division)}" class="active"|cond="$next_page>$page"><span>NEXT {$mi->list_count}</span></a></li>
<li class="last"><a href="{getUrl('page',$total_page,'document_srl','','division',$division,'last_division',$last_division)}" class="active"|cond="$total_page>$page"><span>{$lang->last_page}</span></a></li>
</ul>
</fieldset>
</form>
</div>
<div class="list_footer">
<div class="btnArea">
<a class="btn" href="{getUrl('act','dispBoardWrite','document_srl','')}">{$lang->cmd_write}</a>
</div>
<button type="button" class="bsToggle" title="{$lang->cmd_search}">{$lang->cmd_search}</button>
<form cond="$grant->view" action="{getUrl()}" method="get" onsubmit="return procFilter(this, search)" id="board_search" class="board_search" no-error-return-url="true">
<input type="hidden" name="vid" value="{$vid}" />
<input type="hidden" name="mid" value="{$mid}" />
<input type="hidden" name="category" value="{$category}" />
<input type="text" name="search_keyword" value="{htmlspecialchars($search_keyword)}" title="{$lang->cmd_search}" class="iText" />
<select name="search_target">
<option loop="$search_option=>$key,$val" value="{$key}" selected="selected"|cond="$search_target==$key">{$val}</option>
</select>
<button class="btn" type="submit">{$lang->cmd_search}</button>
<a cond="$last_division" class="btn" href="{getUrl('page',1,'document_srl','','division',$last_division,'last_division','')}">{$lang->cmd_search_next}</a>
</form>
<a href="{getUrl('act','dispBoardTagList')}" class="tagSearch" title="{$lang->tag}">{$lang->tag}</a>
</div>
</div>
<script>
if (typeof window.xe_v3 == 'undefined') window.xe_v3 = {};
jQuery.extend(xe_v3, {
page : '{$page}',
list_count : '{$mi->list_count}',
last_page : '{$total_page}',
content_cut_size : '{$mi->content_cut_size}',
thumbnail_width : '{$mi->thumbnail_width}',
thumbnail_height : '{$mi->thumbnail_height}',
thumbnail_type : '{$mi->thumbnail_type}',
search_keyword : '{addslashes($search_keyword)}',
search_target : '{$search_target}'
});
</script>
*쓰기 페이지 예) XE템플릿으로 만들기 전 퍼블리싱된 게시판 html은 아래와 같은 기본 골격을 같습니다.(너무 길어질것 같아서 헤더,푸터는 생략했습니다. 실제 퍼블리싱에서는 헤더,푸터를 포함한 풀코딩으로 만든 후 분해하는 절차를 거치게 됩니다. )
<include target="_header.html" />
<form action="./" method="post" class="board_write">
<div class="write_header">옵션항목</div>
<div class="exForm">사용자 추가항목</div>
<div class="write_editor">입력에디터폼</div>
<div class="write_footer">글쓴이정보입력창+쓰기버튼</div>
</form>
<include target="_footer.html" />
간단해 보이죠!!! 하지만, 실제로는 <form></form> 사이에 엄청나게 XE템플릿 코드가 많습니다. 퍼블리싱한 결과를 아래 형식에 맞게 분해 해야 합니다.(아래)
<include target="_header.html" />
<form action="./" method="post" onsubmit="return procFilter(this, window.insert)" class="board_write">
<input type="hidden" name="mid" value="{$mid}" />
<input type="hidden" name="content" value="{$oDocument->getContentText()}" />
<input type="hidden" name="document_srl" value="{$document_srl}" />
<div class="write_header">
<select name="category_srl" cond="$module_info->use_category=='Y'">
<option value="">{$lang->category}</option>
<option loop="$category_list => $val" disabled="disabled"|cond="!$val->grant" value="{$val->category_srl}" selected="selected"|cond="$val->grant&&$val->selected||$val->category_srl==$oDocument->get('category_srl')">
{str_repeat(" ",$val->depth)} {$val->title} ({$val->document_count})
</option>
</select>
<input cond="$oDocument->getTitleText()" type="text" name="title" class="iText" title="{$lang->title}" value="{htmlspecialchars($oDocument->getTitleText())}" />
<input cond="!$oDocument->getTitleText()" type="text" name="title" class="iText" title="{$lang->title}" />
<input cond="$grant->manager" type="checkbox" name="is_notice" value="Y" class="iCheck" checked="checked"|cond="$oDocument->isNotice()" id="is_notice" />
<label cond="$grant->manager" for="is_notice">{$lang->notice}</label>
</div>
<div class="exForm" cond="count($extra_keys)">
<table cond="count($extra_keys)" border="1" cellspacing="0" summary="Extra Form">
<caption><em>*</em> : {$lang->is_required}</caption>
<tr loop="$extra_keys=>$key,$val">
<th scope="row"><em cond="$val->is_required=='Y'">*</em> {$val->name}</th>
<td>{$val->getFormHTML()}</td>
</tr>
</table>
</div>
<div class="write_editor">
{$oDocument->getEditor()}
</div>
<div class="write_footer">
<div class="write_option">
<block cond="$grant->manager">
<input type="checkbox" name="title_bold" id="title_bold" class="iCheck" value="Y" checked="checked"|cond="$oDocument->get('title_bold')=='Y'" />
<label for="title_bold">{$lang->title_bold}</label>
</block>
<input cond="$module_info->secret=='Y'" type="checkbox" name="is_secret" class="iCheck" value="Y" checked="checked"|cond="$oDocument->isSecret()" id="is_secret" />
<label cond="$module_info->secret=='Y'" for="is_secret">{$lang->secret}</label>
<input type="checkbox" name="comment_status" class="iCheck" value="ALLOW" checked="checked"|cond="$oDocument->allowComment()" id="comment_status" />
<label for="comment_status">{$lang->allow_comment}</label>
<input type="checkbox" name="allow_trackback" class="iCheck" value="Y" checked="checked"|cond="$oDocument->allowTrackback()" id="allow_trackback" />
<label for="allow_trackback">{$lang->allow_trackback}</label>
<block cond="$is_logged">
<input type="checkbox" name="notify_message" class="iCheck" value="Y" checked="checked"|cond="$oDocument->useNotify()" id="notify_message" />
<label for="notify_message">{$lang->notify}</label>
</block>
<!--@if(is_array($status_list))-->
<!--@foreach($status_list AS $key=>$value)-->
<input type="radio" name="status" value="{$key}" id="{$key}" <!--@if($oDocument->get('status') == $key || ($key == 'PUBLIC' && !$document_srl))-->checked="checked"<!--@end--> />
<label for="{$key}">{$value}</label>
<!--@end-->
<!--@end-->
</div>
<div class="write_author">
<span class="item" cond="!$is_logged">
<label for="userName" class="iLabel">{$lang->writer}</label>
<input type="text" name="nick_name" id="userName" class="iText userName" style="width:80px" value="{$oDocument->getNickName()}" />
</span>
<span class="item" cond="!$is_logged">
<label for="userPw" class="iLabel">{$lang->password}</label>
<input type="password" name="password" id="userPw" class="iText userPw" style="width:80px" />
</span>
<span class="item" cond="!$is_logged">
<label for="homePage" class="iLabel">{$lang->homepage}</label>
<input type="text" name="homepage" id="homePage" class="iText homePage" style="width:140px"value="{htmlspecialchars($oDocument->get('homepage'))}" />
</span>
<span class="item">
<label for="tags" class="iLabel">{$lang->tag} : {$lang->about_tag}</label>
<input type="text" name="tags" id="tags" value="{htmlspecialchars($oDocument->get('tags'))}" class="iText" style="width:260px" title="Tag" />
</span>
</div>
<div class="btnArea">
<input class="btn" type="submit" value="{$lang->cmd_registration}" />
<!--@if(!$oDocument->isExists() || $oDocument->get('status') == 'TEMP')-->
<button cond="$is_logged" class="btn" type="button" onclick="doDocumentSave(this);">{$lang->cmd_temp_save}</button>
<button cond="$is_logged" class="btn" type="button" onclick="doDocumentLoad(this);">{$lang->cmd_load}</button>
<!--@end-->
</div>
</div>
</form>
<include target="_footer.html" />
*읽기 페이지 예) XE템플릿으로 만들기 전 퍼블리싱된 게시판 html은 아래와 같은 기본 골격을 같습니다.(너무 길어질것 같아서 트랙백,댓글은 생략했습니다. 실제 퍼블리싱에서는 헤더,푸터를 포함한 풀코딩으로 만든 후 분해하는 절차를 거치게 됩니다. )
<div class="board_read">
<div class="read_header">제목,글쓴이,조회 수,추천 수,날짜</div>
<div class="exOut">사용자 추가항목</div>
<div class="read_body">게시물 본문</div>
<div class="read_footer">첨부파일,버튼</div>
</div>
<include target="_trackback.html" />
<include target="_comment.html" />
간단해 보이죠!!! 하지만, 실제로는 <div class="board_read"></div> 사이에 엄청나게 XE템플릿 코드가 많습니다. 퍼블리싱한 결과를 아래 형식에 맞게 분해 해야 합니다.(아래)
<div class="board_read">
<!-- READ HEADER -->
<div class="read_header">
<h1>
<a href="{getUrl('category',$oDocument->get('category_srl'), 'document_srl', '')}" class="category" cond="$module_info->use_category=='Y'">{$category_list[$oDocument->get('category_srl')]->title}</a>
<a href="{$oDocument->getPermanentUrl()}">{$oDocument->getTitle()}</a>
</h1>
<p class="time">
{$oDocument->getRegdate('Y.m.d H:i')}
</p>
<p class="meta">
<a cond="$module_info->display_author!='N' && !$oDocument->getMemberSrl() && $oDocument->isExistsHomepage()" href="{$oDocument->getHomepageUrl()}" onclick="window.open(this.href);return false;" class="author">{$oDocument->getNickName()}</a>
<block cond="$module_info->display_author!='N' && !$oDocument->getMemberSrl() && !$oDocument->isExistsHomepage()">{$oDocument->getNickName()}</block>
<a cond="$module_info->display_author!='N' && $oDocument->getMemberSrl()" href="#popup_menu_area" class="member_{$oDocument->get('member_srl')} author" onclick="return false">{$oDocument->getNickName()}</a>
<span class="sum">
<span class="read">{$lang->readed_count}:{$oDocument->get('readed_count')}</span>
<span class="vote" cond="$oDocument->get('voted_count')!=0">{$lang->cmd_vote}:{$oDocument->get('voted_count')}</span>
</span>
</p>
</div>
<!-- /READ HEADER -->
<!-- Extra Output -->
<div class="exOut" cond="$oDocument->isExtraVarsExists() && (!$oDocument->isSecret() || $oDocument->isGranted())">
<table border="1" cellspacing="0" summary="Extra Form Output">
<tr loop="$oDocument->getExtraVars() => $key,$val">
<th scope="row">{$val->name}</th>
<td>{$val->getValueHTML()} </td>
</tr>
</table>
</div>
<!-- /Extra Output -->
<!-- READ BODY -->
<div class="read_body">
<!--@if($oDocument->isSecret() && !$oDocument->isGranted())-->
<form action="./" method="get" onsubmit="return procFilter(this, input_password)">
<input type="hidden" name="mid" value="{$mid}" />
<input type="hidden" name="page" value="{$page}" />
<input type="hidden" name="document_srl" value="{$oDocument->document_srl}" />
<p><label for="cpw">{$lang->msg_is_secret} {$lang->msg_input_password}</label></p>
<p><input type="password" name="password" id="cpw" class="iText" /><input class="btn" type="submit" value="{$lang->cmd_input}" /></p>
</form>
<!--@else-->
{$oDocument->getContent(false)}
<!--@end-->
</div>
<!-- /READ BODY -->
<!-- READ FOOTER -->
<div class="read_footer">
<div cond="$oDocument->hasUploadedFiles()" class="fileList">
<button type="button" class="toggleFile" onclick="jQuery(this).next('ul.files').toggle();">{$lang->uploaded_file} [<strong>{$oDocument->get('uploaded_count')}</strong>]</button>
<ul class="files">
<li loop="$oDocument->getUploadedFiles()=>$key,$file"><a href="{getUrl('')}{$file->download_url}">{$file->source_filename} <span class="fileSize">[File Size:{FileHandler::filesize($file->file_size)}/Download:{number_format($file->download_count)}]</span></a></li>
</ul>
</div>
<div class="tns">
{@ $tag_list = $oDocument->get('tag_list') }
<span class="tags" cond="count($tag_list)">
<!--@for($i=0;$i<count($tag_list);$i++)-->
{@ $tag = $tag_list[$i]; }
<a href="{getUrl('search_target','tag','search_keyword',$tag,'document_srl','')}" class="tag" rel="tag">{htmlspecialchars($tag)}</a><span>,</span>
<!--@end-->
</span>
<a class="document_{$oDocument->document_srl} action" href="#popup_menu_area" onclick="return false">{$lang->cmd_document_do}</a>
<ul class="sns">
<li class="twitter link"><a href="http://twitter.com/">Twitter</a></li>
<li class="me2day link"><a href="http://me2day.net/">Me2day</a></li>
<li class="facebook link"><a href="http://facebook.com/">Facebook</a></li>
<li class="delicious link"><a href="http://delicious.com/">Delicious</a></li>
</ul>
<script>
jQuery(function($){
$('.twitter>a').snspost({
type : 'twitter',
content : '{$oDocument->getTitle()} {$oDocument->getPermanentUrl()}'
});
$('.me2day>a').snspost({
type : 'me2day',
content : '\"{$oDocument->getTitle()}\":{$oDocument->getPermanentUrl()}'
});
$('.facebook>a').snspost({
type : 'facebook',
content : '{$oDocument->getTitle()}'
});
$('.delicious>a').snspost({
type : 'delicious',
content : '{$oDocument->getTitle()}'
});
});
</script>
</div>
<div class="sign" cond="$module_info->display_sign!='N'&&($oDocument->getProfileImage()||$oDocument->getSignature())">
<img cond="$oDocument->getProfileImage()" src="{$oDocument->getProfileImage()}" alt="Profile" class="pf" />
<div cond="$oDocument->getSignature()" class="tx">{$oDocument->getSignature()}</div>
</div>
<div class="btnArea">
<a class="btn" cond="$oDocument->isEditable()" href="{getUrl('act','dispBoardWrite','document_srl',$oDocument->document_srl,'comment_srl','')}">{$lang->cmd_modify}</a>
<a class="btn" cond="$oDocument->isEditable()" href="{getUrl('act','dispBoardDelete','document_srl',$oDocument->document_srl,'comment_srl','')}">{$lang->cmd_delete}</a>
<span class="etc">
<a class="btn" href="{getUrl('document_srl','')}">{$lang->cmd_list}</a>
</span>
</div>
</div>
<!-- /READ FOOTER -->
</div>
<block cond="$oDocument->allowTrackback()">
<include target="_trackback.html" />
</block>
<include target="_comment.html" />
부트스트랩3.x 용 레이아웃 퍼블리싱_01(네이버xe레이아웃을위한) (0) | 2017.05.28 |
---|---|
네이버XE HTML기본 구조 및 부트스트랩3.x 기본지식 (0) | 2017.05.28 |
네이버XE(제로보드xe) XE템플릿 코드로 레이아웃 수정 (0) | 2017.05.22 |
네이버XE로 홈페이지 리뉴얼 실무 (0) | 2017.05.21 |
네이버XE(제로보드XE)를 시작 합니다. (0) | 2017.05.20 |
댓글 영역