diff --git a/modules/guestbook/conf/info.xml b/modules/guestbook/conf/info.xml
new file mode 100644
index 000000000..382fe7356
--- /dev/null
+++ b/modules/guestbook/conf/info.xml
@@ -0,0 +1,8 @@
+
+
+ 방명록
+
+ 제로
+ 모듈 제작을 위해 sample로 제작된 모듈입니다.
+
+
diff --git a/modules/guestbook/conf/module.xml b/modules/guestbook/conf/module.xml
new file mode 100644
index 000000000..d9f009073
--- /dev/null
+++ b/modules/guestbook/conf/module.xml
@@ -0,0 +1,41 @@
+
+
+
+
+ 글 작성
+
+
+ 댓글 작성
+
+
+ 관리
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/guestbook/guestbook.admin.controller.php b/modules/guestbook/guestbook.admin.controller.php
new file mode 100644
index 000000000..780558fc7
--- /dev/null
+++ b/modules/guestbook/guestbook.admin.controller.php
@@ -0,0 +1,247 @@
+module = 'guestbook';
+
+ // mid값을 직접 받지 않고 guestbook_name으로 받는 이유는 mid는 특별히 약속된 변수명이라 오동작이 발생할 수 있어서 다른 이름으로 전달을 받은후 다시 바꾸어준다.
+ $args->mid = $args->guestbook_name;
+ unset($args->guestbook_name);
+
+ // is_default일 경우 별다른 요청이 없는 index페이지의 경우 바로 호출이 되는데 이 값을 설정을 하게 된다.
+ if($args->is_default!='Y') $args->is_default = 'N';
+
+ // 기본 값외의 것들을 정리
+ $extra_var = delObjectVars(Context::getRequestVars(), $args);
+ unset($extra_var->act);
+ unset($extra_var->page);
+ unset($extra_var->guestbook_name);
+
+ // module_srl이 넘어오면 원 모듈이 있는지 확인
+ if($args->module_srl) {
+ $oModuleModel = &getModel('module');
+ $module_info = $oModuleModel->getModuleInfoByModuleSrl($args->module_srl);
+
+ // 만약 원래 모듈이 없으면 새로 입력하기 위한 처리
+ if($module_info->module_srl != $args->module_srl) unset($args->module_srl);
+ }
+
+ // $extra_var를 serialize
+ $args->extra_vars = serialize($extra_var);
+
+ // module 모듈의 controller 객체 생성
+ $oModuleController = &getController('module');
+
+ // is_default=='Y' 이면
+ if($args->is_default=='Y') $oModuleController->clearDefaultModule();
+
+ /**
+ * module_srl값이 없다면 신규 등록으로 처리를 한다.
+ **/
+ if(!$args->module_srl) {
+ // module controller를 이용하여 모듈을 생성한다.
+ $output = $oModuleController->insertModule($args);
+ $msg_code = 'success_registed';
+
+ // 권한의 경우 기본으로 설정을 해주는 것이 좋으며 방명록 모듈의 경우 manager권한을 관리 그룹으로 설정을 한다.
+ if($output->toBool()) {
+ // 관리그룹을 member model객체에서 구할 수 있다.
+ $oMemberModel = &getModel('member');
+ $admin_group = $oMemberModel->getAdminGroup();
+ $admin_group_srl = $admin_group->group_srl;
+
+ $module_srl = $output->get('module_srl');
+ $grants = serialize(array('manager'=>array($admin_group_srl)));
+
+ // module controller의 module 권한 설정 method를 이용하여 기본 권한을 적용한다.
+ $oModuleController->updateModuleGrant($module_srl, $grants);
+ }
+ /**
+ * module_srl이 있다면 모듈의 정보를 수정한다
+ **/
+ } else {
+ $output = $oModuleController->updateModule($args);
+ $msg_code = 'success_updated';
+ }
+
+ // 결과값에 오류가 있을 경우 그대로 객체 리턴.
+ if(!$output->toBool()) return $output;
+
+ // 등록후 페이지 이동을 위해 변수 설정 및 메세지를 설정한다.
+ $this->add('page',Context::get('page'));
+ $this->add('module_srl',$output->get('module_srl'));
+ $this->setMessage($msg_code);
+ }
+
+ /**
+ * @brief 방명록 삭제
+ **/
+ function procGuestbookAdminDeleteGuestbook() {
+ // 삭제할 대상 방명록의 module_srl을 구한다.
+ $module_srl = Context::get('module_srl');
+
+ // 원본을 구해온다
+ $oModuleController = &getController('module');
+ $output = $oModuleController->deleteModule($module_srl);
+
+ // 삭제 처리시 오류가 발생하면 결과 객체를 바로 리턴한다.
+ if(!$output->toBool()) return $output;
+
+ // 등록후 페이지 이동을 위해 변수 설정 및 메세지를 설정한다.
+ $this->add('module','guestbook');
+ $this->add('page',Context::get('page'));
+ $this->setMessage('success_deleted');
+ }
+
+ /**
+ * @brief 권한 설정
+ * 생성된 방명록에 ./conf/module.xml에 정의된 권한과 관리자가 선택한 그룹의 값을 연동하여 권한을 설정하게 된다.
+ **/
+ function procGuestbookAdminInsertGrant() {
+ // 대상 방명록(모듈)의 고유값인 module_srl을 체크한다.
+ $module_srl = Context::get('module_srl');
+
+ /**
+ * 현 모듈의 권한 목록을 가져옴
+ * xml_info 는 guestbook모듈이 요청되었다고 판단될때 ModuleObject에서 이미 세팅해 놓은 상태이다.
+ **/
+ $grant_list = $this->xml_info->grant;
+
+ /**
+ * 권한의 목록을 loop로 돌면서 권한 설정을 한다.
+ * zbxe의 경우 가능한 간단한 xmlrpc사용을 위해서 배열의 경우 |@|를 pipe로 하여 하나의 string으로 전달한다.
+ * 요청받은 권한의 대상 그룹과 권한을 배열로 한 후 serialize하여 modules테이블에 module_srl을 키로 한 rows에 데이터를 적용한다.
+ **/
+ if(count($grant_list)) {
+ foreach($grant_list as $key => $val) {
+ $group_srls = Context::get($key);
+ if($group_srls) $arr_grant[$key] = explode('|@|',$group_srls);
+ }
+ $grants = serialize($arr_grant);
+ }
+
+ // 권한 설정은 모듈 공통이라 module 모듈의 controller을 생성하여 저장하도록 한다.
+ $oModuleController = &getController('module');
+ $oModuleController->updateModuleGrant($module_srl, $grants);
+
+ // 권한 설정후 돌아갈 페이지를 위하여 module_srl값을 세팅하고 성공 메세지 역시 세팅한다.
+ $this->add('module_srl',Context::get('module_srl'));
+ $this->setMessage('success_registed');
+ }
+
+ /**
+ * @brief 스킨 정보 업데이트
+ * 스킨 정보는 skin.xml파일의 extra_vars와 입력된 변수값을 조합하여 serialize하여 modules 테이블에 module_srl을 키로 하여 저장을 하게 된다.
+ **/
+ function procGuestbookAdminUpdateSkinInfo() {
+ // module_srl에 해당하는 정보들을 가져오기
+ $module_srl = Context::get('module_srl');
+
+ // 어떤 스킨이 사용중인지 확인하기 위해서 module_srl을 이용하여 모듈의 정보를 구하고 스킨을 구한다.
+ $oModuleModel = &getModel('module');
+ $module_info = $oModuleModel->getModuleInfoByModuleSrl($module_srl);
+ $skin = $module_info->skin;
+
+ // 스킨의 정보르 구해옴 (extra_vars를 체크하기 위해서)
+ $skin_info = $oModuleModel->loadSkinInfo($this->module_path, $skin);
+
+ // 입력받은 변수들을 체크 (mo, act, module_srl, page등 기본적인 변수들 없앰)
+ $obj = Context::getRequestVars();
+ unset($obj->act);
+ unset($obj->module_srl);
+ unset($obj->page);
+
+ // 원 skin_info에서 extra_vars의 type이 image일 경우 별도 처리를 해줌
+ if($skin_info->extra_vars) {
+ foreach($skin_info->extra_vars as $vars) {
+ if($vars->type!='image') continue;
+
+ $image_obj = $obj->{$vars->name};
+
+ // 삭제 요청에 대한 변수를 구함
+ $del_var = $obj->{"del_".$vars->name};
+ unset($obj->{"del_".$vars->name});
+ if($del_var == 'Y') {
+ @unlink($module_info->{$vars->name});
+ continue;
+ }
+
+ // 업로드 되지 않았다면 이전 데이터를 그대로 사용
+ if(!$image_obj['tmp_name']) {
+ $obj->{$vars->name} = $module_info->{$vars->name};
+ continue;
+ }
+
+ // 정상적으로 업로드된 파일이 아니면 무시
+ if(!is_uploaded_file($image_obj['tmp_name'])) {
+ unset($obj->{$vars->name});
+ continue;
+ }
+
+ // 이미지 파일이 아니어도 무시
+ if(!eregi("\.(jpg|jpeg|gif|png)$", $image_obj['name'])) {
+ unset($obj->{$vars->name});
+ continue;
+ }
+
+ // 경로를 정해서 업로드
+ $path = sprintf("./files/attach/images/%s/", $module_srl);
+
+ // 디렉토리 생성
+ if(!FileHandler::makeDir($path)) return false;
+
+ $filename = $path.$image_obj['name'];
+
+ // 파일 이동
+ if(!move_uploaded_file($image_obj['tmp_name'], $filename)) {
+ unset($obj->{$vars->name});
+ continue;
+ }
+
+ // 변수를 바꿈
+ unset($obj->{$vars->name});
+ $obj->{$vars->name} = $filename;
+ }
+ }
+
+ // serialize하여 저장
+ $skin_vars = serialize($obj);
+
+ // module controller객체를 생성하여 module_srl을 키로 한 rows에 serialize한 스킨 정보를 적용한다.
+ $oModuleController = &getController('module');
+ $oModuleController->updateModuleSkinVars($module_srl, $skin_vars);
+
+ /**
+ * 스킨 정보는 첨부파일때문에 xml로 전달이 되지 않고 POST로 전송이 되어 왔으므로 템플릿을 이용하여 프레임을 refresh시키도록 한다.
+ * 스킨 정보를 수정할때 숨어 있는 iframe을 target으로 삼기에 기본 레이아웃을 이용하면 되므로 직접 레이아웃 경로와 파일을 기본으로 지정한다.
+ **/
+ $this->setLayoutPath('./common/tpl');
+ $this->setLayoutFile('default_layout.html');
+ $this->setTemplatePath($this->module_path.'tpl');
+ $this->setTemplateFile("top_refresh.html");
+ }
+ }
+?>
diff --git a/modules/guestbook/guestbook.admin.view.php b/modules/guestbook/guestbook.admin.view.php
new file mode 100644
index 000000000..e39de281b
--- /dev/null
+++ b/modules/guestbook/guestbook.admin.view.php
@@ -0,0 +1,185 @@
+module_srl) {
+ $module_srl = $this->module_srl;
+ Context::set('module_srl', $module_srl);
+ }
+
+ // module info를 구하기 위해 module model 객체 생성
+ $oModuleModel = &getModel('module');
+
+ // module_srl이 있다면 요청된 모듈의 정보를 미리 구해 놓음
+ if($module_srl) {
+ $module_info = $oModuleModel->getModuleInfoByModuleSrl($module_srl);
+ if(!$module_info) {
+ Context::set('module_srl','');
+ $this->act = 'list';
+ } else {
+ $this->module_info = $module_info;
+ Context::set('module_info',$module_info);
+ }
+ }
+
+ // 템플릿 경로 지정, 관리자 페이지를 위한 템플릿은 별도의 스킨 기능이 없이 ./modules/모듈/tpl/ 에 위치해 놓기에 바로 지정을 해 놓는다.
+ $template_path = sprintf("%stpl/",$this->module_path);
+ $this->setTemplatePath($template_path);
+ }
+
+ /**
+ * @brief 생성된 방명록들의 목록을 보여줌
+ * guestbook이라는 module명으로 등록된 모듈을 구하기 위해서 몇가지 설정을 한 후에 쿼리를 수행한다.
+ * 쿼리수행은 executeQuery(모듈명.쿼리아이디, 인자변수) 로 하게 되며 이 쿼리아이디에 해당하는 xml파일은 모듈의 queries디렉토리에 지정이 되어 있다.
+ *
+ * 이 특정 module의 목록은 module model객체에서 구할 수 있지만 검색등의 각 모듈마다 다른 조건 때문에 각 모듈별로 쿼리를 생성해 놓는다.
+ * 모든 모듈의 결과물(mid)는 modules 테이블에 저장이 된다.
+ **/
+ function dispGuestbookAdminContent() {
+ $args->sort_index = "module_srl"; ///< 정렬 순서는 모듈의 sequence값으로 하고 정렬은 역순. 즉 생성된 순으로 한다.
+ $args->page = Context::get('page'); ///< 현재 페이지를 설정
+ $args->list_count = 40; ///< 한페이지에 40개씩 보여주기로 고정.
+ $args->page_count = 10; ///< 페이지의 수는 10개로 제한.
+ $args->s_module_category_srl = Context::get('module_category_srl'); ///< 모듈분류값을 인자로 추가
+ $output = executeQuery('guestbook.getGuestbookList', $args); ///< guestbook.getGuesbookList 쿼리 실행 (./modules/guestbook/query/getGuestbookList.xml)
+
+ /**
+ * 템플릿에 쓰기 위해서 context::set
+ * xml query에 navigation이 있고 list_count가 정의되어 있으면 결과 변수에 아래 5가지의 값이 세팅이 된다.
+ **/
+ Context::set('total_count', $output->total_count);
+ Context::set('total_page', $output->total_page);
+ Context::set('page', $output->page);
+ Context::set('guestbook_list', $output->data);
+ Context::set('page_navigation', $output->page_navigation);
+
+ // 템플릿 파일 지정 (./modules/guestbook/tpl/index.html파일이 지정이 됨)
+ $this->setTemplateFile('index');
+ }
+
+ /**
+ * @brief 선택된 방명록의 정보 출력
+ **/
+ function dispGuestbookAdminGuestbookInfo() {
+ // module_srl 값이 없다면 그냥 index 페이지를 보여줌
+ if(!Context::get('module_srl')) return $this->dispGuestbookAdminContent();
+
+ // 레이아웃이 정해져 있다면 레이아웃 정보를 추가해줌(layout_title, layout)
+ if($this->module_info->layout_srl) {
+ $oLayoutModel = &getModel('layout');
+ $layout_info = $oLayoutModel->getLayout($this->module_info->layout_srl);
+ $this->module_info->layout = $layout_info->layout;
+ $this->module_info->layout_title = $layout_info->layout_title;
+ }
+
+ // 정해진 스킨이 있으면 해당 스킨의 정보를 구함
+ if($this->module_info->skin) {
+ $oModuleModel = &getModel('module');
+ $skin_info = $oModuleModel->loadSkinInfo($this->module_path, $this->module_info->skin);
+ $this->module_info->skin_title = $skin_info->title;
+ }
+
+ // 템플릿 파일 지정
+ $this->setTemplateFile('guestbook_info');
+ }
+
+ /**
+ * @brief 방명록 설정 폼 출력
+ **/
+ function dispGuestbookAdminInsertGuestbook() {
+ // 스킨 목록을 구해옴
+ $oModuleModel = &getModel('module');
+ $skin_list = $oModuleModel->getSkins($this->module_path);
+ Context::set('skin_list',$skin_list);
+
+ // 레이아웃 목록을 구해옴
+ $oLayoutMode = &getModel('layout');
+ $layout_list = $oLayoutMode->getLayoutList();
+ Context::set('layout_list', $layout_list);
+
+ // 템플릿 파일 지정
+ $this->setTemplateFile('guestbook_insert');
+ }
+
+ /**
+ * @brief 방명록 삭제 화면 출력
+ **/
+ function dispGuestbookAdminDeleteGuestbook() {
+ if(!Context::get('module_srl')) return $this->dispGuestbookAdminContent();
+
+ $module_info = Context::get('module_info');
+
+ // 해당 방명록에 입력된 전체 글의 갯수를 보여줌 (혹시 삭제 실수를 방지하기 위해서)
+ $oDocumentModel = &getModel('document');
+ $document_count = $oDocumentModel->getDocumentCount($module_info->module_srl);
+ $module_info->document_count = $document_count;
+
+ Context::set('module_info',$module_info);
+
+ // 템플릿 파일 지정
+ $this->setTemplateFile('guestbook_delete');
+ }
+
+ /**
+ * @brief 스킨 정보 보여줌
+ **/
+ function dispGuestbookAdminSkinInfo() {
+
+ // 현재 선택된 모듈의 스킨의 정보 xml 파일을 읽음
+ $module_info = Context::get('module_info');
+ $skin = $module_info->skin;
+
+ $oModuleModel = &getModel('module');
+ $skin_info = $oModuleModel->loadSkinInfo($this->module_path, $skin);
+
+ // skin_info에 extra_vars 값을 지정
+ if(count($skin_info->extra_vars)) {
+ foreach($skin_info->extra_vars as $key => $val) {
+ $name = $val->name;
+ $type = $val->type;
+ $value = $module_info->{$name};
+ if($type=="checkbox"&&!$value) $value = array();
+ $skin_info->extra_vars[$key]->value= $value;
+ }
+ }
+
+ Context::set('skin_info', $skin_info);
+ $this->setTemplateFile('skin_info');
+ }
+
+ /**
+ * @brief 권한 목록 출력
+ **/
+ function dispGuestbookAdminGrantInfo() {
+ // module_srl을 구함
+ $module_srl = Context::get('module_srl');
+
+ // module.xml에서 권한 관련 목록을 구해옴
+ $grant_list = $this->xml_info->grant;
+ Context::set('grant_list', $grant_list);
+
+ // 권한 그룹의 목록을 가져온다
+ $oMemberModel = &getModel('member');
+ $group_list = $oMemberModel->getGroups();
+ Context::set('group_list', $group_list);
+
+ $this->setTemplateFile('grant_list');
+ }
+ }
+?>
diff --git a/modules/guestbook/guestbook.class.php b/modules/guestbook/guestbook.class.php
new file mode 100644
index 000000000..0221c0227
--- /dev/null
+++ b/modules/guestbook/guestbook.class.php
@@ -0,0 +1,59 @@
+insertActionForward('guestbook', 'view', 'dispGuestbookAdminContent');
+ $oModuleController->insertActionForward('guestbook', 'view', 'dispGuestbookAdminGuestbookInfo');
+ $oModuleController->insertActionForward('guestbook', 'view', 'dispGuestbookAdminInsertGuestbook');
+ $oModuleController->insertActionForward('guestbook', 'view', 'dispGuestbookAdminDeleteGuestbook');
+ $oModuleController->insertActionForward('guestbook', 'view', 'dispGuestbookAdminSkinInfo');
+ $oModuleController->insertActionForward('guestbook', 'view', 'dispGuestbookAdminGrantInfo');
+ $oModuleController->insertActionForward('guestbook', 'controller', 'procGuestbookAdminUpdateSkinInfo');
+
+
+ // Object 클래스의 객체는 기본적으로 성공(error=0)으로 되어 있고 이 값을 return함으로써 ModuleHandler등에서 오류 유무를 파악할 수 있다.
+ return new Object();
+ }
+
+ /**
+ * @brief 설치가 이상이 없는지 체크하는 method
+ * 설치시 필수 체크 부분이 있다면 검토하는 코드를 추가할 수 있다.
+ **/
+ function moduleIsInstalled() {
+ return new Object();
+ }
+
+ /**
+ * @brief 업데이트 실행
+ * 설치시 이상이 있으면 이 moduleUpdate() 메쏘드를 이용하여 업데이트 구문을 실행할수 있다.
+ **/
+ function moduleUpdate() {
+ return new Object();
+ }
+
+ }
+?>
diff --git a/modules/guestbook/guestbook.controller.php b/modules/guestbook/guestbook.controller.php
new file mode 100644
index 000000000..8e6082d61
--- /dev/null
+++ b/modules/guestbook/guestbook.controller.php
@@ -0,0 +1,246 @@
+grant->write_document) return new Object(-1, 'msg_not_permitted');
+
+ /**
+ * 글작성시 필요한 변수를 세팅한다.
+ * 일단 Context::getReuqestVars()를 통해 모든 입력된 변수값을 가져온다.
+ * 글 작성은 document controller를 이용하여 정리된 변수를 넘겨줌으로서 동작이 된다.
+ **/
+ $obj = Context::getRequestVars();
+
+ // 현재 방명록의 module_srl값을 구해와서 세팅한다.
+ $obj->module_srl = $this->module_srl;
+
+ // 공지사항 지정 변수값인 is_notice가 Y가 아니거나 관리자가 아니라면 공지사항은 무조건 N로 세팅한다.
+ if($obj->is_notice!='Y'||!$this->grant->manager) $obj->is_notice = 'N';
+
+ /**
+ * 문서의 신규 입력인지 수정인지에 대한 체크를 하기 위해서 document model을 통해 원본 문서가 있는지 확인하는 절차를 거쳐야 한다.
+ **/
+ $oDocumentModel = &getModel('document');
+
+ // document module의 controller 객체 생성
+ $oDocumentController = &getController('document');
+
+ // 문서객체를 구해온다.
+ $oDocument = $oDocumentModel->getDocument($obj->document_srl, $this->grant->manager);
+
+ /**
+ * 제목은 document모델에서는 필수 요건이다.
+ * 방명록에서는 제목이 필요 없어서 본문의 내용중 앞 10자리의 글자를 잘라서 제목으로 강제 적용한다.
+ **/
+ $obj->title = cut_str($obj->content,10,'...');
+
+ /**
+ * 이미 존재하는 글일 경우 수정을 한다.
+ * 글 수정은 document controller의 updateDocument() method를 이용한다.
+ * 결과메세지를 일단 강제로 정의 해 놓는다.
+ **/
+ if($oDocument->isExists() && $oDocument->document_srl == $obj->document_srl) {
+ $output = $oDocumentController->updateDocument($oDocument, $obj);
+ $msg_code = 'success_updated';
+
+ /**
+ * 존재하지 않는다고 판단이 되면 신규글 입력을 한다.
+ * 신규글 입력은 document controller의 inesrtDocument() method를 이용한다.
+ * 결과메세지를 일단 강제로 정의 해 놓는다.
+ **/
+ } else {
+ $obj->document_srl = getNextSequence();
+ $output = $oDocumentController->insertDocument($obj);
+ $msg_code = 'success_registed';
+ }
+
+ /**
+ * updateDocument(), insertDocument()에서 오류가 발생하였으면 리턴받은 객체 자체를 바로 돌려준다.
+ * 이 object객체는 error, message등의 내부 변수를 이용하여 에러 발생 유무와 에러 메세지를 가지고 있다.
+ **/
+ if(!$output->toBool()) return $output;
+
+ /**
+ * 결과를 리턴하기 위해서 mid, document_srl값을 세팅을 한다.
+ * controller의 경우 대부분 xml로 요청을 받고 xml로 return을 하게 된다.
+ * $this->add(key, value)로 세팅된 값들은 결과 xml에서 사용이 된다.
+ * 이 값들은 javascript에서 xml handler를 통해서 사용이 가능하게 되고 보통 url조합을 할때 사용이 된다.
+ **/
+ $this->add('mid', Context::get('mid'));
+ $this->add('document_srl', $output->get('document_srl'));
+
+ /**
+ * 성공 메세지 등록
+ * setMessage($message)는 xml에 지정이 되고 이 message는 javascript에서 alert()를 시키게 된다.
+ **/
+ $this->setMessage($msg_code);
+ }
+
+ /**
+ * @brief 문서 삭제
+ **/
+ function procGuestbookDeleteDocument() {
+ // 문서 번호 확인
+ $document_srl = Context::get('document_srl');
+
+ // 문서 번호가 없다면 오류 발생
+ if(!$document_srl) return $this->doError('msg_invalid_document');
+
+ // document module model 객체 생성
+ $oDocumentController = &getController('document');
+
+ // 삭제 시도
+ $output = $oDocumentController->deleteDocument($document_srl, $this->grant->manager);
+
+ // 삭제시 실패하였을 경우 리턴받은 객체를 그대로 리턴.
+ if(!$output->toBool()) return $output;
+
+ // 성공 메세지 등록
+ $this->add('mid', Context::get('mid'));
+ $this->add('page', $output->get('page'));
+ $this->setMessage('success_deleted');
+ }
+
+ /**
+ * @brief 댓글 추가
+ **/
+ function procGuestbookInsertComment() {
+ // 권한 체크
+ if(!$this->grant->write_comment) return new Object(-1, 'msg_not_permitted');
+
+ // 댓글 입력에 필요한 데이터 추출
+ $obj = Context::gets('document_srl','comment_srl','parent_srl','content','password','nick_name','nick_name','member_srl','email_address','homepage');
+ $obj->module_srl = $this->module_srl;
+
+ // comment 모듈의 model 객체 생성
+ $oCommentModel = &getModel('comment');
+
+ // comment 모듈의 controller 객체 생성
+ $oCommentController = &getController('comment');
+
+ /**
+ * 게시판이나 블로그와 달리 방명록의 댓글은 textarea를 그대로 사용한다.
+ * 따라서 줄바꾸임나 태그제거등의 작업을 해주어야 함
+ **/
+ $obj->content = nl2br(strip_tags($obj->content));
+
+ /**
+ * 존재하는 댓글인지를 확인하여 존재 하지 않는 댓글이라면 신규로 등록하기 위해서 comment_srl의 sequence값을 받는다
+ **/
+ if(!$obj->comment_srl) {
+ $obj->comment_srl = getNextSequence();
+ } else {
+ $comment = $oCommentModel->getComment($obj->comment_srl, $this->grant->manager);
+ }
+
+ // comment_srl이 없을 경우 신규 입력
+ if($comment->comment_srl != $obj->comment_srl) {
+
+ // parent_srl이 있으면 답변으로
+ if($obj->parent_srl) {
+ $parent_comment = $oCommentModel->getComment($obj->parent_srl);
+ if(!$parent_comment->comment_srl) return new Object(-1, 'msg_invalid_request');
+
+ $output = $oCommentController->insertComment($obj);
+
+ // 없으면 신규
+ } else {
+ $output = $oCommentController->insertComment($obj);
+ }
+
+ // comment_srl이 있으면 수정으로
+ } else {
+ $obj->parent_srl = $comment->parent_srl;
+ $output = $oCommentController->updateComment($obj, $this->grant->manager);
+ $comment_srl = $obj->comment_srl;
+ }
+
+ // 오류 발생시 객체 그대로 리턴.
+ if(!$output->toBool()) return $output;
+
+ // 댓글 입력후 페이지 이동을 위한 변수 및 메세지를 설정한다.
+ $this->add('mid', Context::get('mid'));
+ $this->add('document_srl', $obj->document_srl);
+ $this->add('comment_srl', $obj->comment_srl);
+
+ $this->setMessage('success_registed');
+ }
+
+ /**
+ * @brief 댓글 삭제
+ **/
+ function procGuestbookDeleteComment() {
+ // 댓글 번호 확인
+ $comment_srl = Context::get('comment_srl');
+ if(!$comment_srl) return $this->doError('msg_invalid_request');
+
+ // comment 모듈의 controller 객체 생성
+ $oCommentController = &getController('comment');
+ $output = $oCommentController->deleteComment($comment_srl, $this->grant->manager);
+
+ // 오류 발생시 객체 그대로 리턴.
+ if(!$output->toBool()) return $output;
+
+ // 댓글 입력후 페이지 이동을 위한 변수 및 메세지를 설정한다.
+ $this->setMessage('success_deleted');
+ $this->add('mid', Context::get('mid'));
+ $this->add('page', Context::get('page'));
+ $this->add('document_srl', $output->get('document_srl'));
+ }
+
+ /**
+ * @brief 문서와 댓글의 비밀번호를 확인
+ * 비밀번호와 문서 혹은 댓글의 비밀번호를 비교하여 이상이 없다면 해당 문서 또는 댓글에 권한을 부여한다.
+ * 이 권한은 세션에 저장이 되어 차후 다시 수정등을 할 경우 비밀번호 검사를 하지 않게 된다.
+ **/
+ function procGuestbookVerificationPassword() {
+ // 비밀번호와 문서 번호를 받음
+ $password = md5(Context::get('password'));
+
+ $document_srl = Context::get('document_srl');
+ $comment_srl = Context::get('comment_srl');
+
+ // comment_srl이 있을 경우 댓글이 대상
+ if($comment_srl) {
+ // 문서번호에 해당하는 글이 있는지 확인
+ $oCommentModel = &getModel('comment');
+ $data = $oCommentModel->getComment($comment_srl);
+ if(!$data) return new Object(-1, 'msg_invalid_request');
+
+ // 문서의 비밀번호와 입력한 비밀번호의 비교
+ if($data->password != $password) return new Object(-1, 'msg_invalid_password');
+
+ $oCommentController = &getController('comment');
+ $oCommentController->addGrant($comment_srl);
+ } else {
+ // 문서번호에 해당하는 글이 있는지 확인
+ $oDocumentModel = &getModel('document');
+ $oDocument = $oDocumentModel->getDocument($document_srl);
+ if(!$oDocument->isExists()) return new Object(-1, 'msg_invalid_request');
+
+ // 문서의 비밀번호와 입력한 비밀번호의 비교
+ if($oDocument->get('password') != $password) return new Object(-1, 'msg_invalid_password');
+
+ $oDocument->setGrant();
+ }
+ }
+
+ }
+?>
diff --git a/modules/guestbook/guestbook.view.php b/modules/guestbook/guestbook.view.php
new file mode 100644
index 000000000..f89565c4e
--- /dev/null
+++ b/modules/guestbook/guestbook.view.php
@@ -0,0 +1,328 @@
+module_srl) Context::set('module_srl',$this->module_srl);
+
+ /**
+ * 현재 방명록 모듈의 정보를 module_info라는 이름으로 템플릿에서 사용할 수 있게 하기 위해 세팅한다
+ **/
+ Context::set('module_info',$this->module_info);
+
+ /**
+ * 스킨 정보에서 받는 목록수나 페이지수를 미리 선언해 놓는다
+ **/
+ $this->list_count = $this->module_info->list_count?$this->module_info->list_count:20;
+ $this->page_count = $this->module_info->page_count?$this->module_info->page_count:10;
+
+ /**
+ * 모듈정보에서 넘어오는 skin값을 이용하여 최종 출력할 템플릿의 위치를 출력한다.
+ * $this->module_path는 ./modules/guestbook/의 값을 가지고 있다
+ **/
+ $template_path = sprintf("%sskins/%s/",$this->module_path, $this->module_info->skin);
+ $this->setTemplatePath($template_path);
+
+ /**
+ * 방명록 모듈 생성 또는 정보 수정시 open_rss값의 세팅에 따라서 rss_url을 선언해 놓는다.
+ * 이 rss_url은 ./common/tpl/common_layout.html에서 application/rss+xml의 href로 지정된다
+ **/
+ if($this->module_info->open_rss != 'N') Context::set('rss_url', getUrl('','mid',$this->mid,'act','rss'));
+ }
+
+ /**
+ * @brief 목록 및 입력항목 출력
+ **/
+ function dispGuestbookContent() {
+ /**
+ * 목록 구현에 필요한 변수들을 가져온다
+ * 방명록은 기본적으로 page변수만 있으면 된다
+ **/
+ $page = Context::get('page');
+
+ $oDocumentModel = &getModel('document'); ///< getModel, getController, getView 함수를 통해서 간단히 원하는 객체를 생성할 수 있다.
+
+ /**
+ * write_form.html을 목록에서도 include를 하게 되는데 write_form.html의 경우 $oDocument라는 선택된 문서의 객체가 필요하다.
+ * 목록에서는 수정이 아닌 입력만 있어서 이 $oDocument라는 object를 생성을 해 준다
+ **/
+ $oDocument = $oDocumentModel->getDocument(0, $this->grant->manager);
+ Context::set('oDocument', $oDocument);
+
+ /**
+ * 글 작성 권한이 있다면 글쓰기 에디터를 세팅한다
+ * write_document는 ./conf/module.xml에 정의되어 있고 관리페이지에서 권한 그룹을 설정한 값이다.
+ **/
+ if($this->grant->write_document) {
+ /**
+ * 에디터에서 사용할 고유 문서 번호를 구해 온다.
+ * ZBXE에서는 모든 고유값을 getNextSequence() 로 구해 올 수 있고 글쓰기(editor) 모듈은 이 고유번호를 바탕으로 동작을 한다.
+ **/
+ $document_srl = getNextSequence();
+
+ /**
+ * editor model객체의 getEditor method를 호출하여 세팅한다.
+ * 이 때 여러가지 옵션을 지정하여 다른 에디터 코드를 받을 수 있다.
+ **/
+ $oEditorModel = &getModel('editor');
+ $option->allow_fileupload = false; ///< 파일 업로드 기능을 제한
+ $option->enable_autosave = true; ///< 자동 저장 기능을 활성화
+ $option->enable_default_component = true; ///< 기본 에디터 컴포넌트의 활성화
+ $option->enable_component = true; ///< 추가 에디터 컴포넌트의 활성화
+ $option->resizable = false; ///< 글쓰기 폼의 상하 조절 가능하도록 설정
+ $option->height = 200; ///< 에디터의 높이 지정
+ $editor = $oEditorModel->getEditor($document_srl, $option); ///< 에디터코드를 받음
+ Context::set('editor', $editor); ///< 에디터코드를 editor라는 이름으로 세팅.
+ }
+
+ /**
+ * document 모듈을 이용해서 현재 방명록의 module_srl로 목록을 구한다.
+ * 목록을 구할때 필요한 변수를 $args에 세팅후 document.model객체를 생성하고 getDocumentList() method를 호출한다.
+ **/
+ // 목록을 구하기 위한 옵션
+ $args->module_srl = $this->module_srl; ///< 현재 모듈의 module_srl
+ $args->page = $page; ///< 페이지
+ $args->list_count = $this->list_count; ///< 한페이지에 보여줄 글 수
+ $args->page_count = $this->page_count; ///< 페이지 네비게이션에 나타날 페이지의 수
+ $args->sort_index = 'list_order'; ///< 목록의 정렬 대상 (list_order, 즉 날짜의 역순을 정렬 대상으로 한다)
+ $args->order_type = 'asc'; ///< 정렬 순서 (list_order는 -1부터 -1되어서 저장되는 값이라 asc로 정렬 순서를 정하면 된다)
+
+ /**
+ * document model객체를 생성하여 목록을 구한다.
+ **/
+ $output = $oDocumentModel->getDocumentList($args);
+
+ /**
+ * 템플릿에 쓰기 위해서 document_model::getDocumentList() 의 return object에 있는 값들을 세팅
+ * ZBXE에서 목록의 경우 5가지의 값으로 결과를 받는다.
+ * total_count : 대상의 전체 글 수
+ * total_page : 대상의 전체 페이지 수 (list_count, page_count로 계산되어진 값)
+ * page : 현재 페이지
+ * data : 목록 배열
+ * page_navigation : 페이지 네비게이션을 출력하기 위한 object
+ **/
+ Context::set('total_count', $output->total_count);
+ Context::set('total_page', $output->total_page);
+ Context::set('page', $output->page);
+ Context::set('document_list', $output->data);
+ Context::set('page_navigation', $output->page_navigation);
+
+ /**
+ * 템플릿 파일을 지정한다.
+ * 이미 template path는 init()에서 정의를 하였다.
+ **/
+ $this->setTemplateFile('list');
+ }
+
+ /**
+ * @brief 글 수정 화면 출력
+ **/
+ function dispGuestbookModify() {
+ // 권한 체크
+ if(!$this->grant->write_document) return $this->dispGuestbookMessage('msg_not_permitted');
+
+ // GET parameter에서 document_srl을 가져옴
+ $document_srl = Context::get('document_srl');
+
+ // document 모듈 객체 생성
+ $oDocumentModel = &getModel('document');
+
+ $oDocument = $oDocumentModel->getDocument(0, $this->grant->manager);
+ $oDocument->setDocument($document_srl);
+
+ if(!$oDocument->isExists()) Context::set('document_srl','');
+
+ if(!$document_srl) $document_srl = getNextSequence();
+
+ // 글을 수정하려고 할 경우 권한이 없는 경우 비밀번호 입력화면으로
+ if($oDocument->isExists()&&!$oDocument->isGranted()) return $this->setTemplateFile('input_password_form');
+
+ Context::set('document_srl',$document_srl);
+ Context::set('oDocument', $oDocument);
+
+ // 에디터 모듈의 getEditor를 호출하여 세팅
+ $oEditorModel = &getModel('editor');
+ $option->allow_fileupload = $this->grant->fileupload;
+ $option->enable_autosave = true;
+ $option->enable_default_component = true;
+ $option->enable_component = true;
+ $option->resizable = true;
+ $option->height = 600;
+ $editor = $oEditorModel->getEditor($document_srl, $option);
+ Context::set('editor', $editor);
+
+ $this->setTemplateFile('write_form');
+ }
+
+
+ /**
+ * @brief 문서 삭제 화면 출력
+ **/
+ function dispGuestbookDelete() {
+ /**
+ * 권한 체크
+ * 글쓰기 권한이 없다면 아예 접근이 불가능하도록 해 버린다.
+ **/
+ if(!$this->grant->write_document) return $this->dispGuestbookMessage('msg_not_permitted');
+
+ /**
+ * 삭제할 문서번호를 가져온다
+ * 이 문서 번호는 get parmameter에 저장되어 있고 Context 클래스에서 미리 세팅을 해 놓은 상태이다.
+ **/
+ $document_srl = Context::get('document_srl');
+
+ /**
+ * 문서 번호가 없으면 잘못된 접근으로 에러 메세지를 출력한다.
+ **/
+ if(!$document_srl) return $this->dispGuestbookMessage('msg_invalid_request');
+
+ /**
+ * 문서 번호로 문서객체를 구해온다
+ **/
+ $oDocumentModel = &getModel('document');
+ $oDocument = $oDocumentModel->getDocument($document_srl);
+
+ // 대상 문서가 없으면 에러
+ if(!$oDocument->isExists()) return $this->dispGuestbookContent();
+
+ /**
+ * 권한을 체크한다.
+ * 권한 체크는 글쓴 사용자와 현재 로그인한 사용자의 정보가 같거나 최고관리자 일 경우 권한이 있다고 판단하고,
+ * 그렇지 않은 경우는 비밀번호 입력 폼을 출력한다.
+ **/
+ if(!$oDocument->isGranted()) return $this->setTemplateFile('input_password_form');
+
+ // 구해진 문서를 context setting하고 delete_form.html 파일을 템플릿 파일로 지정하여 삭제 폼을 출력한다.
+ Context::set('oDocument',$oDocument);
+
+ // delete_from.html 템플릿 파일의 지정
+ $this->setTemplateFile('delete_form');
+ }
+
+ /**
+ * @brief 댓글의 답글 화면 출력
+ **/
+ function dispGuestbookReplyComment() {
+ // 댓글 작성 권한을 체크한다.
+ if(!$this->grant->write_comment) return $this->dispGuestbookMessage('msg_not_permitted');
+
+ // 댓글의 답글을 출력하기 위해서 문서와 원 댓글의 유효성을 검사하기 위해 변수를 가져온다.
+ $document_srl = Context::get('document_srl');
+ $parent_srl = Context::get('comment_srl');
+
+ // 지정된 원 댓글이 없다면 오류
+ if(!$parent_srl) return new Object(-1, 'msg_invalid_request');
+
+ // 해당 댓글를 찾아본다
+ $oCommentModel = &getModel('comment');
+ $source_comment = $oCommentModel->getComment($parent_srl, $this->grant->manager);
+
+ // 댓글이 없다면 오류
+ if(!$source_comment) return $this->dispGuestbookMessage('msg_invalid_request');
+
+ // 필요한 정보들 세팅
+ Context::set('document_srl',$document_srl);
+ Context::set('parent_srl',$parent_srl);
+ Context::set('comment_srl',NULL);
+ Context::set('source_comment',$source_comment);
+
+ /**
+ * comment_form.html 템플릿 파일을 출력할 파일로 지정
+ **/
+ $this->setTemplateFile('comment_form');
+ }
+
+ /**
+ * @brief 댓글 수정 폼 출력
+ **/
+ function dispGuestbookModifyComment() {
+ // 댓글 작성 권한을 체크한다.
+ if(!$this->grant->write_comment) return $this->dispGuestbookMessage('msg_not_permitted');
+
+ // 댓글을 수정하기 위하여 문서와 원 댓글의 유효성을 검사하기 위해 변수를 가져온다.
+ $document_srl = Context::get('document_srl');
+ $comment_srl = Context::get('comment_srl');
+
+ // 지정된 댓글이 없다면 오류
+ if(!$comment_srl) return new Object(-1, 'msg_invalid_request');
+
+ // 해당 댓글를 찾아본다
+ $oCommentModel = &getModel('comment');
+ $comment = $oCommentModel->getComment($comment_srl, $this->grant->manager);
+
+ // 댓글이 없다면 오류
+ if(!$comment) return $this->dispGuestbookMessage('msg_invalid_request');
+
+ // 문서번호를 context setting한다
+ Context::set('document_srl',$comment->document_srl);
+
+ // 글을 수정하려고 할 경우 권한이 없는 경우 비밀번호 입력화면으로
+ if(!$comment->is_granted) return $this->setTemplateFile('input_password_form');
+
+ // 필요한 정보들 세팅
+ Context::set('comment_srl',$comment_srl);
+ Context::set('comment', $comment);
+
+ // comment_form 파일을 템플릿 출력 파일로 지정
+ $this->setTemplateFile('comment_form');
+ }
+
+ /**
+ * @brief 댓글 삭제 화면 출력
+ **/
+ function dispGuestbookDeleteComment() {
+ // 댓글 작성 권한을 체크한다.
+ if(!$this->grant->write_comment) return $this->dispGuestbookMessage('msg_not_permitted');
+
+ // 삭제할 댓글번호를 가져온다
+ $comment_srl = Context::get('comment_srl');
+
+ // 삭제하려는 댓글이 있는지 확인
+ if(!$comment_srl) return $this->dispGuestbookMessage('msg_invalid_request');
+
+ // 해당 댓글을 가져온다.
+ $oCommentModel = &getModel('comment');
+ $comment = $oCommentModel->getComment($comment_srl, $this->grant->manager);
+
+ // 삭제하려는 댓글이 없으면 에러
+ if(!$comment) return $this->dispGuestbookContent('msg_invalid_request');
+
+ // 문서 번호를 context setting한다.
+ Context::set('document_srl',$comment->document_srl);
+
+ // 권한이 없는 경우 비밀번호 입력화면으로
+ if(!$comment->is_granted) return $this->setTemplateFile('input_password_form');
+
+ Context::set('comment',$comment);
+
+ // delete_comemnt_form.html파일을 출력 파일로 지정한다.
+ $this->setTemplateFile('delete_comment_form');
+ }
+
+ /**
+ * @brief 메세지 출력
+ **/
+ function dispGuestbookMessage($msg_code) {
+ $msg = Context::getLang($msg_code);
+ if(!$msg) $msg = $msg_code;
+ Context::set('message', $msg);
+ $this->setTemplateFile('message');
+ }
+
+ }
+?>
diff --git a/modules/guestbook/lang/en.lang.php b/modules/guestbook/lang/en.lang.php
new file mode 100644
index 000000000..c4500716c
--- /dev/null
+++ b/modules/guestbook/lang/en.lang.php
@@ -0,0 +1,17 @@
+board = "board";
+
+ // words used in button
+ $lang->cmd_board_list = 'Boards list';
+ $lang->cmd_module_config = 'Common board setting';
+ $lang->cmd_view_info = 'Board info';
+ $lang->move_target_module = "Target module for changing position";
+
+ $lang->about_board = "This module is used for creating and managing boards.\nSelect the module's name from the list after creating one to configurate specifically.\nBe careful with board's module name, since it will be the url. (ex : http://domain/zb/?mid=modulename)";
+?>
diff --git a/modules/guestbook/lang/es.lang.php b/modules/guestbook/lang/es.lang.php
new file mode 100644
index 000000000..2b14c4def
--- /dev/null
+++ b/modules/guestbook/lang/es.lang.php
@@ -0,0 +1,17 @@
+board = "Boletín";
+
+ // Palabras utiliza en botónes
+ $lang->cmd_board_list = 'Lista de boletín';
+ $lang->cmd_module_config = 'configuración comun de boletínes';
+ $lang->cmd_view_info = 'ver información de boletín';
+ $lang->move_target_module = "Módulo para mover";
+
+ $lang->about_board = "Es el módulo para crear y manejar boletínes.\nDespues de crear un boletín, haga clic en nombre de módulos para configuración mas detallado.\nEl nombre de módulo es nombre de URL, sea cuidoso deseando el nombre. (ej : http://dominio/zb/?mid=nombre de módulo)";
+?>
diff --git a/modules/guestbook/lang/jp.lang.php b/modules/guestbook/lang/jp.lang.php
new file mode 100644
index 000000000..341bc7952
--- /dev/null
+++ b/modules/guestbook/lang/jp.lang.php
@@ -0,0 +1,17 @@
+board = "掲示板";
+
+ // ボタンに使用する用語
+ $lang->cmd_board_list = '掲示板リスト';
+ $lang->cmd_module_config = '掲示板共通設定';
+ $lang->cmd_view_info = '掲示板情報';
+ $lang->move_target_module = "移動対象モジュル";
+
+ $lang->about_board = "掲示板を生成、管理できる掲示板モジュルです。\n生成後、リストからモジュル名を選択すると詳細な設定ができます。\n掲示板のモジュル名はURLになりますので注意してください。 (ex : http://ドメイン/zb/?mid=モジュル名)";
+?>
diff --git a/modules/guestbook/lang/ko.lang.php b/modules/guestbook/lang/ko.lang.php
new file mode 100644
index 000000000..e8bb2da38
--- /dev/null
+++ b/modules/guestbook/lang/ko.lang.php
@@ -0,0 +1,16 @@
+guestbook = "방명록";
+
+ // 버튼에 사용되는 언어
+ $lang->cmd_guestbook_list = '방명록 목록';
+ $lang->cmd_module_config = '방명록 공통 설정';
+ $lang->cmd_view_info = '방명록 정보';
+
+ $lang->about_guestbook = "방명록을 생성하고 관리할 수 있는 방명록 모듈입니다.\n생성하신 후 목록에서 모듈이름을 선택하시면 자세한 설정이 가능합니다.\n방명록의 모듈이름은 접속 url이 되므로 신중하게 입력해주세요. (ex : http://도메인/zb/?mid=모듈이름)";
+?>
diff --git a/modules/guestbook/lang/zh-CN.lang.php b/modules/guestbook/lang/zh-CN.lang.php
new file mode 100644
index 000000000..084ea7d4f
--- /dev/null
+++ b/modules/guestbook/lang/zh-CN.lang.php
@@ -0,0 +1,17 @@
+board = "board";
+
+ // 在按钮使用的语言
+ $lang->cmd_board_list = 'board 目录';
+ $lang->cmd_module_config = 'board 共同设定';
+ $lang->cmd_view_info = 'Board 信息';
+ $lang->move_target_module = "移动目标模块";
+
+ $lang->about_board = "可以生成Board和管理的Board模块\n生成后在目录里选择模块名称可以详细设定。";
+?>
diff --git a/modules/guestbook/queries/getGuestbookList.xml b/modules/guestbook/queries/getGuestbookList.xml
new file mode 100644
index 000000000..090dec365
--- /dev/null
+++ b/modules/guestbook/queries/getGuestbookList.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/guestbook/skins/default/comment.html b/modules/guestbook/skins/default/comment.html
new file mode 100644
index 000000000..6e04ffb12
--- /dev/null
+++ b/modules/guestbook/skins/default/comment.html
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/guestbook/skins/default/comment_form.html b/modules/guestbook/skins/default/comment_form.html
new file mode 100644
index 000000000..9a68d8e01
--- /dev/null
+++ b/modules/guestbook/skins/default/comment_form.html
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | {$lang->date} |
+ {$source_comment->regdate} |
+
+
+ | {$lang->writer} |
+ |
+
+
+ | {$lang->content} |
+ {$source_comment->content} |
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/guestbook/skins/default/css/guestbook.css b/modules/guestbook/skins/default/css/guestbook.css
new file mode 100644
index 000000000..41c4e7a47
--- /dev/null
+++ b/modules/guestbook/skins/default/css/guestbook.css
@@ -0,0 +1,28 @@
+@charset "utf-8";
+
+a { color:#555555; text-decoration:none; }
+
+.titleBox { border:4px solid #EEEEEE; padding:.5em; margin-bottom:1em; }
+.titleBox .title { font-weight:bold; }
+.titleBox .memo { color:#888888; }
+
+.info .status { float:left; overflow:hidden; }
+.info .link { float:right; overflow:hidden; }
+
+.write_form { padding:10px; margin:0px; border:3px solid #CCCCCC; margin-top:.5em; }
+.write_form table { border:0; padding:0; }
+.write_form td.option { text-align:right; padding:.3em; }
+
+.document { padding:0; margin-top:1em; border:3px solid #EEEEEE; }
+.document .info { background-color:#EFEFEF; padding:.5em; }
+.document .subinfo { text-align:right; color:#CCCCCC;}
+.document .content { padding:1em; }
+.document .comment { padding:1em; }
+
+.comment_box { border:0; padding:.5em; border-top:1px solid #EEEEEE; margin:1em 0 1em 0;}
+.comment_box .comment_content { }
+.comment_box .comment_content .content { margin:1em 0 2em 0;}
+.comment_box .comment_content .comment_sub_info { float:left; }
+.comment_box .comment_content .button_area { float:right; }
+
+textarea.comment_form { border:1px solid #EEEEEE; width:90%;}
diff --git a/modules/guestbook/skins/default/delete_comment_form.html b/modules/guestbook/skins/default/delete_comment_form.html
new file mode 100644
index 000000000..a3ed7cf27
--- /dev/null
+++ b/modules/guestbook/skins/default/delete_comment_form.html
@@ -0,0 +1,23 @@
+
+
+
+
+
+
diff --git a/modules/guestbook/skins/default/delete_form.html b/modules/guestbook/skins/default/delete_form.html
new file mode 100644
index 000000000..e0890106c
--- /dev/null
+++ b/modules/guestbook/skins/default/delete_form.html
@@ -0,0 +1,27 @@
+
+
+
+
+
+
diff --git a/modules/guestbook/skins/default/filter/delete_comment.xml b/modules/guestbook/skins/default/filter/delete_comment.xml
new file mode 100644
index 000000000..3780d6a18
--- /dev/null
+++ b/modules/guestbook/skins/default/filter/delete_comment.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/guestbook/skins/default/filter/delete_document.xml b/modules/guestbook/skins/default/filter/delete_document.xml
new file mode 100644
index 000000000..8750f03ad
--- /dev/null
+++ b/modules/guestbook/skins/default/filter/delete_document.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/guestbook/skins/default/filter/input_password.xml b/modules/guestbook/skins/default/filter/input_password.xml
new file mode 100644
index 000000000..2e26fbde8
--- /dev/null
+++ b/modules/guestbook/skins/default/filter/input_password.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/guestbook/skins/default/filter/insert.xml b/modules/guestbook/skins/default/filter/insert.xml
new file mode 100644
index 000000000..debca08a7
--- /dev/null
+++ b/modules/guestbook/skins/default/filter/insert.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/guestbook/skins/default/filter/insert_comment.xml b/modules/guestbook/skins/default/filter/insert_comment.xml
new file mode 100644
index 000000000..e2e46f855
--- /dev/null
+++ b/modules/guestbook/skins/default/filter/insert_comment.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/guestbook/skins/default/footer.html b/modules/guestbook/skins/default/footer.html
new file mode 100644
index 000000000..9d5404ca8
--- /dev/null
+++ b/modules/guestbook/skins/default/footer.html
@@ -0,0 +1 @@
+{$module_info->footer_text}
diff --git a/modules/guestbook/skins/default/header.html b/modules/guestbook/skins/default/header.html
new file mode 100644
index 000000000..a49ef2bc9
--- /dev/null
+++ b/modules/guestbook/skins/default/header.html
@@ -0,0 +1,53 @@
+
+
+{$module_info->header_text}
+
+
+
+
+
+
{$module_info->title}
+
+
+
+
{nl2br($module_info->memo)}
+
+
+
+
+
+
+
+
+ {$lang->document_count} : {number_format($total_count)},
+ {$lang->page_count} : {number_format($page)} / {number_format($total_page)}
+
+
+
+
+
+
+
+
diff --git a/modules/guestbook/skins/default/input_password_form.html b/modules/guestbook/skins/default/input_password_form.html
new file mode 100644
index 000000000..0a26427de
--- /dev/null
+++ b/modules/guestbook/skins/default/input_password_form.html
@@ -0,0 +1,27 @@
+
+
+
+
+
+
diff --git a/modules/guestbook/skins/default/js/guestbook.js b/modules/guestbook/skins/default/js/guestbook.js
new file mode 100644
index 000000000..4695df7e8
--- /dev/null
+++ b/modules/guestbook/skins/default/js/guestbook.js
@@ -0,0 +1,74 @@
+/**
+ * @file modules/guestbook/js/guestbook.js
+ * @author zero (zero@nzeo.com)
+ * @brief guestbook 모듈의 javascript
+ **/
+
+/* 글쓰기 작성후 */
+function completeDocumentInserted(ret_obj) {
+ var error = ret_obj['error'];
+ var message = ret_obj['message'];
+ var mid = ret_obj['mid'];
+ var document_srl = ret_obj['document_srl'];
+
+ alert(message);
+
+ var url = current_url.setQuery('mid',mid).setQuery('document_srl',document_srl).setQuery('act','');
+ location.href = url;
+}
+
+/* 글 삭제 */
+function completeDeleteDocument(ret_obj) {
+ var error = ret_obj['error'];
+ var message = ret_obj['message'];
+ var mid = ret_obj['mid'];
+ var page = ret_obj['page'];
+
+ var url = "./?mid="+mid;
+ if(page) url += "&page="+page;
+
+ alert(message);
+
+ location.href = url;
+}
+
+// 현재 페이지 reload
+function completeReload(ret_obj) {
+ var error = ret_obj['error'];
+ var message = ret_obj['message'];
+
+ location.href = location.href;
+}
+
+/* 댓글 글쓰기 작성후 */
+function completeInsertComment(ret_obj) {
+ var error = ret_obj['error'];
+ var message = ret_obj['message'];
+ var mid = ret_obj['mid'];
+ var document_srl = ret_obj['document_srl'];
+ var comment_srl = ret_obj['comment_srl'];
+
+ var url = "./?mid="+mid+"&document_srl="+document_srl;
+ //if(comment_srl) url += "#comment_"+comment_srl;
+
+ alert(message);
+
+ location.href = url;
+}
+
+/* 댓글 삭제 */
+function completeDeleteComment(ret_obj) {
+ var error = ret_obj['error'];
+ var message = ret_obj['message'];
+ var mid = ret_obj['mid'];
+ var document_srl = ret_obj['document_srl'];
+ var page = ret_obj['page'];
+
+ var url = "./?mid="+mid+'&document_srl='+document_srl;
+ if(page) url += "&page="+page;
+
+ alert(message);
+
+ location.href = url;
+}
+
diff --git a/modules/guestbook/skins/default/list.html b/modules/guestbook/skins/default/list.html
new file mode 100644
index 000000000..627a1910a
--- /dev/null
+++ b/modules/guestbook/skins/default/list.html
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{$document->getNickName()}
+
+
+
+
+
+
+
+
+
+
+ {$document->getContent()}
+
+
+
+ {$document->getRegdate('Y-m-d H:i:s')} |
+ {$document->get('ipaddress')}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/guestbook/skins/default/message.html b/modules/guestbook/skins/default/message.html
new file mode 100644
index 000000000..00bf01814
--- /dev/null
+++ b/modules/guestbook/skins/default/message.html
@@ -0,0 +1,11 @@
+
+
+
+ {$message}
+
+
+
+ {$lang->cmd_login}
+
+
+
diff --git a/modules/guestbook/skins/default/skin.xml b/modules/guestbook/skins/default/skin.xml
new file mode 100644
index 000000000..49bc8ade7
--- /dev/null
+++ b/modules/guestbook/skins/default/skin.xml
@@ -0,0 +1,23 @@
+
+
+ 방명록 기본 스킨
+
+ 제로
+ board모듈의 default스킨
+
+
+
+ 기본
+
+
+
+
+ 제목
+ 방명록의 제목을 적어주세요.
+
+
+ 방명록 설명
+ 내용의 기본값
+
+
+
diff --git a/modules/guestbook/skins/default/write_form.html b/modules/guestbook/skins/default/write_form.html
new file mode 100644
index 000000000..077b81209
--- /dev/null
+++ b/modules/guestbook/skins/default/write_form.html
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/guestbook/tpl/filter/delete_guestbook.xml b/modules/guestbook/tpl/filter/delete_guestbook.xml
new file mode 100644
index 000000000..21817e5d3
--- /dev/null
+++ b/modules/guestbook/tpl/filter/delete_guestbook.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
diff --git a/modules/guestbook/tpl/filter/insert_grant.xml b/modules/guestbook/tpl/filter/insert_grant.xml
new file mode 100644
index 000000000..c8e4c8600
--- /dev/null
+++ b/modules/guestbook/tpl/filter/insert_grant.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
diff --git a/modules/guestbook/tpl/filter/insert_guestbook.xml b/modules/guestbook/tpl/filter/insert_guestbook.xml
new file mode 100644
index 000000000..4b6450fa3
--- /dev/null
+++ b/modules/guestbook/tpl/filter/insert_guestbook.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/guestbook/tpl/grant_list.html b/modules/guestbook/tpl/grant_list.html
new file mode 100644
index 000000000..f96aa9cfa
--- /dev/null
+++ b/modules/guestbook/tpl/grant_list.html
@@ -0,0 +1,42 @@
+
+
+
+
{nl2br($lang->about_grant)}
+
+
diff --git a/modules/guestbook/tpl/guestbook_delete.html b/modules/guestbook/tpl/guestbook_delete.html
new file mode 100644
index 000000000..7c0330778
--- /dev/null
+++ b/modules/guestbook/tpl/guestbook_delete.html
@@ -0,0 +1,34 @@
+
+
+
+
+
diff --git a/modules/guestbook/tpl/guestbook_info.html b/modules/guestbook/tpl/guestbook_info.html
new file mode 100644
index 000000000..998039fd7
--- /dev/null
+++ b/modules/guestbook/tpl/guestbook_info.html
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+ | {$lang->module_category} |
+
+
+ {$lang->not_exists}
+
+ {$module_category[$module_info->module_category_srl]->title}
+
+ |
+
+
+ | {$lang->layout} |
+
+
+ {$module_info->layout_title} ({$module_info->layout})
+
+ {$lang->not_exists}
+
+ |
+
+
+ | {$lang->skin} |
+ {$module_info->skin_title} ({$module_info->skin}) |
+
+
+ | {$lang->browser_title} |
+ {htmlspecialchars($module_info->browser_title)} |
+
+
+ | {$lang->use_category} |
+ {$lang->use}{$lang->notuse} |
+
+
+ | {$lang->open_rss} |
+ {$lang->open_rss_types[$module_info->open_rss]} |
+
+
+ | {$lang->list_count} |
+ {$module_info->list_count?$module_info->list_count:20} |
+
+
+ | {$lang->page_count} |
+ {$module_info->page_count?$module_info->page_count:10} |
+
+
+ | {$lang->description} |
+ {nl2br(htmlspecialchars($module_info->description))} |
+
+
+ | {$lang->header_text} |
+ {htmlspecialchars($module_info->header_text)} |
+
+
+ | {$lang->footer_text} |
+ {htmlspecialchars($module_info->footer_text)} |
+
+
+ | {$lang->admin_id} |
+ {implode(",",$module_info->admin_id)} |
+
+
+
+
diff --git a/modules/guestbook/tpl/guestbook_insert.html b/modules/guestbook/tpl/guestbook_insert.html
new file mode 100644
index 000000000..b953183c1
--- /dev/null
+++ b/modules/guestbook/tpl/guestbook_insert.html
@@ -0,0 +1,137 @@
+
+
+
+
+
+
diff --git a/modules/guestbook/tpl/header.html b/modules/guestbook/tpl/header.html
new file mode 100644
index 000000000..fafc4f08c
--- /dev/null
+++ b/modules/guestbook/tpl/header.html
@@ -0,0 +1,23 @@
+
+
+
{$lang->guestbook} {$lang->cmd_management}
+
+
+
+
+
diff --git a/modules/guestbook/tpl/index.html b/modules/guestbook/tpl/index.html
new file mode 100644
index 000000000..709580a21
--- /dev/null
+++ b/modules/guestbook/tpl/index.html
@@ -0,0 +1,80 @@
+
+
+
{nl2br($lang->about_guestbook)}
+
+
+
+ Total {number_format($total_count)}, Page {number_format($page)}/{number_format($total_page)}
+
+
+
+
+
+
+
+
+
+
+

+
+
+
{$page_no}
+
+
{$page_no}
+
+
+

+
diff --git a/modules/guestbook/tpl/js/guestbook_admin.js b/modules/guestbook/tpl/js/guestbook_admin.js
new file mode 100644
index 000000000..f641d1724
--- /dev/null
+++ b/modules/guestbook/tpl/js/guestbook_admin.js
@@ -0,0 +1,79 @@
+/**
+ * @file modules/guestbook/js/guestbook_admin.js
+ * @author zero (zero@nzeo.com)
+ * @brief guestbook 모듈의 관리자용 javascript
+ **/
+
+/* 모듈 생성 후 */
+function completeInsertGuestbook(ret_obj) {
+ var error = ret_obj['error'];
+ var message = ret_obj['message'];
+
+ var page = ret_obj['page'];
+ var module_srl = ret_obj['module_srl'];
+
+ alert(message);
+
+ var url = current_url.setQuery('act','dispGuestbookAdminGuestbookInfo');
+ if(module_srl) url = url.setQuery('module_srl',module_srl);
+ if(page) url.setQuery('page',page);
+ location.href = url;
+}
+
+/* 모듈 삭제 후 */
+function completeDeleteGuestbook(ret_obj) {
+ var error = ret_obj['error'];
+ var message = ret_obj['message'];
+ var page = ret_obj['page'];
+ alert(message);
+
+ var url = current_url.setQuery('act','dispGuestbookAdminContent').setQuery('module_srl','');
+ if(page) url = url.setQuery('page',page);
+ location.href = url;
+}
+
+/* 권한 관련 */
+function doSelectAll(obj, key) {
+ var fo_obj = obj.parentNode;
+ while(fo_obj.nodeName != 'FORM') {
+ fo_obj = fo_obj.parentNode;
+ }
+
+ for(var i=0;i
+
+
+
+
diff --git a/modules/guestbook/tpl/top_refresh.html b/modules/guestbook/tpl/top_refresh.html
new file mode 100644
index 000000000..2f4983d1d
--- /dev/null
+++ b/modules/guestbook/tpl/top_refresh.html
@@ -0,0 +1,3 @@
+