git-svn-id: http://xe-core.googlecode.com/svn/sandbox@2327 201d5d3c-b55e-5fd7-737f-ddc643e51545
This commit is contained in:
zero 2007-08-12 03:59:52 +00:00
commit 8326004cb2
2773 changed files with 91485 additions and 0 deletions

51
.htaccess Normal file
View file

@ -0,0 +1,51 @@
RewriteEngine On
# page
RewriteRule ^([a-zA-Z0-9_]+)/([[:digit:]]+)page$ ./index.php?mid=$1&page=$2 [L]
# total rss
RewriteRule ^rss/([[:digit:]]+){0,14}/([[:digit:]]+){0,14}/([[:digit:]]+)$ ./index.php?module=rss&act=rss&start_date=$1&end_date=$2&page=$3 [L]
RewriteRule ^rss/([[:digit:]]+)$ ./index.php?module=rss&act=rss&page=$1 [L]
RewriteRule ^rss$ ./index.php?module=rss&act=rss [L]
# administrator page
RewriteRule ^admin$ ./index.php?module=admin [L]
# mid + act
RewriteRule ^([a-zA-Z0-9_]+)/api$ ./index.php?mid=$1&act=api [L]
# document permanent link
RewriteRule ^([[:digit:]]+)$ ./index.php?document_srl=$1 [L]
# document + act link
RewriteRule ^([[:digit:]]+)/([a-zA-Z0-9_]+)$ ./index.php?document_srl=$1&act=$2 [L]
# mid + document link
RewriteRule ^([a-zA-Z0-9_]+)/([[:digit:]]+)$ ./index.php?mid=$1&document_srl=$2 [L]
# mid + act link
RewriteRule ^([a-zA-Z0-9_]+)/([a-zA-Z0-9_]+)$ ./index.php?mid=$1&act=$2 [L]
# mid + page link
RewriteRule ^([a-zA-Z0-9_]+)/page/([[:digit:]]+)$ ./index.php?mid=$1&page=$2 [L]
# mid + category link
RewriteRule ^([a-zA-Z0-9_]+)/category/([[:digit:]]+)$ ./index.php?mid=$1&category=$2 [L]
# mid + category + page
RewriteRule ^([a-zA-Z0-9_]+)/category/([[:digit:]]+)/page/([[:digit:]]+)$ ./index.php?mid=$1&category=$2&page=$3 [L]
# mid + search target regdate (year+month)
RewriteRule ^([a-zA-Z0-9_]+)/([[:digit:]]+){0,4}/([[:digit:]]+){0,2}$ ./index.php?mid=$1&search_target=regdate&search_keyword=$2$3 [L]
# mid + search target regdate (year+month+day)
RewriteRule ^([a-zA-Z0-9_]+)/([[:digit:]]+){0,4}/([[:digit:]]+){0,2}/([[:digit:]]+){0,2}$ ./index.php?mid=$1&search_target=regdate&search_keyword=$2$3$4 [L]
# mid + search target tag
RewriteRule ^([a-zA-Z0-9_]+)/tag/(.*)$ ./index.php?mid=$1&search_target=tag&search_keyword=$2 [L]
# mid + search target writer
RewriteRule ^([a-zA-Z0-9_]+)/writer/(.*)$ ./index.php?mid=$1&search_target=nick_name&search_keyword=$2 [L]
# module link
RewriteRule ^([a-zA-Z0-9_]+)(/){0,1}$ ./index.php?mid=$1 [L]

View file

@ -0,0 +1,394 @@
<?php
if(!defined("__ZBXE__")) exit();
/**
* @file blogapicounter.addon.php
* @author zero (zero@nzeo.com)
* @brief blogAPI 애드온
*
* ms live writer, 파이어폭스의 performancing, zoundry 등의 외부 툴을 이용하여 글을 입력할 있게 합니다.
* 모듈 실행 이전(before_module_proc) 호출이 되어야 하며 정상동작후에는 강제 종료를 한다.
**/
// called_position가 after_module_proc일때 rsd 태그 삽입
if($called_position == 'after_module_proc') {
// 현재 모듈의 rsd주소를 만듬
$rsd_url = sprintf('%s%s/api', Context::getRequestUri(), $this->mid);
// 헤더에 rsd태그 삽입
Context::addHtmlHeader(" ".'<link rel="EditURI" type="application/rsd+xml" title="RSD" href="'.$rsd_url.'" />');
}
// act가 api가 아니면 그냥 리턴~
if($_REQUEST['act']!='api') return;
/**
* blogapi의 경우 GET argument와 XML Content가 같이 오기에 제로보드XE의 경우 XML Content가 오면 이것만 처리하기에
* GET argument중에 mid값을 강제 설정해야 모듈을 정상적으로 찾는다
**/
if($called_position == 'before_module_init') {
$mid = $_REQUEST['mid'];
Context::set('mid', $mid, true);
$this->mid = $mid;
}
// 관련 func 파일 읽음
require_once('./addons/blogapi/blogapi.func.php');
// xmlprc 파싱
// 요청된 xmlrpc를 파싱
$oXmlParser = new XmlParser();
$xmlDoc = $oXmlParser->parse();
$method_name = $xmlDoc->methodcall->methodname->body;
$params = $xmlDoc->methodcall->params->param;
if($params && !is_array($params)) $params = array($params);
// blogger.deletePost일 경우 첫번째 인자 값 삭제
if($method_name == 'blogger.deletePost') array_shift($params);
// user_id, password를 구해서 로그인 시도
$user_id = trim($params[1]->value->string->body);
$password = trim($params[2]->value->string->body);
// 모듈 실행전이라면 인증을 처리한다.
if($called_position == 'before_module_init') {
// member controller을 이용해서 로그인 시도
if($user_id && $password) {
$oMemberController = &getController('member');
$output = $oMemberController->doLogin($user_id, $password);
// 로그인 실패시 에러 메시지 출력
if(!$output->toBool()) {
$content = getXmlRpcFailure(1, $output->getMessage());
printContent($content);
}
} else {
$content = getXmlRpcFailure(1, 'not logged');
printContent($content);
}
}
// 모듈에서 무언가 작업을 하기 전에 blogapi tool의 요청에 대한 처리를 하고 강제 종료한다.
if($called_position == 'before_module_proc') {
// 글쓰기 권한 체크 (권한명의 경우 약속이 필요할듯..)
if(!$this->grant->write_document) {
printContent( getXmlRpcFailure(1, 'no permission') );
}
// 카테고리의 정보를 구해옴
$oDocumentModel = &getModel('document');
$category_list = $oDocumentModel->getCategoryList($this->module_srl);
// 임시 파일 저장 장소 지정
$tmp_uploaded_path = sprintf('./files/cache/blogapi/%s/%s/', $this->mid, $user_id);
$uploaded_target_path = sprintf('/files/cache/blogapi/%s/%s/', $this->mid, $user_id);
switch($method_name) {
// 블로그 정보
case 'blogger.getUsersBlogs' :
$obj->url = Context::getRequestUri().$this->mid;
$obj->blogid = $this->mid;
$obj->blogName = $this->module_info->browser_title;
$blog_list = array($obj);
$content = getXmlRpcResponse($blog_list);
printContent($content);
break;
// 카테고리 목록 return
case 'metaWeblog.getCategories' :
$category_obj_list = array();
if($category_list) {
foreach($category_list as $category_srl => $category_info) {
unset($obj);
$obj->description = $category_info->title;
//$obj->htmlUrl = Context::getRequestUri().$this->mid.'/1';
//$obj->rssUrl= Context::getRequestUri().'rss/'.$this->mid.'/1';
$obj->title = $category_info->title;
$obj->categoryid = $category_srl;
$category_obj_list[] = $obj;
}
}
$content = getXmlRpcResponse($category_obj_list);
printContent($content);
break;
// 파일 업로드
case 'metaWeblog.newMediaObject' :
// 파일 업로드 권한 체크
if(!$this->grant->fileupload) {
printContent( getXmlRpcFailure(1, 'no permission') );
}
$fileinfo = $params[3]->value->struct->member;
foreach($fileinfo as $key => $val) {
$nodename = $val->name->body;
if($nodename == 'bits') $filedata = base64_decode($val->value->base64->body);
elseif($nodename == 'name') $filename = $val->value->string->body;
}
$tmp_arr = explode('/',$filename);
$filename = array_pop($tmp_arr);
if(!is_dir($tmp_uploaded_path)) FileHandler::makeDir($tmp_uploaded_path);
$target_filename = sprintf('%s%s', $tmp_uploaded_path, $filename);
FileHandler::writeFile($target_filename, $filedata);
$obj->url = Context::getRequestUri().$target_filename;
$content = getXmlRpcResponse($obj);
printContent($content);
break;
// 글작성
case 'metaWeblog.newPost' :
unset($obj);
$info = $params[3];
// 글, 제목, 카테고리 정보 구함
for($i=0;$i<count($info->value->struct->member);$i++) {
$val = $info->value->struct->member[$i];
switch($val->name->body) {
case 'title' :
$obj->title = $val->value->string->body;
break;
case 'description' :
$obj->content = $val->value->string->body;
break;
case 'categories' :
$categories = $val->value->array->data->value;
if(!is_array($categories)) $categories = array($categories);
$category = $categories[0]->string->body;
if($category && $category_list) {
foreach($category_list as $category_srl => $category_info) {
if($category_info->title == $category) $obj->category_srl = $category_srl;
}
}
break;
case 'tagwords' :
$tags = $val->value->array->data->value;
if(!is_array($tags)) $tags = array($tags);
for($j=0;$j<count($tags);$j++) {
$tag_list[] = $tags[$j]->string->body;
}
if(count($tag_list)) $obj->tags = implode(',',$tag_list);
break;
}
}
// 문서 번호 설정
$document_srl = getNextSequence();
$obj->document_srl = $document_srl;
$obj->module_srl = $this->module_srl;
// 첨부파일 정리
if(is_dir($tmp_uploaded_path)) {
$file_list = FileHandler::readDir($tmp_uploaded_path);
$file_count = count($file_list);
if($file_count) {
$oFileController = &getController('file');
for($i=0;$i<$file_count;$i++) {
$file_info['tmp_name'] = sprintf('%s%s', $tmp_uploaded_path, $file_list[$i]);
$file_info['name'] = $file_list[$i];
$oFileController->insertFile($file_info, $this->module_srl, $document_srl, 0, true);
}
$obj->uploaded_count = $file_count;
}
}
$obj->content = str_replace($uploaded_target_path,sprintf('/files/attach/images/%s/%s/%s', $this->module_srl, $document_srl, $filename), $obj->content);
$oDocumentController = &getController('document');
$obj->allow_comment = 'Y';
$obj->allow_trackback = 'Y';
$output = $oDocumentController->insertDocument($obj);
if(!$output->toBool()) {
$content = getXmlRpcFailure(1, $output->getMessage());
} else {
//$content = getXmlRpcResponse(Context::getRequestUri().$this->mid.'/'.$document_srl);
$content = getXmlRpcResponse(''.$document_srl);
}
FileHandler::removeDir($tmp_uploaded_path);
printContent($content);
break;
// 글 수정
case 'metaWeblog.editPost' :
$tmp_val = $params[0]->value->string->body;
$tmp_arr = explode('/', $tmp_val);
$document_srl = array_pop($tmp_arr);
$oDocumentModel = &getModel('document');
$oDocument = $oDocumentModel->getDocument($document_srl);
// 글 수정 권한 체크
if(!$oDocument->isGranted()) {
$content = getXmlRpcFailure(1, 'no permission');
break;
}
$obj = $oDocument->getObjectVars();
$info = $params[3];
// 글, 제목, 카테고리 정보 구함
for($i=0;$i<count($info->value->struct->member);$i++) {
$val = $info->value->struct->member[$i];
switch($val->name->body) {
case 'title' :
$obj->title = $val->value->string->body;
break;
case 'description' :
$obj->content = $val->value->string->body;
break;
case 'categories' :
$categories = $val->value->array->data->value;
if(!is_array($categories)) $categories = array($categories);
$category = $categories[0]->string->body;
if($category && $category_list) {
foreach($category_list as $category_srl => $category_info) {
if($category_info->title == $category) $obj->category_srl = $category_srl;
}
}
break;
case 'tagwords' :
$tags = $val->value->array->data->value;
if(!is_array($tags)) $tags = array($tags);
for($j=0;$j<count($tags);$j++) {
$tag_list[] = $tags[$j]->string->body;
}
if(count($tag_list)) $obj->tags = implode(',',$tag_list);
break;
}
}
// 문서 번호 설정
$obj->document_srl = $document_srl;
$obj->module_srl = $this->module_srl;
// 첨부파일 정리
if(is_dir($tmp_uploaded_path)) {
$file_list = FileHandler::readDir($tmp_uploaded_path);
$file_count = count($file_list);
if($file_count) {
$oFileController = &getController('file');
for($i=0;$i<$file_count;$i++) {
$file_info['tmp_name'] = sprintf('%s%s', $tmp_uploaded_path, $file_list[$i]);
$file_info['name'] = $file_list[$i];
$moved_filename = sprintf('./files/attach/images/%s/%s/%s', $this->module_srl, $document_srl, $file_info['name']);
if(file_exists($moved_filename)) continue;
$oFileController->insertFile($file_info, $this->module_srl, $document_srl, 0, true);
}
$obj->uploaded_count += $file_count;
}
}
$obj->content = str_replace($uploaded_target_path,sprintf('/files/attach/images/%s/%s/%s', $this->module_srl, $document_srl, $filename), $obj->content);
$oDocumentController = &getController('document');
$output = $oDocumentController->updateDocument($oDocument,$obj);
if(!$output->toBool()) {
$content = getXmlRpcFailure(1, $output->getMessage());
} else {
$content = getXmlRpcResponse(Context::getRequestUri().$this->mid.'/'.$document_srl);
FileHandler::removeDir($tmp_uploaded_path);
}
printContent($content);
break;
// 글삭제
case 'blogger.deletePost' :
$tmp_val = $params[0]->value->string->body;
$tmp_arr = explode('/', $tmp_val);
$document_srl = array_pop($tmp_arr);
// 글 받아오기
$oDocumentModel = &getModel('document');
$oDocument = $oDocumentModel->getDocument($document_srl);
// 글 삭제 권한 체크
if(!$oDocument->isGranted()) {
$content = getXmlRpcFailure(1, 'no permission');
break;
}
$oDocumentController = &getController('document');
$output = $oDocumentController->deleteDocument($document_srl);
if(!$output->toBool()) $content = getXmlRpcFailure(1, $output->getMessage());
else $content = getXmlRpcResponse(true);
printContent($content);
break;
// 최신글 받기
case 'metaWeblog.getRecentPosts' :
// 목록을 구하기 위한 옵션
$args->module_srl = $this->module_srl; ///< 현재 모듈의 module_srl
$args->page = 1;
$args->list_count = 20;
$args->sort_index = 'list_order'; ///< 소팅 값
$output = $oDocumentModel->getDocumentList($args);
if(!$output->toBool() || !$output->data) {
$content = getXmlRpcFailure(1, 'post not founded');
printContent($content);
} else {
$oContext = &Context::getInstance();
$posts = array();
foreach($output->data as $key => $oDocument) {
$post = null;
$post->link = $post->permaLink = getUrl('','mid',$this->mid,'document_srl',$oDocument->document_srl);
$post->userid = $oDocument->get('user_id');
$post->mt_allow_pings = 0;
$post->mt_allow_comments = $oDocument->allowComment()=='Y'?1:0;
$post->description = htmlspecialchars($oContext->transContent($oDocument->get('content')));
$post->postid = $oDocument->document_srl;
$post->title = htmlspecialchars($oDocument->get('title'));
$year = substr($oDocument->get('regdate'),0,4);
$month = substr($oDocument->get('regdate'),4,2);
$day = substr($oDocument->get('regdate'),6,2);
$hour = substr($oDocument->get('regdate'),8,2);
$min = substr($oDocument->get('regdate'),10,2);
$sec = substr($oDocument->get('regdate'),12,2);
$time = mktime($hour,$min,$sec,$month,$day,$year);
$post->dateCreated = gmdate("D, d M Y H:i:s", $time);
$posts[] = $post;
}
$content = getXmlRpcResponse($posts);
printContent($content);
}
break;
// 아무런 요청이 없을 경우 RSD 출력
default :
$homepagelink = getUrl('','mid',$this->mid);
$api_url = sprintf('%s%s/api', Context::getRequestUri(), $this->mid);
$content = <<<RSDContent
<?xml version="1.0" ?>
<rsd version="1.0" xmlns="http://archipelago.phrasewise.com/rsd" >
<service>
<engineName>zeroboardXE</engineName>
<engineLink>http://www.zeroboard.com/ </engineLink>
<homePageLink>{$homepagelink}</homePageLink>
<apis>
<api name="MetaWeblog" preferred="true" apiLink="{$api_url}" blogID="" />
</apis>
</service>
</rsd>
RSDContent;
printContent($content);
break;
}
}
?>

View file

@ -0,0 +1,67 @@
<?php
if(!defined("__ZBXE__")) exit();
/**
* @file ./addons/blogapi/blogapi.func.php
* @author zero (zero@nzeo.com)
* @brief blogapi구현을 위한 함수 모음집
**/
// 오류 표시
function getXmlRpcFailure($error, $message) {
return
sprintf(
"<methodResponse>\n<fault><value><struct>\n<member>\n<name>faultCode</name>\n<value><int>%d</int></value>\n</member>\n<member>\n<name>faultString</name>\n<value><string>%s</string></value>\n</member>\n</struct></value></fault>\n</methodResponse>\n",
$error,
htmlspecialchars($message)
);
}
// 결과 표시
function getXmlRpcResponse($params) {
$buff = '<?xml version="1.0" encoding="utf-8"?>'."\n<methodResponse><params>";
$buff .= _getEncodedVal($params);
$buff .= "</params>\n</methodResponse>\n";
return $buff;
}
// 인코딩 처리
function _getEncodedVal($val, $is_sub_set = false) {
if(is_int($val)) $buff = sprintf("<value><i4>%d</i4></value>", $val);
elseif(is_double($val)) $buff = sprintf("<value><double>%f</double></value>", $val);
elseif(is_bool($val)) $buff = sprintf("<value><boolean>%d</boolean></value>", $val?1:0);
elseif(is_object($val)) {
$values = get_object_vars($val);
$val_count = count($values);
$buff = "<value><struct>";
foreach($values as $k => $v) {
$buff .= sprintf("<member>\n<name>%s</name>\n%s</member>\n", htmlspecialchars($k), _getEncodedVal($v, true));
}
$buff .= "</struct></value>\n";
} elseif(is_array($val)) {
$val_count = count($val);
$buff = "<value><array>\n<data>";
for($i=0;$i<$val_count;$i++) {
$buff .= _getEncodedVal($val[$i], true);
}
$buff .= "</data>\n</array></value>";
} else {
$buff = sprintf("<value><string>%s</string></value>\n", $val);
}
if(!$is_sub_set) return sprintf("<param>\n%s</param>", $buff);
return $buff;
}
// 결과 출력
function printContent($content) {
header("Content-Type: text/xml; charset=UTF-8");
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
print $content;
exit();
}
?>

View file

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<addon version="0.1">
<title xml:lang="ko">BlogAPI 애드온</title>
<title xml:lang="jp">BlogAPI </title>
<title xml:lang="zh-CN">BlogAPI</title>
<title xml:lang="en">Addon for BlogAPI</title>
<title xml:lang="es">BlogAPI Adicionales</title>
<author email_address="zero@zeroboard.com" link="http://www.zeroboard.com" date="2007. 2. 28">
<name xml:lang="ko">제로</name>
<name xml:lang="jp">Zero</name>
<name xml:lang="zh-CN">zero</name>
<name xml:lang="en">zero</name>
<name xml:lang="es">zero</name>
<description xml:lang="ko">
metaWeblog를 지원하는 blogApi애드온입니다.
사용으로 설정하시면 각 모듈마다 RSD 태그를 노출합니다.
api의 주소는 http://설치주소/모듈명/api 입니다.
사용으로 하셔야 RSD태그 및 api가 동작을 합니다.
</description>
<description xml:lang="jp">
MetaWeblogをサポートするBlogAPI アドオンです。「使用する」をクリックして設定すると各モジュールごとRSDのアドレスを表示します。API のアドレスは「http://インストールURL/モジュール名/api」です。 「使用する」に設定しておけば RSDのアドレスが表示され、 API が動作します。
</description>
<description xml:lang="zh-CN">
支持metaWeblog的 blogApi插件。
设置为"启用"时,会使每个模块都会显示RSD标签。
api地址为http://安装地址/模块名/api。
把状态设置为"使用"时才会激活RSD标签及api。
</description>
<description xml:lang="en">
This blogApi addon supports metaWeblog.
Toggling this with use option lets RSD tag be exposed for each module.
URL to the api is http://setup_path/module_name/api.
Only selecting use option enables RSD tag and api to behave.
</description>
<description xml:lang="es">
Es adicionales para soporte de metaWeblog.
Si marcar en uso, cada modulas van a exponer RSD etiqueta.
dirección de es, http://dirección de instalación/nombre de modula/api.
Para funcionar RSD etiqueta y API, es nesesario marcar USAR.
</description>
</author>
</addon>

View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<addon version="0.1">
<title xml:lang="ko">기본 카운터 애드온</title>
<title xml:lang="jp">接続カウンター</title>
<title xml:lang="zh-CN">网站访问统计</title>
<title xml:lang="en">Addon for basic counter</title>
<title xml:lang="es">Adición Taquilla Predeterminado</title>
<author email_address="zero@zeroboard.com" link="http://www.zeroboard.com" date="2007. 2. 28">
<name xml:lang="ko">제로</name>
<name xml:lang="jp">Zero</name>
<name xml:lang="zh-CN">zero</name>
<name xml:lang="en">zero</name>
<name xml:lang="es">zero</name>
<description xml:lang="ko">
제로보드XE의 기본 카운터 모듈을 이용하여 접속 정보를 기록합니다.
이 애드온을 켜셔야 접속 정보 수집이 됩니다.
</description>
<description xml:lang="jp">
セロボードXEの接続カウンターモジュールは、接続情報を記録します。このアドオンを「使用」に設定しておくと接続情報が記録されます。
</description>
<description xml:lang="zh-CN">
利用ZeroboardXE的网站访问统计模块记录网站访问信息。
把状态设置为"使用"时,才会记录网站访问信息.
</description>
<description xml:lang="en">
This addon logs access information based on the basic counter module within Zeroboard XE.
It is necessary for aggregation of access information to turn on this addon.
</description>
<description xml:lang="es">
Contar los visitantes con la taquilla predeterminado de ZeroBoardXE. Es necesario aprender esta adición para acumular las información de visitos.
</description>
</author>
</addon>

View file

@ -0,0 +1,20 @@
<?php
if(!defined("__ZBXE__")) exit();
/**
* @file counter.addon.php
* @author zero (zero@nzeo.com)
* @brief 카운터 애드온
*
* 카운터 애드온은 제로보드XE의 기본 카운터(counter)모듈을 이용하여 로그를 남깁니다.
* 검색 로봇이나 기타 툴의 접속을 방지하고 부하를 줄이기 위해서 페이지가 로드된 후에 javascript로 다시 로그를 남기도록 합니다.
* 따라서 카운터 애드온은 카운터를 수집하게 하는 javascript 파일을 추가하는 동작만 하며 기본 카운터 모듈의 호출은 해당 javascript
* 파일내에서 이루어집니다.
**/
// called_position가 before_module_init 이고 module이 admin이 아닐 경우
if($called_position == 'before_module_init' && !$GLOBALS['__counter_addon_called__']) {
if($this->module != 'admin') Context::addJsFile('./modules/counter/tpl/js/counter.js');
$GLOBALS['__counter_addon_called__'] = true;
}
?>

View file

@ -0,0 +1,194 @@
<?xml version="1.0" encoding="utf-8"?>
<addon version="0.1">
<title xml:lang="ko">게시글 구글 애드센스 연동</title>
<title xml:lang="jp">グーグル・アドセンス</title>
<title xml:lang="zh-CN">Google AdSense</title>
<title xml:lang="en">Linkage of Articles and Google Adsense</title>
<author email_address="zero@zeroboard.com" link="http://www.zeroboard.com" date="2007. 2. 28">
<name xml:lang="ko">제로</name>
<name xml:lang="jp">Zero</name>
<name xml:lang="zh-CN">zero</name>
<name xml:lang="en">zero</name>
<description xml:lang="ko">
게시글의 상/하단에 구글 애드센스를 추가할 수 있습니다.
추가확장폼에서 사용자에게 구글애드센스 코드를 받게 하여 글쓴이와 연동되는 애드센스 출력이 가능합니다.
이 기능을 사용하지 않거나 사용자가 애드센스 코드를 입력하지 않았다면 기본으로 설정된 애드센스 코드가 사용됩니다.
색상을 입력할때는 # 을 제외한 나머지 6자리의 색상코드를 입력해주세요.
위치를 top 또는 bottom을 입력하시면 게시글 상단 또는 하단에 광고를 출력할 수 있습니다.
</description>
<description xml:lang="jp">
コンテンツ書き込みの上・下段にグーグル・アドセンスGoogle AdSenseを追加することができます。追加拡張フォームに、グーグル・アドセンスGoogle AdSenseコードを入力すると、書き込みにユーザ自身のグーグル・アドセンスGoogle AdSenseが挿入できます。
この機能を使わない、またはユーザがグーグル・アドセンスGoogle AdSenseコードを入力しない場合は、デフォルトで設定されたアドセンス適用されます。文字列などに色を使用する場合は、「 #」を除く6ケタのカラーコードを入力してください。
「top」または「bottom」を入力すると書き込みの上段または下段に広告を出力することができます。
</description>
<description xml:lang="zh-CN">
可以把Google AdSense添加到文章的上/下端。
可以让用户在扩展表单里获得Google AdSense代码从而也可以显示与作者相关联的AdSense。
不使用此功能或用户没有输入Google AdSense代码时使用默认的AdSense代码。
输入颜色时,输入除#以外的 6位颜色代码。
指定显示位置时输入top或bottom即可把AdSense显示在内容的上端或下端。
</description>
<description xml:lang="en">
Google Adsense can be added at top/bottom of article.
An adsense linked with writer can be displayed by Google adsense code input by user in additional form.
If this isn't used or user hasn't input the adsense code, the default adsense code will be used.
To use color, input 6 color code except '#'.
You can display the ad at the top or the bottom of article by input Position as top or bottom.
</description>
</author>
<extra_vars>
<var name="position">
<title xml:lang="ko">위치</title>
<title xml:lang="jp">位置</title>
<title xml:lang="zh-CN">位置</title>
<title xml:lang="en">Position</title>
<description xml:lang="ko">top : 게시글 상단, bottom : 게시글 하단 을 지정하실 수 있습니다.</description>
<description xml:lang="jp">top : 本文の上段, bottom : 本文の下段</description>
<description xml:lang="zh-CN">可以指定显示位置。(top : 内容上端, bottom : 内容下端)</description>
<description xml:lang="en">You can set top : top of article, bottom : bottom of article.</description>
</var>
<var name="ad_client">
<title xml:lang="ko">구글 애드센스 키</title>
<title xml:lang="jp">アドセンスキー</title>
<title xml:lang="zh-CN">Google Adsense Key</title>
<title xml:lang="en">Google Adsense Key</title>
<description xml:lang="ko">기본으로 사용될 구글 애드센스 키를 입력해주세요.</description>
<description xml:lang="ko">デフォルトで使用するグーグルアドセンスキーを入力してください。</description>
<description xml:lang="zh-CN">请输入要使用的默认Google Adsense Key。</description>
<description xml:lang="en">Please input Google Adsense key to use as default.</description>
</var>
<var name="user_ad_client">
<title xml:lang="ko">회원 키 항목 이름</title>
<title xml:lang="jp">キー項目名</title>
<title xml:lang="zh-CN">会员Key项目名</title>
<title xml:lang="en">Name of Member Key Item</title>
<description xml:lang="ko">회원의 구글 애드센스를 허락하시려면 가입항목중 구글 키 입력 항목 이름을 입력해주세요.</description>
<description xml:lang="jp">グーグル・アドセンスが使用できるようするにためには、加入項目でグーグルキー入力項目名を入力してください。
</description>
<description xml:lang="zh-CN">允许会员使用个人Google AdSense时请输入要在会员注册项目中显示的Google Key项目名。</description>
<description xml:lang="en">To permit members' Google Adsense, please input name of Google key item in join items.</description>
</var>
<var name="ad_width">
<title xml:lang="ko">가로 길이</title>
<title xml:lang="jp">横幅</title>
<title xml:lang="zh-CN">宽度</title>
<title xml:lang="en">Width</title>
<description xml:lang="ko">애드센스의 가로길이를 입력해주세요.</description>
<description xml:lang="jp">アドセンスの横幅のサイズを入力してください</description>
<description xml:lang="zh-CN">请输入AdSense的宽度大小。</description>
<description xml:lang="en">Please input width of Adsense.</description>
</var>
<var name="ad_height">
<title xml:lang="ko">세로 길이</title>
<title xml:lang="jp">縦幅</title>
<title xml:lang="zh-CN">高度</title>
<title xml:lang="en">Height</title>
<description xml:lang="ko">애드센스의 세로길이를 입력해주세요.</description>
<description xml:lang="jp">アドセンスの立幅のサイズを入力してください。</description>
<description xml:lang="zh-CN">请输入高度大小。</description>
<description xml:lang="en">Please input height of Adsense.</description>
</var>
<var name="ad_format">
<title xml:lang="ko">포맷</title>
<title xml:lang="jp">フォーマット</title>
<title xml:lang="zh-CN">格式</title>
<title xml:lang="en">Format</title>
<description xml:lang="ko">애드센스 포맷을 입력해주세요.</description>
<description xml:lang="jp">アドセンスのフォーマットを入力してください。</description>
<description xml:lang="zh-CN">请输入广告格式</description>
<description xml:lang="en">Please input format of Adsense.</description>
</var>
<var name="ad_type">
<title xml:lang="ko">타입</title>
<title xml:lang="jp">タイプ</title>
<title xml:lang="zh-CN">样式</title>
<title xml:lang="en">Type</title>
<description xml:lang="ko">애드센스의 타입을 입력해주세요.</description>
<description xml:lang="jp">アドセンスのタイプを入力してください。</description>
<description xml:lang="zh-CN">请输入AdSense样式。</description>
<description xml:lang="en">Please input type of Adsense.</description>
</var>
<var name="ad_channel">
<title xml:lang="ko">채널</title>
<title xml:lang="jp">チャンネル</title>
<title xml:lang="zh-CN">频道</title>
<title xml:lang="en">Channel</title>
<description xml:lang="ko">채널을 입력해주세요. (사용자 정의 구글 애드센스일 경우 사용되지 않습니다)</description>
<description xml:lang="jp">チャンネルを入力してください(カスタマイズ・グーグル・アドセンスには使用されません)。</description>
<description xml:lang="zh-CN">请输入频道。 (用户自定义Google AdSense不使用频道)</description>
<description xml:lang="en">Please input channel. (It will not be used for custom Google Adsense)</description>
</var>
<var name="color_border">
<title xml:lang="ko">외곽선 색상</title>
<title xml:lang="jp">ボーダカラー</title>
<title xml:lang="zh-CN">边框颜色</title>
<title xml:lang="en">Border Color</title>
<description xml:lang="ko">외곽선 색상을 입력해주세요.</description>
<description xml:lang="jp">ボーダーカラーを入力してください。.</description>
<description xml:lang="zh-CN">请输入边框颜色。</description>
<description xml:lang="en">Please input color of border.</description>
</var>
<var name="color_bg">
<title xml:lang="ko">배경색</title>
<title xml:lang="jp">背景色</title>
<title xml:lang="zh-CN">背景色</title>
<title xml:lang="en">Background Color</title>
<description xml:lang="ko">배경색상을 입력해주세요.</description>
<description xml:lang="jp">背景色を入力してください</description>
<description xml:lang="zh-CN">请输入背景颜色。</description>
<description xml:lang="en">Please input color of background.</description>
</var>
<var name="link_color">
<title xml:lang="ko">링크 글자색</title>
<title xml:lang="jp">リンク色</title>
<title xml:lang="zh-CN">链接颜色</title>
<title xml:lang="en">Link Color</title>
<description xml:lang="ko">링크가 걸린 글자의 색상을 입력해주세요.</description>
<description xml:lang="jp">リンクの文字列の色を入力してください。</description>
<description xml:lang="zh-CN">请输入链接颜色。</description>
<description xml:lang="en">Please input color of linked text.</description>
</var>
<var name="text_color">
<title xml:lang="ko">글자 색 </title>
<title xml:lang="jp">文字列色</title>
<title xml:lang="zh-CN">字体颜色 </title>
<title xml:lang="en">Text Color </title>
<description xml:lang="ko">링크가 걸리지 않은 글자의 색을 입력해주세요</description>
<description xml:lang="jp">文字列の色を入力してください。</description>
<description xml:lang="zh-CN">请输入没有链接字体颜色。</description>
<description xml:lang="en">Please input color of text.</description>
</var>
<var name="url_color">
<title xml:lang="ko">URL 색</title>
<title xml:lang="jp">URL色</title>
<title xml:lang="zh-CN">URL 颜色</title>
<title xml:lang="en">URL Color</title>
<description xml:lang="ko">URL의 글자색을 입력해주세요.</description>
<description xml:lang="ko">URLの色を入力してください。</description>
<description xml:lang="zh-CN">请输入URL的颜色。</description>
<description xml:lang="en">Please input color of URL.</description>
</var>
<var name="ui_features">
<title xml:lang="ko">테두리 형태</title>
<title xml:lang="jp">ボーダスタイル</title>
<title xml:lang="zh-CN">边框样式</title>
<title xml:lang="en">Border Style</title>
<description xml:lang="ko">테두리 형태를 입력해주세요.</description>
<description xml:lang="jp">ボーダースタイルを入力してください。</description>
<description xml:lang="zh-CN">请输入边框样式。</description>
<description xml:lang="en">Please input style of border.</description>
</var>
<var name="background_image">
<title xml:lang="ko">배경 이미지를 입력해주세요.</title>
<title xml:lang="jp">背景イメージ</title>
<title xml:lang="zh-CN">请输入背景图片。</title>
<title xml:lang="en">Please input image of background.</title>
<description xml:lang="ko">접근가능한 웹서버에 올린 배경 이미지의 URL을 입력해주세요.</description>
<description xml:lang="jp">アクセス可能なサーバにアップロードされている背景イメージのURLを入力してください。</description>
<description xml:lang="zh-CN">请输入有效的图片URL。</description>
<description xml:lang="en">Please input URL of background image which is uploaded on accessible web server.</description>
</var>
</extra_vars>
</addon>

View file

@ -0,0 +1,23 @@
<?php
if(!defined("__ZBXE__")) exit();
/**
* @file google_adsense.addon.php
* @author zero (zero@nzeo.com)
* @brief google_adsense를 게시글의 /하단에 출력할 있도록 한다.
*
* 모든 출력이 끝난후에 사용이 .
**/
// called_position이 before_module_init일때만 실행
if($called_position != 'before_display_content' || Context::getResponseMethod()=="XMLRPC") return;
require_once("./addons/google_adsense/google_adsense.lib.php");
if($addon_info->position == "top") $pos_regx = "!<\!--BeforeDocument\(([0-9]+),([0-9]+)\)-->!is";
else $pos_regx = "!<\!--AfterDocument\(([0-9]+),([0-9]+)\)-->!is";
$GLOBALS['__g_addon_info__'] = $addon_info;
$output = preg_replace_callback($pos_regx, matchDocument, $output);
?>

View file

@ -0,0 +1,53 @@
<?php
function matchDocument($matches) {
$addon_info = $GLOBALS['__g_addon_info__'];
$source_code = $matches[0];
$document_srl = $matches[1];
$member_srl = $matches[2];
// 사용자 입력을 지원하면 해당 회원의 정보에서 구글 키를 가져옴
if($member_srl && $addon_info->user_ad_client) {
$oMemberModel = &getModel('member');
$member_info = $oMemberModel->getMemberInfoByMemberSrl($member_srl);
$key = $member_info->{$addon_info->user_ad_client};
if($key) {
$addon_info->ad_client = $key;
$addAdSense->ad_type = '';
}
}
$adsense_code = addAdSense($addon_info);
return $source_code.$adsense_code;
}
function addAdSense($addon_info) {
$script_code = <<<EndOfScript
<script type="text/javascript"><!--
google_ad_client = "{$addon_info->ad_client}";
google_ad_width = "{$addon_info->ad_width}";
google_ad_height = "{$addon_info->ad_height}";
google_ad_format = "{$addon_info->ad_format}";
google_ad_type = "{$addon_info->ad_type}";
google_ad_channel = "{$addon_info->ad_channel}";
google_color_border = "{$addon_info->color_border}";
google_color_bg = "{$addon_info->color_bg}";
google_color_link = "{$addon_info->link_color}";
google_color_text = "{$addon_info->text_color}";
google_color_url = "{$addon_info->url_color}";
google_ui_features = "{$addon_info->ui_features}";
//-->
</script>
<script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>
EndOfScript;
if($addon_info->background_image) $backgroundStyle = sprintf('background-image:url(%s)', $addon_info->background_image);
$script_code = sprintf('<div style="width:%dpx;height:%dpx;%s;margin:10px 0 10px 0px;">%s</div>',$addon_info->ad_width, $addon_info->ad_height, $backgroundStyle, $script_code);
return $script_code;
}
?>

View file

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<addon version="0.1">
<title xml:lang="ko">사용자 추가 정보 및 커뮤니케이션 기능 활성화</title>
<title xml:lang="jp">会員情報・コミュニティ</title>
<title xml:lang="zh-CN">用户扩展信息</title>
<title xml:lang="en">Addon for enabling facilities for providing additional information about users and communicating</title>
<title xml:lang="es">Informaciónes addciónales del usuario y actualzación del función communicación.</title>
<author email_address="zero@zeroboard.com" link="http://www.zeroboard.com" date="2007. 2. 28">
<name xml:lang="ko">제로</name>
<name xml:lang="jp">Zero</name>
<name xml:lang="zh-CN">zero</name>
<name xml:lang="en">zero</name>
<name xml:lang="es">zero</name>
<description xml:lang="ko">
사용자의 정보중 이미지이름, 이미지마크, 서명등을 화면에 출력해주는 애드온입니다.
이런 정보들을 사용하지 않을 경우를 대비하여 별도의 애드온으로 빼어서 실행시간을 줄여줍니다.
이미지이름, 이미지마크, 서명등을 노출하고 싶다면 이 애드온을 활성화 시키세요.
1. 출력되기 직전 &amp;lt;div class="member_회원번호"&amp;gt;....&amp;lt;/div&amp;gt; 로 정의가 된 부분을 찾아 회원번호를 구해서 이미지이름, 이미지마크가 있는지를 확인하여 있으면 내용을 변경해버립니다.
2. 출력되기 직전 &amp;lt;div class="document_번호"&amp;gt;...&amp;lt;/div&amp;gt;로 정의된 곳을 찾아 글의 내용이라 판단, 하단에 서명을 추가합니다.
3. 새로운 쪽지가 왔을 경우 팝업으로 띄움
4. MemberModel::getMemberMenu 호출시 대상이 회원일 경우 쪽지 보내기 기능 추가합니다.
5. MemberModel::getMemberMenu 호출시 친구 등록 메뉴를 추가합니다.
</description>
<description xml:lang="jp">
会員情報のイメージ名、イメージマーク、署名などを画面に表示するアドオンです。このような情報を使用しない場合、アドオンを「使用」に設定すれば、実行時間を少なくします。イメージ名、イメージマーク、署名などを表示させたい時は、このアドオンを「使用」に設定して下さい。
1. 出力の直前 &lt;div class="member_会員番号"&gt;....&lt;/div&gt; に定義された部分を探し、会員番号をチェックしてイメージ名、イメージマークがあるかを確認します。あった場合は内容を変更します
2. 出力の直前 &lt;div class="document_番号"&gt;...&lt;/div&gt;に定義された部分を探し、書込みの内容だと判断して、下段に署名を追加します
3. 新しいメッセージが来た場合ポップアップで表示します
4. MemberModel::getMemberMenu を呼出す時、相手が会員の場合はメッセージ送信の機能を追加します
5. MemberModel::getMemberMenu を呼出す時、友達登録メニュを追加します
</description>
<description xml:lang="zh-CN">
此插件将把用户信息中的昵称图片,用户图标,签名等信息显示到页面当中。
因部分用户不使用此项功能或为了考虑页面载入速度,以插件形式提供此项功能。
要想使用会员扩展信息请激活此插件。
1. 显示之前先查找定义为 &amp;lt;div class="member_会员编号"&amp;gt;....&amp;lt;/div&amp;gt; 的部分获得会员编号后,再以会员编号确认有没有昵称图片/用户图标,有的话即可更新相关内容。
2. 显示之前先查找定义为 &amp;lt;div class="document_编号"&amp;gt;...&amp;lt;/div&amp;gt;的部分判断此处为主题内容后,在此处下方添加个人签名。
3. 收到新消息时,用弹出窗口显示。
4. 呼出MemberModel::getMemberMenu时如对方是会员就添加[发送短消息]功能。
5. 呼出MemberModel::getMemberMenu时添加[加为好友]菜单。
</description>
<description xml:lang="en">
Among other information about users, this addon displays image name, image mark, and signature.
In case of you not use this information, this stands apart from basic information so runtime should be reduced.
To expose image name, image mark, and signature, please turn on this addon.
1. Just before displaying user information, this will figure out unique key of member from the definition of "&amp;lt;div class="member_{unique key of member}"&amp;gt;....&amp;lt;/div&amp;gt;" and replace image name and/or image mark if they exsist.
2. Just before displaying, this is gonna locate the definition of "&amp;lt;div class="document_{unique key of document}"&amp;gt;...&amp;lt;/div&amp;gt;" and insert the signature below it.
3. When arrived a new slip, this will popup it.
4. This makes available to send a slip if the target is a member when MemberModel::getMemberMenu is called.
5. This adds a menu for registering as a friend when MemberModel::getMemberMenu is called.
</description>
<description xml:lang="es">
Se muestra nombre de imagén, marcador de imagén, firma del usuario.
para cortar la ejecución de programa, si no es necessario, puede apagar la function adicionales.
Necesita actualizar este funcion, por favor haga clic en ´usar´.
1. Este adición busca &lt;div class="member_Num. usuario"&gt;....&lt;/div&gt; en los documentos, y calcula numero de usuario. Y si hay nombre de imagen, marcador de imagen, se cambia el contenido.
2. Este adicion busca &lt;div class="document_Num. documentos"&gt;...&lt;/div&gt; en los documentos, coloca la firma donde interpreta el termino de contenido.
3. Si llega nota, se muestra en pop up.
4. Si llama MemberModel::getMemberMenu, si es miembro, funcion de "mandar nota" se actualiza.
5. Si llama MemberModel::getMemberMenu, el addiciona menú ¨agregar contacto¨.
</description>
</author>
</addon>

View file

@ -0,0 +1,3 @@
<?php
$lang->alert_new_message_arrived = 'You got a new message. Do you want to check now?';
?>

View file

@ -0,0 +1,3 @@
<?php
$lang->alert_new_message_arrived = '新しいメッセージが届きました。確認しますか。';
?>

View file

@ -0,0 +1,3 @@
<?php
$lang->alert_new_message_arrived = '새로운 메세지가 도착하였습니다. 확인하시겠습니까?';
?>

View file

@ -0,0 +1,3 @@
<?php
$lang->alert_new_message_arrived = '您有新消息。要确认吗?';
?>

View file

@ -0,0 +1,111 @@
<?php
if(!defined("__ZBXE__")) exit();
/**
* @file image_name.addon.php
* @author zero (zero@nzeo.com)
* @brief 사용자의 이미지이름/ 이미지마크나 커뮤니케이션 기능을 추가시킴
*
* 1. 출력되기 직전 <div class="member_회원번호">....</div> 정의가 부분을 찾아 회원번호를 구해서
* 이미지이름, 이미지마크가 있는지를 확인하여 있으면 내용을 변경해버립니다.
*
* 2. 출력되기 직전 <div class="document_회원번호">...</div> 정의된 곳을 찾아 글의 내용이라 판단,
* 하단에 서명을 추가합니다.
*
* 3. 새로운 쪽지가 왔을 경우 팝업으로 띄움
*
* 4. MemberModel::getMemberMenu 호출시 대상이 회원일 경우 쪽지 보내기 기능 추가합니다.
*
* 5. MemberModel::getMemberMenu 호출시 친구 등록 메뉴를 추가합니다.
*
**/
/**
* 1,2 기능 수행 : 출력되기 바로 직전일 경우에 이미지이름/이미지마크등을 변경
* 조건 : called_position == 'before_display_content'
**/
if($called_position == "before_display_content") {
// 기본적인 기능이라 MemberController 에 변경 코드가 있음
$oMemberController = &getController('member');
// 1. 출력문서중에서 <div class="member_번호">content</div>를 찾아 MemberController::transImageName() 를 이용하여 이미지이름/마크로 변경
$output = preg_replace_callback('!<(div|span)([^\>]*)member_([0-9]+)([^\>]*)>(.*?)\<\/(div|span)\>!is', array($oMemberController, 'transImageName'), $output);
// 2. 출력문서중에 <!--AfterDocument(문서번호,회원번호)--> 를 찾아서 member_controller::transSignature()를 이용해서 서명을 추가
$output = preg_replace_callback('/<!--AfterDocument\(([0-9]+),([0-9]+)\)-->/i', array($oMemberController, 'transSignature'), $output);
/**
* 3 기능 수행 : 시작할때 새쪽지가 왔는지 검사
* 조건 : called_position = 'before_module_init', module != 'member'
**/
} elseif($called_position == 'before_module_init' && $this->module != 'member' && Context::get('is_logged') ) {
// 로그인된 사용자 정보를 구함
$logged_info = Context::get('logged_info');
$flag_path = './files/member_extra_info/new_message_flags/'.getNumberingPath($logged_info->member_srl);
$flag_file = sprintf('%s%s', $flag_path, $logged_info->member_srl);
// 새로운 쪽지에 대한 플래그가 있으면 쪽지 보기 팝업 띄움
if(file_exists($flag_file)) {
@unlink($flag_file);
Context::loadLang('./addons/member_extra_info/lang');
$script = sprintf('<script type="text/javascript"> xAddEventListener(window,"load", function() {if(confirm("%s")) { popopen("%s"); }}); </script>', Context::getLang('alert_new_message_arrived'), Context::getRequestUri().'?module=member&act=dispMemberNewMessage');
Context::addHtmlHeader( $script );
}
/**
* 4,5 기능 수행 : 사용자 이름을 클릭시 요청되는 MemberModel::getMemberMenu 후에 $menu_list에 쪽지 발송, 친구추가등의 링크 추가
* 조건 : called_position == 'after_module_proc', module = 'member', act = 'getMemberMenu'
**/
} elseif($called_position == 'after_module_proc' && $this->module == 'member' && $this->act == 'getMemberMenu') {
// 비로그인 사용자라면 패스
if(!Context::get('is_logged')) return;
// 로그인된 사용자 정보를 구함
$logged_info = Context::get('logged_info');
$member_srl = Context::get('member_srl');
// 템플릿에서 사용되기 전의 menu_list를 가져옴
$menu_list = $this->get('menu_list');
// 자신이라면 쪽지함 보기 기능 추가
if($logged_info->member_srl == $member_srl) {
// 4. 자신의 쪽지함 보기 기능 추가
$menu_str = Context::getLang('cmd_view_message_box');
$menu_link = "current_url.setQuery('act','dispMemberMessages')";
$menu_list .= sprintf("\n%s,%s,move_url(%s,'Y')", Context::getRequestUri().'/modules/member/tpl/images/icon_message_box.gif', $menu_str, $menu_link);
// 5. 친구 목록 보기
$menu_str = Context::getLang('cmd_view_friend');
$menu_link = "current_url.setQuery('act','dispMemberFriend')";
$menu_list .= sprintf("\n%s,%s,move_url(%s,'Y')", Context::getRequestUri().'/modules/member/tpl/images/icon_friend_box.gif',$menu_str, $menu_link);
// 아니라면 쪽지 발송, 친구 등록 추가
} else {
// 대상 회원의 정보를 가져옴
$target_member_info = $this->getMemberInfoByMemberSrl($member_srl);
// 4. 쪽지 발송 메뉴를 만듬
if( $target_member_info->allow_message =='Y' || ($target_member_info->allow_message == 'F' && $this->isFriend($member_srl))) {
$menu_str = Context::getLang('cmd_send_message');
$menu_link = sprintf('%s?module=member&amp;act=dispMemberSendMessage&amp;receiver_srl=%s',Context::getRequestUri(),$member_srl);
$menu_list .= sprintf("\n%s,%s,popopen('%s','sendMessage')", Context::getRequestUri().'/modules/member/tpl/images/icon_write_message.gif', $menu_str, $menu_link);
}
// 5. 친구 등록 메뉴를 만듬 (이미 등록된 친구가 아닐 경우)
if(!$this->isAddedFriend($member_srl)) {
$menu_str = Context::getLang('cmd_add_friend');
$menu_link = sprintf('%s?module=member&amp;act=dispMemberAddFriend&amp;target_srl=%s',Context::getRequestUri(),$member_srl);
$menu_list .= sprintf("\n%s,%s,popopen('%s','addFriend')", Context::getRequestUri().'/modules/member/tpl/images/icon_add_friend.gif', $menu_str, $menu_link);
}
}
// 템플릿에 적용되게 하기 위해 module의 variables에 재등록
$this->add('menu_list', $menu_list);
}
?>

View file

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<addon version="0.1">
<title xml:lang="ko">OpenID delegation ID</title>
<title xml:lang="zh-CN">OpenID</title>
<title xml:lang="en">Addon for delegating domain name to OpenID</title>
<title xml:lang="es">delegación ID para OpenID</title>
<title xml:lang="jp">OpenID</title>
<author email_address="zero@zeroboard.com" link="http://www.zeroboard.com" date="2007. 2. 28">
<name xml:lang="ko">제로</name>
<name xml:lang="zh-CN">zero</name>
<name xml:lang="en">zero</name>
<name xml:lang="es">zero</name>
<name xml:lang="jp">Zero</name>
<description xml:lang="ko">
본인의 도메인을 사용하여 오픈아이디로 활용할 수 있도록 합니다.
꼭 설정을 통해서 openid provider관련 값을 입력후 사용해주세요.
</description>
<description xml:lang="zh-CN">
可以把本人的域名当分散式身份验证系统(OpenID)来使用。
必须在设置中输入openid provider相关值后再使用。
</description>
<description xml:lang="en">
This addon enables you to use your own domian name as an OpenID.
Just be sure to set the values related with openid provider before using.
</description>
<description xml:lang="es">
Utliza su dominio para usar OpenID.
La configuración es necesario.
</description>
<description xml:lang="jp">
保有するドメインをオープンとして活用することができます。必ず設定で、OpenIDのプロバイダー関連の情報を入力してから使用してください。
</description>
</author>
<extra_vars>
<var name="server">
<title xml:lang="ko">server</title>
<title xml:lang="zh-CN">server</title>
<title xml:lang="en">server</title>
<title xml:lang="es">Servidor</title>
<title xml:lang="jp">server</title>
<description xml:lang="ko">openid.server 값을 입력해 주세요.</description>
<description xml:lang="zh-CN">请输入 openid.server 值。</description>
<description xml:lang="en">Please input your openid.server value.</description>
<description xml:lang="es">Servidor de OpenID.</description>
<description xml:lang="jp">openid.server の値を入力してください。</description>
</var>
<var name="delegate">
<title xml:lang="ko">delegate</title>
<title xml:lang="en">delegate</title>
<title xml:lang="zh-CN">delegate</title>
<title xml:lang="es">Delegador</title>
<title xml:lang="jp">delegate</title>
<description xml:lang="ko">openid.delegate값을 입력해주세요.</description>
<description xml:lang="zh-CN">请输入 openid.delegate 值。</description>
<description xml:lang="en">Please input your openid.delegate value.</description>
<description xml:lang="en">Delegador de OpenID</description>
<description xml:lang="jp">openid.delegate の値を入力してください。</description>
</var>
<var name="xrds">
<title xml:lang="ko">xrds</title>
<title xml:lang="zh-CN">xrds</title>
<title xml:lang="en">xrds</title>
<title xml:lang="es">xrds</title>
<title xml:lang="jp">xrds</title>
<description xml:lang="ko">X-XRDS-Location값을 입력해주세요.</description>
<description xml:lang="zh-CN">请输入 X-XRDS-Location 值。</description>
<description xml:lang="en">Please input your X-XRDS-Location value.</description>
<description xml:lang="es">ubicación de X-XRDS</description>
<description xml:lang="jp">X-XRDS-Location の値を入力してください。</description>
</var>
</extra_vars>
</addon>

View file

@ -0,0 +1,29 @@
<?php
if(!defined("__ZBXE__")) exit();
/**
* @file openid_delegation_id.addon.php
* @author zero (zero@nzeo.com)
* @brief OpenID Delegation ID 애드온
*
* 오픈아이디를 자신의 홈페이지나 블로그 주소로 이용할 있도록 해줍니다.
* 설정을 통해서 사용하시는 오픈아이디 서비스에 해당하는 정보를 입력해주세요.
**/
// called_position이 before_module_init일때만 실행
if($called_position != 'before_module_init') return;
// openid_delegation_id 애드온 설정 정보를 가져옴
if(!$addon_info->server||!$addon_info->delegate||!$addon_info->xrds) return;
$header_script = sprintf(
'<link rel="openid.server" href="%s" />'."\n".
'<link rel="openid.delegate" href="%s" />'."\n".
'<meta http-equiv="X-XRDS-Location" content="%s" />',
$addon_info->server,
$addon_info->delegate,
$addon_info->xrds
);
Context::addHtmlHeader($header_script);
?>

View file

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<addon version="0.1">
<title xml:lang="ko">포인트 활성화 애드온</title>
<title xml:lang="zh-CN">积分插件</title>
<title xml:lang="jp">ポイントシステム</title>
<title xml:lang="en">Addon for activating point</title>
<author email_address="zero@zeroboard.com" link="http://www.zeroboard.com" date="2007. 7. 26">
<name xml:lang="ko">제로</name>
<name xml:lang="zh-CN">Zero</name>
<name xml:lang="jp">Zero</name>
<name xml:lang="en">zero</name>
<description xml:lang="ko">
포인트시스템 모듈에 설정된 내용을 바탕으로 글작성/삭제/댓글작성/삭제/파일업로드/삭제/다운로드등의 행동에 대해서 포인트를 기록합니다.
</description>
<description xml:lang="zh-CN">
以积分系统模块中设置的内容为基础,对发表/删除新帖,发表/删除评论,上传/下载/删除/文件等动作记录为积分。
</description>
<description xml:lang="jp">
ポイントシステムモジュールで設定された内容を基に、書き込み作成・削除/コメント作成・削除/ファイルアップロード・削除/ダウンロードなどのユーザの活動に対してポイントを記録します。
</description>
<description xml:lang="en">
This addon records point on writing/deleting/adding comments/deleting comments/uploading/downloading following to point system module.
</description>
</author>
</addon>

View file

@ -0,0 +1,221 @@
<?php
if(!defined("__ZBXE__")) exit();
/**
* @file point.addon.php
* @author zero (zero@nzeo.com)
* @brief 포인트 애드온
*
* 포인트 시스템 모듈에 설정된 내용을 토대로 하여 포인트를 부여/차감하고,
* 다운로드를 금지시키고,
* 회원 이름 앞에 레벨 아이콘을 표시한다.
**/
// 관리자 모듈이면 패스~
if(Context::get('module')=='admin') return;
// 로그인 상태일때만 실행
$logged_info = Context::get('logged_info');
if(!$logged_info->member_srl) return;
// point action cache file을 가져와서 현재 속한 캐시파일인지 확인
$act_cache_file = "./files/cache/point.act.cache";
$buff = FileHandler::readFile($act_cache_file);
if(strpos($buff,$this->act)===false) return;
// point 모듈 정보 가져옴
$oModuleModel = &getModel('module');
$config = $oModuleModel->getModuleConfig('point');
// 현재 로그인 사용자의 포인트를 가져옴
$member_srl = $logged_info->member_srl;
$oPointModel = &getModel('point');
$cur_point = $oPointModel->getPoint($member_srl, true);
// 파일다운로드를 제외한 action은 called_position가 before_module_proc일때 실행
if($called_position == 'after_module_proc') {
// 게시글 작성
if(strpos($config->insert_document_act,$this->act)!==false) {
$document_srl = Context::get('document_srl');
$oDocumentModel = &getModel('document');
$oDocument = $oDocumentModel->getDocument($document_srl);
// 신규 글인지 체크
if($oDocument->get('regdate')!=$oDocument->get('last_update')) return;
$module_srl = $oDocument->get('module_srl');
// 포인트를 구해옴
$point = $config->module_point[$module_srl]['insert_document'];
if(!$point) $point = $config->insert_document;
// 포인트 증감
$cur_point += $point;
$oPointController = &getController('point');
$oPointController->setPoint($member_srl,$cur_point);
// 게시글 삭제
} elseif(strpos($config->delete_document_act,$this->act)!==false) {
if(!$this->toBool()) return;
$target_member_srl = Context::get('_point_target_member_srl');
if(!$target_member_srl) return;
$module_srl = $this->module_srl;
// 포인트를 구해옴
$point = $config->module_point[$module_srl]['insert_document'];
if(!$point) $point = $config->insert_document;
// 포인트 차감
$cur_point = $oPointModel->getPoint($target_member_srl, true);
$cur_point -= $point;
$oPointController = &getController('point');
$oPointController->setPoint($target_member_srl,$cur_point);
// 댓글 작성
} elseif(strpos($config->insert_comment_act,$this->act)!==false) {
$comment_srl = Context::get('comment_srl');
$oCommentModel = &getModel('comment');
$comment = $oCommentModel->getComment($comment_srl);
// 이미 존재하는 댓글인지 체크
if($comment->last_update) return;
// 포인트를 구해옴
$module_srl = $comment->module_srl;
// 포인트를 구해옴
$point = $config->module_point[$module_srl]['insert_comment'];
if(!$point) $point = $config->insert_comment;
// 포인트 증감
$cur_point += $point;
$oPointController = &getController('point');
$oPointController->setPoint($member_srl,$cur_point);
// 댓글 삭제
} elseif(strpos($config->delete_comment_act,$this->act)!==false) {
if(!$this->toBool()) return;
$target_member_srl = Context::get('_point_target_member_srl');
if(!$target_member_srl) return;
// 포인트를 구해옴
$module_srl = $this->module_srl;
// 포인트를 구해옴
$point = $config->module_point[$module_srl]['insert_comment'];
if(!$point) $point = $config->insert_comment;
// 포인트 증감
$cur_point = $oPointModel->getPoint($target_member_srl, true);
$cur_point -= $point;
$oPointController = &getController('point');
$oPointController->setPoint($target_member_srl,$cur_point);
// 파일업로드
} elseif(strpos($config->upload_file_act,$this->act)!==false) {
if(!$output->toBool()||!$output->get('file_srl')) return;
$file_srl = $output->get('file_srl');
$oFileModel = &getModel('file');
$file_info = $oFileModel->getFile($file_srl);
$module_srl = $this->module_srl;
// 포인트를 구해옴
$point = $config->module_point[$module_srl]['upload_file'];
if(!$point) $point = $config->upload_file;
// 포인트 증감
$cur_point += $point;
$oPointController = &getController('point');
$oPointController->setPoint($member_srl,$cur_point);
// 파일삭제
} elseif(strpos($config->delete_file_act,$this->act)!==false) {
// 파일 정보를 구해옴
$file_srl = Context::get('file_srl');
if(!$file_srl) return;
$target_member_srl = Context::get('_point_target_member_srl');
if(!$target_member_srl) return;
$module_srl = $this->module_srl;
$target_member_srl = Context::get('_point_target_member_srl');
if(!$target_member_srl) return;
// 포인트를 구해옴
$point = $config->module_point[$module_srl]['upload_file'];
if(!$point) $point = $config->upload_file;
// 포인트 차감
$cur_point = $oPointModel->getPoint($target_member_srl, true);
$cur_point -= $point;
$oPointController = &getController('point');
$oPointController->setPoint($target_member_srl,$cur_point);
}
// 파일다운로드는 before_module_proc 일때 체크
} else if($called_position == "before_module_proc") {
// 파일다운로드
if(strpos($config->download_file_act,$this->act)!==false) {
// 파일 정보를 구해옴
$file_srl = Context::get('file_srl');
if(!$file_srl) return;
$oFileModel = &getModel('file');
$file_info = $oFileModel->getFile($file_srl);
if($file_info->file_srl != $file_srl) return;
$module_srl = $file_info->module_srl;
// 포인트를 구해옴
$point = $config->module_point[$module_srl]['download_file'];
if(!$point) $point = $config->download_file;
// 포인트가 0보다 작고 포인트가 없으면 파일 다운로드가 안되도록 했다면 오류
if($cur_point + $point < 0 && $config->disable_download == 'Y') {
$this->stop('msg_cannot_download');
}
// 포인트 차감
$cur_point += $point;
$oPointController = &getController('point');
$oPointController->setPoint($member_srl,$cur_point);
// 글 삭제일 경우 대상 글의 사용자 번호 저장
} elseif(strpos($config->delete_document_act,$this->act)!==false) {
$document_srl = Context::get('document_srl');
$oDocumentModel = &getModel('document');
$oDocument = $oDocumentModel->getDocument($document_srl);
$target_member_srl = $oDocument->get('member_srl');
if($target_member_srl) Context::set('_point_target_member_srl', $target_member_srl);
// 댓글 삭제일 경우 대상 댓글의 사용자 번호 저장
} elseif(strpos($config->delete_comment_act,$this->act)!==false) {
$comment_srl = Context::get('comment_srl');
$oCommentModel = &getModel('comment');
$comment = $oCommentModel->getComment($comment_srl);
$target_member_srl = $comment->member_srl;
if($target_member_srl) Context::set('_point_target_member_srl', $target_member_srl);
// 파일삭제일 경우 대상 파일의 정보에서 사용자 번호 저장
} elseif(strpos($config->delete_file_act,$this->act)!==false) {
// 파일 정보를 구해옴
$file_srl = Context::get('file_srl');
if(!$file_srl) return;
$oFileModel = &getModel('file');
$file_info = $oFileModel->getFile($file_srl);
if($file_info->file_srl != $file_srl) return;
$target_member_srl = $file_info->member_srl;
if($target_member_srl) Context::set('_point_target_member_srl', $target_member_srl);
}
}
?>

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<addon version="0.1">
<title xml:lang="ko">포인트 레벨 아이콘 표시 애드온</title>
<title xml:lang="zh-CN">积分级别图标</title>
<title xml:lang="jp">ポイントレベルアイコン</title>
<title xml:lang="en">Addon for displaying level icon</title>
<author email_address="zero@zeroboard.com" link="http://www.zeroboard.com" date="2007. 7. 26">
<name xml:lang="ko">제로</name>
<name xml:lang="zh-CN">Zero</name>
<name xml:lang="jp">Zero</name>
<name xml:lang="en">zero</name>
<description xml:lang="ko">
포인트 시스템을 사용중일 경우 사용자 이름 앞에 레벨 아이콘을 표시하도록 합니다.
레벨 아이콘은 모듈 &gt; 포인트시스템에서 선택 가능합니다.
</description>
<description xml:lang="zh-CN">
使用积分系统时,可以在用户名前显示级别图标。
级别图标可以在模块 &gt; 积分系统中进行选择。
</description>
<description xml:lang="jp">
ポイントシステムを使用している場合、ユーザ名の前にレベルアイコンを表示させます。レベルアイコンは、「モジュール&gt;ポイントシステム」で選択できます。
</description>
<description xml:lang="en">
This addon displays level icon in front of user name when point system is using.
You can choose level icon on Module &gt; Point System.
</description>
</author>
</addon>

View file

@ -0,0 +1,17 @@
<?php
if(!defined("__ZBXE__")) exit();
/**
* @file point.addon.php
* @author zero (zero@nzeo.com)
* @brief 포인트 레벨 아이콘 표시 애드온
*
* 포인트 시스템 사용중일때 사용자 이름 앞에 포인트 레벨 아이콘을 표시합니다.
**/
// before_display_content 가 아니면 return
if($called_position != "before_display_content") return;
$oPointController = &getController('point');
$output = preg_replace_callback('!<(div|span)([^\>]*)member_([0-9\-]+)([^\>]*)>(.*?)\<\/(div|span)\>!is', array($oPointController, 'transLevelIcon'), $output);
?>

View file

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<addon version="0.1">
<title xml:lang="ko">레인보우 링크 애드온</title>
<title xml:lang="zh-CN">Rainbow 链接</title>
<title xml:lang="en">Addon for rainbow links</title>
<title xml:lang="es">Adición Enlace Arco Iris</title>
<title xml:lang="jp">レインボーリンク</title>
<author email_address="webmaster@dynamicdrive.com" link="http://dynamicdrive.com" date="2007. 2. 28">
<name xml:lang="ko">dynamicdrive.com</name>
<name xml:lang="zh-CN">dynamicdrive.com</name>
<name xml:lang="en">dynamicdrive.com</name>
<name xml:lang="es">dynamicdrive.com</name>
<name xml:lang="jp">dynamicdrive.com</name>
<description xml:lang="ko">
rainbow.js를 header에 추가하여 링크가 걸린 글의 색을 무지개색으로 나타냅니다.
이 애드온의 rainbow.js는 &lt;a href=&quot;http://www.dynamicdrive.com&quot; target=&quot;_blank&quot;&gt;Dynamicdrive.com&lt;/a&gt;에 저작권이 있습니다.
</description>
<description xml:lang="zh-CN">
把rainbow.js添加到header区使链接显示为彩虹色。
此插件的rainbow.js文件版权属于 &lt;a href=&quot;http://www.dynamicdrive.com&quot; target=&quot;_blank&quot;&gt;Dynamicdrive.com&lt;/a&gt;
</description>
<description xml:lang="en">
This addon adds a file named &quot;rainbow.js&quot; to headers, then linked text will get chameleon(rainbow) color change effect.
&quot;rainbow.js&quot; Copyrightⓒ2007 &lt;a href=&quot;http://www.dynamicdrive.com&quot; target=&quot;_blank&quot;&gt;Dynamicdrive.com&lt;/a&gt;.
</description>
<description xml:lang="es">
incluye rainbow.js en header para mostrar enlaces en colores de arco iris.
&lt;a href=&quot;http://www.dynamicdrive.com&quot; target=&quot;_blank&quot;&gt;Dynamicdrive.com&lt;/a&gt; tiene derecho de autor sobre rainbow.js
</description>
<description xml:lang="jp">
「rainbow.js」をヘッダーに追加し、リンクされている文字列の色を虹色で表示します。この機能拡張の「 rainbow.js」は「&lt;a href=&quot;http://www.dynamicdrive.com&quot; target=&quot;_blank&quot;&gt;Dynamicdrive.com&lt;/a&gt;」に著作権があります。
</description>
</author>
</addon>

View file

@ -0,0 +1,245 @@
/************************************************************************/
/* Rainbow Links Version 1.03 (2003.9.20) */
/* Script updated by Dynamicdrive.com for IE6 */
/* Copyright (C) 1999-2001 TAKANASHI Mizuki */
/* takanasi@hamal.freemail.ne.jp */
/*----------------------------------------------------------------------*/
/* Read it somehow even if my English text is a little wrong! ;-) */
/* */
/* Usage: */
/* Insert '<script src="rainbow.js"></script>' into the BODY section, */
/* right after the BODY tag itself, before anything else. */
/* You don't need to add "onMouseover" and "onMouseout" attributes!! */
/* */
/* If you'd like to add effect to other texts(not link texts), then */
/* add 'onmouseover="doRainbow(this);"' and */
/* 'onmouseout="stopRainbow();"' to the target tags. */
/* */
/* This Script works with IE4,Netscape6,Mozilla browser and above only, */
/* but no error occurs on other browsers. */
/************************************************************************/
////////////////////////////////////////////////////////////////////
// Setting
var rate = 20; // Increase amount(The degree of the transmutation)
////////////////////////////////////////////////////////////////////
// Main routine
/*
if (document.getElementById)
window.onerror=new Function("return true")
*/
var objActive; // The object which event occured in
var act = 0; // Flag during the action
var elmH = 0; // Hue
var elmS = 128; // Saturation
var elmV = 255; // Value
var clrOrg; // A color before the change
var TimerID; // Timer ID
if(xIE4Up) {
xAddEventListener(document, 'mouseover', doRainbowAnchor);
xAddEventListener(document, 'mouseout', stopRainbowAnchor);
} else {
xAddEventListener(document, 'mouseover', Mozilla_doRainbowAnchor);
xAddEventListener(document, 'mouseout', Mozilla_stopRainbowAnchor);
}
/*
if (document.all) {
document.onmouseover = doRainbowAnchor;
document.onmouseout = stopRainbowAnchor;
}
else if (document.getElementById) {
document.captureEvents(Event.MOUSEOVER | Event.MOUSEOUT);
document.onmouseover = Mozilla_doRainbowAnchor;
document.onmouseout = Mozilla_stopRainbowAnchor;
}
*/
//=============================================================================
// doRainbow
// This function begins to change a color.
//=============================================================================
function doRainbow(obj)
{
if (act == 0) {
act = 1;
if (obj)
objActive = obj;
else
objActive = event.srcElement;
clrOrg = objActive.style.color;
TimerID = setInterval("ChangeColor()",100);
}
}
//=============================================================================
// stopRainbow
// This function stops to change a color.
//=============================================================================
function stopRainbow()
{
if (act) {
objActive.style.color = clrOrg;
clearInterval(TimerID);
act = 0;
}
}
//=============================================================================
// doRainbowAnchor
// This function begins to change a color. (of a anchor, automatically)
//=============================================================================
function doRainbowAnchor()
{
try {
if (act == 0) {
var obj = event.srcElement;
while (obj.tagName != 'A' && obj.tagName != 'BODY') {
obj = obj.parentElement;
if (obj.tagName == 'A' || obj.tagName == 'BODY')
break;
}
if (obj.tagName == 'A' && obj.href != '') {
objActive = obj;
act = 1;
clrOrg = objActive.style.color;
TimerID = setInterval("ChangeColor()",100);
}
}
} catch(e) {
}
}
//=============================================================================
// stopRainbowAnchor
// This function stops to change a color. (of a anchor, automatically)
//=============================================================================
function stopRainbowAnchor()
{
if (act) {
if (objActive.tagName == 'A') {
objActive.style.color = clrOrg;
clearInterval(TimerID);
act = 0;
}
}
}
//=============================================================================
// Mozilla_doRainbowAnchor(for Netscape6 and Mozilla browser)
// This function begins to change a color. (of a anchor, automatically)
//=============================================================================
function Mozilla_doRainbowAnchor(evt)
{
var e = new xEvent(evt);
if (act == 0) {
obj = e.target;
while (obj.nodeName != 'A' && obj.nodeName != 'BODY') {
obj = obj.parentNode;
if(typeof(obj)=='undefined'||!obj) return;
if (obj.nodeName == 'A' || obj.nodeName == 'BODY') break;
}
if (obj.nodeName == 'A' && obj.href != '') {
objActive = obj;
act = 1;
clrOrg = obj.style.color;
TimerID = setInterval("ChangeColor()",100);
}
}
}
//=============================================================================
// Mozilla_stopRainbowAnchor(for Netscape6 and Mozilla browser)
// This function stops to change a color. (of a anchor, automatically)
//=============================================================================
function Mozilla_stopRainbowAnchor(e)
{
if (act) {
if (objActive.nodeName == 'A') {
objActive.style.color = clrOrg;
clearInterval(TimerID);
act = 0;
}
}
}
//=============================================================================
// Change Color
// This function changes a color actually.
//=============================================================================
function ChangeColor()
{
objActive.style.color = makeColor();
}
//=============================================================================
// makeColor
// This function makes rainbow colors.
//=============================================================================
function makeColor()
{
// Don't you think Color Gamut to look like Rainbow?
// HSVtoRGB
if (elmS == 0) {
elmR = elmV; elmG = elmV; elmB = elmV;
}
else {
t1 = elmV;
t2 = (255 - elmS) * elmV / 255;
t3 = elmH % 60;
t3 = (t1 - t2) * t3 / 60;
if (elmH < 60) {
elmR = t1; elmB = t2; elmG = t2 + t3;
}
else if (elmH < 120) {
elmG = t1; elmB = t2; elmR = t1 - t3;
}
else if (elmH < 180) {
elmG = t1; elmR = t2; elmB = t2 + t3;
}
else if (elmH < 240) {
elmB = t1; elmR = t2; elmG = t1 - t3;
}
else if (elmH < 300) {
elmB = t1; elmG = t2; elmR = t2 + t3;
}
else if (elmH < 360) {
elmR = t1; elmG = t2; elmB = t1 - t3;
}
else {
elmR = 0; elmG = 0; elmB = 0;
}
}
elmR = Math.floor(elmR).toString(16);
elmG = Math.floor(elmG).toString(16);
elmB = Math.floor(elmB).toString(16);
if (elmR.length == 1) elmR = "0" + elmR;
if (elmG.length == 1) elmG = "0" + elmG;
if (elmB.length == 1) elmB = "0" + elmB;
elmH = elmH + rate;
if (elmH >= 360)
elmH = 0;
return '#' + elmR + elmG + elmB;
}

View file

@ -0,0 +1,19 @@
<?php
if(!defined("__ZBXE__")) exit();
/**
* @file rainbow.addon.php
* @author zero (zero@nzeo.com)
* @brief Rainbow link addon
*
* 링크가 걸린 텍스트에 마우스 오버를 하면 무지개색으로 변하게 하는 애드온입니다.
* rainbow.js 파일만 추가하는 것으로 끝납니다.
* rainbow.js는 http://www.dynamicdrive.com에서 제작하였으며 저작권을 가지고 있습니다.
* before_display_content 에서만 요청이 됩니다.
**/
if(Context::get('module')=='admin' || $called_position != 'before_module_init') return;
// Context::addJsFile()을 이용하면 끝
Context::addJsFile($addon_path.'js/rainbow.js');
?>

View file

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<addon version="0.1">
<title xml:lang="ko">스팸필터 애드온</title>
<title xml:lang="zh-CN">垃圾过滤</title>
<title xml:lang="en">Addon for filtering spam</title>
<title xml:lang="es">Adición Filtros para artículos no deseado</title>
<title xml:lang="jp">スパムフィルター</title>
<author email_address="zero@zeroboard.com" link="http://www.zeroboard.com" date="2007. 2. 28">
<name xml:lang="ko">제로</name>
<name xml:lang="en">zero</name>
<name xml:lang="zh-CN">zero</name>
<name xml:lang="es">zero</name>
<name xml:lang="jp">Zero</name>
<description xml:lang="ko">
SpamFilter 모듈을 이용하여 글/코멘트/트랙백 등록 이전에 스팸 필터링 및 도배 방지를 합니다.
자세한 설정은 &quot; 스팸필터 모듈&quot; 에서 해주세요.
</description>
<description xml:lang="zh-CN">
利用垃圾过滤模块在发表主题/评论/引用之前进行垃圾过滤。
请在&quot; 垃圾过滤模块&quot;中详细设置。
</description>
<description xml:lang="en">
This addon filters spam and prevents users from cluttering before saving entries, comments, or trackbacks to database by setting SpamFilter module.
You can set up more details on &quot;Spam Filter module&quot;.
</description>
<description xml:lang="es">
Utiliza SpamFilter para bloquear los artículos/ commentarios/ trackback no deseado.
Para configurar haga clic &quot; Configurar la modula SpamFilter.&quot;..º
</description>
<description xml:lang="jp">
SpamFilterモジュールを利用して書き込み・コメント・トラックバックが登録される前にフィルタリングを行います。更に連続書き込みやロボットによる自動書き込みなどを防ぐことができます。詳細な設定は &quot; スパムフィルターモジュール &quot; で行ってください。
</description>
</author>
</addon>

View file

@ -0,0 +1,133 @@
<?php
if(!defined("__ZBXE__")) exit();
/**
* @file spamfilter.addon.php
* @author zero (zero@nzeo.com)
* @brief 스팸필터링 애드온
*
* 스팸필터 애드온은 SpamFilter 모듈을 이용합니다.
* /코멘트/트랙백 등록 이전에만 실행이 됩니다.
**/
// called_position가 before_module_proc 일때만 실행
if($called_position != 'before_module_proc') return;
// 이 애드온이 동작할 대상 (이 부분은 특별히 정해진 규약이 없다)
$effecived_target_act = array(
'procBoardInsertDocument', 'procBoardInsertComment',
'procBlogInsertComment',
'trackback',
);
// spam filter모듈이 적용될 module+act를 체크
if(!in_array($this->act, $effecived_target_act)) return;
// 각 모듈별 act에 대해서도 피해갈 부분이 있으면 피해감
switch($this->act) {
// 게시물 작성시 신규 등록이 아니면 패스~
case 'procBoardInsertDocument' :
// document module의 model 객체 생성
$oDocumentModel = &getModel('document');
// 이미 존재하는 글인지 체크
$document_srl = Context::get('document_srl');
$oDocument = $oDocumentModel->getDocument($document_srl);
// 이미 존재하는 글이라면 return
if($oDocument->isExists()) return;
break;
// 댓글 작성시 신규 등록이 아니면 패스~
case 'procBoardInsertComment' :
case 'procBlogInsertComment' :
$comment_srl = Context::get('comment_srl');
$oCommentModel = &getModel('comment');
// 이미 존재하는 댓글인지 체크
$comment = $oCommentModel->getComment($comment_srl);
if($comment->comment_srl == $comment_srl) return;
break;
}
// 현재 모듈의 관리자이거나 그에 준하는 manager권한이면 그냥 패스
if($this->grant->is_admin || $this->grant->manager) return;
// 현 접속자의 ip address를 구함
$ipaddress = $_SERVER['REMOTE_ADDR'];
// spamfilter 모듈 객체 생성
$oSpamFilterController = &getController('spamfilter');
$oSpamFilterModel = &getModel('spamfilter');
// 스팸필터 기본 설정 출력
$config = $oSpamFilterModel->getConfig();
// 스팸 간격을 체크하는 변수
$interval = $config->interval?$config->interval:60;
// 스팸 간격내에 limit_count이상 작성을 시도하면 해당 ip를 금지 시킴
$limit_count = $config->limit_count?$config->limit_count:5;
// 트랙백의 경우 한 글에 하나의 ip에서만 트랙백을 허용함
$check_trackback = $config->check_trackback=='Y'?true:false;
// 스팸 IP에 등록되어 있는지 체크하여 등록되어 있으면 return
$is_denied = $oSpamFilterModel->isDeniedIP($ipaddress);
if($is_denied) {
$output = new Object(-1, 'msg_alert_registered_denied_ip');
$this->stop_proc = true;
return;
}
// act==procReceiveTrackback (트랙백)일때 check_trackback==true이면 검사
if($this->act=='procReceiveTrackback' && $check_trackback){
$oTrackbackModel = &getModel('trackback');
$document_srl = Context::get('document_srl');
$count = $oTrackbackModel->getTrackbackCountByIPAddress($document_srl, $ipaddress);
if($count>0) {
$output = Object(-1, 'msg_alert_trackback_denied');
$this->stop_proc = true;
return;
}
}
// 정해진 시간내에 글 작성 시도를 하였는지 체크
$count = $oSpamFilterModel->getLogCount($interval, $ipaddress);
// 정해진 시간내에 정해진 글의 수를 초과시 스팸 IP로 등록시킴
if($count>=$limit_count) {
$oSpamFilterController->insertIP($ipaddress);
$output = new Object(-1, 'msg_alert_registered_denied_ip');
$this->stop_proc = true;
return;
// 제한 글수까지는 아니지만 정해진 시간내에 글 작성을 계속 할때
} elseif($count) {
$message = sprintf(Context::getLang('msg_alert_limited_by_config'), $interval);
$output = new Object(-1, $message);
$this->stop_proc = true;
}
// 금지 단어 체크를 위해서 몇가지 지정된 변수들을 한데 묶음
$check_vars = implode("\n",get_object_vars(Context::getRequestVars()));
// 금지 단어를 이용하여 본문 내용을 체크
$denied_word_list = $oSpamFilterModel->getDeniedWordList();
$denied_word_count = count($denied_word_list);
if($denied_word_count>0) {
for($i=0;$i<$denied_word_count;$i++) {
$word = preg_quote($denied_word_list[$i]->word,'/');
if(preg_match('/'.$word.'/i', $check_vars)) {
$message = sprintf(Context::getLang('msg_alert_denied_word'), $word);
$output = new Object(-1, $message);
$this->stop_proc = true;
return;
}
}
}
// 로그를 남김
$oSpamFilterController->insertLog();
?>

View file

@ -0,0 +1,923 @@
<?php
/**
* @class Context
* @author zero (zero@nzeo.com)
* @brief Request Argument/환경변수등의 모든 Context를 관리
*
* Context 클래스는 Context::methodname() 처럼 쉽게 사용하기 위해 만들어진 객체를 받아서
* 호출하는 구조를 위해 이중 method 구조를 가지고 있다.
* php5에서 static variables를 사용하게 된다면 불필요한 구조를 제거할 있다.
* php5 쓰고 싶당.. .
**/
class Context {
var $request_method = 'GET'; ///< @brief GET/POST/XMLRPC 중 어떤 방식으로 요청이 왔는지에 대한 값이 세팅. GET/POST/XML 3가지가 있음
var $response_method = ''; ///< @brief HTML/XMLRPC 중 어떤 방식으로 결과를 출력할지 결정. (강제 지정전까지는 request_method를 따름)
var $context = NULL; ///< @brief request parameter 및 각종 환경 변수등을 정리하여 담을 변수
var $db_info = NULL; ///< @brief DB 정보
var $js_files = array(); ///< @brief display시에 사용하게 되는 js files의 목록
var $css_files = array(); ///< @brief display시에 사용하게 되는 css files의 목록
var $html_header = NULL; ///< @brief display시에 사용하게 되는 <head>..</head>내의 스크립트코드
var $html_footer = NULL; ///< @brief display시에 사용하게 되는 </body> 바로 앞에 추가될 코드
var $path = ''; ///< zbxe의 경로
/**
* @brief 언어 정보
*
* 기본으로 ko. HTTP_USER_AGENT나 사용자의 직접 세팅(쿠키이용)등을 통해 변경됨
**/
var $lang_type = ''; ///< 언어 종류
var $lang = NULL; ///< 언어 데이터를 담고 있는 변수
var $loaded_lang_files = array(); ///< 로딩된 언어파일의 목록 (재로딩을 피하기 위함)
var $site_title = ''; ///< @brief 현 사이트의 browser title. Context::setBrowserTitle() 로 변경 가능
var $get_vars = NULL; ///< @brief form이나 get으로 요청이 들어온 변수만 별도로 관리
var $is_uploaded = false; ///< @brief 첨부파일이 업로드 된 요청이였는지에 대한 체크 플래그
/**
* @brief Context 객체를 GLOBALS 변수에 생성
*
* Context는 어디서든 객체 선언없이 사용하기 위해서 static 하게 사용\n
* php5라면 GLOBALS가 아닌 static으로 처리 가능
**/
function &getInstance() {
if(!$GLOBALS['__ContextInstance__']) $GLOBALS['__ContextInstance__'] = new Context();
return $GLOBALS['__ContextInstance__'];
}
/**
* @brief DB정보, Request Argument등을 세팅
*
* Context::init() 한번만 호출되어야 하며 init()시에
* Request Argument, DB/언어/세션정보등의 모든 정보를 세팅한다
**/
function init() {
// context 변수를 $GLOBALS의 변수로 지정
$this->context = &$GLOBALS['__Context__'];
$this->context->lang = &$GLOBALS['lang'];
$this->context->_COOKIE = $_COOKIE;
// 기본적인 DB정보 세팅
$this->_loadDBInfo();
// 쿠키로 설정된 언어타입 가져오기
if($_COOKIE['lang_type']) $this->lang_type = $_COOKIE['lang_type'];
else $this->lang_type = $this->db_info->lang_type;
// 등록된 기본 언어파일 찾기
$lang_files = FileHandler::readDir('./common/lang');
$accept_lang = strtolower($_SERVER['HTTP_ACCEPT_LANGUAGE']);
foreach($lang_files as $key => $val) {
list($lang_prefix) = explode('.',$val);
$lang_supported[] = $lang_prefix;
if(!$this->lang_type && ereg($lang_prefix, strtolower($_SERVER['HTTP_ACCEPT_LANGUAGE']))) {
$this->lang_type = $lang_prefix;
setcookie('lang_type', $this->lang_type, time()+60*60*24*365, '/');
}
}
if(!in_array($this->lang_type, $lang_supported)) $this->lang_type = $this->db_info->lang_type;
if(!$this->lang_type) $this->lang_type = "en";
Context::set('lang_supported', $lang_supported);
$this->setLangType($this->lang_type);
// 기본 언어파일 로드
$this->lang = &$GLOBALS['lang'];
$this->_loadLang("./common/lang/");
// Request Method 설정
$this->_setRequestMethod();
// Request Argument 설정
$this->_setXmlRpcArgument();
$this->_setRequestArgument();
$this->_setUploadedArgument();
// 인증관련 데이터를 Context에 설정
$oMember = getModel('member');
if($oMember->isLogged()) {
$this->_set('is_logged', true);
$this->_set('logged_info', $_SESSION['logged_info']);
} else {
$this->_set('is_logged', false);
$this->_set('logged_info', NULL);
}
// rewrite 모듈사용 상태 체크
if(file_exists('./.htaccess')&&$this->db_info->use_rewrite == 'Y') $this->allow_rewrite = true;
else $this->allow_rewrite = false;
// 상대 경로 설정
$this->path = $this->getRequestUri();
// rewrite module때문에 javascript에서 location.href 문제 해결을 위해 직접 실제 경로 설정
if($_SERVER['REQUEST_METHOD'] == 'GET') {
if($this->get_vars) {
foreach($this->get_vars as $key => $val) {
if(!$val) continue;
$url .= ($url?'&':'').$key.'='.$val;
}
Context::set('current_url',sprintf('%s?%s',$this->path, $url));
} else {
Context::set('current_url',$this->getUrl());
}
} else {
Context::set('current_url',$this->getRequestUri());
}
Context::set('request_uri',Context::getRequestUri());
}
/**
* @brief DB및 기타 자원들의 close
**/
function close() {
// DB close
$oDB = &DB::getInstance();
if(is_object($oDB)&&method_exists($oDB, 'close')) $oDB->close();
}
/**
* @brief DB 정보를 설정하고 DB Type과 DB 정보를 return
**/
function _loadDBInfo() {
if(!$this->isInstalled()) return;
// db 정보 설정
$db_config_file = $this->getConfigFile();
if(file_exists($db_config_file)) @include($db_config_file);
if(!$db_info->time_zone) $db_info->time_zone = date("O");
$this->_setDBInfo($db_info);
$GLOBALS['_time_zone'] = $db_info->time_zone;
}
/**
* @brief DB의 db_type을 return
**/
function getDBType() {
$oContext = &Context::getInstance();
return $oContext->_getDBType();
}
/**
* @brief DB의 db_type을 return
**/
function _getDBType() {
return $this->db_info->db_type;
}
/**
* @brief DB 정보가 담긴 object를 return
**/
function setDBInfo($db_info) {
$oContext = &Context::getInstance();
$oContext->_setDBInfo($db_info);
}
/**
* @brief DB 정보가 담긴 object를 return
**/
function _setDBInfo($db_info) {
$this->db_info = $db_info;
}
/**
* @brief DB 정보가 담긴 object를 return
**/
function getDBInfo() {
$oContext = &Context::getInstance();
return $oContext->_getDBInfo();
}
/**
* @brief DB 정보가 담긴 object를 return
**/
function _getDBInfo() {
return $this->db_info;
}
/**
* @brief 사이트 title setting
**/
function setBrowserTitle($site_title) {
if(!$site_title) return;
$oContext = &Context::getInstance();
$oContext->_setBrowserTitle($site_title);
}
/**
* @brief 사이트 title setting
**/
function _setBrowserTitle($site_title) {
$this->site_title = htmlspecialchars($site_title);
}
/**
* @brief 사이트 title return
**/
function getBrowserTitle() {
$oContext = &Context::getInstance();
return $oContext->_getBrowserTitle();
}
/**
* @brief 사이트 title return
**/
function _getBrowserTitle() {
return $this->site_title;
}
/**
* @brief 지정된 언어파일 로드
**/
function loadLang($path) {
$oContext = &Context::getInstance();
$oContext->_loadLang($path);
}
/**
* @brief 지정된 언어파일 로드
*
* loaded_lang_files 변수를 이용하여 한번 로드된 파일을 다시 로드하지 않음
**/
function _loadLang($path) {
global $lang;
if(substr($path,-1)!='/') $path .= '/';
$filename = sprintf('%s%s.lang.php', $path, $this->lang_type);
if(!file_exists($filename)) $filename = sprintf('%s%s.lang.php', $path, 'ko');
if(!file_exists($filename)) return;
if(!is_array($this->loaded_lang_files)) $this->loaded_lang_files = array();
if(in_array($filename, $this->loaded_lang_files)) return;
$this->loaded_lang_files[] = $filename;
include($filename);
}
/**
* @brief lang_type을 세팅 (기본 ko)
**/
function setLangType($lang_type = 'ko') {
$oContext = &Context::getInstance();
$oContext->_setLangType($lang_type);
}
/**
* @brief lang_type을 세팅 (기본 ko)
**/
function _setLangType($lang_type = 'ko') {
$this->lang_type = $lang_type;
$this->_set('lang_type',$lang_type);
}
/**
* @brief lang_type을 return
**/
function getLangType() {
$oContext = &Context::getInstance();
return $oContext->_getLangType();
}
/**
* @brief lang_type을 return
**/
function _getLangType() {
return $this->lang_type;
}
/**
* @brief code에 해당하는 문자열을 return
*
* 만약 code에 해당하는 문자열이 없다면 code를 그대로 리턴
**/
function getLang($code) {
if(!$code) return;
if($GLOBALS['lang']->{$code}) return $GLOBALS['lang']->{$code};
return $code;
}
/**
* @brief 직접 lang 변수에 데이터를 추가
**/
function setLang($code, $val) {
$GLOBALS['lang']->{$code} = $val;
}
/**
* @brief object내의 variables의 문자열을 utf8로 변경
**/
function convertEncoding($source_obj) {
$charset_list = array(
'UTF-8', 'EUC-KR', 'CP949', 'ISO-8859-1', 'EUC-JP', 'SHIFT_JIS', 'CP932',
'EUC-CN', 'HZ', 'GBK', 'GB18030', 'EUC-TW', 'BIG5', 'CP950', 'BIG5-HKSCS',
'ISO-2022-CN', 'ISO-2022-CN-EXT', 'ISO-2022-JP', 'ISO-2022-JP-2', 'ISO-2022-JP-1',
'ISO-8859-6', 'ISO-8859-8', 'JOHAB', 'ISO-2022-KR', 'CP1255', 'CP1256', 'CP862',
'ASCII', 'ISO-8859-1', 'ISO-8850-2', 'ISO-8850-3', 'ISO-8850-4', 'ISO-8850-5',
'ISO-8850-7', 'ISO-8850-9', 'ISO-8850-10', 'ISO-8850-13', 'ISO-8850-14',
'ISO-8850-15', 'ISO-8850-16', 'CP1250', 'CP1251', 'CP1252', 'CP1253', 'CP1254',
'CP1257', 'CP850', 'CP866',
);
$obj = clone($source_obj);
for($i=0;$i<count($charset_list);$i++) {
$charset = $charset_list[$i];
$flag = true;
foreach($obj as $key=>$val) {
if(!$val) continue;
if($val && !iconv($charset,'UTF-8',$val)) $flag = false;
}
if($flag == true) {
foreach($obj as $key => $val) $obj->{$key} = iconv($charset,'UTF-8',$val);
return $obj;
}
}
}
/**
* @brief response method를 강제로 지정 (기본으로는 request method를 이용함)
*
* method의 종류에는 HTML/ TEXT/ XMLRPC가 있음
**/
function setResponseMethod($method = "HTML") {
$oContext = &Context::getInstance();
return $oContext->_setResponseMethod($method);
}
function _setResponseMethod($method = "HTML") {
$this->response_method = $method;
}
/**
* @brief response method 값을 return
*
* method의 종류에는 HTML/ TEXT/ XMLRPC가 있음
* 별도로 response method를 지정하지 않았다면 request method로 판단하여 결과 return
**/
function getResponseMethod() {
$oContext = &Context::getInstance();
return $oContext->_getResponseMethod();
}
function _getResponseMethod() {
if($this->response_method) return $this->response_method;
if($this->_getRequestMethod()=="XMLRPC") return "XMLRPC";
return "HTML";
}
/**
* @brief request method가 어떤것인지 판단하여 저장 (GET/POST/XMLRPC)
**/
function setRequestMethod($type) {
$oContext = &Context::getInstance();
$oContext->_setRequestMethod($type);
}
/**
* @brief request method가 어떤것인지 판단하여 저장 (GET/POST/XMLRPC)
**/
function _setRequestMethod($type = '') {
if($type) return $this->request_method = $type;
if($GLOBALS['HTTP_RAW_POST_DATA']) return $this->request_method = "XMLRPC";
$this->request_method = $_SERVER['REQUEST_METHOD'];
}
/**
* @brief GET/POST방식일 경우 처리
**/
function _setRequestArgument() {
if($this->_getRequestMethod() == 'XMLRPC') return;
if(!count($_REQUEST)) return;
foreach($_REQUEST as $key => $val) {
if($key == "page" || substr($key,-3)=="srl") $val = (int)$val;
if(is_array($val)) {
for($i=0;$i<count($val);$i++) {
if(get_magic_quotes_gpc()) $val[$i] = stripslashes($val[$i]);
$val[$i] = trim($val[$i]);
}
} else {
if(get_magic_quotes_gpc()) $val = stripslashes($val);
$val = trim($val);
}
if(!$val) continue;
if($this->_getRequestMethod()=='GET'&&$_GET[$key]) $set_to_vars = true;
elseif($this->_getRequestMethod()=='POST'&&$_POST[$key]) $set_to_vars = true;
else $set_to_vars = false;
$this->_set($key, $val, $set_to_vars);
}
}
/**
* @brief XML RPC일때
**/
function _setXmlRpcArgument() {
if($this->_getRequestMethod() != 'XMLRPC') return;
$oXml = new XmlParser();
$xml_obj = $oXml->parse();
$params = $xml_obj->methodcall->params;
unset($params->node_name);
unset($params->attrs);
if(!count($params)) return;
foreach($params as $key => $obj) {
$val = trim($obj->body);
$this->_set($key, $val, true);
}
}
/**
* @brief 업로드 되었을 경우 return true
**/
function isUploaded() {
$oContext = &Context::getInstance();
return $oContext->_isUploaded();
}
/**
* @brief 업로드 되었을 경우 return true
**/
function _isUploaded() {
return $this->is_uploaded;
}
/**
* @brief 업로드된 파일이 있을 경우도 역시 context에 통합 처리 ( 정상적인 업로드인지 체크)
**/
function _setUploadedArgument() {
if($this->_getRequestMethod() != 'POST') return;
if(!eregi("^multipart\/form-data", $_SERVER['CONTENT_TYPE'])) return;
if(!$_FILES) return;
foreach($_FILES as $key => $val) {
$tmp_name = $val['tmp_name'];
if(!$tmp_name || !is_uploaded_file($tmp_name)) continue;
$this->_set($key, $val, true);
$this->is_uploaded = true;
}
}
/**
* @brief Request Method값을 return (GET/POST/XMLRPC);
**/
function getRequestMethod() {
$oContext = &Context::getInstance();
return $oContext->_getRequestMethod();
}
/**
* @brief Request Method값을 return (GET/POST/XMLRPC);
**/
function _getRequestMethod() {
return $this->request_method;
}
/**
* @brief 요청받은 url에 args_list를 적용하여 return
**/
function getUrl($num_args=0, $args_list=array()) {
$oContext = &Context::getInstance();
return $oContext->_getUrl($num_args, $args_list);
}
/**
* @brief 요청받은 url에 args_list를 적용하여 return
**/
function _getUrl($num_args=0, $args_list=array()) {
if(!$this->get_vars || $args_list[0]=='') {
$get_vars = null;
if($args_list[0]=='') {
array_shift($args_list);
$num_args = count($args_list);
}
} else {
$get_vars = get_object_vars($this->get_vars);
}
for($i=0;$i<$num_args;$i=$i+2) {
$key = $args_list[$i];
$val = trim($args_list[$i+1]);
if(!$val) unset($get_vars[$key]);
else $get_vars[$key] = $val;
}
$var_count = count($get_vars);
if(!$var_count) return '';
// rewrite모듈을 사용하고 인자의 값이 4개 이하일 경우
if($this->allow_rewrite && $var_count < 4) {
$var_keys = array_keys($get_vars);
if($var_count == 1) {
if($var_keys[0]=='mid') return $this->path.$get_vars['mid'];
elseif($var_keys[0]=='document_srl') return $this->path.$get_vars['document_srl'];
} elseif($var_count == 2) {
asort($var_keys);
$target = implode('.',$var_keys);
if($target=='act.mid' && !ereg('([A-Z]+)',$get_vars['act'])) return sprintf('%s%s/%s',$this->path,$get_vars['mid'],$get_vars['act']);
elseif($target=='document_srl.mid') return sprintf('%s%s/%s',$this->path,$get_vars['mid'],$get_vars['document_srl']);
elseif($target=='act.document_srl') return sprintf('%s%s/%s',$this->path,$get_vars['document_srl'],$get_vars['act']);
elseif($target=='mid.page') return sprintf('%s%s/page/%s',$this->path,$get_vars['mid'],$get_vars['page']);
elseif($target=='category.mid') return sprintf('%s%s/category/%s',$this->path,$get_vars['mid'],$get_vars['category']);
} elseif($var_count == 3) {
asort($var_keys);
$target = implode('.',$var_keys);
if($target=='category.mid.page') {
return sprintf('%s%s/category/%s/page/%s',$this->path,$get_vars['mid'],$get_vars['category'],$get_vars['page']);
} elseif($target=='mid.search_keyword.search_target' && $get_vars['search_target']=='tag') {
return sprintf('%s%s/tag/%s',$this->path,$get_vars['mid'],str_replace(' ','-',$get_vars['search_keyword']));
} elseif($target=='mid.search_keyword.search_target' && $get_vars['search_target']=='regdate') {
if(strlen($get_vars['search_keyword'])==8) return sprintf('%s%s/%04d/%02d/%02d',$this->path,$get_vars['mid'],substr($get_vars['search_keyword'],0,4),substr($get_vars['search_keyword'],4,2),substr($get_vars['search_keyword'],6,2));
elseif(strlen($get_vars['search_keyword'])==6) return sprintf('%s%s/%04d/%02d',$this->path,$get_vars['mid'],substr($get_vars['search_keyword'],0,4),substr($get_vars['search_keyword'],4,2));
}
}
}
// rewrite 모듈을 사용하지 않고 인자의 값이 2개 이상이거나 rewrite모듈을 위한 인자로 적당하지 않을 경우
foreach($get_vars as $key => $val) {
if(!$val) continue;
$url .= ($url?'&':'').$key.'='.$val;
}
return $this->path.'?'.htmlspecialchars($url);
}
/**
* @brief 요청이 들어온 URL에서 argument를 제거하여 return
**/
function getRequestUri() {
$hostname = $_SERVER['HTTP_HOST'];
$port = $_SERVER['SERVER_PORT'];
if($port!=80) $hostname .= ":{$port}";
$path = str_replace('index.php','',$_SERVER['SCRIPT_NAME']);
return sprintf("http://%s%s",$hostname,$path);
}
/**
* @brief key/val로 context vars 세팅
**/
function set($key, $val, $set_to_get_vars = false) {
$oContext = &Context::getInstance();
$oContext->_set($key, $val, $set_to_get_vars);
}
/**
* @brief key/val로 context vars 세팅
**/
function _set($key, $val, $set_to_get_vars = false) {
$this->context->{$key} = $val;
if($set_to_get_vars || $this->get_vars->{$key}) $this->get_vars->{$key} = $val;
}
/**
* @brief key값에 해당하는 값을 return
**/
function get($key) {
$oContext = &Context::getInstance();
return $oContext->_get($key);
}
/**
* @brief key값에 해당하는 값을 return
**/
function _get($key) {
return $this->context->{$key};
}
/**
* @brief 받고자 하는 변수만 object에 입력하여 받음
*
* key1, key2, key3 .. 등의 인자를 주어 여러개의 변수를 object vars로 세팅하여 받을 있음
**/
function gets() {
$num_args = func_num_args();
if($num_args<1) return;
$args_list = func_get_args();
$oContext = &Context::getInstance();
return $oContext->_gets($num_args, $args_list);
}
/**
* @brief 받고자 하는 변수만 object에 입력하여 받음
*
* key1, key2, key3 .. 등의 인자를 주어 여러개의 변수를 object vars로 세팅하여 받을 있음
**/
function _gets($num_args, $args_list) {
for($i=0;$i<$num_args;$i++) {
$args = $args_list[$i];
$output->{$args} = $this->_get($args);
}
return $output;
}
/**
* @brief 모든 데이터를 return
**/
function getAll() {
$oContext = &Context::getInstance();
return $oContext->_getAll();
}
/**
* @brief 모든 데이터를 return
**/
function _getAll() {
return $this->context;
}
/**
* @brief GET/POST/XMLRPC에서 넘어온 변수값을 return
**/
function getRequestVars() {
$oContext = &Context::getInstance();
return $oContext->_getRequestVars();
}
/**
* @brief GET/POST/XMLRPC에서 넘어온 변수값을 return
**/
function _getRequestVars() {
return clone($this->get_vars);
}
/**
* @brief js file을 추가
**/
function addJsFile($file) {
$oContext = &Context::getInstance();
return $oContext->_addJsFile($file);
}
/**
* @brief js file을 추가
**/
function _addJsFile($file) {
if(in_array($file, $this->js_files)) return;
$this->js_files[] = $file;
}
/**
* @brief js file 목록을 return
**/
function getJsFile() {
$oContext = &Context::getInstance();
return $oContext->_getJsFile();
}
/**
* @brief js file 목록을 return
**/
function _getJsFile() {
return $this->js_files;
}
/**
* @brief CSS file 추가
**/
function addCSSFile($file) {
$oContext = &Context::getInstance();
return $oContext->_addCSSFile($file);
}
/**
* @brief CSS file 추가
**/
function _addCSSFile($file) {
if(in_array($file, $this->css_files)) return;
$this->css_files[] = $file;
}
/**
* @brief CSS file 목록 return
**/
function getCSSFile() {
$oContext = &Context::getInstance();
return $oContext->_getCSSFile();
}
/**
* @brief CSS file 목록 return
**/
function _getCSSFile() {
return $this->css_files;
}
/**
* @brief HtmlHeader 추가
**/
function addHtmlHeader($header) {
$oContext = &Context::getInstance();
return $oContext->_addHtmlHeader($header);
}
/**
* @brief HtmlHeader 추가
**/
function _addHtmlHeader($header) {
$this->html_header .= "\n".$header;
}
/**
* @brief HtmlHeader return
**/
function getHtmlHeader() {
$oContext = &Context::getInstance();
return $oContext->_getHtmlHeader();
}
/**
* @brief HtmlHeader return
**/
function _getHtmlHeader() {
return $this->html_header;
}
/**
* @brief HtmlFooter 추가
**/
function addHtmlFooter($footer) {
$oContext = &Context::getInstance();
return $oContext->_addHtmlFooter($footer);
}
/**
* @brief HtmlFooter 추가
**/
function _addHtmlFooter ($footer) {
$this->html_footer .= ($this->Htmlfooter?"\n":"").$footer;
}
/**
* @brief HtmlFooter return
**/
function getHtmlFooter() {
$oContext = &Context::getInstance();
return $oContext->_getHtmlFooter();
}
/**
* @brief HtmlFooter return
**/
function _getHtmlFooter() {
return $this->html_footer;
}
/**
* @brief db설정내용이 저장되어 있는 config file의 path를 return
**/
function getConfigFile() {
return "./files/config/db.config.php";
}
/**
* @brief 설치가 되어 있는지에 대한 체크
*
* 단순히 db config 파일의 존재 유무로 설치 여부를 체크한다
**/
function isInstalled() {
return file_exists(Context::getConfigFile());
}
/**
* @brief 내용의 위젯이나 기타 기능에 대한 code를 실제 code로 변경
**/
function transContent($content) {
// 위젯 코드 변경
$content = preg_replace_callback('!<img([^\>]*)widget=([^\>]*?)\>!is', array($this,'_transWidget'), $content);
// 메타 파일 변경
$content = preg_replace_callback('!<\!\-\-Meta:([^\-]*?)\-\->!is', array($this,'_transMeta'), $content);
// 에디터 컴포넌트를 찾아서 결과 코드로 변환
$content = preg_replace_callback('!<div([^\>]*)editor_component=([^\>]*)>(.*?)\<\/div\>!is', array($this,'_transEditorComponent'), $content);
$content = preg_replace_callback('!<img([^\>]*)editor_component=([^\>]*?)\>!is', array($this,'_transEditorComponent'), $content);
// body 내의 <style ..></style>를 header로 이동
$content = preg_replace_callback('!<style(.*?)<\/style>!is', array($this,'_moveStyleToHeader'), $content);
// <br> 코드 변환
$content = preg_replace('/<br([^>\/]*)(\/>|>)/i','<br$1 />', $content);
// 몇가지 대문자 태그를 소문자로 변경
//$content = preg_replace_callback('!<(\/){0,1}([A-Z]+)([^>]*?)>!s',array($this,'_transTagToLowerCase'), $content);
// <img ...> 코드를 <img ... /> 코드로 변환
$content = preg_replace('/<img(.*?)(\/){0,1}>/i','<img$1 />', $content);
// blogapi tool에서 삽입된 코드 삭제
//$content = str_replace('atomicselection="true"','',$content);
return $content;
}
/**
* @brief IE위지윅에디터에서 태그가 대문자로 사용되기에 이를 소문자로 치환
**/
function _transTagToLowerCase($matches) {
return sprintf('<%s%s%s>', $matches[1], strtolower($matches[2]), $matches[3]);
}
/**
* @brief <!--Meta:파일이름.(css|js)--> 변경
**/
function _transMeta($matches) {
if(eregi('\.css$', $matches[1])) $this->addCSSFile($matches[1]);
elseif(eregi('\.js$', $matches[1])) $this->addJSFile($matches[1]);
}
/**
* @brief <body>내의 <style태그를 header로 이동
**/
function _moveStyleToHeader($matches) {
$this->addHtmlHeader($matches[0]);
return '';
}
/**
* @brief 내용의 에디터 컴포넌트 코드를 변환
**/
function _fixQuotation($matches) {
$key = $matches[1];
$val = $matches[2];
if(substr($val,0,1)!='"') $val = '"'.$val.'"';
return sprintf('%s=%s', $key, $val);
}
function _transEditorComponent($matches) {
// IE에서는 태그의 특성중에서 " 를 빼어 버리는 경우가 있기에 정규표현식으로 추가해줌
$buff = $matches[0];
$buff = preg_replace_callback('/([^=^"^ ]*)=([^ ]*)/i', array($this, _fixQuotation), $buff);
$buff = str_replace("&","&amp;",$buff);
// 위젯에서 생성된 코드 (img, div태그내에 editor_widget코드 존재)의 parameter를 추출
$oXmlParser = new XmlParser();
$xml_doc = $oXmlParser->parse($buff);
if($xml_doc->div) $xml_doc = $xml_doc->div;
$xml_doc->body = $matches[3];
// attribute가 없으면 return
$editor_component = $xml_doc->attrs->editor_component;
if(!$editor_component) return $matches[0];
// component::transHTML() 을 이용하여 변환된 코드를 받음
$oEditorModel = &getModel('editor');
$oComponent = &$oEditorModel->getComponentObject($editor_component, 0);
if(!is_object($oComponent)||!method_exists($oComponent, 'transHTML')) return $matches[0];
return $oComponent->transHTML($xml_doc);
}
/**
* @brief 위젯 코드를 실제 php코드로 변경
**/
function _transWidget($matches) {
// IE에서는 태그의 특성중에서 " 를 빼어 버리는 경우가 있기에 정규표현식으로 추가해줌
$buff = $matches[0];
$buff = preg_replace('/([^=^"^ ]*)=([^"])([^=^ ]*)/i', '$1="$2$3"', $buff);
$buff = str_replace("&","&amp;",$buff);
$oXmlParser = new XmlParser();
$xml_doc = $oXmlParser->parse(trim($buff));
if($xml_doc->img) $vars = $xml_doc->img->attrs;
else $vars = $xml_doc->attrs;
if(!$vars->widget) return "";
// 캐시 체크
$widget_sequence = $vars->widget_sequence;
$widget_cache = $vars->widget_cache;
if($widget_cache && $widget_sequence) {
$output = WidgetHandler::getCache($widget_sequence, $widget_cache);
if($output) return $output;
}
// 위젯의 이름을 구함
$widget = $vars->widget;
unset($vars->widget);
return WidgetHandler::execute($widget, $vars);
}
}
?>

369
classes/db/DB.class.php Normal file
View file

@ -0,0 +1,369 @@
<?php
/**
* @class DB
* @author zero (zero@nzeo.com)
* @brief DB* 상위 클래스
* @version 0.1
*
* 제로보드의 DB 사용은 xml을 이용하여 이루어짐을 원칙으로 한다.
* xml의 종류에는 query xml, schema xml이 있다.
* query xml의 경우 DB::executeQuery() method를 이용하여 xml파일을 php code로 compile한 후에 실행이 된다.
* query xml은 고유한 query id를 가지며 생성은 module에서 이루어진다.
*
* queryid = 모듈.쿼리명
**/
class DB {
var $cond_operation = array( ///< 조건문에서 조건을 등호로 표시하는 변수
'equal' => '=',
'more' => '>=',
'excess' => '>',
'less' => '<=',
'below' => '<',
'notequal' => '<>',
'notnull' => 'is not null',
'null' => 'is null',
);
var $fd = NULL; ///< connector resource or file description
var $result = NULL; ///< result
var $errno = 0; ///< 에러 발생시 에러 코드 (0이면 에러가 없다고 정의)
var $errstr = ''; ///< 에러 발생시 에러 메세지
var $query = ''; ///< 가장 최근에 수행된 query string
var $transaction_started = false; ///< 트랙잭션 처리 flag
var $is_connected = false; ///< DB에 접속이 되었는지에 대한 flag
var $supported_list = array(); ///< 지원하는 DB의 종류, classes/DB/DB***.class.php 를 이용하여 동적으로 작성됨
var $cache_file = './files/cache/queries/'; ///< query cache파일의 위치
/**
* @brief DB를 상속받는 특정 db type의 instance를 생성 return
**/
function &getInstance($db_type = NULL) {
if(!$db_type) $db_type = Context::getDBType();
if(!$db_type) return new Object(-1, 'msg_db_not_setted');
if(!$GLOBALS['__DB__']) {
$class_name = sprintf("DB%s%s", strtoupper(substr($db_type,0,1)), strtolower(substr($db_type,1)));
$class_file = sprintf("./classes/db/%s.class.php", $class_name);
if(!file_exists($class_file)) new Object(-1, 'msg_db_not_setted');
require_once($class_file);
$eval_str = sprintf('$GLOBALS[\'__DB__\'] = new %s();', $class_name);
eval($eval_str);
}
return $GLOBALS['__DB__'];
}
/**
* @brief 지원 가능한 DB 목록을 return
**/
function getSupportedList() {
$oDB = new DB();
return $oDB->_getSupportedList();
}
/**
* @brief 지원 가능한 DB 목록을 return
**/
function _getSupportedList() {
$db_classes_path = "./classes/db/";
$filter = "/^DB([^\.]+)\.class\.php/i";
$supported_list = FileHandler::readDir($db_classes_path, $filter, true);
sort($supported_list);
// 구해진 클래스의 객체 생성후 isSupported method를 통해 지원 여부를 판단
for($i=0;$i<count($supported_list);$i++) {
$db_type = $supported_list[$i];
if(version_compare(phpversion(), '5.0') < 0 && eregi('pdo',$db_type)) continue;
$class_name = sprintf("DB%s%s", strtoupper(substr($db_type,0,1)), strtolower(substr($db_type,1)));
$class_file = sprintf("./classes/db/%s.class.php", $class_name);
if(!file_exists($class_file)) continue;
unset($oDB);
require_once($class_file);
$eval_str = sprintf('$oDB = new %s();', $class_name);
eval($eval_str);
if(!$oDB || !$oDB->isSupported()) continue;
$this->supported_list[] = $db_type;
}
return $this->supported_list;
}
/**
* @brief 지원하는 DB인지에 대한 check
**/
function isSupported($db_type) {
$supported_list = DB::getSupportedList();
return in_array($db_type, $supported_list);
}
/**
* @brief 접속되었는지 return
**/
function isConnected() {
return $this->is_connected?true:false;
}
/**
* @brief 로그 남김
**/
function actStart($query) {
$this->setError(0,'success');
$this->query = $query;
$this->act_start = getMicroTime();
}
function actFinish() {
if(!$this->query ) return;
$this->act_finish = getMicroTime();
$elapsed_time = $this->act_finish - $this->act_start;
$GLOBALS['__db_elapsed_time__'] += $elapsed_time;
$str = sprintf("\t%02d. %s (%0.6f sec)\n", ++$GLOBALS['__dbcnt'], $this->query, $elapsed_time);
if($this->isError()) {
$str .= sprintf("\t Query Failed : %d\n\t\t\t %s\n", $this->errno, $this->errstr);
if(__DEBUG_DB_OUTPUT__==1) {
$debug_file = "./files/_debug_db_query.php";
$buff = sprintf("%s\n",print_r($str,true));
if($display_line) $buff = "\n====================================\n".$buff."------------------------------------\n";
if(@!$fp = fopen($debug_file,"a")) return;
fwrite($fp, $buff);
fclose($fp);
}
} else {
$str .= "\t Query Success\n";
}
$GLOBALS['__db_queries__'] .= $str;
$this->query = null;
}
/**
* @brief 에러발생시 에러 메세지를 남기고 debug 모드일때는 GLOBALS 변수에 에러 로깅
**/
function setError($errno = 0, $errstr = 'success') {
$this->errno = $errno;
$this->errstr = $errstr;
}
/**
* @brief 오류가 발생하였는지 return
**/
function isError() {
return $this->errno===0?false:true;
}
/**
* @brief 에러결과를 Object 객체로 return
**/
function getError() {
return new Object($this->errno, $this->errstr);
}
/**
* @brief query xml 파일을 실행하여 결과를 return
*
* query_id = module.queryname
* query_id에 해당하는 xml문(or 캐싱파일) 찾아서 컴파일 실행
**/
function executeQuery($query_id, $args = NULL) {
if(!$query_id) return new Object(-1, 'msg_invalid_queryid');
list($module, $id) = explode('.',$query_id);
if(!$module || !$id) return new Object(-1, 'msg_invalid_queryid');
$xml_file = sprintf('./modules/%s/queries/%s.xml', $module, $id);
if(!file_exists($xml_file)) return new Object(-1, 'msg_invalid_queryid');
// 일단 cache 파일을 찾아본다
$cache_file = sprintf('%s%s.cache.php', $this->cache_file, $query_id);
// 없으면 원본 쿼리 xml파일을 찾아서 파싱을 한다
if(!file_exists($cache_file)||filectime($cache_file)<filectime($xml_file)) {
require_once('./classes/xml/XmlQueryParser.class.php');
$oParser = new XmlQueryParser();
$oParser->parse($query_id, $xml_file, $cache_file);
}
// 쿼리를 실행한다
return $this->_executeQuery($cache_file, $args, $query_id);
}
/**
* @brief 쿼리문을 실행하고 결과를 return한다
**/
function _executeQuery($cache_file, $source_args, $query_id) {
global $lang;
if(!file_exists($cache_file)) return new Object(-1, 'msg_invalid_queryid');
if($source_args) $args = clone($source_args);
$output = @include($cache_file);
if( (is_a($output, 'Object')||is_subclass_of($output,'Object'))&&!$output->toBool()) return $output;
// action값에 따라서 쿼리 생성으로 돌입
switch($output->action) {
case 'insert' :
$output = $this->_executeInsertAct($output);
break;
case 'update' :
$output = $this->_executeUpdateAct($output);
break;
case 'delete' :
$output = $this->_executeDeleteAct($output);
break;
case 'select' :
$output = $this->_executeSelectAct($output);
break;
}
if($this->errno !=0 ) return new Object($this->errno, $this->errstr);
if(is_a($output, 'Object') || is_subclass_of($output, 'Object')) return $output;
return new Object();
}
/**
* @brief $val을 $filter_type으로 검사
* XmlQueryParser에서 사용하도록
**/
function checkFilter($key, $val, $filter_type) {
global $lang;
switch($filter_type) {
case 'email' :
case 'email_address' :
if(!eregi('^[_0-9a-z-]+(\.[_0-9a-z-]+)*@[0-9a-z-]+(\.[0-9a-z-]+)*$', $val)) return new Object(-1, sprintf($lang->filter->invalid_email, $lang->{$key}?$lang->{$key}:$key));
break;
case 'homepage' :
if(!eregi('^(http|https)+(:\/\/)+[0-9a-z_-]+\.[^ ]+$', $val)) return new Object(-1, sprintf($lang->filter->invalid_homepage, $lang->{$key}?$lang->{$key}:$key));
break;
case 'userid' :
case 'user_id' :
if(!eregi('^[a-zA-Z]+([_0-9a-zA-Z]+)*$', $val)) return new Object(-1, sprintf($lang->filter->invalid_userid, $lang->{$key}?$lang->{$key}:$key));
break;
case 'number' :
if(!eregi('^[0-9]+$', $val)) return new Object(-1, sprintf($lang->filter->invalid_number, $lang->{$key}?$lang->{$key}:$key));
break;
case 'alpha' :
if(!eregi('^[a-z]+$', $val)) return new Object(-1, sprintf($lang->filter->invalid_alpha, $lang->{$key}?$lang->{$key}:$key));
break;
case 'alpha_number' :
if(!eregi('^[0-9a-z]+$', $val)) return new Object(-1, sprintf($lang->filter->invalid_alpha_number, $lang->{$key}?$lang->{$key}:$key));
break;
}
return new Object();
}
/**
* @brief 컬럼의 타입을 구해옴
* 컬럼의 경우 a.b 같이 되어 있는 경우가 있어서 별도 함수가 필요
**/
function getColumnType($column_type_list, $name) {
if(strpos($name,'.')===false) return $column_type_list[$name];
list($prefix, $name) = explode('.',$name);
return $column_type_list[$name];
}
/**
* @brief 이름, , operation, type으로 값을 변경
* like, like_prefix의 경우 value자체가 변경됨
* type == number가 아니면 addQuotes() 하고 ' ' 묶음
**/
function getConditionValue($name, $value, $operation, $type) {
if($type == 'number') {
if(strpos($value,',')===false && strpos($value,'(')===false) return (int)$value;
return $value;
}
$value = preg_replace('/(^\'|\'$){1}/','',$value);
switch($operation) {
case 'like_prefix' :
$value = $value.'%';
break;
case 'like_tail' :
$value = '%'.$value;
break;
case 'like' :
$value = '%'.$value.'%';
break;
case 'in' :
return "'".$value."'";
break;
}
if(strpos($name,'.')!==false && strpos($value,'.')!==false) return $value;
return "'".$this->addQuotes($value)."'";
}
/**
* @brief 이름, , operation으로 조건절 작성
* 조건절을 완성하기 위해 세부 조건절 마다 정리를 해서 return
**/
function getConditionPart($name, $value, $operation) {
switch($operation) {
case 'equal' :
if(!$value) return;
return $name.' = '.$value;
break;
case 'more' :
if(!$value) return;
return $name.' >= '.$value;
break;
case 'excess' :
if(!$value) return;
return $name.' > '.$value;
break;
case 'less' :
if(!$value) return;
return $name.' <= '.$value;
break;
case 'below' :
if(!$value) return;
return $name.' < '.$value;
break;
case 'like_tail' :
case 'like_prefix' :
case 'like' :
if(!$value) return;
return $name.' like '.$value;
break;
case 'in' :
if(!$value) return;
return $name.' in ('.$value.')';
break;
case 'notequal' :
if(!$value) return;
return $name.' <> '.$value;
break;
case 'notnull' :
return $name.' is not null';
break;
case 'null' :
return $name.' is null';
break;
}
}
}
?>

View file

@ -0,0 +1,688 @@
<?php
/**
* @class DBCubrid
* @author zero (zero@nzeo.com)
* @brief Cubrid DBMS를 이용하기 위한 class
* @version 0.1
*
* cubrid 7.0 에서 테스트 하였음.
* 기본 쿼리만 사용하였기에 특화된 튜닝이 필요
**/
class DBCubrid extends DB {
/**
* @brief Cubrid DB에 접속하기 위한 정보
**/
var $hostname = '127.0.0.1'; ///< hostname
var $userid = NULL; ///< user id
var $password = NULL; ///< password
var $database = NULL; ///< database
var $port = 33000; ///< db server port
var $prefix = 'xe'; ///< 제로보드에서 사용할 테이블들의 prefix (한 DB에서 여러개의 제로보드 설치 가능)
var $cutlen = 12000; ///< 큐브리드의 최대 상수 크기(스트링이 이보다 크면 '...'+'...' 방식을 사용해야 한다
/**
* @brief cubrid에서 사용될 column type
*
* column_type은 schema/query xml에서 공통 선언된 type을 이용하기 때문에
* DBMS에 맞게 replace 해주어야 한다
**/
var $column_type = array(
'bignumber' => 'numeric(20)',
'number' => 'integer',
'varchar' => 'character varying',
'char' => 'character',
'text' => 'character varying(1073741823)',
'bigtext' => 'character varying(1073741823)',
'date' => 'character varying(14)',
);
/**
* @brief constructor
**/
function DBCubrid() {
$this->_setDBInfo();
$this->_connect();
}
/**
* @brief 설치 가능 여부를 return
**/
function isSupported() {
if(!function_exists('cubrid_connect')) return false;
return true;
}
/**
* @brief DB정보 설정 connect/ close
**/
function _setDBInfo() {
$db_info = Context::getDBInfo();
$this->hostname = $db_info->db_hostname;
$this->userid = $db_info->db_userid;
$this->password = $db_info->db_password;
$this->database = $db_info->db_database;
$this->port = $db_info->db_port;
$this->prefix = $db_info->db_table_prefix;
if(!substr($this->prefix,-1)!='_') $this->prefix .= '_';
}
/**
* @brief DB 접속
**/
function _connect() {
// db 정보가 없으면 무시
if(!$this->hostname || !$this->userid || !$this->password || !$this->database || !$this->port) return;
// 접속시도
$this->fd = @cubrid_connect($this->hostname, $this->port, $this->database, $this->userid, $this->password);
// 접속체크
if(!$this->fd) {
$this->setError(-1, 'database connect fail');
return $this->is_connected = false;
}
$this->is_connected = true;
}
/**
* @brief DB접속 해제
**/
function close() {
if(!$this->isConnected()) return;
@cubrid_commit($this->fd);
@cubrid_disconnect($this->fd);
$this->transaction_started = false;
}
/**
* @brief 쿼리에서 입력되는 문자열 변수들의 quotation 조절
**/
function addQuotes($string) {
if(!$this->fd) return $string;
if(get_magic_quotes_gpc()) $string = stripslashes(str_replace("\\","\\\\",$string));
if(!is_numeric($string)) $string = str_replace("'","''",$string);
return $string;
}
/**
* @brief 트랜잭션 시작
**/
function begin() {
if(!$this->isConnected() || $this->transaction_started) return;
$this->transaction_started = true;
}
/**
* @brief 롤백
**/
function rollback() {
if(!$this->isConnected() || !$this->transaction_started) return;
@cubrid_rollback($this->fd);
$this->transaction_started = false;
}
/**
* @brief 커밋
**/
function commit() {
if(!$force && (!$this->isConnected() || !$this->transaction_started)) return;
@cubrid_commit($this->fd);
$this->transaction_started = false;
}
/**
* @brief : 쿼리문의 실행 결과의 fetch 처리
*
* query : query문 실행하고 result return\n
* fetch : reutrn 값이 없으면 NULL\n
* rows이면 array object\n
* row이면 object\n
* return\n
**/
function _query($query) {
//echo "(((".$this->backtrace().")))";
if(!$query || !$this->isConnected()) return;
// 쿼리 시작을 알림
$this->actStart($query);
// 쿼리 문 실행
$result = @cubrid_execute($this->fd, $query);
// 오류 체크
if(cubrid_error_code()) $this->setError(cubrid_error_code(), cubrid_error_msg());
// 쿼리 실행 종료를 알림
$this->actFinish();
// 결과 리턴
return $result;
}
/**
* @brief 결과를 fetch
**/
function _fetch($result) {
if(!$this->isConnected() || $this->isError() || !$result) return;
while($tmp = cubrid_fetch($result, CUBRID_OBJECT)) {
$output[] = $tmp;
}
if($result) cubrid_close_request($result);
if(count($output)==1) return $output[0];
return $output;
}
/**
* @brief 1 증가되는 sequence값을 return (cubrid의 auto_increment는 sequence테이블에서만 사용)
**/
function getNextSequence() {
$query = sprintf("select %ssequence.nextval as seq from db_root", $this->prefix);
$result = $this->_query($query);
$output = $this->_fetch($result);
return $output->seq;
}
/**
* @brief 테이블 기생성 여부 return
**/
function isTableExists($target_name) {
if($target_name == 'sequence')
$query = sprintf("select * from db_serial where name = '%s%s'", $this->prefix, $target_name);
else
$query = sprintf("select * from db_class where class_name = '%s%s'", $this->prefix, $target_name);
$result = $this->_query($query);
if(cubrid_num_rows($result)>0) $output = true;
else $output = false;
if($result) cubrid_close_request($result);
return $output;
}
/**
* @brief 특정 테이블에 특정 column 추가
**/
function addColumn($table_name, $column_name, $type='number', $size='', $default = '', $notnull=false) {
$type = $this->column_type[$type];
if(strtoupper($type)=='INTEGER') $size = '';
$query = sprintf("alter class %s%s add %s ", $this->prefix, $table_name, $column_name);
if($size) $query .= sprintf(" %s(%s) ", $type, $size);
else $query .= sprintf(" %s ", $type);
if($default) $query .= sprintf(" default '%s' ", $default);
if($notnull) $query .= " not null ";
$this->_query($query);
}
/**
* @brief 특정 테이블의 column의 정보를 return
**/
function isColumnExists($table_name, $column_name) {
$query = sprintf("select * from db_attribute where attr_name ='%s' and class_name = '%s%s'",
$column_name, $this->prefix, $table_name);
$result = $this->_query($query);
if(cubrid_num_rows($result)>0) $output = true;
else $output = false;
if($result) cubrid_close_request($result);
return $output;
}
/**
* @brief xml 받아서 테이블을 생성
**/
function createTableByXml($xml_doc) {
return $this->_createTable($xml_doc);
}
/**
* @brief xml 받아서 테이블을 생성
**/
function createTableByXmlFile($file_name) {
if(!file_exists($file_name)) return;
// xml 파일을 읽음
$buff = FileHandler::readFile($file_name);
return $this->_createTable($buff);
}
/**
* @brief schema xml을 이용하여 create class query생성
*
* type : number, varchar, text, char, date, \n
* opt : notnull, default, size\n
* index : primary key, index, unique\n
**/
function _createTable($xml_doc) {
// xml parsing
$oXml = new XmlParser();
$xml_obj = $oXml->parse($xml_doc);
// 테이블 생성 schema 작성
$table_name = $xml_obj->table->attrs->name;
// 만약 테이블 이름이 sequence라면 serial 생성
if($table_name == 'sequence') {
$query = sprintf('create serial %s start with 1 increment by 1 minvalue 1 maxvalue 10000000000000000000000000000000000000 nocycle;', $this->prefix.$table_name);
return $this->_query($query);
}
if($this->isTableExists($table_name)) return;
$table_name = $this->prefix.$table_name;
$query = sprintf('create class %s;', $table_name);
$this->_query($query);
$query = sprintf("call change_owner('%s','%s') on class db_root;", $table_name, $this->userid);
$this->_query($query);
if(!is_array($xml_obj->table->column)) $columns[] = $xml_obj->table->column;
else $columns = $xml_obj->table->column;
$query = sprintf("alter class %s add attribute ", $table_name);
foreach($columns as $column) {
$name = $column->attrs->name;
$type = $column->attrs->type;
$size = $column->attrs->size;
$notnull = $column->attrs->notnull;
$primary_key = $column->attrs->primary_key;
$index = $column->attrs->index;
$unique = $column->attrs->unique;
$default = $column->attrs->default;
switch($this->column_type[$type]) {
case 'integer' :
$size = null;
break;
case 'text' :
$size = null;
break;
}
if($default && !is_numeric($default)) $default = "'".$default."'";
$column_schema[] = sprintf('"%s" %s%s %s %s',
$name,
$this->column_type[$type],
$size?'('.$size.')':'',
$default?"default ".$default:'',
$notnull?'not null':''
);
if($primary_key) $primary_list[] = $name;
else if($unique) $unique_list[$unique][] = $name;
else if($index) $index_list[$index][] = $name;
}
$query .= implode(',', $column_schema).';';
$this->_query($query);
if(count($primary_list)) {
$query = sprintf("alter class %s add attribute constraint \"pkey_%s\" PRIMARY KEY(%s);", $table_name, $table_name, '"'.implode('","',$primary_list).'"');
$this->_query($query);
}
if(count($unique_list)) {
foreach($unique_list as $key => $val) {
$query = sprintf("create unique index %s_%s on %s (%s);", $table_name, $key, $table_name, '"'.implode('","',$val).'"');
$this->_query($query);
}
}
if(count($index_list)) {
foreach($index_list as $key => $val) {
$query = sprintf("create index %s_%s on %s (%s);", $table_name, $key, $table_name, '"'.implode('","',$val).'"');
$this->_query($query);
}
}
}
/**
* @brief 조건문 작성하여 return
**/
function getCondition($output) {
if(!$output->conditions) return;
$condition = "";
foreach($output->conditions as $key => $val) {
$sub_condition = '';
foreach($val['condition'] as $k =>$v) {
if(!$v['value']) continue;
$name = $v['column'];
$operation = $v['operation'];
$value = $v['value'];
$type = $this->getColumnType($output->column_type,$name);
$pipe = $v['pipe'];
$value = $this->getConditionValue($name, $value, $operation, $type);
if(!$value) $value = $v['value'];
if(strpos($name,'.')===false) $name = '"'.$name.'"';
$str = $this->getConditionPart($name, $value, $operation);
if($sub_condition) $sub_condition .= ' '.$pipe.' ';
$sub_condition .= $str;
}
if($sub_condition) {
if($condition && $val['pipe']) $condition .= ' '.$val['pipe'].' ';
$condition .= '('.$sub_condition.')';
}
}
if($condition) $condition = ' where '.$condition;
return $condition;
}
/**
* @brief insertAct 처리
**/
function _executeInsertAct($output) {
// 테이블 정리
foreach($output->tables as $key => $val) {
$table_list[] = '"'.$this->prefix.$key.'"';
}
// 컬럼 정리
foreach($output->columns as $key => $val) {
$name = $val['name'];
$value = $val['value'];
if($this->getColumnType($output->column_type,$name)!='number') {
$clen=strlen($value);
if ($clen <= $this->cutlen)
$value = "'".$this->addQuotes($value)."'";
else {
$wrk="";
$off=0;
while ($off<$clen) {
$wlen=$clen-$off;
if ($wlen>$this->cutlen) $wlen=$this->cutlen;
if ($off>0) $wrk .= "+\n";
$wrk .= "'".$this->addQuotes(substr($value, $off, $wlen))."'";
$off += $wlen;
}
$value = $wrk;
}
if(!$value) $value = 'null';
} elseif(!$value || is_numeric($value)) $value = (int)$value;
if(strpos($name,'.')===false) $column_list[] = '"'.$name.'"';
else $column_list[] = $name;
$value_list[] = $value;
}
$query = sprintf("insert into %s (%s) values (%s);", implode(',',$table_list), implode(',',$column_list), implode(',', $value_list));
return $this->_query($query);
}
/**
* @brief updateAct 처리
**/
function _executeUpdateAct($output) {
// 테이블 정리
foreach($output->tables as $key => $val) {
$table_list[] = "\"".$this->prefix.$key."\" as ".$val;
}
// 컬럼 정리
foreach($output->columns as $key => $val) {
if(!isset($val['value'])) continue;
$name = $val['name'];
$value = $val['value'];
if(strpos($name,'.')!==false&&strpos($value,'.')!==false) $column_list[] = $name.' = '.$value;
else {
if($output->column_type[$name]!='number') {
$clen=strlen($value);
if ($clen <= $this->cutlen)
$value = "'".$this->addQuotes($value)."'";
else {
$wrk="";
$off=0;
while ($off<$clen) {
$wlen=$clen-$off;
if ($wlen>$this->cutlen) $wlen=$this->cutlen;
if ($off>0) $wrk .= "+\n";
$wrk .= "'".$this->addQuotes(substr($value, $off, $wlen))."'";
$off += $wlen;
}
$value = $wrk;
}
}
elseif(!$value || is_numeric($value)) $value = (int)$value;
$column_list[] = sprintf("\"%s\" = %s", $name, $value);
}
}
// 조건절 정리
$condition = $this->getCondition($output);
$query = sprintf("update %s set %s %s", implode(',',$table_list), implode(',',$column_list), $condition);
return $this->_query($query);
}
/**
* @brief deleteAct 처리
**/
function _executeDeleteAct($output) {
// 테이블 정리
foreach($output->tables as $key => $val) {
$table_list[] = '"'.$this->prefix.$key.'"';
}
// 조건절 정리
$condition = $this->getCondition($output);
$query = sprintf("delete from %s %s", implode(',',$table_list), $condition);
return $this->_query($query);
}
/**
* @brief selectAct 처리
*
* select의 경우 특정 페이지의 목록을 가져오는 것을 편하게 하기 위해\n
* navigation이라는 method를 제공
**/
function _executeSelectAct($output) {
// 테이블 정리
$table_list = array();
foreach($output->tables as $key => $val) {
$table_list[] = '"'.$this->prefix.$key.'" as '.$val;
}
if(!$output->columns) {
$columns = '*';
} else {
$column_list = array();
foreach($output->columns as $key => $val) {
$name = $val['name'];
$alias = $val['alias'];
if($name == '*') {
$column_list[] = '*';
} elseif(strpos($name,'.')===false && strpos($name,'(')===false) {
if($alias) $column_list[] = sprintf('"%s" as "%s"', $name, $alias);
else $column_list[] = sprintf('"%s"',$name);
} else {
if(strpos($name,'.')!=false) {
list($prefix, $name) = explode('.',$name);
$deli=($name == '*') ? "" : "\"";
if($alias) $column_list[] = sprintf("%s.$deli%s$deli as \"%s\"", $prefix, $name, $alias);
else $column_list[] = sprintf("%s.$deli%s$deli",$prefix,$name);
} else {
if($alias) $column_list[] = sprintf('%s as "%s"', $name, $alias);
else $column_list[] = sprintf('%s',$name);
}
}
}
$columns = implode(',',$column_list);
}
$condition = $this->getCondition($output);
if($output->list_count) return $this->_getNavigationData($table_list, $columns, $condition, $output);
$query = sprintf("select %s from %s %s", $columns, implode(',',$table_list), $condition);
if(count($output->groups)) $query .= sprintf(' group by %s', implode(',',$output->groups));
if($output->order) {
foreach($output->order as $key => $val) {
$index_list[] = sprintf('%s %s', $val[0], $val[1]);
}
if(count($index_list)) $query .= ' order by '.implode(',',$index_list);
}
$result = $this->_query($query);
if($this->isError()) return;
$data = $this->_fetch($result);
$buff = new Object();
$buff->data = $data;
return $buff;
}
/**
* @brief 현재 시점의 Stack trace를 보여줌.결과를 fetch
**/
function backtrace()
{
$output = "<div style='text-align: left;'>\n";
$output .= "<b>Backtrace:</b><br />\n";
$backtrace = debug_backtrace();
foreach ($backtrace as $bt) {
$args = '';
foreach ($bt['args'] as $a) {
if (!empty($args)) {
$args .= ', ';
}
switch (gettype($a)) {
case 'integer':
case 'double':
$args .= $a;
break;
case 'string':
$a = htmlspecialchars(substr($a, 0, 64)).((strlen($a) > 64) ? '...' : '');
$args .= "\"$a\"";
break;
case 'array':
$args .= 'Array('.count($a).')';
break;
case 'object':
$args .= 'Object('.get_class($a).')';
break;
case 'resource':
$args .= 'Resource('.strstr($a, '#').')';
break;
case 'boolean':
$args .= $a ? 'True' : 'False';
break;
case 'NULL':
$args .= 'Null';
break;
default:
$args .= 'Unknown';
}
}
$output .= "<br />\n";
$output .= "<b>file:</b> {$bt['line']} - {$bt['file']}<br />\n";
$output .= "<b>call:</b> {$bt['class']}{$bt['type']}{$bt['function']}($args)<br />\n";
}
$output .= "</div>\n";
return $output;
}
/**
* @brief query xml에 navigation 정보가 있을 경우 페이징 관련 작업을 처리한다
*
* 그닥 좋지는 않은 구조이지만 편리하다.. -_-;
**/
function _getNavigationData($table_list, $columns, $condition, $output) {
require_once('./classes/page/PageHandler.class.php');
// 전체 개수를 구함
$count_query = sprintf('select count(*) as "count" from %s %s', implode(',',$table_list), $condition);
$result = $this->_query($count_query);
$count_output = $this->_fetch($result);
$total_count = (int)$count_output->count;
$list_count = $output->list_count['value'];
if(!$list_count) $list_count = 20;
$page_count = $output->page_count['value'];
if(!$page_count) $page_count = 10;
$page = $output->page['value'];
if(!$page) $page = 1;
// 전체 페이지를 구함
if($total_count) $total_page = (int)( ($total_count-1) / $list_count) + 1;
else $total_page = 1;
// 페이지 변수를 체크
if($page > $total_page) $page = $total_page;
$start_count = ($page-1)*$list_count;
$query = sprintf("select %s from %s %s", $columns, implode(',',$table_list), $condition);
if(count($output->groups)) $query .= sprintf(' group by %s', implode(',',$output->groups));
if ($output->order) {
foreach($output->order as $key => $val) {
$index_list[] = sprintf('%s %s', $val[0], $val[1]);
}
if(count($index_list)) $query .= ' order by '.implode(',',$index_list);
$query = sprintf('%s for orderby_num() between %d and %d', $query, $start_count, $list_count);
}
else {
if (count($output->groups))
$query = sprintf('%s having groupby_num() between %d and %d', $query, $start_count, $list_count);
else {
if ($condition)
$query = sprintf('%s and inst_num() between %d and %d', $query, $start_count, $list_count);
else
$query = sprintf('%s where inst_num() between %d and %d', $query, $start_count, $list_count);
}
}
$result = $this->_query($query);
if($this->isError()) {
$buff = new Object();
$buff->total_count = 0;
$buff->total_page = 0;
$buff->page = 1;
$buff->data = array();
$buff->page_navigation = new PageHandler($total_count, $total_page, $page, $page_count);
return $buff;
}
$virtual_no = $total_count - ($page-1)*$list_count;
while($tmp = cubrid_fetch($result, CUBRID_OBJECT)) {
$data[$virtual_no--] = $tmp;
}
$buff = new Object();
$buff->total_count = $total_count;
$buff->total_page = $total_page;
$buff->page = $page;
$buff->data = $data;
$buff->page_navigation = new PageHandler($total_count, $total_page, $page, $page_count);
return $buff;
}
}
?>

View file

@ -0,0 +1,552 @@
<?php
/**
* @class DBMysql
* @author zero (zero@nzeo.com)
* @brief MySQL DBMS를 이용하기 위한 class
* @version 0.1
*
* mysql handling class
**/
class DBMysql extends DB {
/**
* @brief Mysql DB에 접속하기 위한 정보
**/
var $hostname = '127.0.0.1'; ///< hostname
var $userid = NULL; ///< user id
var $password = NULL; ///< password
var $database = NULL; ///< database
var $prefix = 'xe'; ///< 제로보드에서 사용할 테이블들의 prefix (한 DB에서 여러개의 제로보드 설치 가능)
/**
* @brief mysql에서 사용될 column type
*
* column_type은 schema/query xml에서 공통 선언된 type을 이용하기 때문에
* DBMS에 맞게 replace 해주어야 한다
**/
var $column_type = array(
'bignumber' => 'bigint',
'number' => 'bigint',
'varchar' => 'varchar',
'char' => 'char',
'text' => 'text',
'bigtext' => 'longtext',
'date' => 'varchar(14)',
);
/**
* @brief constructor
**/
function DBMysql() {
$this->_setDBInfo();
$this->_connect();
}
/**
* @brief 설치 가능 여부를 return
**/
function isSupported() {
if(!function_exists('mysql_connect')) return false;
return true;
}
/**
* @brief DB정보 설정 connect/ close
**/
function _setDBInfo() {
$db_info = Context::getDBInfo();
$this->hostname = $db_info->db_hostname;
$this->userid = $db_info->db_userid;
$this->password = $db_info->db_password;
$this->database = $db_info->db_database;
$this->prefix = $db_info->db_table_prefix;
if(!substr($this->prefix,-1)!='_') $this->prefix .= '_';
}
/**
* @brief DB 접속
**/
function _connect() {
// db 정보가 없으면 무시
if(!$this->hostname || !$this->userid || !$this->password || !$this->database) return;
// 접속시도
$this->fd = @mysql_connect($this->hostname, $this->userid, $this->password);
if(mysql_error()) {
$this->setError(mysql_errno(), mysql_error());
return;
}
// 버전 확인후 4.1 이하면 오류 표시
if(mysql_get_server_info($this->fd)<"4.1") {
$this->setError(-1, "zeroboard xe can not install under mysql 4.1. Current mysql version is ".mysql_get_server_info());
return;
}
// db 선택
@mysql_select_db($this->database, $this->fd);
if(mysql_error()) {
$this->setError(mysql_errno(), mysql_error());
return;
}
// 접속체크
$this->is_connected = true;
// mysql의 경우 utf8임을 지정
$this->_query("set names 'utf8'");
}
/**
* @brief DB접속 해제
**/
function close() {
if(!$this->isConnected()) return;
@mysql_close($this->fd);
}
/**
* @brief 쿼리에서 입력되는 문자열 변수들의 quotation 조절
**/
function addQuotes($string) {
if(get_magic_quotes_gpc()) $string = stripslashes(str_replace("\\","\\\\",$string));
if(!is_numeric($string)) $string = @mysql_escape_string($string);
return $string;
}
/**
* @brief 트랜잭션 시작
**/
function begin() {
}
/**
* @brief 롤백
**/
function rollback() {
}
/**
* @brief 커밋
**/
function commit() {
}
/**
* @brief : 쿼리문의 실행 결과의 fetch 처리
*
* query : query문 실행하고 result return\n
* fetch : reutrn 값이 없으면 NULL\n
* rows이면 array object\n
* row이면 object\n
* return\n
**/
function _query($query) {
if(!$this->isConnected()) return;
// 쿼리 시작을 알림
$this->actStart($query);
// 쿼리 문 실행
$result = @mysql_query($query, $this->fd);
// 오류 체크
if(mysql_error($this->fd)) $this->setError(mysql_errno($this->fd), mysql_error($this->fd));
// 쿼리 실행 종료를 알림
$this->actFinish();
// 결과 리턴
return $result;
}
/**
* @brief 결과를 fetch
**/
function _fetch($result) {
if(!$this->isConnected() || $this->isError() || !$result) return;
while($tmp = mysql_fetch_object($result)) {
$output[] = $tmp;
}
if(count($output)==1) return $output[0];
return $output;
}
/**
* @brief 1 증가되는 sequence값을 return (mysql의 auto_increment는 sequence테이블에서만 사용)
**/
function getNextSequence() {
$query = sprintf("insert into `%ssequence` (seq) values ('')", $this->prefix);
$this->_query($query);
$sequence = mysql_insert_id();
$query = sprintf("delete from `%ssequence` where seq < %d", $this->prefix, $sequence);
$this->_query($query);
return $sequence;
}
/**
* @brief 테이블 기생성 여부 return
**/
function isTableExists($target_name) {
$query = sprintf("show tables like '%s%s'", $this->prefix, $this->addQuotes($target_name));
$result = $this->_query($query);
$tmp = $this->_fetch($result);
if(!$tmp) return false;
return true;
}
/**
* @brief 특정 테이블에 특정 column 추가
**/
function addColumn($table_name, $column_name, $type='number', $size='', $default = '', $notnull=false) {
$type = $this->column_type[$type];
if(strtoupper($type)=='INTEGER') $size = '';
$query = sprintf("alter table %s%s add %s ", $this->prefix, $table_name, $column_name);
if($size) $query .= sprintf(" %s(%s) ", $type, $size);
else $query .= sprintf(" %s ", $type);
if($default) $query .= sprintf(" default '%s' ", $default);
if($notnull) $query .= " not null ";
$this->_query($query);
}
/**
* @brief 특정 테이블의 column의 정보를 return
**/
function isColumnExists($table_name, $column_name) {
$query = sprintf("show fields from %s%s", $this->prefix, $table_name);
$result = $this->_query($query);
if($this->isError()) return;
$output = $this->_fetch($result);
if($output) {
$column_name = strtolower($column_name);
foreach($output as $key => $val) {
$name = strtolower($val->Field);
if($column_name == $name) return true;
}
}
return false;
}
/**
* @brief xml 받아서 테이블을 생성
**/
function createTableByXml($xml_doc) {
return $this->_createTable($xml_doc);
}
/**
* @brief xml 받아서 테이블을 생성
**/
function createTableByXmlFile($file_name) {
if(!file_exists($file_name)) return;
// xml 파일을 읽음
$buff = FileHandler::readFile($file_name);
return $this->_createTable($buff);
}
/**
* @brief schema xml을 이용하여 create table query생성
*
* type : number, varchar, text, char, date, \n
* opt : notnull, default, size\n
* index : primary key, index, unique\n
**/
function _createTable($xml_doc) {
// xml parsing
$oXml = new XmlParser();
$xml_obj = $oXml->parse($xml_doc);
// 테이블 생성 schema 작성
$table_name = $xml_obj->table->attrs->name;
if($this->isTableExists($table_name)) return;
$table_name = $this->prefix.$table_name;
if(!is_array($xml_obj->table->column)) $columns[] = $xml_obj->table->column;
else $columns = $xml_obj->table->column;
foreach($columns as $column) {
$name = $column->attrs->name;
$type = $column->attrs->type;
$size = $column->attrs->size;
$notnull = $column->attrs->notnull;
$primary_key = $column->attrs->primary_key;
$index = $column->attrs->index;
$unique = $column->attrs->unique;
$default = $column->attrs->default;
$auto_increment = $column->attrs->auto_increment;
$column_schema[] = sprintf('`%s` %s%s %s %s %s',
$name,
$this->column_type[$type],
$size?'('.$size.')':'',
$default?"default '".$default."'":'',
$notnull?'not null':'',
$auto_increment?'auto_increment':''
);
if($primary_key) $primary_list[] = $name;
else if($unique) $unique_list[$unique][] = $name;
else if($index) $index_list[$index][] = $name;
}
if(count($primary_list)) {
$column_schema[] = sprintf("primary key (%s)", '`'.implode($primary_list,'`,`').'`');
}
if(count($unique_list)) {
foreach($unique_list as $key => $val) {
$column_schema[] = sprintf("unique %s (%s)", $key, '`'.implode($val,'`,`').'`');
}
}
if(count($index_list)) {
foreach($index_list as $key => $val) {
$column_schema[] = sprintf("index %s (%s)", $key, '`'.implode($val,'`,`').'`');
}
}
$schema = sprintf('create table `%s` (%s%s) %s;', $this->addQuotes($table_name), "\n", implode($column_schema,",\n"), "ENGINE = MYISAM CHARACTER SET utf8 COLLATE utf8_general_ci");
$output = $this->_query($schema);
if(!$output) return false;
}
/**
* @brief 조건문 작성하여 return
**/
function getCondition($output) {
if(!$output->conditions) return;
foreach($output->conditions as $key => $val) {
$sub_condition = '';
foreach($val['condition'] as $k =>$v) {
if(!$v['value']) continue;
$name = $v['column'];
$operation = $v['operation'];
$value = $v['value'];
$type = $this->getColumnType($output->column_type,$name);
$pipe = $v['pipe'];
$value = $this->getConditionValue($name, $value, $operation, $type);
if(!$value) $value = $v['value'];
$str = $this->getConditionPart($name, $value, $operation);
if($sub_condition) $sub_condition .= ' '.$pipe.' ';
$sub_condition .= $str;
}
if($sub_condition) {
if($condition && $val['pipe']) $condition .= ' '.$val['pipe'].' ';
$condition .= '('.$sub_condition.')';
}
}
if($condition) $condition = ' where '.$condition;
return $condition;
}
/**
* @brief insertAct 처리
**/
function _executeInsertAct($output) {
// 테이블 정리
foreach($output->tables as $key => $val) {
$table_list[] = '`'.$this->prefix.$key.'`';
}
// 컬럼 정리
foreach($output->columns as $key => $val) {
$name = $val['name'];
$value = $val['value'];
if($output->column_type[$name]!='number') {
$value = "'".$this->addQuotes($value)."'";
if(!$value) $value = 'null';
} elseif(!$value || is_numeric($value)) $value = (int)$value;
$column_list[] = '`'.$name.'`';
$value_list[] = $value;
}
$query = sprintf("insert into %s (%s) values (%s);", implode(',',$table_list), implode(',',$column_list), implode(',', $value_list));
return $this->_query($query);
}
/**
* @brief updateAct 처리
**/
function _executeUpdateAct($output) {
// 테이블 정리
foreach($output->tables as $key => $val) {
$table_list[] = '`'.$this->prefix.$key.'` as '.$val;
}
// 컬럼 정리
foreach($output->columns as $key => $val) {
if(!isset($val['value'])) continue;
$name = $val['name'];
$value = $val['value'];
if(strpos($name,'.')!==false&&strpos($value,'.')!==false) $column_list[] = $name.' = '.$value;
else {
if($output->column_type[$name]!='number') $value = "'".$this->addQuotes($value)."'";
elseif(!$value || is_numeric($value)) $value = (int)$value;
$column_list[] = sprintf("`%s` = %s", $name, $value);
}
}
// 조건절 정리
$condition = $this->getCondition($output);
$query = sprintf("update %s set %s %s", implode(',',$table_list), implode(',',$column_list), $condition);
return $this->_query($query);
}
/**
* @brief deleteAct 처리
**/
function _executeDeleteAct($output) {
// 테이블 정리
foreach($output->tables as $key => $val) {
$table_list[] = '`'.$this->prefix.$key.'`';
}
// 조건절 정리
$condition = $this->getCondition($output);
$query = sprintf("delete from %s %s", implode(',',$table_list), $condition);
return $this->_query($query);
}
/**
* @brief selectAct 처리
*
* select의 경우 특정 페이지의 목록을 가져오는 것을 편하게 하기 위해\n
* navigation이라는 method를 제공
**/
function _executeSelectAct($output) {
// 테이블 정리
$table_list = array();
foreach($output->tables as $key => $val) {
$table_list[] = '`'.$this->prefix.$key.'` as '.$val;
}
if(!$output->columns) {
$columns = '*';
} else {
$column_list = array();
foreach($output->columns as $key => $val) {
$name = $val['name'];
$alias = $val['alias'];
if($name == '*') {
$column_list[] = '*';
} elseif(strpos($name,'.')===false && strpos($name,'(')===false) {
if($alias) $column_list[] = sprintf('`%s` as `%s`', $name, $alias);
else $column_list[] = sprintf('`%s`',$name);
} else {
if($alias) $column_list[] = sprintf('%s as `%s`', $name, $alias);
else $column_list[] = sprintf('%s',$name);
}
}
$columns = implode(',',$column_list);
}
$condition = $this->getCondition($output);
if($output->list_count) return $this->_getNavigationData($table_list, $columns, $condition, $output);
$query = sprintf("select %s from %s %s", $columns, implode(',',$table_list), $condition);
if(count($output->groups)) $query .= sprintf(' group by %s', implode(',',$output->groups));
if($output->order) {
foreach($output->order as $key => $val) {
$index_list[] = sprintf('%s %s', $val[0], $val[1]);
}
if(count($index_list)) $query .= ' order by '.implode(',',$index_list);
}
$result = $this->_query($query);
if($this->isError()) return;
$data = $this->_fetch($result);
$buff = new Object();
$buff->data = $data;
return $buff;
}
/**
* @brief query xml에 navigation 정보가 있을 경우 페이징 관련 작업을 처리한다
*
* 그닥 좋지는 않은 구조이지만 편리하다.. -_-;
**/
function _getNavigationData($table_list, $columns, $condition, $output) {
require_once('./classes/page/PageHandler.class.php');
// 전체 개수를 구함
$count_query = sprintf("select count(*) as count from %s %s", implode(',',$table_list), $condition);
$result = $this->_query($count_query);
$count_output = $this->_fetch($result);
$total_count = (int)$count_output->count;
$list_count = $output->list_count['value'];
if(!$list_count) $list_count = 20;
$page_count = $output->page_count['value'];
if(!$page_count) $page_count = 10;
$page = $output->page['value'];
if(!$page) $page = 1;
// 전체 페이지를 구함
if($total_count) $total_page = (int)( ($total_count-1) / $list_count) + 1;
else $total_page = 1;
// 페이지 변수를 체크
if($page > $total_page) $page = $total_page;
$start_count = ($page-1)*$list_count;
$query = sprintf("select %s from %s %s", $columns, implode(',',$table_list), $condition);
if(count($output->groups)) $query .= sprintf(' group by %s', implode(',',$output->groups));
if(count($output->order)) {
foreach($output->order as $key => $val) {
$index_list[] = sprintf('%s %s', $val[0], $val[1]);
}
if(count($index_list)) $query .= ' order by '.implode(',',$index_list);
}
$query = sprintf('%s limit %d, %d', $query, $start_count, $list_count);
$result = $this->_query($query);
if($this->isError()) {
$buff = new Object();
$buff->total_count = 0;
$buff->total_page = 0;
$buff->page = 1;
$buff->data = array();
$buff->page_navigation = new PageHandler($total_count, $total_page, $page, $page_count);
return $buff;
}
$virtual_no = $total_count - ($page-1)*$list_count;
while($tmp = mysql_fetch_object($result)) {
$data[$virtual_no--] = $tmp;
}
$buff = new Object();
$buff->total_count = $total_count;
$buff->total_page = $total_page;
$buff->page = $page;
$buff->data = $data;
$buff->page_navigation = new PageHandler($total_count, $total_page, $page, $page_count);
return $buff;
}
}
?>

View file

@ -0,0 +1,562 @@
<?php
/**
* @class DBMysql_innodb
* @author zero (zero@nzeo.com)
* @brief MySQL DBMS를 이용하기 위한 class
* @version 0.1
*
* mysql innodb handling class
**/
class DBMysql_innodb extends DB {
/**
* @brief Mysql DB에 접속하기 위한 정보
**/
var $hostname = '127.0.0.1'; ///< hostname
var $userid = NULL; ///< user id
var $password = NULL; ///< password
var $database = NULL; ///< database
var $prefix = 'xe'; ///< 제로보드에서 사용할 테이블들의 prefix (한 DB에서 여러개의 제로보드 설치 가능)
/**
* @brief mysql에서 사용될 column type
*
* column_type은 schema/query xml에서 공통 선언된 type을 이용하기 때문에
* DBMS에 맞게 replace 해주어야 한다
**/
var $column_type = array(
'bignumber' => 'bigint',
'number' => 'bigint',
'varchar' => 'varchar',
'char' => 'char',
'text' => 'text',
'bigtext' => 'longtext',
'date' => 'varchar(14)',
);
/**
* @brief constructor
**/
function DBMysql_innodb() {
$this->_setDBInfo();
$this->_connect();
}
/**
* @brief 설치 가능 여부를 return
**/
function isSupported() {
if(!function_exists('mysql_connect')) return false;
return true;
}
/**
* @brief DB정보 설정 connect/ close
**/
function _setDBInfo() {
$db_info = Context::getDBInfo();
$this->hostname = $db_info->db_hostname;
$this->userid = $db_info->db_userid;
$this->password = $db_info->db_password;
$this->database = $db_info->db_database;
$this->prefix = $db_info->db_table_prefix;
if(!substr($this->prefix,-1)!='_') $this->prefix .= '_';
}
/**
* @brief DB 접속
**/
function _connect() {
// db 정보가 없으면 무시
if(!$this->hostname || !$this->userid || !$this->password || !$this->database) return;
// 접속시도
$this->fd = @mysql_connect($this->hostname, $this->userid, $this->password);
if(mysql_error()) {
$this->setError(mysql_errno(), mysql_error());
return;
}
// 버전 확인후 4.1 이하면 오류 표시
if(mysql_get_server_info($this->fd)<"4.1") {
$this->setError(-1, "zeroboard xe can not install under mysql 4.1. Current mysql version is ".mysql_get_server_info());
return;
}
// db 선택
@mysql_select_db($this->database, $this->fd);
if(mysql_error()) {
$this->setError(mysql_errno(), mysql_error());
return;
}
// 접속체크
$this->is_connected = true;
// mysql의 경우 utf8임을 지정
$this->_query("set names 'utf8'");
}
/**
* @brief DB접속 해제
**/
function close() {
if(!$this->isConnected()) return;
$this->_query("commit");
@mysql_close($this->fd);
}
/**
* @brief 쿼리에서 입력되는 문자열 변수들의 quotation 조절
**/
function addQuotes($string) {
if(get_magic_quotes_gpc()) $string = stripslashes(str_replace("\\","\\\\",$string));
if(!is_numeric($string)) $string = @mysql_escape_string($string);
return $string;
}
/**
* @brief 트랜잭션 시작
**/
function begin() {
if(!$this->isConnected() || $this->transaction_started) return;
$this->_query("begin");
}
/**
* @brief 롤백
**/
function rollback() {
if(!$this->isConnected() || !$this->transaction_started) return;
$this->_query("rollback");
$this->transaction_started = false;
}
/**
* @brief 커밋
**/
function commit($force = false) {
if(!$force && (!$this->isConnected() || !$this->transaction_started)) return;
$this->_query("commit");
$this->transaction_started = false;
}
/**
* @brief : 쿼리문의 실행 결과의 fetch 처리
*
* query : query문 실행하고 result return\n
* fetch : reutrn 값이 없으면 NULL\n
* rows이면 array object\n
* row이면 object\n
* return\n
**/
function _query($query) {
if(!$this->isConnected()) return;
// 쿼리 시작을 알림
$this->actStart($query);
// 쿼리 문 실행
$result = @mysql_query($query, $this->fd);
// 오류 체크
if(mysql_error($this->fd)) $this->setError(mysql_errno($this->fd), mysql_error($this->fd));
// 쿼리 실행 종료를 알림
$this->actFinish();
// 결과 리턴
return $result;
}
/**
* @brief 결과를 fetch
**/
function _fetch($result) {
if(!$this->isConnected() || $this->isError() || !$result) return;
while($tmp = mysql_fetch_object($result)) {
$output[] = $tmp;
}
if(count($output)==1) return $output[0];
return $output;
}
/**
* @brief 1 증가되는 sequence값을 return (mysql의 auto_increment는 sequence테이블에서만 사용)
**/
function getNextSequence() {
$query = sprintf("insert into `%ssequence` (seq) values ('')", $this->prefix);
$this->_query($query);
$sequence = mysql_insert_id();
$query = sprintf("delete from `%ssequence` where seq < %d", $this->prefix, $sequence);
$this->_query($query);
return $sequence;
}
/**
* @brief 테이블 기생성 여부 return
**/
function isTableExists($target_name) {
$query = sprintf("show tables like '%s%s'", $this->prefix, $this->addQuotes($target_name));
$result = $this->_query($query);
$tmp = $this->_fetch($result);
if(!$tmp) return false;
return true;
}
/**
* @brief 특정 테이블에 특정 column 추가
**/
function addColumn($table_name, $column_name, $type='number', $size='', $default = '', $notnull=false) {
$type = $this->column_type[$type];
if(strtoupper($type)=='INTEGER') $size = '';
$query = sprintf("alter table %s%s add %s ", $this->prefix, $table_name, $column_name);
if($size) $query .= sprintf(" %s(%s) ", $type, $size);
else $query .= sprintf(" %s ", $type);
if($default) $query .= sprintf(" default '%s' ", $default);
if($notnull) $query .= " not null ";
$this->_query($query);
}
/**
* @brief 특정 테이블의 column의 정보를 return
**/
function isColumnExists($table_name, $column_name) {
$query = sprintf("show fields from %s%s", $this->prefix, $table_name);
$result = $this->_query($query);
if($this->isError()) return;
$output = $this->_fetch($result);
if($output) {
$column_name = strtolower($column_name);
foreach($output as $key => $val) {
$name = strtolower($val->Field);
if($column_name == $name) return true;
}
}
return false;
}
/**
* @brief xml 받아서 테이블을 생성
**/
function createTableByXml($xml_doc) {
return $this->_createTable($xml_doc);
}
/**
* @brief xml 받아서 테이블을 생성
**/
function createTableByXmlFile($file_name) {
if(!file_exists($file_name)) return;
// xml 파일을 읽음
$buff = FileHandler::readFile($file_name);
return $this->_createTable($buff);
}
/**
* @brief schema xml을 이용하여 create table query생성
*
* type : number, varchar, text, char, date, \n
* opt : notnull, default, size\n
* index : primary key, index, unique\n
**/
function _createTable($xml_doc) {
// xml parsing
$oXml = new XmlParser();
$xml_obj = $oXml->parse($xml_doc);
// 테이블 생성 schema 작성
$table_name = $xml_obj->table->attrs->name;
if($this->isTableExists($table_name)) return;
$table_name = $this->prefix.$table_name;
if(!is_array($xml_obj->table->column)) $columns[] = $xml_obj->table->column;
else $columns = $xml_obj->table->column;
foreach($columns as $column) {
$name = $column->attrs->name;
$type = $column->attrs->type;
$size = $column->attrs->size;
$notnull = $column->attrs->notnull;
$primary_key = $column->attrs->primary_key;
$index = $column->attrs->index;
$unique = $column->attrs->unique;
$default = $column->attrs->default;
$auto_increment = $column->attrs->auto_increment;
$column_schema[] = sprintf('`%s` %s%s %s %s %s',
$name,
$this->column_type[$type],
$size?'('.$size.')':'',
$default?"default '".$default."'":'',
$notnull?'not null':'',
$auto_increment?'auto_increment':''
);
if($primary_key) $primary_list[] = $name;
else if($unique) $unique_list[$unique][] = $name;
else if($index) $index_list[$index][] = $name;
}
if(count($primary_list)) {
$column_schema[] = sprintf("primary key (%s)", '`'.implode($primary_list,'`,`').'`');
}
if(count($unique_list)) {
foreach($unique_list as $key => $val) {
$column_schema[] = sprintf("unique %s (%s)", $key, '`'.implode($val,'`,`').'`');
}
}
if(count($index_list)) {
foreach($index_list as $key => $val) {
$column_schema[] = sprintf("index %s (%s)", $key, '`'.implode($val,'`,`').'`');
}
}
$schema = sprintf('create table `%s` (%s%s) %s;', $this->addQuotes($table_name), "\n", implode($column_schema,",\n"), "ENGINE = INNODB CHARACTER SET utf8 COLLATE utf8_general_ci");
$output = $this->_query($schema);
if(!$output) return false;
}
/**
* @brief 조건문 작성하여 return
**/
function getCondition($output) {
if(!$output->conditions) return;
foreach($output->conditions as $key => $val) {
$sub_condition = '';
foreach($val['condition'] as $k =>$v) {
if(!$v['value']) continue;
$name = $v['column'];
$operation = $v['operation'];
$value = $v['value'];
$type = $this->getColumnType($output->column_type,$name);
$pipe = $v['pipe'];
$value = $this->getConditionValue($name, $value, $operation, $type);
if(!$value) $value = $v['value'];
$str = $this->getConditionPart($name, $value, $operation);
if($sub_condition) $sub_condition .= ' '.$pipe.' ';
$sub_condition .= $str;
}
if($sub_condition) {
if($condition && $val['pipe']) $condition .= ' '.$val['pipe'].' ';
$condition .= '('.$sub_condition.')';
}
}
if($condition) $condition = ' where '.$condition;
return $condition;
}
/**
* @brief insertAct 처리
**/
function _executeInsertAct($output) {
// 테이블 정리
foreach($output->tables as $key => $val) {
$table_list[] = '`'.$this->prefix.$key.'`';
}
// 컬럼 정리
foreach($output->columns as $key => $val) {
$name = $val['name'];
$value = $val['value'];
if($output->column_type[$name]!='number') {
$value = "'".$this->addQuotes($value)."'";
if(!$value) $value = 'null';
} elseif(!$value || is_numeric($value)) $value = (int)$value;
$column_list[] = '`'.$name.'`';
$value_list[] = $value;
}
$query = sprintf("insert into %s (%s) values (%s);", implode(',',$table_list), implode(',',$column_list), implode(',', $value_list));
return $this->_query($query);
}
/**
* @brief updateAct 처리
**/
function _executeUpdateAct($output) {
// 테이블 정리
foreach($output->tables as $key => $val) {
$table_list[] = '`'.$this->prefix.$key.'` as '.$val;
}
// 컬럼 정리
foreach($output->columns as $key => $val) {
if(!isset($val['value'])) continue;
$name = $val['name'];
$value = $val['value'];
if(strpos($name,'.')!==false&&strpos($value,'.')!==false) $column_list[] = $name.' = '.$value;
else {
if($output->column_type[$name]!='number') $value = "'".$this->addQuotes($value)."'";
elseif(!$value || is_numeric($value)) $value = (int)$value;
$column_list[] = sprintf("`%s` = %s", $name, $value);
}
}
// 조건절 정리
$condition = $this->getCondition($output);
$query = sprintf("update %s set %s %s", implode(',',$table_list), implode(',',$column_list), $condition);
return $this->_query($query);
}
/**
* @brief deleteAct 처리
**/
function _executeDeleteAct($output) {
// 테이블 정리
foreach($output->tables as $key => $val) {
$table_list[] = '`'.$this->prefix.$key.'`';
}
// 조건절 정리
$condition = $this->getCondition($output);
$query = sprintf("delete from %s %s", implode(',',$table_list), $condition);
return $this->_query($query);
}
/**
* @brief selectAct 처리
*
* select의 경우 특정 페이지의 목록을 가져오는 것을 편하게 하기 위해\n
* navigation이라는 method를 제공
**/
function _executeSelectAct($output) {
// 테이블 정리
$table_list = array();
foreach($output->tables as $key => $val) {
$table_list[] = '`'.$this->prefix.$key.'` as '.$val;
}
if(!$output->columns) {
$columns = '*';
} else {
$column_list = array();
foreach($output->columns as $key => $val) {
$name = $val['name'];
$alias = $val['alias'];
if($name == '*') {
$column_list[] = '*';
} elseif(strpos($name,'.')===false && strpos($name,'(')===false) {
if($alias) $column_list[] = sprintf('`%s` as `%s`', $name, $alias);
else $column_list[] = sprintf('`%s`',$name);
} else {
if($alias) $column_list[] = sprintf('%s as `%s`', $name, $alias);
else $column_list[] = sprintf('%s',$name);
}
}
$columns = implode(',',$column_list);
}
$condition = $this->getCondition($output);
if($output->list_count) return $this->_getNavigationData($table_list, $columns, $condition, $output);
$query = sprintf("select %s from %s %s", $columns, implode(',',$table_list), $condition);
if(count($output->groups)) $query .= sprintf(' group by %s', implode(',',$output->groups));
if($output->order) {
foreach($output->order as $key => $val) {
$index_list[] = sprintf('%s %s', $val[0], $val[1]);
}
if(count($index_list)) $query .= ' order by '.implode(',',$index_list);
}
$result = $this->_query($query);
if($this->isError()) return;
$data = $this->_fetch($result);
$buff = new Object();
$buff->data = $data;
return $buff;
}
/**
* @brief query xml에 navigation 정보가 있을 경우 페이징 관련 작업을 처리한다
*
* 그닥 좋지는 않은 구조이지만 편리하다.. -_-;
**/
function _getNavigationData($table_list, $columns, $condition, $output) {
require_once('./classes/page/PageHandler.class.php');
// 전체 개수를 구함
$count_query = sprintf("select count(*) as count from %s %s", implode(',',$table_list), $condition);
$result = $this->_query($count_query);
$count_output = $this->_fetch($result);
$total_count = (int)$count_output->count;
$list_count = $output->list_count['value'];
if(!$list_count) $list_count = 20;
$page_count = $output->page_count['value'];
if(!$page_count) $page_count = 10;
$page = $output->page['value'];
if(!$page) $page = 1;
// 전체 페이지를 구함
if($total_count) $total_page = (int)( ($total_count-1) / $list_count) + 1;
else $total_page = 1;
// 페이지 변수를 체크
if($page > $total_page) $page = $total_page;
$start_count = ($page-1)*$list_count;
$query = sprintf("select %s from %s %s", $columns, implode(',',$table_list), $condition);
if(count($output->groups)) $query .= sprintf(' group by %s', implode(',',$output->groups));
if($output->order) {
foreach($output->order as $key => $val) {
$index_list[] = sprintf('%s %s', $val[0], $val[1]);
}
if(count($index_list)) $query .= ' order by '.implode(',',$index_list);
}
$query = sprintf('%s limit %d, %d', $query, $start_count, $list_count);
$result = $this->_query($query);
if($this->isError()) {
$buff = new Object();
$buff->total_count = 0;
$buff->total_page = 0;
$buff->page = 1;
$buff->data = array();
$buff->page_navigation = new PageHandler($total_count, $total_page, $page, $page_count);
return $buff;
}
$virtual_no = $total_count - ($page-1)*$list_count;
while($tmp = mysql_fetch_object($result)) {
$data[$virtual_no--] = $tmp;
}
$buff = new Object();
$buff->total_count = $total_count;
$buff->total_page = $total_page;
$buff->page = $page;
$buff->data = $data;
$buff->page_navigation = new PageHandler($total_count, $total_page, $page, $page_count);
return $buff;
}
}
?>

View file

@ -0,0 +1,593 @@
<?php
/**
* @class DBSqlite2
* @author zero (zero@nzeo.com)
* @brief SQLite ver 2.x 이용하기 위한 class
* @version 0.1
*
* sqlite handling class (sqlite ver 2.x)
**/
class DBSqlite2 extends DB {
/**
* DB를 이용하기 위한 정보
**/
var $database = NULL; ///< database
var $prefix = 'xe'; ///< 제로보드에서 사용할 테이블들의 prefix (한 DB에서 여러개의 제로보드 설치 가능)
/**
* @brief sqlite 에서 사용될 column type
*
* column_type은 schema/query xml에서 공통 선언된 type을 이용하기 때문에
* DBMS에 맞게 replace 해주어야 한다
**/
var $column_type = array(
'bignumber' => 'INTEGER',
'number' => 'INTEGER',
'varchar' => 'VARHAR',
'char' => 'CHAR',
'text' => 'TEXT',
'bigtext' => 'TEXT',
'date' => 'VARCHAR(14)',
);
/**
* @brief constructor
**/
function DBSqlite2() {
$this->_setDBInfo();
$this->_connect();
}
/**
* @brief 설치 가능 여부를 return
**/
function isSupported() {
if(!function_exists('sqlite_open')) return false;
return true;
}
/**
* @brief DB정보 설정 connect/ close
**/
function _setDBInfo() {
$db_info = Context::getDBInfo();
$this->database = $db_info->db_database;
$this->prefix = $db_info->db_table_prefix;
if(!substr($this->prefix,-1)!='_') $this->prefix .= '_';
}
/**
* @brief DB 접속
**/
function _connect() {
// db 정보가 없으면 무시
if(!$this->database) return;
// 데이터 베이스 파일 접속 시도
$this->fd = sqlite_open($this->database, 0666, &$error);
if(!file_exists($this->database) || $error) {
$this->setError(-1,$error);
$this->is_connected = false;
return;
}
// 접속체크
$this->is_connected = true;
}
/**
* @brief DB접속 해제
**/
function close() {
if(!$this->isConnected()) return;
sqlite_close($this->fd);
}
/**
* @brief 트랜잭션 시작
**/
function begin() {
if(!$this->is_connected || $this->transaction_started) return;
if($this->_query("BEGIN;")) $this->transaction_started = true;
}
/**
* @brief 롤백
**/
function rollback() {
if(!$this->is_connected || !$this->transaction_started) return;
$this->_query("ROLLBACK;");
$this->transaction_started = false;
}
/**
* @brief 커밋
**/
function commit($force = false) {
if(!$force && (!$this->isConnected() || !$this->transaction_started)) return;
if(!$this->is_connected || !$this->transaction_started) return;
$this->_query("COMMIT;");
$this->transaction_started = false;
}
/**
* @brief 쿼리에서 입력되는 문자열 변수들의 quotation 조절
**/
function addQuotes($string) {
if(get_magic_quotes_gpc()) $string = stripslashes(str_replace("\\","\\\\",$string));
if(!is_numeric($string)) $string = str_replace("'","''", $string);
return $string;
}
/**
* @brief : 쿼리문의 실행 결과의 fetch 처리
*
* query : query문 실행하고 result return\n
* fetch : reutrn 값이 없으면 NULL\n
* rows이면 array object\n
* row이면 object\n
* return\n
**/
function _query($query) {
if(!$this->isConnected()) return;
// 쿼리 시작을 알림
$this->actStart($query);
// 쿼리 문 실행
$result = @sqlite_query($query, $this->fd);
// 오류 체크
if(sqlite_last_error($this->fd)) $this->setError(sqlite_last_error($this->fd), sqlite_error_string(sqlite_last_error($this->fd)));
// 쿼리 실행 알림
$this->actFinish();
return $result;
}
/**
* @brief 결과를 fetch
**/
function _fetch($result) {
if($this->isError() || !$result) return;
while($tmp = sqlite_fetch_array($result, SQLITE_ASSOC)) {
unset($obj);
foreach($tmp as $key => $val) {
$pos = strpos($key, '.');
if($pos) $key = substr($key, $pos+1);
$obj->{$key} = $val;
}
$output[] = $obj;
}
if(count($output)==1) return $output[0];
return $output;
}
/**
* @brief 1 증가되는 sequence값을 return
**/
function getNextSequence() {
$query = sprintf("insert into %ssequence (seq) values ('')", $this->prefix);
$this->_query($query);
$sequence = sqlite_last_insert_rowid($this->fd);
$query = sprintf("delete from %ssequence where seq < %d", $this->prefix, $sequence);
$this->_query($query);
return $sequence;
}
/**
* @brief 테이블 기생성 여부 return
**/
function isTableExists($target_name) {
$query = sprintf('pragma table_info(%s%s)', $this->prefix, $this->addQuotes($target_name));
$result = $this->_query($query);
if(sqlite_num_rows($result)==0) return false;
return true;
}
/**
* @brief 특정 테이블에 특정 column 추가
**/
function addColumn($table_name, $column_name, $type='number', $size='', $default = '', $notnull=false) {
$type = $this->column_type[$type];
if(strtoupper($type)=='INTEGER') $size = '';
$query = sprintf("alter table %s%s add %s ", $this->prefix, $table_name, $column_name);
if($size) $query .= sprintf(" %s(%s) ", $type, $size);
else $query .= sprintf(" %s ", $type);
if($default) $query .= sprintf(" default '%s' ", $default);
if($notnull) $query .= " not null ";
return $this->_query($query);
}
/**
* @brief 특정 테이블의 column의 정보를 return
**/
function isColumnExists($table_name, $column_name) {
$query = sprintf("pragma table_info(%s%s)", $this->prefix, $table_name);
$result = $this->_query($query);
$output = $this->_fetch($result);
if($output) {
$column_name = strtolower($column_name);
foreach($output as $key => $val) {
$name = strtolower($val->name);
if($column_name == $name) return true;
}
}
return false;
}
/**
* @brief xml 받아서 테이블을 생성
**/
function createTableByXml($xml_doc) {
return $this->_createTable($xml_doc);
}
/**
* @brief xml 받아서 테이블을 생성
**/
function createTableByXmlFile($file_name) {
if(!file_exists($file_name)) return;
// xml 파일을 읽음
$buff = FileHandler::readFile($file_name);
return $this->_createTable($buff);
}
/**
* @brief schema xml을 이용하여 create table query생성
*
* type : number, varchar, text, char, date, \n
* opt : notnull, default, size\n
* index : primary key, index, unique\n
**/
function _createTable($xml_doc) {
// xml parsing
$oXml = new XmlParser();
$xml_obj = $oXml->parse($xml_doc);
// 테이블 생성 schema 작성
$table_name = $xml_obj->table->attrs->name;
if($this->isTableExists($table_name)) return;
$table_name = $this->prefix.$table_name;
if(!is_array($xml_obj->table->column)) $columns[] = $xml_obj->table->column;
else $columns = $xml_obj->table->column;
foreach($columns as $column) {
$name = $column->attrs->name;
$type = $column->attrs->type;
if(strtoupper($this->column_type[$type])=='INTEGER') $size = '';
else $size = $column->attrs->size;
$notnull = $column->attrs->notnull;
$primary_key = $column->attrs->primary_key;
$index = $column->attrs->index;
$unique = $column->attrs->unique;
$default = $column->attrs->default;
$auto_increment = $column->attrs->auto_increment;
if($auto_increment) {
$column_schema[] = sprintf('%s %s %s',
$name,
$this->column_type[$type],
$auto_increment?'AUTOINCREMENT':''
);
} else {
$column_schema[] = sprintf('%s %s%s %s %s %s %s',
$name,
$this->column_type[$type],
$size?'('.$size.')':'',
$notnull?'NOT NULL':'',
$primary_key?'PRIMARY KEY':'',
$default?"DEFAULT '".$default."'":'',
$auto_increment?'AUTOINCREMENT':''
);
}
if($unique) $unique_list[$unique][] = $name;
else if($index) $index_list[$index][] = $name;
}
$schema = sprintf('CREATE TABLE %s (%s%s) ;', $this->addQuotes($table_name)," ", implode($column_schema,", "));
$this->_query($schema);
if(count($unique_list)) {
foreach($unique_list as $key => $val) {
$query = sprintf('CREATE UNIQUE INDEX %s_%s ON %s (%s)', $this->addQuotes($table_name), $key, $this->addQuotes($table_name), implode(',',$val));
$this->_query($query);
}
}
if(count($index_list)) {
foreach($index_list as $key => $val) {
$query = sprintf('CREATE INDEX %s_%s ON %s (%s)', $this->addQuotes($table_name), $key, $this->addQuotes($table_name), implode(',',$val));
$this->_query($query);
}
}
}
/**
* @brief 조건문 작성하여 return
**/
function getCondition($output) {
if(!$output->conditions) return;
foreach($output->conditions as $key => $val) {
$sub_condition = '';
foreach($val['condition'] as $k =>$v) {
if(!$v['value']) continue;
$name = $v['column'];
$operation = $v['operation'];
$value = $v['value'];
$type = $this->getColumnType($output->column_type,$name);
$pipe = $v['pipe'];
$value = $this->getConditionValue($name, $value, $operation, $type);
if(!$value) $value = $v['value'];
$str = $this->getConditionPart($name, $value, $operation);
if($sub_condition) $sub_condition .= ' '.$pipe.' ';
$sub_condition .= $str;
}
if($sub_condition) {
if($condition && $val['pipe']) $condition .= ' '.$val['pipe'].' ';
$condition .= '('.$sub_condition.')';
}
}
if($condition) $condition = ' where '.$condition;
return $condition;
}
/**
* @brief insertAct 처리
**/
function _executeInsertAct($output) {
// 테이블 정리
foreach($output->tables as $key => $val) {
$table_list[] = $this->prefix.$key;
}
// 컬럼 정리
foreach($output->columns as $key => $val) {
$name = $val['name'];
$value = $val['value'];
if($output->column_type[$name]!='number') {
$value = "'".$this->addQuotes($value)."'";
if(!$value) $value = 'null';
} elseif(!$value || is_numeric($value)) $value = (int)$value;
$column_list[] = $name;
$value_list[] = $value;
}
$query = sprintf("insert into %s (%s) values (%s);", implode(',',$table_list), implode(',',$column_list), implode(',', $value_list));
return $this->_query($query);
}
/**
* @brief updateAct 처리
**/
function _executeUpdateAct($output) {
$table_count = count(array_values($output->tables));
// 대상 테이블이 1개일 경우
if($table_count == 1) {
// 테이블 정리
list($target_table) = array_keys($output->tables);
$target_table = $this->prefix.$target_table;
// 컬럼 정리
foreach($output->columns as $key => $val) {
if(!isset($val['value'])) continue;
$name = $val['name'];
$value = $val['value'];
if(strpos($name,'.')!==false&&strpos($value,'.')!==false) $column_list[] = $name.' = '.$value;
else {
if($output->column_type[$name]!='number') $value = "'".$this->addQuotes($value)."'";
elseif(!$value || is_numeric($value)) $value = (int)$value;
$column_list[] = sprintf("%s = %s", $name, $value);
}
}
// 조건절 정리
$condition = $this->getCondition($output);
$query = sprintf("update %s set %s %s", $target_table, implode(',',$column_list), $condition);
// 대상 테이블이 2개일 경우 (sqlite에서 update 테이블을 1개 이상 지정 못해서 이렇게 꽁수로... 다른 방법이 있으려나..)
} elseif($table_count == 2) {
// 테이블 정리
foreach($output->tables as $key => $val) {
$table_list[$val] = $this->prefix.$val;
}
list($source_table, $target_table) = array_values($table_list);
// 조건절 정리
$condition = $this->getCondition($output);
foreach($table_list as $key => $val) {
$condition = eregi_replace($key.'\\.', $val.'.', $condition);
}
// 컬럼 정리
foreach($output->columns as $key => $val) {
if(!isset($val['value'])) continue;
$name = $val['name'];
$value = $val['value'];
list($s_prefix, $s_column) = explode('.',$name);
list($t_prefix, $t_column) = explode('.',$value);
$s_table = $table_list[$s_prefix];
$t_table = $table_list[$t_prefix];
$column_list[] = sprintf(' %s = (select %s from %s %s) ', $s_column, $t_column, $t_table, $condition);
}
$query = sprintf('update %s set %s where exists(select * from %s %s)', $source_table, implode(',', $column_list), $target_table, $condition);
} else {
return;
}
return $this->_query($query);
}
/**
* @brief deleteAct 처리
**/
function _executeDeleteAct($output) {
// 테이블 정리
foreach($output->tables as $key => $val) {
$table_list[] = $this->prefix.$key;
}
// 조건절 정리
$condition = $this->getCondition($output);
$query = sprintf("delete from %s %s", implode(',',$table_list), $condition);
return $this->_query($query);
}
/**
* @brief selectAct 처리
*
* select의 경우 특정 페이지의 목록을 가져오는 것을 편하게 하기 위해\n
* navigation이라는 method를 제공
**/
function _executeSelectAct($output) {
// 테이블 정리
$table_list = array();
foreach($output->tables as $key => $val) {
$table_list[] = $this->prefix.$key.' as '.$val;
}
if(!$output->columns) {
$columns = '*';
} else {
$column_list = array();
foreach($output->columns as $key => $val) {
$name = $val['name'];
$alias = $val['alias'];
if($name == '*') {
$column_list[] = '*';
} elseif(strpos($name,'.')===false && strpos($name,'(')===false) {
if($alias) $column_list[] = sprintf('%s as %s', $name, $alias);
else $column_list[] = sprintf('%s',$name);
} else {
if($alias) $column_list[] = sprintf('%s as %s', $name, $alias);
else $column_list[] = sprintf('%s',$name);
}
}
$columns = implode(',',$column_list);
}
$condition = $this->getCondition($output);
if($output->list_count) return $this->_getNavigationData($table_list, $columns, $condition, $output);
$query = sprintf("select %s from %s %s", $columns, implode(',',$table_list), $condition);
if(count($output->groups)) $query .= sprintf(' group by %s', implode(',',$output->groups));
if($output->order) {
foreach($output->order as $key => $val) {
$index_list[] = sprintf('%s %s', $val[0], $val[1]);
}
if(count($index_list)) $query .= ' order by '.implode(',',$index_list);
}
$result = $this->_query($query);
if($this->isError()) return;
$data = $this->_fetch($result);
$buff = new Object();
$buff->data = $data;
return $buff;
}
/**
* @brief query xml에 navigation 정보가 있을 경우 페이징 관련 작업을 처리한다
*
* 그닥 좋지는 않은 구조이지만 편리하다.. -_-;
**/
function _getNavigationData($table_list, $columns, $condition, $output) {
require_once('./classes/page/PageHandler.class.php');
// 전체 개수를 구함
$count_query = sprintf("select count(*) as count from %s %s", implode(',',$table_list), $condition);
$result = $this->_query($count_query);
$count_output = $this->_fetch($result);
$total_count = (int)$count_output->count;
$list_count = $output->list_count['value'];
if(!$list_count) $list_count = 20;
$page_count = $output->page_count['value'];
if(!$page_count) $page_count = 10;
$page = $output->page['value'];
if(!$page) $page = 1;
// 전체 페이지를 구함
if($total_count) $total_page = (int)( ($total_count-1) / $list_count) + 1;
else $total_page = 1;
// 페이지 변수를 체크
if($page > $total_page) $page = $total_page;
$start_count = ($page-1)*$list_count;
$query = sprintf("select %s from %s %s", $columns, implode(',',$table_list), $condition);
if(count($output->groups)) $query .= sprintf(' group by %s', implode(',',$output->groups));
if($output->order) {
foreach($output->order as $key => $val) {
$index_list[] = sprintf('%s %s', $val[0], $val[1]);
}
if(count($index_list)) $query .= ' order by '.implode(',',$index_list);
}
$query = sprintf('%s limit %d, %d', $query, $start_count, $list_count);
$result = $this->_query($query);
if($this->isError()) {
$buff = new Object();
$buff->total_count = 0;
$buff->total_page = 0;
$buff->page = 1;
$buff->data = array();
$buff->page_navigation = new PageHandler($total_count, $total_page, $page, $page_count);
return $buff;
}
if($result) {
$virtual_no = $total_count - ($page-1)*$list_count;
while($tmp = sqlite_fetch_array($result, SQLITE_ASSOC)) {
unset($obj);
foreach($tmp as $key => $val) {
$pos = strpos($key, '.');
if($pos) $key = substr($key, $pos+1);
$obj->{$key} = $val;
}
$data[$virtual_no--] = $obj;
}
}
$buff = new Object();
$buff->total_count = $total_count;
$buff->total_page = $total_page;
$buff->page = $page;
$buff->data = $data;
$buff->page_navigation = new PageHandler($total_count, $total_page, $page, $page_count);
return $buff;
}
}
?>

View file

@ -0,0 +1,647 @@
<?php
/**
* @class DBSqlite3_pdo
* @author zero (zero@nzeo.com)
* @brief SQLite3를 PDO로 이용하여 class
* @version 0.1
**/
class DBSqlite3_pdo extends DB {
/**
* DB를 이용하기 위한 정보
**/
var $database = NULL; ///< database
var $prefix = 'xe'; ///< 제로보드에서 사용할 테이블들의 prefix (한 DB에서 여러개의 제로보드 설치 가능)
/**
* PDO 사용시 필요한 변수들
**/
var $handler = NULL;
var $stmt = NULL;
var $bind_idx = 0;
var $bind_vars = array();
/**
* @brief sqlite3 에서 사용될 column type
*
* column_type은 schema/query xml에서 공통 선언된 type을 이용하기 때문에
* DBMS에 맞게 replace 해주어야 한다
**/
var $column_type = array(
'bignumber' => 'INTEGER',
'number' => 'INTEGER',
'varchar' => 'VARHAR',
'char' => 'CHAR',
'text' => 'TEXT',
'bigtext' => 'TEXT',
'date' => 'VARCHAR(14)',
);
/**
* @brief constructor
**/
function DBSqlite3_pdo() {
$this->_setDBInfo();
$this->_connect();
}
/**
* @brief 설치 가능 여부를 return
**/
function isSupported() {
if(!class_exists('PDO')) return false;
return true;
}
/**
* @brief DB정보 설정 connect/ close
**/
function _setDBInfo() {
$db_info = Context::getDBInfo();
$this->database = $db_info->db_database;
$this->prefix = $db_info->db_table_prefix;
if(!substr($this->prefix,-1)!='_') $this->prefix .= '_';
}
/**
* @brief DB 접속
**/
function _connect() {
// db 정보가 없으면 무시
if(!$this->database) return;
// 데이터 베이스 파일 접속 시도
$this->handler = new PDO('sqlite:'.$this->database);
if(!file_exists($this->database) || $error) {
$this->setError(-1,'permission denied to access database');
//$this->setError(-1,$error);
$this->is_connected = false;
return;
}
// 접속체크
$this->is_connected = true;
}
/**
* @brief DB접속 해제
**/
function close() {
if(!$this->isConnected()) return;
$this->commit();
}
/**
* @brief 트랜잭션 시작
**/
function begin() {
if(!$this->isConnected() || $this->transaction_started) return;
if($this->handler->beginTransaction()) $this->transaction_started = true;
}
/**
* @brief 롤백
**/
function rollback() {
if(!$this->isConnected() || !$this->transaction_started) return;
$this->handler->rollBack();
$this->transaction_started = false;
}
/**
* @brief 커밋
**/
function commit($force = false) {
if(!$force && (!$this->isConnected() || !$this->transaction_started)) return;
$this->handler->commit();
$this->transaction_started = false;
}
/**
* @brief 쿼리에서 입력되는 문자열 변수들의 quotation 조절
**/
function addQuotes($string) {
if(get_magic_quotes_gpc()) $string = stripslashes(str_replace("\\","\\\\",$string));
if(!is_numeric($string)) $string = str_replace("'","''",$string);
return $string;
}
/**
* @brief : 쿼리문의 prepare
**/
function _prepare($query) {
if(!$this->isConnected()) return;
// 쿼리 시작을 알림
$this->actStart($query);
$this->stmt = $this->handler->prepare($query);
if($this->handler->errorCode() != '00000') {
$this->setError($this->handler->errorCode(), print_r($this->handler->errorInfo(),true));
$this->actFinish();
}
$this->bind_idx = 0;
$this->bind_vars = array();
}
/**
* @brief : stmt에 binding params
**/
function _bind($val) {
if(!$this->isConnected() || !$this->stmt) return;
$this->bind_idx ++;
$this->bind_vars[] = $val;
$this->stmt->bindParam($this->bind_idx, $val);
}
/**
* @brief : prepare된 쿼리의 execute
**/
function _execute() {
if(!$this->isConnected() || !$this->stmt) return;
$this->stmt->execute();
if($this->stmt->errorCode() === '00000') {
$output = null;
while($tmp = $this->stmt->fetch(PDO::FETCH_ASSOC)) {
unset($obj);
foreach($tmp as $key => $val) {
$pos = strpos($key, '.');
if($pos) $key = substr($key, $pos+1);
$obj->{$key} = str_replace("''","'",$val);
}
$output[] = $obj;
}
} else {
$this->setError($this->stmt->errorCode(),print_r($this->stmt->errorInfo(),true));
}
$this->stmt = null;
$this->actFinish();
if(is_array($output) && count($output)==1) return $output[0];
return $output;
}
/**
* @brief 1 증가되는 sequence값을 return
**/
function getNextSequence() {
$query = sprintf("insert into %ssequence (seq) values (NULL)", $this->prefix);
$this->_prepare($query);
$result = $this->_execute();
$sequence = $this->handler->lastInsertId();
$query = sprintf("delete from %ssequence where seq < %d", $this->prefix, $sequence);
$this->_prepare($query);
$result = $this->_execute();
return $sequence;
}
/**
* @brief 테이블 기생성 여부 return
**/
function isTableExists($target_name) {
$query = sprintf('pragma table_info(%s%s)', $this->prefix, $target_name);
$this->_prepare($query);
if(!$this->_execute()) return false;
return true;
}
/**
* @brief 특정 테이블에 특정 column 추가
**/
function addColumn($table_name, $column_name, $type='number', $size='', $default = '', $notnull=false) {
$type = $this->column_type[$type];
if(strtoupper($type)=='INTEGER') $size = '';
$query = sprintf("alter table %s%s add %s ", $this->prefix, $table_name, $column_name);
if($size) $query .= sprintf(" %s(%s) ", $type, $size);
else $query .= sprintf(" %s ", $type);
if($default) $query .= sprintf(" default '%s' ", $default);
if($notnull) $query .= " not null ";
$this->_prepare($query);
return $this->_execute();
}
/**
* @brief 특정 테이블의 column의 정보를 return
**/
function isColumnExists($table_name, $column_name) {
$query = sprintf("pragma table_info(%s%s)", $this->prefix, $table_name);
$this->_prepare($query);
$output = $this->_execute();
if($output) {
$column_name = strtolower($column_name);
foreach($output as $key => $val) {
$name = strtolower($val->name);
if($column_name == $name) return true;
}
}
return false;
}
/**
* @brief xml 받아서 테이블을 생성
**/
function createTableByXml($xml_doc) {
return $this->_createTable($xml_doc);
}
/**
* @brief xml 받아서 테이블을 생성
**/
function createTableByXmlFile($file_name) {
if(!file_exists($file_name)) return;
// xml 파일을 읽음
$buff = FileHandler::readFile($file_name);
return $this->_createTable($buff);
}
/**
* @brief schema xml을 이용하여 create table query생성
*
* type : number, varchar, text, char, date, \n
* opt : notnull, default, size\n
* index : primary key, index, unique\n
**/
function _createTable($xml_doc) {
// xml parsing
$oXml = new XmlParser();
$xml_obj = $oXml->parse($xml_doc);
// 테이블 생성 schema 작성
$table_name = $xml_obj->table->attrs->name;
if($this->isTableExists($table_name)) return;
$table_name = $this->prefix.$table_name;
if(!is_array($xml_obj->table->column)) $columns[] = $xml_obj->table->column;
else $columns = $xml_obj->table->column;
foreach($columns as $column) {
$name = $column->attrs->name;
$type = $column->attrs->type;
if(strtoupper($this->column_type[$type])=='INTEGER') $size = '';
else $size = $column->attrs->size;
$notnull = $column->attrs->notnull;
$primary_key = $column->attrs->primary_key;
$index = $column->attrs->index;
$unique = $column->attrs->unique;
$default = $column->attrs->default;
$auto_increment = $column->attrs->auto_increment;
if($auto_increment) {
$column_schema[] = sprintf('%s %s PRIMARY KEY %s',
$name,
$this->column_type[$type],
$auto_increment?'AUTOINCREMENT':''
);
} else {
$column_schema[] = sprintf('%s %s%s %s %s %s',
$name,
$this->column_type[$type],
$size?'('.$size.')':'',
$notnull?'NOT NULL':'',
$primary_key?'PRIMARY KEY':'',
$default?"DEFAULT '".$default."'":''
);
}
if($unique) $unique_list[$unique][] = $name;
else if($index) $index_list[$index][] = $name;
}
$schema = sprintf('CREATE TABLE %s (%s%s) ;', $table_name," ", implode($column_schema,", "));
$this->_prepare($schema);
$this->_execute();
if($this->isError()) return;
if(count($unique_list)) {
foreach($unique_list as $key => $val) {
$query = sprintf('CREATE UNIQUE INDEX %s_%s ON %s (%s)', $this->addQuotes($table_name), $key, $this->addQuotes($table_name), implode(',',$val));
$this->_prepare($query);
$this->_execute();
if($this->isError()) $this->rollback();
}
}
if(count($index_list)) {
foreach($index_list as $key => $val) {
$query = sprintf('CREATE INDEX %s_%s ON %s (%s)', $this->addQuotes($table_name), $key, $this->addQuotes($table_name), implode(',',$val));
$this->_prepare($query);
$this->_execute();
if($this->isError()) $this->rollback();
}
}
}
/**
* @brief 조건문 작성하여 return
**/
function getCondition($output) {
if(!$output->conditions) return;
foreach($output->conditions as $key => $val) {
$sub_condition = '';
foreach($val['condition'] as $k =>$v) {
if(!$v['value']) continue;
$name = $v['column'];
$operation = $v['operation'];
$value = $v['value'];
$type = $this->getColumnType($output->column_type,$name);
$pipe = $v['pipe'];
$value = $this->getConditionValue($name, $value, $operation, $type);
if(!$value) $value = $v['value'];
$str = $this->getConditionPart($name, $value, $operation);
if($sub_condition) $sub_condition .= ' '.$pipe.' ';
$sub_condition .= $str;
}
if($sub_condition) {
if($condition && $val['pipe']) $condition .= ' '.$val['pipe'].' ';
$condition .= '('.$sub_condition.')';
}
}
if($condition) $condition = ' where '.$condition;
return $condition;
}
/**
* @brief insertAct 처리
**/
function _executeInsertAct($output) {
// 테이블 정리
foreach($output->tables as $key => $val) {
$table_list[] = $this->prefix.$key;
}
// 컬럼 정리
foreach($output->columns as $key => $val) {
$name = $val['name'];
$value = $val['value'];
$key_list[] = $name;
if($output->column_type[$name]!='number') $val_list[] = $this->addQuotes($value);
else {
if(!$value || is_numeric($value)) $value = (int)$value;
$val_list[] = $value;
}
$prepare_list[] = '?';
}
$query = sprintf("INSERT INTO %s (%s) VALUES (%s);", implode(',',$table_list), implode(',',$key_list), implode(',',$prepare_list));
$this->_prepare($query);
$val_count = count($val_list);
for($i=0;$i<$val_count;$i++) $this->_bind($val_list[$i]);
return $this->_execute();
}
/**
* @brief updateAct 처리
**/
function _executeUpdateAct($output) {
$table_count = count(array_values($output->tables));
// 대상 테이블이 1개일 경우
if($table_count == 1) {
// 테이블 정리
list($target_table) = array_keys($output->tables);
$target_table = $this->prefix.$target_table;
// 컬럼 정리
foreach($output->columns as $key => $val) {
if(!isset($val['value'])) continue;
$name = $val['name'];
$value = $val['value'];
if(strpos($name,'.')!==false&&strpos($value,'.')!==false) $column_list[] = $name.' = '.$value;
else {
if($output->column_type[$name]!='number') $value = "'".$this->addQuotes($value)."'";
elseif(!$value || is_numeric($value)) $value = (int)$value;
$column_list[] = sprintf("%s = %s", $name, $value);
}
}
// 조건절 정리
$condition = $this->getCondition($output);
$query = sprintf("update %s set %s %s", $target_table, implode(',',$column_list), $condition);
// 대상 테이블이 2개일 경우 (sqlite에서 update 테이블을 1개 이상 지정 못해서 이렇게 꽁수로... 다른 방법이 있으려나..)
} elseif($table_count == 2) {
// 테이블 정리
foreach($output->tables as $key => $val) {
$table_list[$val] = $this->prefix.$val;
}
list($source_table, $target_table) = array_values($table_list);
// 조건절 정리
$condition = $this->getCondition($output);
foreach($table_list as $key => $val) {
$condition = eregi_replace($key.'\\.', $val.'.', $condition);
}
// 컬럼 정리
foreach($output->columns as $key => $val) {
if(!isset($val['value'])) continue;
$name = $val['name'];
$value = $val['value'];
list($s_prefix, $s_column) = explode('.',$name);
list($t_prefix, $t_column) = explode('.',$value);
$s_table = $table_list[$s_prefix];
$t_table = $table_list[$t_prefix];
$column_list[] = sprintf(' %s = (select %s from %s %s) ', $s_column, $t_column, $t_table, $condition);
}
$query = sprintf('update %s set %s where exists(select * from %s %s)', $source_table, implode(',', $column_list), $target_table, $condition);
} else {
return;
}
$this->_prepare($query);
return $this->_execute();
}
/**
* @brief deleteAct 처리
**/
function _executeDeleteAct($output) {
// 테이블 정리
foreach($output->tables as $key => $val) {
$table_list[] = $this->prefix.$key;
}
// 조건절 정리
$condition = $this->getCondition($output);
$query = sprintf("delete from %s %s", implode(',',$table_list), $condition);
$this->_prepare($query);
return $this->_execute();
}
/**
* @brief selectAct 처리
*
* select의 경우 특정 페이지의 목록을 가져오는 것을 편하게 하기 위해\n
* navigation이라는 method를 제공
**/
function _executeSelectAct($output) {
// 테이블 정리
$table_list = array();
foreach($output->tables as $key => $val) {
$table_list[] = $this->prefix.$key.' as '.$val;
}
if(!$output->columns) {
$columns = '*';
} else {
$column_list = array();
foreach($output->columns as $key => $val) {
$name = $val['name'];
$alias = $val['alias'];
if($name == '*') {
$column_list[] = '*';
} elseif(strpos($name,'.')===false && strpos($name,'(')===false) {
if($alias) $column_list[] = sprintf('%s as %s', $name, $alias);
else $column_list[] = sprintf('%s',$name);
} else {
if($alias) $column_list[] = sprintf('%s as %s', $name, $alias);
else $column_list[] = sprintf('%s',$name);
}
}
$columns = implode(',',$column_list);
}
$condition = $this->getCondition($output);
if($output->list_count) return $this->_getNavigationData($table_list, $columns, $condition, $output);
$query = sprintf("select %s from %s %s", $columns, implode(',',$table_list), $condition);
if(count($output->groups)) $query .= sprintf(' group by %s', implode(',',$output->groups));
if($output->order) {
foreach($output->order as $key => $val) {
$index_list[] = sprintf('%s %s', $val[0], $val[1]);
}
if(count($index_list)) $query .= ' order by '.implode(',',$index_list);
}
$this->_prepare($query);
$data = $this->_execute();
if($this->isError()) return;
$buff = new Object();
$buff->data = $data;
return $buff;
}
/**
* @brief query xml에 navigation 정보가 있을 경우 페이징 관련 작업을 처리한다
*
* 그닥 좋지는 않은 구조이지만 편리하다.. -_-;
**/
function _getNavigationData($table_list, $columns, $condition, $output) {
require_once('./classes/page/PageHandler.class.php');
// 전체 개수를 구함
$count_query = sprintf("select count(*) as count from %s %s", implode(',',$table_list), $condition);
$this->_prepare($count_query);
$count_output = $this->_execute();
$total_count = (int)$count_output->count;
$list_count = $output->list_count['value'];
if(!$list_count) $list_count = 20;
$page_count = $output->page_count['value'];
if(!$page_count) $page_count = 10;
$page = $output->page['value'];
if(!$page) $page = 1;
// 전체 페이지를 구함
if($total_count) $total_page = (int)( ($total_count-1) / $list_count) + 1;
else $total_page = 1;
// 페이지 변수를 체크
if($page > $total_page) $page = $total_page;
$start_count = ($page-1)*$list_count;
$query = sprintf("select %s from %s %s", $columns, implode(',',$table_list), $condition);
if(count($output->groups)) $query .= sprintf(' group by %s', implode(',',$output->groups));
if($output->order) {
foreach($output->order as $key => $val) {
$index_list[] = sprintf('%s %s', $val[0], $val[1]);
}
if(count($index_list)) $query .= ' order by '.implode(',',$index_list);
}
// return 결과물 생성
$buff = new Object();
$buff->total_count = 0;
$buff->total_page = 0;
$buff->page = 1;
$buff->data = array();
$buff->page_navigation = new PageHandler($total_count, $total_page, $page, $page_count);
// 쿼리 실행
$query = sprintf('%s limit %d, %d', $query, $start_count, $list_count);
$this->_prepare($query);
if($this->isError()) {
$this->setError($this->handler->errorCode(), print_r($this->handler->errorInfo(),true));
$this->actFinish();
return $buff;
}
$this->stmt->execute();
if($this->stmt->errorCode() != '00000') {
$this->setError($this->stmt->errorCode(), print_r($this->stmt->errorInfo(),true));
$this->actFinish();
return $buff;
}
$output = null;
$virtual_no = $total_count - ($page-1)*$list_count;
while($tmp = $this->stmt->fetch(PDO::FETCH_ASSOC)) {
unset($obj);
foreach($tmp as $key => $val) {
$pos = strpos($key, '.');
if($pos) $key = substr($key, $pos+1);
$obj->{$key} = $val;
}
$data[$virtual_no--] = $obj;
}
$this->stmt = null;
$this->actFinish();
$buff = new Object();
$buff->total_count = $total_count;
$buff->total_page = $total_page;
$buff->page = $page;
$buff->data = $data;
$buff->page_navigation = new PageHandler($total_count, $total_page, $page, $page_count);
return $buff;
}
}
?>

View file

@ -0,0 +1,237 @@
<?php
/**
* @class DisplayHandler
* @author zero (zero@nzeo.com)
* @brief 데이터 출력을 위한 class (XML/HTML 데이터를 구분하여 출력)
*
* Response Method에 따라서 html or xml 출력방법을 결정한다
* xml : oModule의 variables를 simple xml 출력
* html : oModule의 template/variables로 html을 만들고 contents_html로 처리
* widget이나 layout의 html과 연동하여 출력
**/
class DisplayHandler extends Handler {
var $content_size = 0; ///< 출력하는 컨텐츠의 사이즈
/**
* @brief 모듈객체를 받아서 content 출력
**/
function printContent(&$oModule) {
// header 출력
$this->_printHeader();
// request method에 따른 처리
$content = $this->getContent($oModule);
// 요청방식에 따라 출력을 별도로
if(Context::getResponseMethod()!="XMLRPC") {
Context::set('content', $content);
// 레이아웃을 컴파일
if(__DEBUG__==3) $start = getMicroTime();
$oTemplate = &TemplateHandler::getInstance();
if(Context::get('layout') != 'none') {
$layout_path = $oModule->getLayoutPath();
$layout_file = $oModule->getLayoutFile();
$edited_layout_file = $oModule->getEditedLayoutFile();
}
if(!$layout_path) $layout_path = './common/tpl/';
if(!$layout_file) $layout_file = 'default_layout.html';
$zbxe_final_content = $oTemplate->compile($layout_path, $layout_file, $edited_layout_file);
if(__DEBUG__==3) $GLOBALS['__layout_compile_elapsed__'] = getMicroTime()-$start;
// 각 위젯, 에디터 컴포넌트의 코드 변경
if(__DEBUG__==3) $start = getMicroTime();
$oContext = &Context::getInstance();
$zbxe_final_content= $oContext->transContent($zbxe_final_content);
if(__DEBUG__==3) $GLOBALS['__trans_widget_editor_elapsed__'] = getMicroTime()-$start;
// 최종 결과를 common_layout에 넣어버림
Context::set('zbxe_final_content', $zbxe_final_content);
$output = $oTemplate->compile('./common/tpl', 'common_layout');
} else {
$output = $content;
}
// 애드온 실행
$called_position = 'before_display_content';
@include("./files/cache/activated_addons.cache.php");
$this->content_size = strlen($output);
// 컨텐츠 출력
$this->display($output);
// 디버깅 데이터 출력
$this->_debugOutput();
}
/**
* @brief 최종 결과물의 출력
**/
function display($content) {
$path = str_replace('index.php','',$_SERVER['SCRIPT_NAME']);
// files로 시작되는 src나 href의 값을 절대경로로 변경
$content = preg_replace('!(href|src)=("|\'){0,1}files!is', '\\1=\\2'.$path.'files', $content);
print preg_replace('!(href|src)=("|\'){0,1}\.\/([a-zA-Z0-9\_^\/]+)\/!is', '\\1=\\2'.$path.'$3/', $content);
}
/**
* @brief 모듈 객체의 content return
**/
function getContent(&$oModule) {
return $this->_toDoc($oModule);
}
/**
* @brief 모듈 객체의 content return
**/
function _toDoc(&$oModule) {
if(Context::getRequestMethod() == 'XMLRPC') $content = $this->_toXmlDoc($oModule);
else $content = $this->_toHTMLDoc($oModule);
return $content;
}
/**
* @brief RequestMethod가 XML이면 XML 데이터로 컨텐츠 생성
**/
function _toXmlDoc(&$oModule) {
$xmlDoc = "<response>\n";
$xmlDoc .= sprintf("<error>%s</error>\n",$oModule->getError());
$xmlDoc .= sprintf("<message>%s</message>\n",str_replace(array('<','>','&'),array('&lt;','&gt;','&amp;'),$oModule->getMessage()));
$variables = $oModule->getVariables();
if(count($variables)) {
foreach($variables as $key => $val) {
if(is_string($val)) $val = '<![CDATA['.$val.']]>';
$xmlDoc .= "<{$key}>{$val}</{$key}>\n";
}
}
$xmlDoc .= "</response>";
return $xmlDoc;
}
/**
* @brief RequestMethod가 XML이 아니면 html 컨텐츠 생성
**/
function _toHTMLDoc(&$oModule) {
// template handler 객체 생성
$oTemplate = &TemplateHandler::getInstance();
// module tpl 변환
$template_path = $oModule->getTemplatePath();
$tpl_file = $oModule->getTemplateFile();
return $oTemplate->compile($template_path, $tpl_file);
}
/**
* @brief content size return
**/
function getContentSize() {
return $this->content_size;
}
/**
* @brief 디버그 모드일 경우 디버기 메세지 출력
*
* __DEBUG__가 1이상일 경우 부분의 실행시간등을 debugPrint 함수를 이용해서 출력\n
* 개발시나 테스트시에 config/config.inc.php의 __DEBUG__를 세팅하고\n
* tail -f ./files/_debug_message.php로 하여 console로 확인하면 편리함\n
**/
function _debugOutput() {
if(!__DEBUG__ || (__DEBUG_OUTPUT!=0 && Context::getResponseMethod()!='HTML') ) return;
$end = getMicroTime();
// debug string 작성 시작
$buff = "\n\n** Debug at ".date('Y-m-d H:i:s')." ************************************************************\n";
// Request/Response 정보 작성
$buff .= "\n- Request/ Response info\n";
$buff .= sprintf("\tRequest URI \t\t\t: %s:%s%s%s%s\n", $_SERVER['SERVER_NAME'], $_SERVER['SERVER_PORT'], $_SERVER['PHP_SELF'], $_SERVER['QUERY_STRING']?'?':'', $_SERVER['QUERY_STRING']);
$buff .= sprintf("\tRequest method \t\t\t: %s\n", $_SERVER['REQUEST_METHOD']);
$buff .= sprintf("\tResponse method \t\t: %s\n", Context::getResponseMethod());
$buff .= sprintf("\tResponse contents size\t\t: %d byte\n", $this->getContentSize());
// DB 로그 작성
if(__DEBUG__>1) {
if($GLOBALS['__db_queries__']) {
$buff .= "\n- DB Queries\n";
$buff .= $GLOBALS['__db_queries__'];
}
$buff .= "\n- Elapsed time\n";
if($GLOBALS['__db_elapsed_time__']) $buff .= sprintf("\tDB queries elapsed time\t\t: %0.5f sec\n", $GLOBALS['__db_elapsed_time__']);
}
// 기타 로그 작성
if(__DEBUG__==3) {
$buff .= sprintf("\tclass file load elapsed time \t: %0.5f sec\n", $GLOBALS['__elapsed_class_load__']);
$buff .= sprintf("\tTemplate compile elapsed time\t: %0.5f sec (%d called)\n", $GLOBALS['__template_elapsed__'], $GLOBALS['__TemplateHandlerCalled__']);
$buff .= sprintf("\tXmlParse compile elapsed time\t: %0.5f sec\n", $GLOBALS['__xmlparse_elapsed__']);
$buff .= sprintf("\tPHP elapsed time \t\t: %0.5f sec\n", $end-__StartTime__-$GLOBALS['__template_elapsed__']-$GLOBALS['__xmlparse_elapsed__']-$GLOBALS['__db_elapsed_time__']-$GLOBALS['__elapsed_class_load__']);
// 위젯 실행 시간 작성
$buff .= sprintf("\n\tWidgets elapsed time \t\t: %0.5f sec", $GLOBALS['__widget_excute_elapsed__']);
// 레이아웃 실행 시간
$buff .= sprintf("\n\tLayout compile elapsed time \t: %0.5f sec", $GLOBALS['__layout_compile_elapsed__']);
// 위젯, 에디터 컴포넌트 치환 시간
$buff .= sprintf("\n\tTrans widget&editor elapsed time: %0.5f sec\n\n", $GLOBALS['__trans_widget_editor_elapsed__']);
}
// 전체 실행 시간 작성
$buff .= sprintf("\tTotal elapsed time \t\t: %0.5f sec", $end-__StartTime__);
debugPrint($buff, false);
}
/**
* @brief RequestMethod에 맞춰 헤더 출력
***/
function _printHeader() {
if(Context::getResponseMethod() != 'HTML') return $this->_printXMLHeader();
else return $this->_printHTMLHeader();
}
/**
* @brief xml header 출력 (utf8 고정)
**/
function _printXMLHeader() {
header("Content-Type: text/xml; charset=UTF-8");
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
}
/**
* @brief html header 출력 (utf8 고정)
**/
function _printHTMLHeader() {
header("Content-Type: text/html; charset=UTF-8");
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
}
}
?>

View file

@ -0,0 +1,27 @@
<?php
/**
* @class EditorHandler
* @author zero (zero@nzeo.com)
* @brief edit component의 상위 클래스임
*
* 주로 하는 일은 컴포넌트 요청시 컴포넌트에서 필요로 하는 변수를 세팅해준다
**/
class EditorHandler extends Object {
/**
* @brief 컴포넌트의 xml및 관련 정보들을 설정
**/
function setInfo($info) {
Context::set('component_info', $info);
if(!$info->extra_vars) return;
foreach($info->extra_vars as $key => $val) {
$this->{$key} = trim($val->value);
}
}
}
?>

View file

@ -0,0 +1,276 @@
<?php
/**
* @class FileHandler
* @author zero (zero@nzeo.com)
* @brief 파일시스템을 쉽게 사용하기 위한 method를 모은 클래스
*
* 굳이 class로 만들필요는 없는데.. 소스 코드의 관리를 위하여..
**/
class FileHandler extends Handler {
/**
* @brief 파일의 내용을 읽어서 return
**/
function readFile($file_name) {
if(!file_exists($file_name)) return;
$filesize = filesize($file_name);
if($filesize<1) return;
$fp = fopen($file_name, "r");
$buff = '';
if($fp) {
while(!feof($fp) && strlen($buff)<=$filesize) {
$str = fgets($fp, 1024);
$buff .= $str;
}
fclose($fp);
}
return $buff;
}
/**
* @brief $buff의 내용을 파일에 쓰기
**/
function writeFile($file_name, $buff, $mode = "w") {
$mode = strtolower($mode);
if($mode != "a") $mode = "w";
if(@!$fp = fopen($file_name,$mode)) return false;
fwrite($fp, $buff);
fclose($fp);
}
/**
* @brief 특정 디렉토리를 이동
**/
function moveDir($source_dir, $target_dir) {
if(!is_dir($source_dir)) return;
if(!is_dir($target_dir)) {
FileHandler::makeDir($target_dir);
@unlink($target_dir);
}
@rename($source_dir, $target_dir);
}
/**
* @brief $path내의 파일들을 return ('.', '..', '.로 시작하는' 파일들은 제외)
**/
function readDir($path, $filter = '', $to_lower = false, $concat_prefix = false) {
if(substr($path,-1)!='/') $path .= '/';
if(!is_dir($path)) return array();
$oDir = dir($path);
while($file = $oDir->read()) {
if(substr($file,0,1)=='.') continue;
if($filter && !preg_match($filter, $file)) continue;
if($to_lower) $file = strtolower($file);
if($filter) $file = preg_replace($filter, '$1', $file);
else $file = $file;
if($concat_prefix) $file = $path.$file;
$output[] = $file;
}
if(!$output) return array();
return $output;
}
/**
* @brief 디렉토리 생성
*
* 주어진 경로를 단계별로 접근하여 recursive하게 디렉토리 생성
**/
function makeDir($path_string) {
$path_list = explode('/', $path_string);
for($i=0;$i<count($path_list);$i++) {
if(!$path_list[$i]) continue;
$path .= $path_list[$i].'/';
if(!is_dir($path)) {
@mkdir($path, 0777);
@chmod($path, 0777);
}
}
return is_dir($path_string);
}
/**
* @brief 지정된 디렉토리 이하 모두 파일을 삭제
**/
function removeDir($path) {
if(!is_dir($path)) return;
$directory = dir($path);
while($entry = $directory->read()) {
if ($entry != "." && $entry != "..") {
if (is_dir($path."/".$entry)) {
FileHandler::removeDir($path."/".$entry);
} else {
@unlink($path."/".$entry);
}
}
}
$directory->close();
@rmdir($path);
}
/**
* @brief byte단위의 파일크기를 적절하게 변환해서 return
**/
function filesize($size) {
if(!$size) return "0Byte";
if($size<1024) return ($size."Byte");
if($size >1024 && $size< 1024 *1024) return sprintf("%0.1fKB",$size / 1024);
return sprintf("%0.2fMB",$size / (1024*1024));
}
/**
* @brief 원격파일을 다운받아서 특정 위치에 저장
**/
function getRemoteFile($url, $target_filename) {
$url_info = parse_url($url);
if(!$url_info['port']) $url_info['port'] = 80;
$fp = @fsockopen($url_info['host'], $url_info['port']);
if(!$fp) return;
// 한글 파일이 있으면 한글파일 부분만 urlencode하여 처리 (iconv 필수)
$path = $url_info['path'];
if(preg_match('/[\xEA-\xED][\x80-\xFF]{2}/', $path)&&function_exists('iconv')) {
$path_list = explode('/',$path);
$cnt = count($path_list);
$filename = $path_list[$cnt-1];
$filename = urlencode(iconv("UTF-8","EUC-KR",$filename));
$path_list[$cnt-1] = $filename;
$path = implode('/',$path_list);
$url_info['path'] = $path;
}
$header = sprintf("GET %s HTTP/2.0\r\nHost: %s\r\nReferer: %s://%s\r\nRequestUrl: %s\r\nConnection: Close\r\n\r\n", $url_info['path'], $url_info['host'], $url_info['scheme'], $url_info['host'], Context::getRequestUri());
@fwrite($fp, $header);
$ft = @fopen($target_filename, 'w');
if(!$ft) return;
$begin = false;
while(!feof($fp)) {
$str = fgets($fp, 1024);
if($begin) @fwrite($ft, $str);
if(!trim($str)) $begin = true;
}
@fclose($ft);
@fclose($fp);
return true;
}
/**
* @brief 특정 이미지 파일을 특정 위치로 옮김 (옮길때 이미지의 크기를 리사이징할 있음..)
**/
function createImageFile($source_file, $target_file, $resize_width = 0, $resize_height = 0, $target_type = '') {
if(!file_exists($source_file)) return;
// 이미지 정보를 구함
list($width, $height, $type, $attrs) = @getimagesize($source_file);
if($width<1 || $height<1) return;
switch($type) {
case '1' :
$type = 'gif';
break;
case '2' :
$type = 'jpg';
break;
case '3' :
$type = 'png';
break;
case '6' :
$type = 'bmp';
break;
default :
return;
break;
}
// 타겟 파일의 type을 구함
if(!$target_type) $target_type = $type;
$target_type = strtolower($target_type);
// 리사이즈를 원하는 크기의 임시 이미지를 만듬
if(function_exists('imagecreatetruecolor')) $thumb = @imagecreatetruecolor($resize_width, $resize_height);
else $thumb = @imagecreate($resize_width, $resize_height);
$white = @imagecolorallocate($thumb, 255,255,255);
@imagefilledrectangle($thumb,0,0,$resize_width-1,$resize_height-1,$white);
// 이미지 정보가 정해진 크기보다 크면 크기를 바꿈 (%를 구해서 처리)
if($resize_width>0 && $width >= $resize_width) $width_per = $resize_width / $width;
if($resize_height>0 && $height >= $resize_height) $height_per = $resize_height / $height;
if($width_per < $height_per) $per = $height_per;
else $per = $width_per;
// 원본 이미지의 타입으로 임시 이미지 생성
switch($type) {
case 'gif' :
$source = @imagecreatefromgif($source_file);
break;
// jpg
case 'jpeg' :
case 'jpg' :
$source = @imagecreatefromjpeg($source_file);
break;
// png
case 'png' :
$source = @imagecreatefrompng($source_file);
break;
// bmp
case 'wbmp' :
case 'bmp' :
$source = @imagecreatefromwbmp($source_file);
break;
default :
return;
}
// 디렉토리 생성
$path = preg_replace('/\/([^\.^\/]*)\.(gif|png|jpeg|bmp|wbmp)$/i','',$target_file);
FileHandler::makeDir($path);
// 원본 이미지의 크기를 조절해서 임시 이미지에 넣음
$new_width = (int)($width * $per);
$new_height = (int)($height * $per);
$x = ($resize_width/2 - $new_width/2);
$y = ($resize_height/2 - $new_height/2);
if($source) {
if($new_width != $width || $new_height != $height) {
if(function_exists('imagecopyresampled')) @imagecopyresampled($thumb, $source, $x, $y, 0, 0, $new_width, $new_height, $width, $height);
else @imagecopyresized($thumb, $source, $x, $y, 0, 0, $new_width, $new_height, $width, $height);
} else $thumb = $source;
}
// 파일을 쓰고 끝냄
switch($target_type) {
case 'gif' :
@imagegif($thumb, $target_file, 100);
break;
case 'jpeg' :
case 'jpg' :
@imagejpeg($thumb, $target_file, 100);
break;
case 'png' :
@imagepng($thumb, $target_file, 100);
break;
case 'wbmp' :
case 'bmp' :
@imagewbmp($thumb, $target_file, 100);
break;
}
}
}
?>

View file

@ -0,0 +1,11 @@
<?php
/**
* @class Handler
* @author zero (zero@nzeo.com)
* @brief (*)Handler 클래스의 추상화 클래스
**/
class Handler {
}
?>

View file

@ -0,0 +1,326 @@
<?php
/**
* @class ModuleHandler
* @author zero (zero@nzeo.com)
* @brief 모듈 핸들링을 위한 Handler
*
* 모듈을 실행시키기 위한 클래스.
* constructor에 아무 인자 없이 객체를 생성하면 현재 요청받은
* 상태를 바탕으로 적절한 모듈을 찾게 되고,
* 별도의 인자 값을 경우 그에 맞는 모듈을 찾아서 실행한다.
* 만약 찾아진 모듈의 요청된 act 없으면 action_foward를 참조하여 다른 모듈의 act를 실행한다.
**/
class ModuleHandler extends Handler {
var $oModule = NULL; ///< 모듈 객체
var $module = NULL; ///< 모듈
var $act = NULL; ///< action
var $mid = NULL; ///< 모듈의 객체명
var $document_srl = NULL; ///< 문서 번호
var $module_info = NULL; ///< 모듈의 정보
var $error = NULL; ///< 진행 도중 에러 발생시 에러 코드를 정의, message 모듈을 호출시 사용
/**
* @brief constructor
*
* ModuleHandler에서 사용할 변수를 미리 세팅
* 인자를 넘겨주지 않으면 페이지 요청받은 Request Arguments를 이용하여
* 변수를 세팅한다.
**/
function ModuleHandler($module = '', $act = '', $mid = '', $document_srl = '') {
// 설치가 안되어 있다면 install module을 지정
if(!Context::isInstalled()) {
$this->module = 'install';
$this->act = Context::get('act');
return;
}
// Request Argument중 모듈을 찾을 수 있는 변수를 구함
if(!$module) $this->module = Context::get('module');
else $this->module = $module;
if(!$act) $this->act = Context::get('act');
else $this->act = $act;
if(!$mid) $this->mid = Context::get('mid');
else $this->mid = $mid;
if(!$document_srl) $this->document_srl = (int)Context::get('document_srl');
else $this->document_srl = (int)$document_srl;
// 기본 변수들의 검사 (XSS방지를 위한 기초적 검사)
if($this->module && !eregi("^([a-z0-9\_\-]+)$",$this->module)) die(Context::getLang("msg_invalid_request"));
if($this->mid && !eregi("^([a-z0-9\_\-]+)$",$this->mid)) die(Context::getLang("msg_invalid_request"));
if($this->act && !eregi("^([a-z0-9\_\-]+)$",$this->act)) die(Context::getLang("msg_invalid_request"));
// 애드온 실행 (모듈 실행 전)
$called_position = 'before_module_init';
@include("./files/cache/activated_addons.cache.php");
}
/**
* @brief module, mid, document_srl을 이용하여 모듈을 찾고 act를 실행하기 위한 준비를
**/
function init() {
// ModuleModel 객체 생성
$oModuleModel = &getModel('module');
// document_srl이 있으면 document_srl로 모듈과 모듈 정보를 구함
if($this->document_srl && !$this->mid) $module_info = $oModuleModel->getModuleInfoByDocumentSrl($this->document_srl);
if($this->module && $module_info->module != $this->module) unset($module_info);
// 아직 모듈을 못 찾았고 $mid값이 있으면 $mid로 모듈을 구함
if(!$module_info && $this->mid) $module_info = $oModuleModel->getModuleInfoByMid($this->mid);
if($this->module && $module_info->module != $this->module) unset($module_info);
// 역시 모듈을 못 찾았고 $module이 없다면 기본 모듈을 찾아봄
if(!$module_info && !$this->module) $module_info = $oModuleModel->getModuleInfoByMid();
// 모듈 정보가 찾아졌을 경우 모듈 정보에서 기본 변수들을 구함, 모듈 정보에서 module 이름을 구해움
if($module_info) {
$this->module = $module_info->module;
$this->mid = $module_info->mid;
$this->module_info = $module_info;
Context::setBrowserTitle($module_info->browser_title);
}
// 여기까지도 모듈 정보를 찾지 못했다면 깔끔하게 시스템 오류 표시
if(!$this->module) $this->error = 'msg_module_is_not_exists';
// mid값이 있을 경우 mid값을 세팅
if($this->mid) Context::set('mid', $this->mid, true);
}
/**
* @brief 모듈과 관련된 정보를 이용하여 객체를 구하고 act 실행까지 진행시킴
**/
function procModule() {
// 에러가 있으면 return
if($this->error) return;
// ModuleModel 객체 생성
$oModuleModel = &getModel('module');
// 해당 모듈의 conf/action.xml 을 분석하여 action 정보를 얻어옴
$xml_info = $oModuleModel->getModuleActionXml($this->module);
// 미설치시에는 act값을 강제로 변경
if($this->module=="install") {
if(!$this->act || !$xml_info->action->{$this->act}) $this->act = $xml_info->default_index_act;
}
// 현재 요청된 act가 있으면 $xml_info에서 type을 찾음, 없다면 기본 action을 이용
if(!$this->act) $this->act = $xml_info->default_index_act;
// act값이 지정이 안되어 있으면 오류 표시
if(!$this->act) {
$this->error = 'msg_module_is_not_exists';
return;
}
// 설정된 mid가 없을 경우 요청된 act의 standalone 여부 체크
/*
if(!$this->mid && !$xml_info->action->{$this->act}->standalone) {
$this->error = 'msg_module_is_not_standalone';
return;
}
*/
// type, grant 값 구함
$type = $xml_info->action->{$this->act}->type;
$grant = $xml_info->action->{$this->act}->grant;
$kind = strpos(strtolower($this->act),'admin')!==false?'admin':'';
if(!$kind && $this->module == 'admin') $kind = 'admin';
// 모듈 객체 생성
$oModule = &$this->getModuleInstance($this->module, $type, $kind);
if(!is_object($oModule)) {
$this->error = 'msg_module_is_not_exists';
return;
}
// 모듈에 act값을 세팅
$oModule->setAct($this->act);
// 모듈 정보 세팅
$oModule->setModuleInfo($this->module_info, $xml_info);
// 모듈을 수행하고 결과가 false이면 message 모듈 호출 지정
if(!$oModule->proc()) $this->error = $oModule->getMessage();
return $oModule;
}
/**
* @ 실행된 모듈의 컨텐츠를 출력
**/
function displayContent($oModule = NULL) {
// 설정된 모듈이 정상이지 않을 경우 message 모듈 객체 생성
if(!$oModule || !is_object($oModule)) {
$this->error = 'msg_module_is_not_exists';
}
// 에러가 발생하였을시 처리
if($this->error) {
// message 모듈 객체를 생성해서 컨텐츠 생성
$oMessageView = &getView('message');
$oMessageView->setError(-1);
$oMessageView->setMessage($this->error);
$oMessageView->dispMessage();
// 정상적으로 호출된 객체가 있을 경우 해당 객체의 template를 변경
if($oModule) {
$oModule->setTemplatePath($oMessageView->getTemplatePath());
$oModule->setTemplateFile($oMessageView->getTemplateFile());
// 그렇지 않으면 message 객체를 호출된 객체로 지정
} else {
$oModule = $oMessageView;
}
}
// 해당 모듈에 layout_srl이 있는지 확인
if($oModule->module_info->layout_srl && !$oModule->getLayoutFile()) {
// layout_srl이 있으면 해당 레이아웃 정보를 가져와 layout_path/ layout_file 위치 변경
$oLayoutModel = &getModel('layout');
$layout_info = $oLayoutModel->getLayout($oModule->module_info->layout_srl);
if($layout_info) {
// 레이아웃 정보중 extra_vars의 이름과 값을 $layout_info에 입력
if($layout_info->extra_var_count) {
foreach($layout_info->extra_var as $var_id => $val) {
$layout_info->{$var_id} = $val->value;
}
}
// 레이아웃 정보중 menu를 Context::set
if($layout_info->menu_count) {
foreach($layout_info->menu as $menu_id => $menu) {
if(file_exists($menu->php_file)) @include($menu->php_file);
Context::set($menu_id, $menu);
}
}
// 레이아웃 정보를 Context::set
Context::set('layout_info', $layout_info);
$oModule->setLayoutPath($layout_info->path);
$oModule->setLayoutFile('layout');
// 레이아웃이 수정되었을 경우 수정본을 지정
$edited_layout = sprintf('./files/cache/layout/%d.html', $layout_info->layout_srl);
if(file_exists($edited_layout)) $oModule->setEditedLayoutFile($edited_layout);
}
}
// 컨텐츠 출력
$oDisplayHandler = new DisplayHandler();
$oDisplayHandler->printContent($oModule);
// DB 및 기타 자원의 종결 처리
Context::close();
}
/**
* @brief module의 위치를 찾아서 return
**/
function getModulePath($module) {
$class_path = sprintf('./modules/%s/', $module);
if(is_dir($class_path)) return $class_path;
return "";
}
/**
* @brief 모듈 객체를 생성함
**/
function &getModuleInstance($module, $type = 'view', $kind = '') {
$class_path = ModuleHandler::getModulePath($module);
if(!$class_path) return NULL;
if(__DEBUG__==3) $start_time = getMicroTime();
if($kind != 'admin') $kind = 'svc';
// global 변수에 미리 생성해 둔 객체가 없으면 새로 생성
if(!$GLOBALS['_loaded_module'][$module][$type][$kind]) {
/**
* 모듈의 위치를 파악
**/
// 상위 클래스명 구함
if(!class_exists($module)) {
$high_class_file = sprintf('%s%s.class.php', $class_path, $module);
if(!file_exists($high_class_file)) return NULL;
require_once($high_class_file);
}
// 객체의 이름을 구함
switch($type) {
case 'controller' :
if($kind == 'admin') {
$instance_name = sprintf("%sAdmin%s",$module,"Controller");
$class_file = sprintf('%s%s.admin.%s.php', $class_path, $module, $type);
} else {
$instance_name = sprintf("%s%s",$module,"Controller");
$class_file = sprintf('%s%s.%s.php', $class_path, $module, $type);
}
break;
case 'model' :
if($kind == 'admin') {
$instance_name = sprintf("%sAdmin%s",$module,"Model");
$class_file = sprintf('%s%s.admin.%s.php', $class_path, $module, $type);
} else {
$instance_name = sprintf("%s%s",$module,"Model");
$class_file = sprintf('%s%s.%s.php', $class_path, $module, $type);
}
break;
case 'class' :
$instance_name = $module;
$class_file = sprintf('%s%s.class.php', $class_path, $module);
break;
default :
$type = 'view';
if($kind == 'admin') {
$instance_name = sprintf("%sAdmin%s",$module,"View");
$class_file = sprintf('%s%s.admin.view.php', $class_path, $module, $type);
} else {
$instance_name = sprintf("%s%s",$module,"View");
$class_file = sprintf('%s%s.view.php', $class_path, $module, $type);
}
break;
}
// 클래스 파일의 이름을 구함
if(!file_exists($class_file)) return NULL;
// eval로 객체 생성
require_once($class_file);
$eval_str = sprintf('$oModule = new %s();', $instance_name);
@eval($eval_str);
if(!is_object($oModule)) return NULL;
// 해당 위치에 속한 lang 파일을 읽음
Context::loadLang($class_path.'lang');
// 생성된 객체에 자신이 호출된 위치를 세팅해줌
$oModule->setModule($module);
$oModule->setModulePath($class_path);
// GLOBALS 변수에 생성된 객체 저장
$GLOBALS['_loaded_module'][$module][$type][$kind] = $oModule;
}
if(__DEBUG__==3) $GLOBALS['__elapsed_class_load__'] += getMicroTime() - $start_time;
// 객체 리턴
return $GLOBALS['_loaded_module'][$module][$type][$kind];
}
}
?>

View file

@ -0,0 +1,342 @@
<?php
/**
* @class ModuleObject
* @author zero (zero@nzeo.com)
* @brief module의 상위 클래스
**/
class ModuleObject extends Object {
var $mid = NULL; ///< module로 생성한 instance(관리상)의 값
var $module = NULL; ///< mid로 찾아서 생성한 모듈 class 이름
var $module_srl = NULL; ///< 모듈 객체의 고유값인 module_srl
var $module_info = NULL; ///< 모듈의 설정 정보
var $xml_info = NULL; ///< 모듈 자체 정보
var $module_path = NULL; ///< 모듈 class file의 실행 위치
var $act = NULL; ///< act 값
var $template_path = NULL; ///< template 경로
var $template_file = NULL; ///< template 파일
var $layout_path = ''; ///< 레이아웃 경로
var $layout_file = ''; ///< 레이아웃 파일
var $edited_layout_file = ''; ///< 관리자 모드에서 수정된 레이아웃 파일
var $stop_proc = false; ///< action 수행중 stop()를 호출하면 ModuleObject::proc()를 수행하지 않음
/**
* @brief 현재 모듈의 이름을 지정
**/
function setModule($module) {
$this->module = $module;
}
/**
* @brief 현재 모듈의 path를 지정
**/
function setModulePath($path) {
if(substr($path,-1)!='/') $path.='/';
$this->module_path = $path;
}
/**
* @brief redirect_url을 정함
*
* redirect_url의 경우 ajax로 request를 받았을 경우에 사용하면 ...
**/
function setRedirectUrl($url='./') {
$this->add('redirect_url', $url);
}
/**
* @brief 현재 페이지를 refresh시킴
*
* 공통 tpl중 refresh.html을 실행할 ..
**/
function setRefreshPage() {
$this->setTemplatePath('./common/tpl');
$this->setTemplateFile('refresh');
}
/**
* @brief act값 지정
**/
function setAct($act) {
$this->act = $act;
}
/**
* @brief 모듈의 정보 세팅
**/
function setModuleInfo($module_info, $xml_info) {
// 기본 변수 설정
$this->mid = $module_info->mid;
$this->module_srl = $module_info->module_srl;
$this->module_info = $module_info;
$this->xml_info = $xml_info;
$this->skin_vars = $module_info->skin_vars;
// 웹서비스에서 꼭 필요한 인증 정보와 권한 설정 체크
$is_logged = Context::get('is_logged');
$logged_info = Context::get('logged_info');
$user_id = $logged_info->user_id;
$user_group = $logged_info->group_list;
// 로그인되어 있다면 admin 체크
if($is_logged && ($logged_info->is_admin == 'Y' || (is_array($this->module_info->admin_id)&&in_array($user_id, $this->module_info->admin_id) )) ) {
$grant->is_admin = true;
} else {
$grant->is_admin = false;
}
// act값에 admin이 들어 있는데 관리자가 아닌 경우 오류 표시
if(substr_count($this->act, 'Admin')) {
if(!$is_logged) {
$this->setAct("dispMemberLoginForm");
} elseif(!$grant->is_admin) {
return $this->stop('msg_not_permitted_act');
}
}
if($module_info->grants) {
foreach($module_info->grants as $key => $val) {
if(!$xml_info->grant->{$key}) {
$xml_info->grant->{$key}->title = $key;
}
}
}
// 권한 설정
if($xml_info->grant) {
// 이 모듈에 action.xml에서 선언된 권한 목록을 루프
foreach($xml_info->grant as $grant_name => $grant_item) {
// 제목과 기타 설정 없을 경우의 기본 권한(guest, member, root)에 대한 변수 설정
$title = $grant_item->title;
$default = $grant_item->default;
// 관리자이면 모든 권한에 대해 true 설정
if($grant->is_admin) {
$grant->{$grant_name} = true;
continue;
}
// 일단 현재 권한에 대해 false 지정
$grant->{$grant_name} = false;
// 모듈의 개별 설정에서 이 권한에 대한 그룹 지정이 있으면 체크
if(count($this->module_info->grants[$grant_name])) {
$group_srls = $this->module_info->grants[$grant_name];
if(!is_array($group_srls)) $group_srls = array($group_srls);
if(count($user_group)) {
foreach($user_group as $group_srl => $group_title) {
if(in_array($group_srl, $group_srls)) {
$grant->{$grant_name} = true;
break;
}
}
}
// 별도의 지정이 없으면 default값으로 권한 체크
} else {
switch($default) {
case 'member' :
if($is_logged) $grant->{$grant_name} = true;
break;
case 'root' :
if($grant->is_admin) $grant->{$grant_name} = true;
break;
default :
$grant->{$grant_name} = true;
break;
}
}
}
}
// 권한변수 설정
$this->grant = $grant;
Context::set('grant', $grant);
if(method_exists($this, 'init')) $this->init();
}
/**
* @brief 메세지 출력
**/
function stop($msg_code) {
// proc 수행을 중지 시키기 위한 플래그 세팅
$this->stop_proc = true;
// 에러 처리
$this->setError(-1);
$this->setMessage($msg_code);
// message 모듈의 에러 표시
$oMessageView = &getView('message');
$oMessageView->setError(-1);
$oMessageView->setMessage($msg_code);
$oMessageView->dispMessage();
$this->setTemplatePath($oMessageView->getTemplatePath());
$this->setTemplateFile($oMessageView->getTemplateFile());
return $this;
}
/**
* @brief template 파일 지정
**/
function setTemplateFile($filename) {
if(substr($filename,-5)!='.html') $filename .= '.html';
$this->template_file = $filename;
}
/**
* @brief template 파일 return
**/
function getTemplateFile() {
return $this->template_file;
}
/**
* @brief template 경로 지정
**/
function setTemplatePath($path) {
if(substr($path,0,2)!='./') $path = './'.$path;
if(substr($path,-1)!='/') $path .= '/';
$this->template_path = $path;
}
/**
* @brief template 경로 return
**/
function getTemplatePath() {
return $this->template_path;
}
/**
* @brief edited layout 파일 지정
**/
function setEditedLayoutFile($filename) {
if(substr($filename,-5)!='.html') $filename .= '.html';
$this->edited_layout_file = $filename;
}
/**
* @brief layout 파일 return
**/
function getEditedLayoutFile() {
return $this->edited_layout_file;
}
/**
* @brief layout 파일 지정
**/
function setLayoutFile($filename) {
if(substr($filename,-5)!='.html') $filename .= '.html';
$this->layout_file = $filename;
}
/**
* @brief layout 파일 return
**/
function getLayoutFile() {
return $this->layout_file;
}
/**
* @brief layout 경로 지정
**/
function setLayoutPath($path) {
if(substr($path,-1)!='/') $path .= '/';
if(substr($path,0,2)!='./') $path = './'.$path;
$this->layout_path = $path;
}
/**
* @brief layout 경로 return
**/
function getLayoutPath() {
return $this->layout_path;
}
/**
* @brief 모듈의 action에 해당하는 method를 실행
*
* $act값에 의해서 $action_list에 선언된 것들을 실행한다
**/
function proc() {
// stop_proc==true이면 그냥 패스
if($this->stop_proc==true) return false;
// addon 실행(called_position 를 before_module_proc로 하여 호출)
$called_position = 'before_module_proc';
@include("./files/cache/activated_addons.cache.php");
// 지금까지 이상이 없었다면 action 실행
if(!$this->stop_proc) {
// 현재 모듈에 act값이 있으면 해당 act를 실행
if(method_exists($this, $this->act)) {
//$output = call_user_method($this->act, $this);
//$output = call_user_func(array($this, $this->act));
$output = $this->{$this->act}();
// act가 없으면 action_forward에서 해당하는 act가 있는지 찾아서 대신 실행
} else if(Context::isInstalled()) {
$oModuleModel = &getModel('module');
$forward = $oModuleModel->getActionForward($this->act);
if($forward->module && $forward->type && $forward->act) {
$kind = strpos(strtolower($forward->act),'admin')!==false?'admin':'';
$oModule = &getModule($forward->module, $forward->type, $kind);
$xml_info = $oModuleModel->getModuleActionXml($forward->module);
$oModule->setAct($forward->act);
$oModule->init();
$oModule->setModuleInfo($this->module_info, $xml_info);
//$output = call_user_method($forward->act, $oModule);
//$output = call_user_func(array($oModule, $forward->act));
$output = $oModule->{$forward->act}();
$this->setTemplatePath($oModule->getTemplatePath());
$this->setTemplateFile($oModule->getTemplateFile());
} else {
if($this->xml_info->default_index_act) {
//$output = call_user_method($this->xml_info->default_index_act, $this);
//$output = call_user_func(array($this, $this->xml_info->default_index_act));
if(method_exists($this, $this->xml_info->default_index_act)) {
$output = $this->{$this->xml_info->default_index_act}();
}
} else {
return false;
}
}
} else {
return false;
}
}
// addon 실행(called_position 를 after_module_proc로 하여 호출)
$called_position = 'after_module_proc';
@include("./files/cache/activated_addons.cache.php");
if(is_a($output, 'Object') || is_subclass_of($output, 'Object')) {
$this->setError($output->getError());
$this->setMessage($output->getMessage());
return false;
}
return true;
}
}
?>

View file

@ -0,0 +1,120 @@
<?php
/**
* @class Object
* @author zero (zero@nzeo.com)
* @brief 모듈간의 데이터를 주고 받기 위한 클래스
*
* 모든 모듈은 Object를 상속하며 Object의 error, message, variables 이용하여 통신을 하게 된다
**/
class Object {
var $error = 0; ///< 에러 코드 (0이면 에러 아님)
var $message = 'success'; ///< 에러 메세지 (success이면 에러 아님)
var $variables = array(); ///< 추가 변수
/**
* @brief constructor
**/
function Object($error = 0, $message = 'success') {
$this->setError($error);
$this->setMessage($message);
}
/**
* @brief error 코드를 지정
**/
function setError($error = 0) {
$this->error = $error;
}
/**
* @brief error 코드를 return
**/
function getError() {
return $this->error;
}
/**
* @brief 에러 메세지 지정
**/
function setMessage($message = 'success') {
if(Context::getLang($message)) $message = Context::getLang($message);
$this->message = $message;
return true;
}
/**
* @brief 에러 메세지 return
**/
function getMessage() {
return $this->message;
}
/**
* @brief 추가 변수
**/
function add($key, $val) {
$this->variables[$key] = $val;
}
/**
* @brief 추가된 변수의 key, value들을 추가
**/
function adds($object) {
if(is_object($object)) {
$vars = get_object_vars($object);
foreach($vars as $key => $val) $this->add($key, $val);
} elseif(is_array($object)) {
foreach($object as $key => $val) $this->add($key, $val);
}
}
/**
* @brief 추가된 변수의 key에 해당하는 값을 return
**/
function get($key) {
return $this->variables[$key];
}
/**
* @brief 추가된 변수의 key들에 해당하는 값을 return
**/
function gets() {
$num_args = func_num_args();
$args_list = func_get_args();
for($i=0;$i<$num_args;$i++) {
$key = $args_list[$i];
$output->{$key} = $this->gets($key);
}
return $output;
}
/**
* @brief 추가변수 전체 return
**/
function getVariables() {
return $this->variables;
}
function getObjectVars() {
foreach($this->variables as $key => $val) $output->{$key} = $val;
return $output;
}
/**
* @brief error값이 0 아니면 오류
**/
function toBool() {
return $this->error==0?true:false;
}
/**
* @brief error값이 0 아니면 오류 (Object::toBool() aliasing)
**/
function toBoolean() {
return $this->toBool();
}
}
?>

View file

@ -0,0 +1,52 @@
<?php
/**
* @class PageHandler
* @author zero (zero@nzeo.com)
* @brief 페이지 네비게이션 담당
* @version 0.1
*
* 전체갯수, 전체페이지, 현재페이지, 페이지당 목록의 수를 넘겨주면 \n
* 페이지 네비게이션에 필요한 variables와 method를 구현\n
**/
class PageHandler extends Handler {
var $total_count = 0; ///< 전체 item의 갯수
var $total_page = 0; ///< 전체 페이지 수
var $cur_page = 0; ///< 현 페이지
var $page_count = 10; ///< 한번에 보일 페이지의 수
var $first_page = 1; ///< 첫 페이지
var $last_page = 1; ///< 마지막 페이지
var $point = 0; ///< getNextPage() 호출시 증가하는 값
/**
* @brief constructor
**/
function PageHandler($total_count, $total_page, $cur_page, $page_count = 10) {
$this->total_count = $total_count;
$this->total_page = $total_page;
$this->cur_page = $cur_page;
$this->page_count = $page_count;
$this->point = 0;
$first_page = $cur_page - (int)($page_count/2);
if($first_page<1) $first_page = 1;
$last_page = $total_page;
if($last_page>$total_page) $last_page = $total_page;
$this->first_page = $first_page;
$this->last_page = $last_page;
if($total_page < $this->page_count) $this->page_count = $total_page;
}
/**
* @brief 다음 페이지 요청
**/
function getNextPage() {
$page = $this->first_page+$this->point++;
if($this->point > $this->page_count || $page > $this->last_page) $page = 0;
return $page;
}
}
?>

View file

@ -0,0 +1,339 @@
<?php
/**
* @class TemplateHandler
* @author zero (zero@nzeo.com)
* @brief 템플릿 컴파일러
* @version 0.1
*
* 정규표현식을 이용하여 템플릿 파일을 컴파일하여 php코드로 변경하고 파일을 caching하여 사용할 있도록 하는 템플릿 컴파일러
**/
class TemplateHandler extends Handler {
var $compiled_path = './files/cache/template_compiled/'; ///< 컴파일된 캐쉬 파일이 놓일 위치
var $tpl_path = ''; ///< 컴파일 대상 경로
var $tpl_file = ''; ///< 컴파일 대상 파일
/**
* @brief TemplateHandler의 기생성된 객체를 return
**/
function &getInstance() {
if(__DEBUG__==3 ) {
if(!isset($GLOBALS['__TemplateHandlerCalled__'])) $GLOBALS['__TemplateHandlerCalled__']=1;
else $GLOBALS['__TemplateHandlerCalled__']++;
}
if(!$GLOBALS['__TemplateHandler__']) {
$GLOBALS['__TemplateHandler__'] = new TemplateHandler();
}
return $GLOBALS['__TemplateHandler__'];
}
/**
* @brief 주어진 tpl파일의 컴파일
**/
function compile($tpl_path, $tpl_filename, $tpl_file = '') {
// 디버그를 위한 컴파일 시작 시간 저장
if(__DEBUG__==3 ) $start = getMicroTime();
// 변수 체크
$tpl_path = ereg_replace('(\/+)$', '', $tpl_path).'/';
if(substr($tpl_filename,-5)!='.html') $tpl_filename .= '.html';
// tpl_file 변수 생성
if(!$tpl_file) $tpl_file = $tpl_path.$tpl_filename;
// tpl_file이 비어 있거나 해당 파일이 없으면 return
if(!$tpl_file || !file_exists($tpl_file)) return;
$this->tpl_path = $tpl_path;
$this->tpl_file = $tpl_file;
// compiled된(or 될) 파일이름을 구함
$compiled_tpl_file = $this->_getCompiledFileName($tpl_file);
// 일단 컴파일
$buff = $this->_compile($tpl_file, $compiled_tpl_file);
// Context와 compiled_tpl_file로 컨텐츠 생성
$output = $this->_fetch($compiled_tpl_file, $buff, $tpl_path);
if(__DEBUG__==3 ) $GLOBALS['__template_elapsed__'] += getMicroTime() - $start;
return $output;
}
/**
* @brief tpl_file이 컴파일이 되어 있는 것이 있는지 체크
**/
function _compile($tpl_file, $compiled_tpl_file) {
if(!file_exists($compiled_tpl_file)) return $this->_compileTplFile($tpl_file, $compiled_tpl_file);
$source_ftime = filectime($tpl_file);
$target_ftime = filectime($compiled_tpl_file);
if($source_ftime>$target_ftime) return $this->_compileTplFile($tpl_file, $compiled_tpl_file);
}
/**
* @brief tpl_file을 compile
**/
function _compileTplFile($tpl_file, $compiled_tpl_file) {
// tpl 파일을 읽음
$buff = FileHandler::readFile($tpl_file);
if(!$buff) return;
// include 변경 <!--#include($filename)-->
$buff = preg_replace_callback('!<\!--#include\(([^\)]*?)\)-->!is', array($this, '_compileIncludeToCode'), $buff);
// include 변경 <!--#include($filename)-->
$buff = preg_replace_callback('!<\!--#include\(([^\)]*?)\)-->!is', array($this, '_compileIncludeToCode'), $buff);
// 이미지 태그 img의 src의 값이 http:// 나 / 로 시작하지 않으면 제로보드의 root경로부터 시작하도록 변경
$buff = preg_replace_callback('!(img|input)([^>]*)src=[\'"]{1}(.*?)[\'"]{1}!is', array($this, '_compileImgPath'), $buff);
// 변수를 변경
$buff = preg_replace_callback('/\{[^@^ ]([^\}]+)\}/i', array($this, '_compileVarToContext'), $buff);
// 결과를 출력하지 않는 구문 변경
$buff = preg_replace_callback('/\{\@([^\}]+)\}/i', array($this, '_compileVarToSilenceExecute'), $buff);
// <!--@, --> 의 변경
$buff = preg_replace_callback('!<\!--@(.*?)-->!is', array($this, '_compileFuncToCode'), $buff);
// import xml filter/ css/ js/ 언어파일 <!--%filename-->
$buff = preg_replace_callback('!<\!--%import\(\"([^\"]*?)\"\)-->!is', array($this, '_compileImportCode'), $buff);
// 파일에 쓰기 전에 직접 호출되는 것을 방지
$buff = sprintf('%s%s%s','<?php if(!defined("__ZBXE__")) exit();?>',"\n",$buff);
// strip white spaces..
// $buff = preg_replace('/ +/', ' ', $buff);
// 컴파일된 코드를 파일에 저장
FileHandler::writeFile($compiled_tpl_file, $buff);
return $buff;
}
/**
* @brief {$와 } 안의 $... 변수를 Context::get(...) 으로 변경
**/
function _compileVarToContext($matches) {
$str = trim(substr($matches[0],1,strlen($matches[0])-2));
return '<?php print('.preg_replace('/\$([a-zA-Z0-9\_\-\>]+)/i','$__Context->\\1', $str).');?>';
}
/**
* @brief {$와 } 안의 $... 변수를 Context::get(...) 으로 변경
**/
function _compileImgPath($matches) {
$str1 = $matches[0];
$str2 = $path = $matches[3];
if(!eregi("^([a-z0-9\_\.])",$path)) return $str1;
$path = preg_replace('/^(\.\/|\/)/','',$path);
$path = '<?=$this->tpl_path?>'.$path;
$output = str_replace($str2, $path, $str1);
return $output;
}
/**
* @brief {@ } 안의 @... 함수를 print func(..) 변경
**/
function _compileVarToSilenceExecute($matches) {
return '<?php @'.preg_replace('/\$([a-zA-Z0-9\_\-\>]+)/i','$__Context->\\1', trim($matches[1])).';?>';
}
/**
* @brief <!--@, --> 사이의 구문을 php코드로 변경
**/
function _compileFuncToCode($matches) {
$code = trim($matches[1]);
if(!$code) return;
switch(strtolower($code)) {
case 'else' :
$output = '}else{';
break;
case 'end' :
case 'endif' :
case 'endfor' :
case 'endforeach' :
$output = '}';
break;
default :
if(substr($code,0,4)=='else') {
$code = '}'.$code;
} elseif(substr($code,0,7)=='foreach') {
$tmp_str = substr($code,8);
$tmp_arr = explode(' ', $tmp_str);
$var_name = $tmp_arr[0];
if(substr($var_name,0,1)=='$') $prefix = sprintf('if(count($__Context->%s)) ', substr($var_name,1));
else $prefix = sprintf('if(count(%s)) ', $var_name);
}
$output = preg_replace('/\$([a-zA-Z0-9\_\-]+)/i','$__Context->\\1', $code).'{';
break;
}
return sprintf('<?php %s %s ?>', $prefix, $output);
}
/**
* @brief <!--#include $path-->를 변환
**/
function _compileIncludeToCode($matches) {
// include하려는 대상문자열에 변수가 있으면 변수 처리
$arg = str_replace(array('"','\''), '', $matches[1]);
if(!$arg) return;
$tmp_arr = explode("/", $arg);
for($i=0;$i<count($tmp_arr);$i++) {
$item1 = trim($tmp_arr[$i]);
if($item1=='.'||eregi("\.html$",$item1)) continue;
$tmp2_arr = explode(".",$item1);
for($j=0;$j<count($tmp2_arr);$j++) {
$item = trim($tmp2_arr[$j]);
if(substr($item,0,1)=='$') $item = Context::get(substr($item,1));
$tmp2_arr[$j] = $item;
}
$tmp_arr[$i] = implode(".",$tmp2_arr);
}
$arg = implode("/",$tmp_arr);
if(substr($arg,0,2)=='./') $arg = substr($arg,2);
// 1단계로 해당 tpl 내의 파일을 체크
$source_filename = sprintf("%s/%s", dirname($this->tpl_file), $arg);
// 2단계로 root로부터 경로를 체크
if(!file_exists($source_filename)) $source_filename = './'.$arg;
if(!file_exists($source_filename)) return;
// path, filename으로 분리
$tmp_arr = explode('/', $source_filename);
$filename = array_pop($tmp_arr);
$path = implode('/', $tmp_arr).'/';
// include 시도
$output = sprintf(
'<?php%s'.
'$oTemplate = &TemplateHandler::getInstance();%s'.
'print $oTemplate->compile(\'%s\',\'%s\');%s'.
'?>%s',
"\n",
"\n",
$path, $filename, "\n",
"\n"
);
return $output;
}
/**
* @brief <!--%filename--> 확장자를 봐서 js filter/ css/ js 파일을 include하도록 수정
**/
function _compileImportCode($matches) {
// 현재 tpl 파일의 위치를 구해서 $base_path에 저장하여 적용하려는 xml file을 찾음
//$base_path = dirname($this->tpl_file).'/';
$base_path = $this->tpl_path;
$given_file = trim($matches[1]);
if(!$given_file) return;
// given_file이 lang으로 끝나게 되면 언어팩을 읽도록 함
if(ereg('lang$', $given_file)) {
if(substr($given_file,0,2)=='./') $given_file = substr($given_file, 2);
$lang_dir = sprintf('%s%s', $this->tpl_path, $given_file);
if(is_dir($lang_dir)) $output = sprintf('<?php Context::loadLang("%s"); ?>', $lang_dir);
// load lang이 아니라면 xml, css, js파일을 읽도록 시도
} else {
$filename = sprintf("%s%s",$base_path, $given_file);
// path와 파일이름을 구함
$tmp_arr = explode("/",$filename);
$filename = array_pop($tmp_arr);
$base_path = implode("/",$tmp_arr)."/";
// 확장자를 구함
$tmp_arr = explode(".",$filename);
$ext = strtolower(array_pop($tmp_arr));
// 확장자에 따라서 파일 import를 별도로
switch($ext) {
// xml js filter
case 'xml' :
// XmlJSFilter 클래스의 객체 생성후 js파일을 만들고 Context::addJsFile처리
$output = sprintf(
'<?php%s'.
'require_once("./classes/xml/XmlJsFilter.class.php");%s'.
'$oXmlFilter = new XmlJSFilter("%s","%s");%s'.
'$oXmlFilter->compile();%s'.
'?>%s',
"\n",
"\n",
$base_path,
$filename,
"\n",
"\n",
"\n"
);
break;
// css file
case 'css' :
$meta_file = sprintf('%s%s', $base_path, $filename);
$output = sprintf('<?php Context::addCSSFile("%s%s"); ?>', $base_path, $filename);
break;
// js file
case 'js' :
$meta_file = sprintf('%s%s', $base_path, $filename);
$output = sprintf('<?php Context::addJsFile("%s%s"); ?>', $base_path, $filename);
break;
}
}
$output = '<!--Meta:'.$meta_file.'-->'.$output;
return $output;
}
/**
* @brief $tpl_file로 compiled_tpl_file이름을 return
**/
function _getCompiledFileName($tpl_file) {
return sprintf('%s%s.compiled.php',$this->compiled_path, md5($tpl_file));
}
/**
* @brief ob_* 함수를 이용하여 fetch...
**/
function _fetch($compiled_tpl_file, $buff = NULL, $tpl_path = '') {
$__Context = &$GLOBALS['__Context__'];
$__Context->tpl_path = $tpl_path;
if($_SESSION['is_logged']) $__Context->logged_info = $_SESSION['logged_info'];
// ob_start를 시킨후 컴파일된 tpl파일을 include하고 결과를 return
ob_start();
// tpl파일을 compile하지 못할 경우 $buff로 넘어온 값을 eval시킴 (미설치시에나..)
if($buff) {
$eval_str = "?>".$buff;
eval($eval_str);
} else {
@include($compiled_tpl_file);
}
$output = ob_get_contents();
ob_end_clean();
return $output;
}
}
?>

View file

@ -0,0 +1,132 @@
<?php
/**
* @class WidgetHandler
* @author zero (zero@nzeo.com)
* @brief 위젯의 실행을 담당
**/
class WidgetHandler {
var $widget_path = '';
/**
* @brief 위젯 캐시 처리
**/
function getCache($sequence, $cache) {
if(!$sequence || !$cache) return;
$cache_path = './files/cache/widget_cache/';
if(!is_dir($cache_path)) {
FileHandler::makeDir($cache_path);
return;
}
$cache_file = sprintf('%s%d.%s.cache', $cache_path, $sequence, Context::getLangType());
if(!file_exists($cache_file)) return;
$filectime = filectime($cache_file);
if($filectime + $cache*60 < time()) return;
$output = FileHandler::readFile($cache_file);
return $output;
}
/**
* @brief 위젯을 찾아서 실행하고 결과를 출력
* <div widget='위젯'...></div> 태그 사용 templateHandler에서 WidgetHandler::execute() 실행하는 코드로 대체하게 된다
**/
function execute($widget, $args) {
// 디버그를 위한 위젯 실행 시간 저장
if(__DEBUG__==3) $start = getMicroTime();
// $widget의 객체를 받음
$oWidget = WidgetHandler::getObject($widget);
// 위젯 실행
if($oWidget) {
$output = $oWidget->proc($args);
}
if($args->widget_fix_width == 'Y') {
$widget_width_type = strtolower($args->widget_width_type);
if(!$widget_width_type||!in_array($widget_width_type,array("px","%"))) $widget_width_type = "px";
if($widget_width_type == "px") {
$style = "overflow:hidden;";
$style .= sprintf("%s:%s%s;", "width", $args->widget_width - $args->widget_margin_right - $args->widget_margin_left, $widget_width_type);
$style .= sprintf("margin-top:%dpx;margin-bottom:%dpx;", $args->widget_margin_top, $args->widget_margin_bottom);
$inner_style = sprintf("margin-left:%dpx;margin-right:%dpx;", $args->widget_margin_left, $args->widget_margin_right);
if($args->widget_position) {
$style .= sprintf("%s:%s;", "float", $args->widget_position);
$output = sprintf('<div style="%s"><div style="%s">%s</div></div>',$style, $inner_style, $output);
} else {
$style .= "float:left;";
$output = sprintf('<div class="clear"></div><div style="%s"><div style="%s">%s</div></div>',$style, $inner_style, $output);
}
} else {
$style = sprintf("padding:0;overflow:hidden;%s:%s%s;", "width", $args->widget_width, $widget_width_type);
$output = sprintf('<div style="margin:%dpx %dpx %dpx %dpx;">%s</div>', $args->widget_margin_top, $args->widget_margin_right,$args->widget_margin_bottom,$args->widget_margin_left, $output);
if($args->widget_position) {
$style .= sprintf("%s:%s;", "float", $args->widget_position);
$output = sprintf('<div style="%s">%s</div>',$style, $output);
} else {
$style .= "float:left;";
$output = sprintf('<div class="clear"></div><div style="%s">%s</div>',$style, $output);
}
}
} else {
$output = sprintf('<div style="margin:%dpx %dpx %dpx %dpx;padding:0;clear:both;">%s</div>', $args->widget_margin_top, $args->widget_margin_right,$args->widget_margin_bottom,$args->widget_margin_left, $output);
}
if(__DEBUG__==3) $GLOBALS['__widget_excute_elapsed__'] += getMicroTime() - $start;
if($args->widget_sequence && $args->widget_cache) {
$cache_path = './files/cache/widget_cache/';
$cache_file = sprintf('%s%d.%s.cache', $cache_path, $args->widget_sequence, Context::getLangType());
FileHandler::writeFile($cache_file, $output);
}
return $output;
}
/**
* @brief 위젯 객체를 return
**/
function getObject($widget) {
if(!$GLOBALS['_xe_loaded_widgets_'][$widget]) {
// 일단 위젯의 위치를 찾음
$oWidgetModel = &getModel('widget');
$path = $oWidgetModel->getWidgetPath($widget);
// 위젯 클래스 파일을 찾고 없으면 에러 출력 (html output)
$class_file = sprintf('%s%s.class.php', $path, $widget);
if(!file_exists($class_file)) return sprintf(Context::getLang('msg_widget_is_not_exists'), $widget);
// 위젯 클래스를 include
require_once($class_file);
// 객체 생성
$eval_str = sprintf('$oWidget = new %s();', $widget);
@eval($eval_str);
if(!is_object($oWidget)) return sprintf(Context::getLang('msg_widget_object_is_null'), $widget);
if(!method_exists($oWidget, 'proc')) return sprintf(Context::getLang('msg_widget_proc_is_null'), $widget);
$oWidget->widget_path = $path;
$GLOBALS['_xe_loaded_widgets_'][$widget] = $oWidget;
}
return $GLOBALS['_xe_loaded_widgets_'][$widget];
}
}
?>

View file

@ -0,0 +1,268 @@
<?php
/**
* @class XmlJsFilter
* @author zero (zero@nzeo.com)
* @brief filter xml문서를 해석하여 js파일로 만듬
* @version 0.1
*
* xml filter 파일은 js script로 컴파일 되어 캐싱됨
*
* <filter name="js function 이름" act="서버에 요청할 action 이름" confirm_msg_code="submit시에 prompt로 물어볼 메세지의 코드" >
* <form> <-- 항목의 체크
* <node target="name" required="true" minlength="1" maxlength="5" filter="email,userid,alpha,number" equalto="target" />
* </form>
* <parameter> <-- 항목을 조합하여 key=val js array로 return, act는 필수
* <param name="key" target="target" />
* </parameter>
* <response callback_func="callback 받게 될 js function 이름 지정" > <-- 서버에 ajax로 전송하여 받을 결과값
* <tag name="error" /> <-- error이름의 결과값을 받겠다는
* </response>
* </filter>
*
* - form - node
* target = element의 이름
* required = true/ false 있어야 하는지에 대한 체크
* minlength, maxlength = 최소/최대 길이
* filter = javascript로 체크하기 위한 체크 필터
* email : email의 형식 ( aaa.aaa@aaa.com)
* userid : 영문+숫자+_, 글자는 영문, 소문자
* alpha : 영문값만 허용
* number : 숫자만 허용
* equalto = target , 현재 폼과 지정 target의 값이 동일해야
*
* - parameter - param
* name = key : key를 이름으로 가지고 value의 값을 가지는 array 생성
* target = target_name : target form element의 값을 가져옴
*
* - response
* tag = key : return받을 결과값의 변수명
**/
class XmlJsFilter extends XmlParser {
var $compiled_path = './files/cache/js_filter_compiled/'; ///< 컴파일된 캐시 파일이 놓일 위치
var $xml_file = NULL; ///< 대상 xml 파일
var $js_file = NULL; ///< 컴파일된 js 파일
/**
* @brief constructor
**/
function XmlJsFilter($path, $xml_file) {
$this->xml_file = sprintf("%s%s",$path, $xml_file);
$this->js_file = $this->_getCompiledFileName($this->xml_file);
}
/**
* @brief xml파일과 compiled된js파일의 시간 비교 유무 비교등을 처리
**/
function compile() {
if(!file_exists($this->xml_file)) return;
if(!file_exists($this->js_file)) $this->_compile();
else if(filectime($this->xml_file)>filectime($this->js_file)) $this->_compile();
Context::addJsFile($this->js_file);
}
/**
* @brief 실제 xml_file을 컴파일하여 js_file을 생성
**/
function _compile() {
global $lang;
// xml 파일을 읽음
$buff = FileHandler::readFile($this->xml_file);
// xml parsing
$xml_obj = parent::parse($buff);
// XmlJsFilter는 filter_name, field, parameter 3개의 데이터를 핸들링
$filter_name = $xml_obj->filter->attrs->name;
$confirm_msg_code = $xml_obj->filter->attrs->confirm_msg_code;
$module = $xml_obj->filter->attrs->module;
$act = $xml_obj->filter->attrs->act;
$extend_filter = $xml_obj->filter->attrs->extend_filter;
$field_node = $xml_obj->filter->form->node;
if($field_node && !is_array($field_node)) $field_node = array($field_node);
$parameter_param = $xml_obj->filter->parameter->param;
if($parameter_param && !is_array($parameter_param)) $parameter_param = array($parameter_param);
$response_tag = $xml_obj->filter->response->tag;
if($response_tag && !is_array($response_tag)) $response_tag = array($response_tag);
// extend_filter가 있을 경우 해당 method를 호출하여 결과를 받음
if($extend_filter) {
// extend_filter가 있을 경우 캐시 사용을 못하도록 js 캐시 파일명을 변경
$this->js_file .= '.nocache.js';
// extend_filter는 module.method 로 지칭되어 이를 분리
list($module_name, $method) = explode('.',$extend_filter);
// 모듈 이름과 method가 있을 경우 진행
if($module_name&&$method) {
// 해당 module의 model 객체를 받음
$oExtendFilter = &getModel($module_name);
// method가 존재하면 실행
if(method_exists($oExtendFilter, $method)) {
// 결과를 받음
//$extend_filter_list = call_user_method($method, $oExtendFilter, true);
//$extend_filter_list = call_user_func(array($oExtendFilter, $method));
$extend_filter_list = $oExtendFilter->{$method}(true);
$extend_filter_count = count($extend_filter_list);
// 결과에서 lang값을 이용 문서 변수에 적용
for($i=0;$i<$extend_filter_count;$i++) {
$name = $extend_filter_list[$i]->name;
$lang_value = $extend_filter_list[$i]->lang;
if($lang_value) $lang->{$name} = $lang_value;
}
}
}
}
$callback_func = $xml_obj->filter->response->attrs->callback_func;
if(!$callback_func) $callback_func = "filterAlertMessage";
// 언어 입력을 위한 사용되는 필드 조사
$target_list = array();
$target_type_list = array();
// js function 을 만들기 시작
$js_doc = sprintf("function %s(fo_obj) {\n", $filter_name);
$js_doc .= sprintf("\tvar oFilter = new XmlJsFilter(fo_obj, \"%s\", \"%s\", %s);\n", $module, $act, $callback_func);
// field, 즉 체크항목의 script 생성
$node_count = count($field_node);
if($node_count) {
foreach($field_node as $key =>$node) {
$attrs = $node->attrs;
$target = trim($attrs->target);
if(!$target) continue;
$required = $attrs->required=='true'?'true':'false';
$minlength = $attrs->minlength>0?$attrs->minlength:'0';
$maxlength = $attrs->maxlength>0?$attrs->maxlength:'0';
$equalto = trim($attrs->equalto);
$filter = $attrs->filter;
$js_doc .= sprintf(
"\toFilter.addFieldItem(\"%s\",%s,%s,%s,\"%s\",\"%s\");\n",
$target, $required, $minlength, $maxlength, $equalto, $filter
);
if(!in_array($target, $target_list)) $target_list[] = $target;
if(!$target_type_list[$target]) $target_type_list[$target] = $filter;
}
}
// extend_filter_item 체크
for($i=0;$i<$extend_filter_count;$i++) {
$filter_item = $extend_filter_list[$i];
$target = trim($filter_item->name);
if(!$target) continue;
$type = $filter_item->type;
$required = $filter_item->required?'true':'false';
// extend filter item의 type으로 filter를 구함
switch($type) {
case 'homepage' :
$filter = 'homepage';
break;
case 'email_address' :
$filter = 'email';
break;
default :
$filter = '';
break;
}
$js_doc .= sprintf(
"\toFilter.addFieldItem(\"%s\",%s,%s,%s,\"%s\",\"%s\");\n",
$target, $required, 0, 0, '', $filter
);
if(!in_array($target, $target_list)) $target_list[] = $target;
if(!$target_type_list[$target]) $target_type_list[$target] = $type;
}
// 데이터를 만들기 위한 parameter script 생성
$parameter_count = count($parameter_param);
if($parameter_count) {
// 기본 필터 내용의 parameter로 구성
foreach($parameter_param as $key =>$param) {
$attrs = $param->attrs;
$name = trim($attrs->name);
$target = trim($attrs->target);
if(!$name || !$target) continue;
$target = htmlentities($target,ENT_QUOTES);
$js_doc .= sprintf(
"\toFilter.addParameterItem(\"%s\",\"%s\");\n",
$name, $target
);
if(!in_array($name, $target_list)) $target_list[] = $name;
}
// extend_filter_item 체크
for($i=0;$i<$extend_filter_count;$i++) {
$filter_item = $extend_filter_list[$i];
$target = $name = trim($filter_item->name);
if(!$name || !$target) continue;
$target = htmlentities($target,ENT_QUOTES);
$js_doc .= sprintf(
"\toFilter.addParameterItem(\"%s\",\"%s\");\n",
$name, $target
);
if(!in_array($name, $target_list)) $target_list[] = $name;
}
}
// response script 생성
$response_count = count($response_tag);
for($i=0;$i<$response_count;$i++) {
$attrs = $response_tag[$i]->attrs;
$name = $attrs->name;
$js_doc .= sprintf("\toFilter.addResponseItem(\"%s\");\n", $name);
}
if($confirm_msg_code) $js_doc .= sprintf("\treturn oFilter.proc(\"%s\");\n",str_replace('"','\"',$lang->{$confirm_msg_code}));
else $js_doc .= sprintf("\treturn oFilter.proc();\n");
$js_doc .= "}\n";
// form 필드 lang 값을 기록
$target_count = count($target_list);
for($i=0;$i<$target_count;$i++) {
$target = $target_list[$i];
if(!$lang->{$target}) $lang->{$target} = $target;
$js_doc .= sprintf("alertMsg[\"%s\"] = \"%s\";\n", $target, str_replace("\"","\\\"",$lang->{$target}));
}
// target type을 기록
$target_type_count = count($target_type_list);
if($target_type_count) {
foreach($target_type_list as $target => $type) {
$js_doc .= sprintf("target_type_list[\"%s\"] = \"%s\";\n", $target, $type);
}
}
// 에러 메세지를 기록
foreach($lang->filter as $key => $val) {
if(!$val) $val = $key;
$js_doc .= sprintf("alertMsg[\"%s\"] = \"%s\";\n", $key, str_replace("\"","\\\"",$val));
}
// js파일 생성
FileHandler::writeFile($this->js_file, $js_doc);
}
/**
* @brief $xml_file로 compiled_xml_file이름을 return
**/
function _getCompiledFileName($xml_file) {
return sprintf('%s%s.%s.compiled.js',$this->compiled_path, md5($xml_file),Context::getLangType());
}
}
?>

View file

@ -0,0 +1,134 @@
<?php
/**
* @class XmlParser
* @author zero (zero@nzeo.com)
* @brief xmlrpc를 해석하여 object로 return 하는 simple xml parser
* @version 0.1
*
* xml 데이터의 attribute중에 xml:lang="ko,en,ch,jp,..." 있을 경우 지정된 lang 값에 해당하는 것만 남기는 트릭이 적용됨.
* 무슨 문제를 일으킬지는 현재 모르나 동작하고 있음
**/
class XmlParser {
var $oParser = NULL; ///< xml parser
var $input = NULL; ///< input xml
var $output = array(); ///< output object
var $lang = "en"; ///< 기본 언어타입
/**
* @brief xml 파일을 로딩하여 parsing 처리 return
**/
function loadXmlFile($filename) {
if(!file_exists($filename)) return;
$buff = FileHandler::readFile($filename);
$oXmlParser = new XmlParser();
return $oXmlParser->parse($buff);
}
/**
* @brief xml 파싱
**/
function parse($input = '') {
// 디버그를 위한 컴파일 시작 시간 저장
if(__DEBUG__==3) $start = getMicroTime();
$this->lang = Context::getLangType();
$this->input = $input?$input:$GLOBALS['HTTP_RAW_POST_DATA'];
// 지원언어 종류를 뽑음
preg_match_all("/xml:lang=\"([^\"].+)\"/i", $this->input, $matches);
// xml:lang이 쓰였을 경우 지원하는 언어종류를 뽑음
if(count($matches[1]) && $supported_lang = array_unique($matches[1])) {
// supported_lang에 현재 접속자의 lang이 없으면 en이 있는지 확인하여 en이 있으면 en을 기본, 아니면 첫번째것을..
if(!in_array($this->lang, $supported_lang)) {
if(in_array('en', $supported_lang)) {
$this->lang = 'en';
} else {
$this->lang = array_shift($supported_lang);
}
}
// 특별한 언어가 지정되지 않았다면 언어체크를 하지 않음
} else {
unset($this->lang);
}
$this->oParser = xml_parser_create();
xml_set_object($this->oParser, $this);
xml_set_element_handler($this->oParser, "_tagOpen", "_tagClosed");
xml_set_character_data_handler($this->oParser, "_tagBody");
xml_parse($this->oParser, $this->input);
xml_parser_free($this->oParser);
if(!count($this->output)) return;
$output = array_shift($this->output);
// 디버그를 위한 컴파일 시작 시간 저장
if(__DEBUG__==3) $GLOBALS['__xmlparse_elapsed__'] += getMicroTime() - $start;
return $output;
}
/**
* @brief 태그 오픈
**/
function _tagOpen($parser, $node_name, $attrs) {
$obj->node_name = strtolower($node_name);
$obj->attrs = $this->_arrToObj($attrs);
array_push($this->output, $obj);
}
/**
* @brief body 내용
**/
function _tagBody($parser, $body) {
if(!trim($body)) return;
$this->output[count($this->output)-1]->body .= $body;
}
/**
* @brief 태그 닫음
**/
function _tagClosed($parser, $node_name) {
$node_name = strtolower($node_name);
$cur_obj = array_pop($this->output);
$parent_obj = &$this->output[count($this->output)-1];
if($this->lang&&$cur_obj->attrs->{'xml:lang'}&&$cur_obj->attrs->{'xml:lang'}!=$this->lang) return;
if($this->lang&&$parent_obj->{$node_name}->attrs->{'xml:lang'}&&$parent_obj->{$node_name}->attrs->{'xml:lang'}!=$this->lang) return;
if($parent_obj->{$node_name}) {
$tmp_obj = $parent_obj->{$node_name};
if(is_array($tmp_obj)) {
array_push($parent_obj->{$node_name}, $cur_obj);
} else {
$parent_obj->{$node_name} = array();
array_push($parent_obj->{$node_name}, $tmp_obj);
array_push($parent_obj->{$node_name}, $cur_obj);
}
} else {
$parent_obj->{$node_name} = $cur_obj;
}
}
/**
* @brief 파싱한 결과를 object vars에 담기 위한 method
**/
function _arrToObj($arr) {
if(!count($arr)) return;
foreach($arr as $key => $val) {
$key = strtolower($key);
$output->{$key} = $val;
}
return $output;
}
}
?>

View file

@ -0,0 +1,337 @@
<?php
/**
* @class XmlQueryParser
* @author zero (zero@nzeo.com)
* @brief query xml을 파싱하여 캐싱을 결과를 return
* @version 0.1
*
* @todo subquery나 union등의 확장 쿼리에 대한 지원이 필요
**/
class XmlQueryParser extends XmlParser {
/**
* @brief 쿼리 파일을 찾아서 파싱하고 캐싱한다
**/
function parse($query_id, $xml_file, $cache_file) {
// query xml 파일을 찾아서 파싱, 결과가 없으면 return
$buff = FileHandler::readFile($xml_file);
$xml_obj = parent::parse($buff);
if(!$xml_obj) return;
unset($buff);
list($module, $id) = explode('.',$query_id);
// insert, update, delete, select등의 action
$action = strtolower($xml_obj->query->attrs->action);
if(!$action) return;
// 테이블 정리 (배열코드로 변환)
$tables = $xml_obj->query->tables->table;
if(!$tables) return;
if(!is_array($tables)) $tables = array($tables);
foreach($tables as $key => $val) {
// 테이블과 alias의 이름을 구함
$table_name = $val->attrs->name;
$alias = $val->attrs->alias;
if(!$alias) $alias = $table_name;
$output->tables[$table_name] = $alias;
// 테이블을 찾아서 컬럼의 속성을 구함
$table_file = sprintf('./modules/%s/schemas/%s.xml', $module, $table_name);
if(!file_exists($table_file)) {
$searched_list = FileHandler::readDir('./modules');
$searched_count = count($searched_list);
for($i=0;$i<$searched_count;$i++) {
$table_file = sprintf('./modules/%s/schemas/%s.xml', $searched_list[$i], $table_name);
if(file_exists($table_file)) break;
}
}
if(file_exists($table_file)) {
$table_xml = FileHandler::readFile($table_file);
$table_obj = parent::parse($table_xml);
if($table_obj->table) {
foreach($table_obj->table->column as $k => $v) {
$buff .= sprintf('$output->column_type["%s"] = "%s";%s', $v->attrs->name, $v->attrs->type, "\n");
}
}
}
}
// 컬럼 정리
$columns = $xml_obj->query->columns->column;
if(!$columns) {
$output->column[] = array("*" => "*");
} else {
if(!is_array($columns)) $columns = array($columns);
foreach($columns as $key => $val) {
$name = $val->attrs->name;
/*
if(strpos('.',$name)===false && count($output->tables)==1) {
$tmp = array_values($output->tables);
$name = sprintf('%s.%s', $tmp[0], $val->attrs->name);
}
*/
$output->columns[] = array(
"name" => $name,
"var" => $val->attrs->var,
"default" => $val->attrs->default,
"notnull" => $val->attrs->notnull,
"filter" => $val->attrs->filter,
"minlength" => $val->attrs->minlength,
"maxlength" => $val->attrs->maxlength,
"alias" => $val->attrs->alias,
);
}
}
// 조건절 정리
$conditions = $xml_obj->query->conditions;
$condition = $conditions->condition;
if($condition) {
$obj->condition = $condition;
unset($condition);
$condition = array($obj);
}
$condition_group = $conditions->group;
if($condition_group && !is_array($condition_group)) $condition_group = array($condition_group);
if($condition && $condition_group) $cond = array_merge($condition, $condition_group);
elseif($condition_group) $cond = $condition_group;
else $cond = $condition;
if($cond) {
foreach($cond as $key => $val) {
unset($cond_output);
if($val->attrs->pipe) $cond_output->pipe = $val->attrs->pipe;
else $cond_output->pipe = null;
if(!$val->condition) continue;
if(!is_array($val->condition)) $val->condition = array($val->condition);
foreach($val->condition as $k => $v) {
$obj = $v->attrs;
if(!$obj->alias) $obj->alias = $obj->column;
$cond_output->condition[] = $obj;
}
$output->conditions[] = $cond_output;
}
}
// group 정리
$group_list = $xml_obj->query->groups->group;
if($group_list) {
if(!is_array($group_list)) $group_list = array($group_list);
for($i=0;$i<count($group_list);$i++) {
$group = $group_list[$i];
$column = trim($group->attrs->column);
if(!$column) continue;
$group_column_list[] = $column;
}
if(count($group_column_list)) $output->groups = $group_column_list;
}
// 네비게이션 정리
$navigation = $xml_obj->query->navigation;
if($navigation) {
$order = $navigation->index;
if($order) {
if(!is_array($order)) $order = array($order);
foreach($order as $order_info) {
$output->order[] = $order_info->attrs;
}
}
$list_count = $navigation->list_count->attrs;
$output->list_count = $list_count;
$page_count = $navigation->page_count->attrs;
$output->page_count = $page_count;
$page = $navigation->page->attrs;
$output->page = $page ;
}
$column_count = count($output->columns);
$condition_count = count($output->conditions);
// php script 생성
// table 정리
$buff .= '$output->tables = array( ';
foreach($output->tables as $key => $val) {
$buff .= sprintf('"%s"=>"%s",', $key, $val);
}
$buff .= ' );'."\n";
// column 정리
if($column_count) {
$buff .= '$output->columns = array ( ';
foreach($output->columns as $key => $val) {
$val['default'] = $this->getDefault($val['name'], $val['default']);
if($val['var'] && strpos($val['var'],'.')===false) {
if($val['default']) $buff .= sprintf('array("name"=>"%s", "alias"=>"%s", "value"=>$args->%s?$args->%s:%s),%s', $val['name'], $val['alias'], $val['var'], $val['var'], $val['default'] ,"\n");
else $buff .= sprintf('array("name"=>"%s", "alias"=>"%s", "value"=>$args->%s),%s', $val['name'], $val['alias'], $val['var'], "\n");
if($val['default']) $default_list[$val['var']] = $val['default'];
if($val['notnull']) $notnull_list[] = $val['var'];
if($val['minlength']) $minlength_list[$val['var']] = $val['minlength'];
if($val['maxlength']) $maxlength_list[$val['var']] = $val['maxlength'];
} else {
if($val['default']) $buff .= sprintf('array("name"=>"%s", "alias"=>"%s", "value"=>%s),%s', $val['name'], $val['alias'], $val['default'] ,"\n");
else $buff .= sprintf('array("name"=>"%s", "alias"=>"%s",),%s', $val['name'], $val['alias'], "\n");
}
}
$buff .= ' );'."\n";
}
// conditions 정리
if($condition_count) {
$buff .= '$output->conditions = array ( ';
foreach($output->conditions as $key => $val) {
$buff .= sprintf('array("pipe"=>"%s",%s"condition"=>array(', $val->pipe,"\n");
foreach($val->condition as $k => $v) {
$v->default = $this->getDefault($v->column, $v->default);
if($v->var) {
if(strpos($v->var,".")===false) {
if($v->default) $default_list[$v->var] = $v->default;
if($v->filter) $filter_list[] = $v;
if($v->default) $buff .= sprintf('array("column"=>"%s", "value"=>$args->%s?$args->%s:%s,"pipe"=>"%s","operation"=>"%s",),%s', $v->column, $v->var, $v->var, $v->default, $v->pipe, $v->operation, "\n");
else $buff .= sprintf('array("column"=>"%s", "value"=>$args->%s,"pipe"=>"%s","operation"=>"%s",),%s', $v->column, $v->var, $v->pipe, $v->operation, "\n");
} else {
$buff .= sprintf('array("column"=>"%s", "value"=>"%s","pipe"=>"%s","operation"=>"%s",),%s', $v->column, $v->var, $v->pipe, $v->operation, "\n");
}
} else {
if($v->default) $buff .= sprintf('array("column"=>"%s", "value"=>%s,"pipe"=>"%s","operation"=>"%s",),%s', $v->column, $v->default ,$v->pipe, $v->operation,"\n");
else $buff .= sprintf('array("column"=>"%s", "pipe"=>"%s","operation"=>"%s",),%s', $v->column, $v->pipe, $v->operation,"\n");
}
}
$buff .= ')),'."\n";
}
$buff .= ' );'."\n";
}
// order 정리
if($output->order) {
$buff .= '$output->order = array(';
foreach($output->order as $key => $val) {
$buff .= sprintf('array($args->%s?$args->%s:"%s",in_array($args->%s,array("asc","desc"))?$args->%s:("%s"?"%s":"asc")),', $val->var, $val->var, $val->default, $val->order, $val->order, $val->order, $val->order);
}
$buff .= ');'."\n";
}
// list_count 정리
if($output->list_count) {
$buff .= sprintf('$output->list_count = array("var"=>"%s", "value"=>$args->%s?$args->%s:"%s");%s', $output->list_count->var, $output->list_count->var, $output->list_count->var, $output->list_count->default,"\n");
}
// page_count 정리
if($output->page_count) {
$buff .= sprintf('$output->page_count = array("var"=>"%s", "value"=>$args->%s?$args->%s:"%s");%s', $output->page_count->var, $output->page_count->var, $output->page_count->var, $output->list_count->default,"\n");
}
// page 정리
if($output->page) {
$buff .= sprintf('$output->page = array("var"=>"%s", "value"=>$args->%s?$args->%s:"%s");%s', $output->page->var, $output->page->var, $output->page->var, $output->list->default,"\n");
}
// group by 정리
if($output->groups) {
$buff .= sprintf('$output->groups = array("%s");%s', implode('","',$output->groups),"\n");
}
// default check
if(count($default_list)) {
foreach($default_list as $key => $val) {
$pre_buff .= 'if(!isset($args->'.$key.')) $args->'.$key.' = '.$val.';'."\n";
}
}
// not null check
if(count($notnull_list)) {
foreach($notnull_list as $key => $val) {
$pre_buff .= 'if(!isset($args->'.$val.')) return new Object(-1, sprintf($lang->filter->isnull, $lang->'.$val.'?$lang->'.$val.':\''.$val.'\'));'."\n";
}
}
// minlength check
if(count($minlength_list)) {
foreach($minlength_list as $key => $val) {
$pre_buff .= 'if($args->'.$key.'&&strlen($args->'.$key.')<'.$val.') return new Object(-1, sprintf($lang->filter->outofrange, $lang->'.$key.'?$lang->'.$key.':\''.$key.'\'));'."\n";
}
}
// maxlength check
if(count($maxlength_list)) {
foreach($maxlength_list as $key => $val) {
$pre_buff .= 'if($args->'.$key.'&&strlen($args->'.$key.')>'.$val.') return new Object(-1, sprintf($lang->filter->outofrange, $lang->'.$key.'?$lang->'.$key.':\''.$key.'\'));'."\n";
}
}
// filter check
if(count($filter_list)) {
foreach($filter_list as $key => $val) {
if(!$notnull_list[$key]) continue;
$pre_buff .= sprintf('unset($_output); $_output = $this->checkFilter("%s",$args->%s,"%s"); if(!$_output->toBool()) return $_output;%s',$val->var,$val->var,$val->filter,"\n");
}
}
$buff = "<?php if(!defined('__ZBXE__')) exit();\n"
. sprintf('$output->query_id = "%s";%s', $query_id, "\n")
. sprintf('$output->action = "%s";%s', $action, "\n")
. $pre_buff
. $buff
. 'return $output; ?>';
// 저장
FileHandler::writeFile($cache_file, $buff);
}
/**
* @brief column, condition등의 key에 default 값을 세팅
**/
function getDefault($name, $value) {
if(!$value) return;
$str_pos = strpos($value, '(');
if($str_pos===false) return '"'.$value.'"';
$func_name = substr($value, 0, $str_pos);
$args = substr($value, $str_pos+1, strlen($value)-1);
switch($func_name) {
case 'ipaddress' :
$val = '$_SERVER[\'REMOTE_ADDR\']';
break;
case 'unixtime' :
$val = 'time()';
break;
case 'curdate' :
$val = 'date("YmdHis")';
break;
case 'sequence' :
$val = '$this->getNextSequence()';
break;
case 'plus' :
$args = abs($args);
$val = sprintf('"%s+%d"', $name, $args);
break;
case 'minus' :
$args = abs($args);
$val = sprintf('"%s-%d"', $name, $args);
break;
}
return $val;
}
}
?>

167
common/css/button.css Normal file
View file

@ -0,0 +1,167 @@
/*
http://www.hedgerwow.com/360/dhtml/css-round-button/demo.php
*/
a.button, span.button, del.button {
display:-moz-inline-box;
display:inline-block;
cursor:pointer;
border:none;
font-size:0;
line-height:0;
/*
for Safari, read this first
http://creativebits.org/webdev/safari_background_repeat_bug_fix
*/
background-position:0 -3px;
background-repeat:no-repeat;
height:24px;
text-decoration:none;
color:#2e523b;
font-style:normal;
margin:0 6px 0px 0;
padding:0 10px 0 0;
vertical-align:middle;
padding-top:-2px;
position:relative;
_width:10px;
_overflow-y:hidden;
}
a.button, span.button, del.button, a.button span, span.button button, span.button input, del.button span {
background-image:url(../tpl/images/form_buttons.png);
_background-image:url(../tpl/images/form_buttons.gif);
}
a.button span, span.button button, span.button input, del.button span {
white-space:nowrap;
cursor:pointer;
color:#222;
display:-moz-inline-box;
display:inline-block;
line-height:1;
letter-spacing:0 !important;
font-size:12px !important;
font-style:normal;
background-color:transparent;
background-position:100% -3px;
background-repeat:no-repeat;
height:24px;
padding:4px 11px 0 7px;
margin:0 -18px 0 6px;
border:none;
vertical-align:text-top;
zoom:1;
_position:relative;
_padding:3px 13px 0 4px;
_margin:0 -10px 0 4px;
_display:block;
_top:0;
_right:-5px;
}
*:first-child+html a.button span, span.button button, span.button input, del.button span { padding-top:6px; }
span.button button {
line-height:2.5;/*Opera need this*/
}
html.safari a.button span, html.safari del.button span {
line-height:1.3;
}
html.safari span.button button {
line-height:2.6;
}
html.safari a.button:focus, html.safari span.button button:focus {
outline:none;
}
del.button{
/* cursor:not-allowed; */
background-position:0 -123px;
}
del.button span {
cursor:default;
color:#aaa !important;
background-position:100% -123px;
}
a.button span {
_padding-top:7px;
}
*:first-child+html a.button span { padding-top:7px; }
span.button button, span.button input {
padding:0 6px 0 0;
line-height:2.5;/*Opera need this*/
}
/** optional **/
/*
a.button:visited{
color:#aaa;
}
*/
/*Hover Style*/
a.button:hover, span.button:hover, a.button:focus, a.dom-button-focus, span.button-behavior-hover {
background-position:0 -63px;
color:#222;
text-decoration:none;
}
a.button:hover span, span.button:hover input, span.button:hover button, a.button:focus span, span.button-behavior-hover button, span.button-behavior-hover input {
background-position:100% -63px;
}
a.button:active, a.button:focus span {
color:#444;
}
del.button-behavior-hover, del.button:hover {
background-position:0 -183px;
/* cursor:not-allowed; */
}
del.button-behavior-hover span, del.button:hover span {
background-position:100% -183px;
/* cursor:not-allowed; */
}
/*Optional hack for IE6 to simulate :hover selector*/
span.button button, del.button span, span.button input {
_behavior:expression(
(function(el){
if( typeof( behavior_onMouseEnter) == 'undefined'){
behavior_onMouseEnter = function(el) {
var dEl = this.parentNode;
var sClass = dEl.className ;
dEl.__defaultClassName = sClass ;
dEl.className = sClass + ' button-behavior-hover';
this.setCapture();
};
behavior_onMouseLeave = function(el) {
var dEl = this.parentNode;
dEl.className = dEl.__defaultClassName ;
dEl.__defaultClassName = undefined;
this.releaseCapture();
};
};
el.runtimeStyle.behavior = 'none';
el.onmouseenter = behavior_onMouseEnter;
el.onmouseleave = behavior_onMouseLeave;
}
)(this));
}

79
common/css/default.css Normal file
View file

@ -0,0 +1,79 @@
@charset "utf-8";
/*
Used Hack
IE6 & Below
{ property:value; _property:value; }
IE7 Only
*:first-child+html #selector
IE7 & IE6 & Below
*:first-child+html #selector,
* html #selector {}
*/
/* Special Class Selector */
.fr { float:right; }
.fl { float:left; }
.clear { clear:both; }
.fwB { font-weight:bold;}
.tCenter { text-align:center; }
.tRight { text-align:right; }
.tLeft { text-align:left; }
.gap1 { margin-top:.8em; }
/* default.css - Type Selector Definition */
* { margin:0; padding:0; }
html { width:100%; position:relative;}
body { margin:0; font-size:.75em; font-family:sans-serif;}
img { border:none; }
label { cursor:pointer; }
form { margin:0; padding:0; }
.iePngFix { behavior:url(./common/js/iePngFix.htc); }
.zbxe_info { vertical-align:middle; behavior:url(./common/js/iePngFix.htc); }
/* Input Style Definition */
.inputTypeText { border:1px solid; border-color:#a6a6a6 #d8d8d8 #d8d8d8 #a6a6a6; height:1.4em; padding:.2em 0 0 .3em; background:#ffffff; font-size:1em; }
.inputTypeText:hover,
.inputTypeText:focus { background:#f4f4f4; }
.inputTypeTextArea { border:1px solid !important; border-color:#a6a6a6 #d8d8d8 #d8d8d8 #a6a6a6 !important; background:#ffffff; font-size:1em;}
.w40 { width:40px; }
.w60 { width:60px; }
.w80 { width:80px; }
.w100 { width:100px; }
.w120 { width:120px; }
.w140 { width:140px; }
.w200 { width:200px; }
.w300 { width:300px; }
.w400 { width:400px; }
/* editor style */
a.bold { font-weight:bold; }
.editor_blue_text { color: #145ff9; text-decoration:underline; }
.editor_blue_text a { color: #145ff9; text-decoration:underline; }
.editor_red_text { color: #f42126; text-decoration:underline; }
.editor_red_text a { color: #f42126; text-decoration:underline; }
.editor_yellow_text { color: #c9bd00; text-decoration:underline; }
.editor_yellow_text a { color: #c9bd00; text-decoration:underline; }
.editor_green_text { color: #08830B; text-decoration:underline; }
.editor_green_text a { color: #08830B; text-decoration:underline; }
.folder_opener { display: block; }
.folder_closer { display: none; }
.folder_area { display: none; }
.zbxe_widget_output { background-image:url(../tpl/images/widget_text.gif) no-repeat center bottom; display:block;}
.member_signature { margin-top:10px; border:1px solid #DDDDDD; padding:10px; }
#waitingforserverresponse { display:inline; border:2px solid #444444; background-color:#FFFFFF; padding:15px 20px 13px 20px; font-weight:bold; color:#444444; top:40px; left:40px; position:absolute; z-index:100; visibility:hidden; }
#fororiginalimageareabg { z-index:1001; background-color:#FFFFFF; width:100%; height:100%; top:0px; left:0px; position:relative; padding:20px 10px 10px 10px; border:8px solid #DDDDDD; }
#fororiginalimagearea { visibility:hidden; padding:0px; margin:0px; width:100%; height:100%; top:0px; left:0px; position:absolute; z-index:1000; text-align:left; overflow:hidden; }
#fororiginalimage { z-index:1002; }
#closeOriginalImageBtn { position:absolute; right:8px; top:5px; cursor:pointer; width:50px; height:12px; z-index:1003; }
#membermenuarea { position:absolute; left:0px; top:0px; z-index:1003; visibility:hidden; border:2px solid #D9D9D9; background-color:#FFFFFF; padding:0; }
#membermenuarea .box { border:1px solid #CACACA; background-color:#FFFFFF; padding:7px; line-height:22px;}
#membermenuarea .item { color:#333333; cursor:pointer; margin:0; padding:3px 0 3px 18px; white-space:nowrap; height:22px;}
#membermenuarea .item_on { color:#333333; font-weight:bold; margin:0; cursor:pointer; padding:3px 0 3px 18px; height:22px; letter-spacing:-1px; white-space:nowrap;}

614
common/js/common.js Normal file
View file

@ -0,0 +1,614 @@
/**
* @file common.js
* @author zero (zero@nzeo.com)
* @brief 몇가지 유용한 & 기본적으로 자주 사용되는 자바스크립트 함수들 모음
**/
/**
* @brief location.href에서 특정 key의 값을 return
**/
String.prototype.getQuery = function(key) {
var idx = this.indexOf('?');
if(idx == -1) return null;
var query_string = this.substr(idx+1, this.length);
var args = {}
query_string.replace(/([^=]+)=([^&]*)(&|$)/g, function() { args[arguments[1]] = arguments[2]; });
var q = args[key];
if(typeof(q)=="undefined") q = "";
return q;
}
/**
* @brief location.href에서 특정 key의 값을 return
**/
String.prototype.setQuery = function(key, val) {
var idx = this.indexOf('?');
var uri = this;
uri = uri.replace(/#$/,'');
if(idx != -1) {
uri = this.substr(0, idx);
var query_string = this.substr(idx+1, this.length);
var args = new Array();
query_string.replace(/([^=]+)=([^&]*)(&|$)/g, function() { args[arguments[1]] = arguments[2]; });
args[key] = val;
var q_list = new Array();
for(var i in args) {
var arg = args[i];
if(!arg.toString().trim()) continue;
q_list[q_list.length] = i+'='+arg;
}
return uri+"?"+q_list.join("&");
} else {
if(val.toString().trim()) return uri+"?"+key+"="+val;
else return uri;
}
}
/**
* @brief string prototype으로 trim 함수 추가
**/
String.prototype.trim = function() {
return this.replace(/(^\s*)|(\s*$)/g, "");
}
/**
* @brief 주어진 인자가 하나라도 defined되어 있지 않으면 false return
**/
function isDef() {
for(var i=0; i<arguments.length; ++i) {
if(typeof(arguments[i])=="undefined") return false;
}
return true;
}
/**
* @brief 윈도우 오픈
* 열려진 윈도우의 관리를 통해 window.focus()등을 FF에서도 비슷하게 구현함
**/
var winopen_list = new Array();
function winopen(url, target, attribute) {
try {
if(target != "_blank" && winopen_list[target]) {
winopen_list[target].close();
winopen_list[target] = null;
}
} catch(e) {
}
if(typeof(target)=='undefined') target = '_blank';
if(typeof(attribute)=='undefined') attribute = '';
var win = window.open(url, target, attribute);
win.focus();
if(target != "_blank") winopen_list[target] = win;
}
/**
* @brief 팝업으로만 띄우기
* common/tpl/popup_layout.html이 요청되는 제로보드 XE내의 팝업일 경우에 사용
**/
function popopen(url, target) {
if(typeof(target)=="undefined") target = "_blank";
winopen(url, target, "left=10,top=10,width=10,height=10,scrollbars=no,resizable=no,toolbars=no");
}
/**
* @brief 메일 보내기용
**/
function sendMailTo(to) {
location.href="mailto:"+to;
}
/**
* @brief url이동 (open_window 값이 N 아니면 새창으로 띄움)
**/
function move_url(url, open_wnidow) {
if(!url) return false;
if(typeof(open_wnidow)=='undefined') open_wnidow = 'N';
if(open_wnidow=='N') open_wnidow = false;
else open_wnidow = true;
if(/^\./.test(url)) url = request_uri+url;
if(open_wnidow) {
winopen(url);
} else {
location.href=url;
}
return false;
}
/**
* @brief 특정 div(or span...) display옵션 토글
**/
function toggleDisplay(obj, opt) {
obj = xGetElementById(obj);
if(typeof(opt)=="undefined") opt = "inline";
if(obj.style.display == "none") obj.style.display = opt;
else obj.style.display = "none";
}
/**
* @brief 멀티미디어 출력용 (IE에서 플래쉬/동영상 주변에 점선 생김 방지용)
**/
function displayMultimedia(src, width, height, auto_start) {
if(src.indexOf('files')==0) src = request_uri+src;
if(auto_start) auto_start = "true";
else auto_start = "false";
var clsid = "";
var codebase = "";
var html = "";
if(/\.swf/i.test(src)) {
clsid = "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000";
codebase = "http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,28,0";
html = ""+
"<object classid=\""+clsid+"\" codebase=\""+codebase+"\" width=\""+width+"\" height=\""+height+"\" >"+
"<param name=\"wmode\" value=\"transparent\" />"+
"<param name=\"allowScriptAccess\" value=\"always\" />"+
"<param name=\"movie\" value=\""+src+"\" />"+
"<param name=\"quality\" value=\"high\" />"+
"<embed src=\""+src+"\" autostart=\""+auto_start+"\" width=\""+width+"\" height=\""+height+"\"></embed>"+
"<\/object>";
} else if(/\.flv/i.test(src)) {
html = "<embed src=\""+request_uri+"common/tpl/images/flvplayer.swf?autoStart="+auto_start+"&file="+src+"\" width=\""+width+"\" height=\""+height+"\" type=\"application/x-shockwave-flash\"></embed>";
} else {
html = "<embed src=\""+src+"\" autostart=\""+auto_start+"\" width=\""+width+"\" height=\""+height+"\"></embed>";
}
document.writeln(html);
}
/**
* @brief 화면내에서 상위 영역보다 이미지가 크면 리사이즈를 하고 클릭시 원본을 보여줄수 있도록 변경
**/
function resizeImageContents() {
var objs = xGetElementsByTagName("IMG");
for(var i in objs) {
var obj = objs[i];
var parent = obj.parentNode;
if(!obj||!parent) continue;
while(parent.parentNode && parent.nodeName != "TD" && parent.nodeName != "DIV") {
parent = parent.parentNode;
}
if(parent.nodeName != "TD" && parent.nodeName != "DIV") continue;
if(obj.parentNode.nodeName =='A') continue;
if(/\/modules\//i.test(obj.src)) continue;
if(/\/layouts\//i.test(obj.src)) continue;
if(/\/widgets\//i.test(obj.src)) continue;
if(/\/classes\//i.test(obj.src)) continue;
if(/\/common\/tpl\//i.test(obj.src)) continue;
if(/\/member_extra_info\//i.test(obj.src)) continue;
var parent_width = xWidth(parent);
var obj_width = xWidth(obj);
var orig_img = new Image();
orig_img.src = obj.src;
if(parent_width<1 || obj_width <1) continue;
if(parent_width>=obj_width && orig_img.width <= obj_width) continue;
obj.style.cursor = "pointer";
obj.source_width = orig_img.width;
obj.source_height = orig_img.height;
if(obj_width >= parent_width) {
var per = parent_width/obj_width;
xWidth(obj, xWidth(parent)-1);
xHeight(obj, xHeight(obj)*per);
}
xAddEventListener(obj,"click", showOriginalImage);
}
}
xAddEventListener(window, "load", resizeImageContents);
/**
* @brief 에디터에서 사용되는 내용 여닫는 코드 (고정, zbxe용)
**/
function zbxe_folder_open(id) {
var open_text_obj = xGetElementById("folder_open_"+id);
var close_text_obj = xGetElementById("folder_close_"+id);
var folder_obj = xGetElementById("folder_"+id);
open_text_obj.style.display = "none";
close_text_obj.style.display = "block";
folder_obj.style.display = "block";
}
function zbxe_folder_close(id) {
var open_text_obj = xGetElementById("folder_open_"+id);
var close_text_obj = xGetElementById("folder_close_"+id);
var folder_obj = xGetElementById("folder_"+id);
open_text_obj.style.display = "block";
close_text_obj.style.display = "none";
folder_obj.style.display = "none";
}
/**
* @brief 에디터에서 사용하되 내용 여닫는 코드 (zb5beta beta 호환용으로 남겨 놓음)
**/
function svc_folder_open(id) {
var open_text_obj = xGetElementById("_folder_open_"+id);
var close_text_obj = xGetElementById("_folder_close_"+id);
var folder_obj = xGetElementById("_folder_"+id);
open_text_obj.style.display = "none";
close_text_obj.style.display = "block";
folder_obj.style.display = "block";
}
function svc_folder_close(id) {
var open_text_obj = xGetElementById("_folder_open_"+id);
var close_text_obj = xGetElementById("_folder_close_"+id);
var folder_obj = xGetElementById("_folder_"+id);
open_text_obj.style.display = "block";
close_text_obj.style.display = "none";
folder_obj.style.display = "none";
}
/**
* @brief 팝업의 경우 내용에 맞춰 윈도우의 크기를 조절해줌
* 팝업의 내용에 맞게 크기를 늘리는 것은... 쉽게 되지는 않음.. .
* popup_layout 에서 window.onload 자동 요청됨.
**/
function setFixedPopupSize() {
if(xGetElementById('popBody')) {
if(xHeight('popBody')>600) {
xGetElementById('popBody').style.overflowY = 'scroll';
xGetElementById('popBody').style.overflowX = 'hidden';
xHeight('popBody', 600);
}
}
var w = xWidth("popup_content");
var h = xHeight("popup_content");
var obj_list = xGetElementsByTagName('div');
for(i=0;i<obj_list.length;i++) {
var ww = xWidth(obj_list[i]);
var id = obj_list[i].id;
if(id == 'waitingforserverresponse' || id == 'fororiginalimagearea' || id == 'fororiginalimageareabg') continue;
if(ww>w) w = ww;
}
// 윈도우에서는 브라우저 상관없이 가로 픽셀이 조금 더 늘어나야 한다.
if(xUA.indexOf('windows')>0) {
if(xOp7Up) w += 10;
else if(xIE4Up) w += 10;
else w += 6;
}
window.resizeTo(w,h);
var h1 = xHeight(window.document.body);
window.resizeBy(0,h-h1);
window.scrollTo(0,0);
}
/**
* @brief 본문내에서 컨텐츠 영역보다 이미지의 경우 원본 크기를 보여줌
**/
function showOriginalImage(evt) {
var e = new xEvent(evt);
var obj = e.target;
var src = obj.src;
var orig_image = xGetElementById("fororiginalimage");
var tmp_image = new Image();
tmp_image.src = src;
var image_width = tmp_image.width;
var image_height = tmp_image.height;
orig_image.style.margin = "0px 0px 0px 0px";
orig_image.style.cursor = "move";
orig_image.src = src;
var areabg = xGetElementById("fororiginalimageareabg");
xWidth(areabg, image_width+36);
xHeight(areabg, image_height+46);
var area = xGetElementById("fororiginalimagearea");
xLeft(area, xScrollLeft());
xTop(area, xScrollTop());
xWidth(area, xWidth(document));
xHeight(area, xHeight(document));
area.style.visibility = "visible";
var area_width = xWidth(area);
var area_height = xHeight(area);
var x = parseInt((area_width-image_width)/2,10);
var y = parseInt((area_height-image_height)/2,10);
if(x<0) x = 0;
if(y<0) y = 0;
xLeft(areabg, x);
xTop(areabg, y);
var sel_list = xGetElementsByTagName("select");
for (var i = 0; i < sel_list.length; ++i) sel_list[i].style.visibility = "hidden";
xAddEventListener(orig_image, "mousedown", origImageDragEnable);
xAddEventListener(orig_image, "dblclick", closeOriginalImage);
xAddEventListener(window, "scroll", closeOriginalImage);
xAddEventListener(window, "resize", closeOriginalImage);
areabg.style.visibility = 'visible';
}
/**
* @brief 원본 이미지 보여준 닫는 함수
**/
function closeOriginalImage(evt) {
var area = xGetElementById("fororiginalimagearea");
if(area.style.visibility != "visible") return;
area.style.visibility = "hidden";
xGetElementById("fororiginalimageareabg").style.visibility = "hidden";
var sel_list = xGetElementsByTagName("select");
for (var i = 0; i < sel_list.length; ++i) sel_list[i].style.visibility = "visible";
xRemoveEventListener(area, "mousedown", closeOriginalImage);
xRemoveEventListener(window, "scroll", closeOriginalImage);
xRemoveEventListener(window, "resize", closeOriginalImage);
}
/**
* @brief 원본 이미지 드래그
**/
var origDragManager = {obj:null, isDrag:false}
function origImageDragEnable(evt) {
var e = new xEvent(evt);
var obj = e.target;
if(obj.id != "fororiginalimage") return;
obj.draggable = true;
obj.startX = e.pageX;
obj.startY = e.pageY;
if(!origDragManager.isDrag) {
origDragManager.isDrag = true;
xAddEventListener(document, "mousemove", origImageDragMouseMove, false);
}
xAddEventListener(document, "mousedown", origImageDragMouseDown, false);
}
function origImageDrag(obj, px, py) {
var x = px - obj.startX;
var y = py - obj.startY;
var areabg = xGetElementById("fororiginalimageareabg");
xLeft(areabg, xLeft(areabg)+x);
xTop(areabg, xTop(areabg)+y);
obj.startX = px;
obj.startY = py;
}
function origImageDragMouseDown(evt) {
var e = new xEvent(evt);
var obj = e.target;
if(obj.id != "fororiginalimage" || !obj.draggable) return;
if(obj) {
xPreventDefault(evt);
obj.startX = e.pageX;
obj.startY = e.pageY;
origDragManager.obj = obj;
xAddEventListener(document, 'mouseup', origImageDragMouseUp, false);
origImageDrag(obj, e.pageX, e.pageY);
}
}
function origImageDragMouseUp(evt) {
if(origDragManager.obj) {
xPreventDefault(evt);
xRemoveEventListener(document, 'mouseup', origImageDragMouseUp, false);
xRemoveEventListener(document, 'mousemove', origImageDragMouseMove, false);
xRemoveEventListener(document, 'mousemdown', origImageDragMouseDown, false);
origDragManager.obj.draggable = false;
origDragManager.obj = null;
origDragManager.isDrag = false;
}
}
function origImageDragMouseMove(evt) {
var e = new xEvent(evt);
var obj = e.target;
if(!obj) return;
if(obj.id != "fororiginalimage") {
xPreventDefault(evt);
xRemoveEventListener(document, 'mouseup', origImageDragMouseUp, false);
xRemoveEventListener(document, 'mousemove', origImageDragMouseMove, false);
xRemoveEventListener(document, 'mousemdown', origImageDragMouseDown, false);
origDragManager.obj.draggable = false;
origDragManager.obj = null;
origDragManager.isDrag = false;
return;
}
xPreventDefault(evt);
origDragManager.obj = obj;
xAddEventListener(document, 'mouseup', origImageDragMouseUp, false);
origImageDrag(obj, e.pageX, e.pageY);
}
/**
* @brief 이름을 클릭하였을 경우 메뉴를 보여주는 함수
* 이름 클릭시 MemberModel::getMemberMenu 호출하여 결과를 보여줌 (사용자의 속성에 따라 메뉴가 달라지고 애드온의 연결을 하기 위해서임)
**/
xAddEventListener(document, 'click', chkMemberMenu);
xAddEventListener(window, 'load', function() { setMemberMenuObjCursor(xGetElementsByTagName("div")); xGetElementsByTagName("span"); } );
var loaded_member_menu_list = new Array();
// className = "member_*" 일 경우의 object가 클릭되면 해당 회원의 메뉴를 출력함
function chkMemberMenu(evt) {
var area = xGetElementById("membermenuarea");
if(!area) return;
if(area.style.visibility!="hidden") area.style.visibility="hidden";
var e = new xEvent(evt);
if(!e) return;
var obj = e.target;
while(obj) {
if(obj && obj.className && obj.className.search("member_")!=-1) break;
obj = obj.parentNode;
}
if(!obj || !obj.className || obj.className.search("member_")==-1) return;
if(obj.className.indexOf('member_-1')>=0) return;
var member_srl = parseInt(obj.className.replace(/member_([0-9]+)/ig,'$1').replace(/([^0-9]*)/ig,''),10);
if(!member_srl) return;
// 현재 글의 mid, module를 구함
var mid = current_mid;
// 서버에 메뉴를 요청
var params = new Array();
params["member_srl"] = member_srl;
params["cur_mid"] = mid;
params["cur_act"] = current_url.getQuery('act');
params["page_x"] = e.pageX;
params["page_y"] = e.pageY;
var response_tags = new Array("error","message","menu_list");
if(loaded_member_menu_list[member_srl]) {
params["menu_list"] = loaded_member_menu_list[member_srl];
displayMemberMenu(params, response_tags, params);
return;
}
show_waiting_message = false;
exec_xml("member", "getMemberMenu", params, displayMemberMenu, response_tags, params);
show_waiting_message = true;
}
function displayMemberMenu(ret_obj, response_tags, params) {
var area = xGetElementById("membermenuarea");
var menu_list = ret_obj['menu_list'];
var member_srl = params["member_srl"];
var html = "";
if(loaded_member_menu_list[member_srl]) {
html = loaded_member_menu_list[member_srl];
} else {
var infos = menu_list.split("\n");
if(infos.length) {
for(var i=0;i<infos.length;i++) {
var info_str = infos[i];
var pos = info_str.indexOf(",");
var icon = info_str.substr(0,pos).trim();
info_str = info_str.substr(pos+1, info_str.length).trim();
var pos = info_str.indexOf(",");
var str = info_str.substr(0,pos).trim();
var func = info_str.substr(pos+1, info_str.length).trim();
var className = "item";
//if(i==infos.length-1) className = "item";
if(!str || !func) continue;
html += "<span class=\""+className+"\" onmouseover=\"this.className='"+className+"_on'\" onmouseout=\"this.className='"+className+"'\" style=\"background:url("+icon+") no-repeat left center;\" onclick=\""+func+"\">"+str+"</span><br />";
}
}
loaded_member_menu_list[member_srl] = html;
}
if(html) {
xInnerHtml('membermenuarea', "<div class=\"box\">"+html+"</div>");
xWidth(area, xWidth(area));
xLeft(area, params["page_x"]);
xTop(area, params["page_y"]);
if(xWidth(area)+xLeft(area)>xClientWidth()+xScrollLeft()) xLeft(area, xClientWidth()-xWidth(area)+xScrollLeft());
if(xHeight(area)+xTop(area)>xClientHeight()+xScrollTop()) xTop(area, xClientHeight()-xHeight(area)+xScrollTop());
area.style.visibility = "visible";
}
}
// className = "member_*" 의 object의 cursor를 pointer로 본경
function setMemberMenuObjCursor(obj) {
for (var i = 0; i < obj.length; ++i) {
var node = obj[i];
if(node.className && node.className.search(/member_([0-9]+)/ig)!=-1) {
var member_srl = parseInt(node.className.replace(/member_([0-9]+)/ig,'$1').replace(/([^0-9]*)/ig,''),10);
if(member_srl<1) continue;
node.style.cursor = "pointer";
}
}
}
// 날짜 선택 (달력 열기)
function open_calendar(fo_id, day_str, callback_func) {
if(typeof(day_str)=="undefined") day_str = "";
var url = "./common/tpl/calendar.php?";
if(fo_id) url+="fo_id="+fo_id;
if(day_str) url+="&day_str="+day_str;
if(callback_func) url+="&callback_func="+callback_func;
popopen(url, 'Calendar');
}
// 언어코드 (lang_type) 쿠키값 변경
function doChangeLangType(obj) {
if(typeof(obj)=="string") {
setLangType(obj);
} else {
var val = obj.options[obj.selectedIndex].value;
setLangType(val);
}
location.reload();
}
function setLangType(lang_type) {
var expire = new Date();
expire.setTime(expire.getTime()+ (7000 * 24 * 3600000));
xSetCookie('lang_type', lang_type, expire);
}
/* 미리보기 */
function doDocumentPreview(obj) {
var fo_obj = obj;
while(fo_obj.nodeName != "FORM") {
fo_obj = fo_obj.parentNode;
}
if(fo_obj.nodeName != "FORM") return;
var content = fo_obj.content.value;
var win = window.open("","previewDocument","toolbars=no,width=700px;height=800px,scrollbars=yes,resizable=yes");
var dummy_obj = xGetElementById("previewDocument");
if(!dummy_obj) {
var fo_code = '<form id="previewDocument" target="previewDocument" method="post" action="'+request_uri+'">'+
'<input type="hidden" name="module" value="document" />'+
'<input type="hidden" name="act" value="dispDocumentPreview" />'+
'<input type="hidden" name="content" />'+
'</form>';
var dummy = xCreateElement("DIV");
xInnerHtml(dummy, fo_code);
window.document.body.insertBefore(dummy,window.document.body.lastChild);
dummy_obj = xGetElementById("previewDocument");
}
if(dummy_obj) {
dummy_obj.content.value = content;
dummy_obj.submit();
}
}
/* 스킨 정보 */
function viewSkinInfo(module, skin) {
popopen("./?module=module&act=dispModuleSkinInfo&selected_module="+module+"&skin="+skin, 'SkinInfo');
}

64
common/js/iePngFix.htc Normal file
View file

@ -0,0 +1,64 @@
<public:component>
<public:attach event="onpropertychange" onevent="doFix()" />
<script type="text/javascript">
// IE5.5+ PNG Alpha Fix v1.0RC4
// (c) 2004-2005 Angus Turnbull http://www.twinhelix.com
// This is licensed under the CC-GNU LGPL, version 2.1 or later.
// For details, see: http://creativecommons.org/licenses/LGPL/2.1/
// This must be a path to a blank image. That's all the configuration you need.
if (typeof blankImg == 'undefined') var blankImg = './common/tpl/images/blank.gif'; // 1x1px 짜리 투명 이미지(blank.gif)의 경로를 변경.
var f = 'DXImageTransform.Microsoft.AlphaImageLoader';
function filt(s, m)
{
if (filters[f])
{
filters[f].enabled = s ? true : false;
if (s) with (filters[f]) { src = s; sizingMethod = m }
}
else if (s) style.filter = 'progid:'+f+'(src="'+s+'",sizingMethod="'+m+'")';
}
function doFix()
{
// Assume IE7 is OK.
if (!/MSIE (5\.5|6\.)/.test(navigator.userAgent) ||
(event && !/(background|src)/.test(event.propertyName))) return;
var bgImg = currentStyle.backgroundImage || style.backgroundImage;
if (tagName == 'IMG')
{
if ((/\.png$/i).test(src))
{
if (currentStyle.width == 'auto' && currentStyle.height == 'auto')
style.width = offsetWidth + 'px';
filt(src, 'image');
// 'scale' 을 'image' 으로 변경하면 padding 적용시 나타나는 이미지 크기의 변화(잘못된 렌더링)를 방지할 수 있다. 하지만 border 표현에 문제가 생긴다.
src = blankImg;
}
else if (src.indexOf(blankImg) < 0) filt();
}
else if (bgImg && bgImg != 'none')
{
if (bgImg.match(/^url[("']+(.*\.png)[)"']+$/i))
{
var s = RegExp.$1;
if (currentStyle.width == 'auto' && currentStyle.height == 'auto')
style.width = offsetWidth + 'px';
style.backgroundImage = 'none';
filt(s, 'crop');
// IE link fix.
for (var n = 0; n < childNodes.length; n++)
if (childNodes[n].style) childNodes[n].style.position = 'relative';
}
else filt();
}
}
doFix();
</script>
</public:component>

636
common/js/tree_menu.js Normal file
View file

@ -0,0 +1,636 @@
/**
* @file tree_menu.js
* @author zero (zero@nzeo.com)
* @brief xml파일을 읽어서 트리 메뉴를 그려줌
*
* 일단 이것 저것 꽁수가 들어간 것이긴 한데 속도나 기타 면에서 쓸만함...\n
* 다만 제로보드에 특화되어 있어서....\n
* GPL License 따릅니당~~~\n
* 언제나 그렇듯 필요하신 분은 가져가서 쓰세요.\n
* 좋게 개량하시면 공유해주세요~\n
**/
// 트리메뉴에서 사용될 아이콘의 위치
var tree_menu_icon_path = "./common/tpl/images/";
// 아이콘을 미리 생성해 놓음
var tree_folder_icon = new Image();
tree_folder_icon.src = tree_menu_icon_path+"page.gif";
var tree_open_folder_icon = new Image();
tree_open_folder_icon.src = tree_menu_icon_path+"page.gif";
var tree_minus_icon = new Image();
tree_minus_icon.src = tree_menu_icon_path+"minus.gif";
var tree_minus_bottom_icon = new Image();
tree_minus_bottom_icon.src = tree_menu_icon_path+"minusbottom.gif";
var tree_plus_icon = new Image();
tree_plus_icon.src = tree_menu_icon_path+"plus.gif";
var tree_plus_bottom_icon = new Image();
tree_plus_bottom_icon.src = tree_menu_icon_path+"plusbottom.gif";
// 폴더를 모두 열고/닫기 위한 변수 설정
var tree_menu_folder_list = new Array();
// 노드의 정보를 가지고 있을 변수
var node_info_list = new Array();
// menu_id별로 요청된 클릭시 실행 될 callback_func
var node_callback_func = new Array();
// menu_id별로 요청된 드래그시 실행될 callback_func
var node_move_callback_func = new Array();
// 트리메뉴의 정보를 담고 있는 xml파일을 읽고 drawTreeMenu()를 호출하는 함수
function loadTreeMenu(url, menu_id, zone_id, title, index_url , callback_func, manual_select_node_srl, callback_move_func) {
// 일단 그릴 곳을 찾아서 사전 작업을 함 (그릴 곳이 없다면 아예 시도를 안함)
var zone = xGetElementById(zone_id);
if(typeof(zone)=="undefined") return;
// 관리가 아닌 사용일경우는 menu_id를 변경
if(typeof(callback_func)=='undefined') menu_id = 'display_'+menu_id;
// 노드 정보들을 담을 변수 세팅
node_info_list[menu_id] = new Array();
if(typeof(title)=='undefined') title = '';
// 사용자 정의 함수가 없다면 moveTreeMenu()라는 기본적인 동작을 하는 함수를 대입
if(typeof(callback_func)=='undefined') {
callback_func = moveTreeMenu;
}
if(typeof(callback_move_func)=='undefined') {
callback_move_func = null;
}
// 한 페이지에 다수의 menu_id가 있을 수 있으므로 menu_id별로 함수를 저장
node_callback_func[menu_id] = callback_func;
node_move_callback_func[menu_id] = callback_move_func;
// 직접 선택시키려는 메뉴 인자값이 없으면 초기화
if(typeof(manual_select_node_srl)=='undefined') manual_select_node_srl = '';
// xml_handler를 이용해서 직접 메뉴 xml파일(layout module에서 생성)을 읽음
if(!url) return;
var oXml = new xml_handler();
oXml.reset();
oXml.xml_path = url;
if(!index_url) index_url= "#";
// menu_id, zone_id는 계속 달고 다녀야함
var param = {"menu_id":menu_id, "zone_id":zone_id, "title":title, "index_url":index_url, "manual_select_node_srl":manual_select_node_srl}
// 요청후 drawTreeMenu()함수를 호출 (xml_handler.js에서 request method를 직접 이용)
oXml.request(drawTreeMenu, oXml, null, null, null, param);
}
// 트리메뉴 XML정보를 이용해서 정해진 zone에 출력
var manual_select_node_srl = '';
function drawTreeMenu(oXml, callback_func, resopnse_tags, null_func, param) {
// 그리기 위한 object를 찾아 놓음
var menu_id = param.menu_id;
var zone_id = param.zone_id;
var title = param.title;
var index_url = param.index_url;
if(param.manual_select_node_srl) manual_select_node_srl = param.manual_select_node_srl;
var zone = xGetElementById(zone_id);
var html = "";
if(title) html = '<div style="cursor:pointer;padding-left:18px;margin-bottom:5px;background:url('+tree_menu_icon_path+'folder.gif) no-repeat left;" onclick="location.href=\''+index_url+'\';return false;" >'+title+'</div>';
var xmlDoc = oXml.getResponseXml();
if(!xmlDoc) {
xInnerHtml(zone, html);
return null;
}
tree_menu_folder_list[menu_id] = new Array();
// node 태그에 해당하는 값들을 가져와서 html을 작성
var node_list = xmlDoc.getElementsByTagName("node");
if(node_list.length>0) {
var root = xmlDoc.getElementsByTagName("root")[0];
var output = drawNode(root, menu_id);
html += output.html;
}
// 출력하려는 zone이 없다면 load후에 출력하도록 함
if(!zone) {
xAddEventListener(window, 'load', function() { drawTeeMenu(zone_id, menu_id, html); });
// 출력하려는 zone을 찾아졌다면 바로 출력
} else {
xInnerHtml(zone, html);
if(manual_select_node_srl) manualSelectNode(menu_id, manual_select_node_srl);
}
return null;
}
// 페이지 랜더링 중에 메뉴의 html이 완성되었을때 window.onload event 후에 그리기 재시도를 하게 될 함수
function drawTeeMenu(zone_id, menu_id, html) {
xInnerHtml(zone_id, html);
if(manual_select_node_srl) manualSelectNode(menu_id, manual_select_node_srl);
}
// root부터 시작해서 recursive하게 노드를 표혐
function drawNode(parent_node, menu_id) {
var output = {html:"", expand:"N"}
for (var i=0; i< parent_node.childNodes.length; i++) {
var html = "";
// nodeName이 node가 아니면 패스~
var node = parent_node.childNodes.item(i);
if(node.nodeName!="node") continue;
// node의 기본 변수들 체크
var node_srl = node.getAttribute("node_srl");
var text = node.getAttribute("text");
var url = node.getAttribute("url");
var expand = node.getAttribute("expand");
if(!text) continue;
// 자식 노드가 있는지 확인
var hasChild = false;
if(node.hasChildNodes()) hasChild = true;
// nextSibling가 있는지 확인
var hasNextSibling = false;
if(i==parent_node.childNodes.length-1) hasNextSibling = true;
// 후에 사용하기 위해 node_info_list에 node_srl을 값으로 하여 node object 추가
node_info_list[menu_id][node_srl] = node;
// zone_id 값을 세팅
var zone_id = "menu_"+menu_id+"_"+node_srl;
tree_menu_folder_list[menu_id][tree_menu_folder_list[menu_id].length] = zone_id;
// url을 확인하여 현재의 url과 동일하다고 판단되면 manual_select_node_srl 에 값을 추가 (관리자페이지일 경우는 무시함)
if(node_callback_func[menu_id] == moveTreeMenu && url && current_url.getQuery('mid') == url) manual_select_node_srl = node_srl;
// manual_select_node_srl이 node_srl과 같으면 펼침으로 처리
if(manual_select_node_srl == node_srl) expand = "Y";
// 아이콘 설정
var line_icon = null;
var folder_icon = null;
// 자식 노드가 있을 경우 자식 노드의 html을 구해옴
var child_output = null;
var child_html = "";
if(hasChild) {
// 자식 노드의 zone id를 세팅
var child_zone_id = zone_id+"_child";
tree_menu_folder_list[menu_id][tree_menu_folder_list[menu_id].length] = child_zone_id;
// html을 받아옴
child_output = drawNode(node, menu_id);
var chtml = child_output.html;
var cexpand = child_output.expand;
if(cexpand == "Y") expand = "Y";
// 무조건 펼침이 아닐 경우
if(expand!="Y") {
if(!hasNextSibling) child_html += '<div id="'+child_zone_id+'"style="display:none;padding-left:16px;background:url('+tree_menu_icon_path+'line.gif) repeat-y left;">'+chtml+'</div>';
else child_html += '<div id="'+child_zone_id+'" style="display:none;padding-left:16px;">'+chtml+'</div>';
// 무조건 펼침일 경우
} else {
if(!hasNextSibling) child_html += '<div id="'+child_zone_id+'"style="display:block;padding-left:16px;background:url('+tree_menu_icon_path+'line.gif) repeat-y left;">'+chtml+'</div>';
else child_html += '<div id="'+child_zone_id+'" style="display:block;padding-left:16px;">'+chtml+'</div>';
}
}
// 자식 노드가 있는지 확인하여 있으면 아이콘을 바꿈
if(hasChild) {
// 무조건 펼침이 아닐 경우
if(expand != "Y") {
if(!hasNextSibling) {
line_icon = "plus";
folder_icon = "page";
} else {
line_icon = "plusbottom";
folder_icon = "page";
}
// 무조건 펼침일 경우
} else {
if(!hasNextSibling) {
line_icon = "minus";
folder_icon = "page";
} else {
line_icon = "minusbottom";
folder_icon = "page";
}
}
// 자식 노드가 없을 경우
} else {
if(hasNextSibling) {
line_icon = "joinbottom";
folder_icon = "page";
} else {
line_icon = "join";
folder_icon = "page";
}
}
// html 작성
html += '<div id="'+zone_id+'" style="margin:0px;font-size:9pt;">';
if(hasChild) html+= '<span style="cursor:pointer;" onclick="toggleFolder(\''+zone_id+'\');return false;">';
else html+= '<span>';
html += '<img id="'+zone_id+'_line_icon" src="'+tree_menu_icon_path+line_icon+'.gif" alt="line" align="top" /><img id="'+zone_id+'_folder_icon" src="'+tree_menu_icon_path+folder_icon+'.gif" alt="folder" align="top" /></span>';
var chk_enable = xGetElementById(menu_id+"_enable_move");
if(chk_enable) {
html += '<span><span id="'+zone_id+'_node" style="cursor:move;padding:1px 2px 1px 2px;margin-top:1px;cursor:pointer;" onmousedown="doNodeFunc(this, \''+menu_id+'\','+node_srl+',\''+zone_id+'\');">';
} else {
html += '<span><span id="'+zone_id+'_node" style="cursor:move;padding:1px 2px 1px 2px;margin-top:1px;cursor:pointer;" onclick="selectNode(\''+menu_id+'\','+node_srl+',\''+zone_id+'\')" ondblclick="toggleFolder(\''+zone_id+'\')">';
}
html += text+'</span></span>';
html += child_html;
html += '</div>';
output.html += html;
if(expand=="Y") output.expand = "Y";
}
return output;
}
// 관리자 모드일 경우 *_enable_move 의 값에 따라 메뉴 이동을 시키거나 정보를 보여주도록 변경
function doNodeFunc(obj, menu_id, node_srl, zone_id) {
var chk_enable = xGetElementById(menu_id+"_enable_move");
if(!chk_enable || chk_enable.checked!=true || !obj) {
selectNode(menu_id,node_srl,zone_id);
return;
}
deSelectNode();
tree_drag_enable(obj,tree_drag_start,tree_drag,tree_drag_end);
}
// 수동으로 메뉴를 선택하도록 함
function manualSelectNode(menu_id, node_srl) {
var zone_id = "menu_"+menu_id+"_"+node_srl;
selectNode(menu_id,node_srl,zone_id,false);
return;
}
// 노드의 폴더 아이콘 클릭시
function toggleFolder(zone_id) {
// 아이콘을 클릭한 대상을 찾아봄
var child_zone = xGetElementById(zone_id+"_child");
if(!child_zone) return;
// 대상의 아이콘들 찾음
var line_icon = xGetElementById(zone_id+'_line_icon');
var folder_icon = xGetElementById(zone_id+'_folder_icon');
var height = 0;
// 대상의 자식 노드들이 숨겨져 있다면 열고 아니면 닫기
if(child_zone.style.display == "block") {
height = xHeight(child_zone)*-1;
child_zone.style.display = "none";
if(line_icon.src.indexOf('bottom')>0) line_icon.src = tree_plus_bottom_icon.src;
else line_icon.src = tree_plus_icon.src;
folder_icon.src = tree_folder_icon.src;
} else {
if(line_icon.src.indexOf('bottom')>0) line_icon.src = tree_minus_bottom_icon.src;
else line_icon.src = tree_minus_icon.src;
folder_icon.src = tree_open_folder_icon.src;
child_zone.style.display = "block";
height = xHeight(child_zone);
}
if(typeof(fixAdminLayoutFooter)=='function') fixAdminLayoutFooter( height );
}
// 노드의 글자 선택시
var prev_selected_node = null;
function selectNode(menu_id, node_srl, zone_id, move_url) {
// 선택된 노드를 찾아봄
var node_zone = xGetElementById(zone_id+'_node');
if(!node_zone) return;
// 이전에 선택된 노드가 있었다면 원래데로 돌림
if(prev_selected_node) {
var prev_zone = xGetElementById(prev_selected_node.id);
if(prev_zone) {
prev_zone.style.backgroundColor = "#ffffff";
prev_zone.style.fontWeight = "normal";
prev_zone.style.color = "#000000";
}
}
// 선택된 노드의 글자를 변경
prev_selected_node = node_zone;
node_zone.style.backgroundColor = "#0e078f";
node_zone.style.fontWeight = "bold";
node_zone.style.color = "#FFFFFF";
// 함수 실행
if(typeof(move_url)=="undefined"||move_url==true) {
var func = node_callback_func[menu_id];
func(menu_id, node_info_list[menu_id][node_srl]);
//toggleFolder(zone_id);
}
}
// 선택된 노드의 표시를 없앰
function deSelectNode() {
// 이전에 선택된 노드가 있었다면 원래데로 돌림
if(!prev_selected_node) return;
prev_selected_node.style.backgroundColor = "#ffffff";
prev_selected_node.style.fontWeight = "normal";
prev_selected_node.style.color = "#000000";
}
// 모두 닫기
function closeAllTreeMenu(menu_id) {
for(var i in tree_menu_folder_list[menu_id]) {
var zone_id = tree_menu_folder_list[menu_id][i];
var zone = xGetElementById(zone_id);
if(!zone) continue;
var child_zone = xGetElementById(zone_id+"_child");
if(!child_zone) continue;
child_zone.style.display = "block";
toggleFolder(zone_id);
}
}
// 모두 열기
function openAllTreeMenu(menu_id) {
for(var i in tree_menu_folder_list[menu_id]) {
var zone_id = tree_menu_folder_list[menu_id][i];
var zone = xGetElementById(zone_id);
if(!zone) continue;
var child_zone = xGetElementById(zone_id+"_child");
if(!child_zone) continue;
child_zone.style.display = "none";
toggleFolder(zone_id);
}
}
// 메뉴 클릭시 기본으로 동작할 함수 (사용자 임의 함수로 대체될 수 있음)
function moveTreeMenu(menu_id, node) {
// url과 open_window값을 구함
var node_srl = node.getAttribute("node_srl");
var url = node.getAttribute("url");
var open_window = node.getAttribute("open_window");
var hasChild = false;
if(node.hasChildNodes()) hasChild = true;
// url이 없고 child가 있으면 해당 폴더 토글한다
if(!url && hasChild) {
var zone_id = "menu_"+menu_id+"_"+node_srl;
toggleFolder(zone_id);
return;
}
// url이 있으면 url을 분석한다 (제로보드 특화된 부분. url이 http나 ftp등으로 시작하면 그냥 해당 url 열기)
if(url) {
// http, ftp등의 연결이 아닌 경우 제로보드용으로 처리
if(url.indexOf('://')==-1) url = "./?"+url;
// open_window에 따라서 처리
if(open_window != "Y") location.href=url;
else {
var win = window.open(url);
win.focus();
}
}
}
// 메뉴 드래그 중이라는 상황을 간직할 변수
var tree_drag_manager = {obj:null, isDrag:false}
var tree_tmp_object = new Array();
var tree_disappear = 0;
/**
* 메뉴 드래깅을 위한 함수들
**/
// 드래깅시 보여줄 임시 object를 생성하는 함수
function tree_create_tmp_object(obj) {
var tmp_obj = tree_tmp_object[obj.id];
if(tmp_obj) return tmp_obj;
tmp_obj = xCreateElement('DIV');
tmp_obj.id = obj.id + '_tmp';
tmp_obj.style.display = 'none';
tmp_obj.style.position = 'absolute';
tmp_obj.style.backgroundColor = obj.style.backgroundColor;
tmp_obj.style.fontSize = obj.style.fontSize;
tmp_obj.style.fontFamlily = obj.style.fontFamlily;
tmp_obj.style.color = "#5277ff";
tmp_obj.style.opacity = 1;
tmp_obj.style.filter = 'alpha(opacity=100)';
document.body.appendChild(tmp_obj);
tree_tmp_object[obj.id] = tmp_obj;
return tmp_obj;
}
// 기생성된 임시 object를 찾아서 return, 없으면 만들어서 return
function tree_get_tmp_object(obj) {
var tmp_obj = tree_tmp_object[obj.id];
if(!tmp_obj) tmp_obj = tree_create_tmp_object(obj);
return tmp_obj;
}
// 메뉴에 마우스 클릭이 일어난 시점에 드래그를 위한 제일 첫 동작 (해당 object에 각종 함수나 상태변수 설정)
function tree_drag_enable(child_obj, funcDragStart, funcDrag, funcDragEnd) {
// 클릭이 일어난 메뉴의 상위 object를 찾음
var obj = child_obj.parentNode.parentNode;
// 상위 object에 드래그 가능하다는 상태와 각 드래그 관련 함수를 설정
obj.draggable = true;
obj.drag_start = funcDragStart;
obj.drag = funcDrag;
obj.drag_end = funcDragEnd;
obj.target_id = null;
// 드래그 가능하지 않다면 드래그 가능하도록 상태 지정하고 mousemove이벤트 등록
if (!tree_drag_manager.isDrag) {
tree_drag_manager.isDrag = true;
xAddEventListener(document, 'mousemove', tree_drag_mouse_move, false);
}
// mousedown이벤트 값을 지정
xAddEventListener(obj, 'mousedown', tree_mouse_down, false);
}
// 드래그를 시작할때 호출되는 함수 (이동되는 형태를 보여주기 위한 작업을 함)
function tree_drag_start(tobj, px, py) {
var obj = tree_get_tmp_object(tobj);
xInnerHtml(obj, xInnerHtml(tobj));
tobj.source_color = tobj.style.color;
tobj.style.color = "#BBBBBB";
xLeft(obj, xPageX(tobj));
xTop(obj, xPageY(tobj));
xWidth(obj, xWidth(tobj));
xHeight(obj, xHeight(tobj));
xDisplay(obj, 'block');
}
// 드래그 시작후 마우스를 이동할때 발생되는 이벤트에 의해 실행되는 함수
function tree_drag(tobj, dx, dy) {
var obj = tree_get_tmp_object(tobj);
xLeft(obj, parseInt(xPageX(obj),10) + parseInt(dx,10));
xTop(obj, parseInt(xPageY(obj),10) + parseInt(dy,10));
var menu_id = tobj.id.replace(/menu_/,'');
menu_id = menu_id.replace(/_([0-9]+)$/,'');
if(!menu_id) return;
for(var node_srl in node_info_list[menu_id]) {
var zone_id = "menu_"+menu_id+"_"+node_srl;
var target_obj = xGetElementById(zone_id);
var hh = parseInt(xHeight(target_obj),10);
var h = parseInt(parseInt(xHeight(target_obj),10)/2,10);
var l = xPageX(target_obj);
var t = xPageY(target_obj);
var ll = parseInt(l,10) + parseInt(xWidth(target_obj),10);
var tt = parseInt(t,10) + hh;
if( tobj != target_obj && tobj.xDPX >= l && tobj.xDPX <= ll) {
if(tobj.xDPY >= t && tobj.xDPY < tt-h) {
try {
target_obj.parentNode.insertBefore(tobj, target_obj);
tobj.target_id = target_obj.id;
} catch(e) {
}
}
}
}
}
// 드래그 종료 (이동되는 object가 이동할 곳에 서서히 이동되는 것처럼 보이는 효과)
function tree_drag_end(tobj, px, py) {
var obj = tree_get_tmp_object(tobj);
tree_disappear = tree_disapear_object(obj, tobj);
tree_drag_disable(tobj.id);
}
// 스르르 사라지게 함;;
function tree_disapear_object(obj, tobj) {
var it = 150;
var ib = 15;
var x = parseInt(xPageX(obj),10);
var y = parseInt(xPageY(obj),10);
var ldt = (x - parseInt(xPageX(tobj),10)) / ib;
var tdt = (y - parseInt(xPageY(tobj),10)) / ib;
return setInterval(function() {
if(ib < 1) {
clearInterval(tree_disappear);
xInnerHtml(tobj,xInnerHtml(obj));
xInnerHtml(obj,'');
xDisplay(obj, 'none');
return;
}
ib -= 5;
x-=ldt;
y-=tdt;
xLeft(obj, x);
xTop(obj, y);
}, it/ib);
}
// 마우스다운 이벤트 발생시 호출됨
function tree_mouse_down(e) {
var evt = new xEvent(e);
var obj = evt.target;
while(obj && !obj.draggable) {
obj = xParent(obj, true);
}
if(obj) {
xPreventDefault(e);
obj.xDPX = evt.pageX;
obj.xDPY = evt.pageY;
tree_drag_manager.obj = obj;
xAddEventListener(document, 'mouseup', tree_mouse_up, false);
if (obj.drag_start) obj.drag_start(obj, evt.pageX, evt.pageY);
}
}
// 마우스 버튼을 놓았을때 동작될 함수 (각종 이벤트 해제 및 변수 설정 초기화)
function tree_mouse_up(e) {
if (tree_drag_manager.obj) {
xPreventDefault(e);
xRemoveEventListener(document, 'mouseup', tree_mouse_up, false);
if (tree_drag_manager.obj.drag_end) {
var evt = new xEvent(e);
tree_drag_manager.obj.drag_end(tree_drag_manager.obj, evt.pageX, evt.pageY);
}
tree_drag_manager.obj = null;
tree_drag_manager.isDrag = false;
}
}
// 드래그할때의 object이동등을 담당
function tree_drag_mouse_move(e) {
var evt = new xEvent(e);
if (tree_drag_manager.obj) {
xPreventDefault(e);
var obj = tree_drag_manager.obj;
var dx = evt.pageX - obj.xDPX;
var dy = evt.pageY - obj.xDPY;
obj.xDPX = evt.pageX;
obj.xDPY = evt.pageY;
if (obj.drag) {
obj.drag(obj, dx, dy);
} else {
xMoveTo(obj, xLeft(obj) + dx, xTop(obj) + dy);
}
}
}
// 해당 object 에 더 이상 drag가 되지 않도록 설정
function tree_drag_disable(id) {
if (!tree_drag_manager) return;
var obj = xGetElementById(id);
obj.draggable = false;
obj.drag_start = null;
obj.drag = null;
obj.drag_end = null;
obj.style.color = obj.source_color;
xRemoveEventListener(obj, 'mousedown', tree_mouse_down, false);
if(obj.id && obj.target_id && obj.id!=obj.target_id) {
var menu_id = obj.id.replace(/menu_/,'');
menu_id = menu_id.replace(/_([0-9]+)$/,'');
if(menu_id) {
var callback_move_func = node_move_callback_func[menu_id];
if(callback_move_func) callback_move_func(menu_id, obj.id, obj.target_id);
}
}
obj.target_id = null;
}

621
common/js/x.js Normal file
View file

@ -0,0 +1,621 @@
/**
* x.js compiled from X 4.0 with XC 0.27b.
* Distributed by GNU LGPL. For copyrights, license, documentation and more visit Cross-Browser.com
* Copyright 2001-2005 Michael Foster (Cross-Browser.com)
**/
var xOp7Up,xOp6Dn,xIE4Up,xIE4,xIE5,xIE6,xNN4,xUA=navigator.userAgent.toLowerCase();
if(window.opera){
var i=xUA.indexOf('opera');
if(i!=-1){
var v=parseInt(xUA.charAt(i+6));
xOp7Up=v>=7;
xOp6Dn=v<7;
}
}
else if(navigator.vendor!='KDE' && document.all && xUA.indexOf('msie')!=-1){
xIE4Up=parseFloat(navigator.appVersion)>=4;
xIE4=xUA.indexOf('msie 4')!=-1;
xIE5=xUA.indexOf('msie 5')!=-1;
xIE6=xUA.indexOf('msie 6')!=-1;
}
else if(document.layers){xNN4=true;}
var xMac=xUA.indexOf('mac')!=-1;
// (element, event(without 'on'), event listener(function name)[, caption])
function xAddEventListener(e,eT,eL,cap)
{
if(!(e=xGetElementById(e))) return;
eT=eT.toLowerCase();
if((!xIE4Up && !xOp7Up) && e==window) {
if(eT=='resize') { window.xPCW=xClientWidth(); window.xPCH=xClientHeight(); window.xREL=eL; xResizeEvent(); return; }
if(eT=='scroll') { window.xPSL=xScrollLeft(); window.xPST=xScrollTop(); window.xSEL=eL; xScrollEvent(); return; }
}
var eh='e.on'+eT+'=eL';
if(e.addEventListener) e.addEventListener(eT,eL,cap);
else if(e.attachEvent) e.attachEvent('on'+eT,eL);
else eval(eh);
}
// called only from the above
function xResizeEvent()
{
if (window.xREL) setTimeout('xResizeEvent()', 250);
var cw = xClientWidth(), ch = xClientHeight();
if (window.xPCW != cw || window.xPCH != ch) { window.xPCW = cw; window.xPCH = ch; if (window.xREL) window.xREL(); }
}
function xScrollEvent()
{
if (window.xSEL) setTimeout('xScrollEvent()', 250);
var sl = xScrollLeft(), st = xScrollTop();
if (window.xPSL != sl || window.xPST != st) { window.xPSL = sl; window.xPST = st; if (window.xSEL) window.xSEL(); }
}
function xAppendChild(oParent, oChild)
{
if (oParent.appendChild) return oParent.appendChild(oChild);
else return null;
}
function xClientHeight()
{
var h=0;
if(xOp6Dn) h=window.innerHeight;
else if(document.compatMode == 'CSS1Compat' && !window.opera && document.documentElement && document.documentElement.clientHeight)
h=document.documentElement.clientHeight;
else if(document.body && document.body.clientHeight)
h=document.body.clientHeight;
else if(xDef(window.innerWidth,window.innerHeight,document.width)) {
h=window.innerHeight;
if(document.width>window.innerWidth) h-=16;
}
return h;
}
function xClientWidth()
{
var w=0;
if(xOp6Dn) w=window.innerWidth;
else if(document.compatMode == 'CSS1Compat' && !window.opera && document.documentElement && document.documentElement.clientWidth)
w=document.documentElement.clientWidth;
else if(document.body && document.body.clientWidth)
w=document.body.clientWidth;
else if(xDef(window.innerWidth,window.innerHeight,document.height)) {
w=window.innerWidth;
if(document.height>window.innerHeight) w-=16;
}
return w;
}
function xCreateElement(sTag)
{
if (document.createElement) return document.createElement(sTag);
else return null;
}
function xDef()
{
for(var i=0; i<arguments.length; ++i){if(typeof(arguments[i])=='undefined') return false;}
return true;
}
function xDeleteCookie(name, path)
{
if (xGetCookie(name)) {
document.cookie = name + "=" +
"; path=" + ((!path) ? "/" : path) +
"; expires=" + new Date(0).toGMTString();
}
}
function xDisplay(e,s)
{
if(!(e=xGetElementById(e))) return null;
if(e.style && xDef(e.style.display)) {
if (xStr(s)) e.style.display = s;
return e.style.display;
}
return null;
}
function xEvent(evt) // object prototype
{
var e = evt || window.event;
if(!e) return;
if(e.type) this.type = e.type;
if(e.target) this.target = e.target;
else if(e.srcElement) this.target = e.srcElement;
// Section B
if (e.relatedTarget) this.relatedTarget = e.relatedTarget;
else if (e.type == 'mouseover' && e.fromElement) this.relatedTarget = e.fromElement;
else if (e.type == 'mouseout') this.relatedTarget = e.toElement;
// End Section B
if(xOp6Dn) { this.pageX = e.clientX; this.pageY = e.clientY; }
else if(xDef(e.pageX,e.pageY)) { this.pageX = e.pageX; this.pageY = e.pageY; }
else if(xDef(e.clientX,e.clientY)) { this.pageX = e.clientX + xScrollLeft(); this.pageY = e.clientY + xScrollTop(); }
// Section A
if (xDef(e.offsetX,e.offsetY)) {
this.offsetX = e.offsetX;
this.offsetY = e.offsetY;
}
else if (xDef(e.layerX,e.layerY)) {
this.offsetX = e.layerX;
this.offsetY = e.layerY;
}
else {
this.offsetX = this.pageX - xPageX(this.target);
this.offsetY = this.pageY - xPageY(this.target);
}
// End Section A
if (e.keyCode) { this.keyCode = e.keyCode; } // for moz/fb, if keyCode==0 use which
else if (xDef(e.which) && e.type.indexOf('key')!=-1) { this.keyCode = e.which; }
this.shiftKey = e.shiftKey;
this.ctrlKey = e.ctrlKey;
this.altKey = e.altKey;
}
function xFirstChild(e, t)
{
var c = e ? e.firstChild : null;
if (t) while (c && c.nodeName != t) { c = c.nextSibling; }
else while (c && c.nodeType != 1) { c = c.nextSibling; }
return c;
}
function xGetBodyWidth() {
var cw = xClientWidth();
var sw = window.document.body.scrollWidth;
return cw>sw?cw:sw;
}
function xGetBodyHeight() {
var cw = xClientHeight();
var sw = window.document.body.scrollHeight;
return cw>sw?cw:sw;
}
function xGetComputedStyle(oEle, sProp, bInt)
{
var s, p = 'undefined';
var dv = document.defaultView;
if(dv && dv.getComputedStyle){
s = dv.getComputedStyle(oEle,'');
if (s) p = s.getPropertyValue(sProp);
}
else if(oEle.currentStyle) {
// convert css property name to object property name for IE
var a = sProp.split('-');
sProp = a[0];
for (var i=1; i<a.length; ++i) {
c = a[i].charAt(0);
sProp += a[i].replace(c, c.toUpperCase());
}
p = oEle.currentStyle[sProp];
}
else return null;
return bInt ? (parseInt(p) || 0) : p;
}
function xGetCookie(name)
{
var value=null, search=name+"=";
if (document.cookie.length > 0) {
var offset = document.cookie.indexOf(search);
if (offset != -1) {
offset += search.length;
var end = document.cookie.indexOf(";", offset);
if (end == -1) end = document.cookie.length;
value = unescape(document.cookie.substring(offset, end));
}
}
return value;
}
function xGetElementById(e)
{
if(typeof(e)!='string') return e;
if(document.getElementById) e=document.getElementById(e);
else if(document.all) e=document.all[e];
else e=null;
return e;
}
function xGetElementsByAttribute(sTag, sAtt, sRE, fn)
{
var a, list, found = new Array(), re = new RegExp(sRE, 'i');
list = xGetElementsByTagName(sTag);
for (var i = 0; i < list.length; ++i) {
a = list[i].getAttribute(sAtt);
if (!a) {a = list[i][sAtt];}
if (typeof(a)=='string' && a.search(re) != -1) {
found[found.length] = list[i];
if (fn) fn(list[i]);
}
}
return found;
}
function xGetElementsByClassName(c,p,t,f)
{
var found = new Array();
var re = new RegExp('\\b'+c+'\\b', 'i');
var list = xGetElementsByTagName(t, p);
for (var i = 0; i < list.length; ++i) {
if (list[i].className && list[i].className.search(re) != -1) {
found[found.length] = list[i];
if (f) f(list[i]);
}
}
return found;
}
function xGetElementsByTagName(t,p)
{
var list = null;
t = t || '*';
p = p || document;
if (xIE4 || xIE5) {
if (t == '*') list = p.all;
else list = p.all.tags(t);
}
else if (p.getElementsByTagName) list = p.getElementsByTagName(t);
return list || new Array();
}
function xGetURLArguments()
{
var idx = location.href.indexOf('?');
var params = new Array();
if (idx != -1) {
var pairs = location.href.substring(idx+1, location.href.length).split('&');
for (var i=0; i<pairs.length; i++) {
nameVal = pairs[i].split('=');
params[i] = nameVal[1];
params[nameVal[0]] = nameVal[1];
}
}
return params;
}
function xHeight(e,h)
{
if(!(e=xGetElementById(e))) return 0;
if (xNum(h)) {
if (h<0) h = 0;
else h=Math.round(h);
}
else h=-1;
var css=xDef(e.style);
if (e == document || e.tagName.toLowerCase() == 'html' || e.tagName.toLowerCase() == 'body') {
h = xClientHeight();
}
else if(css && xDef(e.offsetHeight) && xStr(e.style.height)) {
if(h>=0) {
var pt=0,pb=0,bt=0,bb=0;
if (document.compatMode=='CSS1Compat') {
var gcs = xGetComputedStyle;
pt=gcs(e,'padding-top',1);
if (pt !== null) {
pb=gcs(e,'padding-bottom',1);
bt=gcs(e,'border-top-width',1);
bb=gcs(e,'border-bottom-width',1);
}
// Should we try this as a last resort?
// At this point getComputedStyle and currentStyle do not exist.
else if(xDef(e.offsetHeight,e.style.height)){
e.style.height=h+'px';
pt=e.offsetHeight-h;
}
}
h-=(pt+pb+bt+bb);
if(isNaN(h)||h<0) return null;
else e.style.height=h+'px';
}
h=e.offsetHeight;
}
else if(css && xDef(e.style.pixelHeight)) {
if(h>=0) e.style.pixelHeight=h;
h=e.style.pixelHeight;
}
return h;
}
function xHex(sn, digits, prefix)
{
var p = '';
var n = Math.ceil(sn);
if (prefix) p = prefix;
n = n.toString(16);
for (var i=0; i < digits - n.length; ++i) {
p += '0';
}
return p + n;
}
function xHide(e){return xVisibility(e,0);}
function xInnerHtml(e,h)
{
if(!(e=xGetElementById(e)) || !xStr(e.innerHTML)) return null;
var s = e.innerHTML;
if (xStr(h)) {e.innerHTML = h;}
return s;
}
function xLeft(e, iX)
{
if(!(e=xGetElementById(e))) return 0;
var css=xDef(e.style);
if (css && xStr(e.style.left)) {
if(xNum(iX)) e.style.left=iX+'px';
else {
iX=parseInt(e.style.left);
if(isNaN(iX)) iX=0;
}
}
else if(css && xDef(e.style.pixelLeft)) {
if(xNum(iX)) e.style.pixelLeft=iX;
else iX=e.style.pixelLeft;
}
return iX;
}
function xMoveTo(e,x,y)
{
xLeft(e,x);
xTop(e,y);
}
function xName(e)
{
if (!e) return e;
else if (e.id && e.id != "") return e.id;
else if (e.name && e.name != "") return e.name;
else if (e.nodeName && e.nodeName != "") return e.nodeName;
else if (e.tagName && e.tagName != "") return e.tagName;
else return e;
}
function xNextSib(e,t)
{
var s = e ? e.nextSibling : null;
if (t) while (s && s.nodeName != t) { s = s.nextSibling; }
else while (s && s.nodeType != 1) { s = s.nextSibling; }
return s;
}
function xNum()
{
for(var i=0; i<arguments.length; ++i){if(isNaN(arguments[i]) || typeof(arguments[i])!='number') return false;}
return true;
}
function xOffsetLeft(e)
{
if (!(e=xGetElementById(e))) return 0;
if (xDef(e.offsetLeft)) return e.offsetLeft;
else return 0;
}
function xOffsetTop(e)
{
if (!(e=xGetElementById(e))) return 0;
if (xDef(e.offsetTop)) return e.offsetTop;
else return 0;
}
function xPad(s,len,c,left)
{
if(typeof s != 'string') s=s+'';
if(left) {for(var i=s.length; i<len; ++i) s=c+s;}
else {for (i=s.length; i<len; ++i) s+=c;}
return s;
}
function xPageX(e)
{
if (!(e=xGetElementById(e))) return 0;
var x = 0;
while (e) {
if (xDef(e.offsetLeft)) x += e.offsetLeft;
e = xDef(e.offsetParent) ? e.offsetParent : null;
}
return x;
}
function xPageY(e)
{
if (!(e=xGetElementById(e))) return 0;
var y = 0;
while (e) {
if (xDef(e.offsetTop)) y += e.offsetTop;
e = xDef(e.offsetParent) ? e.offsetParent : null;
}
// if (xOp7Up) return y - document.body.offsetTop; // v3.14, temporary hack for opera bug 130324 (reported 1nov03)
return y;
}
function xParent(e, bNode)
{
if (!(e=xGetElementById(e))) return null;
var p=null;
if (!bNode && xDef(e.offsetParent)) p=e.offsetParent;
else if (xDef(e.parentNode)) p=e.parentNode;
else if (xDef(e.parentElement)) p=e.parentElement;
return p;
}
function xPreventDefault(e)
{
if (e && e.preventDefault) e.preventDefault()
else if (window.event) window.event.returnValue = false;
}
function xPrevSib(e,t)
{
var s = e ? e.previousSibling : null;
if (t) while(s && s.nodeName != t) {s=s.previousSibling;}
else while(s && s.nodeType != 1) {s=s.previousSibling;}
return s;
}
function xRemoveEventListener(e,eT,eL,cap)
{
if(!(e=xGetElementById(e))) return;
eT=eT.toLowerCase();
if((!xIE4Up && !xOp7Up) && e==window) {
if(eT=='resize') { window.xREL=null; return; }
if(eT=='scroll') { window.xSEL=null; return; }
}
var eh='e.on'+eT+'=null';
if(e.removeEventListener) e.removeEventListener(eT,eL,cap);
else if(e.detachEvent) e.detachEvent('on'+eT,eL);
else eval(eh);
}
function xResizeTo(e,w,h)
{
xWidth(e,w);
xHeight(e,h);
}
function xScrollLeft(e, bWin)
{
var offset=0;
if (!xDef(e) || bWin || e == document || e.tagName.toLowerCase() == 'html' || e.tagName.toLowerCase() == 'body') {
var w = window;
if (bWin && e) w = e;
if(w.document.documentElement && w.document.documentElement.scrollLeft) offset=w.document.documentElement.scrollLeft;
else if(w.document.body && xDef(w.document.body.scrollLeft)) offset=w.document.body.scrollLeft;
}
else {
e = xGetElementById(e);
if (e && xNum(e.scrollLeft)) offset = e.scrollLeft;
}
return offset;
}
function xScrollTop(e, bWin)
{
var offset=0;
if (!xDef(e) || bWin || e == document || e.tagName.toLowerCase() == 'html' || e.tagName.toLowerCase() == 'body') {
var w = window;
if (bWin && e) w = e;
if(w.document.documentElement && w.document.documentElement.scrollTop) offset=w.document.documentElement.scrollTop;
else if(w.document.body && xDef(w.document.body.scrollTop)) offset=w.document.body.scrollTop;
}
else {
e = xGetElementById(e);
if (e && xNum(e.scrollTop)) offset = e.scrollTop;
}
return offset;
}
function xSetCookie(name, value, expire, path)
{
document.cookie = name + "=" + escape(value) +
((!expire) ? "" : ("; expires=" + expire.toGMTString())) +
"; path=" + ((!path) ? "/" : path);
}
function xShow(e) {return xVisibility(e,1);}
function xStr(s)
{
for(var i=0; i<arguments.length; ++i){if(typeof(arguments[i])!='string') return false;}
return true;
}
function xTop(e, iY)
{
if(!(e=xGetElementById(e))) return 0;
var css=xDef(e.style);
if(css && xStr(e.style.top)) {
if(xNum(iY)) e.style.top=iY+'px';
else {
iY=parseInt(e.style.top);
if(isNaN(iY)) iY=0;
}
}
else if(css && xDef(e.style.pixelTop)) {
if(xNum(iY)) e.style.pixelTop=iY;
else iY=e.style.pixelTop;
}
return iY;
}
function xVisibility(e, bShow)
{
if(!(e=xGetElementById(e))) return null;
if(e.style && xDef(e.style.visibility)) {
if (xDef(bShow)) e.style.visibility = bShow ? 'visible' : 'hidden';
return e.style.visibility;
}
return null;
}
function xWidth(e,w)
{
if(!(e=xGetElementById(e))) return 0;
if (xNum(w)) {
if (w<0) w = 0;
else w=Math.round(w);
}
else w=-1;
var css=xDef(e.style);
if (e == document || e.tagName.toLowerCase() == 'html' || e.tagName.toLowerCase() == 'body') {
w = xClientWidth();
}
else if(css && xDef(e.offsetWidth) && xStr(e.style.width)) {
if(w>=0) {
var pl=0,pr=0,bl=0,br=0;
if (document.compatMode=='CSS1Compat') {
var gcs = xGetComputedStyle;
pl=gcs(e,'padding-left',1);
if (pl !== null) {
pr=gcs(e,'padding-right',1);
bl=gcs(e,'border-left-width',1);
br=gcs(e,'border-right-width',1);
}
// Should we try this as a last resort?
// At this point getComputedStyle and currentStyle do not exist.
else if(xDef(e.offsetWidth,e.style.width)){
e.style.width=w+'px';
pl=e.offsetWidth-w;
}
}
w-=(pl+pr+bl+br);
if(isNaN(w)||w<0) return null;
else e.style.width=w+'px';
}
w=e.offsetWidth;
}
else if(css && xDef(e.style.pixelWidth)) {
if(w>=0) e.style.pixelWidth=w;
w=e.style.pixelWidth;
}
return w;
}
function xZIndex(e,uZ)
{
if(!(e=xGetElementById(e))) return 0;
if(e.style && xDef(e.style.zIndex)) {
if(xNum(uZ)) e.style.zIndex=uZ;
uZ=parseInt(e.style.zIndex);
}
return uZ;
}
function xStopPropagation(evt)
{
if (evt && evt.stopPropagation) evt.stopPropagation();
else if (window.event) window.event.cancelBubble = true;
}

147
common/js/xml_handler.js Normal file
View file

@ -0,0 +1,147 @@
/**
* @file common/js/xml_handler.js
* @author zero <zero@nzeo.com>
* @brief zbxe내에서 ajax기능을 이용함에 있어 module, act를 사용하기 위한 자바스크립트
**/
// xml handler을 이용하는 user function
var show_waiting_message = true;
function exec_xml(module, act, params, callback_func, response_tags, callback_func_arg, fo_obj) {
var oXml = new xml_handler();
oXml.reset();
for(var key in params) {
var val = params[key];
oXml.addParam(key, val);
}
oXml.addParam("module", module);
oXml.addParam("act", act);
if(typeof(response_tags)=="undefined" || response_tags.length<1) response_tags = new Array('error','message');
var waiting_obj = xGetElementById("waitingforserverresponse");
if(show_waiting_message && waiting_obj) {
xInnerHtml(waiting_obj, wating_message);
xTop(waiting_obj, xScrollTop()+20);
xLeft(waiting_obj, xScrollLeft()+20);
waiting_obj.style.visibility = "visible";
}
oXml.request(xml_response_filter, oXml, callback_func, response_tags, callback_func_arg, fo_obj);
}
// 결과 처리 후 callback_func에 넘겨줌
function xml_response_filter(oXml, callback_func, response_tags, callback_func_arg, fo_obj) {
var xmlDoc = oXml.getResponseXml();
if(!xmlDoc) return null;
var waiting_obj = xGetElementById("waitingforserverresponse");
waiting_obj.style.visibility = "hidden";
xInnerHtml(waiting_obj, '');
var ret_obj = oXml.toZMsgObject(xmlDoc, response_tags);
if(ret_obj["error"]!=0) {
alert(ret_obj["message"]);
return null;
}
if(!callback_func) return null;
callback_func(ret_obj, response_tags, callback_func_arg, fo_obj);
return null;
}
// xml handler
function xml_handler() {
this.obj_xmlHttp = null;
this.method_name = null;
this.xml_path = request_uri+"index.php";
this.params = new Array();
this.reset = xml_handlerReset;
this.getXmlHttp = zGetXmlHttp;
this.request = xml_handlerRequest;
this.setPath = xml_handlerSetPath;
this.addParam = xml_handlerAddParam;
this.getResponseXml = xml_handlerGetResponseXML;
this.toZMsgObject = xml_handlerToZMsgObject;
this.obj_xmlHttp = this.getXmlHttp();
}
function zGetXmlHttp() {
if (window.XMLHttpRequest) return new XMLHttpRequest();
else if (window.ActiveXObject) {
try {
return new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
return new ActiveXObject("Microsoft.XMLHTTP");
}
}
return null;
}
function xml_handlerRequest(callBackFunc, xmlObj, callBackFunc2, response_tags, callback_func_arg, fo_obj) {
var rd = "";
rd += "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
+ "<methodCall>\n"
+ "<params>\n"
for (var key in this.params) {
var val = this.params[key];
rd += "<"+key+"><![CDATA["+val+"]]></"+key+">\n";
}
rd += "</params>\n"
+ "</methodCall>\n";
if(this.obj_xmlHttp.readyState!=0) {
this.obj_xmlHttp.abort();
this.obj_xmlHttp = this.getXmlHttp();
}
this.obj_xmlHttp.onreadystatechange = function () {callBackFunc(xmlObj, callBackFunc2, response_tags, callback_func_arg, fo_obj)};
this.obj_xmlHttp.open("POST", this.xml_path, true);
this.obj_xmlHttp.send(rd);
}
function xml_handlerSetPath(path) {
this.xml_path = "./"+path;
}
function xml_handlerReset() {
this.obj_xmlHttp = this.getXmlHttp();
this.params = new Array();
}
function xml_handlerAddParam(key, val) {
this.params[key] = val;
}
function xml_handlerGetResponseXML() {
if(this.obj_xmlHttp && this.obj_xmlHttp.readyState == 4 && isDef(this.obj_xmlHttp.responseXML)) {
var xmlDoc = this.obj_xmlHttp.responseXML;
this.reset();
return xmlDoc;
}
return null;
}
function xml_handlerToZMsgObject(xmlDoc, tags) {
if(!xmlDoc) return null;
if(!tags) tags = new Array("error","message");
tags[tags.length] = "redirect_url";
tags[tags.length] = "act";
var obj_ret = new Array();
for(var i=0; i<tags.length; i++) {
var key = tags[i];
if(obj_ret[key]) continue;
try {
obj_ret[key] = xmlDoc.getElementsByTagName(tags[i])[0].firstChild.nodeValue;
} catch(e) {
obj_ret[key] = "";
}
}
return obj_ret;
}

305
common/js/xml_js_filter.js Normal file
View file

@ -0,0 +1,305 @@
/**
* @file common/js/xml_js_filter.js
* @author zero (zero@nzeo.com)
* @brief xml filter에서 사용될 js
*
* zbxe 에서 form의 동작시 필수입력 여부등을 선처리하고 xml_handler.js의 exec_xml() 통해서
* 특정 모듈과의 ajax 통신을 통해 process를 진행시킴
**/
var alertMsg = new Array();
var target_type_list = new Array();
var notnull_list = new Array();
var extra_vars = new Array();
/**
* @function filterAlertMessage
* @brief ajax로 서버에 요청후 결과를 처리할 callback_function을 지정하지 않았을 호출되는 기본 함수
**/
function filterAlertMessage(ret_obj) {
var error = ret_obj["error"];
var message = ret_obj["message"];
var act = ret_obj["act"];
var redirect_url = ret_obj["redirect_url"];
var url = location.href;
if(url.substr(-1)=="#") url = url.substr(0,url.length-1);
if(typeof(message)!="undefined"&&message&&message!="success") alert(message);
if(typeof(act)!="undefined" && act) url = current_url.setQuery("act", act);
else if(typeof(redirect_url)!="undefined" && redirect_url) url = redirect_url;
location.href = url;
}
/**
* @class XmlJsFilter
* @authro zero (zero@nzeo.com)
* @brief form elements, module/act, callback_user_func을 이용하여 서버에 ajax로 form 데이터를 넘기고 결과를 받아오는 js class
**/
function XmlJsFilter(form_object, module, act, callback_user_func) {
this.field = new Array();
this.parameter = new Array();
this.response = new Array();
this.fo_obj = form_object;
this.module = module;
this.act = act;
this.user_func = callback_user_func;
this.setFocus = XmlJsFilterSetFocus;
this.addFieldItem = XmlJsFilterAddFieldItem;
this.addParameterItem = XmlJsFilterAddParameterItem;
this.addResponseItem = XmlJsFilterAddResponseItem;
this.getValue = XmlJsFilterGetValue;
this.executeFilter = XmlJsFilterExecuteFilter;
this.checkFieldItem = XmlJsFilterCheckFieldItem;
this.getParameterParam = XmlJsFilterGetParameterParam;
this.alertMsg = XmlJsFilterAlertMsg;
this.proc = XmlJsFilterProc;
}
function XmlJsFilterSetFocus(target_name) {
var obj = this.fo_obj[target_name];
if(typeof(obj)=='undefined' || !obj) return;
var length = obj.length;
try {
if(typeof(length)!='undefined') {
obj[0].focus();
} else {
obj.focus();
}
} catch(e) {
}
}
function XmlJsFilterAddFieldItem(target, required, minlength, maxlength, equalto, filter) {
var obj = new Array(target, required, minlength, maxlength, equalto, filter);
this.field[this.field.length] = obj;
}
function XmlJsFilterAddParameterItem(param, target) {
var obj = new Array(param, target);
this.parameter[this.parameter.length] = obj;
}
function XmlJsFilterAddResponseItem(name) {
this.response[this.response.length] = name;
}
function XmlJsFilterGetValue(target_name) {
var obj = this.fo_obj[target_name];
if(typeof(obj)=='undefined' || !obj) return '';
var value = '';
var length = obj.length;
var type = obj.type;
if((typeof(type)=='undefined'||!type) && typeof(length)!='undefined' && typeof(obj[0])!='undefined' && length>0) type = obj[0].type;
else length = 0;
switch(type) {
case 'checkbox' :
if(length>0) {
var value_list = new Array();
for(var i=0;i<length;i++) {
if(obj[i].checked) value_list[value_list.length] = obj[i].value;
}
value = value_list.join('|@|');
} else {
if(obj.checked) value = obj.value;
else value = '';
}
break;
case 'radio' :
if(length>0) {
for(var i=0;i<length;i++) {
if(obj[i].checked) value = obj[i].value;
}
} else {
if(obj.checked) value = obj.value;
else value = '';
}
break;
case 'select' :
case 'select-one' :
if(obj.selectedIndex>=0) value = obj.options[obj.selectedIndex].value;
break;
default :
if(length>0 && target_type_list[target_name]) {
switch(target_type_list[target_name]) {
case 'kr_zip' :
var val1 = obj[0].value;
var val2 = obj[1].value;
if(val1&&val2) {
value = val1+'|@|'+val2;
}
break;
case 'tel' :
var val1 = obj[0].value;
var val2 = obj[1].value;
var val3 = obj[2].value;
if(val1&&val2&&val3) {
value = val1+'|@|'+val2+'|@|'+val3;
}
break;
}
} else {
value = obj.value;
}
break;
}
if(typeof(value)=='undefined'||!value) return '';
return value.trim();
}
function XmlJsFilterExecuteFilter(filter, value) {
switch(filter) {
case "email" :
case "email_address" :
var regx = /^[_0-9a-zA-Z-]+(\.[_0-9a-zA-Z-]+)*@[0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*$/;
return regx.test(value);
break;
case "userid" :
case "user_id" :
var regx = /^[a-zA-Z]+([_0-9a-zA-Z]+)*$/;
return regx.test(value);
break;
case "homepage" :
var regx = /^(http|https|ftp|mms):\/\/[0-9a-z-]+(\.[_0-9a-z-\/\~]+)+(:[0-9]{2,4})*$/;
return regx.test(value);
break;
case "korean" :
var regx = /^[가-힣]*$/;
return regx.test(value);
break;
case "korean_number" :
var regx = /^[가-힣0-9]*$/;
return regx.test(value);
break;
case "alpha" :
var regx = /^[a-zA-Z]*$/;
return regx.test(value);
break;
case "alpha_number" :
var regx = /^[a-zA-Z0-9\_]*$/;
return regx.test(value);
break;
case "number" :
return !isNaN(value);
break;
}
return null;
}
function XmlJsFilterAlertMsg(target, msg_code, minlength, maxlength) {
var target_msg = "";
if(alertMsg[target]!='undefined') target_msg = alertMsg[target];
else target_msg = target;
var msg = "";
if(typeof(alertMsg[msg_code])!='undefined') {
if(alertMsg[msg_code].indexOf('%s')>=0) msg = alertMsg[msg_code].replace('%s',target_msg);
else msg = target_msg+alertMsg[msg_code];
} else {
msg = msg_code;
}
if(typeof(minlength)!='undefined' && typeof(maxlength)!='undefined') msg += "("+minlength+"~"+maxlength+")";
alert(msg);
this.setFocus(target);
return false;
}
function XmlJsFilterCheckFieldItem() {
for(var i=0; i<extra_vars.length;i++) {
var name = extra_vars[i];
this.addFieldItem(name, false, 0, 0, "", "");
}
for(var i=0; i<this.field.length;i++) {
var item = this.field[i];
var target = item[0];
var required = item[1];
var minlength = item[2];
var maxlength = item[3];
var equalto = item[4];
var filter = item[5].split(",");
for(var j=0; j<notnull_list.length; j++) {
if(notnull_list[j]==target) required = true;
}
var value = this.getValue(target);
if(!required && !value) continue;
if(required && !value && this.fo_obj[target]) return this.alertMsg(target,'isnull');
if(minlength>0 && maxlength>0 && (value.length < minlength || value.length > maxlength)) return this.alertMsg(target, 'outofrange', minlength, maxlength);
if(equalto) {
var equalto_value = this.getValue(equalto);
if(equalto_value != value) return this.alertMsg(target, 'equalto');
}
if(filter.length && filter[0]) {
for(var j=0;j<filter.length;j++) {
var filter_item = filter[j];
if(!this.executeFilter(filter_item, value)) return this.alertMsg(target, "invalid_"+filter_item);
}
}
}
return true;
}
function XmlJsFilterGetParameterParam() {
if(!this.fo_obj) return new Array();
var prev_name = '';
if(this.parameter.length<1) {
for(var i=0;i<this.fo_obj.length;i++) {
var name = this.fo_obj[i].name;
if(typeof(name)=='undefined'||!name||name==prev_name) continue;
this.addParameterItem(name, name);
prev_name = name;
}
}
var params = new Array();
for(var i=0; i<this.parameter.length;i++) {
var item = this.parameter[i];
var param = item[0];
var target = item[1];
var value = this.getValue(target);
params[param] = value;
}
return params;
}
function XmlJsFilterProc(confirm_msg) {
var result = this.checkFieldItem();
if(!result) return false;
if(typeof(confirm_msg)=='undefined') confirm_msg = '';
var params = this.getParameterParam();
var response = this.response;
if(confirm_msg && !confirm(confirm_msg)) return false;
if(!this.act) {
this.user_func(this.fo_obj, params);
return true;
}
exec_xml(this.module, this.act, params, this.user_func, response, params, this.fo_obj);
return null;
}
// form proc
function procFilter(fo_obj, filter_func) {
filter_func(fo_obj);
return false;
}

221
common/lang/en.lang.php Normal file
View file

@ -0,0 +1,221 @@
<?php
/**
* @file common/lang/en.lang.php
* @author zero (zero@nzeo.com)
* @brief English Language Pack (Only basic words are included here)
**/
// words for action, which is basically used
$lang->cmd_write = 'Write';
$lang->cmd_reply = 'Reply';
$lang->cmd_delete = 'Delete';
$lang->cmd_modify = 'Modify';
$lang->cmd_edit = 'Edit';
$lang->cmd_view = 'View';
$lang->cmd_view_all = 'View All';
$lang->cmd_list = 'List';
$lang->cmd_prev = 'Prev';
$lang->cmd_next = 'Next';
$lang->cmd_send_trackback = 'Send Trackback';
$lang->cmd_registration = $lang->cmd_submit = 'Submit';
$lang->cmd_comment_registration = 'Add Comment';
$lang->cmd_insert = 'Insert';
$lang->cmd_save = 'Save';
$lang->cmd_input = 'Input';
$lang->cmd_search = 'Search';
$lang->cmd_cancel = 'Cancel';
$lang->cmd_back = 'Go Back';
$lang->cmd_vote = 'Vote';
$lang->cmd_move = 'Move';
$lang->cmd_move_up = 'Up';
$lang->cmd_move_down = 'Down';
$lang->cmd_add_indent = 'Indent';
$lang->cmd_remove_indent = 'Outdent';
$lang->cmd_management = 'Manage';
$lang->cmd_make = 'Create';
$lang->cmd_select = 'Select';
$lang->cmd_select_all = 'Select All';
$lang->cmd_unselect_all = 'Deselect All';
$lang->cmd_close_all = 'Close All';
$lang->cmd_open_all = 'Open All';
$lang->cmd_reload = 'Reload';
$lang->cmd_close = 'Close';
$lang->cmd_open = 'Open';
$lang->cmd_setup = 'Configure';
$lang->cmd_option = 'Option';
$lang->cmd_apply = 'Apply';
$lang->cmd_open_calendar = 'Select a Date';
$lang->cmd_send = 'Send';
$lang->cmd_print = 'Print';
$lang->cmd_scrap = 'Scrap';
$lang->cmd_preview = 'Preview';
$lang->cmd_reset = 'Reset';
$lang->cmd_remake_cache = "Re-create cache file";
$lang->enable = 'Enable';
$lang->disable = 'Disable';
// Essential Words
$lang->no = 'No.';
$lang->notice = 'Notice';
$lang->secret = 'Secret';
$lang->category = 'Category';
$lang->document_srl = 'Doc. No.';
$lang->user_id = 'User ID';
$lang->author = 'Developer';
$lang->password = 'Password';
$lang->password1 = 'Password';
$lang->password2 = 'Confirm Password';
$lang->admin_id = 'Admin ID';
$lang->writer = 'Author';
$lang->user_name = 'User Name';
$lang->nick_name = 'Nick Name';
$lang->email_address = 'Email';
$lang->homepage = 'Homepage';
$lang->blog = 'Blog';
$lang->birthday = 'Birthdate';
$lang->browser_title = 'Browser Title';
$lang->title = 'Subject';
$lang->title_content = 'Title+Content';
$lang->content = 'Content';
$lang->document = 'Article';
$lang->comment = 'Comment';
$lang->description = 'Description';
$lang->trackback = 'Trackback';
$lang->tag = 'Tag';
$lang->allow_comment = 'Allow Comment';
$lang->lock_comment = 'Block Comment';
$lang->allow_trackback = 'Allow Trackback';
$lang->uploaded_file = 'Attachment';
$lang->grant = 'Permission';
$lang->target = 'Target';
$lang->total = 'Total';
$lang->total_count = 'Count Total';
$lang->ipaddress = 'IP Address';
$lang->path = 'Path';
$lang->cart = 'Selected Item';
$lang->friend = 'Friends';
$lang->notify = 'Notification';
$lang->mid = 'Module Name';
$lang->layout = 'Layout';
$lang->widget = 'Widget';
$lang->module = 'Module';
$lang->skin = 'Theme';
$lang->colorset = 'Colorset';
$lang->extra_vars = 'Extra Vars';
$lang->document_url = 'Entry URL';
$lang->trackback_url = 'Trackback URL';
$lang->blog_name = 'Blog Title';
$lang->excerpt = 'Quotation';
$lang->document_count = 'Total Articles';
$lang->page_count = 'Page Count';
$lang->list_count = 'List Count';
$lang->readed_count = 'Hit';
$lang->voted_count = 'Votes';
$lang->member_count = 'Member Count';
$lang->date = 'Date';
$lang->regdate = 'Registered Date';
$lang->last_update = 'Latest Update';
$lang->signup_date = 'Join Date';
$lang->last_login = 'Latest Login';
$lang->first_page = 'First Page';
$lang->last_page = 'Last Page';
$lang->search_target = 'Searching Target';
$lang->search_keyword = 'Searching Keyword';
$lang->is_default = 'Default';
$lang->no_documents = 'No Articles';
$lang->board_manager = 'Board Settings';
$lang->member_manager = 'Member Settings';
$lang->layout_manager = 'Layout Settings';
$lang->use = 'Use';
$lang->notuse = 'Disuse';
$lang->not_exists = "Does not exist";
$lang->unit_sec = 'sec';
$lang->unit_min = 'min';
$lang->unit_hour = 'hr';
$lang->unit_day = 'day';
$lang->unit_week = 'week';
$lang->unit_month = 'month';
$lang->unit_year = 'year';
// Descriptions
$lang->about_tag = 'You can submit multiple tags by inserting comma(,) between each tags';
$lang->about_layout = 'Layouts decorate an appearance of your module. you could configure it from Layout menu on the top';
// Messages
$lang->msg_call_server = 'Requesting to the server, please wait';
$lang->msg_db_not_setted = 'DB configuration has not been set';
$lang->msg_invalid_queryid = 'Specified query ID value is invalid';
$lang->msg_not_permitted = 'You are not permitted to continue';
$lang->msg_input_password = 'Please enter your password';
$lang->msg_invalid_document = 'Invalid Article Number';
$lang->msg_invalid_request = 'Invalid Request';
$lang->msg_invalid_password = 'Invalid Password';
$lang->msg_error_occured = 'Error has occured';
$lang->msg_not_founded = 'Target could not be found';
$lang->msg_no_result = 'Nothing found';
$lang->msg_not_permitted_act = 'You are not permitted to execute requested action';
$lang->msg_module_is_not_exists = 'Requested module could not be found';
$lang->msg_module_is_not_standalone = 'Requested module cannot be executed independently';
$lang->success_registed = 'Registered successfully';
$lang->success_updated = 'Updated successfully';
$lang->success_deleted = 'Deleted successfully';
$lang->success_voted = 'Voted successfully';
$lang->success_moved = 'Moved successfully';
$lang->success_sended = 'Sent successfully';
$lang->success_reset = 'Reset successfully';
$lang->success_leaved = 'Seceded successfully';
$lang->fail_to_delete = 'Could not be deleted';
$lang->fail_to_move = 'Could not be moved';
$lang->failed_voted = 'Could not be voted';
$lang->fail_to_delete_have_children = 'Please try again after removing replies first';
$lang->confirm_submit = 'Are you sure to submit?';
$lang->confirm_logout = 'Are you sure to logout?';
$lang->confirm_vote = 'Are you sure to vote?';
$lang->confirm_delete = 'Are you sure to delete?';
$lang->confirm_move = 'Are you sure to move?';
$lang->confirm_reset = 'Are you sure to reset?';
$lang->confirm_leave = 'Are you sure to leave?';
$lang->column_type = 'Column Type';
$lang->column_type_list['text'] = 'one-line text';
$lang->column_type_list['homepage'] = 'url';
$lang->column_type_list['email_address'] = 'email';
$lang->column_type_list['tel'] = 'phone number';
$lang->column_type_list['textarea'] = 'multi-line textarea';
$lang->column_type_list['checkbox'] = 'checkbox (multiple selection)';
$lang->column_type_list['select'] = 'select box (single selection)';
$lang->column_type_list['kr_zip'] = 'zip code (Korean)';
$lang->column_type_list['date'] = 'date (yyyy/mm/dd)';
//$lang->column_type_list['jp_zip'] = 'zip code (Japanese)';
$lang->column_name = 'Column Name';
$lang->column_title = 'Column Title';
$lang->default_value = 'Default Value';
$lang->is_active = 'Active';
$lang->is_required = 'Required Field';
// Alert messages for Javascript using by XML filter
$lang->filter->isnull = 'Please input a value for %s';
$lang->filter->outofrange = 'Please align the text length of %s';
$lang->filter->equalto = "%s's value is invalid";
$lang->filter->invalid_email = "%s's format is invalid. ex) zbxe@zeroboard.com";
$lang->filter->invalid_user_id = $lang->filter->invalid_userid = "%s's format is invalid.\\nAll values should consist of alphabets, numbers or underscore(_) and the first letter should be English alphabet";
$lang->filter->invalid_homepage = "%s's format is invalid. ex) http://www.zeroboard.com";
$lang->filter->invalid_korean = "%s's format is invalid. Please input only Korean";
$lang->filter->invalid_korean_number = "%s's format is invalid. Please input Korean or numbers";
$lang->filter->invalid_alpha = "%s's format is invalid. Please input only alphabets";
$lang->filter->invalid_alpha_number = "%s's format is invalid. Please input alphabets or numbers";
$lang->filter->invalid_number = "%s's format is invalid. Please input only numbers";
?>

220
common/lang/es.lang.php Normal file
View file

@ -0,0 +1,220 @@
<?php
/**
* @file common/lang/en.lang.php
* @author zero (zero@nzeo.com)
* @brief English Language Pack (Only basic words are included here)
**/
// words for action, which is basically used
$lang->cmd_write = 'Escribir';
$lang->cmd_reply = 'Contestación';
$lang->cmd_delete = 'Cancelación';
$lang->cmd_modify = 'Modificar';
$lang->cmd_edit = 'Corregir';
$lang->cmd_view = 'Ver';
$lang->cmd_view_all = 'Ver Toda';
$lang->cmd_list = 'List';
$lang->cmd_prev = 'Prev';
$lang->cmd_next = 'Adelante';
$lang->cmd_send_trackback = 'Enviar Trackback';
$lang->cmd_registration = $lang->cmd_submit = 'Someter';
$lang->cmd_comment_registration = 'Agregar El Commentario';
$lang->cmd_insert = 'Insertar';
$lang->cmd_save = 'Guardar';
$lang->cmd_input = 'Entrdada';
$lang->cmd_search = 'Búsqueda';
$lang->cmd_cancel = 'Cancelar';
$lang->cmd_back = 'Ir Detrás';
$lang->cmd_vote = 'Votar';
$lang->cmd_move = 'Mover';
$lang->cmd_move_up = 'Arriba';
$lang->cmd_move_down = 'Abajo';
$lang->cmd_add_indent = 'Agregar Pedido';
$lang->cmd_remove_indent = 'Eliminar Pedido';
$lang->cmd_management = 'Manejar';
$lang->cmd_make = 'Crear';
$lang->cmd_select = 'Seleccionar';
$lang->cmd_select_all = 'Seleccionar Todos';
$lang->cmd_unselect_all = 'Deseleccionar Todos';
$lang->cmd_close_all = 'Cerrar Todos';
$lang->cmd_open_all = 'Abrir Todos';
$lang->cmd_reload = 'Actualizar';
$lang->cmd_close = 'Cerrar';
$lang->cmd_open = 'Abrir';
$lang->cmd_setup = 'Configuración';
$lang->cmd_option = 'Opción';
$lang->cmd_apply = 'Applicar';
$lang->cmd_open_calendar = 'Seleccionar la fecha';
$lang->cmd_send = 'Enviar';
$lang->cmd_print = 'Print';
$lang->cmd_scrap = 'Scrap';
$lang->cmd_preview = 'Preview';
$lang->cmd_reset = 'Reset';
$lang->enable = 'Enable';
$lang->disable = 'Disable';
// Essencial Words
$lang->no = 'No';
$lang->notice = 'Aviso';
$lang->secret = 'Secreto';
$lang->category = 'Categoría';
$lang->document_srl = 'Doc.Num';
$lang->user_id = 'Usuario';
$lang->author = 'Autor';
$lang->password = 'Contraseña';
$lang->password1 = 'Contraseña';
$lang->password2 = 'Confirmar la Contraseña';
$lang->admin_id = 'Identificación Del Admin';
$lang->writer = 'Escritor';
$lang->user_name = 'Nombre Del Usuario';
$lang->nick_name = 'Nombre De la Mella';
$lang->email_address = 'Correo Electrónico';
$lang->homepage = 'Página web';
$lang->blog = 'Blog';
$lang->birthday = 'Fecha de Nacimiento';
$lang->browser_title = 'Título del navegador';
$lang->title = 'Título';
$lang->title_content = 'Título+Contenido';
$lang->content = 'Contenido';
$lang->document = 'Entrada';
$lang->comment = 'Commentario';
$lang->description = 'Descripción';
$lang->trackback = 'Trackback';
$lang->tag = 'Etiqueta';
$lang->allow_comment = 'Permitir Commentarios';
$lang->lock_comment = 'Cerrar Commentarios';
$lang->allow_trackback = 'Permitir Trackback';
$lang->uploaded_file = 'Ajuntos';
$lang->grant = 'Autoridad';
$lang->target = 'Objetivo';
$lang->total = 'Total';
$lang->total_count = 'Visitos en Total';
$lang->ipaddress = 'Dirección IP';
$lang->path = 'Paso';
$lang->cart = 'Artículo Selecciónado';
$lang->friend = 'Amigos';
$lang->notify = 'Notify';
$lang->mid = 'Module Name';
$lang->layout = 'Layout';
$lang->widget = 'Widget';
$lang->module = 'Módulo';
$lang->skin = 'Tema';
$lang->colorset = 'Colección de colores';
$lang->extra_vars = 'Variables Extra';
$lang->document_url = 'Entrada URL';
$lang->trackback_url = 'Trackback URL';
$lang->blog_name = 'Titulo del Blog';
$lang->excerpt = 'Quotación';
$lang->document_count = 'Numeros de la Entradas';
$lang->page_count = 'Cuentas de las Pagínas';
$lang->list_count = 'Cuentas del Listos';
$lang->readed_count = 'Cuentas Referidos';
$lang->voted_count = 'Cuentas Votadas';
$lang->member_count = 'Cuentas de Miembros';
$lang->date = 'Fecha';
$lang->regdate = 'Fecha Publicado';
$lang->last_update = 'Última actualización';
$lang->signup_date = 'Fecha Inscribido';
$lang->last_login = 'Último Entrada';
$lang->first_page = 'Primera Pagína';
$lang->last_page = 'Última Pagína';
$lang->search_target = 'Buscando Objectivo';
$lang->search_keyword = 'Buscando Palabra Clave';
$lang->is_default = 'Prefijado';
$lang->no_documents = 'No Entradas!';
$lang->board_manager = 'Configuración Tablón';
$lang->member_manager = 'Configuración Miembros';
$lang->layout_manager = 'Configuración Diseño';
$lang->use = 'Usar';
$lang->notuse = 'Desusar';
$lang->not_exists = 'No existe';
$lang->unit_sec = 'sec';
$lang->unit_min = 'min';
$lang->unit_hour = 'hr';
$lang->unit_day = 'dia';
$lang->unit_week = 'semana';
$lang->unit_month = 'mes';
$lang->unit_year = 'año';
// Related to Description
$lang->about_tag = 'cuando mecanografiar las etiquetas, insertando un comma() entre cada etiqueta permite que usted entre las etiquetas múltiples';
$lang->about_layout = 'disposición adorna un aspecto de su módulo que usted podría configurarlo de menú de la disposición en la tapa';
// Related to Message
$lang->msg_call_server = 'Solicitando al servidor, espere por favor';
$lang->msg_db_not_setted = 'No tiene la Configuración del DB';
$lang->msg_invalid_queryid = 'El valor de Query ID especificado mal';
$lang->msg_not_permitted = 'Usted no tiene permiso';
$lang->msg_input_password = 'Por favor escribe sus contraseña';
$lang->msg_invalid_document = 'Numero de entrada invalido';
$lang->msg_invalid_request = 'Requesta Invalido';
$lang->msg_invalid_password = 'Contraseñn Invalido';
$lang->msg_error_occured = 'Occurrido Errores';
$lang->msg_not_founded = 'Objetivo no encontrado';
$lang->msg_no_result = 'Esta búsqueda no hay resultado';
$lang->msg_not_permitted_act = 'Usted no tiene permiso para ejecutar la action';
$lang->msg_module_is_not_exists = 'No podía encontrar el modulo que usted solicitado';
$lang->msg_module_is_not_standalone = 'El modulo usted solicitado no puede ejecutado independientemente';
$lang->success_registed = 'Registrado con Éxito';
$lang->success_updated = 'Actualizado con Éxito';
$lang->success_deleted = 'Boarrado con Éxito';
$lang->success_voted = 'Votado con Éxito';
$lang->success_moved = 'Movido con Éxito';
$lang->success_sended = 'Enviado con Éxito';
$lang->success_reset = 'Reajustado con Éxito';
$lang->success_leaved = 'Abandonó con Éxito';
$lang->fail_to_delete = 'Supresión Fallada';
$lang->fail_to_move = 'Mover Fallada';
$lang->failed_voted = 'No podía ser votado';
$lang->fail_to_delete_have_children = 'Supresión Fallda. Hay unas o más contestaciones';
$lang->confirm_submit = 'Confirmar para someter?';
$lang->confirm_logout = 'Confirmar para salir?';
$lang->confirm_vote = 'Confirmar para voto?';
$lang->confirm_delete = 'Confirmar de supresión?';
$lang->confirm_move = 'Confirmar para Movido?';
$lang->confirm_reset = 'Confirmar para reajustar?';
$lang->confirm_leave = 'Confirmar para Éxito?';
$lang->column_type = 'Tipo de la Columna';
$lang->column_type_list['text'] = 'Texto una linea';
$lang->column_type_list['homepage'] = 'url';
$lang->column_type_list['email_address'] = 'Correo Electrónico';
$lang->column_type_list['tel'] = 'Numero de fono';
$lang->column_type_list['textarea'] = 'Texto multi-linea';
$lang->column_type_list['checkbox'] = 'checkbox (selecciónes multiples)';
$lang->column_type_list['select'] = 'select box (selección singlar)';
//$lang->column_type_list['kr_zip'] = 'zip code (Korean)';
$lang->column_type_list['date'] = 'fecha (dd/mm/aaaa)';
//$lang->column_type_list['jp_zip'] = 'zip code (Japanese)';
$lang->column_name = 'Nombre de la Columna';
$lang->column_title = 'Título de la Columna';
$lang->default_value = 'Valor Prefijado';
$lang->is_active = 'Activado';
$lang->is_required = 'Entrada Necesario';
// Alert messages for Javascript using by XML filter
$lang->filter->isnull = 'Entrar un valor para %s';
$lang->filter->outofrange = 'Alinear el lenghth del texto de %s';
$lang->filter->equalto = 'Valor inválido asignado para %s';
$lang->filter->invalid_email = '%s es formateo inválido (eg. zbxe@zeroboard.com)';
$lang->filter->invalid_user_id = $lang->filter->invalid_userid = "%s es formeto inválido.\\nTomas letras, números, y el underbar(ingleses _) de cuál debe comenzar con las letras Alfabeto";
$lang->filter->invalid_homepage = '%s es formateo inválido. (?: http://www.zeroboard.com)';
$lang->filter->invalid_korean = '%s got an invalid format. Solo aceptan las letras Coreanas';
$lang->filter->invalid_korean_number = '%s es formateo inválido. Solamente combinación de letras y de números coreanos se acepta';
$lang->filter->invalid_alpha = '%s es formateo inválido. Solamente se aceptan las alfabeto';
$lang->filter->invalid_alpha_number = '%s es formateo inválido. Solamente se aceptan las alfabeto y números';
$lang->filter->invalid_number = '%s es formateo inválido. Solo aceptan los números.';
?>

216
common/lang/fr.lang.php Normal file
View file

@ -0,0 +1,216 @@
<?php
/**
* @file common/lang/fr.lang.php
* @author zero (zero@nzeo.com)
* @brief Paquet Francais de Langue (mots de base seulement)
**/
// verbes et mots de base decrivant des actions
$lang->cmd_write = 'Ecrire';
$lang->cmd_reply = 'Répondre';
$lang->cmd_delete = 'Supprimer';
$lang->cmd_modify = 'Modifier';
$lang->cmd_edit = 'Editer';
$lang->cmd_view = 'Regarder';
$lang->cmd_view_all = 'Regarder Tous';
$lang->cmd_list = 'Liste';
$lang->cmd_prev = 'Précédent ';
$lang->cmd_next = 'Après';
$lang->cmd_send_trackback = 'Envoyer Trackback';
$lang->cmd_registration = $lang->cmd_submit = 'Soumettre';
$lang->cmd_comment_registration = 'Ajouter le Commentaire';
$lang->cmd_insert = 'Insérer';
$lang->cmd_save = 'Economiser';
$lang->cmd_input = 'Entrer';
$lang->cmd_search = 'Rechercher';
$lang->cmd_cancel = 'Decommander';
$lang->cmd_back = 'Retour';
$lang->cmd_vote = 'Voter';
$lang->cmd_move = 'Deplacer';
$lang->cmd_move_up = 'Relever';
$lang->cmd_move_down = 'Abaisser';
$lang->cmd_add_indent = 'Ajouter le Creux';
$lang->cmd_remove_indent = 'Enlever le Creux';
$lang->cmd_management = 'Gestion';
$lang->cmd_make = 'Creer';
$lang->cmd_select = 'Choisir';
$lang->cmd_select_all = 'Choisir Tous';
$lang->cmd_unselect_all = 'Ne pas Selectionner Tous';
$lang->cmd_close_all = 'Cloturer Tous';
$lang->cmd_open_all = 'Ouvrir Tous';
$lang->cmd_reload = 'Recharger';
$lang->cmd_close = 'Fermer';
$lang->cmd_open = 'Cloturer';
$lang->cmd_setup = 'Configurer';
$lang->cmd_option = 'Option';
$lang->cmd_apply = 'Appliquer';
$lang->cmd_open_calendar = 'Choisir la Date';
$lang->cmd_send = 'Envoyer';
$lang->cmd_print = 'Imprimer';
$lang->enable = 'Permettre';
$lang->disable = 'Neutraliser';
// Mots essentiels
$lang->no = 'numéro';
$lang->notice = 'Notification';
$lang->secret = 'Secret';
$lang->category = 'Categorie';
$lang->document_srl = 'numéro du Document';
$lang->user_id = 'Pseudo';
$lang->author = 'Auteur';
$lang->password = 'Mot de Passe';
$lang->password1 = 'Mot de Passe';
$lang->password2 = 'Confirmer le Mot de Passe';
$lang->admin_id = 'Pseudo d\'Administrateur';
$lang->writer = 'Auteur';
$lang->user_name = 'Nom d\'utilisateur';
$lang->nick_name = 'Surnom';
$lang->email_address = 'E-mail';
$lang->homepage = 'Site Web';
$lang->blog = 'Blog';
$lang->birthday = 'Anniversaire';
$lang->browser_title = 'Titre de Navigateur';
$lang->title = 'Titre';
$lang->title_content = 'Titre + Contenu';
$lang->content = 'Contenu';
$lang->document = 'Article';
$lang->comment = 'Commentaire';
$lang->description = 'Description';
$lang->trackback = 'Trackback';
$lang->tag = 'Tag';
$lang->allow_comment = 'Permettre les Commentaires';
$lang->lock_comment = 'Rejeter les Commentaires';
$lang->allow_trackback = 'Permettre la Trackback';
$lang->uploaded_file = 'Dossier Téléchargé';
$lang->grant = 'Autorité';
$lang->target = 'Cible';
$lang->total = 'Total';
$lang->total_count = 'Comptage Total';
$lang->ipaddress = 'IP Address';
$lang->path = 'Chemin';
$lang->cart = 'Article Choisi';
$lang->friend = 'Les Amis';
$lang->mid = 'Titre de Module';
$lang->layout = 'Disposition';
$lang->widget = 'Widget';
$lang->module = 'Module';
$lang->skin = 'Thème';
$lang->colorset = 'Arrangement de couleur';
$lang->extra_vars = 'Variables Supplementaires';
$lang->document_url = 'URL du Document';
$lang->trackback_url = 'URL de la Trackback';
$lang->blog_name = 'Nom du Blog';
$lang->excerpt = 'Citation';
$lang->document_count = 'Nombre Total des Articles';
$lang->page_count = 'Nombre de Pages';
$lang->list_count = 'Nombre de Listes';
$lang->readed_count = 'Nombre de Fois Lues';
$lang->voted_count = 'Nombre de Voix';
$lang->member_count = 'Nombre de Membres';
$lang->date = 'Date';
$lang->regdate = 'Date d\'Enregistrement';
$lang->last_update = 'La Dernière Mise a Jour';
$lang->signup_date = 'Date d\'Enregistrement';
$lang->last_login = 'La Dernière Ouverture';
$lang->first_page = 'Première Page';
$lang->last_page = 'Dernière Page';
$lang->search_target = 'Cible de Recherche';
$lang->search_keyword = 'Mot-clé de Recherche';
$lang->is_default = 'défaut';
$lang->no_documents = 'Aucunes Entrées';
$lang->board_manager = 'Arrangements des Conseils';
$lang->member_manager = 'Arrangements des Membres';
$lang->layout_manager = 'Arrangements de Dispositions';
$lang->use = 'Utiliser';
$lang->notuse = 'Ne pas Utiliser';
$lang->not_exists = 'n\'Existe Pas';
$lang->unit_sec = 'sec';
$lang->unit_min = 'min';
$lang->unit_hour = 'heur';
$lang->unit_day = 'jour';
$lang->unit_week = 'semaine';
$lang->unit_month = 'mois';
$lang->unit_year = 'année';;
// Descriptions et information
$lang->about_tag = 'Quand l\'insertion des tags, insérent une virgule(,) entre chaque tags te permet d\'entrer les tags multiples.';
$lang->about_layout = 'es dispositions décorent l\'aspect d\'un module. Vous pouvez les configurer du menu de disposition ci-dessus.';
// Messages et alertes
$lang->msg_call_server = 'En demandant le serveur pour information, attendre svp.';
$lang->msg_db_not_setted = 'Les informations de DB n\'ont pas été placees.';
$lang->msg_invalid_queryid = 'La valeur indiquee d\'identification de question est inadmissi.';
$lang->msg_not_permitted = 'Vous n\'etes pas autorises a continuer.';
$lang->msg_input_password = 'Veuillez entrer votre mot de passe.';
$lang->msg_invalid_document = 'Vous avez demandé pour un article inadmissible.';
$lang->msg_invalid_request = 'Votre demande est inadmissible.';
$lang->msg_invalid_password = 'Vous avez entré un mot de passe inadmissible.';
$lang->msg_error_occured = 'Une erreur s\'est produite.';
$lang->msg_not_founded = 'La cible demandée n\'a pas pu être trouvée.';
$lang->msg_no_result = 'Aucun Resultat';
$lang->msg_not_permitted_act = 'Vous n\'êtes pas autorisés pour exécuter l\'action priée.';
$lang->msg_module_is_not_exists = 'Le module demandé n\'a pas pu être trouvé.';
$lang->msg_module_is_not_standalone = 'Le module demandé ne peut pas être exécuté indépendamment.';
$lang->success_registed = 'L\'enregistrement a été avec succès exécuté.';
$lang->success_updated = 'Avec succès mis a jour.';
$lang->success_deleted = 'Avec succès supprime';
$lang->success_voted = 'Avec succès voté';
$lang->success_moved = 'Avec succès deplacé';
$lang->success_sended = 'Avec succès envoyé';
$lang->success_reset = 'Avec succès remisé';
$lang->success_leaved = 'Avec succès fait cécession';
$lang->fail_to_delete = 'N\'a pas pu être supprimé.';
$lang->fail_to_move = 'N\'a pas pu être déplacé';
$lang->failed_voted = 'La demande de voter a échoué';
$lang->fail_to_delete_have_children = 'Une ou plusieurs reponses existent et ne pourraient pas être supprimees ainsi';
$lang->confirm_submit = 'Etes-vous sur vous voulez-vous soumettre?';
$lang->confirm_logout = 'Etes-vous sur vous voulez-vous se deconnecter?';
$lang->confirm_vote = 'Etes-vous sur vous voulez-vous voter?';
$lang->confirm_delete = 'Etes-vous sur vous voulez-vous supprimer?';
$lang->confirm_move = 'Etes-vous sur vous voulez-vous deplacer ceci?';
$lang->confirm_reset = 'Etes-vous sur vous voulez-vous remettre a zero ceci?';
$lang->confirm_leave = 'Etes-vous sur vous voulez-vous partir?';
$lang->column_type = 'Type de Colonne';
$lang->column_type_list['text'] = 'un-ligne texte';
$lang->column_type_list['homepage'] = 'url';
$lang->column_type_list['email_address'] = 'e-mail';
$lang->column_type_list['tel'] = 'numéro de telephone';
$lang->column_type_list['textarea'] = 'secteur multiligne des textes';
$lang->column_type_list['checkbox'] = 'boite de controle (choix multiple)';
$lang->column_type_list['select'] = 'boite choisie (choix simple)';
$lang->column_type_list['kr_zip'] = 'code postal (coréen)';
$lang->column_type_list['date'] = 'date (yyyy/mm/dd)';
//$lang->column_type_list['jp_zip'] = 'code postal (japonais)';
$lang->column_name = 'Nom de colonne';
$lang->column_title = 'Titre de colonne';
$lang->default_value = 'Valeur par défaut';
$lang->is_active = 'Actif';
$lang->is_required = 'Champ requis';
// Messages alertes pour le Javascript a l'aide des filtres de XML
$lang->filter->isnull = 'Entrer une valeur pour %s';
$lang->filter->outofrange = 'Aligner la longueur des textes de %s';
$lang->filter->equalto = "La valeur de %s est inadmissible.";
$lang->filter->invalid_email = "La format de %s est inadmissible. ex) zbxe@zeroboard.com";
$lang->filter->invalid_user_id = $lang->filter->invalid_userid = "La format de %s est inadmissible.\\nToutes les valeurs devraient se composer des alphabets, des nombres ou du soulignage (_) avec la premiere lettre etant un caractere de l'alphabet.";
$lang->filter->invalid_homepage = "La format de %s est inadmissible. ex) http://www.zeroboard.com";
$lang->filter->invalid_korean = "La format de %s est inadmissible. On permet seulement le coréen";
$lang->filter->invalid_korean_number = "La format de %s est inadmissible. Svp caracteres ou nombres coréens d'entrée seulement.";
$lang->filter->invalid_alpha = "La format de %s est inadmissible. Svp alphabets d'entrée seulement.";
$lang->filter->invalid_alpha_number = "La format de %s est inadmissible. Svp alphabets ou nombres d'entrée seulement.";
$lang->filter->invalid_number = "La format de %s est inadmissible. Svp l'entrée numérote seulement.";
?>

221
common/lang/jp.lang.php Normal file
View file

@ -0,0 +1,221 @@
<?php
/**
* @file common/lang/jp.lang.php
* @author zero (zero@nzeo.com) 翻訳RisaPapa
* @brief 日本語言語パッケージ(基本的な実行関連の用語がまとめられている)
**/
// 基本的な実行に用いる用語
$lang->cmd_write = '書き込む';
$lang->cmd_reply = '返信';
$lang->cmd_delete = '削除';
$lang->cmd_modify = '修正';
$lang->cmd_edit = '編集';
$lang->cmd_view = '表示';
$lang->cmd_view_all = 'すべて表示';
$lang->cmd_list = 'リスト';
$lang->cmd_prev = '前へ';
$lang->cmd_next = '次へ';
$lang->cmd_send_trackback = 'TB送信';
$lang->cmd_registration = $lang->cmd_submit = '登録';
$lang->cmd_comment_registration = 'コメント登録';
$lang->cmd_insert = '挿入';
$lang->cmd_save = '保存';
$lang->cmd_input = '入力';
$lang->cmd_search = '検索';
$lang->cmd_cancel = 'キャンセル';
$lang->cmd_back = '戻る';
$lang->cmd_vote = '推薦';
$lang->cmd_move = '移動';
$lang->cmd_move_up = '上へ';
$lang->cmd_move_down = '下へ';
$lang->cmd_add_indent = '字下げ';
$lang->cmd_remove_indent = '字下げ解除';
$lang->cmd_management = '管理';
$lang->cmd_make = '作成';
$lang->cmd_select = '選択';
$lang->cmd_select_all = 'すべて選択';
$lang->cmd_unselect_all = 'すべて解除';
$lang->cmd_close_all = 'すべて閉じる';
$lang->cmd_open_all = 'すべて開く';
$lang->cmd_reload = 'リロード';
$lang->cmd_close = '閉じる';
$lang->cmd_open = '開く';
$lang->cmd_setup = '設定';
$lang->cmd_apply = '適用';
$lang->cmd_option = 'オプション';
$lang->cmd_open_calendar = 'カレンダーを開く';
$lang->cmd_send = '送信';
$lang->cmd_print = '印刷';
$lang->cmd_scrap = 'スクラップ';
$lang->cmd_preview = 'プレビュー';
$lang->cmd_reset = '初期化';
$lang->cmd_remake_cache = "キャッシュファイル再生成";
$lang->enable = '可能';
$lang->disable = '不可';
// 基本用語
$lang->no = '番号';
$lang->notice = 'お知らせ';
$lang->secret = '非公開';
$lang->category = 'カテゴリ';
$lang->document_srl = '書き込み番号';
$lang->user_id = 'ユーザID';
$lang->author = '作者';
$lang->password = 'パスワード';
$lang->password1 = 'パスワード';
$lang->password2 = 'パスワード確認';
$lang->admin_id = '管理者ID';
$lang->writer = '投稿者';
$lang->user_name = '名前';
$lang->nick_name = 'ニックネーム';
$lang->email_address = 'メールアドレス';
$lang->homepage = 'ホームページ';
$lang->blog = 'ブログ';
$lang->birthday = '誕生日';
$lang->browser_title = 'ブラウザタイトル';
$lang->title = 'タイトル';
$lang->title_content = 'タイトル+内容';
$lang->content = '内容';
$lang->document = '書き込み';
$lang->comment = 'コメント';
$lang->description = '説明';
$lang->trackback = 'トラックバック';
$lang->tag = 'タグ';
$lang->allow_comment = 'コメント許可';
$lang->lock_comment = 'コメントロック';
$lang->allow_trackback = 'トラックバック許可';
$lang->uploaded_file = '添付';
$lang->grant = '権限';
$lang->target = '対象';
$lang->total = 'すべて';
$lang->total_count = 'トータル数';
$lang->ipaddress = 'IPアドレス';
$lang->path = 'パス';
$lang->cart = '選択項目';
$lang->friend = '友達';
$lang->notify = 'お知らせ';
$lang->mid = 'モジュール名';
$lang->layout = 'レイアウト';
$lang->widget = 'ウィジェット';
$lang->module = 'モジュール';
$lang->skin = 'スキン';
$lang->colorset = 'カラーセット';
$lang->extra_vars = '拡張変数';
$lang->document_url = '書き込みURL';
$lang->trackback_url = 'トラックバックURL';
$lang->blog_name = 'ブログ名';
$lang->excerpt = '要約';
$lang->document_count = '書き込み数';
$lang->page_count = 'ページ数';
$lang->list_count = 'リスト数';
$lang->readed_count = '照合数';
$lang->voted_count = '推薦数';
$lang->member_count = '会員数';
$lang->date = '年月日';
$lang->regdate = '登録日';
$lang->last_update = '最近修正日';
$lang->signup_date = '加入日';
$lang->last_login = '最近ログイン';
$lang->first_page = '最初のページ';
$lang->last_page = '最後のページ';
$lang->search_target = '検索対象';
$lang->search_keyword = 'キーワード';
$lang->is_default = 'デフォルト';
$lang->no_documents = '書き込みがありません。';
$lang->board_manager = '掲示板管理';
$lang->member_manager = '会員管理';
$lang->layout_manager = 'レイアウト';
$lang->use = '使用';
$lang->notuse = '未使用';
$lang->not_exists = 'なし';
$lang->unit_sec = '秒';
$lang->unit_min = '分';
$lang->unit_hour = '時';
$lang->unit_day = '日';
$lang->unit_week = '週';
$lang->unit_month = '月';
$lang->unit_year = '年';
// 説明関連
$lang->about_tag = 'タグを入力する時、「,」を使うと複数登録できます。';
$lang->about_layout = 'レイアウトでモジュールの枠をデザインします。上のレイアウトメニューで管理できます。';
// メッセージ関連
$lang->msg_call_server = 'サーバへ問合わせ中です。しばらくお待ちください。';
$lang->msg_db_not_setted = 'DBが設定されていません。';
$lang->msg_invalid_queryid = 'クエリIDの値が無効です。';
$lang->msg_not_permitted = '権限がありません。';
$lang->msg_input_password = 'パスワードを入力してください。';
$lang->msg_invalid_document = '無効な書き込み番号です。';
$lang->msg_invalid_request = '無効なリクエストです。';
$lang->msg_invalid_password = 'パスワードが正しくありません。';
$lang->msg_error_occured = 'エラーが発生しました。';
$lang->msg_not_founded = '見つかりません。';
$lang->msg_no_result = '検索結果がありません。';
$lang->msg_not_permitted_act = '現在の操作は実行する権限がありません。';
$lang->msg_module_is_not_exists = 'モジュールが見つかりません。';
$lang->msg_module_is_not_standalone = 'このモジュールはスタンドアローンでは作動しません。';
$lang->success_registed = '登録しました。';
$lang->success_updated = '修正しました。';
$lang->success_deleted = '削除しました。';
$lang->success_voted = '推薦しました。';
$lang->success_moved = '移動しました。';
$lang->success_sended = '送信しました。';
$lang->success_reset = '初期化しました。';
$lang->success_leaved = '脱会しました。';
$lang->fail_to_delete = '削除に失敗しました。';
$lang->fail_to_move = '移動に失敗しました。';
$lang->failed_voted = '推薦できません。';
$lang->fail_to_delete_have_children = '返信の書き込みがあり、削除できません。';
$lang->confirm_submit = '登録しますか?';
$lang->confirm_logout = 'ログアウトしますか?';
$lang->confirm_vote = '推薦しますか?';
$lang->confirm_delete = '削除しますか?';
$lang->confirm_move = '移動しますか?';
$lang->confirm_reset = '初期化しますか?';
$lang->confirm_leave = '脱会しますか?';
$lang->column_type = 'タイプ';
$lang->column_type_list['text'] = '入力フィールド(text)';
$lang->column_type_list['homepage'] = 'ホームページタイプ(url)';
$lang->column_type_list['email_address'] = 'メールタイプ(email)';
$lang->column_type_list['tel'] = '電話番号タイプ(phone)';
$lang->column_type_list['textarea'] = 'テキストエリア(textarea)';
$lang->column_type_list['checkbox'] = 'チェックボックス(checkbox)';
$lang->column_type_list['select'] = '選択(select)';
$lang->column_type_list['kr_zip'] = '韓国住所(zip)';
$lang->column_type_list['date'] = '年月日';
//$lang->column_type_list['jp_zip'] = '日本住所(zip)';
$lang->column_name = 'コラム名';
$lang->column_title = 'コラムタイトル';
$lang->default_value = 'デフォルト値';
$lang->is_active = '活性化';
$lang->is_required = '必須項目';
// xml filterで用いられているjavascript用のアラートメッセージ
$lang->filter->isnull = '%sを入力してください';
$lang->filter->outofrange = '%sの文字の長さを合わせてください';
$lang->filter->equalto = '%sが正しくありません';
$lang->filter->invalid_email = '%sのパターンが正しくありません (例: zbxe@zeroboard.com)';
$lang->filter->invalid_user_id = $lang->filter->invalid_userid = "%sの形式が正しくありません。\\n半角の英数と記号「_」を組み合わせて入力してください。最初の文字は半角英数でなければなりません。";
$lang->filter->invalid_homepage = '%sの形式が正しくありません (例: http://www.zeroboard.com)';
$lang->filter->invalid_korean = '%sの形式が正しくありません。ハングルのみ入力してください。';
$lang->filter->invalid_korean_number = '%sの形式が正しくありません。ハングルと半角数字で入力してください。';
$lang->filter->invalid_alpha = '%sの形式が正しくありません。半角英文字で入力してください。';
$lang->filter->invalid_alpha_number = '%sの形式が正しくありません。半角英数で入力してください。';
$lang->filter->invalid_number = '%sの形式が正しくありません。半角数字で入力してください。';
?>

221
common/lang/ko.lang.php Normal file
View file

@ -0,0 +1,221 @@
<?php
/**
* @file common/lang/ko.lang.php
* @author zero (zero@nzeo.com)
* @brief 한국어 언어팩 (기본적인 내용만 수록)
**/
// 기본적으로 사용되는 action 언어
$lang->cmd_write = '쓰기';
$lang->cmd_reply = '답글';
$lang->cmd_delete = '삭제';
$lang->cmd_modify = '수정';
$lang->cmd_edit = '편집';
$lang->cmd_view = '보기';
$lang->cmd_view_all = '전체 보기';
$lang->cmd_list = '목록';
$lang->cmd_prev = '이전';
$lang->cmd_next = '다음';
$lang->cmd_send_trackback = '엮인글발송';
$lang->cmd_registration = $lang->cmd_submit = '등록';
$lang->cmd_comment_registration = '댓글 등록';
$lang->cmd_insert = '추가';
$lang->cmd_save = '저장';
$lang->cmd_input = '입력';
$lang->cmd_search = '검색';
$lang->cmd_cancel = '취소';
$lang->cmd_back = '돌아가기';
$lang->cmd_vote = '추천';
$lang->cmd_move = '이동';
$lang->cmd_move_up = '위로';
$lang->cmd_move_down = '아래로';
$lang->cmd_add_indent = '들이기';
$lang->cmd_remove_indent = '빼내기';
$lang->cmd_management = '관리';
$lang->cmd_make = '생성';
$lang->cmd_select = '선택';
$lang->cmd_select_all = '모두선택';
$lang->cmd_unselect_all = '모두해제';
$lang->cmd_close_all = '모두닫기';
$lang->cmd_open_all = '모두열기';
$lang->cmd_reload = '다시읽기';
$lang->cmd_close = '닫기';
$lang->cmd_open = '열기';
$lang->cmd_setup = '설정';
$lang->cmd_option = '옵션';
$lang->cmd_apply = '적용';
$lang->cmd_open_calendar = '날짜 선택';
$lang->cmd_send = '발송';
$lang->cmd_print = '인쇄';
$lang->cmd_scrap = '스크랩';
$lang->cmd_preview = '미리 보기';
$lang->cmd_reset = '초기화';
$lang->cmd_remake_cache = "캐시파일 재생성";
$lang->enable = '가능';
$lang->disable = '불가능';
// 기본 단어
$lang->no = '번호';
$lang->notice = '공지';
$lang->secret = '비밀';
$lang->category = '분류';
$lang->document_srl = '문서번호';
$lang->user_id = '아이디';
$lang->author = '작성자';
$lang->password = '비밀번호';
$lang->password1 = '비밀번호';
$lang->password2 = '비밀번호 확인';
$lang->admin_id = '관리자ID';
$lang->writer = '글쓴이';
$lang->user_name = '이름';
$lang->nick_name = '닉네임';
$lang->email_address = '이메일 주소';
$lang->homepage = '홈페이지';
$lang->blog = '블로그';
$lang->birthday = '생일';
$lang->browser_title = '브라우저 제목';
$lang->title = '제목';
$lang->title_content = '제목+내용';
$lang->content = '내용';
$lang->document = '게시물';
$lang->comment = '댓글';
$lang->description = '설명';
$lang->trackback = '엮인글';
$lang->tag = '태그';
$lang->allow_comment = '댓글허용';
$lang->lock_comment = '댓글잠금';
$lang->allow_trackback = '엮인글허용';
$lang->uploaded_file = '첨부';
$lang->grant = '권한';
$lang->target = '대상';
$lang->total = '전체';
$lang->total_count = '전체개수';
$lang->ipaddress = 'IP 주소';
$lang->path = '경로';
$lang->cart = '선택항목';
$lang->friend = '친구';
$lang->notify = '알림';
$lang->mid = '모듈이름';
$lang->layout = '레이아웃';
$lang->widget = '위젯 ';
$lang->module = '모듈';
$lang->skin = '스킨';
$lang->colorset = '컬러셋';
$lang->extra_vars = '확장변수';
$lang->document_url = '게시글 주소';
$lang->trackback_url = '엮인글 주소';
$lang->blog_name = '블로그이름';
$lang->excerpt = '발췌';
$lang->document_count = '글수';
$lang->page_count = '페이지수';
$lang->list_count = '목록 수';
$lang->readed_count = '조회수';
$lang->voted_count = '추천수';
$lang->member_count = '회원수';
$lang->date = '날짜';
$lang->regdate = '등록일';
$lang->last_update = '최근수정일';
$lang->signup_date = '가입일';
$lang->last_login = '최근로그인';
$lang->first_page = '첫페이지';
$lang->last_page = '끝페이지';
$lang->search_target = '검색대상';
$lang->search_keyword = '검색어';
$lang->is_default = '기본';
$lang->no_documents = '등록된 글이 없습니다';
$lang->board_manager = '게시판 관리';
$lang->member_manager = '회원 관리';
$lang->layout_manager = '레이아웃 관리';
$lang->use = '사용';
$lang->notuse = '미사용';
$lang->not_exists = '없음';
$lang->unit_sec = '초';
$lang->unit_min = '분';
$lang->unit_hour = '시';
$lang->unit_day = '일';
$lang->unit_week = '주';
$lang->unit_month = '월';
$lang->unit_year = '년';
// 설명 관련
$lang->about_tag = '태그 입력시 , (쉼표)를 이용하시면 복수 등록이 가능합니다';
$lang->about_layout = '레이아웃은 모듈의 껍데기를 꾸며줍니다. 상단 레이아웃 메뉴에서 관리하실 수 있습니다';
// 메세지 관련
$lang->msg_call_server = '서버에 요청중입니다. 잠시만 기다려주세요.';
$lang->msg_db_not_setted = 'DB설정이 되어 있지 않습니다';
$lang->msg_invalid_queryid = 'Query ID값이 잘못 지정되었습니다';
$lang->msg_not_permitted = '권한이 없습니다';
$lang->msg_input_password = '비밀번호를 입력하여 주세요';
$lang->msg_invalid_document = '잘못된 문서번호입니다';
$lang->msg_invalid_request = '잘못된 요청입니다';
$lang->msg_invalid_password = '비밀번호가 올바르지 않습니다';
$lang->msg_error_occured = '오류가 발생하였습니다';
$lang->msg_not_founded = '대상을 찾을 수 없습니다';
$lang->msg_no_result = '검색 결과가 없습니다';
$lang->msg_not_permitted_act = '요청하신 action을 실행할 수 있는 권한이 없습니다';
$lang->msg_module_is_not_exists = '요청하신 모듈을 찾을 수 없습니다';
$lang->msg_module_is_not_standalone = '요청하신 모듈은 독립적으로 동작할 수가 없습니다';
$lang->success_registed = '등록되었습니다';
$lang->success_updated = '수정되었습니다';
$lang->success_deleted = '삭제되었습니다';
$lang->success_voted = '추천되었습니다';
$lang->success_moved = '이동되었습니다';
$lang->success_sended = '발송되었습니다';
$lang->success_reset = '초기화되었습니다';
$lang->success_leaved = '탈퇴되었습니다';
$lang->fail_to_delete = '삭제되었습니다';
$lang->fail_to_move = '이동되었습니다';
$lang->failed_voted = '추천하실 수 없습니다';
$lang->fail_to_delete_have_children = '답글이 있어서 삭제할 수 없습니다';
$lang->confirm_submit = '등록하시겠습니까?';
$lang->confirm_logout = '로그아웃하시겠습니까?';
$lang->confirm_vote = '추천하시겠습니까?';
$lang->confirm_delete = '삭제하시겠습니까?';
$lang->confirm_move = '이동하시겠습니까?';
$lang->confirm_reset = '초기화 하시겠습니까??';
$lang->confirm_leave = '탈퇴 하시겠습니까??';
$lang->column_type = '형식';
$lang->column_type_list['text'] = '한줄 입력칸 (text)';
$lang->column_type_list['homepage'] = '홈페이지 형식 (url)';
$lang->column_type_list['email_address'] = '이메일 형식 (email)';
$lang->column_type_list['tel'] = '전화번호 형식 (phone)';
$lang->column_type_list['textarea'] = '여러줄 입력칸 (textarea)';
$lang->column_type_list['checkbox'] = '다중 선택 (checkbox)';
$lang->column_type_list['select'] = '단일 선택 (select)';
$lang->column_type_list['kr_zip'] = '한국주소 (zip)';
$lang->column_type_list['date'] = '일자 (년월일)';
//$lang->column_type_list['jp_zip'] = '일본주소 (zip)';
$lang->column_name = '입력항목 이름';
$lang->column_title = '입력항목 제목';
$lang->default_value = '기본 값';
$lang->is_active = '활성';
$lang->is_required = '필수항목';
// xml filter에서 사용되는 javascript용 alert msg
$lang->filter->isnull = '%s의 값을 입력해주세요';
$lang->filter->outofrange = '%s의 글자 길이를 맞추어 주세요.';
$lang->filter->equalto = '%s의 값이 잘못 되었습니다.';
$lang->filter->invalid_email = '%s의 형식이 잘못되었습니다. (예: zbxe@zeroboard.com)';
$lang->filter->invalid_user_id = $lang->filter->invalid_userid = "%s의 형식이 잘못되었습니다.\\n영문,숫자와 _로 만드실 수 있으며 제일 앞은 영문이어야 합니다";
$lang->filter->invalid_homepage = '%s의 형식이 잘못되었습니다. (예: http://www.zeroboard.com)';
$lang->filter->invalid_korean = '%s의 형식이 잘못되었습니다. 한글로만 입력해주셔야 합니다';
$lang->filter->invalid_korean_number = '%s의 형식이 잘못되었습니다. 한글과 숫자로만 입력해주셔야 합니다';
$lang->filter->invalid_alpha = '%s의 형식이 잘못되었습니다. 영문으로만 입력해주셔야 합니다';
$lang->filter->invalid_alpha_number = '%s의 형식이 잘못되었습니다. 영문과 숫자로만 입력해주셔야 합니다';
$lang->filter->invalid_number = '%s의 형식이 잘못되었습니다. 숫자로만 입력해주셔야 합니다';
?>

221
common/lang/zh-CN.lang.php Normal file
View file

@ -0,0 +1,221 @@
<?php
/**
* @file common/lang/zh-CN.lang.php
* @author zero (zero@nzeo.com)
* @brief 简体中文语言包 (只收录基本内容)
**/
// 基本使用的 action 语言
$lang->cmd_write = '发表新帖';
$lang->cmd_reply = '回复';
$lang->cmd_delete = '删除';
$lang->cmd_modify = '修改';
$lang->cmd_edit = '编辑';
$lang->cmd_view = '查看';
$lang->cmd_view_all = '全部查看';
$lang->cmd_list = '目录';
$lang->cmd_prev = '上一页';
$lang->cmd_next = '下一页';
$lang->cmd_send_trackback = '发送引用';
$lang->cmd_registration = $lang->cmd_submit = '提交';
$lang->cmd_comment_registration = '提交评论';
$lang->cmd_insert = '添加';
$lang->cmd_save = '保存';
$lang->cmd_input = '输入';
$lang->cmd_search = '搜索';
$lang->cmd_cancel = '取消';
$lang->cmd_back = '返回';
$lang->cmd_vote = '推荐';
$lang->cmd_move = '查看';
$lang->cmd_move_up = '向上';
$lang->cmd_move_down = '向下';
$lang->cmd_add_indent = '拉长';
$lang->cmd_remove_indent = '缩短';
$lang->cmd_management = '管理';
$lang->cmd_make = '生成';
$lang->cmd_select = '选择';
$lang->cmd_select_all = '全部选择';
$lang->cmd_unselect_all = '全部解除';
$lang->cmd_close_all = '全部关闭';
$lang->cmd_open_all = '全部打开';
$lang->cmd_reload = '从新载入';
$lang->cmd_close = '关闭';
$lang->cmd_open = '打开';
$lang->cmd_setup = '设置';
$lang->cmd_option = '选项';
$lang->cmd_apply = '应用';
$lang->cmd_open_calendar = '选择日期';
$lang->cmd_send = '发送';
$lang->cmd_print = '打印';
$lang->cmd_scrap = '收藏';
$lang->cmd_preview = '预览';
$lang->cmd_reset = '初始化';
$lang->cmd_remake_cache = "重新生成缓冲文件";
$lang->enable = '可用';
$lang->disable = '禁用';
// 基本词语
$lang->no = '编号';
$lang->notice = '公告';
$lang->secret = '密帖';
$lang->category = '分类';
$lang->document_srl = '文章编号';
$lang->user_id = '用户名';
$lang->author = '制作';
$lang->password = '密码';
$lang->password1 = '密码';
$lang->password2 = '确认密码';
$lang->admin_id = '管理员ID';
$lang->writer = '作者';
$lang->user_name = '姓名';
$lang->nick_name = '昵称';
$lang->email_address = '电子邮件';
$lang->homepage = '主页';
$lang->blog = '博客';
$lang->birthday = '生日';
$lang->browser_title = '浏览器标题';
$lang->title = '标题';
$lang->title_content = '标题+内容';
$lang->content = '内容';
$lang->document = '文章';
$lang->comment = '评论';
$lang->description = '说明';
$lang->trackback = '引用';
$lang->tag = '标签';
$lang->allow_comment = '允许评论';
$lang->lock_comment = '关闭评论';
$lang->allow_trackback = '允许引用';
$lang->uploaded_file = '附件';
$lang->grant = '权限';
$lang->target = '目标';
$lang->total = '全部';
$lang->total_count = '总数';
$lang->ipaddress = 'IP地址';
$lang->path = '路径';
$lang->cart = '选择项目';
$lang->friend = '好友';
$lang->notify = '通告';
$lang->mid = '模块名称';
$lang->layout = '布局';
$lang->widget = '控件 ';
$lang->module = '模块';
$lang->skin = '皮肤';
$lang->colorset = '颜色设定';
$lang->extra_vars = '扩展变数';
$lang->document_url = '文章地址';
$lang->trackback_url = '引用地址';
$lang->blog_name = '博客名称';
$lang->excerpt = '载自';
$lang->document_count = '帖子数';
$lang->page_count = '页数';
$lang->list_count = '目录数';
$lang->readed_count = '查看';
$lang->voted_count = '推荐';
$lang->member_count = '会员数';
$lang->date = '日期';
$lang->regdate = '登录日期';
$lang->last_update = '最后更新';
$lang->signup_date = '注册日期';
$lang->last_login = '最近登陆';
$lang->first_page = '第一页';
$lang->last_page = '最后一页';
$lang->search_target = '搜索目标';
$lang->search_keyword = '关键字';
$lang->is_default = '默认';
$lang->no_documents = '现没有主题。';
$lang->board_manager = '版面管理';
$lang->member_manager = '会员管理';
$lang->layout_manager = '布局管理';
$lang->use = '使用';
$lang->notuse = '未使用';
$lang->not_exists = '无';
$lang->unit_sec = '秒';
$lang->unit_min = '分';
$lang->unit_hour = '时';
$lang->unit_day = '日';
$lang->unit_week = '周';
$lang->unit_month = '月';
$lang->unit_year = '年';
// 说明
$lang->about_tag = '用逗号分隔多个标签';
$lang->about_layout = '布局是布置模块的外观,在上端布局菜里单可以进行管理';
// 信息
$lang->msg_call_server = '系统正在链接服务器,请稍后。';
$lang->msg_db_not_setted = '还没有设定 DB';
$lang->msg_invalid_queryid = 'Query ID值指定错误';
$lang->msg_not_permitted = '没有权限';
$lang->msg_input_password = '请输入密码';
$lang->msg_invalid_document = '出错的文章编号';
$lang->msg_invalid_request = '出错的请求';
$lang->msg_invalid_password = '密码错误';
$lang->msg_error_occured = '发生错误';
$lang->msg_not_founded = '没有找到相关内容';
$lang->msg_no_result = '找不到和您查询的相符结果';
$lang->msg_not_permitted_act = '没有权限执行 action命令';
$lang->msg_module_is_not_exists = '找不到您查询的模块';
$lang->msg_module_is_not_standalone = '您请求的模块不能单独执行';
$lang->success_registed = '提交成功!';
$lang->success_updated = '修改成功!';
$lang->success_deleted = '删除成功!';
$lang->success_voted = '推荐成功!';
$lang->success_moved = '移动成功!';
$lang->success_sended = '发送成功!';
$lang->success_reset = '初始化成功';
$lang->success_leaved = '注销成功!';
$lang->fail_to_delete = '删除成功!';
$lang->fail_to_move = '移动成功!';
$lang->failed_voted = '您不能推荐!';
$lang->fail_to_delete_have_children = '不能删除有回复的评论!';
$lang->confirm_submit = '确定要提交吗?';
$lang->confirm_logout = '确定要退出吗?';
$lang->confirm_vote = '确定要推荐吗?';
$lang->confirm_delete = '确定要删除吗?';
$lang->confirm_move = '确定要移动吗?';
$lang->confirm_reset = '确定要初始化吗?';
$lang->confirm_leave = '确定要注销吗?';
$lang->column_type = '格式';
$lang->column_type_list['text'] = '单行文本输入区(text)';
$lang->column_type_list['homepage'] = '网址格式 (url)';
$lang->column_type_list['email_address'] = '邮件格式 (email)';
$lang->column_type_list['tel'] = '电话号码格式 (phone)';
$lang->column_type_list['textarea'] = '多行文本框 (textarea)';
$lang->column_type_list['checkbox'] = '复选框 (checkbox)';
$lang->column_type_list['select'] = '下拉列表框 (select)';
$lang->column_type_list['kr_zip'] = '韩国地址 (zip)';
$lang->column_type_list['date'] = '日期 (年月日)';
//$lang->column_type_list['jp_zip'] = '日本地址 (zip)';
$lang->column_name = '项目名';
$lang->column_title = '项目标题';
$lang->default_value = '缺省值';
$lang->is_active = '激活';
$lang->is_required = '必填';
// 在xml filter使用的 javascript用 alert msg
$lang->filter->isnull = '请输入%s';
$lang->filter->outofrange = '请确认%s字数';
$lang->filter->equalto = '%s值有误。';
$lang->filter->invalid_email = '%s格式有误。zbxe@zeroboard.com)';
$lang->filter->invalid_user_id = $lang->filter->invalid_userid = "%s只能用英文数字和 _首个字符必须是英文字母。";
$lang->filter->invalid_homepage = '%s格式有误。 http://www.zeroboard.com)';
$lang->filter->invalid_korean = '%s只能输入中文';
$lang->filter->invalid_korean_number = '%s只能输入中文或数字';
$lang->filter->invalid_alpha = '%s只能输入英文字母';
$lang->filter->invalid_alpha_number = '%s只能输入英文或数字';
$lang->filter->invalid_number = '%s只能输入数字';
?>

189
common/tpl/calendar.php Normal file
View file

@ -0,0 +1,189 @@
<?php
$year = sprintf("%04d",$_REQUEST['year']);
$month = sprintf("%02d",$_REQUEST['month']);
$method = $_REQUEST['method'];
$fo_id = $_REQUEST['fo_id'];
$callback_func = $_REQUEST['callback_func'];
$day_str = $_REQUEST['day_str'];
if($day_str && strlen($day_str)) {
$year = substr($day_str,0,4);
$month = substr($day_str,4,2);
}
if(!(int)$year) $year = date("Y");
if(!(int)$month) $month = date("m");
switch($method) {
case 'prev_year' :
$year = date("Y", mktime(0,0,0,1,1,$year)-60*60*24);
break;
case 'prev_month' :
$month --;
if($month < 1) {
$month = 12;
$year --;
}
break;
case 'next_month' :
$month ++;
if($month > 12) {
$month = 1;
$year ++;
}
break;
case 'next_year' :
$year = date("Y", mktime(0,0,0,12,31,$year)+60*60*24);
break;
}
$start_week = date("w", mktime(0,0,0,$month,1,$year));
$month_day = date("t", mktime(0,0,0,$month,1,$year));
$before_month_month_day = date("t", mktime(0,0,0,$month,1,$year)-60*60*24);
$next_year = date("m", mktime(0,0,0,12,31, $year)+60*60*24);
$next_month = date("m", mktime(0,0,0,$month,$month_day, $year)+60*60*24);
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="ko" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="imagetoolbar" content="no" />
<title>Calendar</title>
<script type="text/javascript" src="../js/x.js"></script>
<script type="text/javascript" src="../js/common.js"></script>
<script type="text/javascript" src="../js/xml_handler.js"></script>
<link rel="stylesheet" href="../css/default.css" type="text/css" />
<link rel="stylesheet" href="../css/button.css" type="text/css" />
<link rel="stylesheet" href="../../modules/admin/tpl/css/admin.css" type="text/css" />
<link rel="stylesheet" href="./css/calendar.css" type="text/css" />
<script type="text/javascript">
function selectDate(date_str, date_val, callback_func) {
if(!opener) {
window.close();
return;
}
var date_obj = opener.xGetElementById("date_<?=$fo_id?>");
var str_obj = opener.xGetElementById("str_<?=$fo_id?>");
if(date_obj) date_obj.value = date_val;
if(str_obj) xInnerHtml(str_obj, date_str);
if(callback_func) eval('opener.'+callback_func+'('+date_val+')');
window.close();
}
</script>
</head>
<body>
<div id="popup_content" >
<div id="popHeadder">
<h1>Calendar</h1>
</div>
<form action="./calendar.php" method="get">
<input type="hidden" name="fo_id" value="<?=$fo_id?>"/>
<input type="hidden" name="callback_func" value="<?=$callback_func?>"/>
<div id="popBody">
<div class="calendar">
<div class="yymm">
<div class="yy">
<a href="./calendar.php?fo_id=<?=$fo_id?>&amp;year=<?=$year?>&amp;month=<?=$month?>&amp;method=prev_year&amp;callback_func=<?=$callback_func?>" class="left"><img src="./images/buttonLeft2.gif" alt="prev" width="11" height="11" /></a><?=$year?><a href="./calendar.php?fo_id=<?=$fo_id?>&amp;year=<?=$year?>&amp;month=<?=$month?>&amp;method=next_year&amp;callback_func=<?=$callback_func?>" class="right"><img src="./images/buttonRight2.gif" alt="next" width="11" height="11" /></a>
</div>
<div class="mm">
<p><?=date("M", mktime(0,0,0,$month,1,$year))?></p>
<a href="./calendar.php?fo_id=<?=$fo_id?>&amp;year=<?=$year?>&amp;month=<?=$month?>&amp;method=prev_month&amp;callback_func=<?=$callback_func?>" class="left"><img src="./images/buttonLeft2.gif" alt="prev" width="11" height="11" /></a><span><?=$month?></span><a href="./calendar.php?fo_id=<?=$fo_id?>&amp;year=<?=$year?>&amp;month=<?=$month?>&amp;method=next_month&amp;callback_func=<?=$callback_func?>" class="right"><img src="./images/buttonRight2.gif" alt="next" width="11" height="11" /></a>
</div>
<div class="go">
<input type="text" name="year" class="inputTypeY" value="<?=$year?>" />
<input type="text" name="month" class="inputTypeM" value="<?=$month?>" />
<input type="image" src="./images/buttonGo.gif" alt="Go" />
</div>
</div>
<table cellspacing="0" class="dd">
<?php
for($i=0;$i<6;$i++) {
?>
<tr class="<?if($i==0){?>first<?}elseif($i==5){?>last<?}?>">
<?php
for($j=0;$j<7;$j++) {
$m = $month;
$y = $year;
$cell_no = $i*7 + $j;
if($cell_no < $start_week) {
$day = $before_month_month_day + $cell_no - $start_week + 1;
$m = $month - 1;
if($m<1) {
$m = 12;
$y = $year - 1;
}
} else {
$day = $cell_no - $start_week +1;
$m = $month;
if($day > $month_day) {
$day = $day - $month_day;
$m = $month + 1;
if($m>12) {
$m = 1;
$y = $year-1;
}
}
}
if($j==0) $class_name = "sun";
else $class_name= "";
$date = date("Y. m. d", mktime(0,0,0,$m, $day, $y));
$date_str = date("Ymd", mktime(0,0,0,$m, $day, $y));
?>
<td class="<?=$class_name?>">
<?if($m==$month){?>
<?if(date("Ymd")==$date_str){?><strong><?}?>
<?if($day){?><a href="#" onclick="selectDate('<?=$date?>','<?=$date_str?>','<?=$callback_func?>');return false;"><?=$day?></a><?}else{?>&nbsp;<?}?>
<?if(date("Ymd")==$date_str){?></strong><?}?>
<?}else{?>
<span class="disable"><?if($day){?><a href="#" onclick="selectDate('<?=$date?>','<?=$date_str?>','<?=$callback_func?>');return false;"><?=$day?></a><?}else{?>&nbsp;<?}?></span>
<?}?>
</td>
<?
}
?>
</tr>
<?
}
?>
</table>
</div>
</div>
</form>
<div id="popFooter" class="tCenter">
<a href="#" onclick="window.close();" class="button"><span>close</span></a>
</div>
</div>
<script type="text/javascript">
xAddEventListener(window,'load', setFixedPopupSize);
var _isPoped = true;
</script>
</body>
</html>

View file

@ -0,0 +1,47 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="{Context::getLangType()}" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta name="generator" content="zeroboard xe (http://www.zeroboard.com)" />
<!--@if($module_info->module)--><meta name="module" content="{$module_info->module}" />
<!--@end--><!--@if($module_info->skin)--><meta name="module_skin" content="{$module_info->skin}" />
<!--@end--><!--@if($layout_info->title)--><meta name="layout" content="{$layout_info->title} ({$layout_info->layout})" />
<!--@end--><!--@if($layout->author)--><meta name="layout_maker" content="{$layout_info->author->name} ({$layout_info->author->homepage})" />
<!--@end--><meta http-equiv="imagetoolbar" content="no" />
<title>{Context::getBrowserTitle()}</title>
<script type="text/javascript" src="./common/js/x.js"></script>
<script type="text/javascript" src="./common/js/common.js"></script>
<script type="text/javascript" src="./common/js/xml_handler.js"></script>
<script type="text/javascript" src="./common/js/xml_js_filter.js"></script>
<!--@foreach(Context::getJsFile() as $key => $js_file)--><script type="text/javascript" src="{$js_file}"></script>
<!--@end--><!--@if($rss_url)--><link rel="alternate" type="application/rss+xml" title="RSS 2.0" href="{$rss_url}" />
<!--@end--><link rel="stylesheet" href="./common/css/default.css" type="text/css" />
<link rel="stylesheet" href="./common/css/button.css" type="text/css" />
<!--@foreach(Context::getCssFile() as $key => $css_file)--><link rel="stylesheet" href="{$css_file}" type="text/css" />
<!--@end--><!--@if($module=='admin' || strpos($act,'Admin')>0)--><link rel="stylesheet" href="./modules/admin/tpl/css/admin.css" type="text/css" />
<!--@end--><script type="text/javascript">//<![CDATA[
var current_url = "{$current_url}";
var request_uri = "{$request_uri}";
var current_mid = "{$mid}";
var wating_message = "{$lang->msg_call_server}";
//]]></script>
{Context::getHtmlHeader()}
</head>
<body>
{$zbxe_final_content}
{Context::getHtmlFooter()}
<div id="waitingforserverresponse" style="visibility:hidden"></div>
<div id="fororiginalimagearea" style="visibility:hidden">
<div id="fororiginalimageareabg">
<img src="./images/blank.gif" alt="original image" border="0" id="fororiginalimage" />
<img src="./images/original_image_box_close.gif" alt="close original image" border="0" onclick="closeOriginalImage()" id="closeOriginalImageBtn"/>
</div>
</div>
<div id="membermenuarea"></div>
</body>
</html>

View file

@ -0,0 +1,33 @@
@charset "utf-8";
body { margin:0; padding:0; }
#popup_content { width:370px; overflow:hidden;}
#popHeadder { width:370px; }
#popBody { width:350px; }
#popFooter { width:370px; }
#popBody .calendar { width:350px; border:1px solid #c1c0bd; border-left:none; border-right:none; background:#f5f5f5; overflow:hidden; position:relative;}
#popBody .calendar .yymm { width:143px; float:left;}
#popBody .calendar .yymm .yy { padding:9px 0 6px 0; text-align:center; border-bottom:1px solid #ededed; margin:0 15px; font:bold 1.2em Tahoma; color:#444444;}
#popBody .calendar .yymm .mm { margin:0 15px; border-top:1px solid #ffffff; border-bottom:1px solid #ededed; padding:10px 0 7px 0; text-align:center;}
#popBody .calendar .yymm .mm p { color:#969696; font:1.6em "Times New Roman";}
#popBody .calendar .yymm .mm span { font:4em "Times New Roman"; color:#158692; vertical-align:middle;}
#popBody .calendar .yymm .go { padding:7px 0 0 0; margin:0 15px; text-align:center; border-top:1px solid #ffffff;}
#popBody .calendar .yymm .go * { vertical-align:middle;}
#popBody .calendar .yymm .go .inputTypeY { border:1px solid #c9c9c9; text-align:center; font:.9em Tahoma; color:#282828; width:31px; height:16px;}
#popBody .calendar .yymm .go .inputTypeM { border:1px solid #c9c9c9; text-align:center; font:.9em Tahoma; color:#282828; width:20px; height:16px;}
#popBody .calendar .yymm .left { vertical-align:middle; margin-right:14px;}
#popBody .calendar .yymm .right { vertical-align:middle; margin-left:14px;}
#popBody .calendar .yymm .left img,
#popBody .calendar .yymm .right img { vertical-align:middle; position:relative; top:2px;}
#popBody .calendar .dd { float:left; width:207px; background:#ffffff; border-left:10px solid #ffffff;}
#popBody .calendar .dd td { border-bottom:1px solid #ededed; height:32px; text-align:center; color:#636363; font:.9em Tahoma;}
#popBody .calendar .dd td a { color:#636363; font:.9em Tahoma;}
#popBody .calendar .dd td strong { text-decoration:underline;}
#popBody .calendar .dd td strong a { text-decoration:underline;}
#popBody .calendar .dd td.sun { color:#c95b53;}
#popBody .calendar .dd td.sun a { color:#c95b53;}
#popBody .calendar .dd .first { height:35px;}
#popBody .calendar .dd .last td { height:35px; border-bottom:none;}
#popBody .calendar .dd td .disable a { color:#CCCCCC; }

View file

@ -0,0 +1 @@
{$content}

BIN
common/tpl/images/blank.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 454 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 649 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 650 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 650 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 648 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 622 B

BIN
common/tpl/images/info.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 B

BIN
common/tpl/images/join.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 B

BIN
common/tpl/images/line.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 B

BIN
common/tpl/images/minus.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 610 B

BIN
common/tpl/images/page.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B

BIN
common/tpl/images/plus.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

BIN
common/tpl/images/widget_bg.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
common/tpl/images/widget_text.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 970 B

View file

@ -0,0 +1,7 @@
<!--%import("css/popup.css")-->
<div id="popup_content">{$content}</div>
<script type="text/javascript">
xAddEventListener(window, 'load', setFixedPopupSize);
var _isPoped = true;
</script>

3
common/tpl/refresh.html Normal file
View file

@ -0,0 +1,3 @@
<script type="text/javascript">
top.location.href = top.location.href;
</script>

70
config/config.inc.php Normal file
View file

@ -0,0 +1,70 @@
<?php
/**
* @file config/config.inc.php
* @author zero (zero@nzeo.com)
* @brief 기본적으로 사용하는 class파일의 include 환경 설정을
**/
/**
* @brief 기본적인 상수 선언, 웹에서 직접 호출되는 것을 막기 위해 체크하는 상수 선언
**/
define('__ZBXE__', true);
/**
* @brief 디버그 메세지의 출력 장소
* 0 : files/_debug_message.php 연결하여 출력
* 1 : Response Method XML 형식이 아닐 경우 브라우저에 최상단에 주석으로 표시
**/
define('__DEBUG_OUTPUT__', 0);
/**
* @brief DB 오류 메세지 출력 정의
* 0 : 출력하지 않음
* 1 : files/_debug_db_query.php 연결하여 출력
**/
define('__DEBUG_DB_OUTPUT__', 0);
/**
* @brief 디버깅 메세지 출력
* 0 : 디버그 메세지를 생성/ 출력하지 않음
* 1 : 전체 실행 시간에 대해서만 메세지 생성/ 출력
* 2 : 1 + DB 쿼리
* 3 : 모든 로그
**/
define('__DEBUG__', 0);
/**
* @brief 간단하게 사용하기 위한 함수 정의한 파일 require
**/
require_once("./config/func.inc.php");
if(__DEBUG__) define('__StartTime__', getMicroTime());
/**
* @brief 기본적인 class 파일 include
*
* php5 기반으로 바꾸게 되면 _autoload를 이용할 있기에 제거 대상
**/
if(__DEBUG__) define('__ClassLosdStartTime__', getMicroTime());
require_once("./classes/object/Object.class.php");
require_once("./classes/handler/Handler.class.php");
require_once("./classes/xml/XmlParser.class.php");
require_once("./classes/context/Context.class.php");
require_once("./classes/db/DB.class.php");
require_once("./classes/file/FileHandler.class.php");
require_once("./classes/widget/WidgetHandler.class.php");
require_once("./classes/editor/EditorHandler.class.php");
require_once("./classes/module/ModuleObject.class.php");
require_once("./classes/module/ModuleHandler.class.php");
require_once("./classes/display/DisplayHandler.class.php");
require_once("./classes/template/TemplateHandler.class.php");
if(__DEBUG__) $GLOBALS['__elapsed_class_load__'] = getMicroTime() - __ClassLosdStartTime__;
/**
* @brief 세션 설정
**/
@session_cache_limiter('no-cache, must-revalidate');
@ini_set("session.gc_maxlifetime", "18000");
@session_start();
?>

361
config/func.inc.php Normal file
View file

@ -0,0 +1,361 @@
<?php
/**
* @file config/func.inc.php
* @author zero (zero@nzeo.com)
* @brief 편의 목적으로 만든 함수라이브러리 파일
**/
/**
* @brief php5에 대비하여 clone 정의
**/
if (version_compare(phpversion(), '5.0') < 0) {
eval('
function clone($object) {
return $object;
}
');
}
$time_zone = array(
'-1200' => '[GMT -12:00] Baker Island Time',
'-1100' => '[GMT -11:00] Niue Time, Samoa Standard Time',
'-1000' => '[GMT -10:00] Hawaii-Aleutian Standard Time, Cook Island Time',
'-0930' => '[GMT -09:30] Marquesas Islands Time',
'-0900' => '[GMT -09:00] Alaska Standard Time, Gambier Island Time',
'-0800' => '[GMT -08:00] Pacific Standard Time',
'-0700' => '[GMT -07:00] Mountain Standard Time',
'-0600' => '[GMT -06:00] Central Standard Time',
'-0500' => '[GMT -05:00] Eastern Standard Time',
'-0400' => '[GMT -04:00] Atlantic Standard Time',
'-0330' => '[GMT -03:30] Newfoundland Standard Time',
'-0300' => '[GMT -03:00] Amazon Standard Time, Central Greenland Time',
'-0200' => '[GMT -02:00] Fernando de Noronha Time, South Georgia &amp; the South Sandwich Islands Time',
'-0100' => '[GMT -01:00] Azores Standard Time, Cape Verde Time, Eastern Greenland Time',
'0000' => '[GMT 00:00] Western European Time, Greenwich Mean Time',
'+0100' => '[GMT +01:00] Central European Time, West African Time',
'+0200' => '[GMT +02:00] Eastern European Time, Central African Time',
'+0300' => '[GMT +03:00] Moscow Standard Time, Eastern African Time',
'+0330' => '[GMT +03:30] Iran Standard Time',
'+0400' => '[GMT +04:00] Gulf Standard Time, Samara Standard Time',
'+0430' => '[GMT +04:30] Afghanistan Time',
'+0500' => '[GMT +05:00] Pakistan Standard Time, Yekaterinburg Standard Time',
'+0530' => '[GMT +05:30] Indian Standard Time, Sri Lanka Time',
'+0545' => '[GMT +05:45] Nepal Time',
'+0600' => '[GMT +06:00] Bangladesh Time, Bhutan Time, Novosibirsk Standard Time',
'+0630' => '[GMT +06:30] Cocos Islands Time, Myanmar Time',
'+0700' => '[GMT +07:00] Indochina Time, Krasnoyarsk Standard Time',
'+0800' => '[GMT +08:00] Chinese Standard Time, Australian Western Standard Time, Irkutsk Standard Time',
'+0845' => '[GMT +08:45] Southeastern Western Australia Standard Time',
'+0900' => '[GMT +09:00] Korea Standard Time, Japan Standard Time, China Standard Time',
'+0930' => '[GMT +09:30] Australian Central Standard Time',
'+1000' => '[GMT +10:00] Australian Eastern Standard Time, Vladivostok Standard Time',
'+1030' => '[GMT +10:30] Lord Howe Standard Time',
'+1100' => '[GMT +11:00] Solomon Island Time, Magadan Standard Time',
'+1130' => '[GMT +11:30] Norfolk Island Time',
'+1200' => '[GMT +12:00] New Zealand Time, Fiji Time, Kamchatka Standard Time',
'+1245' => '[GMT +12:45] Chatham Islands Time',
'+1300' => '[GMT +13:00] Tonga Time, Phoenix Islands Time',
'+1400' => '[GMT +14:00] Line Island Time',
) ;
/**
* @brief ModuleHandler::getModuleObject($module_name, $type) 쓰기 쉽게 함수로 선언
* @param module_name 모듈이름
* @param type disp, proc, controller, class
* @param kind admin, null
* @return module instance
**/
function &getModule($module_name, $type = 'view', $kind = '') {
return ModuleHandler::getModuleInstance($module_name, $type, $kind);
}
/**
* @brief module의 controller 객체 생성용
* @param module_name 모듈이름
* @return module controller instance
**/
function &getController($module_name) {
return getModule($module_name, 'controller');
}
/**
* @brief module의 admin controller 객체 생성용
* @param module_name 모듈이름
* @return module admin controller instance
**/
function &getAdminController($module_name) {
return getModule($module_name, 'controller','admin');
}
/**
* @brief module의 view 객체 생성용
* @param module_name 모듈이름
* @return module view instance
**/
function &getView($module_name) {
return getModule($module_name, 'view');
}
/**
* @brief module의 admin view 객체 생성용
* @param module_name 모듈이름
* @return module admin view instance
**/
function &getAdminView($module_name) {
return getModule($module_name, 'view','admin');
}
/**
* @brief module의 model 객체 생성용
* @param module_name 모듈이름
* @return module model instance
**/
function &getModel($module_name) {
return getModule($module_name, 'model');
}
/**
* @brief module의 admin model 객체 생성용
* @param module_name 모듈이름
* @return module admin model instance
**/
function &getAdminModel($module_name) {
return getModule($module_name, 'model','admin');
}
/**
* @brief module의 상위 class 객체 생성용
* @param module_name 모듈이름
* @return module class instance
**/
function &getClass($module_name) {
return getModule($module_name, 'class');
}
/**
* @brief DB::executeQuery() alias
* @param query_id 쿼리 ID ( 모듈명.쿼리XML파일 )
* @param args object 변수로 선언된 인자값
* @return 처리결과
**/
function executeQuery($query_id, $args = null) {
$oDB = &DB::getInstance();
return $oDB->executeQuery($query_id, $args);
}
/**
* @brief DB::getNextSequence() alias
* @return big int
**/
function getNextSequence() {
$oDB = &DB::getInstance();
return $oDB->getNextSequence();
}
/**
* @brief Context::getUrl($args_list) 쓰기 쉽게 함수로 선언
* @return string
*
* getUrl() 현재 요청된 RequestURI에 주어진 인자의 값으로 변형하여 url을 리턴한다\n
* 1. 인자는 (key, value)... 형식으로 주어져야 한다.\n
* ex) getUrl('key1','val1', 'key2', '') : key1, key2를 val1과 '' 변형\n
* 2. 아무런 인자가 없으면 argument를 제외한 url을 리턴
* 3. 인자값이 '' 이면 RequestUri에다가 추가된 args_list로 url을 만듬
**/
function getUrl() {
$num_args = func_num_args();
$args_list = func_get_args();
if(!$num_args) return Context::getRequestUri();
return Context::getUrl($num_args, $args_list);
}
/**
* @brief 주어진 문자를 주어진 크기로 자르고 잘라졌을 경우 주어진 꼬리를
* @param string 자를 문자열
* @param cut_size 주어진 문자열을 자를 크기
* @param tail 잘라졌을 경우 문자열의 제일 뒤에 붙을 꼬리
* @return string
*
* 손쉽고 확실한 변환을 위해 2byte unicode로 변형한후 처리를 한다
**/
function cut_str($string, $cut_size, $tail='...') {
if(!function_exists('iconv')) return $string;
if(!$string || !$cut_size) return $string;
$unicode_str = iconv("UTF-8","UCS-2",$string);
if(strlen($unicode_str) < $cut_size*2) return $string;
$output_str = substr($unicode_str, 0, $cut_size*2);
return iconv("UCS-2","UTF-8",$output_str).$tail;
}
/**
* @brief YYYYMMDDHHIISS 형식의 시간값을 unix time으로 변경
* @param str YYYYMMDDHHIISS 형식의 시간값
* @return int
**/
function ztime($str) {
if(!$str) return;
$hour = (int)substr($str,8,2);
$min = (int)substr($str,10,2);
$sec = (int)substr($str,12,2);
$year = (int)substr($str,0,4);
$month = (int)substr($str,4,2);
$day = (int)substr($str,6,2);
$time_zone = $GLOBALS['_time_zone'];
if($time_zone<0) $to = -1; else $to = 1;
$t_hour = substr($time_zone,1,2)*$to;
$t_min = substr($time_zone,3,2)*$to;
$server_time_zone = date("O");
if($server_time_zone<0) $so = -1; else $so = 1;
$c_hour = substr($server_time_zone,1,2)*$so;
$c_min = substr($server_time_zone,3,2)*$so;
$g_min = $t_min - $c_min;
$g_hour = $t_hour - $c_hour;
$gap = $g_min*60 + $g_hour*60*60;
return mktime($hour, $min, $sec, $month?$month:1, $day?$day:1, $year)+$gap;
}
/**
* @brief YYYYMMDDHHIISS 형식의 시간값을 원하는 시간 포맷으로 변형
* @param str YYYYMMDDHHIISS 형식의 시간값
* @param format php date()함수의 시간 포맷
* @return string
**/
function zdate($str, $format = "Y-m-d H:i:s") {
if(!$str) return;
switch(Context::getLangType()) {
case "en" :
case "es" :
if($format == "Y-m-d") $format = "M d, Y";
elseif($format == "Y-m-d H:i:s") $format = "M d, Y H:i:s";
elseif($format == "Y-m-d H:i") $format = "M d, Y H:i";
break;
}
return date($format, ztime($str));
}
/**
* @brief 간단한 console debugging 함수
* @param buff 출력하고자 하는 object
* @param display_line 구분자를 출력할 것인지에 대한 플래그 (기본:true)
* @return none
*
* ./files/_debug_message.php 파일에 $buff 내용을 출력한다.
* tail -f ./files/_debug_message.php 하여 계속 살펴 있다
**/
function debugPrint($buff = null, $display_line = true) {
//if(!$buff) return;
if(__DEBUG_OUTPUT__==1) {
print sprintf("<!--\n%s\n-->", print_r($buff,true));
} else {
$debug_file = "./files/_debug_message.php";
$buff = sprintf("%s\n",print_r($buff,true));
if($display_line) $buff = "\n====================================\n".$buff."------------------------------------\n";
if(@!$fp = fopen($debug_file,"a")) return;
fwrite($fp, $buff);
fclose($fp);
}
}
/**
* @brief microtime() return
* @return float
**/
function getMicroTime() {
list($time1, $time2) = explode(' ', microtime());
return (float)$time1+(float)$time2;
}
/**
* @brief 첫번째 인자로 오는 object var에서 2번째 object의 var들을 제거
* @param target_obj object
* @param del_obj object의 vars에서 del_obj의 vars를 제거한다
* @return object
**/
function delObjectVars($target_obj, $del_obj) {
if(!is_object($target_obj)) return;
if(!is_object($del_obj)) return;
$target_vars = get_object_vars($target_obj);
$del_vars = get_object_vars($del_obj);
$target = array_keys($target_vars);
$del = array_keys($del_vars);
if(!count($target)||!count($del)) return $target_obj;
$return_obj = NULL;
$target_count = count($target);
for($i=0;$i<$target_count;$i++) {
$target_key = $target[$i];
if(!in_array($target_key, $del)) $return_obj->{$target_key} = $target_obj->{$target_key};
}
return $return_obj;
}
/**
* @brief php5 이상에서 error_handing을 debugPrint로 변경
* @param errno
* @param errstr
* @return file
* @return line
**/
function handleError($errno, $errstr, $file, $line) {
if(!__DEBUG__) return;
$errors = array(E_USER_ERROR, E_ERROR, E_PARSE);
if(!in_array($errno,$errors)) return;
$output = sprintf("Fatal error : %s - %d", $file, $line);
$output .= sprintf("%d - %s", $errno, $errstr);
debugPrint($output);
}
/**
* @brief 주어진 숫자를 주어진 크기로 recursive하게 잘라줌
* @param no 주어진 숫자
* @param size 잘라낼 크기
*
* ex) 12, 3 => 012/
* ex) 1234, 3 => 123/004/
**/
function getNumberingPath($no, $size=3) {
$mod = pow(10,$size);
$output = sprintf('%0'.$size.'d/', $no%$mod);
if($no >= $mod) $output .= getNumberingPath((int)$no/$mod, $size);
return $output;
}
/**
* @brief 한글이 들어간 url의 decode
**/
function url_decode($str) {
return preg_replace('/%u([[:alnum:]]{4})/', '&#x\\1;',$str);
}
/**
* @brief iframe, script코드 제거
**/
function removeHackTag($content) {
// iframe 제거
$content = preg_replace("!<iframe(.*?)<\/iframe>!is","",$content);
// script code 제거
$content = preg_replace("!<script(.*?)<\/script>!is","",$content);
return $content;
}
?>

50
index.php Normal file
View file

@ -0,0 +1,50 @@
<?php
/**
* @file index.php
* @author zero (zero@zeroboard.com)
* @brief 시작 페이지
*
* zeroboard XE의 시작 페이지
*
* Request Argument에서 mid, act로 module 객체를 찾아서 생성하고 \n
* 모듈 정보를 세팅함
*
* @mainpage 첫페이지
* @section intro 소개
* zeroboard XE 오픈 프로젝트로 개발되는 오픈 소스입니다.\n
* 자세한 내용은 아래 링크를 참조하세요.
* - 공식홈페이지 : http://www.zeroboard.com
* - 오픈프로젝트 사이트 : http://spring.zeroboard.com
* - 이슈트래킹 : http://trac.zeroboard.com
* - SVN Repository : http://svn.zeroboard.com/zeroboard_xe/trunk
* - document : http://doc.zeroboard.com
* - pdf 문서 : http://doc.zeroboard.com/zeroboard_xe.pdf
*
**/
/**
* @brief 필요한 설정 파일들을 include
**/
require_once("./config/config.inc.php");
/**
* @brief Context 객체를 생성하여 초기화
* 모든 Request Argument/ 환경변수등을 세팅
**/
$oContext = &Context::getInstance();
$oContext->init();
/**
* @brief ModuleHandler 객체를 생성/ 실행
*
* 모듈 핸들러는 Request Argument를 바탕으로 모듈을 찾아서\n
* 객체를 생성하고 기본 정보를 setting 해준다.\n
* ModuleHandler는 외에도 설치가 되어 있는지에 대한 체크를\n
* 하여 미설치시 Install 모듈을 실행하도록 한다\n
* 그리고 해당 모듈을 실행후 컨텐츠를 출력한다\n
**/
$oModuleHandler = new ModuleHandler();
$oModuleHandler->init();
$oModule = &$oModuleHandler->procModule();
$oModuleHandler->displayContent($oModule);
?>

View file

@ -0,0 +1,108 @@
<?xml version="1.0" encoding="utf-8"?>
<layout version="0.1">
<title xml:lang="ko">견본 레이아웃 (JS 메뉴)</title>
<title xml:lang="zh-CN">样本布局JS 菜单)</title>
<title xml:lang="en">Sample Layout (Menu in JS)</title>
<title xml:lang="es">Diseño predeterminado (menú JS)</title>
<title xml:lang="jp">レイアウトサンプル(JS)</title>
<author email_address="zero@zeroboard.com" link="http://www.zeroboard.com" date="2007. 2. 28">
<name xml:lang="ko">제로</name>
<name xml:lang="zh-CN">zero</name>
<name xml:lang="en">zero</name>
<name xml:lang="es">zero</name>
<name xml:lang="jp">Zero</name>
<description xml:lang="ko">
가장 기본적인 기능으로 이루어진 견본 레이아웃입니다.
main_menu, bottom_menu 2개의 메뉴로 구성되어 있으며 각 메뉴의 출력은 javascript 함수를 이용해서 출력합니다.
레이아웃을 만들때 편리하지만 자유도가 떨어집니다.
</description>
<description xml:lang="zh-CN">
此布局是最基本的功能形成的样本布局。
此布局由 main_menu, bottom_menu 两个菜单构成,每个菜单的呼出方式是利用 javascript 函数显示。
制作布局的时候方便的同时自由度相对落后。
</description>
<description xml:lang="en">
This is a sample layout consists of the most basic features.
It has two menues each named 'main_menu' and 'bottom_menu', and they are displayed by using Javascript function.
Javascript version is so convenient to create layouts, but reduces the degree of freedom though.
</description>
<description xml:lang="es">
Es el diseño predeterminado para funciónes basicos. Tiene 2 menús de main_menu, bottom_menu. La salida de cada menu usa funciónes de javascript. Es facil de manejar diseño, pero falta de libertad.
</description>
<description xml:lang="jp">
一番基本的な機能で構成されたレイアウトのサンプルです。「main_menu」と「bottom_menu」つのメニューで構成されていて、各メニューは「javascript」関数を用いて 出力します。 レイアウトをデザインする際に便利ですが、自由度は制限されます。
</description>
</author>
<extra_vars>
<var name="colorset" type="select">
<title xml:lang="ko">컬러셋</title>
<title xml:lang="zh-CN">颜色</title>
<title xml:lang="en">Colorset</title>
<title xml:lang="es">collección de colores</title>
<title xml:lang="jp">カラーセット</title>
<description xml:lang="ko">원하시는 컬러셋을 선택해주세요.</description>
<description xml:lang="zh-CN">请选择颜色。</description>
<description xml:lang="en">Please select a colorset you want.</description>
<description xml:lang="es">Seleccióne la collección de colores.</description>
<description xml:lang="jp">お好みのカラーセットを選択してください。</description>
<options name="red">
<title xml:lang="ko">적색</title>
<title xml:lang="zh-CN">红色</title>
<title xml:lang="en">Red</title>
<title xml:lang="es">Rojo</title>
<title xml:lang="jp"></title>
<value>red</value>
</options>
<options name="blue">
<title xml:lang="ko">청색</title>
<title xml:lang="zh-CN">青色</title>
<title xml:lang="en">Blue</title>
<title xml:lang="es">Azul</title>
<title xml:lang="jp"></title>
<value>blue</value>
</options>
</var>
<var name="top_title" type="text">
<title xml:lang="ko">상단 제목</title>
<title xml:lang="zh-CN">顶部标题</title>
<title xml:lang="en">Title on the top</title>
<title xml:lang="es">Título</title>
<title xml:lang="jp">上段タイトル</title>
<description xml:lang="ko">레이아웃의 상단에 표시할 제목을 입력하세요.</description>
<description xml:lang="zh-CN">请输入布局顶部显示的标题。(网站名称)</description>
<description xml:lang="en">Please input a title to be displayed on the top of the layout.</description>
<description xml:lang="es">Escribe el título para la pagína de web.</description>
<description xml:lang="jp">レイアウトの上段に表示するタイトルを入力してください。</description>
</var>
<var name="index_url" type="text">
<title xml:lang="ko">홈 페이지 URL</title>
<title xml:lang="zh-CN">网站 URL</title>
<title xml:lang="en">Home page URL</title>
<title xml:lang="es">URL de pagína de web</title>
<title xml:lang="jp">ホームページ URL</title>
<description xml:lang="ko">로고나 제목을 클릭시에 이동할 홈 페이지 URL을 입력해 주세요.</description>
<description xml:lang="zh-CN">点击网站LOGO或标题时要移动的页面URL。</description>
<description xml:lang="en">Please input URL to the page that you want to be loaded when a logo or title is clicked.</description>
<description xml:lang="es">Escribe el URL para título.</description>
<description xml:lang="jp">ロゴまたはタイトルをクリックした際、移動するホームページの URL を入力してください。</description>
</var>
</extra_vars>
<menus>
<menu name="main_menu" default="true">
<title xml:lang="ko">주 메뉴</title>
<title xml:lang="zh-CN">主 菜单</title>
<title xml:lang="en">Main menu</title>
<title xml:lang="es">menú principal</title>
<title xml:lang="jp">メインメニュー</title>
<maxdepth>2</maxdepth>
</menu>
<menu name="bottom_menu">
<title xml:lang="ko">하단 메뉴 </title>
<title xml:lang="zh-CN">底部菜单</title>
<title xml:lang="en">Menu on the bottom </title>
<title xml:lang="es">sub menú </title>
<title xml:lang="jp">下段メニュー</title>
<maxdepth>1</maxdepth>
</menu>
</menus>
</layout>

View file

@ -0,0 +1,188 @@
@charset "utf-8";
body {
margin:0px;
padding:0px;
}
/**
* 레이아웃 style
**/
/**
* 상단 로고 메인 1차 메뉴
**/
.layout_top {
margin:10px 10px 0px 10px;
}
/* 상단 로고 부분 */
.layout_logo A {
font-weight:bold;
font-size:16pt;
color:#888888;
text-decoration:none;
}
/* 상단 1차 메뉴 */
.layout_first_menu {
border-bottom:2px solid #DDDDDD;
width:100%;
text-align:right;
padding:3px 0px 3px 0px;
}
/**
* 좌측 메뉴 메인 2차 메뉴, 로그인 위젯 기타
**/
.layout_left {
width:220px;
float:left;
margin-top:10px;
position:absolute;
left:10px;
}
/* 좌측 2차 메뉴 */
.layout_second_menu {
}
/* 좌측 관리자 메뉴 */
.layout_admin {
margin-top:10px;
border:3px solid #EEEEEE;
padding:5px;
text-align:center;
}
.layout_admin A {
color:#AAAAAA;
text-decoration:none;
}
/* 컨텐츠 */
.layout_content {
padding:10px 10px 0px 240px;
}
#content {
}
/**
* 하단 메뉴 영역
**/
.layout_bottom {
clear:both;
margin-top:20px;
}
/* 하단 메뉴 */
.layout_bottom_menu {
width:100%;
text-align:center;
}
/**
* 메뉴 style, main_menu:1~3차, bottom_menu로 구성
**/
/* 1차 메뉴 */
.first_menu {
background-color:#999999;
padding:3px 10px 3px 10px;
margin-left:5px;
}
.first_menu A {
font-weight:normal;
color:#FFFFFF;
text-decoration:none;
}
.first_menu_selected {
background-color:#000000;
padding:3px 10px 3px 10px;
margin-left:5px;
}
.first_menu_selected A {
color:#FFFFFF;
font-weight:bold;
text-decoration:none;
}
/* 2차 메뉴 */
.second_menu {
padding:5px 0px 5px 5px;
background-color:#AAAAAA;
display:block;
border-bottom:1px solid #999999;
}
.second_menu A {
color:#FFFFFF;
text-decoration:none;
font-weight:normal;
}
.second_menu_selected {
padding:5px 0px 5px 5px;
background-color:#666666;
display:block;
border-bottom:1px solid #444444;
}
.second_menu_selected A {
color:#FFFFFF;
font-weight:bold;
text-decoration:none;
}
/* 3차 메뉴 */
.third_menu {
padding:5px 0px 5px 20px;
border-bottom:1px solid #EEEEEE;
display:block;
}
.third_menu A {
color:#000000;
text-decoration:none;
}
.third_menu_selected {
padding:5px 0px 5px 20px;
border-bottom:1px solid #EEEEEE;
font-weight:bold;
display:block;
background-color:#EFEFEF;
}
.third_menu_selected A {
color:#000000;
font-weight:bold;
text-decoration:none;
}
/* 하단 메뉴 */
.bottom_menu {
margin-right:5px;
font-weight:normal;
}
.bottom_menu A {
color:#000000;
text-decoration:none;
}
.bottom_menu_selected {
margin-right:5px;
font-weight:bold;
}
.bottom_menu_selected A {
color:#000000;
font-weight:bold;
text-decoration:none;
}

View file

@ -0,0 +1,72 @@
/**
* @brief sample_layout 메뉴 출력용 javascript
* @author zero (zero@nzeo.com)
**/
/**
* 메뉴를 담을 javascript 변수
*
* xe_layout_menu 구조
*
* xe_layout_menu[메뉴명][depth][menu_srl] = Object
*
* Object {
* text : 메뉴
* href : 연결할 주소
* open_window : [Y|N] 새창으로 띄울 것인지에 대한
* normal_btn : 이미지 버튼
* hover_btn : 이미지 버튼일 경우 mouseover 경우
* active_btn : 선택되어 있을 경우의 이미지
* className : normal 상태의 className
* selectedClassName : 선택된 상태의 className
* selected : 선택된 메뉴라면 true, 아니면 false
* }
**/
var xe_layout_menu = new Array();
/**
* @brief sample_layout에서 메뉴를 출력하는 함수
* menu_name : 레이아웃 설정상의 메뉴 이름
* depth : 입력된 단계
* print_child : true로 하면 메뉴 출력중 하위 메뉴가 있을 출력
**/
function xe_print_menu(menu_name, depth, print_child) {
if(typeof(xe_layout_menu[menu_name])=='undefined' || typeof(xe_layout_menu[menu_name][depth])=='undefined') return;
if(typeof(print_child)=='undefined') print_child = false;
var menu_list = xe_layout_menu[menu_name][depth];
var html = "";
for(var menu_key in menu_list) {
var menu_obj = menu_list[menu_key];
if(typeof(menu_obj)=='undefined'||!menu_obj) continue;
if(!menu_obj.text && !menu_obj.normal_btn) continue;
var className = menu_obj.className;
if(menu_obj.selected) className = menu_obj.selectedClassName;
if(!menu_obj.href) menu_obj.href = "#";
// 텍스트일 경우
if(!menu_obj.normal_btn) {
if(menu_obj.open_window == "Y") html = "<span class=\""+className+"\"><a href=\"#\" onclick=\"winopen('"+menu_obj.href+"'); return false;\">"+menu_obj.text+"</a></span>";
else html = "<span class=\""+className+"\"><a href=\""+menu_obj.href+"\">"+menu_obj.text+"</a></span>";
// 이미지 버튼 일 경우
} else if(menu_obj.normal_btn) {
if(!menu_obj.hover_btn) menu_obj.hover_btn = menu_obj.normal_btn;
if(!menu_obj.active_btn) menu_obj.active_btn = menu_obj.normal_btn;
if(menu_obj.selected) menu_obj.normal_btn = menu_obj.active_btn;
if(menu_obj.open_window == "Y") html = "<span class=\""+className+"\"><a href=\"#\" onclick=\"winopen('"+menu_obj.href+"'); return false;\"><img src=\""+menu_obj.normal_btn+"\" border=\"0\" alt=\""+menu_obj.text+"\" onmouseover=\"this.src='"+menu_obj.hover_btn+"'\" onmouseout=\"this.src='"+menu_obj.normal_btn+"'\" /></a></span>";
else html = "<span class=\""+className+"\"><a href=\""+menu_obj.href+"\"><img src=\""+menu_obj.normal_btn+"\" border=\"0\" alt=\""+menu_obj.text+"\" onmouseover=\"this.src='"+menu_obj.hover_btn+"'\" onmouseout=\"this.src='"+menu_obj.normal_btn+"'\" /></a></span>";
}
if(html) document.write(html);
if(print_child && menu_obj.selected && typeof(xe_layout_menu[menu_name][depth+1])!='undefined') xe_print_menu('main_menu', depth+1, true);
}
}

View file

@ -0,0 +1,83 @@
<!-- 메뉴를 출력하기 위한 javascript 소스가 담긴 파일을 import -->
<!--%import("js/layout.js")-->
<!-- 레이아웃과 연동될 css 파일 import -->
<!--%import("css/layout.css")-->
<!-- 메뉴를 미리 javscript object에 등록 (이 부분은 그냥 그대로 두면 됨) -->
<script type="text/javascript">
xe_layout_menu['main_menu'] = new Array();
xe_layout_menu['main_menu'][1] = new Array();
<!--@foreach($main_menu->list as $first_key => $first_menu)-->
xe_layout_menu['main_menu'][1][{$first_key}] = { "text":"{htmlspecialchars($first_menu['text'])}","href":"{htmlspecialchars($first_menu['href'])}","open_window":"{$first_menu['open_window']}","normal_btn":"{$first_menu['normal_btn']}","hover_btn":"{$first_menu['hover_btn']}","active_btn":"{$first_menu['active_btn']}","className":"first_menu", "selectedClassName":"first_menu_selected", "selected":{$first_menu['selected']?'true':'false'}};
<!--@if($first_menu['selected'])-->
xe_layout_menu['main_menu'][2] = new Array();
<!--@foreach($first_menu['list'] as $second_key => $second_menu)-->
xe_layout_menu['main_menu'][2][{$second_key}] = { "text":"{htmlspecialchars($second_menu['text'])}","href":"{htmlspecialchars($second_menu['href'])}","open_window":"{$second_menu['open_window']}","normal_btn":"{$second_menu['normal_btn']}","hover_btn":"{$second_menu['hover_btn']}","active_btn":"{$second_menu['active_btn']}","className":"second_menu", "selectedClassName":"second_menu_selected", "selected":{$second_menu['selected']?'true':'false'}};
<!--@if($second_menu['selected'])-->
xe_layout_menu['main_menu'][3] = new Array();
<!--@foreach($second_menu['list'] as $third_key => $third_menu)-->
xe_layout_menu['main_menu'][3][{$third_key}] = { "text":"{htmlspecialchars($third_menu['text'])}","href":"{htmlspecialchars($third_menu['href'])}","open_window":"{$third_menu['open_window']}","normal_btn":"{$third_menu['normal_btn']}","hover_btn":"{$third_menu['hover_btn']}","active_btn":"{$third_menu['active_btn']}","className":"third_menu", "selectedClassName":"third_menu_selected", "selected":{$third_menu['selected']?'true':'false'}};
<!--@end-->
<!--@end-->
<!--@end-->
<!--@end-->
<!--@end-->
xe_layout_menu['bottom_menu'] = new Array();
xe_layout_menu['bottom_menu'][1] = new Array();
<!--@foreach($bottom_menu->list as $first_menu)-->
xe_layout_menu['bottom_menu'][1][{$first_key}] = { "text":"{htmlspecialchars($first_menu['text'])}","href":"{htmlspecialchars($first_menu['href'])}","open_window":"{$first_menu['open_window']}","normal_btn":"{$first_menu['normal_btn']}","hover_btn":"{$first_menu['hover_btn']}","active_btn":"{$first_menu['active_btn']}","className":"bottom_menu", "selectedClassName":"bottom_menu_selected", "selected":{$first_menu['selected']?'true':'false'}};
<!--@end-->
</script>
<!-- 상단 로고 및 main_menu 1차 출력 -->
<div class="layout_top">
<div class="layout_logo">
<a href="{$layout_info->index_url}"><!--@if(!$layout_info->top_title)-->top title<!--@else-->{$layout_info->top_title}<!--@end--></a>
</div>
<div class="layout_first_menu">
<!-- main_menu 1차 시작 -->
<script type="text/javascript">
xe_print_menu('main_menu', 1);
</script>
<!-- main_menu 1차 끝 -->
</div>
</div>
<!-- 왼쪽 2차 메뉴 및 로그인과 기타 위젯 부분 -->
<div class="layout_left">
<!-- 로그인 위젯 -->
<img src="./common/tpl/images/blank.gif" class="zbxe_widget_output" widget="login_info" skin="default" colorset="normal" />
<!-- main_menu 2차 시작 -->
<div class="layout_second_menu">
<script type="text/javascript">
xe_print_menu('main_menu', 2, true);
</script>
</div>
<!--@if($grant->is_admin)-->
<!-- 관리자일 경우 레이아웃 관리 메뉴 -->
<div class="layout_admin">
<a href="{getUrl('act','dispLayoutAdminModify','layout_srl',$layout_info->layout_srl)}">{$lang->cmd_layout_management}</a> /
<a href="{getUrl('act','dispLayoutAdminEdit','layout_srl',$layout_info->layout_srl)}">{$lang->cmd_edit}</a>
</div>
<!--@end-->
</div>
<!-- 컨텐츠 출력 부분 -->
<div class="layout_content">
<div id="content">{$content}</div>
<!-- bottom_menu 메뉴 시작 -->
<div class="layout_bottom">
<div class="layout_bottom_menu">
<script type="text/javascript">
xe_print_menu('bottom_menu', 1);
</script>
</div>
</div>
</div>

Some files were not shown because too many files have changed in this diff Show more