diff --git a/.htaccess b/.htaccess index 747246c69..596c92996 100644 --- a/.htaccess +++ b/.htaccess @@ -9,7 +9,7 @@ RewriteRule ^(modules|addons|widgets)/(.+)/(conf|queries|schemas)/(.+)\.xml$ ./i RewriteCond %{SCRIPT_FILENAME} !-f RewriteRule ^(.+)/files/(member_extra_info|attach|cache|faceOff)/(.*) ./files/$2/$3 [L] RewriteCond %{SCRIPT_FILENAME} !-f -RewriteRule ^(.+)/(files|modules|common|widgets|widgetstyles|layouts|m.layouts|addons)/(.*) ./$2/$3 [L] +RewriteRule ^(.+)/(files|modules|widgets|widgetstyles|layouts|m.layouts|addons)/(.*) ./$2/$3 [L] # rss , blogAPI RewriteRule ^(rss|atom)$ ./index.php?module=rss&act=$1 [L] @@ -24,22 +24,22 @@ RewriteRule ^([a-zA-Z0-9_]+)/([0-9]+)/(.+)/trackback$ ./index.php?vid=$1&documen RewriteRule ^admin/?$ ./index.php?module=admin [L] # document permanent link -RewriteRule ^([0-9]+)$ ./index.php?document_srl=$1 [L] +RewriteRule ^([0-9]+)$ ./index.php?document_srl=$1 [L,QSA] # mid link RewriteCond %{SCRIPT_FILENAME} !-d -RewriteRule ^([a-zA-Z0-9_]+)/?$ ./index.php?mid=$1 [L] +RewriteRule ^([a-zA-Z0-9_]+)/?$ ./index.php?mid=$1 [L,QSA] # mid + document link -RewriteRule ^([a-zA-Z0-9_]+)/([0-9]+)$ ./index.php?mid=$1&document_srl=$2 [L] +RewriteRule ^([a-zA-Z0-9_]+)/([0-9]+)$ ./index.php?mid=$1&document_srl=$2 [L,QSA] # vid + mid link RewriteCond %{SCRIPT_FILENAME} !-d -RewriteRule ^([a-zA-Z0-9_]+)/([a-zA-Z0-9_]+)/?$ ./index.php?vid=$1&mid=$2 [L] +RewriteRule ^([a-zA-Z0-9_]+)/([a-zA-Z0-9_]+)/?$ ./index.php?vid=$1&mid=$2 [L,QSA] # vid + mid + document link -RewriteRule ^([a-zA-Z0-9_]+)/([a-zA-Z0-9_]+)/([0-9]+)$ ./index.php?vid=$1&mid=$2&document_srl=$3 [L] +RewriteRule ^([a-zA-Z0-9_]+)/([a-zA-Z0-9_]+)/([0-9]+)$ ./index.php?vid=$1&mid=$2&document_srl=$3 [L,QSA] # mid + entry title -RewriteRule ^([a-zA-Z0-9_]+)/entry/(.+)$ ./index.php?mid=$1&entry=$2 [L] +RewriteRule ^([a-zA-Z0-9_]+)/entry/(.+)$ ./index.php?mid=$1&entry=$2 [L,QSA] # vid + mid + entry title -RewriteRule ^([a-zA-Z0-9_]+)/([a-zA-Z0-9_]+)/entry/(.+)$ ./index.php?vid=$1&mid=$2&entry=$3 [L] +RewriteRule ^([a-zA-Z0-9_]+)/([a-zA-Z0-9_]+)/entry/(.+)$ ./index.php?vid=$1&mid=$2&entry=$3 [L,QSA] diff --git a/addons/adminlogging/adminlogging.addon.php b/addons/adminlogging/adminlogging.addon.php new file mode 100644 index 000000000..a3eb4981a --- /dev/null +++ b/addons/adminlogging/adminlogging.addon.php @@ -0,0 +1,17 @@ +is_admin == 'Y') { + $oAdminloggingController = &getController('adminlogging'); + $oAdminloggingController->insertLog($this->module, $this->act); +} +?> diff --git a/addons/adminlogging/conf/info.xml b/addons/adminlogging/conf/info.xml new file mode 100644 index 000000000..3fc29c2c4 --- /dev/null +++ b/addons/adminlogging/conf/info.xml @@ -0,0 +1,22 @@ + + + 어드민 메뉴 접근 로깅 + admin menu access logging + + admin menu에 접근한 기록을 로깅하는 애드온입니다. + + 0.1 + 2011-09-28 + + + NHN + NHN + NHN + NHN + NHN + NHN + NHN + NHN + NHN + + diff --git a/addons/autolink/autolink.addon.php b/addons/autolink/autolink.addon.php index 0463eb57a..b5910a76a 100644 --- a/addons/autolink/autolink.addon.php +++ b/addons/autolink/autolink.addon.php @@ -1,12 +1,12 @@ - + diff --git a/addons/autolink/autolink.js b/addons/autolink/autolink.js index 5b03d4bd7..ad2dcecf6 100644 --- a/addons/autolink/autolink.js +++ b/addons/autolink/autolink.js @@ -1,80 +1,80 @@ -/** - * @file autolink.js - * @brief javascript code for autolink addon - * @author NHN (developers@xpressengine.com) - */ -(function($){ - var protocol_re = '(https?|ftp|news|telnet|irc|mms)://'; - var domain_re = '(?:[\\w\\-]+\\.)+(?:[a-z]+)'; - var max_255_re = '(?:1[0-9]{2}|2[0-4][0-9]|25[0-5]|[1-9]?[0-9])'; - var ip_re = '(?:'+max_255_re+'\\.){3}'+max_255_re; - var port_re = '(?::([0-9]+))?'; - var user_re = '(?:/~[\\w-]+)?'; - var path_re = '((?:/[\\w!"$-/:-@]+)*)'; - var hash_re = '(?:#([\\w!-@]+))?'; - - var url_regex = new RegExp('('+protocol_re+'('+domain_re+'|'+ip_re+'|localhost'+')'+port_re+user_re+path_re+hash_re+')', 'ig'); - - var AutoLink = xe.createPlugin("autolink", { - targets : [], - init : function() { - this.targets = []; - }, - API_ONREADY : function() { - var thisPlugin = this; - - // extract target text nodes - this.extractTargets($('.xe_content')); - - $(this.targets).each(function(){ - thisPlugin.cast('AUTOLINK', [this]); - }); - }, - API_AUTOLINK : function(oSender, params) { - var textNode = params[0]; - if(!$(textNode).parent().length || $(textNode).parent().get(0).nodeName.toLowerCase() == 'a') return; - var content = textNode.nodeValue; - var dummy = $(''); - - content = content.replace(//g, '>'); - content = content.replace(url_regex, '$1'); - - $(textNode).before(dummy); - $(textNode).replaceWith(content); - params[0] = dummy.next('a'); - dummy.remove(); - }, - extractTargets : function(obj) { - var thisPlugin = this; - var wrap = $('.xe_content', obj); - if(wrap.length) { - this.extractTargets(wrap); - return; - } - - $(obj) - .contents() - .each(function(){ - var node_name = this.nodeName.toLowerCase(); - if($.inArray(node_name, ['a', 'pre', 'xml', 'textarea', 'input', 'select', 'option', 'code', 'script', 'style', 'iframe', 'button', 'img', 'embed', 'object', 'ins']) != -1) return; - - // FIX ME : When this meanless code wasn't executed, url_regex do not run correctly. why? - url_regex.exec(''); - - if(this.nodeType == 3) { // text node - var content = this.nodeValue; - - if(content.length < 5) return; - - if(!/(http|https|ftp|news|telnet|irc|mms):\/\//i.test(content)) return; - - thisPlugin.targets.push(this); - } else { - thisPlugin.extractTargets(this); - } - }); - } - }); - - xe.registerPlugin(new AutoLink()); +/** + * @file autolink.js + * @brief javascript code for autolink addon + * @author NHN (developers@xpressengine.com) + */ +(function($){ + var protocol_re = '(https?|ftp|news|telnet|irc|mms)://'; + var domain_re = '(?:[\\w\\-]+\\.)+(?:[a-z]+)'; + var max_255_re = '(?:1[0-9]{2}|2[0-4][0-9]|25[0-5]|[1-9]?[0-9])'; + var ip_re = '(?:'+max_255_re+'\\.){3}'+max_255_re; + var port_re = '(?::([0-9]+))?'; + var user_re = '(?:/~[\\w-]+)?'; + var path_re = '((?:/[\\w!"$-/:-@]+)*)'; + var hash_re = '(?:#([\\w!-@]+))?'; + + var url_regex = new RegExp('('+protocol_re+'('+domain_re+'|'+ip_re+'|localhost'+')'+port_re+user_re+path_re+hash_re+')', 'ig'); + + var AutoLink = xe.createPlugin("autolink", { + targets : [], + init : function() { + this.targets = []; + }, + API_ONREADY : function() { + var thisPlugin = this; + + // extract target text nodes + this.extractTargets($('.xe_content')); + + $(this.targets).each(function(){ + thisPlugin.cast('AUTOLINK', [this]); + }); + }, + API_AUTOLINK : function(oSender, params) { + var textNode = params[0]; + if(!$(textNode).parent().length || $(textNode).parent().get(0).nodeName.toLowerCase() == 'a') return; + var content = textNode.nodeValue; + var dummy = $(''); + + content = content.replace(//g, '>'); + content = content.replace(url_regex, '$1'); + + $(textNode).before(dummy); + $(textNode).replaceWith(content); + params[0] = dummy.next('a'); + dummy.remove(); + }, + extractTargets : function(obj) { + var thisPlugin = this; + var wrap = $('.xe_content', obj); + if(wrap.length) { + this.extractTargets(wrap); + return; + } + + $(obj) + .contents() + .each(function(){ + var node_name = this.nodeName.toLowerCase(); + if($.inArray(node_name, ['a', 'pre', 'xml', 'textarea', 'input', 'select', 'option', 'code', 'script', 'style', 'iframe', 'button', 'img', 'embed', 'object', 'ins']) != -1) return; + + // FIX ME : When this meanless code wasn't executed, url_regex do not run correctly. why? + url_regex.exec(''); + + if(this.nodeType == 3) { // text node + var content = this.nodeValue; + + if(content.length < 5) return; + + if(!/(http|https|ftp|news|telnet|irc|mms):\/\//i.test(content)) return; + + thisPlugin.targets.push(this); + } else { + thisPlugin.extractTargets(this); + } + }); + } + }); + + xe.registerPlugin(new AutoLink()); })(jQuery); \ No newline at end of file diff --git a/addons/autolink/conf/info.xml b/addons/autolink/conf/info.xml index 017183672..0790b8198 100644 --- a/addons/autolink/conf/info.xml +++ b/addons/autolink/conf/info.xml @@ -1,53 +1,53 @@ - - - 자동 링크 애드온 - 自動リンクアドオン - Auto Link - Auto Link - 自动链接插件 - auto vínculo addon - авто ссылка аддон - Auto-Link Addon - 自動連結 - - 글과 댓글의 내용 중 URL 문자열에 링크를 걸어주는 애드온입니다. - - - 書き込み本文とコメントに登録された内容の中、httpで始まる一般文字列に自動にリンクを貼り付け、そのリンクにマウスオーバすると、別ウィンドウ、または同一ウィンドウに開くメニュが現れるアドオンです。 - - - This addon makes a link to a string that starts with http. - - - Addon này sẽ tự động tạo ra một đường Link khi gặp chuỗi kí tự 'http' có trong bài viết. - - - 主题及评论中以http开始的字符串,自动转换为链接。并且鼠标移到链接上方时,将出现可选(新窗/本页面)提示框。 - - - Los comentarios que comienzan con http naeyongjung tema común de la cadena para vincular automáticamente a colgar el puntero del ratón sobre cada uno de los vínculos y saechang Ciudad y aparecen en el menú de add-on de decoración. - - - Комментарии, которые начинаются с http naeyongjung темой общей строки автоматически ссылку повесить мышь над каждой ссылке и saechang Сити и появляться на меню добавить-на украшения. - - - Kommentare beginnen mit http naeyongjung Thema der gemeinsamen String automatisch Link zu hängen Sie mit der Maus über die einzelnen Links und saechang Stadt und auf dem Menü des Add-On Dekoration. - - - 是種可將主題和評論內容中的URL網址字串自動轉換成連結的附加元件。 - - 0.1 - 2008-04-22 - - - NHN - NHN - NHN - NHN - NHN - NHN - NHN - NHN - NHN - - + + + 자동 링크 애드온 + 自動リンクアドオン + Auto Link Addon + Auto Link + 自动链接插件 + auto vínculo addon + Аддон авто-ссылки + Auto-Link Addon + 自動連結 + + 글과 댓글의 내용 중 URL 문자열에 링크를 걸어주는 애드온입니다. + + + 書き込み本文とコメントに登録された内容の中、httpで始まる一般文字列に自動にリンクを貼り付け、そのリンクにマウスオーバすると、別ウィンドウ、または同一ウィンドウに開くメニュが現れるアドオンです。 + + + This addon automatically transforms text URLs encountered in posts and comments into linkable URLs. + + + Addon này sẽ tự động tạo ra một đường Link khi gặp chuỗi kí tự 'http' có trong bài viết. + + + 主题及评论中以http开始的字符串,自动转换为链接。并且鼠标移到链接上方时,将出现可选(新窗/本页面)提示框。 + + + Los comentarios que comienzan con http naeyongjung tema común de la cadena para vincular automáticamente a colgar el puntero del ratón sobre cada uno de los vínculos y saechang Ciudad y aparecen en el menú de add-on de decoración. + + + Этот аддон-дополнение переводит все неактивные ссылки, которые встречаются в статьях и комментариях, в рабочие ссылки. + + + Kommentare beginnen mit http naeyongjung Thema der gemeinsamen String automatisch Link zu hängen Sie mit der Maus über die einzelnen Links und saechang Stadt und auf dem Menü des Add-On Dekoration. + + + 是種可將主題和評論內容中的URL網址字串自動轉換成連結的附加元件。 + + 0.1 + 2008-04-22 + + + NHN + NHN + NHN + NHN + NHN + NHN + NHN + NHN + NHN + + diff --git a/addons/blogapi/blogapi.addon.php b/addons/blogapi/blogapi.addon.php index 7b294e8e3..e3e241d6f 100644 --- a/addons/blogapi/blogapi.addon.php +++ b/addons/blogapi/blogapi.addon.php @@ -1,468 +1,435 @@ -domain, '', 'mid',$site_module_info->mid, 'act','api'); - - // 헤더에 rsd태그 삽입 - Context::addHtmlHeader(" ".''); - } - - // act가 api가 아니면 그냥 리턴~ - if($_REQUEST['act']!='api') return; - - // 관련 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); - - // 일부 methodname에 대한 호환 - if(in_array($method_name, array('metaWeblog.deletePost', 'metaWeblog.getUsersBlogs', 'metaWeblog.getUserInfo'))) { - $method_name = str_replace('metaWeblog.', 'blogger.', $method_name); - } - - // 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 = getFullSiteUrl(''); - $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' : - // 파일 업로드 권한 체크 - $oFileModel = &getModel('file'); - $file_module_config = $oFileModel->getFileModuleConfig($this->module_srl); - if(is_array($file_module_config->download_grant) && count($file_module_config->download_grant)>0) { - $logged_info = Context::get('logged_info'); - if($logged_info->is_admin != 'Y') { - $is_permitted = false; - for($i=0;$idownload_grant);$i++) { - $group_srl = $file_module_config->download_grant[$i]; - if($logged_info->group_list[$group_srl]) { - $is_permitted = true; - break; - } - } - if(!$is_permitted) 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.getPost' : - $document_srl = $params[0]->value->string->body; - if(!$document_srl) { - printContent( getXmlRpcFailure(1, 'no permission') ); - } else { - $oDocumentModel = &getModel('document'); - $oDocument = $oDocumentModel->getDocument($document_srl); - if(!$oDocument->isExists() || !$oDocument->isGranted()) { - printContent( getXmlRpcFailure(1, 'no permission') ); - } else { - // 카테고리를 사용하는지 확인후 사용시 카테고리 목록을 구해와서 Context에 세팅 - $category = ""; - if($oDocument->get('category_srl')) { - $oDocumentModel = &getModel('document'); - $category_list = $oDocumentModel->getCategoryList($oDocument->get('module_srl')); - if($category_list[$oDocument->get('category_srl')]) { - $category = $category_list[$oDocument->get('category_srl')]->title; - } - } - - $content = sprintf( - ''. - ''. - ''. - ''. - ''. - ''. - 'categories'. - 'dateCreated%s'. - 'description'. - 'link%s'. - 'postid%s'. - 'title'. - 'publish1'. - ''. - ''. - ''. - ''. - '', - $category, - date("Ymd", $oDocument->getRegdateTime()).'T'.date("H:i:s", $oDocument->getRegdateTime()), - $oDocument->getContent(false, false, true,false), - getFullUrl('','document_srl', $oDocument->document_srl), - $oDocument->document_srl, - $oDocument->getTitleText() - ); - printContent($content); - } - } - break; - - // 글작성 - case 'metaWeblog.newPost' : - unset($obj); - $info = $params[3]; - // 글, 제목, 카테고리 정보 구함 - for($i=0;$ivalue->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;$jstring->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, getNumberingPath($document_srl,3), $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(strval($document_srl)); - } - FileHandler::removeDir($tmp_uploaded_path); - - printContent($content); - break; - - // 글 수정 - case 'metaWeblog.editPost' : - $tmp_val = $params[0]->value->string->body; - if(!$tmp_val) $tmp_val = $params[0]->value->i4->body; - if(!$tmp_val) { - $content = getXmlRpcFailure(1, 'no permission'); - break; - } - $tmp_arr = explode('/', $tmp_val); - $document_srl = array_pop($tmp_arr); - if(!$document_srl) { - $content = getXmlRpcFailure(1, 'no permission'); - break; - } - - $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;$ivalue->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;$jstring->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, getNumberingPath($document_srl,3), $filename), $obj->content); - - $oDocumentController = &getController('document'); - $output = $oDocumentController->updateDocument($oDocument,$obj); - - if(!$output->toBool()) { - $content = getXmlRpcFailure(1, $output->getMessage()); - } else { - $content = getXmlRpcResponse(true); - 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->isExists()) { - $content = getXmlRpcFailure(1, 'not exists'); - - // 글 삭제 권한 체크 - } elseif(!$oDocument->isGranted()) { - $content = getXmlRpcFailure(1, 'no permission'); - break; - - // 삭제 - } else { - $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'; ///< 소팅 값 - $logged_info = Context::get('logged_info'); - $args->search_target = 'member_srl'; - $args->search_keyword = $logged_info->member_srl; - $output = $oDocumentModel->getDocumentList($args); - if(!$output->toBool() || !$output->data) { - $content = getXmlRpcFailure(1, 'post not founded'); - printContent($content); - } else { - $oEditorController = &getController('editor'); - - $posts = array(); - foreach($output->data as $key => $oDocument) { - $post = null; - $post->categories = array(); - $post->dateCreated = date("Ymd", $oDocument->getRegdateTime()).'T'.date("H:i:s", $oDocument->getRegdateTime()); - $post->description = htmlspecialchars($oEditorController->transComponent($oDocument->getContent(false,false,true,false))); - $post->link = $post->permaLink = getFullUrl('','document_srl',$oDocument->document_srl); - $post->postid = $oDocument->document_srl; - $post->title = htmlspecialchars($oDocument->get('title')); - $post->publish = 1; - $post->userid = $oDocument->get('user_id'); - $post->mt_allow_pings = 0; - $post->mt_allow_comments = $oDocument->allowComment()=='Y'?1:0; - $posts[] = $post; - } - $content = getXmlRpcResponse($posts); - printContent($content); - } - break; - - // 아무런 요청이 없을 경우 RSD 출력 - default : - - $homepagelink = getUrl('','mid',$this->mid); - $site_module_info = Context::get('site_module_info'); - $api_url = getFullSiteUrl($site_module_info->domain, '', 'mid',$site_module_info->mid, 'act','api'); - $content = << - - - XpressEngine - http://www.xpressengine.com/ - {$homepagelink} - - - - - -RSDContent; - printContent($content); - break; - } - } -?> +domain, '', 'mid',$site_module_info->mid, 'act','api'); + // Insert rsd tag into the header + Context::addHtmlHeader(" ".''); +} +// If act isnot api, just return +if($_REQUEST['act']!='api') return; +// Read func file +require_once('./addons/blogapi/blogapi.func.php'); +// xmlprc parsing +// Parse the requested 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); +// Compatible with some of methodname +if(in_array($method_name, array('metaWeblog.deletePost', 'metaWeblog.getUsersBlogs', 'metaWeblog.getUserInfo'))) { + $method_name = str_replace('metaWeblog.', 'blogger.', $method_name); +} +// Delete the first argument if it is blogger.deletePost +if($method_name == 'blogger.deletePost') array_shift($params); +// Get user_id, password and attempt log-in +$user_id = trim($params[1]->value->string->body); +$password = trim($params[2]->value->string->body); +// Before executing the module, authentication is processed. +if($called_position == 'before_module_init') { + // Attempt log-in by using member controller + if($user_id && $password) { + $oMemberController = &getController('member'); + $output = $oMemberController->doLogin($user_id, $password); + // If login fails, an error message appears + if(!$output->toBool()) { + $content = getXmlRpcFailure(1, $output->getMessage()); + printContent($content); + } + } else { + $content = getXmlRpcFailure(1, 'not logged'); + printContent($content); + } +} +// Before module processing, handle requests from blogapi tool and then terminate. +if($called_position == 'before_module_proc') { + // Check writing permission + if(!$this->grant->write_document) { + printContent( getXmlRpcFailure(1, 'no permission') ); + } + // Get information of the categories + $oDocumentModel = &getModel('document'); + $category_list = $oDocumentModel->getCategoryList($this->module_srl); + // Specifies a temporary file storage + $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) { + // Blog information + case 'blogger.getUsersBlogs' : + $obj->url = getFullSiteUrl(''); + $obj->blogid = $this->mid; + $obj->blogName = $this->module_info->browser_title; + $blog_list = array($obj); + + $content = getXmlRpcResponse($blog_list); + printContent($content); + break; + // Return a list of categories + 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; + // Upload file + case 'metaWeblog.newMediaObject' : + // Check a file upload permission + $oFileModel = &getModel('file'); + $file_module_config = $oFileModel->getFileModuleConfig($this->module_srl); + if(is_array($file_module_config->download_grant) && count($file_module_config->download_grant)>0) { + $logged_info = Context::get('logged_info'); + if($logged_info->is_admin != 'Y') { + $is_permitted = false; + for($i=0;$idownload_grant);$i++) { + $group_srl = $file_module_config->download_grant[$i]; + if($logged_info->group_list[$group_srl]) { + $is_permitted = true; + break; + } + } + if(!$is_permitted) 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; + // Get posts + case 'metaWeblog.getPost' : + $document_srl = $params[0]->value->string->body; + if(!$document_srl) { + printContent( getXmlRpcFailure(1, 'no permission') ); + } else { + $oDocumentModel = &getModel('document'); + $oDocument = $oDocumentModel->getDocument($document_srl); + if(!$oDocument->isExists() || !$oDocument->isGranted()) { + printContent( getXmlRpcFailure(1, 'no permission') ); + } else { + // Get a list of categories and set Context + $category = ""; + if($oDocument->get('category_srl')) { + $oDocumentModel = &getModel('document'); + $category_list = $oDocumentModel->getCategoryList($oDocument->get('module_srl')); + if($category_list[$oDocument->get('category_srl')]) { + $category = $category_list[$oDocument->get('category_srl')]->title; + } + } + + $content = sprintf( + ''. + ''. + ''. + ''. + ''. + ''. + 'categories'. + 'dateCreated%s'. + 'description'. + 'link%s'. + 'postid%s'. + 'title'. + 'publish1'. + ''. + ''. + ''. + ''. + '', + $category, + date("Ymd", $oDocument->getRegdateTime()).'T'.date("H:i:s", $oDocument->getRegdateTime()), + $oDocument->getContent(false, false, true,false), + getFullUrl('','document_srl', $oDocument->document_srl), + $oDocument->document_srl, + $oDocument->getTitleText() + ); + printContent($content); + } + } + break; + // Write a new post + case 'metaWeblog.newPost' : + unset($obj); + $info = $params[3]; + // Get information of post, title, and category + for($i=0;$ivalue->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;$jstring->body; + } + if(count($tag_list)) $obj->tags = implode(',',$tag_list); + break; + } + + } + // Set document srl + $document_srl = getNextSequence(); + $obj->document_srl = $document_srl; + $obj->module_srl = $this->module_srl; + // Attachment + 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, getNumberingPath($document_srl,3), $filename), $obj->content); + + $oDocumentController = &getController('document'); + $obj->commentStatus = 'ALLOW'; + $obj->allow_trackback = 'Y'; + $output = $oDocumentController->insertDocument($obj); + + if(!$output->toBool()) { + $content = getXmlRpcFailure(1, $output->getMessage()); + } else { + $content = getXmlRpcResponse(strval($document_srl)); + } + FileHandler::removeDir($tmp_uploaded_path); + + printContent($content); + break; + // Edit post + case 'metaWeblog.editPost' : + $tmp_val = $params[0]->value->string->body; + if(!$tmp_val) $tmp_val = $params[0]->value->i4->body; + if(!$tmp_val) { + $content = getXmlRpcFailure(1, 'no permission'); + break; + } + $tmp_arr = explode('/', $tmp_val); + $document_srl = array_pop($tmp_arr); + if(!$document_srl) { + $content = getXmlRpcFailure(1, 'no permission'); + break; + } + + $oDocumentModel = &getModel('document'); + $oDocument = $oDocumentModel->getDocument($document_srl); + // Check if a permission to modify a document is granted + if(!$oDocument->isGranted()) { + $content = getXmlRpcFailure(1, 'no permission'); + break; + } + + $obj = $oDocument->getObjectVars(); + + $info = $params[3]; + // Get information of post, title, and category + for($i=0;$ivalue->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;$jstring->body; + } + if(count($tag_list)) $obj->tags = implode(',',$tag_list); + break; + } + + } + // Document srl + $obj->document_srl = $document_srl; + $obj->module_srl = $this->module_srl; + // Attachment + 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, getNumberingPath($document_srl,3), $filename), $obj->content); + + $oDocumentController = &getController('document'); + $output = $oDocumentController->updateDocument($oDocument,$obj); + + if(!$output->toBool()) { + $content = getXmlRpcFailure(1, $output->getMessage()); + } else { + $content = getXmlRpcResponse(true); + FileHandler::removeDir($tmp_uploaded_path); + } + + printContent($content); + break; + // Delete the post + case 'blogger.deletePost' : + $tmp_val = $params[0]->value->string->body; + $tmp_arr = explode('/', $tmp_val); + $document_srl = array_pop($tmp_arr); + // Get a document + $oDocumentModel = &getModel('document'); + $oDocument = $oDocumentModel->getDocument($document_srl); + // If the document exists + if(!$oDocument->isExists()) { + $content = getXmlRpcFailure(1, 'not exists'); + // Check if a permission to delete a document is granted + } elseif(!$oDocument->isGranted()) { + $content = getXmlRpcFailure(1, 'no permission'); + break; + // Delete + } else { + $oDocumentController = &getController('document'); + $output = $oDocumentController->deleteDocument($document_srl); + if(!$output->toBool()) $content = getXmlRpcFailure(1, $output->getMessage()); + else $content = getXmlRpcResponse(true); + } + + printContent($content); + break; + // Get recent posts + case 'metaWeblog.getRecentPosts' : + // Options to get a list + $args->module_srl = $this->module_srl; // /< module_srl of the current module + $args->page = 1; + $args->list_count = 20; + $args->sort_index = 'list_order'; // /< Sorting values + $logged_info = Context::get('logged_info'); + $args->search_target = 'member_srl'; + $args->search_keyword = $logged_info->member_srl; + $output = $oDocumentModel->getDocumentList($args); + if(!$output->toBool() || !$output->data) { + $content = getXmlRpcFailure(1, 'post not founded'); + printContent($content); + } else { + $oEditorController = &getController('editor'); + + $posts = array(); + foreach($output->data as $key => $oDocument) { + $post = null; + $post->categories = array(); + $post->dateCreated = date("Ymd", $oDocument->getRegdateTime()).'T'.date("H:i:s", $oDocument->getRegdateTime()); + $post->description = htmlspecialchars($oEditorController->transComponent($oDocument->getContent(false,false,true,false))); + $post->link = $post->permaLink = getFullUrl('','document_srl',$oDocument->document_srl); + $post->postid = $oDocument->document_srl; + $post->title = htmlspecialchars($oDocument->get('title')); + $post->publish = 1; + $post->userid = $oDocument->get('user_id'); + $post->mt_allow_pings = 0; + $post->mt_allow_comments = $oDocument->allowComment()?1:0; + $posts[] = $post; + } + $content = getXmlRpcResponse($posts); + printContent($content); + } + break; + // Display RSD if there is no request + default : + $homepagelink = getUrl('','mid',$this->mid); + $site_module_info = Context::get('site_module_info'); + $api_url = getFullSiteUrl($site_module_info->domain, '', 'mid',$site_module_info->mid, 'act','api'); + $content = << + + + XpressEngine + http://www.xpressengine.com/ + {$homepagelink} + + + + + +RSDContent; + printContent($content); + break; + } +} +?> diff --git a/addons/blogapi/blogapi.func.php b/addons/blogapi/blogapi.func.php index 511ac44e3..2d289bf40 100644 --- a/addons/blogapi/blogapi.func.php +++ b/addons/blogapi/blogapi.func.php @@ -1,69 +1,66 @@ -\n\n\nfaultCode\n%d\n\n\nfaultString\n%s\n\n\n\n", - $error, - htmlspecialchars($message) - ); - } - - // 결과 표시 - function getXmlRpcResponse($params) { - $buff = ''."\n"; - $buff .= _getEncodedVal($params); - $buff .= "\n\n"; - - return $buff; - } - - // 인코딩 처리 - function _getEncodedVal($val, $is_sub_set = false) { - if(is_int($val)) $buff = sprintf("%d", $val); - elseif(is_string($val)&&preg_match('/^([0-9]+)T([0-9\:]+)$/', $val)) $buff = sprintf("%s\n", $val); - elseif(is_double($val)) $buff = sprintf("%f", $val); - elseif(is_bool($val)) $buff = sprintf("%d", $val?1:0); - elseif(is_object($val)) { - $values = get_object_vars($val); - $val_count = count($values); - $buff = ""; - foreach($values as $k => $v) { - $buff .= sprintf("\n%s\n%s\n", htmlspecialchars($k), _getEncodedVal($v, true)); - } - $buff .= "\n"; - } elseif(is_array($val)) { - $val_count = count($val); - $buff = "\n"; - for($i=0;$i<$val_count;$i++) { - $buff .= _getEncodedVal($val[$i], true); - } - $buff .= "\n"; - } else { - $buff = sprintf("%s\n", $val); - } - if(!$is_sub_set) return sprintf("\n%s", $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; - Context::close(); - exit(); - } -?> +\n\n\nfaultCode\n%d\n\n\nfaultString\n%s\n\n\n\n", + $error, + htmlspecialchars($message) + ); + } + // Display results + function getXmlRpcResponse($params) { + $buff = ''."\n"; + $buff .= _getEncodedVal($params); + $buff .= "\n\n"; + + return $buff; + } + // Encoding + function _getEncodedVal($val, $is_sub_set = false) { + if(is_int($val)) $buff = sprintf("%d", $val); + elseif(is_string($val)&&preg_match('/^([0-9]+)T([0-9\:]+)$/', $val)) $buff = sprintf("%s\n", $val); + elseif(is_double($val)) $buff = sprintf("%f", $val); + elseif(is_bool($val)) $buff = sprintf("%d", $val?1:0); + elseif(is_object($val)) { + $values = get_object_vars($val); + $val_count = count($values); + $buff = ""; + foreach($values as $k => $v) { + $buff .= sprintf("\n%s\n%s\n", htmlspecialchars($k), _getEncodedVal($v, true)); + } + $buff .= "\n"; + } elseif(is_array($val)) { + $val_count = count($val); + $buff = "\n"; + for($i=0;$i<$val_count;$i++) { + $buff .= _getEncodedVal($val[$i], true); + } + $buff .= "\n"; + } else { + $buff = sprintf("%s\n", $val); + } + if(!$is_sub_set) return sprintf("\n%s", $buff); + return $buff; + } + // Display the result + 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; + Context::close(); + exit(); + } +?> diff --git a/addons/blogapi/conf/info.xml b/addons/blogapi/conf/info.xml index f533cd4a6..90ce15f96 100644 --- a/addons/blogapi/conf/info.xml +++ b/addons/blogapi/conf/info.xml @@ -1,80 +1,80 @@ - - - BlogAPI 애드온 - BlogAPIアドオン - BlogAPI - Addon for BlogAPI - BlogAPI Addon - Addon für BlogAPI - Addon para BlogAPI - Аддон для BlogAPI - 部落格 API - - metaWeblog를 지원하는 blogApi애드온입니다. - 사용으로 설정하시면 각 모듈마다 RSD 태그를 노출합니다. - api의 주소는 http://설치주소/모듈명/api 입니다. - 사용으로 하셔야 RSD태그 및 api가 동작을 합니다. - - - MetaWeblogをサポートするBlog APIアドオンです。 - 「使用する」にチェックすると各モジュールごとにRSDのアドレスを表示します。 - APIのアドレスは「http://インストールURL/モジュール名/api」です。 - 「使用する」に設定してから、RSDタグ、およびAPIが作動します。 - - - 支持metaWeblog的 blogApi插件。 - 设置为"启用"时,会使每个模块都会显示RSD标签。 - api地址为http://安装地址/模块名/api。 - 把状态设置为"使用"时,才会激活RSD标签及api。 - - - This blogApi addon supports metaWeblog. - By using this option, it lets the RSD tag to be exposed to each module. - URL to the api is http://setup_path/module_name/api. - RSD tag and the api will work only if u use this addon. - - - Addon BlogAPI này hỗ trợ metaWeblog.. - Bằng việc sử dụng tùy chọn này, Tag RSD sẽ được hiển thị đến mỗi Module. - URL cho API có dạng http://setup_path/module_name/api. - RSD Tag và API chỉ làm việc khi Addon này được kích hoạt. - - - Diese blogApi addon metaWeblog unterstützt. - Durch die Verwendung dieser Option, die es ermöglicht RSD Tag ausgesetzt werden jedes Modul. - URL der api ist http://setup_path/module_name/api. - RSD-Tag und dem API arbeiten und nur dann, wenn Sie über dieses Addon. - - - Este blogApi addon soporta el metaWeblog. - Si seleccionas la optión usar, cada módulo entregará la etiqueta RSD. - La dirección de api es http://dirección de la instalación/nombre de módulo/api. - Sólo si seleccionas la opción usar, funcionará la etiqueta RSD y api. - - - Этот blogApi аддон поддерживает metaWeblog. - Используя этот аддон, RSD тег становится доступным для каждого модуля. - URL для api - http://setup_path/module_name/api. - тег RSD и api работают только при включенном аддоне. - - - 支援 MetaWeblog 的部落格 API 附加元件。 - 設置成"啟用"時,會使每個模組都顯示 RSD 圖示。 - API網址是 http://安裝位置/模組名稱/api。 - 將狀態設置成"啟用"時,才可使用 RSD 和 API - - 0.1 - 2007-02-28 - - - NHN - NHN - NHN - NHN - NHN - NHN - NHN - NHN - NHN - - + + + BlogAPI 애드온 + BlogAPIアドオン + BlogAPI + Addon for BlogAPI + BlogAPI Addon + Addon für BlogAPI + Addon para BlogAPI + Аддон для BlogAPI + 部落格 API + + metaWeblog를 지원하는 blogApi애드온입니다. + 사용으로 설정하시면 각 모듈마다 RSD 태그를 노출합니다. + api의 주소는 http://설치주소/모듈명/api 입니다. + 사용으로 하셔야 RSD태그 및 api가 동작을 합니다. + + + MetaWeblogをサポートするBlog APIアドオンです。 + 「使用する」にチェックすると各モジュールごとにRSDのアドレスを表示します。 + APIのアドレスは「http://インストールURL/モジュール名/api」です。 + 「使用する」に設定してから、RSDタグ、およびAPIが作動します。 + + + 支持metaWeblog的 blogApi插件。 + 设置为"启用"时,会使每个模块都会显示RSD标签。 + api地址为http://安装地址/模块名/api。 + 把状态设置为"使用"时,才会激活RSD标签及api。 + + + This blogAPI addon supports metaWeblog. + By using this option, it lets the RSD tag to be exposed to each module. + URL to the API is http://setup_path/module_name/api. + RSD tag and the api will work only if you use this addon. + + + Addon BlogAPI này hỗ trợ metaWeblog.. + Bằng việc sử dụng tùy chọn này, Tag RSD sẽ được hiển thị đến mỗi Module. + URL cho API có dạng http://setup_path/module_name/api. + RSD Tag và API chỉ làm việc khi Addon này được kích hoạt. + + + Diese blogApi addon metaWeblog unterstützt. + Durch die Verwendung dieser Option, die es ermöglicht RSD Tag ausgesetzt werden jedes Modul. + URL der api ist http://setup_path/module_name/api. + RSD-Tag und dem API arbeiten und nur dann, wenn Sie über dieses Addon. + + + Este blogApi addon soporta el metaWeblog. + Si seleccionas la optión usar, cada módulo entregará la etiqueta RSD. + La dirección de api es http://dirección de la instalación/nombre de módulo/api. + Sólo si seleccionas la opción usar, funcionará la etiqueta RSD y api. + + + Этот blogApi аддон поддерживает metaWeblog. + Используя этот аддон, RSD тег становится доступным для каждого модуля. + URL для api - http://setup_path/module_name/api. + тег RSD и api работают только при включенном аддоне. + + + 支援 MetaWeblog 的部落格 API 附加元件。 + 設置成"啟用"時,會使每個模組都顯示 RSD 圖示。 + API網址是 http://安裝位置/模組名稱/api。 + 將狀態設置成"啟用"時,才可使用 RSD 和 API + + 0.1 + 2007-02-28 + + + NHN + NHN + NHN + NHN + NHN + NHN + NHN + NHN + NHN + + diff --git a/addons/captcha/captcha.addon.php b/addons/captcha/captcha.addon.php index 83dc587f1..ad6520eec 100644 --- a/addons/captcha/captcha.addon.php +++ b/addons/captcha/captcha.addon.php @@ -1,263 +1,326 @@ -addon_info = $addon_info; - } - - function before_module_proc() - { - if($this->addon_info->act_type == 'everytime' && $_SESSION['captcha_authed']) { - unset($_SESSION['captcha_authed']); - } - } - - function before_module_init(&$ModuleHandler) - { - $logged_info = Context::get('logged_info'); - if($logged_info->is_admin == 'Y' || $logged_info->is_site_admin) return false; - if($this->addon_info->target != 'all' && Context::get('is_logged')) return false; - if($_SESSION['captcha_authed']) return false; - - $target_acts = array('procBoardInsertDocument','procBoardInsertComment','procIssuetrackerInsertIssue','procIssuetrackerInsertHistory','procTextyleInsertComment'); - if($this->addon_info->apply_find_account=='apply') $target_acts[] = 'procMemberFindAccount'; - if($this->addon_info->apply_resend_auth_mail=='apply') $target_acts[] = 'procMemberResendAuthMail'; - if($this->addon_info->apply_signup=='apply') $target_acts[] = 'procMemberInsert'; - - if(Context::getRequestMethod()!='XMLRPC' && Context::getRequestMethod()!=='JSON') - { - Context::addHtmlHeader(''); - Context::addJsFile('./addons/captcha/captcha.js',false, '', null, 'body'); - } - - // 게시판/ 이슈트래커의 글쓰기/댓글쓰기 액션 호출시 세션 비교 - if(!$_SESSION['captcha_authed'] && in_array(Context::get('act'), $target_acts)) { - Context::loadLang('./addons/captcha/lang'); - $ModuleHandler->error = "captcha_denied"; - } - - return true; - } - - function before_module_init_setCaptchaSession() - { - if($_SESSION['captcha_authed']) return false; - - // 언어파일 로드 - Context::loadLang(_XE_PATH_.'addons/captcha/lang'); - - // 키워드 생성 - $arr = range('A','Y'); - shuffle($arr); - $arr = array_slice($arr,0,6); - $_SESSION['captcha_keyword'] = join('', $arr); - - $target = Context::getLang('target_captcha'); - 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"); - printf("\r\n 0\r\n success\r\n \r\n \r\n \r\n \r\n \r\n " - ,Context::getLang('about_captcha') - ,Context::getLang('captcha_reload') - ,Context::getLang('captcha_play') - ,Context::getLang('cmd_input') - ,Context::getLang('cmd_cancel') - ); - Context::close(); - exit(); - } - - function before_module_init_captchaImage() - { - if($_SESSION['captcha_authed']) return false; - - $keyword = $_SESSION['captcha_keyword']; - $im = $this->createCaptchaImage($keyword); - - header("Cache-Control: "); - header("Pragma: "); - header("Content-Type: image/png"); - - imagepng($im); - imagedestroy($im); - - Context::close(); - exit(); - } - - function createCaptchaImage($string) - { - $arr = array(); - for($i=0,$c=strlen($string);$i<$c;$i++) $arr[] = $string{$i}; - - // 글자 하나 사이즈 - $w = 18; - $h = 25; - - // 글자 수 - $c = count($arr); - - // 글자 이미지 - $im = array(); - - // 총사이즈로 바탕 이미지 생성 - $im[] = imagecreate(($w+2)*count($arr), $h); - - $deg = range(-30,30); - shuffle($deg); - - // 글자별 이미지 생성 - foreach($arr as $i => $str) - { - $im[$i+1] = @imagecreate($w, $h); - $background_color = imagecolorallocate($im[$i+1], 255, 255, 255); - $text_color = imagecolorallocate($im[$i+1], 0, 0, 0); - - // 글자폰트(사이즈) 조절 - $ran = range(1,20); - shuffle($ran); - - if(function_exists('imagerotate')) - { - imagestring($im[$i+1], (array_pop($ran)%3)+3, 2, (array_pop($ran)%8), $str, $text_color); - $im[$i+1] = imagerotate($im[$i+1], array_pop($deg), 0); - - $background_color = imagecolorallocate($im[$i+1], 255, 255, 255); - imagecolortransparent($im[$i+1], $background_color); - } - else - { - imagestring($im[$i+1], (array_pop($ran)%3)+3, 2, (array_pop($ran)%4), $str, $text_color); - } - } - - // 각글자 이미지를 합침 - for($i=1;$icreateCaptchaAudio($keyword); - - header('Content-type: audio/mpeg'); - header("Content-Disposition: attachment; filename=\"captcha_audio.mp3\""); - header('Cache-Control: no-store, no-cache, must-revalidate'); - header('Expires: Sun, 1 Jan 2000 12:00:00 GMT'); - header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . 'GMT'); - header('Content-Length: ' . strlen($data)); - - echo $data; - Context::close(); - exit(); - } - - function createCaptchaAudio($string) - { - $data = ''; - $_audio = './addons/captcha/audio/F_%s.mp3'; - for($i=0,$c=strlen($string);$i<$c;$i++) - { - $_data = FileHandler::readFile(sprintf($_audio, $string{$i})); - - $start = rand(5, 68); // 해더 4바이트, 데이터 영역 64바이트 정도 랜덤하게 시작 - $datalen = strlen($_data) - $start - 256; // 마지막 unchanged 256 바이트 - - for($j=$start;$j<$datalen;$j+=64) - { - $ch = ord($_data{$j}); - if($ch<9 || $ch>119) continue; - $_data{$j} = chr($ch+rand(-8,8)); - } - - $data .= $_data; - } - - return $data; - } - - function before_module_init_captchaCompare() - { - if($_SESSION['captcha_authed']) return false; - - if(strtoupper($_SESSION['captcha_keyword']) == strtoupper(Context::get('secret_text'))) $_SESSION['captcha_authed'] = true; - else unset($_SESSION['captcha_authed']); - - 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("\r\n0\r\nsuccess\r\n"); - - Context::close(); - exit(); - } - } - - $GLOBALS['__AddonCaptcha__'] = new AddonCaptcha; - $GLOBALS['__AddonCaptcha__']->setInfo($addon_info); - } - - $oAddonCaptcha = &$GLOBALS['__AddonCaptcha__']; - - if(method_exists(&$oAddonCaptcha, $called_position)) - { - if(!call_user_func(array(&$oAddonCaptcha, $called_position), &$this)) return false; - } - - $addon_act = Context::get('captcha_action'); - if($addon_act && method_exists(&$oAddonCaptcha, $called_position.'_'.$addon_act)) - { - if(!call_user_func(array(&$oAddonCaptcha, $called_position.'_'.$addon_act), &$this)) return false; - } - -?> +addon_info = $addon_info; + } + + function before_module_proc() + { + if($this->addon_info->act_type == 'everytime' && $_SESSION['captcha_authed']) { + unset($_SESSION['captcha_authed']); + } + } + + function before_module_init(&$ModuleHandler) + { + $logged_info = Context::get('logged_info'); + if($logged_info->is_admin == 'Y' || $logged_info->is_site_admin) return false; + if($this->addon_info->target != 'all' && Context::get('is_logged')) return false; + if($_SESSION['captcha_authed']) return false; + + $type = Context::get('captchaType'); + + $target_acts = array('procBoardInsertDocument','procBoardInsertComment','procIssuetrackerInsertIssue','procIssuetrackerInsertHistory','procTextyleInsertComment'); + if($this->addon_info->apply_find_account=='apply') $target_acts[] = 'procMemberFindAccount'; + if($this->addon_info->apply_resend_auth_mail=='apply') $target_acts[] = 'procMemberResendAuthMail'; + if($this->addon_info->apply_signup=='apply') $target_acts[] = 'procMemberInsert'; + + if(Context::getRequestMethod()!='XMLRPC' && Context::getRequestMethod()!=='JSON') + { + if($type == 'inline') { + $this->compareCaptcha(); + } else { + Context::addHtmlHeader(''); + Context::loadFile(array('./addons/captcha/captcha.js', 'body', '', null), true); + } + } + + // compare session when calling actions such as writing a post or a comment on the board/issue tracker module + if(!$_SESSION['captcha_authed'] && in_array(Context::get('act'), $target_acts)) { + Context::loadLang('./addons/captcha/lang'); + $ModuleHandler->error = "captcha_denied"; + } + + return true; + } + + function createKeyword() + { + $type = Context::get('captchaType'); + if ($type == 'inline' && $_SESSION['captcha_keyword']) return; + + $arr = range('A','Y'); + shuffle($arr); + $arr = array_slice($arr,0,6); + $_SESSION['captcha_keyword'] = join('', $arr); + } + + function before_module_init_setCaptchaSession() + { + if($_SESSION['captcha_authed']) return false; + // Load language files + + Context::loadLang(_XE_PATH_.'addons/captcha/lang'); + // Generate keywords + + $this->createKeyword(); + + $target = Context::getLang('target_captcha'); + 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"); + printf("\r\n 0\r\n success\r\n \r\n \r\n \r\n \r\n \r\n " + ,Context::getLang('about_captcha') + ,Context::getLang('captcha_reload') + ,Context::getLang('captcha_play') + ,Context::getLang('cmd_input') + ,Context::getLang('cmd_cancel') + ); + Context::close(); + exit(); + } + + function before_module_init_captchaImage() + { + if($_SESSION['captcha_authed']) return false; + if(Context::get('renew')) $this->createKeyword(); + + $keyword = $_SESSION['captcha_keyword']; + $im = $this->createCaptchaImage($keyword); + + header("Cache-Control: "); + header("Pragma: "); + header("Content-Type: image/png"); + + imagepng($im); + imagedestroy($im); + + Context::close(); + exit(); + } + + function createCaptchaImage($string) + { + $arr = array(); + for($i=0,$c=strlen($string);$i<$c;$i++) $arr[] = $string{$i}; + // Font site + + $w = 18; + $h = 25; + // Character length + + $c = count($arr); + // Character image + + $im = array(); + // Create an image by total size + + $im[] = imagecreate(($w+2)*count($arr), $h); + + $deg = range(-30,30); + shuffle($deg); + // Create an image for each letter + + foreach($arr as $i => $str) + { + $im[$i+1] = @imagecreate($w, $h); + $background_color = imagecolorallocate($im[$i+1], 255, 255, 255); + $text_color = imagecolorallocate($im[$i+1], 0, 0, 0); + // Control font size + + $ran = range(1,20); + shuffle($ran); + + if(function_exists('imagerotate')) + { + imagestring($im[$i+1], (array_pop($ran)%3)+3, 2, (array_pop($ran)%8), $str, $text_color); + $im[$i+1] = imagerotate($im[$i+1], array_pop($deg), 0); + + $background_color = imagecolorallocate($im[$i+1], 255, 255, 255); + imagecolortransparent($im[$i+1], $background_color); + } + else + { + imagestring($im[$i+1], (array_pop($ran)%3)+3, 2, (array_pop($ran)%4), $str, $text_color); + } + } + + // Combine images of each character + + for($i=1;$icreateCaptchaAudio($keyword); + + header('Content-type: audio/mpeg'); + header("Content-Disposition: attachment; filename=\"captcha_audio.mp3\""); + header('Cache-Control: no-store, no-cache, must-revalidate'); + header('Expires: Sun, 1 Jan 2000 12:00:00 GMT'); + header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . 'GMT'); + header('Content-Length: ' . strlen($data)); + + echo $data; + Context::close(); + exit(); + } + + function createCaptchaAudio($string) + { + $data = ''; + $_audio = './addons/captcha/audio/F_%s.mp3'; + for($i=0,$c=strlen($string);$i<$c;$i++) + { + $_data = FileHandler::readFile(sprintf($_audio, $string{$i})); + + $start = rand(5, 68); // Random start in 4-byte header and 64 byte data + $datalen = strlen($_data) - $start - 256; // Last unchanged 256 bytes + + for($j=$start;$j<$datalen;$j+=64) + { + $ch = ord($_data{$j}); + if($ch<9 || $ch>119) continue; + $_data{$j} = chr($ch+rand(-8,8)); + } + + $data .= $_data; + } + + return $data; + } + + function compareCaptcha() + { + if($_SESSION['captcha_authed']) return true; + + if(strtoupper($_SESSION['captcha_keyword']) == strtoupper(Context::get('secret_text'))) { + $_SESSION['captcha_authed'] = true; + return true; + } + + unset($_SESSION['captcha_authed']); + + return false; + } + + function before_module_init_captchaCompare() + { + if(!$this->compareCaptcha()) return false; + + 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("\r\n0\r\nsuccess\r\n"); + + Context::close(); + exit(); + } + + function inlineDisplay() + { + unset($_SESSION['captcha_authed']); + $this->createKeyword(); + + $swfURL = getUrl().'addons/captcha/swf/play.swf'; + Context::unloadFile('./addons/captcha/captcha.js'); + Context::loadFile(array('./addons/captcha/inline_captcha.js','body')); + + global $lang; + + $tags=<< + + + + + + + + + + +
+ + +EOD; + $tags = sprintf($tags, getUrl('captcha_action','captchaImage', 'rand', mt_rand(10000, 99999)) + , $swfURL + , $swfURL + , $lang->reload + , $lang->play); + return $tags; + } + } + + $GLOBALS['__AddonCaptcha__'] = new AddonCaptcha; + $GLOBALS['__AddonCaptcha__']->setInfo($addon_info); + Context::set('oCaptcha', &$GLOBALS['__AddonCaptcha__']); + } + + $oAddonCaptcha = &$GLOBALS['__AddonCaptcha__']; + + if(method_exists($oAddonCaptcha, $called_position)) + { + if(!call_user_func_array(array(&$oAddonCaptcha, $called_position), array(&$this))) return false; + } + + $addon_act = Context::get('captcha_action'); + if($addon_act && method_exists($oAddonCaptcha, $called_position.'_'.$addon_act)) + { + if(!call_user_func_array(array(&$oAddonCaptcha, $called_position.'_'.$addon_act), array(&$this))) return false; + } + +?> diff --git a/addons/captcha/conf/info.xml b/addons/captcha/conf/info.xml index e2353c16b..904baabf2 100644 --- a/addons/captcha/conf/info.xml +++ b/addons/captcha/conf/info.xml @@ -1,222 +1,221 @@ - - - Captcha 애드온 - CAPTCHA - Captcha Addon - 验证码插件 - Captchaアドオン - Аддон Captcha - 圖形驗證 - - 프로그램 글 등록기를 막기 위해 게시판/ issueTracker에서 글/ 댓글을 입력하려 할 때 알파벳을 입력해야 글/댓글이 입력되는 애드온 입니다. - 로그인하지 않은 경우에만 해당됩니다. - - - To block spam written by programs, let users to choose a suitable image to text when writing a posting or comment. - This addon applies only to not-logged-in users. - - - Addon này tạo ra một hình ảnh xác nhận khi đăng kí, gửi bài, hay viết bình luận nếu thành viên không đăng nhập. - Addon này chỉ hoạt động khi được kích hoạt. - - - 为了解决互联网垃圾而开发的验证码机制。 - 非登录用户发布话题或评论时将会弹出验证图片选择框,选择正确的图片才可以正常发布(适用于版面/issueTracker)。 - - - ボット(bot)がプログラムによるスパム行為を防ぐために、掲示板/issueTrackerで書き込み・コメントを登録する際、ランダムな文字や数字の列を画面に表示し、表示されたものと一致した情報を入力した時、登録が出来るようにするアドオンです。 - ログインしてない時だけ、動作します。 - - - To block spam written by programs, let users to choose a suitable image to text when writing a posting or comment. - This addon applies only to not-logged-in users. - - - 可防止機器人程式的垃圾留言,非用戶要發表主題或回覆時,必須要輸入正確圖片中所顯示的文字才能發表。 - - 1.0 - 2010-08-19 - - - NHN - NHN - NHN - NHN - NHN - NHN - NHN - - - - - Captcha 표시 대상 - 应用对象 - Captchaを表示する対象 - 選擇目標 - Captcha Target - Captcha Target - Mục tiêu Captcha hiển thị - 글/댓글 등록시 captcha가 동작할 대상을 정할 수 있습니다. 관리자는 무조건 제외됩니다 - 可以指定验证码应用对象(管理员除外)。 - 管理者を除き、書き込み・コメントを入力する際にcaptchaイメージを見せる対象を設定します。 - 除了管理員,可以選擇圖形驗證應用的對象。 - You may specify targets CAPTCHA work. It's not applied when administrator writes. - You may specify targets CAPTCHA work. It's not applied when administrator writes. - Khi gửi bài, bình luận, Capcha sẽ hiển thị để xác nhận hành động của người sử dụng. Chức năng này không hoạt động với người quản lý. - - 로그인하지 않은 사용자 - 非登录用户 - ログインしてないユーザー - 非用戶 - Not logged-in users - Not logged-in users - Người dùng chưa đăng nhập - - - 모든 사용자 - 所有用户 - すべてのユーザー - 所有用戶 - All users - All users - Tất cả mọi người - - - - 동작 방식 - 验证方式 - 動作方式 - 驗證模式 - How it works - How it works - Sử dụng - "1번만 동작"을 선택하시면 1번만 동작후 상태를 저장해서 다음부터 물어보지 않고 그렇지 않으면 매번 물어보게 됩니다 - "一次"就是每个IP只出现一次验证。 - 「1回だけ表示」を選択すると、最初だけ動作した後、その情報を記憶して次回からはCaptchaを見せないようにします。また、もう一つのオプションは毎回Captchaを表示します。 - 選擇"單次",下次不會再顯示;選擇"每次"則會一直顯示。 - If you choose "Once", CAPTCHA works only once for the user by storing status. Otherwise, this addon would show an image every time the user writes. - If you choose "Once", CAPTCHA works only once for the user by storing status. Otherwise, this addon would show an image every time the user writes. - Nếu chọn "Chỉ một lần" thì sau lần hiển thị đó Capcha sẽ không hiển thị với người sử dụng đó nữa. - - 1번만 동작 - 一次 - 1回だけ表示 - 單次 - Chỉ một lần - once - 1 раз - - - 매번 동작 - 每次 - 毎回表示 - 每次 - every time - каждый раз - Luôn sử dụng - - - - 비밀번호 찾기 적용 - 应用到查找密码功能 - 비밀번호 찾기 적용 - 忘記密碼 - applying to an action finding account - applying to an action finding account - Khi lấy lại mật khẩu - 적용으로 하시면 비밀번호 찾기 기능에도 적용되어 악의적인 봇(또는 프로그램)에 의한 메일 발송을 막을 수 있습니다. - 启用此项功能可以有效地拦截以查找密码名义发送的垃圾邮件。 - 적용으로 하시면 비밀번호찾기 기능에도 적용되어 악의적인 봇(또는 프로그램)에 의한 메일 발송을 막을 수 있습니다. - 開啟功能後在忘記密碼時會顯示驗證碼。 - If you set this option as apply, CAPTCHA will work for finding account action, too. - If you set this option as apply, CAPTCHA will work for finding account action, too. - Nếu áp dụng, khi thành viên cần lấy lại mật khẩu khi lỡ quên, Capcha sẽ hiện thị để xác nhận việc này. - - 적용하지 않음 - 不启用 - 적용하지 않음 - 關閉 - Not apply - Not apply - Không áp dụng - - - 적용 - 启用 - 적용 - 開啟 - Apply - Apply - Áp dụng - - - - 인증 메일 재발송 적용 - 应用到认证邮件重新发送功能 - 인증 메일 재발송 적용 - 重寄認證信 - apply to an action resending authmail - apply to an action resending authmail - Khi lấy lại mã kích hoạt - 적용으로 하시면 인증 메일 재발송 기능에도 적용되어 악의적인 봇(또는 프로그램)에 의한 메일 발송을 막을 수 있습니다. - 启用此项功能可以有效地拦截以重新发送认证邮件名义发送的垃圾邮件。 - 적용으로 하시면 인증 메일 재발송 기능에도 적용되어 악의적인 봇(또는 프로그램)에 의한 메일 발송을 막을 수 있습니다. - 開啟功能後在重寄認證信時會顯示驗證碼。 - If you set this option as apply, CAPTCHA will work for resending authmail action, too. - If you set this option as apply, CAPTCHA will work for resending authmail action, too. - Nếu áp dụng, khi thành viên cần lấy lại mã kích hoạt thành viên, Capcha sẽ hiện thị để xác nhận việc này. - - 적용하지 않음 - 不启用 - 적용하지 않음 - 關閉 - Not apply - Not apply - Không áp dụng - - - 적용 - 启用 - 적용 - 開啟 - Apply - Apply - Áp dụng - - - - 회원 가입 적용 - 应用到用户注册表单 - 인증 메일 재발송 적용 - 會員註冊 - Apply to member signup - Apply to member signup - Apply to member signup - 적용으로 하시면 회원가입 기능에도 적용되어 악의적인 봇(또는 프로그램)의 회원가입을 막을 수 있습니다. - 启用此项功能可以有效地拦截自动注册软件的施虐。 - 적용으로 하시면 회원가입 기능에도 적용되어 악의적인 봇(또는 프로그램)의 회원가입을 막을 수 있습니다. - 開啟功能後在註冊時會顯示驗證碼。 - If you set this option as apply, CAPTCHA will work for signup action, too. - If you set this option as apply, CAPTCHA will work for signup action, too. - If you set this option as apply, CAPTCHA will work for signup action, too. - - 적용하지 않음 - 不启用 - 적용하지 않음 - 關閉 - Not apply - Not apply - Không áp dụng - - - 적용 - 启用 - 적용 - 開啟 - Apply - Apply - Áp dụng - - - - + + + Captcha 애드온 + CAPTCHA + Captcha Addon + 验证码插件 + Captchaアドオン + Аддон Captcha + 圖形驗證 + + 프로그램 글 등록기를 막기 위해 게시판/ issueTracker에서 글/ 댓글을 입력하려 할 때 알파벳을 입력해야 글/댓글이 입력되는 애드온 입니다. + 로그인하지 않은 경우에만 해당됩니다. + + + This addon helps to prevent spam messages to be posted by requesting non-logged-in users to type characters displayed in the image before submitting comments or posts. + + + Addon này tạo ra một hình ảnh xác nhận khi đăng kí, gửi bài, hay viết bình luận nếu thành viên không đăng nhập. + Addon này chỉ hoạt động khi được kích hoạt. + + + 为了解决互联网垃圾而开发的验证码机制。 + 非登录用户发布话题或评论时将会弹出验证图片选择框,选择正确的图片才可以正常发布(适用于版面/issueTracker)。 + + + ボット(bot)がプログラムによるスパム行為を防ぐために、掲示板/issueTrackerで書き込み・コメントを登録する際、ランダムな文字や数字の列を画面に表示し、表示されたものと一致した情報を入力した時、登録が出来るようにするアドオンです。 + ログインしてない時だけ、動作します。 + + + To block spam written by programs, let users to choose a suitable image to text when writing a posting or comment. + This addon applies only to not-logged-in users. + + + 可防止機器人程式的垃圾留言,非用戶要發表主題或回覆時,必須要輸入正確圖片中所顯示的文字才能發表。 + + 1.0 + 2010-08-19 + + + NHN + NHN + NHN + NHN + NHN + NHN + NHN + + + + + Captcha 표시 대상 + 应用对象 + Captchaを表示する対象 + 選擇目標 + Captcha Target + Captcha Target + Mục tiêu Captcha hiển thị + 글/댓글 등록시 captcha가 동작할 대상을 정할 수 있습니다. 관리자는 무조건 제외됩니다 + 可以指定验证码应用对象(管理员除外)。 + 管理者を除き、書き込み・コメントを入力する際にcaptchaイメージを見せる対象を設定します。 + 除了管理員,可以選擇圖形驗證應用的對象。 + You can specify if CAPTCHA should be displayed when posting an article or comment. It will not apply to administrators. + You may specify targets CAPTCHA work. It's not applied when administrator writes. + Khi gửi bài, bình luận, Capcha sẽ hiển thị để xác nhận hành động của người sử dụng. Chức năng này không hoạt động với người quản lý. + + 로그인하지 않은 사용자 + 非登录用户 + ログインしてないユーザー + 非用戶 + Not logged-in users + Not logged-in users + Người dùng chưa đăng nhập + + + 모든 사용자 + 所有用户 + すべてのユーザー + 所有用戶 + All users + All users + Tất cả mọi người + + + + 동작 방식 + 验证方式 + 動作方式 + 驗證模式 + How it works + How it works + Sử dụng + "1번만 동작"을 선택하시면 1번만 동작후 상태를 저장해서 다음부터 물어보지 않고 그렇지 않으면 매번 물어보게 됩니다 + "一次"就是每个IP只出现一次验证。 + 「1回だけ表示」を選択すると、最初だけ動作した後、その情報を記憶して次回からはCaptchaを見せないようにします。また、もう一つのオプションは毎回Captchaを表示します。 + 選擇"單次",下次不會再顯示;選擇"每次"則會一直顯示。 + If you choose "Once", CAPTCHA works only once for the user by storing status. Otherwise, this addon would show an image every time the user writes. + If you choose "Once", CAPTCHA works only once for the user by storing status. Otherwise, this addon would show an image every time the user writes. + Nếu chọn "Chỉ một lần" thì sau lần hiển thị đó Capcha sẽ không hiển thị với người sử dụng đó nữa. + + 1번만 동작 + 一次 + 1回だけ表示 + 單次 + Chỉ một lần + once + 1 раз + + + 매번 동작 + 每次 + 毎回表示 + 每次 + every time + каждый раз + Luôn sử dụng + + + + 비밀번호 찾기 적용 + 应用到查找密码功能 + 비밀번호 찾기 적용 + 忘記密碼 + applying to an action finding account + applying to an action finding account + Khi lấy lại mật khẩu + 적용으로 하시면 비밀번호 찾기 기능에도 적용되어 악의적인 봇(또는 프로그램)에 의한 메일 발송을 막을 수 있습니다. + 启用此项功能可以有效地拦截以查找密码名义发送的垃圾邮件。 + 적용으로 하시면 비밀번호찾기 기능에도 적용되어 악의적인 봇(또는 프로그램)에 의한 메일 발송을 막을 수 있습니다. + 開啟功能後在忘記密碼時會顯示驗證碼。 + If you set this option as apply, CAPTCHA will work for finding account action, too. + If you set this option as apply, CAPTCHA will work for finding account action, too. + Nếu áp dụng, khi thành viên cần lấy lại mật khẩu khi lỡ quên, Capcha sẽ hiện thị để xác nhận việc này. + + 적용하지 않음 + 不启用 + 적용하지 않음 + 關閉 + Not apply + Not apply + Không áp dụng + + + 적용 + 启用 + 적용 + 開啟 + Apply + Apply + Áp dụng + + + + 인증 메일 재발송 적용 + 应用到认证邮件重新发送功能 + 인증 메일 재발송 적용 + 重寄認證信 + apply to an action resending authmail + apply to an action resending authmail + Khi lấy lại mã kích hoạt + 적용으로 하시면 인증 메일 재발송 기능에도 적용되어 악의적인 봇(또는 프로그램)에 의한 메일 발송을 막을 수 있습니다. + 启用此项功能可以有效地拦截以重新发送认证邮件名义发送的垃圾邮件。 + 적용으로 하시면 인증 메일 재발송 기능에도 적용되어 악의적인 봇(또는 프로그램)에 의한 메일 발송을 막을 수 있습니다. + 開啟功能後在重寄認證信時會顯示驗證碼。 + If you set this option as apply, CAPTCHA will work for resending authmail action, too. + If you set this option as apply, CAPTCHA will work for resending authmail action, too. + Nếu áp dụng, khi thành viên cần lấy lại mã kích hoạt thành viên, Capcha sẽ hiện thị để xác nhận việc này. + + 적용하지 않음 + 不启用 + 적용하지 않음 + 關閉 + Not apply + Not apply + Không áp dụng + + + 적용 + 启用 + 적용 + 開啟 + Apply + Apply + Áp dụng + + + + 회원 가입 적용 + 应用到用户注册表单 + 인증 메일 재발송 적용 + 會員註冊 + Apply to member signup + Apply to member signup + Apply to member signup + 적용으로 하시면 회원가입 기능에도 적용되어 악의적인 봇(또는 프로그램)의 회원가입을 막을 수 있습니다. + 启用此项功能可以有效地拦截自动注册软件的施虐。 + 적용으로 하시면 회원가입 기능에도 적용되어 악의적인 봇(또는 프로그램)의 회원가입을 막을 수 있습니다. + 開啟功能後在註冊時會顯示驗證碼。 + If you set this option as apply, CAPTCHA will work for signup action, too. + If you set this option as apply, CAPTCHA will work for signup action, too. + If you set this option as apply, CAPTCHA will work for signup action, too. + + 적용하지 않음 + 不启用 + 적용하지 않음 + 關閉 + Not apply + Not apply + Không áp dụng + + + 적용 + 启用 + 적용 + 開啟 + Apply + Apply + Áp dụng + + + + diff --git a/addons/captcha/inline_captcha.js b/addons/captcha/inline_captcha.js new file mode 100644 index 000000000..26ff64a1b --- /dev/null +++ b/addons/captcha/inline_captcha.js @@ -0,0 +1,17 @@ +jQuery(function($){ + $('button.captchaPlay') + .click(function(){ + var swf = document['captcha_audio'] || window['captcha_audio']; + var audio = current_url.setQuery('captcha_action','captchaAudio').setQuery('rand', (new Date).getTime()); + + if(swf.length > 1) swf = swf[0]; + + $('input[type=text]#secret_text').focus(); + swf.setSoundTarget(audio,'1'); + }); + + $('button.captchaReload') + .click(function(){ + $("#captcha_image").attr("src", current_url.setQuery('captcha_action','captchaImage').setQuery('rand', (new Date).getTime()).setQuery('renew',1)); + }); +}); diff --git a/addons/captcha/lang/en.lang.php b/addons/captcha/lang/en.lang.php deleted file mode 100644 index e08259fde..000000000 --- a/addons/captcha/lang/en.lang.php +++ /dev/null @@ -1,6 +0,0 @@ -about_captcha = "Please type alphabets above in order. They are not case-sensitive."; - $lang->captcha_reload = 'Refresh Image'; - $lang->captcha_play = 'Play sound of words'; - $lang->captcha_denied = 'You have typed wrong alphabets.'; -?> diff --git a/addons/captcha/lang/jp.lang.php b/addons/captcha/lang/jp.lang.php deleted file mode 100644 index 791d6ad96..000000000 --- a/addons/captcha/lang/jp.lang.php +++ /dev/null @@ -1,6 +0,0 @@ -about_captcha = "위 영어 알파벳을 순서대로 입력해 주세요. 대소문자는 구분하지 않습니다."; - $lang->captcha_reload = '이미지 새로고침'; - $lang->captcha_play = '음성으로 듣기'; - $lang->captcha_denied = '잘못 입력하셨습니다'; -?> diff --git a/addons/captcha/lang/ko.lang.php b/addons/captcha/lang/ko.lang.php deleted file mode 100644 index 791d6ad96..000000000 --- a/addons/captcha/lang/ko.lang.php +++ /dev/null @@ -1,6 +0,0 @@ -about_captcha = "위 영어 알파벳을 순서대로 입력해 주세요. 대소문자는 구분하지 않습니다."; - $lang->captcha_reload = '이미지 새로고침'; - $lang->captcha_play = '음성으로 듣기'; - $lang->captcha_denied = '잘못 입력하셨습니다'; -?> diff --git a/addons/captcha/lang/lang.xml b/addons/captcha/lang/lang.xml new file mode 100644 index 000000000..d78bdd578 --- /dev/null +++ b/addons/captcha/lang/lang.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/addons/captcha/lang/ru.lang.php b/addons/captcha/lang/ru.lang.php deleted file mode 100644 index 791d6ad96..000000000 --- a/addons/captcha/lang/ru.lang.php +++ /dev/null @@ -1,6 +0,0 @@ -about_captcha = "위 영어 알파벳을 순서대로 입력해 주세요. 대소문자는 구분하지 않습니다."; - $lang->captcha_reload = '이미지 새로고침'; - $lang->captcha_play = '음성으로 듣기'; - $lang->captcha_denied = '잘못 입력하셨습니다'; -?> diff --git a/addons/captcha/lang/vi.lang.php b/addons/captcha/lang/vi.lang.php deleted file mode 100644 index 791d6ad96..000000000 --- a/addons/captcha/lang/vi.lang.php +++ /dev/null @@ -1,6 +0,0 @@ -about_captcha = "위 영어 알파벳을 순서대로 입력해 주세요. 대소문자는 구분하지 않습니다."; - $lang->captcha_reload = '이미지 새로고침'; - $lang->captcha_play = '음성으로 듣기'; - $lang->captcha_denied = '잘못 입력하셨습니다'; -?> diff --git a/addons/captcha/lang/zh-CN.lang.php b/addons/captcha/lang/zh-CN.lang.php deleted file mode 100644 index 791d6ad96..000000000 --- a/addons/captcha/lang/zh-CN.lang.php +++ /dev/null @@ -1,6 +0,0 @@ -about_captcha = "위 영어 알파벳을 순서대로 입력해 주세요. 대소문자는 구분하지 않습니다."; - $lang->captcha_reload = '이미지 새로고침'; - $lang->captcha_play = '음성으로 듣기'; - $lang->captcha_denied = '잘못 입력하셨습니다'; -?> diff --git a/addons/captcha/lang/zh-TW.lang.php b/addons/captcha/lang/zh-TW.lang.php deleted file mode 100644 index 6e557f93d..000000000 --- a/addons/captcha/lang/zh-TW.lang.php +++ /dev/null @@ -1,6 +0,0 @@ -about_captcha = "請依序輸入圖片中的文字,不分大小寫。"; - $lang->captcha_reload = '更換'; - $lang->captcha_play = '播放'; - $lang->captcha_denied = '輸入錯誤'; -?> diff --git a/addons/counter/conf/info.xml b/addons/counter/conf/info.xml index 7114c2380..c3ea6dccb 100644 --- a/addons/counter/conf/info.xml +++ b/addons/counter/conf/info.xml @@ -1,62 +1,62 @@ - - - 기본 카운터 애드온 - アクセスカウンターアドオン - 网站访问统计 - Counter Addon - Counter Addon - Counter Addon - Addon contador básico - Аддон счетчика - 網站訪問統計 - - XE의 기본 카운터 모듈을 이용하여 접속 정보를 기록합니다. - 이 애드온을 켜셔야 접속 정보 수집이 됩니다. - - - XEのアクセスカウンターモジュールで接続(アクセス)情報を記録します。 - このアドオンを「使用」に設定してから、接続(アクセス)情報が記録されます。 - - - 利用XE的网站访问统计模块记录网站访问信息。 - 把状态设置为"使用"时,才会记录网站访问信息. - - - This addon logs access information based on the basic counter module within XE. - The access information will be collected only if you turn on this addon. - - - Addon này sẽ tổng hợp tất cả những lượt truy cập vào Website qua Module Counter có sẵn bên trong XE. - Thông tin truy nhập chỉ được tập hợp khi bạn bật Addon này.. - - - Dieses Addon-Logs Zugriff auf Informationen basiert auf den grundlegenden Zähler-Modul innerhalb XE. - Der Zugang zu Informationen wird nur erhoben, wenn Sie über dieses Addon. - - - Este addon contador básico de XE permite llevar la información de acceso a la página web de los visitantes. - Es necesario activar este addon para agregar la información de acceso. - - - Этот аддон пишет в лог информацию о доступе к сайту, основанную на базовом модуле счетчика в XE. - Для сбора информации необходимо включить этот аддон. - - - 使用XE的網站訪問統計模組記錄網站訪問資料。 - 將狀態設置成"使用"時,才會紀錄網站訪問資料。 - - 0.1 - 2007-02-28 - - - NHN - NHN - NHN - NHN - NHN - NHN - NHN - NHN - NHN - - + + + 기본 카운터 애드온 + アクセスカウンターアドオン + 网站访问统计 + Counter Addon + Counter Addon + Counter Addon + Addon contador básico + Аддон счетчика + 網站訪問統計 + + XE의 기본 카운터 모듈을 이용하여 접속 정보를 기록합니다. + 이 애드온을 켜셔야 접속 정보 수집이 됩니다. + + + XEのアクセスカウンターモジュールで接続(アクセス)情報を記録します。 + このアドオンを「使用」に設定してから、接続(アクセス)情報が記録されます。 + + + 利用XE的网站访问统计模块记录网站访问信息。 + 把状态设置为"使用"时,才会记录网站访问信息. + + + This addon logs access information based on the basic counter module within XE. + The access information will be collected only if you enable this addon. + + + Addon này sẽ tổng hợp tất cả những lượt truy cập vào Website qua Module Counter có sẵn bên trong XE. + Thông tin truy nhập chỉ được tập hợp khi bạn bật Addon này.. + + + Dieses Addon-Logs Zugriff auf Informationen basiert auf den grundlegenden Zähler-Modul innerhalb XE. + Der Zugang zu Informationen wird nur erhoben, wenn Sie über dieses Addon. + + + Este addon contador básico de XE permite llevar la información de acceso a la página web de los visitantes. + Es necesario activar este addon para agregar la información de acceso. + + + Этот аддон пишет в лог информацию о доступе к сайту, основанную на базовом модуле счетчика в XE. + Для сбора информации необходимо включить этот аддон. + + + 使用XE的網站訪問統計模組記錄網站訪問資料。 + 將狀態設置成"使用"時,才會紀錄網站訪問資料。 + + 0.1 + 2007-02-28 + + + NHN + NHN + NHN + NHN + NHN + NHN + NHN + NHN + NHN + + diff --git a/addons/counter/counter.addon.php b/addons/counter/counter.addon.php index d46ad7b9e..3d6c8115e 100644 --- a/addons/counter/counter.addon.php +++ b/addons/counter/counter.addon.php @@ -1,14 +1,14 @@ -procCounterExecute(); - } -?> +counterExecute(); +} +?> diff --git a/addons/member_communication/conf/info.xml b/addons/member_communication/conf/info.xml index 25f3b7677..8fc13b5a7 100644 --- a/addons/member_communication/conf/info.xml +++ b/addons/member_communication/conf/info.xml @@ -1,58 +1,58 @@ - - - 커뮤니케이션 - コミュニケーション - 会员交流 - Communication - Truyền thông - 커뮤니케이션 - 커뮤니케이션 - Общение - 交流 - - 커뮤니케이션 모듈의 기능을 활성화 시켜 쪽지나 친구기능을 사용할 수 있도록 해줍니다. - 쪽지, 친구기능등을 사용하기 위해서는 이 애드온을 사용으로 해주시면 됩니다. - - - メッセージ・友達機能を使うにはこのアドオンを「使用」にして下さい。 - - - 此插件可激活短信箱及添加好友功能。 - - - This addon enables communication module in order to use message or friend function. - Please enable this addon in case you want to use those functions. - - - Addon này cho phép sử dụng Module truyền thông để sử dụng tin nhắn hay chức năng bạn bè. - Hãy kích hoạt nếu bạn muốn sử dụng chức năng này. - - - 커뮤니케이션 모듈의 기능을 활성화 시켜 쪽지나 친구기능을 사용할 수 있도록 해줍니다. - 쪽지, 친구기능등을 사용하기 위해서는 이 애드온을 사용으로 해주시면 됩니다. - - - 커뮤니케이션 모듈의 기능을 활성화 시켜 쪽지나 친구기능을 사용할 수 있도록 해줍니다. - 쪽지, 친구기능등을 사용하기 위해서는 이 애드온을 사용으로 해주시면 됩니다. - - - Активизирует модуль Общение, позволяет использование сообщений между друзьями. - - - 讓會員擁有短訊和新增好友功能。 - - 0.1 - 2008-05-28 - - - NHN - NHN - NHN - NHN - NHN - NHN - NHN - NHN - NHN - - + + + 커뮤니케이션 + コミュニケーション + 会员交流 + Communication + Truyền thông + 커뮤니케이션 + 커뮤니케이션 + Общение + 交流 + + 커뮤니케이션 모듈의 기능을 활성화 시켜 쪽지나 친구기능을 사용할 수 있도록 해줍니다. + 쪽지, 친구기능등을 사용하기 위해서는 이 애드온을 사용으로 해주시면 됩니다. + + + メッセージ・友達機能を使うにはこのアドオンを「使用」にして下さい。 + + + 此插件可激活短信箱及添加好友功能。 + + + This addon enables the communication module in order to use messaging or friend functions. + Please enable this addon in case you want to use those functions. + + + Addon này cho phép sử dụng Module truyền thông để sử dụng tin nhắn hay chức năng bạn bè. + Hãy kích hoạt nếu bạn muốn sử dụng chức năng này. + + + 커뮤니케이션 모듈의 기능을 활성화 시켜 쪽지나 친구기능을 사용할 수 있도록 해줍니다. + 쪽지, 친구기능등을 사용하기 위해서는 이 애드온을 사용으로 해주시면 됩니다. + + + 커뮤니케이션 모듈의 기능을 활성화 시켜 쪽지나 친구기능을 사용할 수 있도록 해줍니다. + 쪽지, 친구기능등을 사용하기 위해서는 이 애드온을 사용으로 해주시면 됩니다. + + + Активизирует модуль Общение, позволяет использование сообщений между друзьями. + + + 讓會員擁有短訊和新增好友功能。 + + 0.1 + 2008-05-28 + + + NHN + NHN + NHN + NHN + NHN + NHN + NHN + NHN + NHN + + diff --git a/addons/member_communication/lang/en.lang.php b/addons/member_communication/lang/en.lang.php deleted file mode 100644 index cc9cce249..000000000 --- a/addons/member_communication/lang/en.lang.php +++ /dev/null @@ -1,3 +0,0 @@ -alert_new_message_arrived = 'You have a new message. Do you want to check now?'; -?> diff --git a/addons/member_communication/lang/es.lang.php b/addons/member_communication/lang/es.lang.php deleted file mode 100644 index 51933d3f8..000000000 --- a/addons/member_communication/lang/es.lang.php +++ /dev/null @@ -1,3 +0,0 @@ -alert_new_message_arrived = 'Usted tiene un nuevo mensaje. Quiere comprobar ahora?'; -?> diff --git a/addons/member_communication/lang/ge.lang.php b/addons/member_communication/lang/ge.lang.php deleted file mode 100644 index d9b9dcb81..000000000 --- a/addons/member_communication/lang/ge.lang.php +++ /dev/null @@ -1,3 +0,0 @@ -alert_new_message_arrived = 'Sie haben eine neue Nachricht. Wollen Sie jetzt prüfen, ob?'; -?> diff --git a/addons/member_communication/lang/jp.lang.php b/addons/member_communication/lang/jp.lang.php deleted file mode 100644 index 7ff03c9fe..000000000 --- a/addons/member_communication/lang/jp.lang.php +++ /dev/null @@ -1,3 +0,0 @@ -alert_new_message_arrived = '%d個の新しいメッセージが届いています。 確認しますか?'; -?> diff --git a/addons/member_communication/lang/ko.lang.php b/addons/member_communication/lang/ko.lang.php deleted file mode 100644 index 697ef6d93..000000000 --- a/addons/member_communication/lang/ko.lang.php +++ /dev/null @@ -1,3 +0,0 @@ -alert_new_message_arrived = '%d개의 새로운 메시지가 도착하였습니다. 확인하시겠습니까?'; -?> diff --git a/addons/member_communication/lang/lang.xml b/addons/member_communication/lang/lang.xml new file mode 100644 index 000000000..0843743ac --- /dev/null +++ b/addons/member_communication/lang/lang.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/addons/member_communication/lang/ru.lang.php b/addons/member_communication/lang/ru.lang.php deleted file mode 100644 index 4e47c4c23..000000000 --- a/addons/member_communication/lang/ru.lang.php +++ /dev/null @@ -1,9 +0,0 @@ -alert_new_message_arrived = 'У Вас новые сообщения. Проверите сейчас?'; -?> diff --git a/addons/member_communication/lang/vi.lang.php b/addons/member_communication/lang/vi.lang.php deleted file mode 100644 index 4015a8be0..000000000 --- a/addons/member_communication/lang/vi.lang.php +++ /dev/null @@ -1,11 +0,0 @@ -alert_new_message_arrived = 'Bạn có một tin nhắn mới. bạn có muốn kiểm tra ngay bây giờ không?'; -?> diff --git a/addons/member_communication/lang/zh-CN.lang.php b/addons/member_communication/lang/zh-CN.lang.php deleted file mode 100644 index 497b4ef68..000000000 --- a/addons/member_communication/lang/zh-CN.lang.php +++ /dev/null @@ -1,3 +0,0 @@ -alert_new_message_arrived = '您有新消息。要确认吗?'; -?> diff --git a/addons/member_communication/lang/zh-TW.lang.php b/addons/member_communication/lang/zh-TW.lang.php deleted file mode 100644 index f2ffd16e8..000000000 --- a/addons/member_communication/lang/zh-TW.lang.php +++ /dev/null @@ -1,3 +0,0 @@ -alert_new_message_arrived = '您收到%d個新訊息。您想要檢視嗎?'; -?> diff --git a/addons/member_communication/member_communication.addon.php b/addons/member_communication/member_communication.addon.php index c34822a62..519c7db47 100644 --- a/addons/member_communication/member_communication.addon.php +++ b/addons/member_communication/member_communication.addon.php @@ -1,85 +1,71 @@ -module != 'member') { - - // 커뮤니케이션 모듈의 언어파일을 읽음 - Context::loadLang('./modules/communication/lang'); - - // 회원 로그인 정보중에서 쪽지등의 메뉴를 추가 - $oMemberController = &getController('member'); - $oMemberController->addMemberMenu('dispCommunicationFriend', 'cmd_view_friend'); - $oMemberController->addMemberMenu('dispCommunicationMessages', 'cmd_view_message_box'); - - // 새로운 쪽지에 대한 플래그가 있으면 쪽지 보기 팝업 띄움 - $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)) { - $new_message_count = FileHandler::readFile($flag_file); - FileHandler::removeFile($flag_file); - Context::loadLang('./addons/member_communication/lang'); - - $script = sprintf('', sprintf(Context::getLang('alert_new_message_arrived'), $new_message_count), Context::getRequestUri().'?module=communication&act=dispCommunicationNewMessage'); - - Context::addHtmlHeader( $script ); - } - - /** - * 기능 수행 : 사용자 이름을 클릭시 요청되는 팝업메뉴의 메뉴에 쪽지 발송, 친구추가등의 링크 추가 - **/ - } elseif($called_position == 'before_module_proc' && $this->act == 'getMemberMenu') { - - $oMemberController = &getController('member'); - $member_srl = Context::get('target_srl'); - $mid = Context::get('cur_mid'); - - // communication 모델 객체 생성 - $oCommunicationModel = &getModel('communication'); - - // 자신이라면 쪽지함 보기 기능 추가 - if($logged_info->member_srl == $member_srl) { - - // 자신의 쪽지함 보기 기능 추가 - $oMemberController->addMemberPopupMenu(getUrl('','mid',$mid,'act','dispCommunicationMessages'), 'cmd_view_message_box', './modules/communication/tpl/images/icon_message_box.gif', 'self'); - - // 친구 목록 보기 - $oMemberController->addMemberPopupMenu(getUrl('','mid',$mid,'act','dispCommunicationFriend'), 'cmd_view_friend', './modules/communication/tpl/images/icon_friend_box.gif', 'self'); - - // 아니라면 쪽지 발송, 친구 등록 추가 - } else { - // 대상 회원의 정보를 가져옴 - $oMemberModel = &getModel('member'); - $target_member_info = $oMemberModel->getMemberInfoByMemberSrl($member_srl); - if(!$target_member_info->member_srl) return; - - // 로그인된 사용자 정보를 구함 - $logged_info = Context::get('logged_info'); - - // 쪽지 발송 메뉴를 만듬 - if( $logged_info->is_admin == 'Y' || $target_member_info->allow_message =='Y' || ($target_member_info->allow_message == 'F' && $oCommunicationModel->isFriend($member_srl))) - $oMemberController->addMemberPopupMenu(getUrl('','module','communication','act','dispCommunicationSendMessage','receiver_srl',$member_srl), 'cmd_send_message', './modules/communication/tpl/images/icon_write_message.gif', 'popup'); - - // 친구 등록 메뉴를 만듬 (이미 등록된 친구가 아닐 경우) - if(!$oCommunicationModel->isAddedFriend($member_srl)) - $oMemberController->addMemberPopupMenu(getUrl('','module','communication','act','dispCommunicationAddFriend','target_srl',$member_srl), 'cmd_add_friend', './modules/communication/tpl/images/icon_add_friend.gif', 'popup'); - } - } -?> +module != 'member') { + // Load a language file from the communication module + Context::loadLang('./modules/communication/lang'); + // Add menus on the member login information + $oMemberController = &getController('member'); + $oMemberController->addMemberMenu('dispCommunicationFriend', 'cmd_view_friend'); + $oMemberController->addMemberMenu('dispCommunicationMessages', 'cmd_view_message_box'); + // Pop-up to display messages if a flag on new message is set + $flag_path = './files/member_extra_info/new_message_flags/'.getNumberingPath($logged_info->member_srl); + $flag_file = $flag_path.$logged_info->member_srl; + + if(file_exists($flag_file)) { + $new_message_count = trim(FileHandler::readFile($flag_file)); + FileHandler::removeFile($flag_file); + Context::loadLang('./addons/member_communication/lang'); + Context::loadFile(array('./addons/member_communication/tpl/member_communication.js'), true); + + $text = preg_replace('@\r?\n@', '\\n', addslashes(Context::getLang('alert_new_message_arrived'))); + $link = Context::getRequestUri().'?module=communication&act=dispCommunicationNewMessage'; + $script = ""; + + Context::addHtmlFooter($script); + } +} elseif($called_position == 'before_module_proc' && $this->act == 'getMemberMenu') { + $oMemberController = &getController('member'); + $member_srl = Context::get('target_srl'); + $mid = Context::get('cur_mid'); + // Creates communication model object + $oCommunicationModel = &getModel('communication'); + // Add a feature to display own message box. + if($logged_info->member_srl == $member_srl) { + // Add your own viewing Note Template + $oMemberController->addMemberPopupMenu(getUrl('','mid',$mid,'act','dispCommunicationMessages'), 'cmd_view_message_box', '', 'self'); + // Display a list of friends + $oMemberController->addMemberPopupMenu(getUrl('','mid',$mid,'act','dispCommunicationFriend'), 'cmd_view_friend', '', 'self'); + // If not, Add menus to send message and to add friends + } else { + // Get member information + $oMemberModel = &getModel('member'); + $target_member_info = $oMemberModel->getMemberInfoByMemberSrl($member_srl); + if(!$target_member_info->member_srl) return; + // Get logged-in user information + $logged_info = Context::get('logged_info'); + // Add a menu for sending message + if( $logged_info->is_admin == 'Y' || $target_member_info->allow_message =='Y' || ($target_member_info->allow_message == 'F' && $oCommunicationModel->isFriend($member_srl))) + $oMemberController->addMemberPopupMenu(getUrl('','module','communication','act','dispCommunicationSendMessage','receiver_srl',$member_srl), 'cmd_send_message', '', 'popup'); + // Add a menu for listing friends (if a friend is new) + if(!$oCommunicationModel->isAddedFriend($member_srl)) + $oMemberController->addMemberPopupMenu(getUrl('','module','communication','act','dispCommunicationAddFriend','target_srl',$member_srl), 'cmd_add_friend', '', 'popup'); + } +} +?> diff --git a/addons/member_communication/tpl/member_communication.js b/addons/member_communication/tpl/member_communication.js new file mode 100644 index 000000000..d2245efa0 --- /dev/null +++ b/addons/member_communication/tpl/member_communication.js @@ -0,0 +1,29 @@ +(function($){ + +window.xeNotifyMessage = function(text, count){ + var $bar; + + $bar = $('div.notifyMessage'); + if(!$bar.length) { + $bar = $('
') + .hide() + .css({ + position : 'absolute', + background : '#ff0', + border : '1px solid #990', + textAlign : 'center' + }) + .appendTo(document.body); + } + + text = text.replace('%d', count); + h = $bar.html(''+text+'').height(); + $bar.css('top', -h-4).show().animate({top:0}); + + // hide after 10 seconds + setTimeout(function(){ + $bar.animate({top:-h-4}, function(){ $bar.hide() }); + }, 10000); +}; + +})(jQuery); diff --git a/addons/member_extra_info/conf/info.xml b/addons/member_extra_info/conf/info.xml index 9a82ec315..f75c6d929 100644 --- a/addons/member_extra_info/conf/info.xml +++ b/addons/member_extra_info/conf/info.xml @@ -1,53 +1,53 @@ - - - 회원 확장 정보 출력 - 会員情報拡張表示 - 用户扩展信息 - Extra Member Info - Bổ xung thông tin thành viên - 회원 확장 정보 출력 - 회원 확장 정보 출력 - Экстраинформация о пользователях - 用戶延伸資料 - - 회원이 등록한 이미지이름, 이미지마크를 사용하기 위해서는 이 애드온을 활성화 시키세요. - - - 会員が登録したイメージニックネーム、イメージマークを使うためにはこのアドオンをオンにして下さい。 - - - 此插件将把用户信息中的昵称图片,用户图标,签名等信息显示到页面当中。 - - - This addon displays a member's image name, image mark. - - - Addon này sẽ hiển thị hình ảnh thay thế tên và hình đánh dấu của thành viên. - - - This addon displays a member's image name, image mark. - - - This addon displays a member's image name, image mark. - - - Аддон изображает имя, марку картинки пользователя. - - - 可將用戶資料中的暱稱圖片、用戶圖示、簽名檔等資料顯示到頁面當中。 - - 0.2 - 2007-02-28 - - - NHN - NHN - NHN - NHN - NHN - NHN - NHN - NHN - NHN - - + + + 회원 확장 정보 출력 + 会員情報拡張表示 + 用户扩展信息 + Extra Member Info + Bổ xung thông tin thành viên + 회원 확장 정보 출력 + 회원 확장 정보 출력 + Экстраинформация о пользователях + 用戶延伸資料 + + 회원이 등록한 이미지이름, 이미지마크를 사용하기 위해서는 이 애드온을 활성화 시키세요. + + + 会員が登録したイメージニックネーム、イメージマークを使うためにはこのアドオンをオンにして下さい。 + + + 此插件将把用户信息中的昵称图片,用户图标,签名等信息显示到页面当中。 + + + This addon displays a member's image name, image mark. + + + Addon này sẽ hiển thị hình ảnh thay thế tên và hình đánh dấu của thành viên. + + + This addon displays a member's image name, image mark. + + + This addon displays a member's image name, image mark. + + + Аддон изображает имя, марку картинки пользователя. + + + 可將用戶資料中的暱稱圖片、用戶圖示、簽名檔等資料顯示到頁面當中。 + + 0.2 + 2007-02-28 + + + NHN + NHN + NHN + NHN + NHN + NHN + NHN + NHN + NHN + + diff --git a/addons/member_extra_info/member_extra_info.addon.php b/addons/member_extra_info/member_extra_info.addon.php index 585cc3f86..a107faaa4 100644 --- a/addons/member_extra_info/member_extra_info.addon.php +++ b/addons/member_extra_info/member_extra_info.addon.php @@ -1,23 +1,21 @@ -....
로 정의가 된 부분을 찾아 회원번호를 구해서 - * 이미지이름, 이미지마크가 있는지를 확인하여 있으면 내용을 변경해버립니다. - **/ - - /** - * 출력되기 바로 직전일 경우에 이미지이름/이미지마크등을 변경 - **/ - if($called_position != "before_display_content" || Context::get('act')=='dispPageAdminContentModify') return; - - // 회원 이미지이름/ 마크/ 찾아서 대체할 함수를 담고 있는 파일을 include - require_once('./addons/member_extra_info/member_extra_info.lib.php'); - - // 1. 출력문서중에서
content
를 찾아 MemberController::transImageName() 를 이용하여 이미지이름/마크로 변경 - $output = preg_replace_callback('!<(div|span|a)([^\>]*)member_([0-9]+)([^\>]*)>(.*?)\<\/(div|span|a)\>!is', 'memberTransImageName', $output); -?> + .... + * Check if ther is image name and image mark. Then change it. + **/ + +/** + * Just before displaying, change image name/ image mark + **/ +if($called_position != "before_display_content" || Context::get('act')=='dispPageAdminContentModify') return; +// Include a file having functions to replace member image name/mark +require_once('./addons/member_extra_info/member_extra_info.lib.php'); +// 1. Find a part
content
in the output document, change it to image name/mark by using MemberController::transImageName() +$output = preg_replace_callback('!<(div|span|a)([^\>]*)member_([0-9]+)([^\>]*)>(.*?)\<\/(div|span|a)\>!is', 'memberTransImageName', $output); +?> diff --git a/addons/member_extra_info/member_extra_info.lib.php b/addons/member_extra_info/member_extra_info.lib.php index 75fe982ea..cf6af6c29 100644 --- a/addons/member_extra_info/member_extra_info.lib.php +++ b/addons/member_extra_info/member_extra_info.lib.php @@ -1,22 +1,19 @@ getGroupImageMark($member_srl,$site_module_info->site_srl); - - // 회원이 아닐경우(member_srl = 0) 본문 전체를 return + // If member_srl=o(not a member), return the entire body $nick_name = $matches[5]; if(!$member_srl) return $matches[0]; - - // 전역변수에 미리 설정한 데이터가 있다면 그걸 return + // If pre-defined data in the global variablesm return it if(!$GLOBALS['_transImageNameList'][$member_srl]->cached) { $GLOBALS['_transImageNameList'][$member_srl]->cached = true; $image_name_file = sprintf('files/member_extra_info/image_name/%s%d.gif', getNumberingPath($member_srl), $member_srl); @@ -29,14 +26,17 @@ $image_name_file = $GLOBALS['_transImageNameList'][$member_srl]->image_name_file; $image_mark_file = $GLOBALS['_transImageNameList'][$member_srl]->image_mark_file; } - - // 이미지이름이나 마크가 없으면 원본 정보를 세팅 + // If image name and mark doesn't exist, set the original information if(!$image_name_file && !$image_mark_file && !$group_image) return $matches[0]; - if($image_name_file) $nick_name = sprintf('id: %s', Context::getRequestUri(),$image_name_file, strip_tags($nick_name), strip_tags($nick_name)); - if($image_mark_file) $nick_name = sprintf('id: %s%s', Context::getRequestUri(),$image_mark_file, strip_tags($nick_name), strip_tags($nick_name), $nick_name); + // check member_config + + $config = $oMemberModel->getMemberConfig(); - if($group_image) $nick_name = sprintf('%s%s', $group_image->src, $group_image->title, $group_image->description, $nick_name); + if($config->image_name == 'Y' && $image_name_file) $nick_name = sprintf('id: %s', Context::getRequestUri(),$image_name_file, strip_tags($nick_name), strip_tags($nick_name)); + if($config->image_mark == 'Y' && $image_mark_file) $nick_name = sprintf('id: %s%s', Context::getRequestUri(),$image_mark_file, strip_tags($nick_name), strip_tags($nick_name), $nick_name); + + if($group_image) $nick_name = sprintf('%s%s', $group_image->src, $group_image->title, $group_image->description, $nick_name); $orig_text = preg_replace('/'.preg_quote($matches[5],'/').'<\/'.$matches[6].'>$/', '', $matches[0]); diff --git a/addons/mobile/classes/hdml.class.php b/addons/mobile/classes/hdml.class.php index 1c3ee4262..fbad40d06 100644 --- a/addons/mobile/classes/hdml.class.php +++ b/addons/mobile/classes/hdml.class.php @@ -1,103 +1,103 @@ -charset); - header("Cache-Control: no-store, no-cache, must-revalidate"); - header("Cache-Control: post-check=0, pre-check=0", false); - header("Pragma: no-cache"); - - print ''; - print "\n"; - print $this->hasChilds()?'':''; - print "\n"; - - if($this->upperUrl) { - $url = $this->upperUrl; - printf('%s', $url->url, $url->text, "\n"); - } - } - - /** - * @brief 제목을 출력 - **/ - function printTitle() { - if($this->totalPage > $this->mobilePage) $titlePageStr = sprintf("(%d/%d)",$this->mobilePage, $this->totalPage); - printf('<%s%s>%s', $this->title,$titlePageStr,"\n"); - } - - /** - * @brief 내용을 출력 - * hasChilds()가 있으면 목록형을 그렇지 않으면 컨텐츠를 출력 - **/ - function printContent() { - if($this->hasChilds()) { - foreach($this->getChilds() as $key => $val) { - if(!$val['link']) continue; - printf('%s%s',Context::getLang('cmd_select'), $val['href'], $val['text'], "\n"); - } - } else { - printf('%s
%s', $this->getContent(),"\n"); - } - } - - /** - * @brief 버튼을 출력함 - **/ - function printBtn() { - // 메뉴 형식 - if($this->hasChilds()) { - if($this->nextUrl) { - $url = $this->nextUrl; - printf('%s%s', $url->text, $url->url, $url->text, "\n"); - } - if($this->prevUrl) { - $url = $this->prevUrl; - printf('%s%s', $url->text, $url->url, $url->text, "\n"); - } - if($this->homeUrl) { - $url = $this->homeUrl; - printf('%s%s', $url->text, $url->url, $url->text, "\n"); - } - // 컨텐츠 형식 - } else { - if($this->nextUrl) { - $url = $this->nextUrl; - printf('%s', $url->text, $url->url, $url->text); - } - if($this->prevUrl) { - $url = $this->prevUrl; - printf('%s', $url->text, $url->url, $url->text); - } - if($this->homeUrl) { - $url = $this->homeUrl; - printf('%s', $url->text, $url->url, $url->text); - } - } - } - - /** - * @brief 푸터 정보를 출력 - **/ - function printFooter() { - print $this->hasChilds()?'
':''; - print "\n"; - print("
"); - } - - } -?> +charset); + header("Cache-Control: no-store, no-cache, must-revalidate"); + header("Cache-Control: post-check=0, pre-check=0", false); + header("Pragma: no-cache"); + + print ''; + print "\n"; + print $this->hasChilds()?'':''; + print "\n"; + + if($this->upperUrl) { + $url = $this->upperUrl; + printf('%s', $url->url, $url->text, "\n"); + } + } + + /** + * @brief Output title + **/ + function printTitle() { + if($this->totalPage > $this->mobilePage) $titlePageStr = sprintf("(%d/%d)",$this->mobilePage, $this->totalPage); + printf('<%s%s>%s', $this->title,$titlePageStr,"\n"); + } + + /** + * @brief Output information + * hasChilds() if there is a list of content types, otherwise output + **/ + function printContent() { + if($this->hasChilds()) { + foreach($this->getChilds() as $key => $val) { + if(!$val['link']) continue; + printf('%s%s',Context::getLang('cmd_select'), $val['href'], $val['text'], "\n"); + } + } else { + printf('%s
%s', $this->getContent(),"\n"); + } + } + + /** + * @brief Button to output + **/ + function printBtn() { + // Menu Types + if($this->hasChilds()) { + if($this->nextUrl) { + $url = $this->nextUrl; + printf('%s%s', $url->text, $url->url, $url->text, "\n"); + } + if($this->prevUrl) { + $url = $this->prevUrl; + printf('%s%s', $url->text, $url->url, $url->text, "\n"); + } + if($this->homeUrl) { + $url = $this->homeUrl; + printf('%s%s', $url->text, $url->url, $url->text, "\n"); + } + // Content Types + } else { + if($this->nextUrl) { + $url = $this->nextUrl; + printf('%s', $url->text, $url->url, $url->text); + } + if($this->prevUrl) { + $url = $this->prevUrl; + printf('%s', $url->text, $url->url, $url->text); + } + if($this->homeUrl) { + $url = $this->homeUrl; + printf('%s', $url->text, $url->url, $url->text); + } + } + } + + /** + * @brief Footer information output + **/ + function printFooter() { + print $this->hasChilds()?'
':''; + print "\n"; + print("
"); + } + + } +?> diff --git a/addons/mobile/classes/mhtml.class.php b/addons/mobile/classes/mhtml.class.php index 5b1b55ce6..a03ffb20b 100644 --- a/addons/mobile/classes/mhtml.class.php +++ b/addons/mobile/classes/mhtml.class.php @@ -1,82 +1,80 @@ -\n"); - if($this->totalPage > $this->mobilePage) $titlePageStr = sprintf("(%d/%d)",$this->mobilePage, $this->totalPage); - printf("%s%s\n", htmlspecialchars($this->title),htmlspecialchars($titlePageStr)); - } - - // 제목을 출력 - function printTitle() { - if($this->totalPage > $this->mobilePage) $titlePageStr = sprintf("(%d/%d)",$this->mobilePage, $this->totalPage); - printf('<%s%s>
%s', htmlspecialchars($this->title),htmlspecialchars($titlePageStr),"\n"); - } - - /** - * @brief 내용을 출력 - * hasChilds()가 있으면 목록형을 그렇지 않으면 컨텐츠를 출력 - **/ - function printContent() { - if($this->hasChilds()) { - foreach($this->getChilds() as $key => $val) { - if(!$val['link']) continue; - printf('%s
%s', $val['href'], $this->getNo(), $val['text'], "\n"); - if($val['extra']) printf("
%s\n",str_replace('
','
',$val['extra'])); - } - } else { - print(str_replace('
','
',$this->getContent())."\n"); - } - print "

"; - } - - /** - * @brief 버튼을 출력함 - **/ - function printBtn() { - if($this->nextUrl) { - $url = $this->nextUrl; - printf('%s
%s', $url->url, $url->text, "\n"); - } - if($this->prevUrl) { - $url = $this->prevUrl; - printf('%s
%s', $url->url, $url->text, "\n"); - } - // 언어선택 - if(!parent::isLangChange()){ - $url = getUrl('','lcm','1','sel_lang',Context::getLangType(),'return_uri',Context::get('current_url')); - printf('%s
%s', $url, 'Language : '.Context::getLang('select_lang'), "\n"); - } - else { - printf('%s
%s', Context::get('return_uri'), Context::getLang('lang_return'), "\n"); - } - if($this->upperUrl) { - $url = $this->upperUrl; - printf('%s', $url->url, $url->text, "\n"); - } - if($this->homeUrl) { - $url = $this->homeUrl; - printf('%s
%s', $url->text, $url->url, $url->text, "\n"); - } - } - - // 푸터 정보를 출력 - function printFooter() { - print("\n"); - } - } -?> +\n"); + if($this->totalPage > $this->mobilePage) $titlePageStr = sprintf("(%d/%d)",$this->mobilePage, $this->totalPage); + printf("%s%s\n", htmlspecialchars($this->title),htmlspecialchars($titlePageStr)); + } + // Output title + function printTitle() { + if($this->totalPage > $this->mobilePage) $titlePageStr = sprintf("(%d/%d)",$this->mobilePage, $this->totalPage); + printf('<%s%s>
%s', htmlspecialchars($this->title),htmlspecialchars($titlePageStr),"\n"); + } + + /** + * @brief Output information + * hasChilds() if there is a list of content types, otherwise output + **/ + function printContent() { + if($this->hasChilds()) { + foreach($this->getChilds() as $key => $val) { + if(!$val['link']) continue; + printf('%s
%s', $val['href'], $this->getNo(), $val['text'], "\n"); + if($val['extra']) printf("
%s\n",str_replace('
','
',$val['extra'])); + } + } else { + print(str_replace('
','
',$this->getContent())."\n"); + } + print "

"; + } + + /** + * @brief Button to output + **/ + function printBtn() { + if($this->nextUrl) { + $url = $this->nextUrl; + printf('%s
%s', $url->url, $url->text, "\n"); + } + if($this->prevUrl) { + $url = $this->prevUrl; + printf('%s
%s', $url->url, $url->text, "\n"); + } + // Select Language + if(!parent::isLangChange()){ + $url = getUrl('','lcm','1','sel_lang',Context::getLangType(),'return_uri',Context::get('current_url')); + printf('%s
%s', $url, 'Language : '.Context::getLang('select_lang'), "\n"); + } + else { + printf('%s
%s', Context::get('return_uri'), Context::getLang('lang_return'), "\n"); + } + if($this->upperUrl) { + $url = $this->upperUrl; + printf('%s', $url->url, $url->text, "\n"); + } + if($this->homeUrl) { + $url = $this->homeUrl; + printf('%s
%s', $url->text, $url->url, $url->text, "\n"); + } + } + // Footer information output + function printFooter() { + print("\n"); + } + } +?> diff --git a/addons/mobile/classes/mobile.class.php b/addons/mobile/classes/mobile.class.php index e6f7f6435..bd460da87 100644 --- a/addons/mobile/classes/mobile.class.php +++ b/addons/mobile/classes/mobile.class.php @@ -1,598 +1,565 @@ -lang = FileHandler::readFile('./files/cache/addons/mobile/setLangType/personal_settings/'.md5(trim($_SERVER['HTTP_USER_AGENT']).trim($_SERVER['HTTP_PHONE_NUMBER']).trim($_SERVER['HTTP_HTTP_PHONE_NUMBER'])).'.php'); - if($this->lang) { - $lang_supported = Context::get('lang_supported'); - $this->lang = str_replace(array(''),array('',''),$this->lang); - if(isset($lang_supported[$this->lang])) Context::setLangType($this->lang); - } - Context::loadLang(_XE_PATH_.'addons/mobile/lang'); - - $instance = new wap(); - - $mobilePage = (int)Context::get('mpage'); - if(!$mobilePage) $mobilePage = 1; - - $instance->setMobilePage($mobilePage); - - } - - return $instance; - } - - /** - * @brief constructor - **/ - function mobileXE() { - // navigation mode 체크 - if(Context::get('nm')) { - $this->navigationMode = 1; - $this->cmid = (int)Context::get('cmid'); - } - - if(Context::get('lcm')) { - $this->languageMode = 1; - $this->lang = Context::get('sel_lang'); - } - } - - /** - * @brief navigation mode 체크 - * navigationMode 세팅과 모듈 정보의 menu_srl이 있어야 navigation mode = true로 return - **/ - function isNavigationMode() { - return ($this->navigationMode && $this->module_info->menu_srl)?true:false; - } - - /** - * @brief langchange mode 체크 - * languageMode 세팅 있어야 true return - **/ - function isLangChange() { - if($this->languageMode) return true; - else return false; - } - - /** - * @brief 언어 설정 - * 쿠키가 안되기 때문에 휴대전화마다 고유한 파일로 언어설정을 저장하는 파일 생성 - **/ - function setLangType() { - $lang_supported = Context::get('lang_supported'); - // 언어 변수가 있는지 확인하고 변수가 유효한지 확인 - if($this->lang && isset($lang_supported[$this->lang])) { - $langbuff = FileHandler::readFile('./files/cache/addons/mobile/setLangType/personal_settings/'.md5(trim($_SERVER['HTTP_USER_AGENT']).trim($_SERVER['HTTP_PHONE_NUMBER']).trim($_SERVER['HTTP_HTTP_PHONE_NUMBER'])).'.php'); - if($langbuff) FileHandler::removeFile('./files/cache/addons/mobile/setLangType/personal_settings/'.md5(trim($_SERVER['HTTP_USER_AGENT']).trim($_SERVER['HTTP_PHONE_NUMBER']).trim($_SERVER['HTTP_HTTP_PHONE_NUMBER'])).'.php'); - $langbuff = 'lang.'**/ ?>'; - FileHandler::writeFile('./files/cache/addons/mobile/setLangType/personal_settings/'.md5(trim($_SERVER['HTTP_USER_AGENT']).trim($_SERVER['HTTP_PHONE_NUMBER']).trim($_SERVER['HTTP_HTTP_PHONE_NUMBER'])).'.php',$langbuff); - } - } - - /** - * @brief 현재 요청된 모듈 정보 세팅 - **/ - function setModuleInfo(&$module_info) { - if($this->module_info) return; - $this->module_info = $module_info; - } - - /** - * @brief 현재 실행중인 모듈 instance 세팅 - **/ - function setModuleInstance(&$oModule) { - if($this->oModule) return; - - // instance 저장 - $this->oModule = $oModule; - - // 현재 모듈의 메뉴가 설정되어 있으면 메뉴 정리 - $menu_cache_file = sprintf(_XE_PATH_.'files/cache/menu/%d.php', $this->module_info->menu_srl); - if(!file_exists($menu_cache_file)) return; - - include $menu_cache_file; - - // 정리된 menu들을 1차원으로 변경 - $this->getListedItems($menu->list, $listed_items, $node_list); - - $this->listed_items = $listed_items; - $this->node_list = $node_list; - $this->menu = $menu->list; - - $k = array_keys($node_list); - $v = array_values($node_list); - $this->index_mid = $k[0]; - - // 현재 메뉴의 depth가 1이상이면 상위 버튼을 지정 - $cur_menu_item = $listed_items[$node_list[$this->module_info->mid]]; - if($cur_menu_item['parent_srl']) { - $parent_srl = $cur_menu_item['parent_srl']; - if($parent_srl && $listed_items[$parent_srl]) { - $parent_item = $listed_items[$parent_srl]; - if($parent_item) $this->setUpperUrl(getUrl('','mid',$parent_item['mid']), Context::getLang('cmd_go_upper')); - } - } elseif (!$this->isNavigationMode()) { - $this->setUpperUrl(getUrl('','mid',$this->index_mid,'nm','1','cmid',0), Context::getLang('cmd_view_sitemap')); - } - } - - /** - * @brief 접속 브라우저의 헤더를 판단하여 브라우저 타입을 return - * 모바일 브라우저가 아닐 경우 null return - **/ - function getBrowserType() { - if(Context::get('smartphone')) return null; - // 브라우저 타입을 판별 - $browserAccept = $_SERVER['HTTP_ACCEPT']; - $userAgent = $_SERVER['HTTP_USER_AGENT']; - $wap_sid = $_SERVER['HTTP_X_UP_SUBNO']; - - if(eregi("SKT11", $userAgent) || eregi("skt", $browserAccept)) { - Context::set('mobile_skt',1); - return "wml"; - } - elseif(eregi("hdml", $browserAccept)) return "hdml"; - elseif(eregi("CellPhone", $userAgent)) return "mhtml"; - return null; - } - - /** - * @brief charset 지정 - **/ - function setCharSet($charset = 'UTF-8') { - if(!$charset) $charset = 'UTF-8'; - - //SKT는 euc-kr만 지원 - if(Context::get('mobile_skt')==1) $charset = 'euc-kr'; - - $this->charset = $charset; - } - - /** - * @brief 모바일 기기의 용량 제한에 다른 가상 페이지 지정 - **/ - function setMobilePage($page=1) { - if(!$page) $page = 1; - $this->mobilePage = $page; - } - - /** - * @brief 목록형 데이터 설정을 위한 child menu지정 - **/ - function setChilds($childs) { - // menu개수가 9개 이상일 경우 자체 페이징 처리 - $menu_count = count($childs); - if($menu_count>9) { - $startNum = ($this->mobilePage-1)*9; - $idx = 0; - $new_childs = array(); - foreach($childs as $k => $v) { - if($idx >= $startNum && $idx < $startNum+9) { - $new_childs[$k] = $v; - } - $idx ++; - } - $childs = $new_childs; - - $this->totalPage = (int)(($menu_count-1)/9)+1; - - // next/prevUrl 지정 - if($this->mobilePage>1) { - $url = getUrl('mid',$_GET['mid'],'mpage',$this->mobilePage-1); - $text = sprintf('%s (%d/%d)', Context::getLang('cmd_prev'), $this->mobilePage-1, $this->totalPage); - $this->setPrevUrl($url, $text); - } - - if($this->mobilePage<$this->totalPage) { - $url = getUrl('mid',$_GET['mid'],'mpage',$this->mobilePage+1); - $text = sprintf('%s (%d/%d)', Context::getLang('cmd_next'), $this->mobilePage+1, $this->totalPage); - $this->setNextUrl($url, $text); - } - } - $this->childs = $childs; - } - - /** - * @brief menu 출력대상이 있는지 확인 - **/ - function hasChilds() { - return count($this->childs)?true:0; - } - - /** - * @brief child menu반환 - **/ - function getChilds() { - return $this->childs; - } - - /** - * @brief title 지정 - **/ - function setTitle($title) { - $oModuleController = &getController('module'); - $this->title = $title; - $oModuleController->replaceDefinedLangCode($this->title); - } - - /** - * @brief title 반환 - **/ - function getTitle() { - return $this->title; - } - - /** - * @brief 컨텐츠 정리 - * HTML 컨텐츠에서 텍스트와 링크만 추출하는 기능 - **/ - function setContent($content) { - $oModuleController = &getController('module'); - $allow_tag_array = array('','
','

','','','','','','','','','','", "" ], + legend: [ 1, "
", "
" ], + thead: [ 1, "
'); - - - // 링크/ 줄바꿈, 강조만 제외하고 모든 태그 제거 - $content = strip_tags($content, implode($allow_tag_array)); - - // 탭 여백 제거 - $content = str_replace("\t", "", $content); - - // 2번 이상 반복되는 공백과 줄나눔을 제거 - $content = preg_replace('/( ){2,}/s', '', $content); - $content = preg_replace("/([\r\n]+)/s", "\r\n", $content); - $content = preg_replace(array("/","
"), array("
","
"), $content); - - while(strpos($content, '

')) { - $content = str_replace('

','
',$content); - } - - // 모바일의 경우 한 덱에 필요한 사이즈가 적어서 내용을 모두 페이지로 나눔 - $contents = array(); - while($content) { - $tmp = $this->cutStr($content, $this->deckSize, ''); - $contents[] = $tmp; - $content = substr($content, strlen($tmp)); - - //$content = str_replace(array('&','<','>','"','&nbsp;'), array('&','<','>','"',' '), $content); - - foreach($allow_tag_array as $tag) { - if($tag == '
') continue; - $tag_open_pos = strpos($content, str_replace('>','',$tag)); - $tag_close_pos = strpos($content, str_replace('<','totalPage = count($contents); - - // next/prevUrl 지정 - if($this->mobilePage>1) { - $url = getUrl('mid',$_GET['mid'],'mpage',$this->mobilePage-1); - $text = sprintf('%s (%d/%d)', Context::getLang('cmd_prev'), $this->mobilePage-1, $this->totalPage); - $this->setPrevUrl($url, $text); - } - - if($this->mobilePage<$this->totalPage) { - $url = getUrl('mid',$_GET['mid'],'mpage',$this->mobilePage+1); - $text = sprintf('%s (%d/%d)', Context::getLang('cmd_next'), $this->mobilePage+1, $this->totalPage); - $this->setNextUrl($url, $text); - } - - $this->content = $contents[$this->mobilePage-1]; - $oModuleController->replaceDefinedLangCode($this->content); - $content = str_replace(array('$','\''), array('$$','''), $content); - } - - /** - * @brief byte수로 자르는 함수 - **/ - function cutStr($string, $cut_size) { - return preg_match('/.{'.$cut_size.'}/su', $string, $arr) ? $arr[0] : $string; - } - - /** - * @brief 컨텐츠 반환 - **/ - function getContent() { - return $this->content; - } - - /** - * @brief home url 지정 - **/ - function setHomeUrl($url, $text) { - if(!$url) $url = '#'; - $this->homeUrl->url = $url; - $this->homeUrl->text = $text; - } - - /** - * @brief upper url 지정 - **/ - function setUpperUrl($url, $text) { - if(!$url) $url = '#'; - $this->upperUrl->url = $url; - $this->upperUrl->text = $text; - } - - /** - * @brief prev url 지정 - **/ - function setPrevUrl($url, $text) { - if(!$url) $url = '#'; - $this->prevUrl->url = $url; - $this->prevUrl->text = $text; - } - - /** - * @brief next url 지정 - **/ - function setNextUrl($url, $text) { - if(!$url) $url = '#'; - $this->nextUrl->url = $url; - $this->nextUrl->text = $text; - } - - /** - * @brief 다음, 이전, 상위 이외에 기타 버튼 지정 - **/ - function setEtcBtn($url, $text) { - if(!$url) $url = '#'; - $etc['url'] = $url; - $etc['text'] = htmlspecialchars($text); - $this->etcBtn[] = $etc; - } - - /** - * @brief display - **/ - function display() { - // 홈버튼 지정 - $this->setHomeUrl(getUrl(), Context::getLang('cmd_go_home')); - - // 제목 지정 - if(!$this->title) $this->setTitle(Context::getBrowserTitle()); - - ob_start(); - - // 헤더를 출력 - $this->printHeader(); - - // 제목을 출력 - $this->printTitle(); - - // 내용 출력 - $this->printContent(); - - // 버튼 출력 - $this->printBtn(); - - // 푸터를 출력 - $this->printFooter(); - - $content = ob_get_clean(); - - // 변환 후 출력 - if(strtolower($this->charset) == 'utf-8') print $content; - else print iconv('UTF-8',$this->charset."//TRANSLIT//IGNORE", $content); - - exit(); - } - - /** - * @brief 페이지 이동 - **/ - function movepage($url) { - header("location:$url"); - exit(); - } - - /** - * @brief 목록등에서 일련 번호를 리턴한다 - **/ - function getNo() { - $this->no++; - $str = $this->no; - return $str; - } - - /** - * @brief XE의 Menu 모듈이 값을 사용하기 쉽게 정리해주는 함수 - **/ - function getListedItems($menu, &$listed_items, &$node_list) { - if(!count($menu)) return; - foreach($menu as $node_srl => $item) { - if(preg_match('/^([a-zA-Z0-9\_\-]+)$/', $item['url'])) { - $mid = $item['mid'] = $item['url']; - $node_list[$mid] = $node_srl; - } else { - $mid = $item['mid'] = null; - } - - $listed_items[$node_srl] = $item; - $this->getListedItems($item['list'], $listed_items, $node_list); - } - } - - /** - * @brief XE 네비게이션 출력 - **/ - function displayNavigationContent() { - $childs = array(); - - if($this->cmid) { - $cur_item = $this->listed_items[$this->cmid]; - $upper_srl = $cur_item['parent_srl'];; - $list = $cur_item['list'];; - $this->setUpperUrl(getUrl('cmid',$upper_srl), Context::getLang('cmd_go_upper')); - if(preg_match('/^([a-zA-Z0-9\_\-]+)$/', $cur_item['url'])) { - $obj = null; - $obj['href'] = getUrl('','mid',$cur_item['url']); - $obj['link'] = $obj['text'] = '['.$cur_item['text'].']'; - $childs[] = $obj; - } - - } else { - $list = $this->menu; - $upper_srl = 0; - } - - - if(count($list)) { - foreach($list as $key => $val) { - if(!$val['text']) continue; - $obj = null; - if(!count($val['list'])) { - $obj['href'] = getUrl('','mid',$val['url']); - } else { - $obj['href'] = getUrl('cmid',$val['node_srl']); - } - $obj['link'] = $obj['text'] = $val['text']; - $childs[] = $obj; - } - $this->setChilds($childs); - } - - // 출력 - $this->display(); - } - - /** - * @brief 언어설정 메뉴 출력 - **/ - function displayLangSelect() { - $childs = array(); - - $this->lang = FileHandler::readFile('./files/cache/addons/mobile/setLangType/personal_settings/'.md5(trim($_SERVER['HTTP_USER_AGENT']).trim($_SERVER['HTTP_PHONE_NUMBER']).trim($_SERVER['HTTP_HTTP_PHONE_NUMBER'])).'.php'); - if($this->lang) { - $this->lang = str_replace(array(''),array('',''),$this->lang); - Context::setLangType($this->lang); - } - $lang_supported = Context::get('lang_supported'); - $lang_type = Context::getLangType(); - $obj = null; - $obj['link'] = $obj['text'] = Context::getLang('president_lang').' : '.$lang_supported[$lang_type]; - $obj['href'] = getUrl('sel_lang',$lang_type); - $childs[] = $obj; - - if(is_array($lang_supported)) { - foreach($lang_supported as $key => $val) { - $obj = null; - $obj['link'] = $obj['text'] = $val; - $obj['href'] = getUrl('sel_lang',$key); - $childs[] = $obj; - } - } - - $this->setChilds($childs); - - $this->display(); - } - - /** - * @brief 모듈의 WAP 클래스 객체 생성하여 WAP 준비 - **/ - function displayModuleContent() { - // 선택된 모듈의 WAP class 객체 생성 - $oModule = &getWap($this->module_info->module); - if(!$oModule || !method_exists($oModule, 'procWAP') ) return; - - $vars = get_object_vars($this->oModule); - if(count($vars)) foreach($vars as $key => $val) $oModule->{$key} = $val; - - // 실행 - $oModule->procWAP($this); - - // 출력 - $this->display(); - } - - /** - * @brief WAP 컨텐츠를 별도로 구할 수 없으면 최종 결과물을 출력 - **/ - function displayContent() { - Context::set('layout','none'); - - // 템플릿 컴파일 - $oTemplate = new TemplateHandler(); - $oContext = &Context::getInstance(); - - $content = $oTemplate->compile($this->oModule->getTemplatePath(), $this->oModule->getTemplateFile()); - $this->setContent($content); - - // 출력 - $this->display(); - } - } -?> +lang = FileHandler::readFile('./files/cache/addons/mobile/setLangType/personal_settings/'.md5(trim($_SERVER['HTTP_USER_AGENT']).trim($_SERVER['HTTP_PHONE_NUMBER']).trim($_SERVER['HTTP_HTTP_PHONE_NUMBER'])).'.php'); + if($this->lang) { + $lang_supported = Context::get('lang_supported'); + $this->lang = str_replace(array(''),array('',''),$this->lang); + if(isset($lang_supported[$this->lang])) Context::setLangType($this->lang); + } + Context::loadLang(_XE_PATH_.'addons/mobile/lang'); + + $instance = new wap(); + + $mobilePage = (int)Context::get('mpage'); + if(!$mobilePage) $mobilePage = 1; + + $instance->setMobilePage($mobilePage); + + } + + return $instance; + } + + /** + * @brief constructor + **/ + function mobileXE() { + // Check navigation mode + if(Context::get('nm')) { + $this->navigationMode = 1; + $this->cmid = (int)Context::get('cmid'); + } + + if(Context::get('lcm')) { + $this->languageMode = 1; + $this->lang = Context::get('sel_lang'); + } + } + + /** + * @brief Check navigation mode + * navigationMode settings and modules of information must be menu_srl return to navigation mode = true + **/ + function isNavigationMode() { + return ($this->navigationMode && $this->module_info->menu_srl)?true:false; + } + + /** + * @brief Check langchange mode + * true return should be set languageMode + **/ + function isLangChange() { + if($this->languageMode) return true; + else return false; + } + + /** + * @brief Language settings + * Cookies Since you set your phone to store language-specific file, file creation + **/ + function setLangType() { + $lang_supported = Context::get('lang_supported'); + // Make sure that the language variables and parameters are valid + if($this->lang && isset($lang_supported[$this->lang])) { + $langbuff = FileHandler::readFile('./files/cache/addons/mobile/setLangType/personal_settings/'.md5(trim($_SERVER['HTTP_USER_AGENT']).trim($_SERVER['HTTP_PHONE_NUMBER']).trim($_SERVER['HTTP_HTTP_PHONE_NUMBER'])).'.php'); + if($langbuff) FileHandler::removeFile('./files/cache/addons/mobile/setLangType/personal_settings/'.md5(trim($_SERVER['HTTP_USER_AGENT']).trim($_SERVER['HTTP_PHONE_NUMBER']).trim($_SERVER['HTTP_HTTP_PHONE_NUMBER'])).'.php'); + $langbuff = 'lang.'**/ ?>'; + FileHandler::writeFile('./files/cache/addons/mobile/setLangType/personal_settings/'.md5(trim($_SERVER['HTTP_USER_AGENT']).trim($_SERVER['HTTP_PHONE_NUMBER']).trim($_SERVER['HTTP_HTTP_PHONE_NUMBER'])).'.php',$langbuff); + } + } + + /** + * @brief Information currently requested module settings + **/ + function setModuleInfo(&$module_info) { + if($this->module_info) return; + $this->module_info = $module_info; + } + + /** + * @brief Set the module instance is currently running + **/ + function setModuleInstance(&$oModule) { + if($this->oModule) return; + // Save instance + $this->oModule = $oModule; + // Of the current module if there is a menu by menu + $menu_cache_file = sprintf(_XE_PATH_.'files/cache/menu/%d.php', $this->module_info->menu_srl); + if(!file_exists($menu_cache_file)) return; + + include $menu_cache_file; + // One-dimensional arrangement of menu changes + $this->getListedItems($menu->list, $listed_items, $node_list); + + $this->listed_items = $listed_items; + $this->node_list = $node_list; + $this->menu = $menu->list; + + $k = array_keys($node_list); + $v = array_values($node_list); + $this->index_mid = $k[0]; + // The depth of the current menu, the top button to specify if one or more + $cur_menu_item = $listed_items[$node_list[$this->module_info->mid]]; + if($cur_menu_item['parent_srl']) { + $parent_srl = $cur_menu_item['parent_srl']; + if($parent_srl && $listed_items[$parent_srl]) { + $parent_item = $listed_items[$parent_srl]; + if($parent_item) $this->setUpperUrl(getUrl('','mid',$parent_item['mid']), Context::getLang('cmd_go_upper')); + } + } elseif (!$this->isNavigationMode()) { + $this->setUpperUrl(getUrl('','mid',$this->index_mid,'nm','1','cmid',0), Context::getLang('cmd_view_sitemap')); + } + } + + /** + * @brief Access the browser's header to determine the return type of the browser + * Mobile browser, if not null return + **/ + function getBrowserType() { + if(Context::get('smartphone')) return null; + // Determine the type of browser + $browserAccept = $_SERVER['HTTP_ACCEPT']; + $userAgent = $_SERVER['HTTP_USER_AGENT']; + $wap_sid = $_SERVER['HTTP_X_UP_SUBNO']; + + if(preg_match("/SKT11/i", $userAgent) || preg_match("/skt/i", $browserAccept)) { + Context::set('mobile_skt',1); + return "wml"; + } + elseif(preg_match("/hdml/i", $browserAccept)) return "hdml"; + elseif(preg_match("/CellPhone/i", $userAgent)) return "mhtml"; + return null; + } + + /** + * @brief Specify charset + **/ + function setCharSet($charset = 'UTF-8') { + if(!$charset) $charset = 'UTF-8'; + // SKT supports the euc-kr + if(Context::get('mobile_skt')==1) $charset = 'euc-kr'; + + $this->charset = $charset; + } + + /** + * @brief Limited capacity of mobile devices, specifying a different virtual page + **/ + function setMobilePage($page=1) { + if(!$page) $page = 1; + $this->mobilePage = $page; + } + + /** + * @brief Mokrokhyeong child menu for specifying the data set + **/ + function setChilds($childs) { + // If more than nine the number of menu paging processing itself + $menu_count = count($childs); + if($menu_count>9) { + $startNum = ($this->mobilePage-1)*9; + $idx = 0; + $new_childs = array(); + foreach($childs as $k => $v) { + if($idx >= $startNum && $idx < $startNum+9) { + $new_childs[$k] = $v; + } + $idx ++; + } + $childs = $new_childs; + + $this->totalPage = (int)(($menu_count-1)/9)+1; + // next/prevUrl specify + if($this->mobilePage>1) { + $url = getUrl('mid',$_GET['mid'],'mpage',$this->mobilePage-1); + $text = sprintf('%s (%d/%d)', Context::getLang('cmd_prev'), $this->mobilePage-1, $this->totalPage); + $this->setPrevUrl($url, $text); + } + + if($this->mobilePage<$this->totalPage) { + $url = getUrl('mid',$_GET['mid'],'mpage',$this->mobilePage+1); + $text = sprintf('%s (%d/%d)', Context::getLang('cmd_next'), $this->mobilePage+1, $this->totalPage); + $this->setNextUrl($url, $text); + } + } + $this->childs = $childs; + } + + /** + * @brief Check the menu to be output + **/ + function hasChilds() { + return count($this->childs)?true:0; + } + + /** + * @brief Returns the child menu + **/ + function getChilds() { + return $this->childs; + } + + /** + * @brief Specify title + **/ + function setTitle($title) { + $oModuleController = &getController('module'); + $this->title = $title; + $oModuleController->replaceDefinedLangCode($this->title); + } + + /** + * @brief return title + **/ + function getTitle() { + return $this->title; + } + + /** + * @brief Content Cleanup + * In HTML content, the ability to extract text and links + **/ + function setContent($content) { + $oModuleController = &getController('module'); + $allow_tag_array = array('
','
','

','','','','','','','','','','\s]+\/)>/g,P={option:[1, -""],legend:[1,"
","
"],thead:[1,"
'); + // Links/wrap, remove all tags except gangjoman + $content = strip_tags($content, implode($allow_tag_array)); + // Margins tab removed + $content = str_replace("\t", "", $content); + // Repeat two more times the space and remove julnanumeul + $content = preg_replace('/( ){2,}/s', '', $content); + $content = preg_replace("/([\r\n]+)/s", "\r\n", $content); + $content = preg_replace(array("/","
"), array("
","
"), $content); + + while(strpos($content, '

')) { + $content = str_replace('

','
',$content); + } + // If the required size of a deck of mobile content to write down all the dividing pages + $contents = array(); + while($content) { + $tmp = $this->cutStr($content, $this->deckSize, ''); + $contents[] = $tmp; + $content = substr($content, strlen($tmp)); + + //$content = str_replace(array('&','<','>','"','&nbsp;'), array('&','<','>','"',' '), $content); + + foreach($allow_tag_array as $tag) { + if($tag == '
') continue; + $tag_open_pos = strpos($content, str_replace('>','',$tag)); + $tag_close_pos = strpos($content, str_replace('<','totalPage = count($contents); + // next/prevUrl specify + if($this->mobilePage>1) { + $url = getUrl('mid',$_GET['mid'],'mpage',$this->mobilePage-1); + $text = sprintf('%s (%d/%d)', Context::getLang('cmd_prev'), $this->mobilePage-1, $this->totalPage); + $this->setPrevUrl($url, $text); + } + + if($this->mobilePage<$this->totalPage) { + $url = getUrl('mid',$_GET['mid'],'mpage',$this->mobilePage+1); + $text = sprintf('%s (%d/%d)', Context::getLang('cmd_next'), $this->mobilePage+1, $this->totalPage); + $this->setNextUrl($url, $text); + } + + $this->content = $contents[$this->mobilePage-1]; + $oModuleController->replaceDefinedLangCode($this->content); + $content = str_replace(array('$','\''), array('$$','''), $content); + } + + /** + * @brief cutting the number of byte functions + **/ + function cutStr($string, $cut_size) { + return preg_match('/.{'.$cut_size.'}/su', $string, $arr) ? $arr[0] : $string; + } + + /** + * @brief Return content + **/ + function getContent() { + return $this->content; + } + + /** + * @brief Specifies the home url + **/ + function setHomeUrl($url, $text) { + if(!$url) $url = '#'; + $this->homeUrl->url = $url; + $this->homeUrl->text = $text; + } + + /** + * @brief Specify upper url + **/ + function setUpperUrl($url, $text) { + if(!$url) $url = '#'; + $this->upperUrl->url = $url; + $this->upperUrl->text = $text; + } + + /** + * @brief Specify prev url + **/ + function setPrevUrl($url, $text) { + if(!$url) $url = '#'; + $this->prevUrl->url = $url; + $this->prevUrl->text = $text; + } + + /** + * @brief Specify next url + **/ + function setNextUrl($url, $text) { + if(!$url) $url = '#'; + $this->nextUrl->url = $url; + $this->nextUrl->text = $text; + } + + /** + * @brief Next, Previous, Top button assignments other than + **/ + function setEtcBtn($url, $text) { + if(!$url) $url = '#'; + $etc['url'] = $url; + $etc['text'] = htmlspecialchars($text); + $this->etcBtn[] = $etc; + } + + /** + * @brief display + **/ + function display() { + // Home button assignments + $this->setHomeUrl(getUrl(), Context::getLang('cmd_go_home')); + // Specify the title + if(!$this->title) $this->setTitle(Context::getBrowserTitle()); + + ob_start(); + // Output header + $this->printHeader(); + // Output title + $this->printTitle(); + // Information output + $this->printContent(); + // Button output + $this->printBtn(); + // Footer output + $this->printFooter(); + + $content = ob_get_clean(); + // After conversion output + if(strtolower($this->charset) == 'utf-8') print $content; + else print iconv('UTF-8',$this->charset."//TRANSLIT//IGNORE", $content); + + exit(); + } + + /** + * @brief Move page + **/ + function movepage($url) { + header("location:$url"); + exit(); + } + + /** + * @brief And returns a list of serial numbers in + **/ + function getNo() { + $this->no++; + $str = $this->no; + return $str; + } + + /** + * @brief XE is easy to use Menu module is relieved during the function, value + **/ + function getListedItems($menu, &$listed_items, &$node_list) { + if(!count($menu)) return; + foreach($menu as $node_srl => $item) { + if(preg_match('/^([a-zA-Z0-9\_\-]+)$/', $item['url'])) { + $mid = $item['mid'] = $item['url']; + $node_list[$mid] = $node_srl; + } else { + $mid = $item['mid'] = null; + } + + $listed_items[$node_srl] = $item; + $this->getListedItems($item['list'], $listed_items, $node_list); + } + } + + /** + * @brief XE navigation output + **/ + function displayNavigationContent() { + $childs = array(); + + if($this->cmid) { + $cur_item = $this->listed_items[$this->cmid]; + $upper_srl = $cur_item['parent_srl'];; + $list = $cur_item['list'];; + $this->setUpperUrl(getUrl('cmid',$upper_srl), Context::getLang('cmd_go_upper')); + if(preg_match('/^([a-zA-Z0-9\_\-]+)$/', $cur_item['url'])) { + $obj = null; + $obj['href'] = getUrl('','mid',$cur_item['url']); + $obj['link'] = $obj['text'] = '['.$cur_item['text'].']'; + $childs[] = $obj; + } + + } else { + $list = $this->menu; + $upper_srl = 0; + } + + + if(count($list)) { + foreach($list as $key => $val) { + if(!$val['text']) continue; + $obj = null; + if(!count($val['list'])) { + $obj['href'] = getUrl('','mid',$val['url']); + } else { + $obj['href'] = getUrl('cmid',$val['node_srl']); + } + $obj['link'] = $obj['text'] = $val['text']; + $childs[] = $obj; + } + $this->setChilds($childs); + } + // Output + $this->display(); + } + + /** + * @brief Language Settings menu, the output + **/ + function displayLangSelect() { + $childs = array(); + + $this->lang = FileHandler::readFile('./files/cache/addons/mobile/setLangType/personal_settings/'.md5(trim($_SERVER['HTTP_USER_AGENT']).trim($_SERVER['HTTP_PHONE_NUMBER']).trim($_SERVER['HTTP_HTTP_PHONE_NUMBER'])).'.php'); + if($this->lang) { + $this->lang = str_replace(array(''),array('',''),$this->lang); + Context::setLangType($this->lang); + } + $lang_supported = Context::get('lang_supported'); + $lang_type = Context::getLangType(); + $obj = null; + $obj['link'] = $obj['text'] = Context::getLang('president_lang').' : '.$lang_supported[$lang_type]; + $obj['href'] = getUrl('sel_lang',$lang_type); + $childs[] = $obj; + + if(is_array($lang_supported)) { + foreach($lang_supported as $key => $val) { + $obj = null; + $obj['link'] = $obj['text'] = $val; + $obj['href'] = getUrl('sel_lang',$key); + $childs[] = $obj; + } + } + + $this->setChilds($childs); + + $this->display(); + } + + /** + * @brief Module to create a class object of the WAP WAP ready + **/ + function displayModuleContent() { + // Create WAP class objects of the selected module + $oModule = &getWap($this->module_info->module); + if(!$oModule || !method_exists($oModule, 'procWAP') ) return; + + $vars = get_object_vars($this->oModule); + if(count($vars)) foreach($vars as $key => $val) $oModule->{$key} = $val; + // Run + $oModule->procWAP($this); + // Output + $this->display(); + } + + /** + * @brief WAP content is available as a separate output if the final results + **/ + function displayContent() { + Context::set('layout','none'); + // Compile a template + $oTemplate = new TemplateHandler(); + $oContext = &Context::getInstance(); + + $content = $oTemplate->compile($this->oModule->getTemplatePath(), $this->oModule->getTemplateFile()); + $this->setContent($content); + // Output + $this->display(); + } + } +?> diff --git a/addons/mobile/classes/wml.class.php b/addons/mobile/classes/wml.class.php index 08103b37c..a9a91fb95 100644 --- a/addons/mobile/classes/wml.class.php +++ b/addons/mobile/classes/wml.class.php @@ -1,106 +1,104 @@ -charset); - if($this->totalPage > $this->mobilePage) $titlePageStr = sprintf("(%d/%d)",$this->mobilePage, $this->totalPage); - print("charset."\"?>\n"); - // 카드제목 - printf("\n\n

\n",htmlspecialchars($this->title),htmlspecialchars($titlePageStr)); - } - - /** - * @brief 제목을 출력 - **/ - function printTitle() { - if($this->totalPage > $this->mobilePage) $titlePageStr = sprintf("(%d/%d)",$this->mobilePage, $this->totalPage); - printf('<%s%s>
%s', htmlspecialchars($this->title),htmlspecialchars($titlePageStr),"\n"); - } - - /** - * @brief 내용을 출력 - * hasChilds()가 있으면 목록형을 그렇지 않으면 컨텐츠를 출력 - **/ - function printContent() { - if($this->hasChilds()) { - foreach($this->getChilds() as $key => $val) { - if(!$val['link']) continue; - printf('%s', $this->getNo(), htmlspecialchars($val['text']), $val['href'], "\n"); - if($val['extra']) printf("%s\n",$val['extra']); - } - } else { - printf('%s
%s', str_replace("
","
",$this->getContent()),"\n"); - } - print('
'); - } - - /** - * @brief 버튼을 출력함 - **/ - function printBtn() { - if($this->nextUrl) { - $url = $this->nextUrl; - printf('%s', $url->text, $url->url, "\n"); - } - if($this->prevUrl) { - $url = $this->prevUrl; - printf('%s', $url->text, $url->url, "\n"); - } - // 기타 해당사항 없는 버튼 출력 담당 (array로 전달) type?? - if($this->etcBtn) { - if(is_array($this->etcBtn)) { - foreach($this->etcBtn as $key=>$val) { - printf('%s', $key, $val['text'], $val['url'], "\n"); - } - } - } - // 언어선택 - if(!parent::isLangChange()){ - $url = getUrl('','lcm','1','sel_lang',Context::getLangType(),'return_uri',Context::get('current_url')); - printf('%s', 'Language : '.Context::getLang('select_lang'), $url, "\n"); - } - else { - printf('%s', Context::getLang('lang_return'), Context::get('return_uri'), "\n"); - } - if($this->homeUrl) { - $url = $this->homeUrl; - printf('%s', $url->text, $url->url, "\n"); - } - if($this->upperUrl) { - $url = $this->upperUrl; - printf('%s', $url->text, $url->url, "\n"); - } - } - - // 푸터 정보를 출력 - function printFooter() { - print("

\n
\n
"); - } - - // 목록등에서 일련 번호를 리턴한다 - function getNo() { - if(Context::get('mobile_skt')==1) { - return "vnd.skmn".parent::getNo(); - } - else { - return parent::getNo(); - } - return $str; - } - } -?> +charset); + if($this->totalPage > $this->mobilePage) $titlePageStr = sprintf("(%d/%d)",$this->mobilePage, $this->totalPage); + print("charset."\"?>\n"); + // Card Title + printf("\n\n

\n",htmlspecialchars($this->title),htmlspecialchars($titlePageStr)); + } + + /** + * @brief Output title + **/ + function printTitle() { + if($this->totalPage > $this->mobilePage) $titlePageStr = sprintf("(%d/%d)",$this->mobilePage, $this->totalPage); + printf('<%s%s>
%s', htmlspecialchars($this->title),htmlspecialchars($titlePageStr),"\n"); + } + + /** + * @brief Output information + * hasChilds() if there is a list of content types, otherwise output + **/ + function printContent() { + if($this->hasChilds()) { + foreach($this->getChilds() as $key => $val) { + if(!$val['link']) continue; + printf('%s', $this->getNo(), htmlspecialchars($val['text']), $val['href'], "\n"); + if($val['extra']) printf("%s\n",$val['extra']); + } + } else { + printf('%s
%s', str_replace("
","
",$this->getContent()),"\n"); + } + print('
'); + } + + /** + * @brief Button to output + **/ + function printBtn() { + if($this->nextUrl) { + $url = $this->nextUrl; + printf('%s', $url->text, $url->url, "\n"); + } + if($this->prevUrl) { + $url = $this->prevUrl; + printf('%s', $url->text, $url->url, "\n"); + } + // Others are not applicable in charge of the button output (array passed) type?? + if($this->etcBtn) { + if(is_array($this->etcBtn)) { + foreach($this->etcBtn as $key=>$val) { + printf('%s', $key, $val['text'], $val['url'], "\n"); + } + } + } + // Select Language + if(!parent::isLangChange()){ + $url = getUrl('','lcm','1','sel_lang',Context::getLangType(),'return_uri',Context::get('current_url')); + printf('%s', 'Language : '.Context::getLang('select_lang'), $url, "\n"); + } + else { + printf('%s', Context::getLang('lang_return'), Context::get('return_uri'), "\n"); + } + if($this->homeUrl) { + $url = $this->homeUrl; + printf('%s', $url->text, $url->url, "\n"); + } + if($this->upperUrl) { + $url = $this->upperUrl; + printf('%s', $url->text, $url->url, "\n"); + } + } + // Footer information output + function printFooter() { + print("

\n
\n
"); + } + // And returns a list of serial numbers in + function getNo() { + if(Context::get('mobile_skt')==1) { + return "vnd.skmn".parent::getNo(); + } + else { + return parent::getNo(); + } + return $str; + } + } +?> diff --git a/addons/mobile/conf/info.xml b/addons/mobile/conf/info.xml index 3dcee3654..d9f43b2a5 100644 --- a/addons/mobile/conf/info.xml +++ b/addons/mobile/conf/info.xml @@ -1,112 +1,112 @@ - - - 모바일XE 애드온 - モバイルXEアドオン - 手机XE插件 - Mobile XE - Mobile XE - Mobile XE - XE行動上網 - - 모바일에서 접속시 헤더 정보를 분석하여 메뉴 - 모듈의 관계를 이용하여 WAP 태그로 출력하는 애드온입니다. - wml, hdml, mhtml를 지원하고 그 이외의 경우에는 동작하지 않습니다. - - - モバイル端末機からアクセス時、ヘッダー(header)情報を分析して「メニュー」と「モジュール」の関係を利用してWAPタグに変換表示するアドオンです。 - wml, hdml, mhtmlをサポートし、その以外は対応していません。 - - - 通过手机访问网站时将网页输出为WAP标签的插件。 - 支持语言:wml, hdml, mhtml - - - This addon displays WAP tag by analyzing header information on mobile connection. - Only wml, hdml, mhtml formats are provided. - - - Addon này hiển thị WAP Tag bởi việc phân tích thông tin khi kết nối bằng di động. - Chỉ hỗ trợ cho các định dạng wml, hdml, mhtml. - - - Данный аддон показывает WAP теги, анализирую информацию мобильного соединения. - Поддерживаются только wml, hdml, mhtml форматы. - - - 透過行動工具上網時,會將網頁轉換為WAP標籤顯示。 - 只限於 wml, hdml, mhtml格式。 - - 0.1.1 - 2009-05-23 - - - NHN - NHN - NHN - NHN - NHN - NHN - NHN - - - - - misol - misol - misol - misol - misol - misol - misol - - 언어선택 추가(WML, mHTML) - 인코딩 설정 개선 - 그 외 소소한 개선들 - - - - - 문자셋 - 文字コード - 编码 - Charset - Charset - Charset - 編碼 - - 모바일 기기의 경우 UTF-8 문자셋을 인식하지 못할 수 있습니다. - 문자셋에 원하시는 문자셋을 입력하면 자동으로 변환하여 출력하여 모바일에서 이상없이 출력하도록 합니다. - 기본값은 UTF-8입니다. - (*SK Telecom 휴대전화의 경우 euc-kr인코딩만 지원하므로, 강제로 euc-kr인코딩만 지원합니다.) - - - ある特定のモバイル機器ではutf-8文字コードの認識が出来ない場合があります。 - 文字コードを設定すると、(日本語だけの場合)該当文字コードに自動変換して正常に表示出来るようになります。 - 本アドオンのデフォルト値はUTF-8で、日本の携帯はshift-jisが一般的です。 - - - 手机有时无法识别utf-8编码,这时输入相应的编码值即可自动转换。 - 默认编码为UTF-8。 - - - utf-8 may be read with mobile tools. - Mobile tools will display correct charset when you input charset you want. - Default charset is UTF-8. - - - UTF-8 không thể đọc được cho các công cụ di động. - Những công cụ di động sẽ trình bày Charset đúng khi bạn nhập vào Charset bạn muốn. - Charset mặc định là UTF-8. - - - utf-8 may be read with mobile tools. - Mobile tools will display correct charset when you input charset you want. - Default charset is UTF-8. - - - 行動工具無法讀取utf-8編碼。 - 當您輸入所想要的編碼時,行動工具將會正確的顯示。 - 預設編碼是UTF-8. - - - - + + + 모바일XE 애드온 + モバイルXEアドオン + 手机XE插件 + Mobile XE + Mobile XE + Mobile XE + XE行動上網 + + 모바일에서 접속시 헤더 정보를 분석하여 메뉴 - 모듈의 관계를 이용하여 WAP 태그로 출력하는 애드온입니다. + wml, hdml, mhtml를 지원하고 그 이외의 경우에는 동작하지 않습니다. + + + モバイル端末機からアクセス時、ヘッダー(header)情報を分析して「メニュー」と「モジュール」の関係を利用してWAPタグに変換表示するアドオンです。 + wml, hdml, mhtmlをサポートし、その以外は対応していません。 + + + 通过手机访问网站时将网页输出为WAP标签的插件。 + 支持语言:wml, hdml, mhtml + + + This addon displays WAP tag by analyzing header information on mobile connection. + Only wml, hdml, mhtml formats are provided. + + + Addon này hiển thị WAP Tag bởi việc phân tích thông tin khi kết nối bằng di động. + Chỉ hỗ trợ cho các định dạng wml, hdml, mhtml. + + + Данный аддон показывает WAP теги, анализирую информацию мобильного соединения. + Поддерживаются только wml, hdml, mhtml форматы. + + + 透過行動工具上網時,會將網頁轉換為WAP標籤顯示。 + 只限於 wml, hdml, mhtml格式。 + + 0.1.1 + 2009-05-23 + + + NHN + NHN + NHN + NHN + NHN + NHN + NHN + + + + + misol + misol + misol + misol + misol + misol + misol + + 언어선택 추가(WML, mHTML) + 인코딩 설정 개선 + 그 외 소소한 개선들 + + + + + 문자셋 + 文字コード + 编码 + Charset + Charset + Charset + 編碼 + + 모바일 기기의 경우 UTF-8 문자셋을 인식하지 못할 수 있습니다. + 문자셋에 원하시는 문자셋을 입력하면 자동으로 변환하여 출력하여 모바일에서 이상없이 출력하도록 합니다. + 기본값은 UTF-8입니다. + (*SK Telecom 휴대전화의 경우 euc-kr인코딩만 지원하므로, 강제로 euc-kr인코딩만 지원합니다.) + + + ある特定のモバイル機器ではutf-8文字コードの認識が出来ない場合があります。 + 文字コードを設定すると、(日本語だけの場合)該当文字コードに自動変換して正常に表示出来るようになります。 + 本アドオンのデフォルト値はUTF-8で、日本の携帯はshift-jisが一般的です。 + + + 手机有时无法识别utf-8编码,这时输入相应的编码值即可自动转换。 + 默认编码为UTF-8。 + + + utf-8 may be read with mobile tools. + Mobile tools will display correct charset when you input charset you want. + Default charset is UTF-8. + + + UTF-8 không thể đọc được cho các công cụ di động. + Những công cụ di động sẽ trình bày Charset đúng khi bạn nhập vào Charset bạn muốn. + Charset mặc định là UTF-8. + + + utf-8 may be read with mobile tools. + Mobile tools will display correct charset when you input charset you want. + Default charset is UTF-8. + + + 行動工具無法讀取utf-8編碼。 + 當您輸入所想要的編碼時,行動工具將會正確的顯示。 + 預設編碼是UTF-8. + + + + diff --git a/addons/mobile/lang/en.lang.php b/addons/mobile/lang/en.lang.php deleted file mode 100644 index 806187185..000000000 --- a/addons/mobile/lang/en.lang.php +++ /dev/null @@ -1,15 +0,0 @@ -president_lang = 'selected Language'; - $lang->select_lang = 'select Language'; - $lang->lang_return = 'Go Back'; - - $lang->cmd_go_upper = 'Upper'; - $lang->cmd_go_home = 'Go Home'; - $lang->cmd_view_sitemap = 'View site map'; -?> diff --git a/addons/mobile/lang/jp.lang.php b/addons/mobile/lang/jp.lang.php deleted file mode 100644 index d9300e7e6..000000000 --- a/addons/mobile/lang/jp.lang.php +++ /dev/null @@ -1,17 +0,0 @@ -president_lang = '現在言語'; - $lang->select_lang = '言語選択'; - $lang->lang_return = '戻る'; - - $lang->cmd_go_upper = '上位メニュー'; - $lang->cmd_go_home = 'トップへ'; - $lang->cmd_view_sitemap = 'サイトマップ'; - -?> diff --git a/addons/mobile/lang/ko.lang.php b/addons/mobile/lang/ko.lang.php deleted file mode 100644 index 48bf404a0..000000000 --- a/addons/mobile/lang/ko.lang.php +++ /dev/null @@ -1,17 +0,0 @@ -president_lang = '현재 언어'; - $lang->select_lang = '언어 선택'; - $lang->lang_return = '돌아가기'; - - $lang->cmd_go_upper = '상위'; - $lang->cmd_go_home = '홈으로'; - $lang->cmd_view_sitemap = '사이트맵 보기'; - -?> diff --git a/addons/mobile/lang/lang.xml b/addons/mobile/lang/lang.xml new file mode 100644 index 000000000..fab31405b --- /dev/null +++ b/addons/mobile/lang/lang.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/addons/mobile/lang/ru.lang.php b/addons/mobile/lang/ru.lang.php deleted file mode 100644 index 87cc015cf..000000000 --- a/addons/mobile/lang/ru.lang.php +++ /dev/null @@ -1,17 +0,0 @@ -president_lang = 'Дейсвующй язык'; - $lang->select_lang = 'Выбор языка'; - $lang->lang_return = 'Вернуться'; - - $lang->cmd_go_upper = 'Вверх'; - $lang->cmd_go_home = 'На главную страницу'; - $lang->cmd_view_sitemap = 'Посмотреть карту сайта'; - -?> diff --git a/addons/mobile/lang/vi.lang.php b/addons/mobile/lang/vi.lang.php deleted file mode 100644 index 1ed1fa8a5..000000000 --- a/addons/mobile/lang/vi.lang.php +++ /dev/null @@ -1,18 +0,0 @@ -president_lang = 'Chọn ngôn ngữ'; - $lang->select_lang = 'Chọn ngôn ngữ'; - $lang->lang_return = 'Trở lại'; - - $lang->cmd_go_upper = 'Lên trên'; - $lang->cmd_go_home = 'Về trang chủ'; - $lang->cmd_view_sitemap = 'Xem sơ đồ Web'; -?> diff --git a/addons/mobile/lang/zh-CN.lang.php b/addons/mobile/lang/zh-CN.lang.php deleted file mode 100644 index 1610b859f..000000000 --- a/addons/mobile/lang/zh-CN.lang.php +++ /dev/null @@ -1,11 +0,0 @@ -cmd_go_upper = '上一级'; - $lang->cmd_go_home = '首页'; - $lang->cmd_view_sitemap = '网站地图'; -?> diff --git a/addons/mobile/lang/zh-TW.lang.php b/addons/mobile/lang/zh-TW.lang.php deleted file mode 100644 index 6de5fd45e..000000000 --- a/addons/mobile/lang/zh-TW.lang.php +++ /dev/null @@ -1,15 +0,0 @@ -president_lang = '已選擇語言'; - $lang->select_lang = '選擇語言'; - $lang->lang_return = '返回'; - - $lang->cmd_go_upper = '回上頁'; - $lang->cmd_go_home = '回首頁'; - $lang->cmd_view_sitemap = '網站地圖'; -?> diff --git a/addons/mobile/mobile.addon.php b/addons/mobile/mobile.addon.php index fd55b714c..15bde8a8a 100644 --- a/addons/mobile/mobile.addon.php +++ b/addons/mobile/mobile.addon.php @@ -1,61 +1,50 @@ - 모바일 처리를 위해 모듈의 일반 설정을 변경해야 할 경우 호출 - * - * after_module_proc > 모바일 컨텐츠 출력 - * 동작 조건 - **/ - - // 관리자 페이지는 무시 - if(Context::get('module')=='admin') return; - - // 동작 시점 관리 - if($called_position != 'before_module_proc' && $called_position != 'after_module_proc' ) return; - - // 모바일 브라우저가 아니라면 무시 - require_once(_XE_PATH_.'addons/mobile/classes/mobile.class.php'); - if(!mobileXE::getBrowserType()) return; - - // mobile instance 생성 - $oMobile = &mobileXE::getInstance(); - if(!$oMobile) return; - - // 애드온 설정에서 지정된 charset으로 지정 - $oMobile->setCharSet($addon_info->charset); - - // 모듈의 정보를 세팅 - $oMobile->setModuleInfo($this->module_info); - - // 현재 모듈 객체 등록 - $oMobile->setModuleInstance($this); - - // 네비게이트 모드이거나 WAP class가 있을 경우 미리 컨텐츠를 추출하여 출력/ 종료 - if($called_position == 'before_module_proc') { - - if($oMobile->isLangChange()) { - $oMobile->setLangType(); - $oMobile->displayLangSelect(); - } - - // 네비게이트 모드이면 네비게이션 컨텐츠 출력 - if($oMobile->isNavigationMode()) $oMobile->displayNavigationContent(); - - // WAP class가 있으면 WAP class를 통해 컨텐츠 출력 - else $oMobile->displayModuleContent(); - - // 네비게이트 모드가 아니고 WAP 클래스가 아니면 모듈의 결과를 출력 - } else if($called_position == 'after_module_proc') { - // 내용 준비 - $oMobile->displayContent(); - } -?> + call when changing general settings for mobile + * + * after_module_proc > display mobile content + * Condition + **/ +// Ignore admin page +if(Context::get('module')=='admin') return; +// Manage when to call it +if($called_position != 'before_module_proc' && $called_position != 'after_module_proc' ) return; +// Ignore if not mobile browser +require_once(_XE_PATH_.'addons/mobile/classes/mobile.class.php'); +if(!mobileXE::getBrowserType()) return; +// Generate mobile instance +$oMobile = &mobileXE::getInstance(); +if(!$oMobile) return; +// Specify charset on the add-on settings +$oMobile->setCharSet($addon_info->charset); +// Set module information +$oMobile->setModuleInfo($this->module_info); +// Register the current module object +$oMobile->setModuleInstance($this); +// Extract content and display/exit if navigate mode is or if WAP class exists +if($called_position == 'before_module_proc') { + + if($oMobile->isLangChange()) { + $oMobile->setLangType(); + $oMobile->displayLangSelect(); + } + // On navigation mode, display navigation content + if($oMobile->isNavigationMode()) $oMobile->displayNavigationContent(); + // If you have a WAP class content output via WAP class + else $oMobile->displayModuleContent(); +// If neither navigation mode nor WAP class is, display the module's result +} else if($called_position == 'after_module_proc') { + // Display + $oMobile->displayContent(); +} +?> diff --git a/addons/openid_delegation_id/conf/info.xml b/addons/openid_delegation_id/conf/info.xml index 796abb29e..9adf737f8 100644 --- a/addons/openid_delegation_id/conf/info.xml +++ b/addons/openid_delegation_id/conf/info.xml @@ -1,125 +1,125 @@ - - - OpenID delegation ID - OpenID - OpenID Delegation ID - OpenID Delegation ID - OpenID Delegation ID - Delegación ID para OpenID - OpenIDアドオン - Открытый ID(OpenID) - OpenID - - 본인의 도메인을 사용하여 오픈아이디로 활용할 수 있도록 합니다. - 꼭 설정을 통해서 openid provider관련 값을 입력후 사용해주세요. - - - 可以把本人的域名当分散式身份验证系统(OpenID)来使用。 - 必须在设置中输入openid provider相关值后再使用。 - - - This addon enables you to use your own domain name as an OpenID. - Just be sure to set the values related with openid provider before using. - - - Addon này cho phép bạn sử dụng tên miền của mình như một OpenID. - Hãy kiểm tra để đặt giá trị liên quan với OpenID trước khi sử dụng. - - - Dieses Addon ermöglicht es Ihnen, mit Ihrem eigenen Domain-Namen als OpenID. - Einfach sicher sein, dass die Werte im Zusammenhang mit OpenID-Provider, bevor Sie. - - - Utlizando su propio dominio puede usar como OpenID. - Debe utilizar luego de ingresar los valores relacionado con openid provider a través de la configuracion. - - - 保有しているオリジナルドメインをオープンIDとして活用することが出来ます。 - 必ず設定にて、OpenIDプロバイダーの関連情報を入力してから使用して下さい。 - - - Этот аддон позволяет Вам использовать Ваше доменное имя как OpenID. - Прежде, чем использовать, установите значения, имеющие отношение к openid-провайдеру . - - - 可將原本的域名當做OpenID來使用。 - 必須在設置中輸入openid provider相關資料後再使用。 - - 0.1 - 2007-02-28 - - - NHN - NHN - NHN - NHN - NHN - NHN - NHN - NHN - NHN - - - - - server - server - server - Server - server - Servidor - server - server - server - openid.server 값을 입력해 주세요. - 请输入 openid.server 值。 - Hãy nhập OpenID Server của bạn. - Please input your openid.server value. - Bitte geben Sie Ihre openid.server Wert. - Ingrese el valor del openid.server. - openid.server値を入力して下さい。 - Пожалуйста, введите Ваше значение openid.server - 請輸入 openid.server 值。 - - - delegate - delegate - Delegate - delegate - delegate - delegado - delegate - delegate - delegate - openid.delegate값을 입력해주세요. - 请输入 openid.delegate 值。 - Hãy nhập OpenID Delegate của bạn. - Please input your openid.delegate value. - Bitte geben Sie Ihre openid.delegate Wert. - Ingresar el valor del openid.delegate - openid.delegate値を入力して下さい。 - Пожалуйста, введите Ваше значение openid.delegate - 請輸入 openid.delegate 值。 - - - xrds - xrds - xrds - xrds - xrds - xrds - xrds - xrds - xrds - X-XRDS-Location값을 입력해주세요. - 请输入 X-XRDS-Location 值。 - Please input your X-XRDS-Location value. - Hãy nhập X-XRDS-Location của bạn. - Bitte geben Sie Ihre X-XRDS-Standort Wert. - Ingresar el valor de X-XRDS-Location - X-XRDS-Location値を入力して下さい。 - Пожалуйста, введите Ваше значение X-XRDS-Локации. - 請輸入 X-XRDS-Location 值。 - - - + + + OpenID delegation ID + OpenID + OpenID Delegation ID + OpenID Delegation ID + OpenID Delegation ID + Delegación ID para OpenID + OpenIDアドオン + Открытый ID(OpenID) + OpenID + + 본인의 도메인을 사용하여 오픈아이디로 활용할 수 있도록 합니다. + 꼭 설정을 통해서 openid provider관련 값을 입력후 사용해주세요. + + + 可以把本人的域名当分散式身份验证系统(OpenID)来使用。 + 必须在设置中输入openid provider相关值后再使用。 + + + This addon enables you to use your own domain name as an OpenID. + Just be sure to set the values related with openid provider before using. + + + Addon này cho phép bạn sử dụng tên miền của mình như một OpenID. + Hãy kiểm tra để đặt giá trị liên quan với OpenID trước khi sử dụng. + + + Dieses Addon ermöglicht es Ihnen, mit Ihrem eigenen Domain-Namen als OpenID. + Einfach sicher sein, dass die Werte im Zusammenhang mit OpenID-Provider, bevor Sie. + + + Utlizando su propio dominio puede usar como OpenID. + Debe utilizar luego de ingresar los valores relacionado con openid provider a través de la configuracion. + + + 保有しているオリジナルドメインをオープンIDとして活用することが出来ます。 + 必ず設定にて、OpenIDプロバイダーの関連情報を入力してから使用して下さい。 + + + Этот аддон позволяет Вам использовать Ваше доменное имя как OpenID. + Прежде, чем использовать, установите значения, имеющие отношение к openid-провайдеру . + + + 可將原本的域名當做OpenID來使用。 + 必須在設置中輸入openid provider相關資料後再使用。 + + 0.1 + 2007-02-28 + + + NHN + NHN + NHN + NHN + NHN + NHN + NHN + NHN + NHN + + + + + server + server + server + Server + server + Servidor + server + server + server + openid.server 값을 입력해 주세요. + 请输入 openid.server 值。 + Hãy nhập OpenID Server của bạn. + Please input your openid.server value. + Bitte geben Sie Ihre openid.server Wert. + Ingrese el valor del openid.server. + openid.server値を入力して下さい。 + Пожалуйста, введите Ваше значение openid.server + 請輸入 openid.server 值。 + + + delegate + delegate + Delegate + delegate + delegate + delegado + delegate + delegate + delegate + openid.delegate값을 입력해주세요. + 请输入 openid.delegate 值。 + Hãy nhập OpenID Delegate của bạn. + Please input your openid.delegate value. + Bitte geben Sie Ihre openid.delegate Wert. + Ingresar el valor del openid.delegate + openid.delegate値を入力して下さい。 + Пожалуйста, введите Ваше значение openid.delegate + 請輸入 openid.delegate 值。 + + + xrds + xrds + xrds + xrds + xrds + xrds + xrds + xrds + xrds + X-XRDS-Location값을 입력해주세요. + 请输入 X-XRDS-Location 值。 + Please input your X-XRDS-Location value. + Hãy nhập X-XRDS-Location của bạn. + Bitte geben Sie Ihre X-XRDS-Standort Wert. + Ingresar el valor de X-XRDS-Location + X-XRDS-Location値を入力して下さい。 + Пожалуйста, введите Ваше значение X-XRDS-Локации. + 請輸入 X-XRDS-Location 值。 + + + diff --git a/addons/openid_delegation_id/openid_delegation_id.addon.php b/addons/openid_delegation_id/openid_delegation_id.addon.php index aa5a373dd..db1e90c88 100644 --- a/addons/openid_delegation_id/openid_delegation_id.addon.php +++ b/addons/openid_delegation_id/openid_delegation_id.addon.php @@ -1,29 +1,27 @@ -server||!$addon_info->delegate||!$addon_info->xrds) return; - - $header_script = sprintf( - ''."\n". - ''."\n". - '', - $addon_info->server, - $addon_info->delegate, - $addon_info->xrds - ); - - Context::addHtmlHeader($header_script); -?> +server||!$addon_info->delegate||!$addon_info->xrds) return; + +$header_script = sprintf( + ''."\n". + ''."\n". + '', + $addon_info->server, + $addon_info->delegate, + $addon_info->xrds +); + +Context::addHtmlHeader($header_script); +?> diff --git a/addons/point_level_icon/conf/info.xml b/addons/point_level_icon/conf/info.xml index adde6ae69..201cdfb7c 100644 --- a/addons/point_level_icon/conf/info.xml +++ b/addons/point_level_icon/conf/info.xml @@ -1,62 +1,62 @@ - - - 포인트 레벨 아이콘 표시 애드온 - 积分级别图标 - ポイントレベルアイコン表示アドオン - Point Level Icon - Icon cấp độ của điểm - Point-Level-Symbol - Addon para mostar el nivel del ícono - Иконка уровня поинтов - 點數等級圖案 - - 포인트 시스템을 사용중일 경우 사용자 이름 앞에 레벨 아이콘을 표시하도록 합니다. - 레벨 아이콘은 모듈 > 포인트시스템에서 선택 가능합니다. - - - 使用积分系统时,可以在用户名前显示级别图标。 - 级别图标可以在模块 > 积分系统中进行选择。 - - - ポイントシステムを使用する場合、ユーザ名の前にレベルアイコンの表示します。 - レベルアイコンは、「モジュール > ポイントシステム」で選択します。 - - - This addon displays level icon in front of the user name when you are using the point system. - You can choose the level icon in Module > Point System. - - - Addon này sẽ hiển thị Icon cấp độ trước tên người sử dụng khi bạn sử dụng hệ thống tính điểm. - Bạn có thể chọn Icon cấp độ tại Module > cỉa hệ thống điểm. - - - Dieses Addon zeigt Level Icon vor dem Benutzernamen, wenn Sie die Punkte-System. - Sie können wählen, der Level Icon in Modul> Point-System. - - - Este addon muestra el nivel del ícono delante del nombre del usuario cuando es usado el sistema de puntos. - Tu puedes elegir los icono de cada nivel en el módulo > Sistema de Puntos. - - - Этот аддон отображает иконку уровня поинтов напротив имени пользователя, когда используется система поинтов. - Вы можете выбрать иконку уровня в Модуле Системы Поинтов. - - - 使用點數系統時,可以在用戶名前顯示等級圖案。 - 等級圖案可以在模組 > 點數系統中進行選擇。 - - 0.1 - 2007-07-26 - - - NHN - NHN - NHN - NHN - NHN - NHN - NHN - NHN - NHN - - + + + 포인트 레벨 아이콘 표시 애드온 + 积分级别图标 + ポイントレベルアイコン表示アドオン + Point Level Icon + Icon cấp độ của điểm + Point-Level-Symbol + Addon para mostar el nivel del ícono + Иконка уровня поинтов + 點數等級圖案 + + 포인트 시스템을 사용중일 경우 사용자 이름 앞에 레벨 아이콘을 표시하도록 합니다. + 레벨 아이콘은 모듈 > 포인트시스템에서 선택 가능합니다. + + + 使用积分系统时,可以在用户名前显示级别图标。 + 级别图标可以在模块 > 积分系统中进行选择。 + + + ポイントシステムを使用する場合、ユーザ名の前にレベルアイコンの表示します。 + レベルアイコンは、「モジュール > ポイントシステム」で選択します。 + + + This addon displays level icon in front of the user name when you are using the point system. + You can choose the level icon in Module > Point System. + + + Addon này sẽ hiển thị Icon cấp độ trước tên người sử dụng khi bạn sử dụng hệ thống tính điểm. + Bạn có thể chọn Icon cấp độ tại Module > cỉa hệ thống điểm. + + + Dieses Addon zeigt Level Icon vor dem Benutzernamen, wenn Sie die Punkte-System. + Sie können wählen, der Level Icon in Modul> Point-System. + + + Este addon muestra el nivel del ícono delante del nombre del usuario cuando es usado el sistema de puntos. + Tu puedes elegir los icono de cada nivel en el módulo > Sistema de Puntos. + + + Этот аддон отображает иконку уровня поинтов напротив имени пользователя, когда используется система поинтов. + Вы можете выбрать иконку уровня в Модуле Системы Поинтов. + + + 使用點數系統時,可以在用戶名前顯示等級圖案。 + 等級圖案可以在模組 > 點數系統中進行選擇。 + + 0.1 + 2007-07-26 + + + NHN + NHN + NHN + NHN + NHN + NHN + NHN + NHN + NHN + + diff --git a/addons/point_level_icon/point_level_icon.addon.php b/addons/point_level_icon/point_level_icon.addon.php index bbf847334..28cedf744 100644 --- a/addons/point_level_icon/point_level_icon.addon.php +++ b/addons/point_level_icon/point_level_icon.addon.php @@ -1,19 +1,18 @@ -]*)member_([0-9\-]+)([^\>]*)>(.*?)\<\/(div|span|a)\>!is', 'pointLevelIconTrans', $output); -?> +]*)member_([0-9\-]+)([^\>]*)>(.*?)\<\/(div|span|a)\>!is', 'pointLevelIconTrans', $output); +?> diff --git a/addons/point_level_icon/point_level_icon.lib.php b/addons/point_level_icon/point_level_icon.lib.php index 749eda5a7..23daedd7c 100644 --- a/addons/point_level_icon/point_level_icon.lib.php +++ b/addons/point_level_icon/point_level_icon.lib.php @@ -1,56 +1,52 @@ $/', '', $matches[0]); + $orig_text = preg_replace('/'.preg_quote($matches[5],'/').'<\/'.$matches[6].'>$/', '', $matches[0]); - // Check Group Image Mark - $oMemberModel = &getModel('member'); - if($oMemberModel->getGroupImageMark($member_srl)) return $orig_text.$matches[5].''; + // Check Group Image Mark + $oMemberModel = &getModel('member'); + if($oMemberModel->getGroupImageMark($member_srl)) return $orig_text.$matches[5].''; - if(!isset($GLOBALS['_pointLevelIcon'][$member_srl])) { - // 포인트 설정을 구해옴 - if(!$GLOBALS['_pointConfig']) { - $oModuleModel = &getModel('module'); - $GLOBALS['_pointConfig'] = $oModuleModel->getModuleConfig('point'); - } - $config = $GLOBALS['_pointConfig']; + if(!isset($GLOBALS['_pointLevelIcon'][$member_srl])) { + // Get point configuration + if(!$GLOBALS['_pointConfig']) { + $oModuleModel = &getModel('module'); + $GLOBALS['_pointConfig'] = $oModuleModel->getModuleConfig('point'); + } + $config = $GLOBALS['_pointConfig']; + // Get point model + if(!$GLOBALS['_pointModel']) $GLOBALS['_pointModel'] = getModel('point'); + $oPointModel = &$GLOBALS['_pointModel']; + // Get points + if(!$oPointModel->isExistsPoint($member_srl)) return $matches[0]; + $point = $oPointModel->getPoint($member_srl); + // Get level + $level = $oPointModel->getLevel($point, $config->level_step); + $text = $matches[5]; + // Get a path where level icon is + $level_icon = sprintf('%smodules/point/icons/%s/%d.gif', Context::getRequestUri(), $config->level_icon, $level); + // Get per to go to the next level if not a top level + if($level < $config->max_level) { + $next_point = $config->level_step[$level+1]; + $present_point = $config->level_step[$level]; + if($next_point > 0) { + $per = (int)(($point - $present_point) / ($next_point - $present_point)*100); + $per = $per.'%'; + } + } - // 포인트 모델을 구해 놓음 - if(!$GLOBALS['_pointModel']) $GLOBALS['_pointModel'] = getModel('point'); - $oPointModel = &$GLOBALS['_pointModel']; + $title = sprintf('%s:%s%s%s, %s:%s/%s', Context::getLang('point'), $point, $config->point_name, $per?' ('.$per.')':'', Context::getLang('level'), $level, $config->max_level); + $alt = sprintf('[%s:%s]', Context::getLang('level'), $level); - // 포인트를 구함 - $point = $oPointModel->getPoint($member_srl); + $GLOBALS['_pointLevelIcon'][$member_srl] = sprintf('%s', $level_icon, $alt, $title); + } + $text = $GLOBALS['_pointLevelIcon'][$member_srl]; - // 레벨을 구함 - $level = $oPointModel->getLevel($point, $config->level_step); - $text = $matches[5]; - - // 레벨 아이콘의 위치를 구함 - $level_icon = sprintf('%smodules/point/icons/%s/%d.gif', Context::getRequestUri(), $config->level_icon, $level); - - // 최고 레벨이 아니면 다음 레벨로 가기 위한 per을 구함 :: 주석과 실제 내용이 맞지 않아 실제 내용을 수정 - if($level < $config->max_level) { - $next_point = $config->level_step[$level+1]; - $present_point = $config->level_step[$level]; - if($next_point > 0) { - $per = (int)(($point - $present_point) / ($next_point - $present_point)*100); - $per = $per.'%'; - } - } - - $title = sprintf('%s:%s%s%s, %s:%s/%s', Context::getLang('point'), $point, $config->point_name, $per?' ('.$per.')':'', Context::getLang('level'), $level, $config->max_level); - $alt = sprintf('[%s:%s]', Context::getLang('level'), $level); - - $GLOBALS['_pointLevelIcon'][$member_srl] = sprintf('%s', $level_icon, $alt, $title); - } - $text = $GLOBALS['_pointLevelIcon'][$member_srl]; - - return $orig_text.$text.$matches[5].''; - } + return $orig_text.$text.$matches[5].''; +} ?> diff --git a/addons/resize_image/conf/info.xml b/addons/resize_image/conf/info.xml index b16c38058..e50589268 100644 --- a/addons/resize_image/conf/info.xml +++ b/addons/resize_image/conf/info.xml @@ -1,53 +1,53 @@ - - - 본문내 이미지 조절 애드온 - 本文内イメージリーサイズアドオン - 内容区图片缩放插件 - Image Resizer - Thay đổi cỡ hình ảnh - Imagen de control add-on bonmunnae - Аддон редактирования размера картинки в тексте - Image-Add-on bonmunnae - 圖片縮放 - - 본문내에 삽입된 이미지의 크기를 본문크기에 맞게 하고 클릭시 원본을 보여주는 애드온입니다. - - - 本文内に挿入されたイメージのサイズを本文の幅サイズに合わせてリーサイズし、クリックした時、オリジナルサイズのイメージを表示します。 - - - 自动调整主题内容区内的图片大小,点击将显示原始大小的插件。 - - - Addon này sẽ lấy lại kích thước nguyên bản của hình ảnh trong bài viết hoặc bình luận khi bạn bấm vào hình. - - - This addon resizes images inserted in the article, and shows original image when you click on them. - - - La imagen corporal se inserta dentro del cuerpo para que se adapte al tamaño de la muestra original cuando hago clic en los add-ons. - - - Аддон, изменяющий размер картинки к размеру текста, при клике на картинку, появляется полное изображение. - - - Body Bild eingefügt im Inneren des Körpers zu passen die Größe des Originals zeigen, wenn ich darauf klicke auf das Add-ons. - - - 自動調整文章内的圖片大小,點擊圖片後會顯示原始大小。 - - 0.1 - 2008-04-22 - - - NHN - NHN - NHN - NHN - NHN - NHN - NHN - NHN - NHN - - + + + 본문내 이미지 조절 애드온 + 本文内イメージリーサイズアドオン + 内容区图片缩放插件 + Image Resizer + Thay đổi cỡ hình ảnh + Imagen de control add-on bonmunnae + Аддон редактирования размера картинки в тексте + Image-Add-on bonmunnae + 圖片縮放 + + 본문내에 삽입된 이미지의 크기를 본문크기에 맞게 하고 클릭시 원본을 보여주는 애드온입니다. + + + 本文内に挿入されたイメージのサイズを本文の幅サイズに合わせてリーサイズし、クリックした時、オリジナルサイズのイメージを表示します。 + + + 自动调整主题内容区内的图片大小,点击将显示原始大小的插件。 + + + Addon này sẽ lấy lại kích thước nguyên bản của hình ảnh trong bài viết hoặc bình luận khi bạn bấm vào hình. + + + This addon resizes images inserted in the article. When clicked, the original images are shown. + + + La imagen corporal se inserta dentro del cuerpo para que se adapte al tamaño de la muestra original cuando hago clic en los add-ons. + + + Аддон, изменяющий размер картинки к размеру текста, при клике на картинку, появляется полное изображение. + + + Body Bild eingefügt im Inneren des Körpers zu passen die Größe des Originals zeigen, wenn ich darauf klicke auf das Add-ons. + + + 自動調整文章内的圖片大小,點擊圖片後會顯示原始大小。 + + 0.1 + 2008-04-22 + + + NHN + NHN + NHN + NHN + NHN + NHN + NHN + NHN + NHN + + diff --git a/addons/resize_image/js/resize_image.js b/addons/resize_image/js/resize_image.js index 70c3d02ed..be1e197eb 100644 --- a/addons/resize_image/js/resize_image.js +++ b/addons/resize_image/js/resize_image.js @@ -121,21 +121,18 @@ function getScreen() { prevbtn.css("visibility", (this.index>0)?"visible":"hidden"); nextbtn.css("visibility", (this.index").attr("id","xe_gallery_screen").css({position:"absolute",display:"none",backgroundColor:"black",zIndex:500,opacity:0.5});controls=$("
").attr("id","xe_gallery_controls").css({position:"absolute",display:"none",overflow:"hidden",zIndex:510});closebtn=$("").attr("id","xe_gallery_closebtn").attr("src",request_uri+"addons/resize_image/iconClose.png").css({top:"10px"}).click(function(){xScreen.xeHide()}).appendTo(controls);prevbtn=$("").attr("id","xe_gallery_prevbtn").attr("src",request_uri+"addons/resize_image/iconLeft.png").css("left","10px").click(function(){xScreen.xePrev()}).appendTo(controls);nextbtn=$("").attr("id","xe_gallery_nextbtn").attr("src",request_uri+"addons/resize_image/iconRight.png").css("right","10px").click(function(){xScreen.xeNext()}).appendTo(controls);controls.find("img").attr({width:60,height:60,className:"iePngFix"}).css({position:"absolute",width:"60px",height:"60px",zIndex:530,cursor:"pointer"});imgframe=$("").attr("id","xe_gallery_holder").css("border","7px solid white").css("zIndex",520).appendTo(controls).draggable();body.append(xScreen).append(controls);xScreen.xeShow=function(){var clientWidth=$(window).width();var clientHeight=$(window).height();$("#xe_gallery_controls,#xe_gallery_screen").css({display:"block",width:clientWidth+"px",height:clientHeight+"px",left:$(document).scrollLeft(),top:$(document).scrollTop()});closebtn.css("left",Math.round((clientWidth-60)/2)+"px");$("#xe_gallery_prevbtn,#xe_gallery_nextbtn").css("top",Math.round((clientHeight-60)/2)+"px");this.xeMove(0);};xScreen.xeHide=function(event){xScreen.css("display","none");controls.css("display","none");};xScreen.xePrev=function(){this.xeMove(-1);};xScreen.xeNext=function(){this.xeMove(1);};xScreen.xeMove=function(val){var clientWidth=$(window).width();var clientHeight=$(window).height();this.index+=val;prevbtn.css("visibility",(this.index>0)?"visible":"hidden");nextbtn.css("visibility",(this.index").attr("id","xe_gallery_screen").css({position:"absolute",display:"none",backgroundColor:"black",zIndex:500,opacity:0.5});controls=$("
").attr("id","xe_gallery_controls").css({position:"absolute",display:"none",overflow:"hidden",zIndex:510});closebtn=$("").attr("id","xe_gallery_closebtn").attr("src",request_uri+"addons/resize_image/iconClose.png").css({top:"10px"}).click(function(){xScreen.xeHide()}).appendTo(controls);prevbtn=$("").attr("id","xe_gallery_prevbtn").attr("src",request_uri+"addons/resize_image/iconLeft.png").css("left","10px").click(function(){xScreen.xePrev()}).appendTo(controls);nextbtn=$("").attr("id","xe_gallery_nextbtn").attr("src",request_uri+"addons/resize_image/iconRight.png").css("right","10px").click(function(){xScreen.xeNext()}).appendTo(controls);controls.find("img").attr({width:60,height:60,className:"iePngFix"}).css({position:"absolute",width:"60px",height:"60px",zIndex:530,cursor:"pointer"});imgframe=$("").attr("id","xe_gallery_holder").css("border","7px solid white").css("zIndex",520).appendTo(controls).draggable();body.append(xScreen).append(controls);xScreen.xeShow=function(){var clientWidth=$(window).width();var clientHeight=$(window).height();$("#xe_gallery_controls,#xe_gallery_screen").css({display:"block",width:clientWidth+"px",height:clientHeight+"px",left:$(document).scrollLeft(),top:$(document).scrollTop()});closebtn.css("left",Math.round((clientWidth-60)/2)+"px");$("#xe_gallery_prevbtn,#xe_gallery_nextbtn").css("top",Math.round((clientHeight-60)/2)+"px");this.xeMove(0);};xScreen.xeHide=function(event){xScreen.css("display","none");controls.css("display","none");};xScreen.xePrev=function(){this.xeMove(-1);};xScreen.xeNext=function(){this.xeMove(1);};xScreen.xeMove=function(val){var clientWidth=$(window).width();var clientHeight=$(window).height();this.index+=val;prevbtn.css("visibility",(this.index>0)?"visible":"hidden");nextbtn.css("visibility",(this.index
');function doResize(contentWidth,count){if(!count)count=0;if(count>=10)return;var $img=this;var beforSize={'width':$img.width(),'height':$img.height()};if(!beforSize.width||!beforSize.height){setTimeout(function(){doResize.call($img,contentWidth,++count)},200);return;} if(beforSize.width<=contentWidth)return;var resize_ratio=contentWidth/beforSize.width;$img.removeAttr('width').removeAttr('height').css({'width':contentWidth,'height':parseInt(beforSize.height*resize_ratio,10)});} $('div.xe_content').each(function(){dummy.appendTo(this);var contentWidth=dummy.width();dummy.remove();if(!contentWidth)return;$('img',this).each(function(){var $img=$(this);var imgSrc=$img.attr('src');if(regx_skip.test(imgSrc)&&!regx_allow_i6pngfix.test(imgSrc))return;$img.attr('rel','xe_gallery');doResize.call($img,contentWidth);});$('img[rel=xe_gallery]',this).live('mouseover',function(){var $img=$(this);if(!$img.parent('a').length&&!$img.attr('onclick')){$img.css('cursor','pointer').click(slideshow);}});});});})(jQuery); diff --git a/addons/resize_image/resize_image.addon.php b/addons/resize_image/resize_image.addon.php index bcc74e01a..5b10d33ec 100644 --- a/addons/resize_image/resize_image.addon.php +++ b/addons/resize_image/resize_image.addon.php @@ -1,18 +1,18 @@ - + diff --git a/classes/cache/CacheApc.class.php b/classes/cache/CacheApc.class.php index 84b6c88fc..f3e8657a4 100644 --- a/classes/cache/CacheApc.class.php +++ b/classes/cache/CacheApc.class.php @@ -1,72 +1,72 @@ -valid_time; - return apc_store(md5(_XE_PATH_.$key), array(time(), $buff), $valid_time); - } - - function isValid($key, $modified_time = 0) { - $_key = md5(_XE_PATH_.$key); - $obj = apc_fetch($_key, $success); - if(!$success || !is_array($obj)) return false; - unset($obj[1]); - - if($modified_time > 0 && $modified_time > $obj[0]) { - $this->_delete($_key); - return false; - } - - return true; - } - - function get($key, $modified_time = 0) { - $_key = md5(_XE_PATH_.$key); - $obj = apc_fetch($_key, $success); - if(!$success || !is_array($obj)) return false; - - if($modified_time > 0 && $modified_time > $obj[0]) { - $this->_delete($_key); - return false; - } - - return $obj[1]; - } - - function _delete($_key) { - $this->put($_key,null,1); - } - - function delete($key) { - $this->_delete(md5(_XE_PATH_.$key)); - } - - function truncate() { - return apc_clear_cache('user'); - } - } -?> +valid_time; + return apc_store(md5(_XE_PATH_.$key), array(time(), $buff), $valid_time); + } + + function isValid($key, $modified_time = 0) { + $_key = md5(_XE_PATH_.$key); + $obj = apc_fetch($_key, $success); + if(!$success || !is_array($obj)) return false; + unset($obj[1]); + + if($modified_time > 0 && $modified_time > $obj[0]) { + $this->_delete($_key); + return false; + } + + return true; + } + + function get($key, $modified_time = 0) { + $_key = md5(_XE_PATH_.$key); + $obj = apc_fetch($_key, $success); + if(!$success || !is_array($obj)) return false; + + if($modified_time > 0 && $modified_time > $obj[0]) { + $this->_delete($_key); + return false; + } + + return $obj[1]; + } + + function _delete($_key) { + $this->put($_key,null,1); + } + + function delete($key) { + $this->_delete($key); + } + + function truncate() { + return apc_clear_cache('user'); + } +} + +/* End of file CacheApc.class.php */ +/* Location: ./classes/cache/CacheApc.class.php */ diff --git a/classes/cache/CacheHandler.class.php b/classes/cache/CacheHandler.class.php index 4ca454a4e..b7ded39be 100644 --- a/classes/cache/CacheHandler.class.php +++ b/classes/cache/CacheHandler.class.php @@ -1,95 +1,130 @@ -use_object_cache =='apc') $type = 'apc'; - else if(substr($info->use_object_cache,0,8)=='memcache'){ - $type = 'memcache'; - $url = $info->use_object_cache; - } - }else if($target == 'template'){ - if($info->use_template_cache =='apc') $type = 'apc'; - else if(substr($info->use_template_cache,0,8)=='memcache'){ - $type = 'memcache'; - $url = $info->use_template_cache; - } - } - - if($type){ - $class = 'Cache' . ucfirst($type); - include_once sprintf('%sclasses/cache/%s.class.php', _XE_PATH_, $class); - $this->handler = call_user_func(array($class,'getInstance'), $url); - } - } - } - - function isSupport(){ - if($this->handler && $this->handler->isSupport()) return true; - return false; - } - - function get($key, $modified_time = 0){ - if(!$this->handler) return false; - return $this->handler->get($key, $modified_time); - } - - function put($key, $obj, $valid_time = 0){ - if(!$this->handler) return false; - return $this->handler->put($key, $obj, $valid_time); - } - - function delete($key){ - if(!$this->handler) return false; - return $this->handler->delete($key); - } - - function isValid($key, $modified_time){ - if(!$this->handler) return false; - return $this->handler->isValid($key, $modified_time); - } - - function truncate(){ - if(!$this->handler) return false; - return $this->handler->truncate(); - } - } - - class CacheBase{ - function get($key, $modified_time = 0){ - return false; - } - - function put($key, $obj, $valid_time = 0){ - return false; - } - - function isValid($key, $modified_time = 0){ - return false; - } - - function isSupport(){ - return false; - } - - function truncate(){ - return false; - } - } -?> +use_object_cache =='apc') $type = 'apc'; + else if(substr($info->use_object_cache,0,8)=='memcache'){ + $type = 'memcache'; + $url = $info->use_object_cache; + } + }else if($target == 'template'){ + if($info->use_template_cache =='apc') $type = 'apc'; + else if(substr($info->use_template_cache,0,8)=='memcache'){ + $type = 'memcache'; + $url = $info->use_template_cache; + } + } + + if($type){ + $class = 'Cache' . ucfirst($type); + include_once sprintf('%sclasses/cache/%s.class.php', _XE_PATH_, $class); + $this->handler = call_user_func(array($class,'getInstance'), $url); + $this->keyGroupVersions = $this->handler->get('key_group_versions', 0); + if(!$this->keyGroupVersions) { + $this->keyGroupVersions = array(); + $this->handler->put('key_group_versions', $this->keyGroupVersions, 0); + } + } + } + } + + function isSupport(){ + if($this->handler && $this->handler->isSupport()) return true; + return false; + } + + function get($key, $modified_time = 0){ + if(!$this->handler) return false; + return $this->handler->get($key, $modified_time); + } + + function put($key, $obj, $valid_time = 0){ + if(!$this->handler) return false; + return $this->handler->put($key, $obj, $valid_time); + } + + function delete($key){ + if(!$this->handler) return false; + return $this->handler->delete($key); + } + + function isValid($key, $modified_time){ + if(!$this->handler) return false; + return $this->handler->isValid($key, $modified_time); + } + + function truncate(){ + if(!$this->handler) return false; + return $this->handler->truncate(); + } + + /** + * Function used for generating keys for similar objects. + * + * Ex: 1:document:123 + * 1:document:777 + * + * This allows easily removing all object of type "document" + * from cache by simply invalidating the group key. + * + * The new key will be 2:document:123, thus forcing the document + * to be reloaded from the database. + */ + function getGroupKey($keyGroupName, $key){ + if(!$this->keyGroupVersions[$keyGroupName]){ + $this->keyGroupVersions[$keyGroupName] = 1; + $this->handler->put('key_group_versions', $this->keyGroupVersions, 0); + } + + return $this->keyGroupVersions[$keyGroupName] . ':' . $keyGroupName . ':' . $key; + } + + function invalidateGroupKey($keyGroupName){ + $this->keyGroupVersions[$keyGroupName]++; + $this->handler->put('key_group_versions', $this->keyGroupVersions, 0); + } +} + +class CacheBase{ + function get($key, $modified_time = 0){ + return false; + } + + function put($key, $obj, $valid_time = 0){ + return false; + } + + function isValid($key, $modified_time = 0){ + return false; + } + + function isSupport(){ + return false; + } + + function truncate(){ + return false; + } +} + +/* End of file CacheHandler.class.php */ +/* Location: ./classes/cache/CacheHandler.class.php */ diff --git a/classes/cache/CacheMemcache.class.php b/classes/cache/CacheMemcache.class.php index 6ce27624c..80d1adfe3 100644 --- a/classes/cache/CacheMemcache.class.php +++ b/classes/cache/CacheMemcache.class.php @@ -1,91 +1,97 @@ -Memcache = new Memcache; - - foreach($config['url'] as $url) { - $info = parse_url($url); - $this->Memcache->addServer($info['host'], $info['port']); - } - } - - function isSupport(){ - return $this->Memcache->set('xe', 'xe', MEMCACHE_COMPRESSED, 1); - } - - function getKey($key){ - return md5(_XE_PATH_.$key); - } - - function put($key, $buff, $valid_time = 0){ - if($valid_time == 0) $valid_time = $this->valid_time; - - return $this->Memcache->set($this->getKey($key), array(time(), $buff), MEMCACHE_COMPRESSED, $valid_time); - } - - function isValid($key, $modified_time = 0) { - $_key = $this->getKey($key); - - $obj = $this->Memcache->get($_key); - if(!$obj || !is_array($obj)) return false; - unset($obj[1]); - - if($modified_time > 0 && $modified_time > $obj[0]) { - $this->_delete($_key); - return false; - } - - return true; - } - - function get($key, $modified_time = 0) { - $_key = $this->getKey($key); - $obj = $this->Memcache->get($_key); - if(!$obj || !is_array($obj)) return false; - - if($modified_time > 0 && $modified_time > $obj[0]) { - $this->_delete($_key); - return false; - } - - unset($obj[0]); - - return $obj[1]; - } - - function delete($key) { - $_key = $this->getKey($key); - $this->_delete($_key); - } - - function _delete($_key) { - $this->Memcache->delete($_key); - } - - function truncate() { - // not support memcached - return false; - } - } -?> +Memcache = new Memcache; + + foreach($config['url'] as $url) { + $info = parse_url($url); + $this->Memcache->addServer($info['host'], $info['port']); + } + } + + function isSupport(){ + if($GLOBALS['XE_MEMCACHE_SUPPORT']) return true; + if($this->Memcache->set('xe', 'xe', MEMCACHE_COMPRESSED, 1)) { + $GLOBALS['XE_MEMCACHE_SUPPORT'] = true; + } else { + $GLOBALS['XE_MEMCACHE_SUPPORT'] = false; + } + return $GLOBALS['XE_MEMCACHE_SUPPORT']; + } + + function getKey($key){ + return md5(_XE_PATH_.$key); + } + + function put($key, $buff, $valid_time = 0){ + if($valid_time == 0) $valid_time = $this->valid_time; + + return $this->Memcache->set($this->getKey($key), array(time(), $buff), MEMCACHE_COMPRESSED, $valid_time); + } + + function isValid($key, $modified_time = 0) { + $_key = $this->getKey($key); + + $obj = $this->Memcache->get($_key); + if(!$obj || !is_array($obj)) return false; + unset($obj[1]); + + if($modified_time > 0 && $modified_time > $obj[0]) { + $this->_delete($_key); + return false; + } + + return true; + } + + function get($key, $modified_time = 0) { + $_key = $this->getKey($key); + $obj = $this->Memcache->get($_key); + if(!$obj || !is_array($obj)) return false; + + if($modified_time > 0 && $modified_time > $obj[0]) { + $this->_delete($_key); + return false; + } + + unset($obj[0]); + + return $obj[1]; + } + + function delete($key) { + $_key = $this->getKey($key); + $this->_delete($_key); + } + + function _delete($_key) { + $this->Memcache->delete($_key); + } + + function truncate() { + // not supported on memcached + return false; + } +} + +/* End of file CacheMemcache.class.php */ +/* Location: ./classes/cache/CacheMemcache.class.php */ diff --git a/classes/context/Context.class.php b/classes/context/Context.class.php index c5e315fc8..fbcdf4f0a 100644 --- a/classes/context/Context.class.php +++ b/classes/context/Context.class.php @@ -24,8 +24,7 @@ class Context { var $ftp_info = NULL; ///< FTP info. var $ssl_actions = array(); ///< list of actions to be sent via ssl (it is used by javascript xml handler for ajax) - var $js_files_map = array(); ///< hash map of javascript files. The file name is used as a key - var $css_files_map = array(); ///< hash map of css files. The file name is used as a key + var $oFrontEndFileHandler; var $html_header = NULL; ///< script codes in .. var $body_class = array(); ///< classnames of @@ -57,6 +56,14 @@ class Context { return $theInstance; } + /** + * @brief cunstructor + **/ + function Context() + { + $this->oFrontEndFileHandler = new FrontEndFileHandler(); + } + /** * @brief initialization, it sets DB information, request arguments and so on. * @return none @@ -120,19 +127,20 @@ class Context { $this->loadLang(_XE_PATH_.'modules/module/lang'); // set session handler - if($this->db_info->use_db_session != 'N') { + if(Context::isInstalled() && $this->db_info->use_db_session != 'N') { $oSessionModel = &getModel('session'); $oSessionController = &getController('session'); session_set_save_handler( - array(&$oSessionController,"open"), - array(&$oSessionController,"close"), - array(&$oSessionModel,"read"), - array(&$oSessionController,"write"), - array(&$oSessionController,"destroy"), - array(&$oSessionController,"gc") + array(&$oSessionController, 'open'), + array(&$oSessionController, 'close'), + array(&$oSessionModel, 'read'), + array(&$oSessionController, 'write'), + array(&$oSessionController, 'destroy'), + array(&$oSessionController, 'gc') ); } session_start(); + if($sess=$_POST[session_name()]) session_id($sess); // set authentication information in Context and session if(Context::isInstalled()) { @@ -166,7 +174,7 @@ class Context { if($_SERVER['REQUEST_METHOD'] == 'GET') { if($this->get_vars) { foreach($this->get_vars as $key=>$val) { - if(!$val) continue; + if(!strlen($val)) continue; if(is_array($val)&&count($val)) { foreach($val as $k => $v) { $url .= ($url?'&':'').$key.'['.$k.']='.urlencode($v); @@ -210,7 +218,28 @@ class Context { $config_file = $self->getConfigFile(); if(is_readable($config_file)) @include($config_file); - if(!$db_info->time_zone) $db_info->time_zone = date("O"); + // If master_db information does not exist, the config file needs to be updated + if(!isset($db_info->master_db)) { + $db_info->master_db = array(); + $db_info->master_db["db_type"] = $db_info->db_type; unset($db_info->db_type); + $db_info->master_db["db_port"] = $db_info->db_port; unset($db_info->db_port); + $db_info->master_db["db_hostname"] = $db_info->db_hostname; unset($db_info->db_hostname); + $db_info->master_db["db_password"] = $db_info->db_password; unset($db_info->db_password); + $db_info->master_db["db_database"] = $db_info->db_database; unset($db_info->db_database); + $db_info->master_db["db_userid"] = $db_info->db_userid; unset($db_info->db_userid); + $db_info->master_db["db_table_prefix"] = $db_info->db_table_prefix; unset($db_info->db_table_prefix); + if(substr($db_info->master_db["db_table_prefix"],-1)!='_') $db_info->master_db["db_table_prefix"] .= '_'; + + $slave_db = $db_info->master_db; + $db_info->slave_db = array($slave_db); + + $self->setDBInfo($db_info); + + $oInstallController = &getController('install'); + $oInstallController->makeConfigFile(); + } + + if(!$db_info->time_zone) $db_info->time_zone = date('O'); $GLOBALS['_time_zone'] = $db_info->time_zone; if($db_info->qmail_compatibility != 'Y') $db_info->qmail_compatibility = 'N'; @@ -231,7 +260,7 @@ class Context { **/ function getDBType() { is_a($this,'Context')?$self=&$this:$self=&Context::getInstance(); - return $self->db_info->db_type; + return $self->db_info->master_db["db_type"]; } /** @@ -253,6 +282,16 @@ class Context { return $self->db_info; } + /** + * @brief return ssl status + * @return none|always|optional + **/ + function getSslStatus() + { + $dbInfo = Context::getDBInfo(); + return $dbInfo->use_ssl; + } + /** * @brief return default URL * @return default URL string @@ -330,7 +369,7 @@ class Context { $url_info = parse_url($url); $url_info['query'].= ($url_info['query']?'&':'').'SSOID='.session_id(); $redirect_url = sprintf('%s://%s%s%s?%s',$url_info['scheme'],$url_info['host'],$url_info['port']?':'.$url_info['port']:'',$url_info['path'], $url_info['query']); - header("location:".$redirect_url); + header('location:'.$redirect_url); return false; } // for sites requesting SSO validation @@ -341,13 +380,13 @@ class Context { setcookie(session_name(), $session_name); $url = preg_replace('/([\?\&])$/','',str_replace('SSOID='.$session_name,'',Context::getRequestUrl())); - header("location:".$url); + header('location:'.$url); return false; // send SSO request } else if($_COOKIE['sso']!=md5(Context::getRequestUri()) && !Context::get('SSOID')) { setcookie('sso',md5(Context::getRequestUri()),0,'/'); $url = sprintf("%s?default_url=%s", $default_url, base64_encode(Context::getRequestUrl())); - header("location:".$url); + header('location:'.$url); return false; } } @@ -413,7 +452,7 @@ class Context { $oModuleController = &getController('module'); $oModuleController->replaceDefinedLangCode($self->site_title); - return $self->site_title; + return htmlspecialchars($self->site_title); } /** * @deprecated @@ -432,20 +471,61 @@ class Context { if(!is_object($lang)) $lang = new stdClass; if(!$self->lang_type) return; - if(substr($path,-1)!='/') $path .= '/'; - $path_tpl = $path.'%s.lang.php'; - $filename = sprintf($path_tpl, $self->lang_type); - $langs = array('ko','en'); // this will be configurable. - while(!is_readable($filename) && $langs[0]) { - $filename = sprintf($path_tpl, array_shift($langs)); - } - if(!is_readable($filename)) return; + $filename = $self->_loadXmlLang($path); + if(!$filename) $filename = $self->_loadPhpLang($path); if(!is_array($self->loaded_lang_files)) $self->loaded_lang_files = array(); if(in_array($filename, $self->loaded_lang_files)) return; - $self->loaded_lang_files[] = $filename; - @include($filename); + if ($filename && is_readable($filename)){ + $self->loaded_lang_files[] = $filename; + @include($filename); + }else{ + $self->_evalxmlLang($path); + } + } + + function _evalxmlLang($path) { + global $lang; + + $_path = 'eval://'.$path; + + if(in_array($_path, $this->loaded_lang_files)) return; + + if(substr($path,-1)!='/') $path .= '/'; + $file = $path.'lang.xml'; + + $oXmlLangParser = new XmlLangParser($file, $this->lang_type); + $content = $oXmlLangParser->getCompileContent(); + + if ($content){ + $this->loaded_lang_files[] = $_path; + eval($content); + } + } + + function _loadXmlLang($path) { + if(substr($path,-1)!='/') $path .= '/'; + $file = $path.'lang.xml'; + + $oXmlLangParser = new XmlLangParser($file, $this->lang_type); + $file = $oXmlLangParser->compile(); + + return $file; + } + + function _loadPhpLang($path) { + if(substr($path,-1)!='/') $path .= '/'; + $path_tpl = $path.'%s.lang.php'; + $file = sprintf($path_tpl, $this->lang_type); + + $langs = array('ko','en'); // this will be configurable. + while(!is_readable($file) && $langs[0]) { + $file = sprintf($path_tpl, array_shift($langs)); + } + + if(!is_readable($file)) return false; + return $file; } /** @@ -564,15 +644,15 @@ class Context { /** * @brief determine request method (GET/POST/XMLRPC/JSON) - * @param[in] $type request method + * @param[in] $type request method (optional) * @return none **/ - function setRequestMethod($type) { + function setRequestMethod($type='') { is_a($this,'Context')?$self=&$this:$self=&Context::getInstance(); ($type && $self->request_method=$type) or - (strpos($_SERVER['CONTENT_TYPE'],'json') && $this->request_method='JSON') or - ($GLOBALS['HTTP_RAW_POST_DATA'] && $this->request_method='XMLRPC') or + (strpos($_SERVER['CONTENT_TYPE'],'json') && $self->request_method='JSON') or + ($GLOBALS['HTTP_RAW_POST_DATA'] && $self->request_method='XMLRPC') or ($self->request_method = $_SERVER['REQUEST_METHOD']); } @@ -584,7 +664,7 @@ class Context { if(!count($_REQUEST)) return; foreach($_REQUEST as $key => $val) { - if($val === "" || Context::get($key)) continue; + if($val === '' || Context::get($key)) continue; $val = $this->_filterRequestVar($key, $val); if($this->getRequestMethod()=='GET'&&isset($_GET[$key])) $set_to_vars = true; @@ -640,15 +720,15 @@ class Context { * @return filtered value **/ function _filterRequestVar($key, $val, $do_stripslashes = 1) { - if( ($key == "page" || $key == "cpage" || substr($key,-3)=="srl")) return !preg_match('/^[0-9,]+$/',$val)?(int)$val:$val; + if( ($key == 'page' || $key == 'cpage' || substr($key,-3)=='srl')) return !preg_match('/^[0-9,]+$/',$val)?(int)$val:$val; if(is_array($val) && count($val) ) { foreach($val as $k => $v) { - if($do_stripslashes && version_compare(PHP_VERSION, "5.9.0", "<") && get_magic_quotes_gpc()) $v = stripslashes($v); + if($do_stripslashes && version_compare(PHP_VERSION, '5.9.0', '<') && get_magic_quotes_gpc()) $v = stripslashes($v); $v = trim($v); $val[$k] = $v; } } else { - if($do_stripslashes && version_compare(PHP_VERSION, "5.9.0", "<") && get_magic_quotes_gpc()) $val = stripslashes($val); + if($do_stripslashes && version_compare(PHP_VERSION, '5.9.0', '<') && get_magic_quotes_gpc()) $val = stripslashes($val); $val = trim($val); } return $val; @@ -669,7 +749,7 @@ class Context { **/ function _setUploadedArgument() { if($this->getRequestMethod() != 'POST') return; - if(!preg_match("/multipart\/form-data/i",$_SERVER['CONTENT_TYPE'])) return; + if(!preg_match('/multipart\/form-data/i',$_SERVER['CONTENT_TYPE'])) return; if(!$_FILES) return; foreach($_FILES as $key => $val) { @@ -709,7 +789,7 @@ class Context { * @brief make URL with args_list upon request URL * @return result URL **/ - function getUrl($num_args=0, $args_list=array(), $domain = null, $encode = true) { + function getUrl($num_args=0, $args_list=array(), $domain = null, $encode = true, $autoEncode = false) { static $site_module_info = null; static $current_info = null; @@ -788,7 +868,9 @@ class Context { // if using rewrite mod if($self->allow_rewrite) { $var_keys = array_keys($get_vars); - $target = implode('.', $var_keys); + sort($var_keys); + + $target = implode('.', $var_keys); $act = $get_vars['act']; $vid = $get_vars['vid']; @@ -817,7 +899,7 @@ class Context { 'act.document_srl.key.vid'=>($act=='trackback')?"$vid/$srl/$key/$act":'' ); - $query = $target_map[$target]; + $query = $target_map[$target]; } if(!$query) { @@ -853,18 +935,37 @@ class Context { else $query = getScriptPath().$query; } - return $encode?htmlspecialchars($query):$query; + if ($encode){ + if($autoEncode){ + $parsedUrl = parse_url($query); + parse_str($parsedUrl['query'], $output); + $encode_queries = array(); + foreach($output as $key=>$value){ + if (preg_match('/&([a-z]{2,}|#\d+);/', urldecode($value))){ + $value = urlencode(htmlspecialchars_decode(urldecode($value))); + } + $encode_queries[] = $key.'='.$value; + } + $encode_query = implode('&', $encode_queries); + return htmlspecialchars($parsedUrl['path'].'?'.$encode_query); + } + else{ + return htmlspecialchars($query); + } + }else{ + return $query; + } } /** - * @brief 요청이 들어온 URL에서 argument를 제거하여 return + * @brief Return after removing an argument on the requested URL **/ function getRequestUri($ssl_mode = FOLLOW_REQUEST_SSL, $domain = null) { static $url = array(); // HTTP Request가 아니면 패스 if(!isset($_SERVER['SERVER_PROTOCOL'])) return ; - if(Context::get('_use_ssl') == "always") $ssl_mode = ENFORCE_SSL; + if(Context::get('_use_ssl') == 'always') $ssl_mode = ENFORCE_SSL; if($domain) $domain_key = md5($domain); else $domain_key = 'default'; @@ -903,7 +1004,7 @@ class Context { elseif($url_info['port']==80) unset($url_info['port']); } - $url[$ssl_mode][$domain_key] = sprintf("%s://%s%s%s",$use_ssl?'https':$url_info['scheme'], $url_info['host'], $url_info['port']&&$url_info['port']!=80?':'.$url_info['port']:'',$url_info['path']); + $url[$ssl_mode][$domain_key] = sprintf('%s://%s%s%s',$use_ssl?'https':$url_info['scheme'], $url_info['host'], $url_info['port']&&$url_info['port']!=80?':'.$url_info['port']:'',$url_info['path']); return $url[$ssl_mode][$domain_key]; } @@ -919,17 +1020,19 @@ class Context { } /** - * @brief key값에 해당하는 값을 return + * @brief return key value **/ function get($key) { is_a($this,'Context')?$self=&$this:$self=&Context::getInstance(); + + if(!isset($self->context->{$key})) return null; return $self->context->{$key}; } /** - * @brief 받고자 하는 변수만 object에 입력하여 받음 - * - * key1, key2, key3 .. 등의 인자를 주어 여러개의 변수를 object vars로 세팅하여 받을 수 있음 + * @brief get a specified var in object + * + * get one more vars in object vars with given arguments(key1, key2, key3,...) **/ function gets() { $num_args = func_num_args(); @@ -944,7 +1047,7 @@ class Context { } /** - * @brief 모든 데이터를 return + * @brief Return all data **/ function getAll() { is_a($this,'Context')?$self=&$this:$self=&Context::getInstance(); @@ -952,16 +1055,17 @@ class Context { } /** - * @brief GET/POST/XMLRPC에서 넘어온 변수값을 return + * @brief Return values from the GET/POST/XMLRPC **/ function getRequestVars() { is_a($this,'Context')?$self=&$this:$self=&Context::getInstance(); - return clone($self->get_vars); + if($self->get_vars) return clone($self->get_vars); + return new stdClass; } /** - * @brief SSL로 인증되어야 할 action이 있을 경우 등록 - * common/js/xml_handler.js에서 이 action들에 대해서 https로 전송되도록 함 + * @brief Register if actions is to be encrypted by SSL + * Those actions are sent to https in common/js/xml_handler.js **/ function addSSLAction($action) { is_a($this,'Context')?$self=&$this:$self=&Context::getInstance(); @@ -982,6 +1086,7 @@ class Context { /** * @brief normalize file path * @return normalized file path + * @deprecated */ function normalizeFilePath($file) { if(strpos($file,'://')===false && $file{0}!='/' && $file{0}!='.') $file = './'.$file; @@ -992,37 +1097,78 @@ class Context { } /** - * @brief js file을 추가 + * @deprecated **/ - function addJsFile($file, $optimized = false, $targetie = '',$index=0, $type='head') { - is_a($this,'Context')?$self=&$this:$self=&Context::getInstance(); + function getAbsFileUrl($file) { + $file = Context::normalizeFilePath($file); + if(strpos($file,'./')===0) $file = dirname($_SERVER['SCRIPT_NAME']).'/'.substr($file,2); + elseif(strpos($file,'../')===0) $file = Context::normalizeFilePath(dirname($_SERVER['SCRIPT_NAME'])."/{$file}"); - $avail_types = array('head', 'body'); - if(!in_array($type, $avail_types)) $type = $avail_types[0]; - - $key = $self->normalizeFilePath($file)."\t".$targetie; - $map = &$self->js_files_map; - - // Is this file already registered? - if (!is_array($map[$type])) $map[$type] = array(); - if (!isset($map[$type][$key]) || (int)$map[$type][$key] > (int)$index) $map[$type][$key] = (int)$index+count($map[$type])/1000-1; + return $file; } /** - * @brief js file을 제거 + * @brief load front end file + * @params $args array + * case js + * $args[0]: file name + * $args[1]: type (head | body) + * $args[2]: target IE + * $args[3]: index + * case css + * $args[0]: file name + * $args[1]: media + * $args[2]: target IE + * $args[3]: index + **/ + function loadFile($args, $useCdn = false, $cdnPrefix = '', $cdnVersion = '') + { + is_a($this,'Context')?$self=&$this:$self=&Context::getInstance(); + + if ($useCdn && !$cdnPrefix) + { + $cdnPrefix = __XE_CDN_PREFIX__; + $cdnVersion = __XE_CDN_VERSION__; + } + + $self->oFrontEndFileHandler->loadFile($args, $useCdn, $cdnPrefix, $cdnVersion); + } + + function unloadFile($file, $targetIe = '', $media = 'all') + { + is_a($this,'Context')?$self=&$this:$self=&Context::getInstance(); + $self->oFrontEndFileHandler->unloadFile($file, $targetIe, $media); + } + + function unloadAllFiles($type = 'all') + { + is_a($this,'Context')?$self=&$this:$self=&Context::getInstance(); + $self->oFrontEndFileHandler->unloadAllFiles($type); + } + + /** + * @brief Add the js file + * @deprecated + **/ + function addJsFile($file, $optimized = false, $targetie = '',$index=0, $type='head', $isRuleset = false) { + if($isRuleset) + { + $validator = new Validator($file); + $validator->setCacheDir('files/cache'); + $file = $validator->getJsPath(); + } + + is_a($this,'Context')?$self=&$this:$self=&Context::getInstance(); + $self->oFrontEndFileHandler->loadFile(array($file, $type, $targetie, $index)); + } + + /** + * @brief Remove the js file + * @deprecated **/ function unloadJsFile($file, $optimized = false, $targetie = '') { is_a($this,'Context')?$self=&$this:$self=&Context::getInstance(); - - $realfile = realpath($file); - - foreach($self->js_files_map as $key=>$val) { - list($_file, $_targetie) = explode("\t", $key); - if(realpath($_file)==$realfile && $_targetie == $targetie) { - unset($self->js_files_map[$key]); - return; - } - } + $self->oFrontEndFileHandler->unloadFile($file, $targetie); } /** @@ -1030,18 +1176,18 @@ class Context { **/ function unloadAllJsFiles() { is_a($this,'Context')?$self=&$this:$self=&Context::getInstance(); - $self->js_files_map = array(); + $self->oFrontEndFileHandler->unloadAllJsFiles(); } /** - * @brief javascript filter 추가 + * @brief Add javascript filter **/ function addJsFilter($path, $filename) { $oXmlFilter = new XmlJSFilter($path, $filename); $oXmlFilter->compile(); } /** - * @brief array_unique와 동작은 동일하나 file 첨자에 대해서만 동작함 + * @brief Same as array_unique but works only for file subscript * @deprecated **/ function _getUniqueFileList($files) { @@ -1063,49 +1209,25 @@ class Context { **/ function getJsFile($type='head') { is_a($this,'Context')?$self=&$this:$self=&Context::getInstance(); - - if(!is_array($self->js_files_map[$type])) $self->js_files_map[$type] = array(); - - $ret = array(); - $map = &$self->js_files_map[$type]; - - asort($self->js_files_map[$type]); - - foreach($map as $key=>$val) { - list($file, $targetie) = explode("\t", $key); - $ret[] = array('file'=>$file, 'targetie'=>$targetie); - } - - return $ret; + return $self->oFrontEndFileHandler->getJsFileList($type); } /** - * @brief CSS file 추가 + * @brief Add CSS file + * @deprecated **/ function addCSSFile($file, $optimized=false, $media='all', $targetie='',$index=0) { is_a($this,'Context')?$self=&$this:$self=&Context::getInstance(); - - $key = $self->normalizeFilePath($file)."\t".$targetie."\t".$media; - $map = &$self->css_files_map; - - if (!isset($map[$key]) || (int)$map[$key] > (int)$index) $map[$key] = (int)$index+count($map)/100-1; + $self->oFrontEndFileHandler->loadFile(array($file, $media, $targetie, $index)); } /** - * @brief css file을 제거 + * @brief Remove css file + * @deprecated **/ function unloadCSSFile($file, $optimized = false, $media = 'all', $targetie = '') { is_a($this,'Context')?$self=&$this:$self=&Context::getInstance(); - - $realfile = realpath($file); - - foreach($self->css_files_map as $key => $val) { - list($_file, $_targetie, $_media) = explode("\t", $key); - if(realpath($_file)==$realfile && $_media==$media && $_targetie==$targetie) { - unset($self->css_files_map[$key]); - return; - } - } + $self->oFrontEndFileHandler->unloadFile($file, $targetie, $media); } /** @@ -1113,7 +1235,7 @@ class Context { **/ function unloadAllCSSFiles() { is_a($this,'Context')?$self=&$this:$self=&Context::getInstance(); - $self->css_files_map = array(); + $self->oFrontEndFileHandler->unloadAllCssFiles(); } /** @@ -1121,16 +1243,7 @@ class Context { **/ function getCSSFile() { is_a($this,'Context')?$self=&$this:$self=&Context::getInstance(); - - asort($self->css_files_map); - $ret = array(); - - foreach($self->css_files_map as $key=>$val) { - list($_file, $_targetie, $_media) = explode("\t", $key); - $ret[] = array('file'=>$_file, 'media'=>$_media, 'targetie'=>$_targetie); - } - - return $ret; + return $self->oFrontEndFileHandler->getCssFileList(); } /** @@ -1145,7 +1258,7 @@ class Context { if($loaded_plugins[$plugin_name]) return; $loaded_plugins[$plugin_name] = true; - $plugin_path = "./common/js/plugins/$plugin_name/"; + $plugin_path = './common/js/plugins/'.$plugin_name.'/'; $info_file = $plugin_path.'plugin.load'; if(!is_readable($info_file)) return; @@ -1155,15 +1268,15 @@ class Context { if(!$filename) continue; if(substr($filename,0,2)=='./') $filename = substr($filename,2); - if(preg_match('/\.js$/i', $filename)) $self->addJsFile($plugin_path.$filename, false, '', 0, 'body'); - elseif(preg_match('/\.css$/i', $filename)) $self->addCSSFile($plugin_path.$filename, false, 'all', '', 0); + if(preg_match('/\.js$/i', $filename)) $self->loadFile(array($plugin_path.$filename, 'body', '', 0), true); + elseif(preg_match('/\.css$/i', $filename)) $self->loadFile(array($plugin_path.$filename, 'all', '', 0), true); } if(is_dir($plugin_path.'lang')) $self->loadLang($plugin_path.'lang'); } /** - * @brief HtmlHeader 추가 + * @brief Add HtmlHeader **/ function addHtmlHeader($header) { is_a($this,'Context')?$self=&$this:$self=&Context::getInstance(); @@ -1179,7 +1292,7 @@ class Context { } /** - * @brief Html Body에 css class 추가 + * @brief Add css class to Html Body **/ function addBodyClass($class_name) { is_a($this,'Context')?$self=&$this:$self=&Context::getInstance(); @@ -1187,7 +1300,7 @@ class Context { } /** - * @brief Html Body에 css class return + * @brief Return css class to Html Body **/ function getBodyClass() { is_a($this,'Context')?$self=&$this:$self=&Context::getInstance(); @@ -1232,14 +1345,14 @@ class Context { * @brief returns the path of the config file that contains database settings **/ function getConfigFile() { - return _XE_PATH_."files/config/db.config.php"; + return _XE_PATH_.'files/config/db.config.php'; } /** * @brief returns the path of the config file that contains FTP settings **/ function getFTPConfigFile() { - return _XE_PATH_."files/config/ftp.config.php"; + return _XE_PATH_.'files/config/ftp.config.php'; } /** @@ -1251,7 +1364,7 @@ class Context { } /** - * @brief 내용의 위젯이나 기타 기능에 대한 code를 실제 code로 변경 + * @brief Transforms codes about widget or other features into the actual code, deprecatred **/ function transContent($content) { return $content; @@ -1300,5 +1413,36 @@ class Context { if(substr($path,-1)!='/') $path .= '/'; return $path; } + + /** + * @brief returns the list of meta tags + **/ + function getMetaTag() { + is_a($this,'Context')?$self=&$this:$self=&Context::getInstance(); + + if(!is_array($self->meta_tags)) $self->meta_tags = array(); + + $ret = array(); + $map = &$self->meta_tags; + + foreach($map as $key=>$val) { + list($name, $is_http_equiv) = explode("\t", $key); + $ret[] = array('name'=>$name, 'is_http_equiv'=>$is_http_equiv, 'content' => $val); + } + + return $ret; + } + + /** + * @brief Add the meta tag + **/ + function addMetaTag($name, $content, $is_http_equiv = false) { + is_a($this,'Context')?$self=&$this:$self=&Context::getInstance(); + + $key = $name."\t".($is_http_equiv ? '1' : '0'); + $map = &$self->meta_tags; + + $map[$key] = $content; + } } -?> \ No newline at end of file +?> diff --git a/classes/db/DB.class.php b/classes/db/DB.class.php index 8b38d39e6..85339e092 100644 --- a/classes/db/DB.class.php +++ b/classes/db/DB.class.php @@ -1,708 +1,846 @@ - '=', - '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; ///< error code (0 means no error) - var $errstr = ''; ///< error message - var $query = ''; ///< query string of latest executed query - var $elapsed_time = 0; ///< elapsed time of latest executed query - - var $transaction_started = false; ///< transaction flag - - var $is_connected = false; ///< is db connected - - var $supported_list = array(); ///< list of supported db, (will be written by classes/DB/DB***.class.php) - - var $cache_file = 'files/cache/queries/'; ///< location of query cache - - /** - * @brief returns instance of certain db type - * @param[in] $db_type type of db - * @return instance - **/ - function &getInstance($db_type = NULL) { - if(!$db_type) $db_type = Context::getDBType(); - if(!$db_type && Context::isInstalled()) return new Object(-1, 'msg_db_not_setted'); - - if(!$GLOBALS['__DB__']) { - $class_name = 'DB'.ucfirst($db_type); - $class_file = _XE_PATH_."classes/db/$class_name.class.php"; - if(!file_exists($class_file)) return new Object(-1, 'msg_db_not_setted'); - - // get a singletone instance of the database driver class - require_once($class_file); - $GLOBALS['__DB__'][$db_type] = call_user_func(array($class_name, 'create')); - } - - return $GLOBALS['__DB__'][$db_type]; - } - - function create() { - return new DB; - } - - /** - * @brief constructor - * @return none - **/ - function DB() { - $this->count_cache_path = _XE_PATH_.$this->count_cache_path; - $this->cache_file = _XE_PATH_.$this->cache_file; - } - - /** - * @brief returns list of supported db - * @return list of supported db - **/ - function getSupportedList() { - $oDB = new DB(); - return $oDB->_getSupportedList(); - } - - /** - * @brief returns list of supported db - * @return list of supported db - **/ - function _getSupportedList() { - $db_classes_path = _XE_PATH_."classes/db/"; - $filter = "/^DB([^\.]+)\.class\.php/i"; - $supported_list = FileHandler::readDir($db_classes_path, $filter, true); - sort($supported_list); - - // after creating instance of class, check is supported - for($i = 0; $i < count($supported_list); $i++) { - $db_type = $supported_list[$i]; - - if(version_compare(phpversion(), '5.0') < 0 && preg_match('/pdo/i',$db_type)) continue; - - $class_name = sprintf("DB%s%s", strtoupper(substr($db_type,0,1)), strtolower(substr($db_type,1))); - $class_file = sprintf(_XE_PATH_."classes/db/%s.class.php", $class_name); - if(!file_exists($class_file)) continue; - - unset($oDB); - require_once($class_file); - $tmp_fn = create_function('', "return new {$class_name}();"); - $oDB = $tmp_fn(); - - if(!$oDB) continue; - - $obj = null; - $obj->db_type = $db_type; - $obj->enable = $oDB->isSupported() ? true : false; - - $this->supported_list[] = $obj; - } - - return $this->supported_list; - } - - /** - * @brief check if the db_type is supported - * @param[in] $db_type type of db to check - * @return true: is supported, false: is not supported - **/ - function isSupported($db_type) { - $supported_list = DB::getSupportedList(); - return in_array($db_type, $supported_list); - } - - /** - * @brief check if is connected - * @return true: connected, false: not connected - **/ - function isConnected() { - return $this->is_connected ? true : false; - } - - /** - * @brief start recording log - * @return none - **/ - function actStart($query) { - $this->setError(0, 'success'); - $this->query = $query; - $this->act_start = getMicroTime(); - $this->elapsed_time = 0; - } - - /** - * @brief finish recording log - * @return none - **/ - function actFinish() { - if(!$this->query) return; - $this->act_finish = getMicroTime(); - $elapsed_time = $this->act_finish - $this->act_start; - $this->elapsed_time = $elapsed_time; - $GLOBALS['__db_elapsed_time__'] += $elapsed_time; - - $log['query'] = $this->query; - $log['elapsed_time'] = $elapsed_time; - - // leave error log if an error occured (if __DEBUG_DB_OUTPUT__ is defined) - if($this->isError()) { - $site_module_info = Context::get('site_module_info'); - $log['module'] = $site_module_info->module; - $log['act'] = Context::get('act'); - $log['query_id'] = $this->query_id; - $log['time'] = date('Y-m-d H:i:s'); - $log['result'] = 'Failed'; - $log['errno'] = $this->errno; - $log['errstr'] = $this->errstr; - - if(__DEBUG_DB_OUTPUT__ == 1) { - $debug_file = _XE_PATH_."files/_debug_db_query.php"; - $buff = array(); - if(!file_exists($debug_file)) $buff[] = ''; - $buff[] = print_r($log, true); - - if(@!$fp = fopen($debug_file, "a")) return; - fwrite($fp, implode("\n", $buff)."\n\n"); - fclose($fp); - } - } else { - $log['result'] = 'Success'; - } - $GLOBALS['__db_queries__'][] = $log; - - // if __LOG_SLOW_QUERY__ if defined, check elapsed time and leave query log - if(__LOG_SLOW_QUERY__ > 0 && $elapsed_time > __LOG_SLOW_QUERY__) { - $buff = ''; - $log_file = _XE_PATH_.'files/_db_slow_query.php'; - if(!file_exists($log_file)) { - $buff = ''."\n"; - } - - $buff .= sprintf("%s\t%s\n\t%0.6f sec\tquery_id:%s\n\n", date("Y-m-d H:i"), $this->query, $elapsed_time, $this->query_id); - - if($fp = fopen($log_file, 'a')) { - fwrite($fp, $buff); - fclose($fp); - } - } - } - - /** - * @brief set error - * @param[in] $errno error code - * @param[in] $errstr error message - * @return none - **/ - function setError($errno = 0, $errstr = 'success') { - $this->errno = $errno; - $this->errstr = $errstr; - } - - /** - * @brief check if an error occured - * @return true: error, false: no error - **/ - function isError() { - return $this->errno === 0 ? false : true; - } - - /** - * @brief returns object of error info - * @return object of error - **/ - function getError() { - $this->errstr = Context::convertEncodingStr($this->errstr); - return new Object($this->errno, $this->errstr); - } - - /** - * @brief query xml 파일을 실행하여 결과를 return - * @param[in] $query_id query id (module.queryname - * @param[in] $args arguments for query - * @return result of query - * @remarks this function finds xml file or cache file of $query_id, compiles it and then execute it - **/ - function executeQuery($query_id, $args = NULL, $arg_columns = NULL) { - if(!$query_id) return new Object(-1, 'msg_invalid_queryid'); - $this->query_id = $query_id; - - $id_args = explode('.', $query_id); - if(count($id_args) == 2) { - $target = 'modules'; - $module = $id_args[0]; - $id = $id_args[1]; - } elseif(count($id_args) == 3) { - $target = $id_args[0]; - if(!in_array($target, array('addons','widgets'))) return; - $module = $id_args[1]; - $id = $id_args[2]; - } - if(!$target || !$module || !$id) return new Object(-1, 'msg_invalid_queryid'); - - $xml_file = sprintf('%s%s/%s/queries/%s.xml', _XE_PATH_, $target, $module, $id); - if(!file_exists($xml_file)) return new Object(-1, 'msg_invalid_queryid'); - - // look for cache file - $cache_file = $this->checkQueryCacheFile($query_id, $xml_file); - - // execute query - return $this->_executeQuery($cache_file, $args, $query_id, $arg_columns); - } - - - /** - * @brief look for cache file - * @param[in] $query_id query id for finding - * @param[in] $xml_file original xml query file - * @return cache file - **/ - function checkQueryCacheFile($query_id,$xml_file){ - - // first try finding cache file - $cache_file = sprintf('%s%s%s.cache.php', _XE_PATH_, $this->cache_file, $query_id); - - if(file_exists($cache_file)) $cache_time = filemtime($cache_file); - else $cache_time = -1; - - // if there is no cache file or is not new, find original xml query file and parse it - if($cache_time < filemtime($xml_file) || $cache_time < filemtime(_XE_PATH_.'classes/db/DB.class.php') || $cache_time < filemtime(_XE_PATH_.'classes/xml/XmlQueryParser.class.php')) { - require_once(_XE_PATH_.'classes/xml/XmlQueryParser.class.php'); - $oParser = new XmlQueryParser(); - $oParser->parse($query_id, $xml_file, $cache_file); - } - - return $cache_file; - } - - - /** - * @brief execute query and return the result - * @param[in] $cache_file cache file of query - * @param[in] $source_args arguments for query - * @param[in] $query_id query id - * @return result of query - **/ - function _executeQuery($cache_file, $source_args, $query_id, $arg_columns) { - 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; - $output->_tables = ($output->_tables && is_array($output->_tables)) ? $output->_tables : array(); - - // execute appropriate query - switch($output->action) { - case 'insert' : - $this->resetCountCache($output->tables); - $output = $this->_executeInsertAct($output); - break; - case 'update' : - $this->resetCountCache($output->tables); - $output = $this->_executeUpdateAct($output); - break; - case 'delete' : - $this->resetCountCache($output->tables); - $output = $this->_executeDeleteAct($output); - break; - case 'select' : - $output->arg_columns = is_array($arg_columns)?$arg_columns:array(); - $output = $this->_executeSelectAct($output); - break; - } - - if($this->isError()) $output = $this->getError(); - else if(!is_a($output, 'Object') && !is_subclass_of($output, 'Object')) $output = new Object(); - $output->add('_query', $this->query); - $output->add('_elapsed_time', sprintf("%0.5f", $this->elapsed_time)); - - return $output; - } - - /** - * @brief check $val with $filter_type - * @param[in] $key key value - * @param[in] $val value of $key - * @param[in] $filter_type type of filter to check $val - * @return object - * @remarks this function is to be used from XmlQueryParser - **/ - function checkFilter($key, $val, $filter_type) { - global $lang; - - switch($filter_type) { - case 'email' : - case 'email_address' : - if(!preg_match('/^[_0-9a-z-]+(\.[_0-9a-z-]+)*@[0-9a-z-]+(\.[0-9a-z-]+)*$/is', $val)) return new Object(-1, sprintf($lang->filter->invalid_email, $lang->{$key} ? $lang->{$key} : $key)); - break; - case 'homepage' : - if(!preg_match('/^(http|https)+(:\/\/)+[0-9a-z_-]+\.[^ ]+$/is', $val)) return new Object(-1, sprintf($lang->filter->invalid_homepage, $lang->{$key} ? $lang->{$key} : $key)); - break; - case 'userid' : - case 'user_id' : - if(!preg_match('/^[a-zA-Z]+([_0-9a-zA-Z]+)*$/is', $val)) return new Object(-1, sprintf($lang->filter->invalid_userid, $lang->{$key} ? $lang->{$key} : $key)); - break; - case 'number' : - case 'numbers' : - if(is_array($val)) $val = join(',', $val); - if(!preg_match('/^(-?)[0-9]+(,\-?[0-9]+)*$/is', $val)) return new Object(-1, sprintf($lang->filter->invalid_number, $lang->{$key} ? $lang->{$key} : $key)); - break; - case 'alpha' : - if(!preg_match('/^[a-z]+$/is', $val)) return new Object(-1, sprintf($lang->filter->invalid_alpha, $lang->{$key} ? $lang->{$key} : $key)); - break; - case 'alpha_number' : - if(!preg_match('/^[0-9a-z]+$/is', $val)) return new Object(-1, sprintf($lang->filter->invalid_alpha_number, $lang->{$key} ? $lang->{$key} : $key)); - break; - } - - return new Object(); - } - - /** - * @brief returns type of column - * @param[in] $column_type_list list of column type - * @param[in] $name name of column type - * @return column type of $name - * @remarks columns are usually like a.b, so it needs another function - **/ - 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 returns the value of condition - * @param[in] $name name of condition - * @param[in] $value value of condition - * @param[in] $operation operation this is used in condition - * @param[in] $type type of condition - * @param[in] $column_type type of column - * @return well modified $value - * @remarks if $operation is like or like_prefix, $value itself will be modified - * @remarks if $type is not 'number', call addQuotes() and wrap with ' ' - **/ - function getConditionValue($name, $value, $operation, $type, $column_type) { - - if(!in_array($operation,array('in','notin','between')) && $type == 'number') { - if(is_array($value)){ - $value = join(',',$value); - } - if(strpos($value, ',') === false && strpos($value, '(') === false) return (int)$value; - return $value; - } - - if(!is_array($value) && strpos($name, '.') !== false && strpos($value, '.') !== false) { - list($table_name, $column_name) = explode('.', $value); - if($column_type[$column_name]) return $value; - } - - switch($operation) { - case 'like_prefix' : - if(!is_array($value)) $value = preg_replace('/(^\'|\'$){1}/', '', $value); - $value = $value.'%'; - break; - case 'like_tail' : - if(!is_array($value)) $value = preg_replace('/(^\'|\'$){1}/', '', $value); - $value = '%'.$value; - break; - case 'like' : - if(!is_array($value)) $value = preg_replace('/(^\'|\'$){1}/', '', $value); - $value = '%'.$value.'%'; - break; - case 'notin' : - if(is_array($value)) - { - $value = $this->addQuotesArray($value); - if($type=='number') return join(',',$value); - else return "'". join("','",$value)."'"; - } - else - { - return $value; - } - break; - case 'in' : - if(is_array($value)) - { - $value = $this->addQuotesArray($value); - if($type=='number') return join(',',$value); - else return "'". join("','",$value)."'"; - } - else - { - return $value; - } - break; - case 'between' : - if(!is_array($value)) $value = array($value); - $value = $this->addQuotesArray($value); - if($type!='number') - { - foreach($value as $k=>$v) - { - $value[$k] = "'".$v."'"; - } - } - - return $value; - break; - default: - if(!is_array($value)) $value = preg_replace('/(^\'|\'$){1}/', '', $value); - } - - return "'".$this->addQuotes($value)."'"; - } - - /** - * @brief returns part of condition - * @param[in] $name name of condition - * @param[in] $value value of condition - * @param[in] $operation operation that is used in condition - * @return detail condition - **/ - function getConditionPart($name, $value, $operation) { - switch($operation) { - case 'equal' : - case 'more' : - case 'excess' : - case 'less' : - case 'below' : - case 'like_tail' : - case 'like_prefix' : - case 'like' : - case 'in' : - case 'notin' : - case 'notequal' : - // if variable is not set or is not string or number, return - if(!isset($value)) return; - if($value === '') return; - if(!in_array(gettype($value), array('string', 'integer'))) return; - break; - case 'between' : - if(!is_array($value)) return; - if(count($value)!=2) return; - - } - - switch($operation) { - case 'equal' : - return $name.' = '.$value; - break; - case 'more' : - return $name.' >= '.$value; - break; - case 'excess' : - return $name.' > '.$value; - break; - case 'less' : - return $name.' <= '.$value; - break; - case 'below' : - return $name.' < '.$value; - break; - case 'like_tail' : - case 'like_prefix' : - case 'like' : - return $name.' like '.$value; - break; - case 'in' : - return $name.' in ('.$value.')'; - break; - case 'notin' : - return $name.' not in ('.$value.')'; - break; - case 'notequal' : - return $name.' <> '.$value; - break; - case 'notnull' : - return $name.' is not null'; - break; - case 'null' : - return $name.' is null'; - break; - case 'between' : - return $name.' between ' . $value[0] . ' and ' . $value[1]; - break; - } - } - - /** - * @brief returns condition key - * @param[in] $output result of query - * @return array of conditions of $output - **/ - function getConditionList($output) { - $conditions = array(); - if(count($output->conditions)) { - foreach($output->conditions as $key => $val) { - if($val['condition']) { - foreach($val['condition'] as $k => $v) { - $conditions[] = $v['column']; - } - } - } - } - - return $conditions; - } - - /** - * @brief returns counter cache data - * @param[in] $tables tables to get data - * @param[in] $condition condition to get data - * @return count of cache data - **/ - function getCountCache($tables, $condition) { - return false; - if(!$tables) return false; - if(!is_dir($this->count_cache_path)) return FileHandler::makeDir($this->count_cache_path); - - $condition = md5($condition); - - if(!is_array($tables)) $tables_str = $tables; - else $tables_str = implode('.',$tables); - - $cache_path = sprintf('%s/%s%s', $this->count_cache_path, $this->prefix, $tables_str); - if(!is_dir($cache_path)) FileHandler::makeDir($cache_path); - - $cache_filename = sprintf('%s/%s.%s', $cache_path, $tables_str, $condition); - if(!file_exists($cache_filename)) return false; - - $cache_mtime = filemtime($cache_filename); - - if(!is_array($tables)) $tables = array($tables); - foreach($tables as $alias => $table) { - $table_filename = sprintf('%s/cache.%s%s', $this->count_cache_path, $this->prefix, $table) ; - if(!file_exists($table_filename) || filemtime($table_filename) > $cache_mtime) return false; - } - - $count = (int)FileHandler::readFile($cache_filename); - return $count; - } - - /** - * @brief save counter cache data - * @param[in] $tables tables to save data - * @param[in] $condition condition to save data - * @param[in] $count count of cache data to save - * @return none - **/ - function putCountCache($tables, $condition, $count = 0) { - return false; - if(!$tables) return false; - if(!is_dir($this->count_cache_path)) return FileHandler::makeDir($this->count_cache_path); - - $condition = md5($condition); - - if(!is_array($tables)) $tables_str = $tables; - else $tables_str = implode('.',$tables); - - $cache_path = sprintf('%s/%s%s', $this->count_cache_path, $this->prefix, $tables_str); - if(!is_dir($cache_path)) FileHandler::makeDir($cache_path); - - $cache_filename = sprintf('%s/%s.%s', $cache_path, $tables_str, $condition); - - FileHandler::writeFile($cache_filename, $count); - } - - /** - * @brief reset counter cache data - * @param[in] $tables tables to reset cache data - * @return true: success, false: failed - **/ - function resetCountCache($tables) { - return false; - if(!$tables) return false; - if(!is_dir($this->count_cache_path)) return FileHandler::makeDir($this->count_cache_path); - - if(!is_array($tables)) $tables = array($tables); - foreach($tables as $alias => $table) { - $filename = sprintf('%s/cache.%s%s', $this->count_cache_path, $this->prefix, $table); - FileHandler::removeFile($filename); - FileHandler::writeFile($filename, ''); - } - - return true; - } - - /** - * @brief returns supported database list - * @return list of supported database - **/ - function getSupportedDatabase(){ - $result = array(); - - if(function_exists('mysql_connect')) $result[] = 'MySQL'; - if(function_exists('cubrid_connect')) $result[] = 'Cubrid'; - if(function_exists('ibase_connect')) $result[] = 'FireBird'; - if(function_exists('pg_connect')) $result[] = 'Postgre'; - if(function_exists('sqlite_open')) $result[] = 'sqlite2'; - if(function_exists('mssql_connect')) $result[] = 'MSSQL'; - if(function_exists('PDO')) $result[] = 'sqlite3(PDO)'; - - return $result; - } - - function dropTable($table_name){ - if(!$table_name) return; - $query = sprintf("drop table %s%s", $this->prefix, $table_name); - $this->_query($query); - } - - function addQuotesArray($arr) - { - if(is_array($arr)) - { - foreach($arr as $k => $v) - { - $arr[$k] = $this->addQuotes($v); - } - } - else - { - $arr = $this->addQuotes($arr); - } - - return $arr; - } - - /** - * @brief Just like numbers, and operations needed to remove the rest - **/ - function _filterNumber(&$value) - { - $value = preg_replace('/[^\d\w\+\-\*\/\.\(\)]/', '', $value); - $value = preg_replace('@\b(?:select|update|delete)\b|[/+\*]{2,}|(-){2,}@i', '$1', $value); - if(!$value) $value = 0; - } - } -?> + '=', + 'more' => '>=', + 'excess' => '>', + 'less' => '<=', + 'below' => '<', + 'notequal' => '<>', + 'notnull' => 'is not null', + 'null' => 'is null', + ); + + var $master_db = NULL; // master database connection string + var $slave_db = NULL; // array of slave databases connection strings + + var $result = NULL; ///< result + + var $errno = 0; ///< error code (0 means no error) + var $errstr = ''; ///< error message + var $query = ''; ///< query string of latest executed query + var $connection = ''; + var $elapsed_time = 0; ///< elapsed time of latest executed query + var $elapsed_dbclass_time = 0; ///< elapsed time of latest executed query + + var $transaction_started = false; ///< transaction flag + + var $is_connected = false; ///< is db connected + + var $supported_list = array(); ///< list of supported db, (will be written by classes/DB/DB***.class.php) + + var $cache_file = 'files/cache/queries/'; ///< location of query cache + + var $db_type; ///< stores database type: 'mysql','cubrid','mssql' etc. or 'db' when database is not yet set + + /** + * @brief returns instance of certain db type + * @param[in] $db_type type of db + * @return instance + **/ + function &getInstance($db_type = NULL) { + if(!$db_type) $db_type = Context::getDBType(); + if(!$db_type && Context::isInstalled()) return new Object(-1, 'msg_db_not_setted'); + + if(!isset($GLOBALS['__DB__'])) $GLOBALS['__DB__'] = array(); + if(!isset($GLOBALS['__DB__'][$db_type])) { + $class_name = 'DB'.ucfirst($db_type); + $class_file = _XE_PATH_."classes/db/$class_name.class.php"; + if(!file_exists($class_file)) return new Object(-1, 'msg_db_not_setted'); + + // get a singletone instance of the database driver class + require_once($class_file); + $GLOBALS['__DB__'][$db_type] = call_user_func(array($class_name, 'create')); + $GLOBALS['__DB__'][$db_type]->db_type = $db_type; + } + + return $GLOBALS['__DB__'][$db_type]; + } + + function create() { + return new DB; + } + + /** + * @brief constructor + * @return none + **/ + function DB() { + $this->count_cache_path = _XE_PATH_.$this->count_cache_path; + $this->cache_file = _XE_PATH_.$this->cache_file; + } + + /** + * @brief returns list of supported db + * @return list of supported db + **/ + function getSupportedList() { + $oDB = new DB(); + return $oDB->_getSupportedList(); + } + + /** + * @brief returns list of enable in supported db + * @return list of enable in supported db + **/ + function getEnableList() + { + if(!$this->supported_list) + { + $oDB = new DB(); + $this->supported_list = $oDB->_getSupportedList(); + } + + $enableList = array(); + if(is_array($this->supported_list)) + { + foreach($this->supported_list AS $key=>$value) + if($value->enable) array_push($enableList, $value); + } + return $enableList; + } + + /** + * @brief returns list of disable in supported db + * @return list of disable in supported db + **/ + function getDisableList() + { + if(!$this->supported_list) + { + $oDB = new DB(); + $this->supported_list = $oDB->_getSupportedList(); + } + + $disableList = array(); + if(is_array($this->supported_list)) + { + foreach($this->supported_list AS $key=>$value) + if(!$value->enable) array_push($disableList, $value); + } + return $disableList; + } + + /** + * @brief returns list of supported db + * @return list of supported db + **/ + function _getSupportedList() { + $db_classes_path = _XE_PATH_."classes/db/"; + $filter = "/^DB([^\.]+)\.class\.php/i"; + $supported_list = FileHandler::readDir($db_classes_path, $filter, true); + sort($supported_list); + + // after creating instance of class, check is supported + for($i = 0; $i < count($supported_list); $i++) { + $db_type = $supported_list[$i]; + + if(version_compare(phpversion(), '5.0') < 0 && preg_match('/pdo/i',$db_type)) continue; + + $class_name = sprintf("DB%s%s", strtoupper(substr($db_type,0,1)), strtolower(substr($db_type,1))); + $class_file = sprintf(_XE_PATH_."classes/db/%s.class.php", $class_name); + if(!file_exists($class_file)) continue; + + unset($oDB); + require_once($class_file); + $tmp_fn = create_function('', "return new {$class_name}();"); + $oDB = $tmp_fn(); + + if(!$oDB) continue; + + $obj = null; + $obj->db_type = $db_type; + $obj->enable = $oDB->isSupported() ? true : false; + + $this->supported_list[] = $obj; + } + + return $this->supported_list; + } + + /** + * @brief check if the db_type is supported + * @param[in] $db_type type of db to check + * @return true: is supported, false: is not supported + **/ + function isSupported($db_type) { + $supported_list = DB::getSupportedList(); + return in_array($db_type, $supported_list); + } + + /** + * @brief check if is connected + * @return true: connected, false: not connected + **/ + function isConnected($type = 'master', $indx = 0) { + if($type == 'master') return $this->master_db["is_connected"] ? true : false; + else return $this->slave_db[$indx]["is_connected"] ? true : false; + } + + /** + * @brief start recording log + * @return none + **/ + function actStart($query) { + $this->setError(0, 'success'); + $this->query = $query; + $this->act_start = getMicroTime(); + $this->elapsed_time = 0; + } + + /** + * @brief finish recording log + * @return none + **/ + function actFinish() { + if(!$this->query) return; + $this->act_finish = getMicroTime(); + $elapsed_time = $this->act_finish - $this->act_start; + $this->elapsed_time = $elapsed_time; + $GLOBALS['__db_elapsed_time__'] += $elapsed_time; + + $log['query'] = $this->query; + $log['elapsed_time'] = $elapsed_time; + $log['connection'] = $this->connection; + + // leave error log if an error occured (if __DEBUG_DB_OUTPUT__ is defined) + if($this->isError()) { + $site_module_info = Context::get('site_module_info'); + $log['module'] = $site_module_info->module; + $log['act'] = Context::get('act'); + $log['query_id'] = $this->query_id; + $log['time'] = date('Y-m-d H:i:s'); + $log['result'] = 'Failed'; + $log['errno'] = $this->errno; + $log['errstr'] = $this->errstr; + + if(__DEBUG_DB_OUTPUT__ == 1) { + $debug_file = _XE_PATH_."files/_debug_db_query.php"; + $buff = array(); + if(!file_exists($debug_file)) $buff[] = ''; + $buff[] = print_r($log, true); + + if(@!$fp = fopen($debug_file, "a")) return; + fwrite($fp, implode("\n", $buff)."\n\n"); + fclose($fp); + } + } else { + $log['result'] = 'Success'; + } + $GLOBALS['__db_queries__'][] = $log; + + // if __LOG_SLOW_QUERY__ if defined, check elapsed time and leave query log + if(__LOG_SLOW_QUERY__ > 0 && $elapsed_time > __LOG_SLOW_QUERY__) { + $buff = ''; + $log_file = _XE_PATH_.'files/_db_slow_query.php'; + if(!file_exists($log_file)) { + $buff = ''."\n"; + } + + $buff .= sprintf("%s\t%s\n\t%0.6f sec\tquery_id:%s\n\n", date("Y-m-d H:i"), $this->query, $elapsed_time, $this->query_id); + + if($fp = fopen($log_file, 'a')) { + fwrite($fp, $buff); + fclose($fp); + } + } + } + + /** + * @brief set error + * @param[in] $errno error code + * @param[in] $errstr error message + * @return none + **/ + function setError($errno = 0, $errstr = 'success') { + $this->errno = $errno; + $this->errstr = $errstr; + } + + /** + * @brief check if an error occured + * @return true: error, false: no error + **/ + function isError() { + return $this->errno === 0 ? false : true; + } + + /** + * @brief returns object of error info + * @return object of error + **/ + function getError() { + $this->errstr = Context::convertEncodingStr($this->errstr); + return new Object($this->errno, $this->errstr); + } + + /** + * @brief Run the result of the query xml file + * @param[in] $query_id query id (module.queryname + * @param[in] $args arguments for query + * @return result of query + * @remarks this function finds xml file or cache file of $query_id, compiles it and then execute it + **/ + function executeQuery($query_id, $args = NULL, $arg_columns = NULL) { + if(!$query_id) return new Object(-1, 'msg_invalid_queryid'); + if(!$this->db_type) return; + + $this->actDBClassStart(); + + $this->query_id = $query_id; + + $id_args = explode('.', $query_id); + if(count($id_args) == 2) { + $target = 'modules'; + $module = $id_args[0]; + $id = $id_args[1]; + } elseif(count($id_args) == 3) { + $target = $id_args[0]; + if(!in_array($target, array('addons','widgets'))){ + $this->actDBClassFinish(); + return; + } + $module = $id_args[1]; + $id = $id_args[2]; + } + if(!$target || !$module || !$id){ + $this->actDBClassFinish(); + return new Object(-1, 'msg_invalid_queryid'); + } + + $xml_file = sprintf('%s%s/%s/queries/%s.xml', _XE_PATH_, $target, $module, $id); + if(!file_exists($xml_file)){ + $this->actDBClassFinish(); + return new Object(-1, 'msg_invalid_queryid'); + } + + // look for cache file + $cache_file = $this->checkQueryCacheFile($query_id, $xml_file); + $result = $this->_executeQuery($cache_file, $args, $query_id, $arg_columns); + + $this->actDBClassFinish(); + // execute query + return $result; + } + + + /** + * @brief look for cache file + * @param[in] $query_id query id for finding + * @param[in] $xml_file original xml query file + * @return cache file + **/ + function checkQueryCacheFile($query_id,$xml_file){ + + // first try finding cache file + $cache_file = sprintf('%s%s%s.%s.%s.cache.php', _XE_PATH_, $this->cache_file, $query_id, __ZBXE_VERSION__, $this->db_type); + + if(file_exists($cache_file)) $cache_time = filemtime($cache_file); + else $cache_time = -1; + + // if there is no cache file or is not new, find original xml query file and parse it + if($cache_time < filemtime($xml_file) || $cache_time < filemtime(_XE_PATH_.'classes/db/DB.class.php') || $cache_time < filemtime(_XE_PATH_.'classes/xml/XmlQueryParser.150.class.php')) { + require_once(_XE_PATH_.'classes/xml/XmlQueryParser.150.class.php'); + $oParser = new XmlQueryParser(); + $oParser->parse($query_id, $xml_file, $cache_file); + } + + return $cache_file; + } + + + /** + * @brief execute query and return the result + * @param[in] $cache_file cache file of query + * @param[in] $source_args arguments for query + * @param[in] $query_id query id + * @return result of query + **/ + function _executeQuery($cache_file, $source_args, $query_id, $arg_columns) { + 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; + + // execute appropriate query + switch($output->getAction()) { + case 'insert' : + $this->resetCountCache($output->tables); + $output = $this->_executeInsertAct($output); + break; + case 'update' : + $this->resetCountCache($output->tables); + $output = $this->_executeUpdateAct($output); + break; + case 'delete' : + $this->resetCountCache($output->tables); + $output = $this->_executeDeleteAct($output); + break; + case 'select' : + $arg_columns = is_array($arg_columns)?$arg_columns:array(); + $output->setColumnList($arg_columns); + $connection = $this->_getConnection('slave'); + $output = $this->_executeSelectAct($output, $connection); + break; + } + + if($this->isError()) $output = $this->getError(); + else if(!is_a($output, 'Object') && !is_subclass_of($output, 'Object')) $output = new Object(); + $output->add('_query', $this->query); + $output->add('_elapsed_time', sprintf("%0.5f", $this->elapsed_time)); + + return $output; + } + + + /** + * @brief returns counter cache data + * @param[in] $tables tables to get data + * @param[in] $condition condition to get data + * @return count of cache data + **/ + function getCountCache($tables, $condition) { + return false; + if(!$tables) return false; + if(!is_dir($this->count_cache_path)) return FileHandler::makeDir($this->count_cache_path); + + $condition = md5($condition); + + if(!is_array($tables)) $tables_str = $tables; + else $tables_str = implode('.',$tables); + + $cache_path = sprintf('%s/%s%s', $this->count_cache_path, $this->prefix, $tables_str); + if(!is_dir($cache_path)) FileHandler::makeDir($cache_path); + + $cache_filename = sprintf('%s/%s.%s', $cache_path, $tables_str, $condition); + if(!file_exists($cache_filename)) return false; + + $cache_mtime = filemtime($cache_filename); + + if(!is_array($tables)) $tables = array($tables); + foreach($tables as $alias => $table) { + $table_filename = sprintf('%s/cache.%s%s', $this->count_cache_path, $this->prefix, $table) ; + if(!file_exists($table_filename) || filemtime($table_filename) > $cache_mtime) return false; + } + + $count = (int)FileHandler::readFile($cache_filename); + return $count; + } + + /** + * @brief save counter cache data + * @param[in] $tables tables to save data + * @param[in] $condition condition to save data + * @param[in] $count count of cache data to save + * @return none + **/ + function putCountCache($tables, $condition, $count = 0) { + return false; + if(!$tables) return false; + if(!is_dir($this->count_cache_path)) return FileHandler::makeDir($this->count_cache_path); + + $condition = md5($condition); + + if(!is_array($tables)) $tables_str = $tables; + else $tables_str = implode('.',$tables); + + $cache_path = sprintf('%s/%s%s', $this->count_cache_path, $this->prefix, $tables_str); + if(!is_dir($cache_path)) FileHandler::makeDir($cache_path); + + $cache_filename = sprintf('%s/%s.%s', $cache_path, $tables_str, $condition); + + FileHandler::writeFile($cache_filename, $count); + } + + /** + * @brief reset counter cache data + * @param[in] $tables tables to reset cache data + * @return true: success, false: failed + **/ + function resetCountCache($tables) { + return false; + if(!$tables) return false; + if(!is_dir($this->count_cache_path)) return FileHandler::makeDir($this->count_cache_path); + + if(!is_array($tables)) $tables = array($tables); + foreach($tables as $alias => $table) { + $filename = sprintf('%s/cache.%s%s', $this->count_cache_path, $this->prefix, $table); + FileHandler::removeFile($filename); + FileHandler::writeFile($filename, ''); + } + + return true; + } + + /** + * @brief returns supported database list + * @return list of supported database + **/ + function getSupportedDatabase(){ + $result = array(); + + if(function_exists('mysql_connect')) $result[] = 'MySQL'; + if(function_exists('cubrid_connect')) $result[] = 'Cubrid'; + if(function_exists('ibase_connect')) $result[] = 'FireBird'; + if(function_exists('pg_connect')) $result[] = 'Postgre'; + if(function_exists('sqlite_open')) $result[] = 'sqlite2'; + if(function_exists('mssql_connect')) $result[] = 'MSSQL'; + if(function_exists('PDO')) $result[] = 'sqlite3(PDO)'; + + return $result; + } + + function dropTable($table_name){ + if(!$table_name) return; + $query = sprintf("drop table %s%s", $this->prefix, $table_name); + $this->_query($query); + } + + function getSelectSql($query, $with_values = true){ + $select = $query->getSelectString($with_values); + if($select == '') return new Object(-1, "Invalid query"); + $select = 'SELECT ' .$select; + + $from = $query->getFromString($with_values); + if($from == '') return new Object(-1, "Invalid query"); + $from = ' FROM '.$from; + + $where = $query->getWhereString($with_values); + if($where != '') $where = ' WHERE ' . $where; + + $tableObjects = $query->getTables(); + $index_hint_list = ''; + foreach($tableObjects as $tableObject){ + if(is_a($tableObject, 'CubridTableWithHint')) + $index_hint_list .= $tableObject->getIndexHintString() . ', '; + } + $index_hint_list = substr($index_hint_list, 0, -2); + if($index_hint_list != '') + $index_hint_list = 'USING INDEX ' . $index_hint_list; + + $groupBy = $query->getGroupByString(); + if($groupBy != '') $groupBy = ' GROUP BY ' . $groupBy; + + $orderBy = $query->getOrderByString(); + if($orderBy != '') $orderBy = ' ORDER BY ' . $orderBy; + + $limit = $query->getLimitString(); + if($limit != '') $limit = ' LIMIT ' . $limit; + + return $select . ' ' . $from . ' ' . $where . ' ' . $index_hint_list . ' ' . $groupBy . ' ' . $orderBy . ' ' . $limit; + } + + function getDeleteSql($query, $with_values = true, $with_priority = false){ + $sql = 'DELETE '; + + $sql .= $with_priority?$query->getPriority():''; + $tables = $query->getTables(); + + $sql .= $tables[0]->getAlias(); + + $from = $query->getFromString($with_values); + if($from == '') return new Object(-1, "Invalid query"); + $sql .= ' FROM '.$from; + + $where = $query->getWhereString($with_values); + if($where != '') $sql .= ' WHERE ' . $where; + + return $sql; + } + + function getUpdateSql($query, $with_values = true, $with_priority = false){ + $columnsList = $query->getUpdateString($with_values); + if($columnsList == '') return new Object(-1, "Invalid query"); + + $tables = $query->getFromString($with_values); + if($tables == '') return new Object(-1, "Invalid query"); + + $where = $query->getWhereString($with_values); + if($where != '') $where = ' WHERE ' . $where; + + $priority = $with_priority?$query->getPriority():''; + + return "UPDATE $priority $tables SET $columnsList ".$where; + } + + function getInsertSql($query, $with_values = true, $with_priority = false){ + $tableName = $query->getFirstTableName(); + $values = $query->getInsertString($with_values); + $priority = $with_priority?$query->getPriority():''; + + return "INSERT $priority INTO $tableName \n $values"; + } + + function _getSlaveConnectionStringIndex() { + $max = count($this->slave_db); + $indx = rand(0, $max - 1); + return $indx; + } + + function _getConnection($type = 'master', $indx = NULL){ + if($type == master){ + if(!$this->master_db['is_connected']) + $this->_connect($type); + $this->connection = 'Master ' . $this->master_db['db_hostname']; + return $this->master_db["resource"]; + } + + if($indx === NULL) + $indx = $this->_getSlaveConnectionStringIndex($type); + + if(!$this->slave_db[$indx]['is_connected']) + $this->_connect($type, $indx); + + $this->connection = 'Slave ' . $this->slave_db[$indx]['db_hostname']; + return $this->slave_db[$indx]["resource"]; + } + + function _dbInfoExists() { + if (!$this->master_db) + return false; + if (count($this->slave_db) === 0) + return false; + return true; + } + + function _close($connection){ + + } + + /** + * @brief DB disconnection + * */ + function close($type = 'master', $indx = 0) { + if (!$this->isConnected($type, $indx)) + return; + + if ($type == 'master') + $connection = &$this->master_db; + else + $connection = &$this->slave_db[$indx]; + + $this->_close($connection["resource"]); + + $connection["is_connected"] = false; + } + + function _begin(){ + return true; + } + /** + * @brief Begin transaction + * */ + function begin() { + if (!$this->isConnected() || $this->transaction_started) + return; + + if($this->_begin()) + $this->transaction_started = true; + } + + function _rollback(){ + return true; + } + + /** + * @brief Rollback + * */ + function rollback() { + if (!$this->isConnected() || !$this->transaction_started) + return; + if($this->_rollback()) + $this->transaction_started = false; + } + + function _commit(){ + return true; + } + /** + * @brief Commits + * */ + function commit($force = false) { + if (!$force && (!$this->isConnected() || !$this->transaction_started)) + return; + if($this->_commit()) + $this->transaction_started = false; + } + + function __query($query, $connection){ + + } + /** + * @brief : Run a query and fetch the result + * + * query: run a query and return the result \n + * fetch: NULL if no value is returned \n + * array object if rows are returned \n + * object if a row is returned \n + * return\n + * */ + function _query($query, $connection = null) { + if($connection == null) + $connection = $this->_getConnection('master'); + // Notify to start a query execution + $this->actStart($query); + + // Run the query statement + $result = $this->__query($query, $connection); + + // Notify to complete a query execution + $this->actFinish(); + // Return result + return $result; + } + + /** + * @brief DB settings and connect/close + * */ + function _setDBInfo(){ + $db_info = Context::getDBInfo(); + $this->master_db = $db_info->master_db; + if($db_info->master_db["db_hostname"] == $db_info->slave_db[0]["db_hostname"] + && $db_info->master_db["db_port"] == $db_info->slave_db[0]["db_port"] + && $db_info->master_db["db_userid"] == $db_info->slave_db[0]["db_userid"] + && $db_info->master_db["db_password"] == $db_info->slave_db[0]["db_password"] + && $db_info->master_db["db_database"] == $db_info->slave_db[0]["db_database"] + ) + $this->slave_db[0] = &$this->master_db; + else + $this->slave_db = $db_info->slave_db; + $this->prefix = $db_info->master_db["db_table_prefix"]; + } + + function __connect(){ + + } + + function _afterConnect($connection){ + + } + /** + * @brief DB Connection + * */ + function _connect($type = 'master', $indx = 0) { + if ($this->isConnected($type, $indx)) + return; + + // Ignore if no DB information exists + if (!$this->_dbInfoExists()) + return; + + if ($type == 'master') + $connection = &$this->master_db; + else + $connection = &$this->slave_db[$indx]; + + $result = $this->__connect($connection); + if($result === NULL || $result === false) { + $connection["is_connected"] = false; + return; + } + + // Check connections + $connection["resource"] = $result; + $connection["is_connected"] = true; + + // Save connection info for db logs + $this->connection = ucfirst($type) . ' ' . $connection["db_hostname"]; + + $this->_afterConnect($result); + } + /** + * @brief start recording DBClass log + * @return none + **/ + function actDBClassStart() { + $this->setError(0, 'success'); + $this->act_dbclass_start = getMicroTime(); + $this->elapsed_dbclass_time = 0; + } + + /** + * @brief finish recording DBClass log + * @return none + **/ + function actDBClassFinish() { + if(!$this->query) return; + $this->act_dbclass_finish = getMicroTime(); + $elapsed_dbclass_time = $this->act_dbclass_finish - $this->act_dbclass_start; + $this->elapsed_dbclass_time = $elapsed_dbclass_time; + $GLOBALS['__dbclass_elapsed_time__'] += $elapsed_dbclass_time; + } + + /** + * Returns a database specific parser class + * used for escaping expressions and table/column identifiers + * + * Requires an implementation of the DB class (won't work if database is not set) + * + * @remarks singleton + */ + function &getParser($force = false){ + static $dbParser = null; + if(!$dbParser || $force) { + $oDB = &DB::getInstance(); + $dbParser = $oDB->getParser(); + } + + return $dbParser; + } + + } +?> diff --git a/classes/db/DBCubrid.class.php b/classes/db/DBCubrid.class.php index e8d349aa9..58167b9a9 100644 --- a/classes/db/DBCubrid.class.php +++ b/classes/db/DBCubrid.class.php @@ -1,1253 +1,705 @@ - 'numeric(20)', - 'number' => 'integer', - 'varchar' => 'character varying', - 'char' => 'character', - 'tinytext' => 'character varying(256)', - 'text' => 'character varying(1073741823)', - 'bigtext' => 'character varying(1073741823)', - 'date' => 'character varying(14)', - 'float' => 'float', - ); - - /** - * @brief constructor - **/ - function DBCubrid() - { - $this->_setDBInfo(); - $this->_connect(); - } - - /** - * @brief create an instance of this class - */ - function create() - { - return new DBCubrid; - } - - /** - * @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; - $this->password = md5 ($this->password); - } - - /** - * @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 (version_compare (PHP_VERSION, "5.9.0", "<") && - get_magic_quotes_gpc ()) { - $string = stripslashes (str_replace ("\\","\\\\", $string)); - } - - if (!is_numeric ($string)) { - /* - if ($this->isConnected()) { - $string = cubrid_real_escape_string($string); - } - else { - $string = str_replace("'","\'",$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) - { - if (!$query || !$this->isConnected ()) return; - - // 쿼리 시작을 알림 - $this->actStart ($query); - - // 쿼리 문 실행 - $result = @cubrid_execute ($this->fd, $query); - // 오류 체크 - if (cubrid_error_code ()) { - $code = cubrid_error_code (); - $msg = cubrid_error_msg (); - - $this->setError ($code, $msg); - } - - // 쿼리 실행 종료를 알림 - $this->actFinish (); - - // 결과 리턴 - return $result; - } - - /** - * @brief 결과를 fetch - **/ - function _fetch($result) - { - if (!$this->isConnected() || $this->isError() || !$result) return; - - $col_types = cubrid_column_types ($result); - $col_names = cubrid_column_names ($result); - $max = count ($col_types); - - for ($count = 0; $count < $max; $count++) { - if (preg_match ("/^char/", $col_types[$count]) > 0) { - $char_type_fields[] = $col_names[$count]; - } - } - - while ($tmp = cubrid_fetch ($result, CUBRID_OBJECT)) { - if (is_array ($char_type_fields)) { - foreach ($char_type_fields as $val) { - $tmp->{$val} = rtrim ($tmp->{$val}); - } - } - - $output[] = $tmp; - } - - unset ($char_type_fields); - - 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() - { - $this->_makeSequence(); - - $query = sprintf ("select \"%ssequence\".\"nextval\" as \"seq\" from db_root", $this->prefix); - $result = $this->_query($query); - $output = $this->_fetch($result); - - return $output->seq; - } - - /** - * @brief 마이그레이션시 sequence 가 없을 경우 생성 - **/ - function _makeSequence() - { - if($_GLOBALS['XE_EXISTS_SEQUENCE']) return; - - // check cubrid serial - $query = sprintf('select count(*) as "count" from "db_serial" where name=\'%ssequence\'', $this->prefix); - $result = $this->_query($query); - $output = $this->_fetch($result); - - // if do not create serial - if ($output->count == 0) { - $query = sprintf('select max("a"."srl") as "srl" from '. - '( select max("document_srl") as "srl" from '. - '"%sdocuments" UNION '. - 'select max("comment_srl") as "srl" from '. - '"%scomments" UNION '. - 'select max("member_srl") as "srl" from '. - '"%smember"'. - ') as "a"', $this->prefix, $this->prefix, $this->prefix); - - $result = $this->_query($query); - $output = $this->_fetch($result); - $srl = $output->srl; - if ($srl < 1) { - $start = 1; - } - else { - $start = $srl + 1000000; - } - - // create sequence - $query = sprintf('create serial "%ssequence" start with %s increment by 1 minvalue 1 maxvalue 10000000000000000000000000000000000000 nocycle;', $this->prefix, $start); - $this->_query($query); - } - - $_GLOBALS['XE_EXISTS_SEQUENCE'] = true; - } - - - /** - * @brief 테이블 기생성 여부 return - **/ - function isTableExists ($target_name) - { - if($target_name == 'sequence') { - $query = sprintf ("select \"name\" from \"db_serial\" where \"name\" = '%s%s'", $this->prefix, $target_name); - } - else { - $query = sprintf ("select \"class_name\" 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 = strtoupper($this->column_type[$type]); - if ($type == 'INTEGER') $size = ''; - - $query = sprintf ("alter class \"%s%s\" add \"%s\" ", $this->prefix, $table_name, $column_name); - - if ($type == 'char' || $type == 'varchar') { - if ($size) $size = $size * 3; - } - - if ($size) { - $query .= sprintf ("%s(%s) ", $type, $size); - } - else { - $query .= sprintf ("%s ", $type); - } - - if ($default) { - if ($type == 'INTEGER' || $type == 'BIGINT' || $type=='INT') { - $query .= sprintf ("default %d ", $default); - } - else { - $query .= sprintf ("default '%s' ", $default); - } - } - - if ($notnull) $query .= "not null "; - - $this->_query ($query); - } - - /** - * @brief 특정 테이블에 특정 column 제거 - **/ - function dropColumn ($table_name, $column_name) - { - $query = sprintf ("alter class \"%s%s\" drop \"%s\" ", $this->prefix, $table_name, $column_name); - - $this->_query ($query); - } - - /** - * @brief 특정 테이블의 column의 정보를 return - **/ - function isColumnExists ($table_name, $column_name) - { - $query = sprintf ("select \"attr_name\" 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 특정 테이블에 특정 인덱스 추가 - * $target_columns = array(col1, col2) - * $is_unique? unique : none - **/ - function addIndex ($table_name, $index_name, $target_columns, $is_unique = false) - { - if (!is_array ($target_columns)) { - $target_columns = array ($target_columns); - } - - $query = sprintf ("create %s index \"%s\" on \"%s%s\" (%s);", $is_unique?'unique':'', $this->prefix .$index_name, $this->prefix, $table_name, '"'.implode('","',$target_columns).'"'); - - $this->_query ($query); - } - - /** - * @brief 특정 테이블의 특정 인덱스 삭제 - **/ - function dropIndex ($table_name, $index_name, $is_unique = false) - { - $query = sprintf ("drop %s index \"%s\" on \"%s%s\"", $is_unique?'unique':'', $this->prefix .$index_name, $this->prefix, $table_name); - - $this->_query($query); - } - - /** - * @brief 특정 테이블의 index 정보를 return - **/ - function isIndexExists ($table_name, $index_name) - { - $query = sprintf ("select \"index_name\" from \"db_index\" where ". "\"class_name\" = '%s%s' and \"index_name\" = '%s' ", $this->prefix, $table_name, $this->prefix .$index_name); - $result = $this->_query ($query); - - if ($this->isError ()) return false; - - $output = $this->_fetch ($result); - - if (!$output) return false; - return true; - } - - /** - * @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, tinytext, text, bigtext, 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 the table already exists exit function - if ($this->isTableExists($table_name)) return; - - // 만약 테이블 이름이 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); - } - - - $table_name = $this->prefix.$table_name; - - $query = sprintf ('create class "%s";', $table_name); - $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 (isset ($default) && ($type == 'varchar' || $type == 'char' || - $type == 'text' || $type == 'tinytext' || $type == 'bigtext')) { - $default = sprintf ("'%s'", $default); - } - - if ($type == 'varchar' || $type == 'char') { - if($size) $size = $size * 3; - } - - - $column_schema[] = sprintf ('"%s" %s%s %s %s', - $name, - $this->column_type[$type], - $size?'('.$size.')':'', - isset($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\" on \"%s\" ". "(%s);", $this->prefix .$key, $table_name, '"'.implode('","', $val).'"'); - $this->_query ($query); - } - } - - if (count ($index_list)) { - foreach ($index_list as $key => $val) { - $query = sprintf ("create index \"%s\" on \"%s\" (%s);", $this->prefix .$key, $table_name, '"'.implode('","',$val).'"'); - $this->_query ($query); - } - } - } - - /** - * @brief 조건문 작성하여 return - **/ - function getCondition ($output) - { - if (!$output->conditions) return; - $condition = $this->_getCondition ($output->conditions, $output->column_type, $output); - if ($condition) $condition = ' where '.$condition; - - return $condition; - } - - function _getCondition ($conditions, $column_type, &$output) - { - $condition = ''; - - foreach ($conditions as $val) { - $sub_condition = ''; - - foreach ($val['condition'] as $v) { - if (!isset ($v['value'])) continue; - if ($v['value'] === '') continue; - if(!in_array(gettype($v['value']), array('string', 'integer', 'double', 'array'))) continue; - - $name = $v['column']; - $operation = $v['operation']; - $value = $v['value']; - $type = $this->getColumnType ($column_type, $name); - $pipe = $v['pipe']; - $value = $this->getConditionValue ($name, $value, $operation, $type, $column_type); - - if (!$value) { - $value = $v['value']; - if (strpos ($value, '(')) { - $valuetmp = $value; - } - elseif (strpos ($value, ".") === false) { - $valuetmp = $value; - } - else { - $valuetmp = '"'.str_replace('.', '"."', $value).'"'; - } - } - else { - $tmp = explode('.',$value); - - if (count($tmp)==2) { - $table = $tmp[0]; - $column = $tmp[1]; - - if ($column_type[$column] && (in_array ($table, $output->tables) || - array_key_exists($table, $output->tables))) { - $valuetmp = sprintf('"%s"."%s"', $table, $column); - } - else { - $valuetmp = $value; - } - } - else { - $valuetmp = $value; - } - } - - if (strpos ($name, '(') > 0) { - $nametmp = $name; - } - elseif (strpos ($name, ".") === false) { - $nametmp = '"'.$name.'"'; - } - else { - $nametmp = '"'.str_replace('.', '"."', $name).'"'; - } - $str = $this->getConditionPart ($nametmp, $valuetmp, $operation); - if ($sub_condition) $sub_condition .= ' '.$pipe.' '; - $sub_condition .= $str; - } - - if ($sub_condition) { - if ($condition && $val['pipe']) { - $condition .= ' '.$val['pipe'].' '; - } - $condition .= '('.$sub_condition.')'; - } - } - - return $condition; - } - - /** - * @brief insertAct 처리 - **/ - function _executeInsertAct ($output) - { - // 테이블 정리 - foreach ($output->tables as $val) { - $table_list[] = '"'.$this->prefix.$val.'"'; - } - - // 컬럼 정리 - foreach ($output->columns as $key => $val) { - $name = $val['name']; - $value = $val['value']; - //if ($this->getColumnType ($output->column_type, $name) != 'number') - if ($output->column_type[$name] != 'number') { - if (!is_null($value)) { - $value = "'" . $this->addQuotes($value) ."'"; - } - else { - if ($val['notnull']=='notnull') { - $value = "''"; - } - else { - //$value = 'null'; - $value = "''"; - } - } - } - else $this->_filterNumber(&$value); - - $column_list[] = '"'.$name.'"'; - $value_list[] = $value; - } - - $query = sprintf ("insert into %s (%s) values (%s);", implode(',', $table_list), implode(',', $column_list), implode(',', $value_list)); - - $query .= (__DEBUG_QUERY__&1 && $output->query_id)?sprintf (' '.$this->comment_syntax, $this->query_id):''; - $result = $this->_query ($query); - if ($result && !$this->transaction_started) { - @cubrid_commit ($this->fd); - } - - return $result; - } - - /** - * @brief updateAct 처리 - **/ - function _executeUpdateAct ($output) - { - // 테이블 정리 - foreach ($output->tables as $key => $val) { - $table_list[] = '"'.$this->prefix.$val.'" as "'.$key.'"'; - } - - $check_click_count = true; - - // 컬럼 정리 - foreach ($output->columns as $key => $val) { - if (!isset ($val['value'])) continue; - $name = $val['name']; - $value = $val['value']; - - if (substr ($value, -2) != '+1' || $output->column_type[$name] != 'number') { - $check_click_count = false; - } - - for ($i = 0; $i < $key; $i++) { - /* 한문장에 같은 속성에 대한 중복 설정은 큐브리드에서는 허용치 않음 */ - if ($output->columns[$i]['name'] == $name) break; - } - if ($i < $key) continue; // 중복이 발견되면 이후의 설정은 무시 - - if (strpos ($name, '.') !== false && strpos ($value, '.') !== false) { - $column_list[] = $name.' = '.$value; - } - else { - if ($output->column_type[$name] != 'number') { - $check_column = false; - $value = "'".$this->addQuotes ($value)."'"; - } - else $this->_filterNumber(&$value); - - $column_list[] = sprintf ("\"%s\" = %s", $name, $value); - } - } - - // 조건절 정리 - $condition = $this->getCondition ($output); - - $check_click_count_condition = false; - if ($check_click_count) { - foreach ($output->conditions as $val) { - if ($val['pipe'] == 'or') { - $check_click_count_condition = false; - break; - } - - foreach ($val['condition'] as $v) { - if ($v['operation'] == 'equal') { - $check_click_count_condition = true; - } - else { - if ($v['operation'] == 'in' && !strpos ($v['value'], ',')) { - $check_click_count_condition = true; - } - else { - $check_click_count_condition = false; - } - } - - if ($v['pipe'] == 'or') { - $check_click_count_condition = false; - break; - } - } - } - } - - if ($check_click_count&& $check_click_count_condition && count ($output->tables) == 1 && count ($output->conditions) > 0 && count ($output->groups) == 0 && count ($output->order) == 0) { - foreach ($output->columns as $v) { - $incr_columns[] = 'incr("'.$v['name'].'")'; - } - - $query = sprintf ('select %s from %s %s', join (',', $incr_columns), implode(',', $table_list), $condition); - } - else { - $query = sprintf ("update %s set %s %s", implode (',', $table_list), implode (',', $column_list), $condition); - } - - $result = $this->_query ($query); - if ($result && !$this->transaction_started) @cubrid_commit ($this->fd); - - return $result; - } - - /** - * @brief deleteAct 처리 - **/ - function _executeDeleteAct ($output) - { - // 테이블 정리 - foreach ($output->tables as $val) { - $table_list[] = '"'.$this->prefix.$val.'"'; - } - - // 조건절 정리 - $condition = $this->getCondition ($output); - - $query = sprintf ("delete from %s %s", implode (',',$table_list), $condition); - $result = $this->_query ($query); - if ($result && !$this->transaction_started) @cubrid_commit ($this->fd); - - return $result; - } - - /** - * @brief selectAct 처리 - * - * select의 경우 특정 페이지의 목록을 가져오는 것을 편하게 하기 위해\n - * navigation이라는 method를 제공 - **/ - function _executeSelectAct ($output) - { - // 테이블 정리 - $table_list = array (); - foreach ($output->tables as $key => $val) { - $table_list[] = '"'.$this->prefix.$val.'" as "'.$key.'"'; - } - $left_join = array (); - // why??? - $left_tables = (array) $output->left_tables; - - foreach ($left_tables as $key => $val) { - $condition = $this->_getCondition ($output->left_conditions[$key], $output->column_type, $output); - if ($condition) { - $left_join[] = $val.' "'.$this->prefix.$output->_tables[$key]. '" "'.$key.'" on ('.$condition.')'; - } - } - - $click_count = array(); - if(!$output->columns){ - $output->columns = array(array('name'=>'*')); - } - - $column_list = array (); - foreach ($output->columns as $key => $val) { - $name = $val['name']; - - $click_count = '%s'; - if ($val['click_count'] && count ($output->conditions) > 0) { - $click_count = 'incr(%s)'; - } - - $alias = $val['alias'] ? sprintf ('"%s"', $val['alias']) : null; - $_alias = $val['alias']; - - if ($name == '*') { - $column_list[] = $name; - } - elseif (strpos ($name, '.') === false && strpos ($name, '(') === false) { - $name = sprintf ($click_count,$name); - if ($alias) { - $column_list[$alias] = sprintf('"%s" as %s', $name, $alias); - } - else { - $column_list[] = sprintf ('"%s"', $name); - } - } - else { - if (strpos ($name, '.') != false) { - list ($prefix, $name) = explode('.', $name); - if (($now_matchs = preg_match_all ("/\(/", $prefix, $xtmp)) > 0) { - if ($now_matchs == 1) { - $tmpval = explode ("(", $prefix); - $tmpval[1] = sprintf ('"%s"', $tmpval[1]); - $prefix = implode ("(", $tmpval); - $tmpval = explode (")", $name); - $tmpval[0] = sprintf ('"%s"', $tmpval[0]); - $name = implode (")", $tmpval); - } - } - else { - $prefix = sprintf ('"%s"', $prefix); - $name = ($name == '*') ? $name : sprintf('"%s"',$name); - } - $xtmp = null; - $now_matchs = null; - if($alias) $column_list[$_alias] = sprintf ($click_count, sprintf ('%s.%s', $prefix, $name)) . ($alias ? sprintf (' as %s',$alias) : ''); - else $column_list[] = sprintf ($click_count, sprintf ('%s.%s', $prefix, $name)); - } - elseif (($now_matchs = preg_match_all ("/\(/", $name, $xtmp)) > 0) { - if ($now_matchs == 1 && preg_match ("/[a-zA-Z0-9]*\(\*\)/", $name) < 1) { - $open_pos = strpos ($name, "("); - $close_pos = strpos ($name, ")"); - - if (preg_match ("/,/", $name)) { - $tmp_func_name = sprintf ('%s', substr ($name, 0, $open_pos)); - $tmp_params = sprintf ('%s', substr ($name, $open_pos + 1, $close_pos - $open_pos - 1)); - $tmpval = null; - $tmpval = explode (',', $tmp_params); - - foreach ($tmpval as $tmp_param) { - $tmp_param_list[] = (!is_numeric ($tmp_param)) ? sprintf ('"%s"', $tmp_param) : $tmp_param; - } - - $tmpval = implode (',', $tmp_param_list); - $name = sprintf ('%s(%s)', $tmp_func_name, $tmpval); - } - else { - $name = sprintf ('%s("%s")', substr ($name, 0, $open_pos), substr ($name, $open_pos + 1, $close_pos - $open_pos - 1)); - } - } - - if($alias) $column_list[$_alias] = sprintf ($click_count, $name). ($alias ? sprintf (' as %s', $alias) : ''); - else $column_list[] = sprintf ($click_count, $name); - } - else { - if($alias) $column_list[$_alias] = sprintf($click_count, $name). ($alias ? sprintf(' as %s',$alias) : ''); - else $column_list[] = sprintf($click_count, $name); - } - } - $columns = implode (',', $column_list); - } - - $condition = $this->getCondition ($output); - - $output->column_list = $column_list; - if ($output->list_count && $output->page) { - return ($this->_getNavigationData($table_list, $columns, $left_join, $condition, $output)); - } - - if ($output->order) { - $conditions = $this->getConditionList($output); - //if(in_array('list_order', $conditions) || in_array('update_order', $conditions)) { - foreach($output->order as $key => $val) { - $col = $val[0]; - if(!in_array($col, array('list_order','update_order'))) continue; - if ($condition) $condition .= sprintf(' and %s < 2100000000 ', $col); - else $condition = sprintf(' where %s < 2100000000 ', $col); - } - //} - } - - - if (count ($output->groups)) { - foreach ($output->groups as $key => $value) { - if (strpos ($value, '.')) { - $tmp = explode ('.', $value); - $tmp[0] = sprintf ('"%s"', $tmp[0]); - $tmp[1] = sprintf ('"%s"', $tmp[1]); - $value = implode ('.', $tmp); - } - elseif (strpos ($value, '(')) { - $value = $value; - } - else { - $value = sprintf ('"%s"', $value); - } - $output->groups[$key] = $value; - - - if(count($output->arg_columns)) - { - if($column_list[$value]) $output->arg_columns[] = $column_list[$value]; - } - } - $groupby_query = sprintf ('group by %s', implode(',', $output->groups)); - } - - - // list_count를 사용할 경우 적용 - if ($output->list_count['value']) { - $start_count = 0; - $list_count = $output->list_count['value']; - - if ($output->order) { - foreach ($output->order as $val) { - if (strpos ($val[0], '.')) { - $tmpval = explode ('.', $val[0]); - $tmpval[0] = sprintf ('"%s"', $tmpval[0]); - $tmpval[1] = sprintf ('"%s"', $tmpval[1]); - $val[0] = implode ('.', $tmpval); - } - elseif (strpos ($val[0], '(')) $val[0] = $val[0]; - elseif ($val[0] == 'count') $val[0] = 'count (*)'; - else $val[0] = sprintf ('"%s"', $val[0]); - $index_list[] = sprintf('%s %s', $val[0], $val[1]); - } - if (count($index_list)) - $orderby_query = ' order by '.implode(',', $index_list); - $orderby_query = sprintf ('%s for orderby_num() between %d and %d', $orderby_query, $start_count + 1, $list_count + $start_count); - } - else { - if (count ($output->groups)) { - $orderby_query = sprintf ('%s having groupby_num() between %d'. ' and %d', $orderby_query, $start_count + 1, $list_count + $start_count); - } - else { - if ($condition) { - $orderby_query = sprintf ('%s and inst_num() between %d'. ' and %d', $orderby_query, $start_count + 1, $list_count + $start_count); - } - else { - $orderby_query = sprintf ('%s where inst_num() between %d'. ' and %d', $orderby_query, $start_count + 1, $list_count + $start_count); - } - } - } - } - else { - if ($output->order) { - foreach ($output->order as $val) { - if (strpos ($val[0], '.')) { - $tmpval = explode ('.', $val[0]); - $tmpval[0] = sprintf ('"%s"', $tmpval[0]); - $tmpval[1] = sprintf ('"%s"', $tmpval[1]); - $val[0] = implode ('.', $tmpval); - } - elseif (strpos ($val[0], '(')) $val[0] = $val[0]; - elseif ($val[0] == 'count') $val[0] = 'count (*)'; - else $val[0] = sprintf ('"%s"', $val[0]); - $index_list[] = sprintf('%s %s', $val[0], $val[1]); - - if(count($output->arg_columns) && $column_list[$val]) $output->arg_columns[] = $column_list[$key]; - } - - if (count ($index_list)) { - $orderby_query = ' order by '.implode(',', $index_list); - } - } - } - - - if(count($output->arg_columns)) - { - $columns = array(); - foreach($output->arg_columns as $col){ - if(strpos($col,'"')===false && strpos($col,' ')===false) $columns[] = '"'.$col.'"'; - else $columns[] = $col; - } - - $columns = join(',',$columns); - } - - $query = sprintf ("select %s from %s %s %s %s", $columns, implode (',',$table_list), implode (' ',$left_join), $condition, $groupby_query.$orderby_query); - $query .= (__DEBUG_QUERY__&1 && $output->query_id)?sprintf (' '.$this->comment_syntax, $this->query_id):''; - $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 = "
\n"; - $output .= "Backtrace:
\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 .= "
\n"; - $output .= "file: ".$bt['line']." - ". $bt['file']."
\n"; - $output .= "call: ".$bt['class']. $bt['type'].$bt['function'].$args."
\n"; - } - $output .= "
\n"; - return $output; - } - - /** - * @brief query xml에 navigation 정보가 있을 경우 페이징 관련 작업을 처리한다 - * - * 그닥 좋지는 않은 구조이지만 편리하다.. -_-; - **/ - function _getNavigationData ($table_list, $columns, $left_join, $condition, $output) { - require_once (_XE_PATH_.'classes/page/PageHandler.class.php'); - - $column_list = $output->column_list; - - $count_condition = count($output->groups) ? sprintf('%s group by %s', $condition, implode(', ', $output->groups)) : $condition; - $count_query = sprintf('select count(*) as "count" from %s %s %s', implode(', ', $table_list), implode(' ', $left_join), $count_condition); - if (count($output->groups)) { - $count_query = sprintf('select count(*) as "count" from (%s) xet', $count_query); - } - - $count_query .= (__DEBUG_QUERY__&1 && $output->query_id)?sprintf (' '.$this->comment_syntax, $this->query_id):''; - $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; - - if ($output->order) { - $conditions = $this->getConditionList($output); - //if(in_array('list_order', $conditions) || in_array('update_order', $conditions)) { - foreach ($output->order as $key => $val) { - $col = $val[0]; - if(!in_array($col, array('list_order','update_order'))) continue; - if($condition) $condition .= sprintf(' and %s < 2100000000 ', $col); - else $condition = sprintf(' where %s < 2100000000 ', $col); - } - //} - } - - - if (count ($output->groups)) { - foreach ($output->groups as $key => $value) { - if (strpos ($value, '.')) { - $tmp = explode ('.', $value); - $tmp[0] = sprintf ('"%s"', $tmp[0]); - $tmp[1] = sprintf ('"%s"', $tmp[1]); - $value = implode ('.', $tmp); - } - elseif (strpos ($value, '(')) $value = $value; - else $value = sprintf ('"%s"', $value); - $output->groups[$key] = $value; - } - - $groupby_query = sprintf (' group by %s', implode (',', $output->groups)); - } - - if ($output->order) { - foreach ($output->order as $val) { - if (strpos ($val[0], '.')) { - $tmpval = explode ('.', $val[0]); - $tmpval[0] = sprintf ('"%s"', $tmpval[0]); - $tmpval[1] = sprintf ('"%s"', $tmpval[1]); - $val[0] = implode ('.', $tmpval); - } - elseif (strpos ($val[0], '(')) $val[0] = $val[0]; - elseif ($val[0] == 'count') $val[0] = 'count (*)'; - else $val[0] = sprintf ('"%s"', $val[0]); - $index_list[] = sprintf ('%s %s', $val[0], $val[1]); - } - - if (count ($index_list)) { - $orderby_query = ' order by '.implode(',', $index_list); - } - - $orderby_query = sprintf ('%s for orderby_num() between %d and %d', $orderby_query, $start_count + 1, $list_count + $start_count); - } - else { - if (count($output->groups)) { - $orderby_query = sprintf ('%s having groupby_num() between %d and %d', $orderby_query, $start_count + 1, $list_count + $start_count); - } - else { - if ($condition) { - $orderby_query = sprintf ('%s and inst_num() between %d and %d', $orderby_query, $start_count + 1, $list_count + $start_count); - } - else { - $orderby_query = sprintf('%s where inst_num() between %d and %d', $orderby_query, $start_count + 1, $list_count + $start_count); - } - } - } - - if(count($output->arg_columns)) - { - $columns = array(); - foreach($output->arg_columns as $col){ - if(strpos($col,'"')===false) $columns[] = '"'.$col.'"'; - else $columns[] = $col; - } - - $columns = join(',',$columns); - } - - $query = sprintf ("select %s from %s %s %s %s", $columns, implode (',',$table_list), implode (' ',$left_join), $condition, $groupby_query.$orderby_query); - $query .= (__DEBUG_QUERY__&1 && $output->query_id)?sprintf (' '.$this->comment_syntax, $this->query_id):''; - $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)) { - if ($tmp) { - foreach ($tmp as $k => $v) { - $tmp->{$k} = rtrim($v); - } - } - $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; - } - } - -return new DBCubrid; -?> + 'numeric(20)', + 'number' => 'integer', + 'varchar' => 'character varying', + 'char' => 'character', + 'tinytext' => 'character varying(256)', + 'text' => 'character varying(1073741823)', + 'bigtext' => 'character varying(1073741823)', + 'date' => 'character varying(14)', + 'float' => 'float', + ); + + /** + * @brief constructor + **/ + function DBCubrid() + { + $this->_setDBInfo(); + $this->_connect(); + } + + /** + * @brief create an instance of this class + */ + function create() + { + return new DBCubrid; + } + + /** + * @brief Return if installable + **/ + function isSupported() + { + if (!function_exists('cubrid_connect')) return false; + return true; + } + + /** + * @brief DB Connection + **/ + function __connect($connection) + { + // attempts to connect + $result = @cubrid_connect($connection["db_hostname"], $connection["db_port"], $connection["db_database"], $connection["db_userid"], $connection["db_password"]); + + // check connections + if (!$result) { + $this->setError (-1, 'database connect fail'); + return; + } + return $result; + } + + /** + * @brief DB disconnect + **/ + function _close($connection) + { + @cubrid_commit ($connection); + @cubrid_disconnect ($connection); + $this->transaction_started = false; + } + + /** + * @brief handles quatation of the string variables from the query + **/ + function addQuotes($string) + { + if (version_compare (PHP_VERSION, "5.9.0", "<") && + get_magic_quotes_gpc ()) { + $string = stripslashes (str_replace ("\\","\\\\", $string)); + } + + if (!is_numeric ($string)) { + /* + if ($this->isConnected()) { + $string = cubrid_real_escape_string($string); + } + else { + $string = str_replace("'","\'",$string); + } + */ + + $string = str_replace("'","''",$string); + } + + return $string; + } + + /** + * @brief Begin transaction + **/ + function _begin() + { + return true; + } + + /** + * @brief Rollback + **/ + function _rollback() + { + $connection = $this->_getConnection('master'); + @cubrid_rollback ($connection); + return true; + } + + /** + * @brief Commit + **/ + function _commit() + { + $connection = $this->_getConnection('master'); + @cubrid_commit($connection); + return true; + } + + /** + * @brief : executing the query and fetching the result + * + * query: run a query and return the result\n + * fetch: NULL if no value returned \n + * array object if rows returned \n + * object if a row returned \n + * return\n + **/ + function __query($query, $connection) + { + // Execute the query + $result = @cubrid_execute ($connection, $query); + // error check + if (cubrid_error_code ()) { + $code = cubrid_error_code (); + $msg = cubrid_error_msg (); + + $this->setError ($code, $msg); + } + // Return the result + return $result; + } + + /** + * @brief Fetch the result + **/ + function _fetch($result, $arrayIndexEndValue = NULL) + { + if (!$this->isConnected() || $this->isError() || !$result) return; + + // TODO Improve this piece of code + // This code trims values from char type columns + $col_types = cubrid_column_types ($result); + $col_names = cubrid_column_names ($result); + $max = count ($col_types); + + for ($count = 0; $count < $max; $count++) { + if (preg_match ("/^char/", $col_types[$count]) > 0) { + $char_type_fields[] = $col_names[$count]; + } + } + + while ($tmp = cubrid_fetch ($result, CUBRID_OBJECT)) { + if (is_array ($char_type_fields)) { + foreach ($char_type_fields as $val) { + $tmp->{$val} = rtrim ($tmp->{$val}); + } + } + + if($arrayIndexEndValue) $output[$arrayIndexEndValue--] = $tmp; + else $output[] = $tmp; + } + + unset ($char_type_fields); + + if ($result) cubrid_close_request($result); + + if(count($output)==1){ + // If call is made for pagination, always return array + if(isset($arrayIndexEndValue)) return $output; + // Else return object instead of array + else return $output[0]; + } + return $output; + } + + /** + * @brief return the sequence value incremented by 1(auto_increment column only used in the CUBRID sequence table) + **/ + function getNextSequence() + { + $this->_makeSequence(); + + $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 if the table already exists + **/ + function _makeSequence() + { + if($_GLOBALS['XE_EXISTS_SEQUENCE']) return; + + // check cubrid serial + $query = sprintf('select count(*) as "count" from "db_serial" where name=\'%ssequence\'', $this->prefix); + $result = $this->_query($query); + $output = $this->_fetch($result); + + // if do not create serial + if ($output->count == 0) { + $query = sprintf('select max("a"."srl") as "srl" from '. + '( select max("document_srl") as "srl" from '. + '"%sdocuments" UNION '. + 'select max("comment_srl") as "srl" from '. + '"%scomments" UNION '. + 'select max("member_srl") as "srl" from '. + '"%smember"'. + ') as "a"', $this->prefix, $this->prefix, $this->prefix); + + $result = $this->_query($query); + $output = $this->_fetch($result); + $srl = $output->srl; + if ($srl < 1) { + $start = 1; + } + else { + $start = $srl + 1000000; + } + + // create sequence + $query = sprintf('create serial "%ssequence" start with %s increment by 1 minvalue 1 maxvalue 10000000000000000000000000000000000000 nocycle;', $this->prefix, $start); + $this->_query($query); + } + + $_GLOBALS['XE_EXISTS_SEQUENCE'] = true; + } + + + /** + * brief return a table if exists + **/ + function isTableExists ($target_name) + { + if($target_name == 'sequence') { + $query = sprintf ("select \"name\" from \"db_serial\" where \"name\" = '%s%s'", $this->prefix, $target_name); + } + else { + $query = sprintf ("select \"class_name\" 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 add a column to the table + **/ + function addColumn($table_name, $column_name, $type = 'number', $size = '', $default = '', $notnull = false) + { + $type = strtoupper($this->column_type[$type]); + if ($type == 'INTEGER') $size = ''; + + $query = sprintf ("alter class \"%s%s\" add \"%s\" ", $this->prefix, $table_name, $column_name); + + if ($type == 'char' || $type == 'varchar') { + if ($size) $size = $size * 3; + } + + if ($size) { + $query .= sprintf ("%s(%s) ", $type, $size); + } + else { + $query .= sprintf ("%s ", $type); + } + + if ($default) { + if ($type == 'INTEGER' || $type == 'BIGINT' || $type=='INT') { + $query .= sprintf ("default %d ", $default); + } + else { + $query .= sprintf ("default '%s' ", $default); + } + } + + if ($notnull) $query .= "not null "; + + $this->_query ($query); + } + + /** + * @brief drop a column from the table + **/ + function dropColumn ($table_name, $column_name) + { + $query = sprintf ("alter class \"%s%s\" drop \"%s\" ", $this->prefix, $table_name, $column_name); + + $this->_query ($query); + } + + /** + * @brief return column information of the table + **/ + function isColumnExists ($table_name, $column_name) + { + $query = sprintf ("select \"attr_name\" 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 add an index to the table + * $target_columns = array(col1, col2) + * $is_unique? unique : none + **/ + function addIndex ($table_name, $index_name, $target_columns, $is_unique = false) + { + if (!is_array ($target_columns)) { + $target_columns = array ($target_columns); + } + + $query = sprintf ("create %s index \"%s\" on \"%s%s\" (%s);", $is_unique?'unique':'', $this->prefix .$index_name, $this->prefix, $table_name, '"'.implode('","',$target_columns).'"'); + + $this->_query ($query); + } + + /** + * @brief drop an index from the table + **/ + function dropIndex ($table_name, $index_name, $is_unique = false) + { + $query = sprintf ("drop %s index \"%s\" on \"%s%s\"", $is_unique?'unique':'', $this->prefix .$index_name, $this->prefix, $table_name); + + $this->_query($query); + } + + /** + * @brief return index information of the table + **/ + function isIndexExists ($table_name, $index_name) + { + $query = sprintf ("select \"index_name\" from \"db_index\" where ". "\"class_name\" = '%s%s' and \"index_name\" = '%s' ", $this->prefix, $table_name, $this->prefix .$index_name); + $result = $this->_query ($query); + + if ($this->isError ()) return false; + + $output = $this->_fetch ($result); + + if (!$output) return false; + return true; + } + + /** + * @brief creates a table by using xml file + **/ + function createTableByXml ($xml_doc) + { + return $this->_createTable ($xml_doc); + } + + /** + * @brief creates a table by using xml file + **/ + function createTableByXmlFile ($file_name) + { + if (!file_exists ($file_name)) return; + // read xml file + $buff = FileHandler::readFile ($file_name); + return $this->_createTable ($buff); + } + + /** + * @brief create table by using the schema xml + * + * type : number, varchar, tinytext, text, bigtext, 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); + // Create a table schema + $table_name = $xml_obj->table->attrs->name; + + // if the table already exists exit function + if ($this->isTableExists($table_name)) return; + + // If the table name is sequence, it creates a 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); + } + + + $table_name = $this->prefix.$table_name; + + $query = sprintf ('create class "%s";', $table_name); + $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 (isset ($default) && ($type == 'varchar' || $type == 'char' || + $type == 'text' || $type == 'tinytext' || $type == 'bigtext')) { + $default = sprintf ("'%s'", $default); + } + + if ($type == 'varchar' || $type == 'char') { + if($size) $size = $size * 3; + } + + + $column_schema[] = sprintf ('"%s" %s%s %s %s', + $name, + $this->column_type[$type], + $size?'('.$size.')':'', + isset($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\" on \"%s\" ". "(%s);", $this->prefix .$key, $table_name, '"'.implode('","', $val).'"'); + $this->_query ($query); + } + } + + if (count ($index_list)) { + foreach ($index_list as $key => $val) { + $query = sprintf ("create index \"%s\" on \"%s\" (%s);", $this->prefix .$key, $table_name, '"'.implode('","',$val).'"'); + $this->_query ($query); + } + } + } + + + + + /** + * @brief handles insertAct + **/ + function _executeInsertAct($queryObject) + { + $query = $this->getInsertSql($queryObject); + if(is_a($query, 'Object')) return; + + $query .= (__DEBUG_QUERY__&1 && $output->query_id)?sprintf (' '.$this->comment_syntax, $this->query_id):''; + + $result = $this->_query ($query); + if ($result && !$this->transaction_started) { + $this->_commit(); + } + + return $result; + } + + /** + * @brief handles updateAct + **/ + function _executeUpdateAct($queryObject) + { + $query = $this->getUpdateSql($queryObject); + if(is_a($query, 'Object')) return; + + $result = $this->_query($query); + + if ($result && !$this->transaction_started) $this->_commit(); + + return $result; + } + + + /** + * @brief handles deleteAct + **/ + function _executeDeleteAct($queryObject) + { + $query = $this->getDeleteSql($queryObject); + if(is_a($query, 'Object')) return; + + $result = $this->_query ($query); + + if ($result && !$this->transaction_started) $this->_commit(); + + return $result; + } + + /** + * @brief Handle selectAct + * + * to get a specific page list easily in select statement,\n + * a method, navigation, is used + **/ + function _executeSelectAct($queryObject, $connection = null){ + $limit = $queryObject->getLimit(); + if ($limit && $limit->isPageHandler()) + return $this->queryPageLimit($queryObject, $result, $connection); + else { + $query = $this->getSelectSql($queryObject); + if(is_a($query, 'Object')) return; + + $query .= (__DEBUG_QUERY__&1 && $queryObject->query_id)?sprintf (' '.$this->comment_syntax, $this->query_id):''; + $result = $this->_query ($query, $connection); + + if ($this->isError ()) + return $this->queryError($queryObject); + + $data = $this->_fetch($result); + $buff = new Object (); + $buff->data = $data; + return $buff; + } + } + + function queryError($queryObject){ + $limit = $queryObject->getLimit(); + if ($limit && $limit->isPageHandler()){ + $buff = new Object (); + $buff->total_count = 0; + $buff->total_page = 0; + $buff->page = 1; + $buff->data = array (); + $buff->page_navigation = new PageHandler (/*$total_count*/0, /*$total_page*/1, /*$page*/1, /*$page_count*/10);//default page handler values + return $buff; + }else + return; + } + + function queryPageLimit($queryObject, $result, $connection){ + $limit = $queryObject->getLimit(); + // Total count + $temp_where = $queryObject->getWhereString(true, false); + $count_query = sprintf('select count(*) as "count" %s %s', 'FROM ' . $queryObject->getFromString(), ($temp_where === '' ? '' : ' WHERE '. $temp_where)); + if ($queryObject->getGroupByString() != '') { + $count_query = sprintf('select count(*) as "count" from (%s) xet', $count_query); + } + + $count_query .= (__DEBUG_QUERY__&1 && $queryObject->query_id)?sprintf (' '.$this->comment_syntax, $this->query_id):''; + $result = $this->_query($count_query, $connection); + $count_output = $this->_fetch($result); + $total_count = (int)$count_output->count; + + $list_count = $limit->list_count->getValue(); + if (!$list_count) $list_count = 20; + $page_count = $limit->page_count->getValue(); + if (!$page_count) $page_count = 10; + $page = $limit->page->getValue(); + if (!$page) $page = 1; + + // total pages + if ($total_count) { + $total_page = (int) (($total_count - 1) / $list_count) + 1; + } + else { + $total_page = 1; + } + + // check the page variables + if ($page > $total_page) $page = $total_page; + $start_count = ($page - 1) * $list_count; + + $query = $this->getSelectPageSql($queryObject, true, $start_count, $list_count); + $query .= (__DEBUG_QUERY__&1 && $queryObject->query_id)?sprintf (' '.$this->comment_syntax, $this->query_id):''; + $result = $this->_query ($query, $connection); + if ($this->isError ()) + return $this->queryError($queryObject); + + $virtual_no = $total_count - ($page - 1) * $list_count; + $data = $this->_fetch($result, $virtual_no); + + $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; + } + + function getParser(){ + return new DBParser('"', '"', $this->prefix); + } + + function getSelectPageSql($query, $with_values = true, $start_count = 0, $list_count = 0) { + + $select = $query->getSelectString($with_values); + if($select == '') return new Object(-1, "Invalid query"); + $select = 'SELECT ' .$select; + + $from = $query->getFromString($with_values); + if($from == '') return new Object(-1, "Invalid query"); + $from = ' FROM '.$from; + + $where = $query->getWhereString($with_values); + if($where != '') + $where = ' WHERE ' . $where; + + $groupBy = $query->getGroupByString(); + if($groupBy != '') $groupBy = ' GROUP BY ' . $groupBy; + + $orderBy = $query->getOrderByString(); + if($orderBy != '') $orderBy = ' ORDER BY ' . $orderBy; + + $limit = $query->getLimitString(); + if ($limit != '') $limit = sprintf (' LIMIT %d, %d', $start_count, $list_count); + + return $select . ' ' . $from . ' ' . $where . ' ' . $groupBy . ' ' . $orderBy . ' ' . $limit; + } +} + + return new DBCubrid; +?> diff --git a/classes/db/DBFirebird.class.php b/classes/db/DBFirebird.class.php index e7ee592fe..8327cdcb6 100644 --- a/classes/db/DBFirebird.class.php +++ b/classes/db/DBFirebird.class.php @@ -1,8 +1,8 @@ 'BIGINT', @@ -45,7 +41,7 @@ $this->_setDBInfo(); $this->_connect(); } - + /** * @brief create an instance of this class */ @@ -55,7 +51,7 @@ } /** - * @brief 설치 가능 여부를 return + * @brief Return if installable **/ function isSupported() { if(!function_exists('ibase_connect')) return false; @@ -63,48 +59,28 @@ } /** - * @brief DB정보 설정 및 connect/ close + * @brief DB Connection **/ - function _setDBInfo() { - $db_info = Context::getDBInfo(); - $this->hostname = $db_info->db_hostname; - $this->port = $db_info->db_port; - $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->port || !$this->userid || !$this->password || !$this->database) return; - + function __connect($connection) { //if(strpos($this->hostname, ':')===false && $this->port) $this->hostname .= ':'.$this->port; + // attempts to connect + $host = $connection["db_hostname"]."/".$connection["db_port"].":".$connection["db_database"]; - // 접속시도 - - $host = $this->hostname."/".$this->port.":".$this->database; - - $this->fd = @ibase_connect($host, $this->userid, $this->password); + $result = ibase_connect($host, $connection["db_userid"], $connection["db_password"]); if(ibase_errmsg()) { $this->setError(ibase_errcode(), ibase_errmsg()); - return $this->is_connected = false; + return; } - - // Firebird 버전 확인후 2.0 이하면 오류 표시 - if (($service = ibase_service_attach($this->hostname, $this->userid, $this->password)) != FALSE) { + // Error when Firebird version is lower than 2.0 + if (($service = ibase_service_attach($connection["db_hostname"], $connection["db_userid"], $connection["db_password"])) != FALSE) { // get server version and implementation strings $server_info = ibase_server_info($service, IBASE_SVC_SERVER_VERSION); ibase_service_detach($service); } else { $this->setError(ibase_errcode(), ibase_errmsg()); - @ibase_close($this->fd); - return $this->is_connected = false; + ibase_close($result); + return; } $pos = strpos($server_info, "Firebird"); @@ -115,36 +91,31 @@ if($ver < "2.0") { $this->setError(-1, "XE cannot be installed under the version of firebird 2.0. Current firebird version is ".$ver); - @ibase_close($this->fd); - return $this->is_connected = false; + ibase_close($result); + return; } - - // 접속체크 - $this->is_connected = true; - $this->password = md5($this->password); + return $result; } /** - * @brief DB접속 해제 + * @brief DB disconnect **/ - function close() { - if(!$this->isConnected()) return; - @ibase_commit($this->fd); - @ibase_close($this->fd); - $this->transaction_started = false; + function _close($connection) { + ibase_commit($connection); + ibase_close($connection); } /** - * @brief 쿼리에서 입력되는 문자열 변수들의 quotation 조절 + * @brief handles quatation of the string variables from the query **/ function addQuotes($string) { -// if(get_magic_quotes_gpc()) $string = stripslashes(str_replace("\\","\\\\",$string)); -// if(!is_numeric($string)) $string = str_replace("'","''", $string); + if(get_magic_quotes_gpc()) $string = stripslashes(str_replace("\\","\\\\",$string)); + if(!is_numeric($string)) $string = str_replace("'","''", $string); return $string; } /** - * @brief 쿼리에서 입력되는 table, column 명에 더블쿼터를 넣어줌 + * @brief put double quotes for tabls, column names in the query statement **/ function addDoubleQuotes($string) { if($string == "*") return $string; @@ -162,12 +133,11 @@ } /** - * @brief 쿼리에서 입력되는 table, column 명에 더블쿼터를 넣어줌 + * @brief put double quotes for tabls, column names in the query statement **/ function autoQuotes($string){ $string = strtolower($string); - - // substr 함수 일경우 + // for substr function if(strpos($string, "substr(") !== false) { $tokken = strtok($string, "(,)"); $tokken = strtok("(,)"); @@ -190,8 +160,7 @@ $as = trim($as); $as = $this->addDoubleQuotes($as); } - - // 함수 사용시 + // for functions $tmpFunc1 = null; $tmpFunc2 = null; if(($no1 = strpos($string,'('))!==false && ($no2 = strpos($string, ')'))!==false) { @@ -199,8 +168,7 @@ $tmpFunc2 = substr($string, $no2, strlen($string)-$no2+1); $string = trim(substr($string, $no1+1, $no2-$no1-1)); } - - // (테이블.컬럼) 구조 일때 처리 + // for (table.column) structure preg_match("/((?i)[a-z0-9_-]+)[.]((?i)[a-z0-9_\-\*]+)/", $string, $matches); if($matches) { @@ -225,7 +193,7 @@ } foreach($values as $val1) { - // (테이블.컬럼) 구조 일때 처리 + // for (table.column) structure preg_match("/((?i)[a-z0-9_-]+)[.]((?i)[a-z0-9_\-\*]+)/", $val1, $matches); if($matches) { $isTable = false; @@ -257,87 +225,111 @@ } /** - * @brief 트랜잭션 시작 + * @brief Begin transaction **/ - function begin() { - if(!$this->isConnected() || $this->transaction_started) return; - $this->transaction_started = true; + function _begin() { + return true; } /** - * @brief 롤백 + * @brief Rollback **/ - function rollback() { - if(!$this->isConnected() || !$this->transaction_started) return; - @ibase_rollback($this->fd); - $this->transaction_started = false; + function _rollback() { + $connection = $this->_getConnection('master'); + ibase_rollback($connection); + return true; } /** - * @brief 커밋 + * @brief Commits **/ - function commit() { - if(!$force && (!$this->isConnected() || !$this->transaction_started)) return; - @ibase_commit($this->fd); - $this->transaction_started = false; + function _commit() { + $connection = $this->_getConnection('master'); + ibase_commit($connection); + return true; } /** - * @brief : 쿼리문의 실행 및 결과의 fetch 처리 + * @brief : Run a query and fetch the result * - * query : query문 실행하고 result return\n - * fetch : reutrn 된 값이 없으면 NULL\n - * rows이면 array object\n - * row이면 object\n - * return\n + * query: run a query and return the result\n + * fetch: NULL if no value returned \n + * array object if rows returned \n + * object if a row returned \n + * return\n **/ - function _query($query, $params=null) { - if(!$this->isConnected()) return; - + function __query($query, $connection, $params = null) { if(count($params) == 0) { - // 쿼리 시작을 알림 - $this->actStart($query); - - // 쿼리 문 실행 - $result = ibase_query($this->fd, $query); + // Execute the query statement + $result = ibase_query($connection, $query); } else { - // 쿼리 시작을 알림 - $log = $query."\n\t\t\t"; - $log .= implode(",", $params); - $this->actStart($log); - - // 쿼리 문 실행 (blob type 입력하기 위한 방법) - $query = ibase_prepare($this->fd, $query); - $fnarr = array_merge(array($query), $params); - $result = call_user_func_array("ibase_execute", $fnarr); + // Execute the query(for blob type) + $query = ibase_prepare($connection, $query); + //$fnarr = array_merge(array($query), $params); + $result = ibase_execute($query); } - - // 오류 체크 + // Error Check if(ibase_errmsg()) $this->setError(ibase_errcode(), ibase_errmsg()); - // 쿼리 실행 종료를 알림 - $this->actFinish(); - - // 결과 리턴 return $result; } + function _queryInsertUpdateDeleteSelect($query, $params=null, $connection) { + if(!$connection) return; + + if(count($params) == 0) { + // Notify to start a query execution + $this->actStart($query); + // Execute the query statement + $trans = ibase_trans(IBASE_DEFAULT,$connection); + $result = ibase_query($trans, $query); + ibase_commit($trans); + unset($trans); + } + else { + // Notify to start a query execution + $log = $query."\n\t\t\t"; + $log .= implode(",", $params); + $this->actStart($log); + // Execute the query(for blob type) + $query = ibase_prepare($connection, $query); + //$fnarr = array_merge(array($query), $params); + $result = ibase_execute($query); + } + // Error Check + if(ibase_errmsg()) $this->setError(ibase_errcode(), ibase_errmsg()); + // Notify to complete a query execution + $this->actFinish(); + // Return the result + return $result; + } + + function getTableInfo($result){ + $coln = ibase_num_fields($result); + $column_type = array(); + for ($i = 0; $i < $coln; $i++) { + $col_info = ibase_field_info($result, $i); + if($col_info['name'] === "") $column_type[$col_info['alias']] = $col_info['type']; + else $column_type[$col_info['name']] = $col_info['type']; + } + return $column_type; + } /** - * @brief 결과를 fetch + * @brief Fetch the result **/ function _fetch($result, $output = null) { if(!$this->isConnected() || $this->isError() || !$result) return; + $output->column_type = $this->getTableInfo($result); while($tmp = ibase_fetch_object($result)) { foreach($tmp as $key => $val) { $type = $output->column_type[$key]; - - // type 값이 null 일때는 $key값이 alias인 경우라 실제 column 이름을 찾아 type을 구함 + // type value is null when $key is an alias. so get a type by finding actual coloumn name if($type == null && $output->columns && count($output->columns)) { foreach($output->columns as $cols) { if($cols['alias'] == $key) { - // table.column 형식인지 정규식으로 검사 함 + // checks if the format is table.column or a regular expression preg_match("/\w+[.](\w+)/", $cols['name'], $matches); if($matches) { $type = $output->column_type[$matches[1]]; @@ -349,14 +341,26 @@ } } - if(($type == "text" || $type == "bigtext") && $tmp->{$key}) { + if(($type == "text" || $type == "bigtext" || $type == "BLOB") && $tmp->{$key}) { $blob_data = ibase_blob_info($tmp->{$key}); $blob_hndl = ibase_blob_open($tmp->{$key}); - $tmp->{$key} = ibase_blob_get($blob_hndl, $blob_data[0]); - ibase_blob_close($blob_hndl); + + if($blob_data[1] === 1) { + $tmp->{$key} = ibase_blob_get($blob_hndl, $blob_data[0]); + } else { + for ($i = 0; $i < $blob_data[1]; $i++) { + $readsize = $blob_data[2]; + if ($i == ($blob_data[1] - 1)) { + $readsize = $blob_data[0] - (($blob_data[1] - 1) * $blob_data[2]); + } + $totalimage .= ibase_blob_get($blob_hndl, $readsize); + } + } + + ibase_blob_close($blob_hndl); } - else if($type == "char") { - $tmp->{$key} = trim($tmp->{$key}); // DB의 character set이 UTF8일때 생기는 빈칸을 제거 + else if($type == "CHAR") { + $tmp->{$key} = trim($tmp->{$key}); // remove blanks generated when DB character set is UTF8 } } @@ -368,31 +372,33 @@ } /** - * @brief 1씩 증가되는 sequence값을 return (firebird의 generator 값을 증가) + * @brief return sequence value incremented by 1(increase the value of the generator in firebird) **/ function getNextSequence() { - $gen = "GEN_".$this->prefix."sequence_ID"; + //$gen = "GEN_".$this->prefix."sequence_ID"; + $gen = 'GEN_XE_SEQUENCE_ID'; $sequence = ibase_gen_id($gen, 1); return $sequence; } /** - * @brief 테이블 기생성 여부 return + * @brief returns if the table already exists **/ function isTableExists($target_name) { $query = sprintf("select rdb\$relation_name from rdb\$relations where rdb\$system_flag=0 and rdb\$relation_name = '%s%s';", $this->prefix, $target_name); $result = $this->_query($query); $tmp = $this->_fetch($result); + $connection = $this->_getConnection('master'); if(!$tmp) { - if(!$this->transaction_started) @ibase_rollback($this->fd); + if(!$this->transaction_started) ibase_rollback($connection); return false; } - if(!$this->transaction_started) @ibase_commit($this->fd); + if(!$this->transaction_started) ibase_commit($connection); return true; } /** - * @brief 특정 테이블에 특정 column 추가 + * @brief add a column to the table **/ function addColumn($table_name, $column_name, $type='number', $size='', $default = '', $notnull=false) { $type = $this->column_type[$type]; @@ -408,32 +414,42 @@ if($notnull) $query .= " NOT NULL "; $this->_query($query); - if(!$this->transaction_started) @ibase_commit($this->fd); + + if(!$this->transaction_started) { + $connection = $this->_getConnection('master'); + ibase_commit($connection); + } } /** - * @brief 특정 테이블에 특정 column 제거 + * @brief drop a column from the table **/ function dropColumn($table_name, $column_name) { $query = sprintf("alter table %s%s drop %s ", $this->prefix, $table_name, $column_name); $this->_query($query); - if(!$this->transaction_started) @ibase_commit($this->fd); + if(!$this->transaction_started) { + $connection = $this->_getConnection('master'); + ibase_commit($connection); + } + } /** - * @brief 특정 테이블의 column의 정보를 return + * @brief return column information of the table **/ function isColumnExists($table_name, $column_name) { $query = sprintf("SELECT RDB\$FIELD_NAME as \"FIELD\" FROM RDB\$RELATION_FIELDS WHERE RDB\$RELATION_NAME = '%s%s'", $this->prefix, $table_name); $result = $this->_query($query); + $connection = $this->_getConnection('master'); + if($this->isError()) { - if(!$this->transaction_started) @ibase_rollback($this->fd); + if(!$this->transaction_started) ibase_rollback($connection); return false; } $output = $this->_fetch($result); - if(!$this->transaction_started) @ibase_commit($this->fd); + if(!$this->transaction_started) ibase_commit($connection); if($output) { $column_name = strtolower($column_name); @@ -446,36 +462,37 @@ } /** - * @brief 특정 테이블에 특정 인덱스 추가 + * @brief add an index to the table * $target_columns = array(col1, col2) * $is_unique? unique : none **/ function addIndex($table_name, $index_name, $target_columns, $is_unique = false) { - // index name 크기가 31byte로 제한으로 index name을 넣지 않음 - // Firebird에서는 index name을 넣지 않으면 "RDB$10"처럼 자동으로 이름을 부여함 - // table을 삭제 할 경우 인덱스도 자동으로 삭제 됨 - + // index name size should be limited to 31 byte. no index name assigned + // if index name omitted, Firebird automatically assign its name like "RDB $10" + // deletes indexes when deleting the table if(!is_array($target_columns)) $target_columns = array($target_columns); $query = sprintf('CREATE %s INDEX "" ON "%s%s" ("%s");', $is_unique?'UNIQUE':'', $this->prefix, $table_name, implode('", "',$target_columns)); $this->_query($query); - if(!$this->transaction_started) @ibase_commit($this->fd); + $connection = $this->_getConnection('master'); + if(!$this->transaction_started) ibase_commit($connection); } /** - * @brief 특정 테이블의 특정 인덱스 삭제 + * @brief drop an index from the table **/ function dropIndex($table_name, $index_name, $is_unique = false) { $query = sprintf('DROP INDEX "%s" ON "%s%s"', $index_name, $this->prefix, $table_name); $this->_query($query); - if(!$this->transaction_started) @ibase_commit($this->fd); + $connection = $this->_getConnection('master'); + if(!$this->transaction_started) ibase_commit($connection); } /** - * @brief 특정 테이블의 index 정보를 return + * @brief return index information of the table **/ function isIndexExists($table_name, $index_name) { $query = "SELECT\n"; @@ -497,11 +514,15 @@ $output = $this->_fetch($result); if(!$output) { - if(!$this->transaction_started) @ibase_rollback($this->fd); + $connection = $this->_getConnection('master'); + if(!$this->transaction_started) ibase_rollback($connection); return false; } - if(!$this->transaction_started) @ibase_commit($this->fd); + if(!$this->transaction_started) { + $connection = $this->_getConnection('master'); + ibase_commit($connection); + } if(!is_array($output)) $output = array($output); for($i=0;$i_createTable($xml_doc); } /** - * @brief xml 을 받아서 테이블을 생성 + * @brief creates a table by using xml file **/ function createTableByXmlFile($file_name) { if(!file_exists($file_name)) return; - // xml 파일을 읽음 + // read xml file $buff = FileHandler::readFile($file_name); return $this->_createTable($buff); } /** - * @brief schema xml을 이용하여 create table query생성 + * @brief create table by using the schema xml * * type : number, varchar, text, char, date, \n * opt : notnull, default, size\n @@ -539,8 +560,7 @@ // xml parsing $oXml = new XmlParser(); $xml_obj = $oXml->parse($xml_doc); - - // 테이블 생성 schema 작성 + // Create a table schema $table_name = $xml_obj->table->attrs->name; if($this->isTableExists($table_name)) return; $table_name = $this->prefix.$table_name; @@ -591,34 +611,48 @@ $schema = sprintf("CREATE TABLE \"%s\" (%s%s); \n", $table_name, "\n", implode($column_schema, ",\n")); $output = $this->_query($schema); - if(!$this->transaction_started) @ibase_commit($this->fd); + if(!$this->transaction_started) { + $connection = $this->_getConnection('master'); + ibase_commit($connection); + } if(!$output) return false; if(count($index_list)) { foreach($index_list as $key => $val) { - // index name 크기가 31byte로 제한으로 index name을 넣지 않음 - // Firebird에서는 index name을 넣지 않으면 "RDB$10"처럼 자동으로 이름을 부여함 - // table을 삭제 할 경우 인덱스도 자동으로 삭제 됨 - + // index name size should be limited to 31 byte. no index name assigned + // if index name omitted, Firebird automatically assign its name like "RDB $10" + // deletes indexes when deleting the table $schema = sprintf("CREATE INDEX \"\" ON \"%s\" (\"%s\");", $table_name, implode($val, "\",\"")); $output = $this->_query($schema); - if(!$this->transaction_started) @ibase_commit($this->fd); + if(!$this->transaction_started) { + $connection = $this->_getConnection('master'); + ibase_commit($connection); + } if(!$output) return false; } } + if($_GLOBALS['XE_EXISTS_SEQUENCE']) return; + $schema = 'CREATE GENERATOR GEN_XE_SEQUENCE_ID;'; + $output = $this->_query($schema); + if(!$this->transaction_started) { + $connection = $this->_getConnection('master'); + ibase_commit($connection); + } + if(!$output) return false; + $_GLOBALS['XE_EXISTS_SEQUENCE'] = true; + /*if($auto_increment_list) foreach($auto_increment_list as $increment) { $schema = sprintf('CREATE GENERATOR GEN_%s_ID;', $table_name); $output = $this->_query($schema); - if(!$this->transaction_started) @ibase_commit($this->fd); - if(!$output) return false; - - // Firebird에서 auto increment는 generator를 만들어 insert 발생시 트리거를 실행시켜 - // generator의 값을 증가시키고 그값을 테이블에 넣어주는 방식을 사용함. - // 아래 트리거가 auto increment 역할을 하지만 쿼리로 트리거 등록이 되지 않아 주석처리 하였음. - // php 함수에서 generator 값을 증가시켜 주는 함수가 있어 XE에서는 굳이 - // auto increment를 사용 할 필요가 없어보임. + if(!$this->transaction_started) ibase_commit($this->fd); + if(!$output) return false;*/ + // auto_increment in Firebird creates a generator which activates a trigger when insert occurs + // the generator increases the value of the generator and then insert to the table + // The trigger below acts like auto_increment however I commented the below because the trigger cannot be defined by a query statement + // php api has a function to increase a generator, so + // no need to use auto increment in XE /* $schema = 'SET TERM ^ ; '; $schema .= sprintf('CREATE TRIGGER "%s_BI" FOR "%s" ', $table_name, $table_name); @@ -630,442 +664,196 @@ $output = $this->_query($schema); if(!$output) return false; */ - } + //} } + /** - * @brief 조건문 작성하여 return + * @brief Handle the insertAct **/ - function getCondition($output) { - if(!$output->conditions) return; - $condition = $this->_getCondition($output->conditions,$output->column_type,$output->_tables); - if($condition) $condition = ' where '.$condition; - return $condition; - } - - function getLeftCondition($conditions,$column_type,$tables){ - return $this->_getCondition($conditions,$column_type,$tables); - } - - - function _getCondition($conditions,$column_type,$tables) { - $condition = ''; - foreach($conditions as $val) { - $sub_condition = ''; - foreach($val['condition'] as $v) { - if(!isset($v['value'])) continue; - if($v['value'] === '') continue; - if(!in_array(gettype($v['value']), array('string', 'integer', 'double'))) continue; - - $name = $v['column']; - $operation = $v['operation']; - $value = $v['value']; - $type = $this->getColumnType($column_type,$name); - $pipe = $v['pipe']; - - $value = $this->getConditionValue('"'.$name.'"', $value, $operation, $type, $column_type); - if(!$value) $value = $v['value']; - - $name = $this->autoQuotes($name); - $value = $this->autoValueQuotes($value, $tables); - - $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.')'; - } - } - - return $condition; + function _executeInsertAct($queryObject) { + $query = $this->getInsertSql($queryObject); + if(is_a($query, 'Object')) return; + return $this->_queryInsertUpdateDeleteSelect($query); } /** - * @brief insertAct 처리 + * @brief handles updateAct **/ - function _executeInsertAct($output) { - // 테이블 정리 - foreach($output->tables as $key => $val) { - $table_list[] = '"'.$this->prefix.$val.'"'; - } - - // 컬럼 정리 - foreach($output->columns as $key => $val) { - $name = $val['name']; - $value = $val['value']; - - $value = str_replace("'", "`", $value); - - if($output->column_type[$name]=="text" || $output->column_type[$name]=="bigtext"){ - if(!isset($val['value'])) continue; - $blh = ibase_blob_create($this->fd); - ibase_blob_add($blh, $value); - $value = ibase_blob_close($blh); - } - else if($output->column_type[$name]!='number') { -// if(!$value) $value = 'null'; - } - else $this->_filterNumber(&$value); - - $column_list[] = '"'.$name.'"'; - $value_list[] = $value; - $questions[] = "?"; - } - - $query = sprintf("insert into %s (%s) values (%s);", implode(',',$table_list), implode(',',$column_list), implode(',', $questions)); - - $result = $this->_query($query, $value_list); - if(!$this->transaction_started) @ibase_commit($this->fd); - return $result; + function _executeUpdateAct($queryObject) { + $query = $this->getUpdateSql($queryObject); + if(is_a($query, 'Object')) return; + return $this->_queryInsertUpdateDeleteSelect($query); } /** - * @brief updateAct 처리 + * @brief handles deleteAct **/ - function _executeUpdateAct($output) { - // 테이블 정리 - foreach($output->tables as $key => $val) { - $table_list[] = '"'.$this->prefix.$val.'"'; - } - - // 컬럼 정리 - foreach($output->columns as $key => $val) { - if(!isset($val['value'])) continue; - $name = $val['name']; - $value = $val['value']; - - $value = str_replace("'", "`", $value); - - if(strpos($name,'.')!==false&&strpos($value,'.')!==false) $column_list[] = $name.' = '.$value; - else { - if($output->column_type[$name]=="text" || $output->column_type[$name]=="bigtext"){ - $blh = ibase_blob_create($this->fd); - ibase_blob_add($blh, $value); - $value = ibase_blob_close($blh); - } - else if($output->column_type[$name]=='number' || - $output->column_type[$name]=='bignumber' || - $output->column_type[$name]=='float') { - // 연산식이 들어갔을 경우 컬럼명이 있는 지 체크해 더블쿼터를 넣어줌 - preg_match("/(?i)[a-z][a-z0-9_]+/", $value, $matches); - - foreach($matches as $key => $val) { - $value = str_replace($val, "\"".$val."\"", $value); - } - - if($matches != null) { - $column_list[] = sprintf("\"%s\" = %s", $name, $value); - continue; - } - } - - $values[] = $value; - $column_list[] = sprintf('"%s" = ?', $name); - } - } - - // 조건절 정리 - $condition = $this->getCondition($output); - - $query = sprintf("update %s set %s %s;", implode(',',$table_list), implode(',',$column_list), $condition); - $result = $this->_query($query, $values); - if(!$this->transaction_started) @ibase_commit($this->fd); - return $result; + function _executeDeleteAct($queryObject) { + $query = $this->getDeleteSql($queryObject); + if(is_a($query, 'Object')) return; + return $this->_queryInsertUpdateDeleteSelect($query); } /** - * @brief deleteAct 처리 - **/ - function _executeDeleteAct($output) { - // 테이블 정리 - foreach($output->tables as $key => $val) { - $table_list[] = '"'.$this->prefix.$val.'"'; - } - - // 조건절 정리 - $condition = $this->getCondition($output); - - $query = sprintf("delete from %s %s;", implode(',',$table_list), $condition); - - $result = $this->_query($query); - if(!$this->transaction_started) @ibase_commit($this->fd); - return $result; - } - - /** - * @brief selectAct 처리 + * @brief Handle selectAct * - * select의 경우 특정 페이지의 목록을 가져오는 것을 편하게 하기 위해\n - * navigation이라는 method를 제공 + * In order to get a list of pages easily when selecting \n + * it supports a method as navigation **/ - function _executeSelectAct($output) { - // 테이블 정리 - $table_list = array(); - foreach($output->tables as $key => $val) { - $table_list[] = sprintf("\"%s%s\" as \"%s\"", $this->prefix, $val, $key); - } - - $left_join = array(); - // why??? - $left_tables= (array)$output->left_tables; - - foreach($left_tables as $key => $val) { - $condition = $this->getLeftCondition($output->left_conditions[$key],$output->column_type,$output->_tables); - if($condition){ - $left_join[] = $val . ' "'.$this->prefix.$output->_tables[$key].'" as "'.$key.'" on (' . $condition . ')'; - } - } - - $click_count = array(); - if(!$output->columns){ - $output->columns = array(array('name'=>'*')); + function _executeSelectAct($queryObject, $connection) { + $query = $this->getSelectSql($queryObject); + if(strpos($query, "substr")) { + $query = str_replace ("substr", "substring", $query); + $query = $this->replaceSubstrFormat($query); } + if(is_a($query, 'Object')) return; + $query .= (__DEBUG_QUERY__&1 && $queryObject->query_id)?sprintf(' '.$this->comment_syntax,$this->query_id):''; + $result = $this->_queryInsertUpdateDeleteSelect ($query, null, $connection); - $column_list = array(); - foreach($output->columns as $key => $val) { - $name = $val['name']; - $alias = $val['alias']; - if($val['click_count']) $click_count[] = $val['name']; - - if($alias == "") - $column_list[] = $this->autoQuotes($name); - else - $column_list[$alias] = sprintf("%s as \"%s\"", $this->autoQuotes($name), $alias); - } - $columns = implode(',',$column_list); - - $condition = $this->getCondition($output); - - $output->column_list = $column_list; - if($output->list_count && $output->page) return $this->_getNavigationData($table_list, $columns, $left_join, $condition, $output); - - // list_order, update_order 로 정렬시에 인덱스 사용을 위해 condition에 쿼리 추가 - if($output->order) { - $conditions = $this->getConditionList($output); - if(!in_array('list_order', $conditions) && !in_array('update_order', $conditions)) { - foreach($output->order as $key => $val) { - $col = $val[0]; - if(!in_array($col, array('list_order','update_order'))) continue; - if($condition) $condition .= sprintf(' and "%s" < 2100000000 ', $col); - else $condition = sprintf(' where "%s" < 2100000000 ', $col); - } - } - } - - // list_count를 사용할 경우 적용 - if($output->list_count['value']) $limit = sprintf('FIRST %d', $output->list_count['value']); - else $limit = ''; - - - if($output->groups) { - foreach($output->groups as $key => $val) { - $group_list[] = $this->autoQuotes($val); - if($column_list[$val]) $output->arg_columns[] = $column_list[$val]; - } - if(count($group_list)) $groupby_query = sprintf(" group by %s", implode(",",$group_list)); - } - - if($output->order) { - foreach($output->order as $key => $val) { - $index_list[] = sprintf("%s %s", $this->autoQuotes($val[0]), $val[1]); - if(count($output->arg_columns) && $column_list[$val[0]]) $output->arg_columns[] = $column_list[$val[0]]; - } - if(count($index_list)) $orderby_query = sprintf(" order by %s", implode(",",$index_list)); - } - - if(count($output->arg_columns)) - { - $columns = array(); - foreach($output->arg_columns as $col){ - if(strpos($col,'"')===false && strpos($col,' ')==false) $columns[] = '"'.$col.'"'; - else $columns[] = $col; - } - - $columns = join(',',$columns); - } - - $query = sprintf("select %s from %s %s %s %s", $columns, implode(',',$table_list),implode(' ',$left_join), $condition, $groupby_query.$orderby_query); - $query .= ";"; - $query .= (__DEBUG_QUERY__&1 && $output->query_id)?sprintf(' '.$this->comment_syntax,$this->query_id):''; - $result = $this->_query($query); - if($this->isError()) { - if(!$this->transaction_started) @ibase_rollback($this->fd); - return; - } - - $data = $this->_fetch($result, $output); - if(!$this->transaction_started) @ibase_commit($this->fd); - - if(count($click_count)>0 && count($output->conditions)>0){ - $_query = ''; - foreach($click_count as $k => $c) $_query .= sprintf(',%s=%s+1 ',$c,$c); - $_query = sprintf('update %s set %s %s',implode(',',$table_list), substr($_query,1), $condition); - $this->_query($_query); - } - - $buff = new Object(); - $buff->data = $data; - return $buff; + if ($this->isError ()) return $this->queryError($queryObject); + else return $this->queryPageLimit($queryObject, $result, $connection); } - /** - * @brief query xml에 navigation 정보가 있을 경우 페이징 관련 작업을 처리한다 - * - * 그닥 좋지는 않은 구조이지만 편리하다.. -_-; - **/ - function _getNavigationData($table_list, $columns, $left_join, $condition, $output) { - require_once(_XE_PATH_.'classes/page/PageHandler.class.php'); - - $column_list = $output->column_list; - - $query_groupby = ''; - if ($output->groups) { - foreach ($output->groups as $key => $val){ - $group_list[] = $this->autoQuotes($val); - if($column_list[$val]) $output->arg_columns[] = $column_list[$val]; - } - if (count($group_list)) $query_groupby = sprintf(" GROUP BY %s", implode(", ", $group_list)); - } - - /* - // group by 절이 포함된 SELECT 쿼리의 전체 갯수를 구하기 위한 수정 - // 정상적인 동작이 확인되면 주석으로 막아둔 부분으로 대체합니다. - // - $count_condition = strlen($query_groupby) ? sprintf('%s group by %s', $condition, $query_groupby) : $condition; - $total_count = $this->getCountCache($output->tables, $count_condition); - if($total_count === false) { - $count_query = sprintf('select count(*) as "count" from %s %s %s', implode(', ', $table_list), implode(' ', $left_join), $count_condition); - if (count($output->groups)) - $count_query = sprintf('select count(*) as "count" from (%s) xet', $count_query); - $result = $this->_query($count_query); - $count_output = $this->_fetch($result); - $total_count = (int)$count_output->count; - $this->putCountCache($output->tables, $count_condition, $total_count); - } - */ - - // 전체 개수를 구함 - $count_query = sprintf("select count(*) as \"count\" from %s %s %s", implode(',',$table_list),implode(' ',$left_join), $condition); - $count_query .= (__DEBUG_QUERY__&1 && $output->query_id)?sprintf(' '.$this->comment_syntax,$this->query_id . ' count(*)'):''; - $result = $this->_query($count_query); - $count_output = $this->_fetch($result); - if(!$this->transaction_started) @ibase_commit($this->fd); - - $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; - - // list_order, update_order 로 정렬시에 인덱스 사용을 위해 condition에 쿼리 추가 - if($output->order) { - $conditions = $this->getConditionList($output); - if(!in_array('list_order', $conditions) && !in_array('update_order', $conditions)) { - foreach($output->order as $key => $val) { - $col = $val[0]; - if(!in_array($col, array('list_order','update_order'))) continue; - if($condition) $condition .= sprintf(' and "%s" < 2100000000 ', $col); - else $condition = sprintf(' where "%s" < 2100000000 ', $col); - } - } - } - - $limit = sprintf('FIRST %d SKIP %d ', $list_count, $start_count); - - - if($output->order) { - foreach($output->order as $key => $val) { - $index_list[] = sprintf("%s %s", $this->autoQuotes($val[0]), $val[1]); - if(count($output->arg_columns) && $column_list[$val[0]]) $output->arg_columns[] = $column_list[$val[0]]; - } - if(count($index_list)) $orderby_query = sprintf(" ORDER BY %s", implode(",",$index_list)); - } - - if(count($output->arg_columns)) - { - $columns = array(); - foreach($output->arg_columns as $col){ - if(strpos($col,'"')===false && strpos($col,' ')==false) $columns[] = '"'.$col.'"'; - else $columns[] = $col; - } - - $columns = join(',',$columns); - } - - $query = sprintf('SELECT %s %s FROM %s %s %s, %s', $limit, $columns, implode(',',$table_list), implode(' ',$left_join), $condition, $groupby_query.$orderby_query); - $query .= ";"; - $query .= (__DEBUG_QUERY__&1 && $output->query_id)?sprintf(' '.$this->comment_syntax,$this->query_id):''; - $result = $this->_query($query); - if($this->isError()) { - if(!$this->transaction_started) @ibase_rollback($this->fd); - - $buff = new Object(); + function queryError($queryObject) { + $limit = $queryObject->getLimit(); + if ($limit && $limit->isPageHandler()) { + $buff = new Object (); $buff->total_count = 0; $buff->total_page = 0; $buff->page = 1; $buff->data = array(); + $buff->page_navigation = new PageHandler(/* $total_count */0, /* $total_page */1, /* $page */1, /* $page_count */10); //default page handler values + }else + return; + } - $buff->page_navigation = new PageHandler($total_count, $total_page, $page, $page_count); - return $buff; + function queryPageLimit($queryObject, $result, $connection) { + $limit = $queryObject->getLimit(); + if ($limit && $limit->isPageHandler()) { + // Total count + $temp_where = $queryObject->getWhereString(true, false); + $count_query = sprintf('select count(*) as "count" %s %s', 'FROM ' . $queryObject->getFromString(), ($temp_where === '' ? '' : ' WHERE ' . $temp_where)); + if ($queryObject->getGroupByString() != '') { + $count_query = sprintf('select count(*) as "count" from (%s) xet', $count_query); } - $virtual_no = $total_count - ($page-1)*$list_count; - while($tmp = ibase_fetch_object($result)) { - foreach($tmp as $key => $val){ - $type = $output->column_type[$key]; + $count_query .= ( __DEBUG_QUERY__ & 1 && $output->query_id) ? sprintf(' ' . $this->comment_syntax, $this->query_id) : ''; + $result_count = $this->_query($count_query, null, $connection); + $count_output = $this->_fetch($result_count); + $total_count = (int) $count_output->count; - // type 값이 null 일때는 $key값이 alias인 경우라 실제 column 이름을 찾아 type을 구함 - if($type == null && $output->columns && count($output->columns)) { - foreach($output->columns as $cols) { - if($cols['alias'] == $key) { - // table.column 형식인지 정규식으로 검사 함 - preg_match("/\w+[.](\w+)/", $cols['name'], $matches); - if($matches) { - $type = $output->column_type[$matches[1]]; - } - else { - $type = $output->column_type[$cols['name']]; - } - } - } - } + $list_count = $limit->list_count->getValue(); + if (!$list_count) $list_count = 20; + $page_count = $limit->page_count->getValue(); + if (!$page_count) $page_count = 10; + $page = $limit->page->getValue(); + if (!$page) $page = 1; + // Total pages + if ($total_count) $total_page = (int) (($total_count - 1) / $list_count) + 1; + else $total_page = 1; - if(($type == "text" || $type == "bigtext") && $tmp->{$key}) { - $blob_data = ibase_blob_info($tmp->{$key}); - $blob_hndl = ibase_blob_open($tmp->{$key}); - $tmp->{$key} = ibase_blob_get($blob_hndl, $blob_data[0]); - ibase_blob_close($blob_hndl); - } - } + if($page > $total_page) $page = $total_page; + $start_count = ($page-1)*$list_count; + $query = $this->getSelectSql($queryObject, true, $start_count); + if(strpos($query, "substr")) { + $query = str_replace ("substr", "substring", $query); + $query = $this->replaceSubstrFormat($query); + } + $query .= (__DEBUG_QUERY__&1 && $queryObject->query_id)?sprintf (' '.$this->comment_syntax, $this->query_id):''; + $result = $this->_query ($query, null, $connection); + if ($this->isError ()) + return $this->queryError($queryObject); + + $virtual_no = $total_count - ($page - 1) * $list_count; + while ($tmp = ibase_fetch_object($result)) $data[$virtual_no--] = $tmp; - } - if(!$this->transaction_started) @ibase_commit($this->fd); + if (!$this->transaction_started) + ibase_commit($connection); - $buff = new Object(); + $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; + }else { + $data = $this->_fetch($result); + $buff = new Object (); + $buff->data = $data; } + return $buff; } + function getParser() { + return new DBParser('"', '"', $this->prefix); + } + + function getSelectSql($query, $with_values = true, $start_count = 0) { + $limit = $query->getLimit(); + if ($limit && $limit->isPageHandler()) { + $list_count = $limit->list_count->getValue(); + if(!$limit->page) $page = 1; + else $page = $limit->page->getValue(); + if(!$start_count) + $start_count = ($page - 1) * $list_count; + $limit = sprintf('SELECT FIRST %d SKIP %d ', $list_count, $start_count); + } + + $select = $query->getSelectString($with_values); + + if ($select == '') + return new Object(-1, "Invalid query"); + + if ($limit && $limit->isPageHandler()) + $select = $limit . ' ' . $select; + else + $select = 'SELECT ' . $select; + $from = $query->getFromString($with_values); + if ($from == '') + return new Object(-1, "Invalid query"); + $from = ' FROM ' . $from; + + $where = $query->getWhereString($with_values); + if ($where != '') + $where = ' WHERE ' . $where; + + $groupBy = $query->getGroupByString(); + if ($groupBy != '') + $groupBy = ' GROUP BY ' . $groupBy; + + $orderBy = $query->getOrderByString(); + if ($orderBy != '') + $orderBy = ' ORDER BY ' . $orderBy; + + return $select . ' ' . $from . ' ' . $where . ' ' . $groupBy . ' ' . $orderBy; + } + + function getDeleteSql($query, $with_values = true){ + $sql = 'DELETE '; + + $from = $query->getFromString($with_values); + if($from == '') return new Object(-1, "Invalid query"); + + $sql .= ' FROM '.$from; + + $where = $query->getWhereString($with_values); + if($where != '') $sql .= ' WHERE ' . $where; + + return $sql; + } + + function replaceSubstrFormat($queryString){ + //replacing substr("string",number,number) with substr("string" from number for number) + $pattern = '/substring\("(\w+)",(\d+),(\d+)\)/i'; + $replacement = 'substring("${1}" from $2 for $3)'; + + return preg_replace($pattern, $replacement, $queryString); + } + +} + return new DBFireBird; ?> diff --git a/classes/db/DBMssql.class.php b/classes/db/DBMssql.class.php index 0d5055759..a9579edd5 100644 --- a/classes/db/DBMssql.class.php +++ b/classes/db/DBMssql.class.php @@ -1,909 +1,546 @@ - 'bigint', - 'number' => 'int', - 'varchar' => 'varchar', - 'char' => 'char', - 'text' => 'text', - 'bigtext' => 'text', - 'date' => 'varchar(14)', - 'float' => 'float', - ); - - /** - * @brief constructor - **/ - function DBMssql() { - $this->_setDBInfo(); - $this->_connect(); - } - - /** - * @brief create an instance of this class - */ - function create() - { - return new DBMssql; - } - - /** - * @brief 설치 가능 여부를 return - **/ - function isSupported() { - if (!extension_loaded("sqlsrv")) return false; - return true; - } - - /** - * @brief DB정보 설정 및 connect/ close - **/ - function _setDBInfo() { - $db_info = Context::getDBInfo(); - $this->hostname = $db_info->db_hostname; - $this->port = $db_info->db_port; - $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->database) return; - - //sqlsrv_configure( 'WarningsReturnAsErrors', 0 ); - //sqlsrv_configure( 'LogSeverity', SQLSRV_LOG_SEVERITY_ALL ); - //sqlsrv_configure( 'LogSubsystems', SQLSRV_LOG_SYSTEM_ALL ); - - $this->conn = sqlsrv_connect( $this->hostname, - array( 'Database' => $this->database,'UID'=>$this->userid,'PWD'=>$this->password )); - - - // 접속체크 - if($this->conn){ - $this->is_connected = true; - $this->password = md5($this->password); - }else{ - $this->is_connected = false; - } - } - - /** - * @brief DB접속 해제 - **/ - function close() { - if($this->is_connected == false) return; - - $this->commit(); - sqlsrv_close($this->conn); - $this->conn = null; - } - - /** - * @brief 쿼리에서 입력되는 문자열 변수들의 quotation 조절 - **/ - function addQuotes($string) { - if(version_compare(PHP_VERSION, "5.9.0", "<") && get_magic_quotes_gpc()) $string = stripslashes(str_replace("\\","\\\\",$string)); - //if(!is_numeric($string)) $string = str_replace("'","''",$string); - - return $string; - } - - /** - * @brief 트랜잭션 시작 - **/ - function begin() { - if($this->is_connected == false || $this->transaction_started) return; - if(sqlsrv_begin_transaction( $this->conn ) === false) return; - - $this->transaction_started = true; - } - - /** - * @brief 롤백 - **/ - function rollback() { - if($this->is_connected == false || !$this->transaction_started) return; - - $this->transaction_started = false; - sqlsrv_rollback( $this->conn ); - } - - /** - * @brief 커밋 - **/ - function commit($force = false) { - if(!$force && ($this->is_connected == false || !$this->transaction_started)) return; - - $this->transaction_started = false; - sqlsrv_commit( $this->conn ); - } - - /** - * @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->is_connected == false || !$query) return; - - $_param = array(); - - if(count($this->param)){ - foreach($this->param as $k => $o){ - if($o['type'] == 'number'){ - $_param[] = &$o['value']; - }else{ - $_param[] = array(&$o['value'], SQLSRV_PARAM_IN, SQLSRV_PHPTYPE_STRING('utf-8')); - } - } - } - - // 쿼리 시작을 알림 - $this->actStart($query); - - // 쿼리 문 실행 - $result = false; - if(count($_param)){ - $result = @sqlsrv_query($this->conn, $query, $_param); - }else{ - $result = @sqlsrv_query($this->conn, $query); - } - - // 오류 체크 - if(!$result) $this->setError(print_r(sqlsrv_errors(),true)); - - // 쿼리 실행 종료를 알림 - $this->actFinish(); - $this->param = array(); - - return $result; - } - - /** - * @brief 결과를 fetch - **/ - function _fetch($result) { - if(!$this->isConnected() || $this->isError() || !$result) return; - - $c = sqlsrv_num_fields($result); - $m = null; - $output = array(); - - while(sqlsrv_fetch($result)){ - if(!$m) $m = sqlsrv_field_metadata($result); - unset($row); - for($i=0;$i<$c;$i++){ - $row->{$m[$i]['Name']} = sqlsrv_get_field( $result, $i, SQLSRV_PHPTYPE_STRING( 'utf-8' )); - } - $output[] = $row; - } - - if(count($output)==1) return $output[0]; - return $output; - - } - - /** - * @brief 1씩 증가되는 sequence값을 return (mssql의 auto_increment는 sequence테이블에서만 사용) - **/ - function getNextSequence() { - $query = sprintf("insert into %ssequence (seq) values (ident_incr('%ssequence'))", $this->prefix, $this->prefix); - $this->_query($query); - - $query = sprintf("select ident_current('%ssequence')+1 as sequence", $this->prefix); - $result = $this->_query($query); - $tmp = $this->_fetch($result); - - - return $tmp->sequence; - } - - /** - * @brief 테이블 기생성 여부 return - **/ - function isTableExists($target_name) { - $query = sprintf("select name from sysobjects where name = '%s%s' and xtype='U'", $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) { - if($this->isColumnExists($table_name, $column_name)) return; - $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 제거 - **/ - function dropColumn($table_name, $column_name) { - if(!$this->isColumnExists($table_name, $column_name)) return; - $query = sprintf("alter table %s%s drop %s ", $this->prefix, $table_name, $column_name); - $this->_query($query); - } - - /** - * @brief 특정 테이블의 column의 정보를 return - **/ - function isColumnExists($table_name, $column_name) { - $query = sprintf("select syscolumns.name as name from syscolumns, sysobjects where sysobjects.name = '%s%s' and sysobjects.id = syscolumns.id and syscolumns.name = '%s'", $this->prefix, $table_name, $column_name); - $result = $this->_query($query); - if($this->isError()) return; - $tmp = $this->_fetch($result); - if(!$tmp->name) return false; - return true; - } - - /** - * @brief 특정 테이블에 특정 인덱스 추가 - * $target_columns = array(col1, col2) - * $is_unique? unique : none - **/ - function addIndex($table_name, $index_name, $target_columns, $is_unique = false) { - if($this->isIndexExists($table_name, $index_name)) return; - if(!is_array($target_columns)) $target_columns = array($target_columns); - - $query = sprintf("create %s index %s on %s%s (%s)", $is_unique?'unique':'', $index_name, $this->prefix, $table_name, implode(',',$target_columns)); - $this->_query($query); - } - - /** - * @brief 특정 테이블의 특정 인덱스 삭제 - **/ - function dropIndex($table_name, $index_name, $is_unique = false) { - if(!$this->isIndexExists($table_name, $index_name)) return; - $query = sprintf("drop index %s%s.%s", $this->prefix, $table_name, $index_name); - $this->_query($query); - } - - /** - * @brief 특정 테이블의 index 정보를 return - **/ - function isIndexExists($table_name, $index_name) { - $query = sprintf("select sysindexes.name as name from sysindexes, sysobjects where sysobjects.name = '%s%s' and sysobjects.id = sysindexes.id and sysindexes.name = '%s'", $this->prefix, $table_name, $index_name); - - $result = $this->_query($query); - if($this->isError()) return; - $tmp = $this->_fetch($result); - - if(!$tmp->name) return false; - return true; - } - - /** - * @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; - - if($table_name == 'sequence') { - $table_name = $this->prefix.$table_name; - $query = sprintf('create table %s ( sequence int identity(1,1), seq int )', $table_name); - return $this->_query($query); - } else { - $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 %s', - $name, - $this->column_type[$type], - !in_array($type,array('number','text'))&&$size?'('.$size.')':'', - $primary_key?'primary key':'', - isset($default)?"default '".$default."'":'', - $notnull?'not null':'null', - $auto_increment?'identity(1,1)':'' - ); - - if($unique) $unique_list[$unique][] = $name; - else if($index) $index_list[$index][] = $name; - } - - $schema = sprintf('create table [%s] (xe_seq int identity(1,1),%s%s)', $this->addQuotes($table_name), "\n", implode($column_schema,",\n")); - $output = $this->_query($schema); - if(!$output) return false; - - if(count($unique_list)) { - foreach($unique_list as $key => $val) { - $query = sprintf("create unique index %s on %s (%s);", $key, $table_name, '['.implode('],[',$val).']'); - $this->_query($query); - } - } - - if(count($index_list)) { - foreach($index_list as $key => $val) { - $query = sprintf("create index %s on %s (%s);", $key, $table_name, '['.implode('],[',$val).']'); - $this->_query($query); - } - } - return true; - } - } - - /** - * @brief 조건문 작성하여 return - **/ - function getCondition($output) { - if(!$output->conditions) return; - $condition = $this->_getCondition($output->conditions,$output->column_type); - if($condition) $condition = ' where '.$condition; - return $condition; - } - - function getLeftCondition($conditions,$column_type){ - return $this->_getCondition($conditions,$column_type); - } - - - function _getCondition($conditions,$column_type) { - $condition = ''; - - foreach($conditions as $val) { - $sub_condition = ''; - foreach($val['condition'] as $v) { - if(!isset($v['value'])) continue; - if($v['value'] === '') continue; - if(!in_array(gettype($v['value']), array('string', 'integer', 'double'))) continue; - - $name = $v['column']; - if(preg_match('/^substr\(/i',$name)) $name = preg_replace('/^substr\(/i','substring(',$name); - $operation = $v['operation']; - $value = $v['value']; - - $type = $this->getColumnType($column_type,$name); - $pipe = $v['pipe']; - - $value = $this->getConditionValue($name, $value, $operation, $type, $column_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.')'; - } - } - return $condition; - } - - - function getConditionValue($name, $value, $operation, $type, $column_type) { - - if($type == 'number') { - if(strpos($value,',')===false && strpos($value,'(')===false){ - - if(is_integer($value)){ - $this->param[] = array('type'=>'number','value'=>(int)$value); - return '?'; - }else{ - return $value; - } - } - } - - if(strpos($name,'.')!==false&&strpos($value,'.')!==false) { - list($table_name, $column_name) = explode('.',$value); - if($column_type[$column_name]){ - return $value; - } - } - - switch($operation) { - case 'like_prefix' : - $value = preg_replace('/(^\'|\'$){1}/','',$value); - $this->param[] = array('type'=>$column_type[$name],'value'=>$value); - - $value = "? + '%'"; - break; - case 'like_tail' : - $value = preg_replace('/(^\'|\'$){1}/','',$value); - $this->param[] = array('type'=>$column_type[$name],'value'=>$value); - - $value = "'%' + ?"; - break; - case 'like' : - $value = preg_replace('/(^\'|\'$){1}/','',$value); - $this->param[] = array('type'=>$column_type[$name],'value'=>$value); - - $value = "'%' + ? + '%'"; - break; - case 'notin' : - preg_match_all('/,?\'([^\']*)\'/',$value,$match); - $val = array(); - foreach($match[1] as $k => $v){ - $this->param[] = array('type'=>$column_type[$name],'value'=>trim($v)); - $val[] ='?'; - } - $value = join(',',$val); - break; - case 'in' : - preg_match_all('/,?\'([^\']*)\'/',$value,$match); - $val = array(); - foreach($match[1] as $k => $v){ - $this->param[] = array('type'=>$column_type[$name],'value'=>trim($v)); - $val[] ='?'; - } - $value = join(',',$val); - break; - default: - $value = preg_replace('/(^\'|\'$){1}/','',$value); - $this->param[] = array('type'=>$column_type[$name],'value'=>$value); - $value = '?'; - break; - } - - return $value; - } - - /** - * @brief insertAct 처리 - **/ - function _executeInsertAct($output) { - - // 테이블 정리 - foreach($output->tables as $key => $val) { - $table_list[] = '['.$this->prefix.$val.']'; - } - - // 컬럼 정리 - 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 = ''; - } elseif(is_numeric($value)){ - if(!$value) $value = ''; - $value = (int)$value; - } elseif(!$value){ - $value = ''; - } - // sql injection 문제로 xml 선언이 number인 경우이면서 넘어온 값이 숫자형이 아니면 숫자형으로 강제 형변환 - else $this->_filterNumber(&$value); - - $column_list[] = '['.$name.']'; - $value_list[] = '?'; - - $this->param[] = array('type'=>$output->column_type[$name], 'value'=>$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.$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); - if(!$value) $value = ''; - - $this->param[] = array('type'=>$output->column_type[$name], 'value'=>$value); - $column_list[] = sprintf("[%s] = ?", $name); - }elseif(!$value || is_numeric($value)){ - $value = (int)$value; - - $this->param[] = array('type'=>$output->column_type[$name], 'value'=>$value); - $column_list[] = sprintf("[%s] = ?", $name); - }else{ - if(!$value) $value = ''; - $this->_filterNumber(&$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.$val.']'; - } - - // 조건절 정리 - $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.$val.'] as '.$key; - } - - $left_join = array(); - // why??? - $left_tables= (array)$output->left_tables; - - foreach($left_tables as $key => $val) { - $condition = $this->_getCondition($output->left_conditions[$key],$output->column_type); - if($condition){ - $left_join[] = $val . ' ['.$this->prefix.$output->_tables[$key].'] as '.$key . ' on (' . $condition . ')'; - } - } - - $click_count = array(); - if(!$output->columns){ - $output->columns = array(array('name'=>'*')); - } - - $column_list = array(); - foreach($output->columns as $key => $val) { - $name = $val['name']; - if(preg_match('/^substr\(/i',$name)) $name = preg_replace('/^substr\(/i','substring(',$name); - $alias = $val['alias']; - if($val['click_count']) $click_count[] = $val['name']; - - if(substr($name,-1) == '*') { - $column_list[] = $name; - } elseif(strpos($name,'.')===false && strpos($name,'(')===false) { - if($alias) $column_list[$alias] = sprintf('[%s] as [%s]', $name, $alias); - else $column_list[] = sprintf('[%s]',$name); - } else { - if($alias) $column_list[$alias] = sprintf('%s as [%s]', $name, $alias); - else $column_list[] = sprintf('%s',$name); - } - } - $columns = implode(',',$column_list); - - $condition = $this->getCondition($output); - - $output->column_list = $column_list; - if($output->list_count && $output->page) return $this->_getNavigationData($table_list, $columns, $left_join, $condition, $output); - - // list_order, update_order 로 정렬시에 인덱스 사용을 위해 condition에 쿼리 추가 - if($output->order) { - $conditions = $this->getConditionList($output); - if(!in_array('list_order', $conditions) && !in_array('update_order', $conditions)) { - foreach($output->order as $key => $val) { - $col = $val[0]; - if(!in_array($col, array('list_order','update_order'))) continue; - if($condition) $condition .= sprintf(' and %s < 2100000000 ', $col); - else $condition = sprintf(' where %s < 2100000000 ', $col); - } - } - } - - if(count($output->groups)){ - foreach($output->groups as $k => $v ){ - if(preg_match('/^substr\(/i',$v)) $output->groups[$k] = preg_replace('/^substr\(/i','substring(',$v); - if($column_list[$v]) $output->arg_columns[] = $column_list[$v]; - } - $groupby_query = sprintf(' group by %s', implode(',',$output->groups)); - } - - if($output->order && !preg_match('/count\(\*\)/i',$columns) ) { - foreach($output->order as $key => $val) { - if(preg_match('/^substr\(/i',$val[0])) $name = preg_replace('/^substr\(/i','substring(',$val[0]); - $index_list[] = sprintf('%s %s', $val[0], $val[1]); - if(count($output->arg_columns) && $column_list[$val[0]]) $output->arg_columns[] = $column_list[$val[0]]; - } - if(count($index_list)) $orderby_query = ' order by '.implode(',',$index_list); - } - - if(count($output->arg_columns)) - { - $columns = array(); - foreach($output->arg_columns as $col){ - if(strpos($col,'[')===false && strpos($col,' ')==false) $columns[] = '['.$col.']'; - else $columns[] = $col; - } - - $columns = join(',',$columns); - } - - $query = sprintf("%s from %s %s %s %s", $columns, implode(',',$table_list),implode(' ',$left_join), $condition, $groupby_query.$orderby_query); - // list_count를 사용할 경우 적용 - if($output->list_count['value']) $query = sprintf('select top %d %s', $output->list_count['value'], $query); - else $query = "select ".$query; - - $query .= (__DEBUG_QUERY__&1 && $output->query_id)?sprintf(' '.$this->comment_syntax,$this->query_id):''; - $result = $this->_query($query); - if($this->isError()) return; - - if(count($click_count)>0 && count($output->conditions)>0){ - $_query = ''; - foreach($click_count as $k => $c) $_query .= sprintf(',%s=%s+1 ',$c,$c); - $_query = sprintf('update %s set %s %s',implode(',',$table_list), substr($_query,1), $condition); - $this->_query($_query); - } - - $data = $this->_fetch($result); - - $buff = new Object(); - $buff->data = $data; - return $buff; - } - - /** - * @brief query xml에 navigation 정보가 있을 경우 페이징 관련 작업을 처리한다 - * - * 그닥 좋지는 않은 구조이지만 편리하다.. -_-; - **/ - function _getNavigationData($table_list, $columns, $left_join, $condition, $output) { - require_once(_XE_PATH_.'classes/page/PageHandler.class.php'); - - $column_list = $output->column_list; - - // 전체 개수를 구함 - if(count($output->groups)){ - foreach($output->groups as $k => $v ){ - if(preg_match('/^substr\(/i',$v)) $output->groups[$k] = preg_replace('/^substr\(/i','substring(',$v); - if($column_list[$v]) $output->arg_columns[] = $column_list[$v]; - } - $count_condition = sprintf('%s group by %s', $condition, implode(', ', $output->groups)); - }else{ - $count_condition = $condition; - } - - $count_query = sprintf("select count(*) as count from %s %s %s", implode(', ', $table_list), implode(' ', $left_join), $count_condition); - if (count($output->groups)) $count_query = sprintf('select count(*) as count from (%s) xet', $count_query); - - $param = $this->param; - - $count_query .= (__DEBUG_QUERY__&1 && $output->query_id)?sprintf(' '.$this->comment_syntax,$this->query_id . ' count(*)'):''; - $result = $this->_query($count_query); - - $this->param = $param; - $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; - - // list_order, update_order 로 정렬시에 인덱스 사용을 위해 condition에 쿼리 추가 - $conditions = $this->getConditionList($output); - if($output->order) { - if(!in_array('list_order', $conditions) && !in_array('update_order', $conditions)) { - foreach($output->order as $key => $val) { - $col = $val[0]; - if(!in_array($col, array('list_order','update_order'))) continue; - if($condition) $condition .= sprintf(' and %s < 2100000000 ', $col); - else $condition = sprintf(' %s < 2100000000 ', $col); - } - } - } - - // group by 절 추가 - if(count($output->groups)){ - foreach($output->groups as $k => $v ){ - if(preg_match('/^substr\(/i',$v)) $output->groups[$k] = preg_replace('/^substr\(/i','substring(',$v); - if($column_list[$v]) $output->arg_columns[] = $column_list[$v]; - } - - $group = sprintf('group by %s', implode(',',$output->groups)); - } - - // order 절 추가 - $order_targets = array(); - if($output->order) { - foreach($output->order as $key => $val) { - if(preg_match('/^substr\(/i',$val[0])) $name = preg_replace('/^substr\(/i','substring(',$val[0]); - $order_targets[$val[0]] = $val[1]; - $index_list[] = sprintf('%s %s', $val[0], $val[1]); - if(count($output->arg_columns) && $column_list[$val[0]]) $output->arg_columns[] = $column_list[$val[0]]; - } - if(count($index_list)) $order .= 'order by '.implode(',',$index_list); - } - if(!count($order_targets)) { - if(in_array('list_order',$conditions)) $order_targets['list_order'] = 'asc'; - else $order_targets['xe_seq'] = 'desc'; - } - - if(count($output->arg_columns)) - { - $columns = array(); - foreach($output->arg_columns as $col){ - if(strpos($col,'[')===false && strpos($col,' ')==false) $columns[] = '['.$col.']'; - else $columns[] = $col; - } - - $columns = join(',',$columns); - } - - if($start_count<1) { - $query = sprintf('select top %d %s from %s %s %s %s %s', $list_count, $columns, implode(',',$table_list), implode(' ',$left_join), $condition, $group, $order); - - } else { - foreach($order_targets as $k => $v) { - $first_columns[] = sprintf('%s(%s) as %s', $v=='asc'?'max':'min', $k, $k); - $first_sub_columns[] = $k; - } - - // 1차로 order 대상에 해당 하는 값을 가져옴 - $param = $this->param; - $first_query = sprintf("select %s from (select top %d %s from %s %s %s %s %s) xet", implode(',',$first_columns), $start_count, implode(',',$first_sub_columns), implode(',',$table_list), implode(' ',$left_join), $condition, $group, $order); - $result = $this->_query($first_query); - $this->param = $param; - $tmp = $this->_fetch($result); - - - - // 1차에서 나온 값을 이용 다시 쿼리 실행 - $sub_cond = array(); - foreach($order_targets as $k => $v) { - $sub_cond[] = sprintf("%s %s '%s'", $k, $v=='asc'?'>':'<', $tmp->{$k}); - } - $sub_condition = ' and( '.implode(' and ',$sub_cond).' )'; - - if($condition) $condition .= $sub_condition; - else $condition = ' where '.$sub_condition; - $query = sprintf('select top %d %s from %s %s %s %s %s', $list_count, $columns, implode(',',$table_list), implode(' ',$left_join), $condition, $group, $order); - } - - $query .= (__DEBUG_QUERY__&1 && $output->query_id)?sprintf(' '.$this->comment_syntax,$this->query_id):''; - $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; - - $output = $this->_fetch($result); - if(!is_array($output)) $output = array($output); - - foreach($output as $k => $v) { - $data[$virtual_no--] = $v; - } - - $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; - } - - } - -return new DBMssql; -?> + 'bigint', + 'number' => 'int', + 'varchar' => 'varchar', + 'char' => 'char', + 'text' => 'text', + 'bigtext' => 'text', + 'date' => 'varchar(14)', + 'float' => 'float', + ); + + /** + * @brief constructor + **/ + function DBMssql() { + $this->_setDBInfo(); + $this->_connect(); + } + + /** + * @brief create an instance of this class + */ + function create() + { + return new DBMssql; + } + + /** + * @brief Return if installable + **/ + function isSupported() { + if (!extension_loaded("sqlsrv")) return false; + return true; + } + + /** + * @brief DB Connection + **/ + function __connect($connection) { + //sqlsrv_configure( 'WarningsReturnAsErrors', 0 ); + //sqlsrv_configure( 'LogSeverity', SQLSRV_LOG_SEVERITY_ALL ); + //sqlsrv_configure( 'LogSubsystems', SQLSRV_LOG_SYSTEM_ALL ); + $result = @sqlsrv_connect($connection["db_hostname"], array('Database' => $connection["db_database"], 'UID' => $connection["db_userid"], 'PWD' => $connection["db_password"])); + + if(!$result) + { + $errors = print_r(sqlsrv_errors(), true); + $this->setError (-1, 'database connect fail' . PHP_EOL . $errors); + return; + } + return $result; + } + + /** + * @brief DB disconnect + **/ + function _close($connection) { + $this->commit(); + sqlsrv_close($connection); + } + + /** + * @brief handles quatation of the string variables from the query + **/ + // TODO See what to do about this + function addQuotes($string) { + if(version_compare(PHP_VERSION, "5.9.0", "<") && get_magic_quotes_gpc()) $string = stripslashes(str_replace("\\","\\\\",$string)); + //if(!is_numeric($string)) $string = str_replace("'","''",$string); + + return $string; + } + + /** + * @brief Begin transaction + **/ + function _begin() { + $connection = $this->_getConnection('master'); + if(sqlsrv_begin_transaction($connection) === false) return; + return true; + } + + /** + * @brief Rollback + **/ + function _rollback() { + $connection = $this->_getConnection('master'); + sqlsrv_rollback($connection); + return true; + } + + /** + * @brief Commit + **/ + function _commit() { + $connection = $this->_getConnection('master'); + sqlsrv_commit($connection); + return true; + } + + /** + * @brief : executing the query and fetching the result + * + * query: run a query and return the result\n + * fetch: NULL if no value returned \n + * array object if rows returned \n + * object if a row returned \n + * return\n + **/ + function __query($query, $connection) { + $_param = array(); + + if(count($this->param)){ + foreach($this->param as $k => $o){ + if($o->getType() == 'number'){ + $value = $o->getUnescapedValue(); + if(is_array($value)) $_param = array_merge($_param, $value); + else $_param[] = $o->getUnescapedValue(); + }else{ + $value = $o->getUnescapedValue(); + if(is_array($value)) { + foreach($value as $v) + $_param[] = array($v, SQLSRV_PARAM_IN, SQLSRV_PHPTYPE_STRING('utf-8')); + } + else $_param[] = array($value, SQLSRV_PARAM_IN, SQLSRV_PHPTYPE_STRING('utf-8')); + } + } + } + + // Run the query statement + $result = false; + if(count($_param)){ + $result = @sqlsrv_query($connection, $query, $_param); + }else{ + $result = @sqlsrv_query($connection, $query); + } + // Error Check + + if(!$result) $this->setError(print_r(sqlsrv_errors(),true)); + + $this->param = array(); + + return $result; + } + + /** + * @brief Fetch results + **/ + function _fetch($result, $arrayIndexEndValue = NULL) { + if(!$this->isConnected() || $this->isError() || !$result) return; + + $c = sqlsrv_num_fields($result); + $m = null; + $output = array(); + + while(sqlsrv_fetch($result)){ + if(!$m) $m = sqlsrv_field_metadata($result); + unset($row); + for($i=0;$i<$c;$i++){ + $row->{$m[$i]['Name']} = sqlsrv_get_field( $result, $i, SQLSRV_PHPTYPE_STRING( 'utf-8' )); + } + if($arrayIndexEndValue) $output[$arrayIndexEndValue--] = $row; + else $output[] = $row; + } + + if(count($output)==1) { + if(isset($arrayIndexEndValue)) return $output; + else return $output[0]; + } + return $output; + + } + + /** + * @brief Return sequence value incremented by 1(auto_increment is usd in the sequence table only) + **/ + function getNextSequence() { + $query = sprintf("insert into %ssequence (seq) values (ident_incr('%ssequence'))", $this->prefix, $this->prefix); + $this->_query($query); + + $query = sprintf("select ident_current('%ssequence')+1 as sequence", $this->prefix); + $result = $this->_query($query); + $tmp = $this->_fetch($result); + + + return $tmp->sequence; + } + + /** + * @brief Return if a table already exists + **/ + function isTableExists($target_name) { + $query = sprintf("select name from sysobjects where name = '%s%s' and xtype='U'", $this->prefix, $this->addQuotes($target_name)); + $result = $this->_query($query); + $tmp = $this->_fetch($result); + + if(!$tmp) return false; + return true; + } + + /** + * @brief Add a column to a table + **/ + function addColumn($table_name, $column_name, $type='number', $size='', $default = '', $notnull=false) { + if($this->isColumnExists($table_name, $column_name)) return; + $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 Delete a column from a table + **/ + function dropColumn($table_name, $column_name) { + if(!$this->isColumnExists($table_name, $column_name)) return; + $query = sprintf("alter table %s%s drop %s ", $this->prefix, $table_name, $column_name); + $this->_query($query); + } + + /** + * @brief Return column information of a table + **/ + function isColumnExists($table_name, $column_name) { + $query = sprintf("select syscolumns.name as name from syscolumns, sysobjects where sysobjects.name = '%s%s' and sysobjects.id = syscolumns.id and syscolumns.name = '%s'", $this->prefix, $table_name, $column_name); + $result = $this->_query($query); + if($this->isError()) return; + $tmp = $this->_fetch($result); + if(!$tmp->name) return false; + return true; + } + + /** + * @brief Add an index to a table + * $target_columns = array(col1, col2) + * $is_unique? unique : none + **/ + function addIndex($table_name, $index_name, $target_columns, $is_unique = false) { + if($this->isIndexExists($table_name, $index_name)) return; + if(!is_array($target_columns)) $target_columns = array($target_columns); + + $query = sprintf("create %s index %s on %s%s (%s)", $is_unique?'unique':'', $index_name, $this->prefix, $table_name, implode(',',$target_columns)); + $this->_query($query); + } + + /** + * @brief Drop an index from a table + **/ + function dropIndex($table_name, $index_name, $is_unique = false) { + if(!$this->isIndexExists($table_name, $index_name)) return; + $query = sprintf("drop index %s%s.%s", $this->prefix, $table_name, $index_name); + $this->_query($query); + } + + /** + * @brief Return index information of a table + **/ + function isIndexExists($table_name, $index_name) { + $query = sprintf("select sysindexes.name as name from sysindexes, sysobjects where sysobjects.name = '%s%s' and sysobjects.id = sysindexes.id and sysindexes.name = '%s'", $this->prefix, $table_name, $index_name); + + $result = $this->_query($query); + if($this->isError()) return; + $tmp = $this->_fetch($result); + + if(!$tmp->name) return false; + return true; + } + + /** + * @brief Create a table by using xml file + **/ + function createTableByXml($xml_doc) { + return $this->_createTable($xml_doc); + } + + /** + * @brief Create a table by using xml file + **/ + function createTableByXmlFile($file_name) { + if(!file_exists($file_name)) return; + // read xml file + $buff = FileHandler::readFile($file_name); + return $this->_createTable($buff); + } + + /** + * @brief generate a query statement to create a table by using schema xml + * + * 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); + // Create a table schema + $table_name = $xml_obj->table->attrs->name; + if($this->isTableExists($table_name)) return; + + if($table_name == 'sequence') { + $table_name = $this->prefix.$table_name; + $query = sprintf('create table %s ( sequence int identity(1,1), seq int )', $table_name); + return $this->_query($query); + } else { + $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 %s', + $name, + $this->column_type[$type], + !in_array($type,array('number','text'))&&$size?'('.$size.')':'', + $primary_key?'primary key':'', + isset($default)?"default '".$default."'":'', + $notnull?'not null':'null', + $auto_increment?'identity(1,1)':'' + ); + + if($unique) $unique_list[$unique][] = $name; + else if($index) $index_list[$index][] = $name; + } + + $schema = sprintf('create table [%s] (xe_seq int identity(1,1),%s%s)', $this->addQuotes($table_name), "\n", implode($column_schema,",\n")); + $output = $this->_query($schema); + if(!$output) return false; + + if(count($unique_list)) { + foreach($unique_list as $key => $val) { + $query = sprintf("create unique index %s on %s (%s);", $key, $table_name, '['.implode('],[',$val).']'); + $this->_query($query); + } + } + + if(count($index_list)) { + foreach($index_list as $key => $val) { + $query = sprintf("create index %s on %s (%s);", $key, $table_name, '['.implode('],[',$val).']'); + $this->_query($query); + } + } + return true; + } + } + + + /** + * @brief Handle the insertAct + **/ + // TODO Lookup _filterNumber against sql injection - see if it is still needed and how to integrate + function _executeInsertAct($queryObject) { + $query = $this->getInsertSql($queryObject, false); + $this->param = $queryObject->getArguments(); + return $this->_query($query); + } + + /** + * @brief Handle updateAct + **/ + function _executeUpdateAct($queryObject) { + $query = $this->getUpdateSql($queryObject, false); + $this->param = $queryObject->getArguments(); + return $this->_query($query); + } + + /** + * @brief Handle deleteAct + **/ + function _executeDeleteAct($queryObject) { + $query = $this->getDeleteSql($queryObject, false); + $this->param = $queryObject->getArguments(); + return $this->_query($query); + } + + function getSelectSql($query){ + $with_value = false; + + //$limitOffset = $query->getLimit()->getOffset(); + //if($limitOffset) + // TODO Implement Limit with offset with subquery + $limit = '';$limitCount = ''; + $limitQueryPart = $query->getLimit(); + if($limitQueryPart) + $limitCount = $limitQueryPart->getLimit(); + if($limitCount != '') $limit = 'SELECT TOP ' . $limitCount; + + $select = $query->getSelectString($with_values); + if($select == '') return new Object(-1, "Invalid query"); + if($limit != '') + $select = $limit.' '.$select; + else + $select = 'SELECT ' .$select; + + $from = $query->getFromString($with_values); + if($from == '') return new Object(-1, "Invalid query"); + $from = ' FROM '.$from; + + $where = $query->getWhereString($with_values); + if($where != '') $where = ' WHERE ' . $where; + + $groupBy = $query->getGroupByString(); + if($groupBy != '') $groupBy = ' GROUP BY ' . $groupBy; + + $orderBy = $query->getOrderByString(); + if($orderBy != '') $orderBy = ' ORDER BY ' . $orderBy; + + + + return $select . ' ' . $from . ' ' . $where . ' ' . $groupBy . ' ' . $orderBy; + } + + /** + * @brief Handle selectAct + * + * In order to get a list of pages easily when selecting \n + * it supports a method as navigation + **/ + function _executeSelectAct($queryObject, $connection = null) { + $query = $this->getSelectSql($queryObject); + + if(strpos($query, "substr")) $query = str_replace ("substr", "substring", $query); + + // TODO Decide if we continue to pass parameters like this + $this->param = $queryObject->getArguments(); + + $query .= (__DEBUG_QUERY__&1 && $output->query_id)?sprintf(' '.$this->comment_syntax,$this->query_id):''; + $result = $this->_query($query, $connection); + + if ($this->isError ()) return $this->queryError($queryObject); + else return $this->queryPageLimit($queryObject, $result, $connection); + } + + function getParser(){ + return new DBParser("[", "]", $this->prefix); + } + + function queryError($queryObject){ + $limit = $queryObject->getLimit(); + if ($limit && $limit->isPageHandler()){ + $buff = new Object (); + $buff->total_count = 0; + $buff->total_page = 0; + $buff->page = 1; + $buff->data = array (); + $buff->page_navigation = new PageHandler (/*$total_count*/0, /*$total_page*/1, /*$page*/1, /*$page_count*/10);//default page handler values + return $buff; + }else + return; + } + + function queryPageLimit($queryObject, $result, $connection){ + $limit = $queryObject->getLimit(); + if ($limit && $limit->isPageHandler()) { + // Total count + $temp_where = $queryObject->getWhereString(true, false); + $count_query = sprintf('select count(*) as "count" %s %s', 'FROM ' . $queryObject->getFromString(), ($temp_where === '' ? '' : ' WHERE '. $temp_where)); + if ($queryObject->getGroupByString() != '') { + $count_query = sprintf('select count(*) as "count" from (%s) xet', $count_query); + } + + $count_query .= (__DEBUG_QUERY__&1 && $output->query_id)?sprintf (' '.$this->comment_syntax, $this->query_id):''; + $result_count = $this->_query($count_query, $connection); + $count_output = $this->_fetch($result_count); + $total_count = (int)$count_output->count; + + $list_count = $limit->list_count->getValue(); + if (!$list_count) $list_count = 20; + $page_count = $limit->page_count->getValue(); + if (!$page_count) $page_count = 10; + $page = $limit->page->getValue(); + if (!$page) $page = 1; + // Total pages + if ($total_count) { + $total_page = (int) (($total_count - 1) / $list_count) + 1; + } else $total_page = 1; + + // check the page variables + if ($page > $total_page) $page = $total_page; + $start_count = ($page - 1) * $list_count; + + $query .= (__DEBUG_QUERY__&1 && $queryObject->query_id)?sprintf (' '.$this->comment_syntax, $this->query_id):''; + $result = $this->_query ($query, $connection); + if ($this->isError ()) + return $this->queryError($queryObject); + + $virtual_no = $total_count - ($page - 1) * $list_count; + $data = $this->_fetch($result, $virtual_no); + + $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); + }else{ + $data = $this->_fetch($result); + $buff = new Object (); + $buff->data = $data; + } + return $buff; + } + + } + +return new DBMssql; +?> diff --git a/classes/db/DBMysql.class.php b/classes/db/DBMysql.class.php index 7e5be0f3e..d5fe2b462 100644 --- a/classes/db/DBMysql.class.php +++ b/classes/db/DBMysql.class.php @@ -1,782 +1,537 @@ - 'bigint', - 'number' => 'bigint', - 'varchar' => 'varchar', - 'char' => 'char', - 'text' => 'text', - 'bigtext' => 'longtext', - 'date' => 'varchar(14)', - 'float' => 'float', - ); - - /** - * @brief constructor - **/ - function DBMysql() { - $this->_setDBInfo(); - $this->_connect(); - } - - function create() { - return new DBMysql; - } - - /** - * @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->port = $db_info->db_port; - $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; - - if(strpos($this->hostname, ':')===false && $this->port) $this->hostname .= ':'.$this->port; - - // 접속시도 - $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, "XE cannot be installed under the version of 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; - $this->password = md5($this->password); - - // 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(version_compare(PHP_VERSION, "5.9.0", "<") && 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 = $this->db_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 ('0')", $this->prefix); - $this->_query($query); - $sequence = $this->db_insert_id(); - if($sequence % 10000 == 0) { - $query = sprintf("delete from `%ssequence` where seq < %d", $this->prefix, $sequence); - $this->_query($query); - } - - return $sequence; - } - - /** - * @brief mysql old password를 가져오는 함수 (mysql에서만 사용) - **/ - function isValidOldPassword($password, $saved_password) { - $query = sprintf("select password('%s') as password, old_password('%s') as old_password", $this->addQuotes($password), $this->addQuotes($password)); - $result = $this->_query($query); - $tmp = $this->_fetch($result); - if($tmp->password == $saved_password || $tmp->old_password == $saved_password) return true; - return false; - } - - /** - * @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 제거 - **/ - function dropColumn($table_name, $column_name) { - $query = sprintf("alter table `%s%s` drop `%s` ", $this->prefix, $table_name, $column_name); - $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 특정 테이블에 특정 인덱스 추가 - * $target_columns = array(col1, col2) - * $is_unique? unique : none - **/ - function addIndex($table_name, $index_name, $target_columns, $is_unique = false) { - if(!is_array($target_columns)) $target_columns = array($target_columns); - - $query = sprintf("alter table `%s%s` add %s index `%s` (%s);", $this->prefix, $table_name, $is_unique?'unique':'', $index_name, implode(',',$target_columns)); - $this->_query($query); - } - - /** - * @brief 특정 테이블의 특정 인덱스 삭제 - **/ - function dropIndex($table_name, $index_name, $is_unique = false) { - $query = sprintf("alter table `%s%s` drop index `%s`", $this->prefix, $table_name, $index_name); - $this->_query($query); - } - - - /** - * @brief 특정 테이블의 index 정보를 return - **/ - function isIndexExists($table_name, $index_name) { - //$query = sprintf("show indexes from %s%s where key_name = '%s' ", $this->prefix, $table_name, $index_name); - $query = sprintf("show indexes from `%s%s`", $this->prefix, $table_name); - $result = $this->_query($query); - if($this->isError()) return; - $output = $this->_fetch($result); - if(!$output) return; - if(!is_array($output)) $output = array($output); - - for($i=0;$iKey_name == $index_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.')':'', - isset($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; - $condition = $this->_getCondition($output->conditions,$output->column_type); - if($condition) $condition = ' where '.$condition; - return $condition; - } - - function getLeftCondition($conditions,$column_type){ - return $this->_getCondition($conditions,$column_type); - } - - - function _getCondition($conditions,$column_type) { - $condition = ''; - foreach($conditions as $val) { - $sub_condition = ''; - foreach($val['condition'] as $v) { - if(!isset($v['value'])) continue; - if($v['value'] === '') continue; - if(!in_array(gettype($v['value']), array('string', 'integer', 'double', 'array'))) continue; - - $name = $v['column']; - $operation = $v['operation']; - $value = $v['value']; - $type = $this->getColumnType($column_type,$name); - $pipe = $v['pipe']; - $value = $this->getConditionValue($name, $value, $operation, $type, $column_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.')'; - } - } - return $condition; - } - - /** - * @brief insertAct 처리 - **/ - function _executeInsertAct($output) { - // 테이블 정리 - foreach($output->tables as $key => $val) { - $table_list[] = '`'.$this->prefix.$val.'`'; - } - - // 컬럼 정리 - foreach($output->columns as $key => $val) { - $name = $val['name']; - $value = $val['value']; - - if($output->column_type[$name]!='number') { - - if(!is_null($value)){ - $value = "'" . $this->addQuotes($value) ."'"; - }else{ - if($val['notnull']=='notnull') { - $value = "''"; - } else { - //$value = 'null'; - $value = "''"; - } - } - - } - //elseif(!$value || is_numeric($value)) $value = (int)$value; - else $this->_filterNumber(&$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.$val.'` as '.$key; - } - - // 컬럼 정리 - 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)."'"; - else $this->_filterNumber(&$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.$val.'`'; - } - - // 조건절 정리 - $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.$val.'` as '.$key; - } - - $left_join = array(); - // why??? - $left_tables= (array)$output->left_tables; - - foreach($left_tables as $key => $val) { - $condition = $this->_getCondition($output->left_conditions[$key],$output->column_type); - if($condition){ - $left_join[] = $val . ' `'.$this->prefix.$output->_tables[$key].'` as '.$key . ' on (' . $condition . ')'; - } - } - - $click_count = array(); - if(!$output->columns){ - $output->columns = array(array('name'=>'*')); - } - - $column_list = array(); - foreach($output->columns as $key => $val) - { - $name = $val['name']; - $alias = $val['alias']; - if($val['click_count']) $click_count[] = $val['name']; - - if(substr($name,-1) == '*') - { - $column_list[] = $name; - } - else if(strpos($name,'.')===false && strpos($name,'(')===false) - { - if($alias) - { - $col = sprintf('`%s` as `%s`', $name, $alias); - $column_list[$alias] = $col; - } - else - { - $column_list[] = sprintf('`%s`',$name); - } - } - else - { - if($alias) - { - $col = sprintf('%s as `%s`', $name, $alias); - $column_list[$alias] = $col; - } - else - { - $column_list[] = sprintf('%s',$name); - } - } - } - - $columns = implode(',',$column_list); - $output->column_list = $column_list; - $condition = $this->getCondition($output); - - if($output->list_count && $output->page) return $this->_getNavigationData($table_list, $columns, $left_join, $condition, $output); - - // list_order, update_order 로 정렬시에 인덱스 사용을 위해 condition에 쿼리 추가 - if($output->order) { - $conditions = $this->getConditionList($output); - if(!in_array('list_order', $conditions) && !in_array('update_order', $conditions)) { - foreach($output->order as $key => $val) { - $col = $val[0]; - if(!in_array($col, array('list_order','update_order'))) continue; - if($condition) $condition .= sprintf(' and %s < 2100000000 ', $col); - else $condition = sprintf(' where %s < 2100000000 ', $col); - } - } - } - - - if(count($output->groups)) - { - $groupby_query = sprintf(' group by %s', implode(',',$output->groups)); - - if(count($output->arg_columns)) - { - foreach($output->groups as $group) - { - if($column_list[$group]) $output->arg_columns[] = $column_list[$group]; - } - } - } - - if($output->order) { - foreach($output->order as $key => $val) { - $index_list[] = sprintf('%s %s', $val[0], $val[1]); - if(count($output->arg_columns) && $column_list[$val[0]]) $output->arg_columns[] = $column_list[$val[0]]; - } - if(count($index_list)) $orderby_query .= ' order by '.implode(',',$index_list); - } - - if(count($output->arg_columns)) - { - $columns = array(); - foreach($output->arg_columns as $col){ - if(strpos($col,'`')===false && strpos($col,' ')==false) $columns[] = '`'.$col.'`'; - else $columns[] = $col; - } - - $columns = join(',',$columns); - } - - $query = sprintf("select %s from %s %s %s %s", $columns, implode(',',$table_list),implode(' ',$left_join), $condition, $groupby_query.$orderby_query); - - // list_count를 사용할 경우 적용 - if($output->list_count['value']) $query = sprintf('%s limit %d', $query, $output->list_count['value']); - - $query .= (__DEBUG_QUERY__&1 && $output->query_id)?sprintf(' '.$this->comment_syntax,$this->query_id):''; - - $result = $this->_query($query); - if($this->isError()) return; - if(count($click_count) && count($output->conditions)){ - $_query = ''; - foreach($click_count as $k => $c) $_query .= sprintf(',%s=%s+1 ',$c,$c); - $_query = sprintf('update %s set %s %s',implode(',',$table_list), substr($_query,1), $condition); - $this->_query($_query); - } - - $data = $this->_fetch($result); - - $buff = new Object(); - $buff->data = $data; - - return $buff; - } - - /** - * @brief query xml에 navigation 정보가 있을 경우 페이징 관련 작업을 처리한다 - * - * 그닥 좋지는 않은 구조이지만 편리하다.. -_-; - **/ - function _getNavigationData($table_list, $columns, $left_join, $condition, $output) { - require_once(_XE_PATH_.'classes/page/PageHandler.class.php'); - - $column_list = $output->column_list; - - // 전체 개수를 구함 - $count_condition = count($output->groups) ? sprintf('%s group by %s', $condition, implode(', ', $output->groups)) : $condition; - $count_query = sprintf("select count(*) as count from %s %s %s", implode(', ', $table_list), implode(' ', $left_join), $count_condition); - if (count($output->groups)) $count_query = sprintf('select count(*) as count from (%s) xet', $count_query); - - $count_query .= (__DEBUG_QUERY__&1 && $output->query_id)?sprintf(' '.$this->comment_syntax,$this->query_id . ' count(*)'):''; - $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; - - // list_order, update_order 로 정렬시에 인덱스 사용을 위해 condition에 쿼리 추가 - if($output->order) { - $conditions = $this->getConditionList($output); - if(!in_array('list_order', $conditions) && !in_array('update_order', $conditions)) { - foreach($output->order as $key => $val) { - $col = $val[0]; - if(!in_array($col, array('list_order','update_order'))) continue; - if($condition) $condition .= sprintf(' and %s < 2100000000 ', $col); - else $condition = sprintf(' where %s < 2100000000 ', $col); - } - } - } - - if(count($output->groups)){ - $groupby_query = sprintf(' group by %s', implode(',',$output->groups)); - - if(count($output->arg_columns)) - { - foreach($output->groups as $group) - { - if($column_list[$group]) $output->arg_columns[] = $column_list[$group]; - } - } - } - - if(count($output->order)) { - foreach($output->order as $key => $val) { - $index_list[] = sprintf('%s %s', $val[0], $val[1]); - if(count($output->arg_columns) && $column_list[$val[0]]) $output->arg_columns[] = $column_list[$val[0]]; - } - if(count($index_list)) $orderby_query = ' order by '.implode(',',$index_list); - } - - if(count($output->arg_columns)) - { - $columns = array(); - foreach($output->arg_columns as $col){ - if(strpos($col,'`')===false && strpos($col,' ')==false) $columns[] = '`'.$col.'`'; - else $columns[] = $col; - } - - $columns = join(',',$columns); - } - - $query = sprintf("select %s from %s %s %s %s", $columns, implode(',',$table_list), implode(' ',$left_join), $condition, $groupby_query.$orderby_query); - $query = sprintf('%s limit %d, %d', $query, $start_count, $list_count); - $query .= (__DEBUG_QUERY__&1 && $output->query_id)?sprintf(' '.$this->comment_syntax,$this->query_id):''; - - $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; - $data = array(); - while($tmp = $this->db_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; - } - - function db_insert_id() - { - return mysql_insert_id($this->fd); - } - - function db_fetch_object(&$result) - { - return mysql_fetch_object($result); - } - } - -return new DBMysql; -?> + 'bigint', + 'number' => 'bigint', + 'varchar' => 'varchar', + 'char' => 'char', + 'text' => 'text', + 'bigtext' => 'longtext', + 'date' => 'varchar(14)', + 'float' => 'float', + ); + + /** + * @brief constructor + **/ + function DBMysql() { + $this->_setDBInfo(); + $this->_connect(); + } + + function create() { + return new DBMysql; + } + + /** + * @brief Return if it is installable + **/ + function isSupported() { + if(!function_exists('mysql_connect')) return false; + return true; + } + + /** + * @brief DB Connection + **/ + function __connect($connection) { + // Ignore if no DB information exists + if (strpos($connection["db_hostname"], ':') === false && $connection["db_port"]) + $connection["db_hostname"] .= ':' . $connection["db_port"]; + + // Attempt to connect + $result = @mysql_connect($connection["db_hostname"], $connection["db_userid"], $connection["db_password"]); + + if(mysql_error()) { + $this->setError(mysql_errno(), mysql_error()); + return; + } + // Error appears if the version is lower than 4.1 + if(mysql_get_server_info($result)<"4.1") { + $this->setError(-1, "XE cannot be installed under the version of mysql 4.1. Current mysql version is ".mysql_get_server_info()); + return; + } + // select db + @mysql_select_db($connection["db_database"], $result); + if(mysql_error()) { + $this->setError(mysql_errno(), mysql_error()); + return; + } + + return $result; + } + + function _afterConnect($connection){ + // Set utf8 if a database is MySQL + $this->_query("set names 'utf8'", $connection); + } + + /** + * @brief DB disconnection + **/ + function _close($connection) { + @mysql_close($connection); + } + + /** + * @brief Add quotes on the string variables in a query + **/ + function addQuotes($string) { + if(version_compare(PHP_VERSION, "5.9.0", "<") && get_magic_quotes_gpc()) $string = stripslashes(str_replace("\\","\\\\",$string)); + if(!is_numeric($string)) $string = @mysql_real_escape_string($string); + return $string; + } + + /** + * @brief Begin transaction + **/ + function _begin() { + return true; + } + + /** + * @brief Rollback + **/ + function _rollback() { + return true; + } + + /** + * @brief Commits + **/ + function _commit() { + return true; + } + + /** + * @brief : Run a query and fetch the result + * + * query: run a query and return the result \n + * fetch: NULL if no value is returned \n + * array object if rows are returned \n + * object if a row is returned \n + * return\n + **/ + function __query($query, $connection) { + // Run the query statement + $result = mysql_query($query, $connection); + // Error Check + if(mysql_error($connection)) $this->setError(mysql_errno($connection), mysql_error($connection)); + // Return result + return $result; + } + + /** + * @brief Fetch results + **/ + function _fetch($result, $arrayIndexEndValue = NULL) { + if(!$this->isConnected() || $this->isError() || !$result) return; + while($tmp = $this->db_fetch_object($result)) { + if($arrayIndexEndValue) $output[$arrayIndexEndValue--] = $tmp; + else $output[] = $tmp; + } + if(count($output)==1){ + if(isset($arrayIndexEndValue)) return $output; + else return $output[0]; + } + mysql_free_result($result); + return $output; + } + + /** + * @brief Return sequence value incremented by 1(auto_increment is used in sequence table only in MySQL) + **/ + function getNextSequence() { + $query = sprintf("insert into `%ssequence` (seq) values ('0')", $this->prefix); + $this->_query($query); + $sequence = $this->db_insert_id(); + if($sequence % 10000 == 0) { + $query = sprintf("delete from `%ssequence` where seq < %d", $this->prefix, $sequence); + $this->_query($query); + } + + return $sequence; + } + + /** + * @brief Function to obtain mysql old password(mysql only) + **/ + function isValidOldPassword($password, $saved_password) { + $query = sprintf("select password('%s') as password, old_password('%s') as old_password", $this->addQuotes($password), $this->addQuotes($password)); + $result = $this->_query($query); + $tmp = $this->_fetch($result); + if($tmp->password == $saved_password || $tmp->old_password == $saved_password) return true; + return false; + } + + /** + * @brief Return if a table already exists + **/ + 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 Add a column to a table + **/ + 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 Delete a column from a table + **/ + function dropColumn($table_name, $column_name) { + $query = sprintf("alter table `%s%s` drop `%s` ", $this->prefix, $table_name, $column_name); + $this->_query($query); + } + + /** + * @brief Return column information of a table + **/ + 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 Add an index to a table + * $target_columns = array(col1, col2) + * $is_unique? unique : none + **/ + function addIndex($table_name, $index_name, $target_columns, $is_unique = false) { + if(!is_array($target_columns)) $target_columns = array($target_columns); + + $query = sprintf("alter table `%s%s` add %s index `%s` (%s);", $this->prefix, $table_name, $is_unique?'unique':'', $index_name, implode(',',$target_columns)); + $this->_query($query); + } + + /** + * @brief Drop an index from a table + **/ + function dropIndex($table_name, $index_name, $is_unique = false) { + $query = sprintf("alter table `%s%s` drop index `%s`", $this->prefix, $table_name, $index_name); + $this->_query($query); + } + + + /** + * @brief Return index information of a table + **/ + function isIndexExists($table_name, $index_name) { + //$query = sprintf("show indexes from %s%s where key_name = '%s' ", $this->prefix, $table_name, $index_name); + $query = sprintf("show indexes from `%s%s`", $this->prefix, $table_name); + $result = $this->_query($query); + if($this->isError()) return; + $output = $this->_fetch($result); + if(!$output) return; + if(!is_array($output)) $output = array($output); + + for($i=0;$iKey_name == $index_name) return true; + } + return false; + } + + /** + * @brief Create a table by using xml file + **/ + function createTableByXml($xml_doc) { + return $this->_createTable($xml_doc); + } + + /** + * @brief Create a table by using xml file + **/ + function createTableByXmlFile($file_name) { + if(!file_exists($file_name)) return; + // read xml file + $buff = FileHandler::readFile($file_name); + return $this->_createTable($buff); + } + + /** + * @brief generate a query statement to create a table by using schema xml + * + * 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); + // Create a table 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.')':'', + isset($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 Handle the insertAct + **/ + function _executeInsertAct($queryObject) { + // TODO See what priority does + //priority setting + //$priority = ''; + //if($output->priority) $priority = $output->priority['type'].'_priority'; + + $query = $this->getInsertSql($queryObject, true, true); + if(is_a($query, 'Object')) return; + return $this->_query($query); + } + + /** + * @brief Handle updateAct + **/ + function _executeUpdateAct($queryObject) { + // TODO See what proiority does + //priority setting + //$priority = ''; + //if($output->priority) $priority = $output->priority['type'].'_priority'; + + $query = $this->getUpdateSql($queryObject, true, true); + if(is_a($query, 'Object')) return; + return $this->_query($query); + } + + /** + * @brief Handle deleteAct + **/ + function _executeDeleteAct($queryObject) { + $query = $this->getDeleteSql($queryObject, true, true); + + if(is_a($query, 'Object')) return; + + //priority setting + // TODO Check what priority does + //$priority = ''; + //if($output->priority) $priority = $output->priority['type'].'_priority'; + return $this->_query($query); + } + + /** + * @brief Handle selectAct + * + * In order to get a list of pages easily when selecting \n + * it supports a method as navigation + **/ + function _executeSelectAct($queryObject, $connection = null) { + $limit = $queryObject->getLimit(); + if ($limit && $limit->isPageHandler()) + return $this->queryPageLimit($queryObject, $result, $connection); + else { + $query = $this->getSelectSql($queryObject); + if(is_a($query, 'Object')) return; + $query .= (__DEBUG_QUERY__&1 && $queryObject->query_id)?sprintf(' '.$this->comment_syntax,$this->query_id):''; + + $result = $this->_query ($query, $connection); + if ($this->isError ()) return $this->queryError($queryObject); + + $data = $this->_fetch($result); + $buff = new Object (); + $buff->data = $data; + return $buff; + } + } + + function db_insert_id() + { + $connection = $this->_getConnection('master'); + return mysql_insert_id($connection); + } + + function db_fetch_object(&$result) + { + return mysql_fetch_object($result); + } + + function getParser(){ + return new DBParser('`', '`', $this->prefix); + } + + function queryError($queryObject){ + $limit = $queryObject->getLimit(); + if ($limit && $limit->isPageHandler()){ + $buff = new Object (); + $buff->total_count = 0; + $buff->total_page = 0; + $buff->page = 1; + $buff->data = array (); + $buff->page_navigation = new PageHandler (/*$total_count*/0, /*$total_page*/1, /*$page*/1, /*$page_count*/10);//default page handler values + return $buff; + }else + return; + } + + function queryPageLimit($queryObject, $result, $connection){ + $limit = $queryObject->getLimit(); + // Total count + $temp_where = $queryObject->getWhereString(true, false); + $count_query = sprintf('select count(*) as "count" %s %s', 'FROM ' . $queryObject->getFromString(), ($temp_where === '' ? '' : ' WHERE '. $temp_where)); + if ($queryObject->getGroupByString() != '') { + $count_query = sprintf('select count(*) as "count" from (%s) xet', $count_query); + } + + $count_query .= (__DEBUG_QUERY__&1 && $queryObject->query_id)?sprintf (' '.$this->comment_syntax, $this->query_id):''; + $result_count = $this->_query($count_query, $connection); + $count_output = $this->_fetch($result_count); + $total_count = (int)$count_output->count; + + $list_count = $limit->list_count->getValue(); + if (!$list_count) $list_count = 20; + $page_count = $limit->page_count->getValue(); + if (!$page_count) $page_count = 10; + $page = $limit->page->getValue(); + if (!$page) $page = 1; + + // total pages + if ($total_count) + $total_page = (int) (($total_count - 1) / $list_count) + 1; + else + $total_page = 1; + + // check the page variables + if ($page > $total_page) $page = $total_page; + $start_count = ($page - 1) * $list_count; + + $query = $this->getSelectPageSql($queryObject, true, $start_count, $list_count); + + $query .= (__DEBUG_QUERY__&1 && $queryObject->query_id)?sprintf (' '.$this->comment_syntax, $this->query_id):''; + $result = $this->_query ($query, $connection); + if ($this->isError ()) + return $this->queryError($queryObject); + + $virtual_no = $total_count - ($page - 1) * $list_count; + $data = $this->_fetch($result, $virtual_no); + + $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; + } + + function getSelectPageSql($query, $with_values = true, $start_count = 0, $list_count = 0) { + $select = $query->getSelectString($with_values); + if($select == '') return new Object(-1, "Invalid query"); + $select = 'SELECT ' .$select; + + $from = $query->getFromString($with_values); + if($from == '') return new Object(-1, "Invalid query"); + $from = ' FROM '.$from; + + $where = $query->getWhereString($with_values); + if($where != '') $where = ' WHERE ' . $where; + + $groupBy = $query->getGroupByString(); + if($groupBy != '') $groupBy = ' GROUP BY ' . $groupBy; + + $orderBy = $query->getOrderByString(); + if($orderBy != '') $orderBy = ' ORDER BY ' . $orderBy; + + $limit = $query->getLimitString(); + if ($limit != '') $limit = sprintf (' LIMIT %d, %d', $start_count, $list_count); + + return $select . ' ' . $from . ' ' . $where . ' ' . $groupBy . ' ' . $orderBy . ' ' . $limit; + } + } + +return new DBMysql; +?> diff --git a/classes/db/DBMysql_innodb.class.php b/classes/db/DBMysql_innodb.class.php index 7ba8f304a..fb93ae8c2 100644 --- a/classes/db/DBMysql_innodb.class.php +++ b/classes/db/DBMysql_innodb.class.php @@ -1,164 +1,152 @@ -_setDBInfo(); - $this->_connect(); - } - - /** - * @brief create an instance of this class - */ - function create() - { - return new DBMysql_innodb; - } - - /** - * @brief DB접속 해제 - **/ - function close() { - if(!$this->isConnected()) return; - $this->_query("commit"); - @mysql_close($this->fd); - } - - /** - * @brief 트랜잭션 시작 - **/ - function begin() { - if(!$this->isConnected() || $this->transaction_started) return; - $this->transaction_started = true; - $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 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.')':'', - isset($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; - } - } - -return new DBMysql_innodb; -?> +_setDBInfo(); + $this->_connect(); + } + + /** + * @brief create an instance of this class + */ + function create() + { + return new DBMysql_innodb; + } + + /** + * @brief DB disconnection + **/ + function _close($connection) { + $this->_query("commit", $connection); + @mysql_close($connection); + } + + /** + * @brief Begin transaction + **/ + function _begin() { + $connection = $this->_getConnection('master'); + $this->_query("begin", $connection); + return true; + } + + /** + * @brief Rollback + **/ + function _rollback() { + $connection = $this->_getConnection('master'); + $this->_query("rollback", $connection); + return true; + } + + /** + * @brief Commits + **/ + function _commit() { + $connection = $this->_getConnection('master'); + $this->_query("commit", $connection); + return true; + } + + /** + * @brief : Run a query and fetch the result + * + * query: run a query and return the result \n + * fetch: NULL if no value is returned \n + * array object if rows are returned \n + * object if a row is returned \n + * return\n + **/ + function __query($query, $connection) { + // Run the query statement + $result = @mysql_query($query, $connection); + // Error Check + if(mysql_error($connection)) $this->setError(mysql_errno($connection), mysql_error($connection)); + // Return result + return $result; + } + + /** + * @brief generate a query statement to create a table by using schema xml + * + * 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); + // Create a table 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.')':'', + isset($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; + } + } + +return new DBMysql_innodb; +?> diff --git a/classes/db/DBMysqli.class.php b/classes/db/DBMysqli.class.php index 4ccaf3c41..933fd8c60 100644 --- a/classes/db/DBMysqli.class.php +++ b/classes/db/DBMysqli.class.php @@ -1,123 +1,118 @@ -_setDBInfo(); - $this->_connect(); - } - - /** - * @brief 설치 가능 여부를 return - **/ - function isSupported() { - if(!function_exists('mysqli_connect')) return false; - return true; - } - - /** - * @brief create an instance of this class - */ - function create() - { - return new DBMysqli; - } - - /** - * @brief DB 접속 - **/ - function _connect() { - // db 정보가 없으면 무시 - if(!$this->hostname || !$this->userid || !$this->password || !$this->database) return; - - // 접속시도 - if($this->port){ - $this->fd = @mysqli_connect($this->hostname, $this->userid, $this->password, $this->database, $this->port); - }else{ - $this->fd = @mysqli_connect($this->hostname, $this->userid, $this->password, $this->database); - } - $error = mysqli_connect_errno(); - if($error) { - $this->setError($error,mysqli_connect_error()); - return; - } - mysqli_set_charset($this->fd,'utf8'); - - // 접속체크 - $this->is_connected = true; - $this->password = md5($this->password); - } - - /** - * @brief DB접속 해제 - **/ - function close() { - if(!$this->isConnected()) return; - mysqli_close($this->fd); - } - - /** - * @brief 쿼리에서 입력되는 문자열 변수들의 quotation 조절 - **/ - function addQuotes($string) { - if(version_compare(PHP_VERSION, "5.9.0", "<") && get_magic_quotes_gpc()) $string = stripslashes(str_replace("\\","\\\\",$string)); - if(!is_numeric($string)) $string = mysqli_escape_string($this->fd,$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 = mysqli_query($this->fd,$query); - // 오류 체크 - $error = mysqli_error($this->fd); - if($error){ - $this->setError(mysqli_errno($this->fd), $error); - } - - // 쿼리 실행 종료를 알림 - $this->actFinish(); - - // 결과 리턴 - return $result; - } - - function db_insert_id() - { - return mysqli_insert_id($this->fd); - } - - function db_fetch_object(&$result) - { - return mysqli_fetch_object($result); - } - } - -return new DBMysqli; -?> +_setDBInfo(); + $this->_connect(); + } + + /** + * @brief Return if it is installable + **/ + function isSupported() { + if(!function_exists('mysqli_connect')) return false; + return true; + } + + /** + * @brief create an instance of this class + */ + function create() + { + return new DBMysqli; + } + + /** + * @brief DB Connection + **/ + function __connect($connection) { + // Attempt to connect + if ($connection["db_port"]) { + $result = @mysqli_connect($connection["db_hostname"] + , $connection["db_userid"] + , $connection["db_password"] + , $connection["db_database"] + , $connection["db_port"]); + } else { + $result = @mysqli_connect($connection["db_hostname"] + , $connection["db_userid"] + , $connection["db_password"] + , $connection["db_database"]); + } + $error = mysqli_connect_errno(); + if($error) { + $this->setError($error,mysqli_connect_error()); + return; + } + mysqli_set_charset($result,'utf8'); + return $result; + } + + /** + * @brief DB disconnection + **/ + function _close($connection) { + mysqli_close($connection); + } + + /** + * @brief Add quotes on the string variables in a query + **/ + function addQuotes($string) { + if(version_compare(PHP_VERSION, "5.9.0", "<") && get_magic_quotes_gpc()) $string = stripslashes(str_replace("\\","\\\\",$string)); + if(!is_numeric($string)){ + $connection = $this->_getConnection('master'); + $string = mysqli_escape_string($connection,$string); + } + return $string; + } + + /** + * @brief : Run a query and fetch the result + * + * query: run a query and return the result \n + * fetch: NULL if no value is returned \n + * array object if rows are returned \n + * object if a row is returned \n + * return\n + **/ + function __query($query, $connection) { + // Run the query statement + $result = mysqli_query($connection,$query); + // Error Check + $error = mysqli_error($connection); + if($error){ + $this->setError(mysqli_errno($connection), $error); + } + // Return result + return $result; + } + + function db_insert_id() + { + $connection = $this->_getConnection('master'); + return mysqli_insert_id($connection); + } + + function db_fetch_object(&$result) + { + return mysqli_fetch_object($result); + } + } + +return new DBMysqli; +?> diff --git a/classes/db/DBPostgresql.class.php b/classes/db/DBPostgresql.class.php index 88264e9c6..b108c6571 100644 --- a/classes/db/DBPostgresql.class.php +++ b/classes/db/DBPostgresql.class.php @@ -2,43 +2,39 @@ /** * @class DBPostgreSQL * @author ioseph (ioseph@postgresql.kr) updated by yoonjong.joh@gmail.com - * @brief MySQL DBMS를 이용하기 위한 class + * @brief Class to use PostgreSQL DBMS * @version 0.2 * * postgresql handling class - * 2009.02.10 update 와 delete query를 실행할때 table 이름에 alias 사용하는 것을 없앰. 지원 안함 - * order by clause를 실행할때 함수를 실행 하는 부분을 column alias로 대체. - * 2009.02.11 dropColumn() function이 추가 - * 2009.02.13 addColumn() 함수 변경 + * 2009.02.10 update and delete query for the table name at runtime, eliminating the alias to use. Not Supported + * when running order by clause column alias to run a function to replace parts. + * 2009.02.11 dropColumn() function added + * 2009.02.13 addColumn() function changes **/ class DBPostgresql extends DB { /** - * @brief PostgreSQL DB에 접속하기 위한 정보 + * @brief Connection information for PostgreSQL DB **/ - var $hostname = '127.0.0.1'; ///< hostname - var $userid = null; ///< user id - var $password = null; ///< password - var $database = null; ///< database - var $prefix = 'xe'; ///< XE에서 사용할 테이블들의 prefix (한 DB에서 여러개의 XE설치 가능) - var $comment_syntax = '/* %s */'; + var $prefix = 'xe'; // / 'bigint', + 'bignumber' => 'bigint', 'number' => 'integer', - 'varchar' => 'varchar', - 'char' => 'char', - 'text' => 'text', + 'varchar' => 'varchar', + 'char' => 'char', + 'text' => 'text', 'bigtext' => 'text', - 'date' => 'varchar(14)', + 'date' => 'varchar(14)', 'float' => 'real', ); @@ -50,7 +46,7 @@ class DBPostgresql extends DB $this->_setDBInfo(); $this->_connect(); } - + /** * @brief create an instance of this class */ @@ -60,7 +56,7 @@ class DBPostgresql extends DB } /** - * @brief 설치 가능 여부를 return + * @brief Return if it is installable **/ function isSupported() { @@ -70,66 +66,38 @@ class DBPostgresql extends DB } /** - * @brief DB정보 설정 및 connect/ close + * @brief DB Connection **/ - function _setDBInfo() + function __connect($connection) { - $db_info = Context::getDBInfo(); - $this->hostname = $db_info->db_hostname; - $this->port = $db_info->db_port; - $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() - { - // pg용 connection string + // the connection string for PG $conn_string = ""; + // Create connection string + $conn_string .= ($connection["db_hostname"]) ? ' host='.$connection["db_hostname"] : ""; + $conn_string .= ($connection["db_userid"]) ? " user=" . $connection["db_userid"] : ""; + $conn_string .= ($connection["db_password"]) ? " password=" . $connection["db_password"] : ""; + $conn_string .= ($connection["db_database"]) ? " dbname=" . $connection["db_database"] : ""; + $conn_string .= ($connection["db_port"]) ? " port=" . $connection["db_port"] : ""; - // db 정보가 없으면 무시 - if (!$this->hostname || !$this->userid || !$this->database) - return; - - // connection string 만들기 - $conn_string .= ($this->hostname) ? " host=$this->hostname" : ""; - $conn_string .= ($this->userid) ? " user=$this->userid" : ""; - $conn_string .= ($this->password) ? " password=$this->password" : ""; - $conn_string .= ($this->database) ? " dbname=$this->database" : ""; - $conn_string .= ($this->port) ? " port=$this->port" : ""; - - // 접속시도 - $this->fd = @pg_connect($conn_string); - if (!$this->fd || pg_connection_status($this->fd) != PGSQL_CONNECTION_OK) { + // Attempt to connect + $result = @pg_connect($conn_string); + if (!$result || pg_connection_status($result) != PGSQL_CONNECTION_OK) { $this->setError(-1, "CONNECTION FAILURE"); return; } - - // 접속체크 - $this->is_connected = true; - $this->password = md5($this->password); - // utf8임을 지정 - //$this ->_query('set client_encoding to uhc'); + return $result; } /** - * @brief DB접속 해제 + * @brief DB disconnection **/ - function close() + function _close($connection) { - if (!$this->isConnected()) - return; - @pg_close($this->fd); + @pg_close($connection); } /** - * @brief 쿼리에서 입력되는 문자열 변수들의 quotation 조절 + * @brief Add quotes on the string variables in a query **/ function addQuotes($string) { @@ -141,48 +109,43 @@ class DBPostgresql extends DB } /** - * @brief 트랜잭션 시작 + * @brief Begin transaction **/ - function begin() + function _begin() { - if (!$this->isConnected() || $this->transaction_started == false) - return; - if ($this->_query($this->fd, 'BEGIN')) - $this->transaction_started = true; + $connection = $this->_getConnection('master'); + if (!$this->_query('BEGIN')) return false; + return true; } /** - * @brief 롤백 + * @brief Rollback **/ - function rollback() + function _rollback() { - if (!$this->isConnected() || $this->transaction_started == false) - return; - if ($this->_query($this->fd, 'ROLLBACK')) - $this->transaction_started = false; + if (!$this->_query('ROLLBACK')) return false; + return true; } /** - * @brief 커밋 + * @brief Commits **/ - function commit() + function _commit() { - if (!$this->isConnected() || $this->transaction_started == false) - return; - if ($this->_query($this->fd, 'COMMIT')) - $this->transaction_started = false; + if (!$this->_query('COMMIT')) return false; + return true; } /** - * @brief : 쿼리문의 실행 및 결과의 fetch 처리 + * @brief : Run a query and fetch the result * - * query : query문 실행하고 result return\n - * fetch : reutrn 된 값이 없으면 NULL\n - * rows이면 array object\n - * row이면 object\n + * query: run a query and return the result \n + * fetch: NULL if no value is returned \n + * array object if rows are returned \n + * object if a row is returned \n * return\n **/ - function _query($query) + function __query($query, $connection) { if (!$this->isConnected()) return; @@ -198,58 +161,53 @@ class DBPostgresql extends DB $query = implode(" ",$l_query_array); } } - else if ($l_query_array[0] = "delete") + else if ($l_query_array[0] = "delete") { if (strtolower($l_query_array[3]) == "as") { $l_query_array[3] = ""; - $l_query_array[4] = ""; + $l_query_array[4] = ""; $query = implode(" ",$l_query_array); } } */ - - // 쿼리 시작을 알림 - $this->actStart($query); - $arr = array('Hello', 'World!', 'Beautiful', 'Day!'); - - - // 쿼리 문 실행 - $result = @pg_query($this->fd, $query); - - // 오류 체크 + // Notify to start a query execution + // $arr = array('Hello', 'World!', 'Beautiful', 'Day!'); + // Run the query statement + $result = @pg_query($connection, $query); + // Error Check if (!$result) { // var_dump($l_query_array); //var_dump($query); //die("\nin query statement\n"); //var_dump(debug_backtrace()); - $this->setError(1, pg_last_error($this->fd)); + $this->setError(1, pg_last_error($connection)); } - - // 쿼리 실행 종료를 알림 - $this->actFinish(); - - // 결과 리턴 + // Return result return $result; } /** - * @brief 결과를 fetch + * @brief Fetch results **/ - function _fetch($result) + // TODO This is duplicate code - maybe we can find away to abastract the driver + function _fetch($result, $arrayIndexEndValue = NULL) { if (!$this->isConnected() || $this->isError() || !$result) return; while ($tmp = pg_fetch_object($result)) { - $output[] = $tmp; + if($arrayIndexEndValue) $output[$arrayIndexEndValue--] = $tmp; + else $output[] = $tmp; + } + if(count($output)==1){ + if(isset($arrayIndexEndValue)) return $output; + else return $output[0]; } - if (count($output) == 1) - return $output[0]; return $output; } /** - * @brief 1씩 증가되는 sequence값을 return (postgresql의 auto_increment는 sequence테이블에서만 사용) + * @brief Return sequence value incremented by 1(in postgresql, auto_increment is used in the sequence table only) **/ function getNextSequence() { @@ -260,7 +218,7 @@ class DBPostgresql extends DB } /** - * @brief 테이블 기생성 여부 return + * @brief Return if a table already exists **/ function isTableExists($target_name) { @@ -277,7 +235,7 @@ class DBPostgresql extends DB } /** - * @brief 특정 테이블에 특정 column 추가 + * @brief Add a column to a table **/ function addColumn($table_name, $column_name, $type = 'number', $size = '', $default = NULL, $notnull = false) @@ -301,7 +259,7 @@ class DBPostgresql extends DB } if ($notnull) { $query = sprintf("update %s%s set %s = %s ", $this->prefix, $table_name, $column_name, $default); - $this->_query($query); + $this->_query($query); $query = sprintf("alter table %s%s alter %s set not null ", $this->prefix, $table_name, $column_name); $this->_query($query); } @@ -309,7 +267,7 @@ class DBPostgresql extends DB /** - * @brief 특정 테이블의 column의 정보를 return + * @brief Return column information of a table **/ function isColumnExists($table_name, $column_name) { @@ -329,7 +287,7 @@ class DBPostgresql extends DB } /** - * @brief 특정 테이블에 특정 인덱스 추가 + * @brief Add an index to a table * $target_columns = array(col1, col2) * $is_unique? unique : none **/ @@ -340,8 +298,7 @@ class DBPostgresql extends DB if (strpos($table_name, $this->prefix) === false) $table_name = $this->prefix . $table_name; - - // index_name의 경우 앞에 table이름을 붙여줘서 중복을 피함 + // Use a tablename before an index name to avoid defining the same index $index_name = $table_name . $index_name; $query = sprintf("create %s index %s on %s (%s);", $is_unique ? 'unique' : '', $index_name, @@ -350,7 +307,7 @@ class DBPostgresql extends DB } /** - * @brief 특정 테이블에 특정 column 제거 + * @brief Delete a column from a table **/ function dropColumn($table_name, $column_name) { @@ -359,14 +316,13 @@ class DBPostgresql extends DB } /** - * @brief 특정 테이블의 특정 인덱스 삭제 + * @brief Drop an index from a table **/ function dropIndex($table_name, $index_name, $is_unique = false) { if (strpos($table_name, $this->prefix) === false) $table_name = $this->prefix . $table_name; - - // index_name의 경우 앞에 table이름을 붙여줘서 중복을 피함 + // Use a tablename before an index name to avoid defining the same index $index_name = $table_name . $index_name; $query = sprintf("drop index %s", $index_name); @@ -375,14 +331,13 @@ class DBPostgresql extends DB /** - * @brief 특정 테이블의 index 정보를 return + * @brief Return index information of a table **/ function isIndexExists($table_name, $index_name) { if (strpos($table_name, $this->prefix) === false) $table_name = $this->prefix . $table_name; - - // index_name의 경우 앞에 table이름을 붙여줘서 중복을 피함 + // Use a tablename before an index name to avoid defining the same index $index_name = $table_name . $index_name; //$query = sprintf("show indexes from %s%s where key_name = '%s' ", $this->prefix, $table_name, $index_name); @@ -402,7 +357,7 @@ class DBPostgresql extends DB } /** - * @brief xml 을 받아서 테이블을 생성 + * @brief Create a table by using xml file **/ function createTableByXml($xml_doc) { @@ -410,19 +365,19 @@ class DBPostgresql extends DB } /** - * @brief xml 을 받아서 테이블을 생성 + * @brief Create a table by using xml file **/ function createTableByXmlFile($file_name) { if (!file_exists($file_name)) return; - // xml 파일을 읽음 + // read xml file $buff = FileHandler::readFile($file_name); return $this->_createTable($buff); } /** - * @brief schema xml을 이용하여 create table query생성 + * @brief generate a query statement to create a table by using schema xml * * type : number, varchar, text, char, date, \n * opt : notnull, default, size\n @@ -433,8 +388,7 @@ class DBPostgresql extends DB // xml parsing $oXml = new XmlParser(); $xml_obj = $oXml->parse($xml_doc); - - // 테이블 생성 schema 작성 + // Create a table schema $table_name = $xml_obj->table->attrs->name; if ($table_name == 'sequence') { @@ -507,434 +461,140 @@ class DBPostgresql extends DB } - /** - * @brief 조건문 작성하여 return - **/ - function getCondition($output) - { - if (!$output->conditions) - return; - $condition = $this->_getCondition($output->conditions, $output->column_type); - if ($condition) - $condition = ' where ' . $condition; - return $condition; - } - - function getLeftCondition($conditions, $column_type) - { - return $this->_getCondition($conditions, $column_type); - } - - - function _getCondition($conditions, $column_type) - { - $condition = ''; - foreach ($conditions as $val) { - $sub_condition = ''; - foreach ($val['condition'] as $v) { - if (!isset($v['value'])) - continue; - if ($v['value'] === '') - continue; - if(!in_array(gettype($v['value']), array('string', 'integer', 'double', 'array'))) continue; - continue; - - $name = $v['column']; - $operation = $v['operation']; - $value = $v['value']; - $type = $this->getColumnType($column_type, $name); - $pipe = $v['pipe']; - - $value = $this->getConditionValue($name, $value, $operation, $type, $column_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 . ')'; - } - } - return $condition; - } - /** - * @brief insertAct 처리 + * @brief Handle the insertAct **/ - function _executeInsertAct($output) + function _executeInsertAct($queryObject) { - // 테이블 정리 - foreach ($output->tables as $key => $val) { - $table_list[] = $this->prefix . $val; - } - - // 컬럼 정리 - 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'; - } - // sql injection 문제로 xml 선언이 number인 경우이면서 넘어온 값이 숫자형이 아니면 숫자형으로 강제 형변환 - // elseif (!$value || is_numeric($value)) $value = (int)$value; - else $this->_filterNumber(&$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.$val.' as '.$key; - $table_list[] = $this->prefix . $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) . "'"; - // sql injection 문제로 xml 선언이 number인 경우이면서 넘어온 값이 숫자형이 아니면 숫자형으로 강제 형변환 - else $this->_filterNumber(&$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); + $query = $this->getInsertSql($queryObject); + if(is_a($query, 'Object')) return; return $this->_query($query); } /** - * @brief deleteAct 처리 + * @brief Handle updateAct **/ - function _executeDeleteAct($output) + function _executeUpdateAct($queryObject) { - // 테이블 정리 - foreach ($output->tables as $key => $val) { - $table_list[] = $this->prefix . $val; - } - - // 조건절 정리 - $condition = $this->getCondition($output); - - $query = sprintf("delete from %s %s", implode(',', $table_list), $condition); - + $query = $this->getUpdateSql($queryObject); + if(is_a($query, 'Object')) return; + return $this->_query($query); + } + + /** + * @brief Handle deleteAct + **/ + function _executeDeleteAct($queryObject) + { + $query = $this->getDeleteSql($queryObject); + + if(is_a($query, 'Object')) return; return $this->_query($query); } /** - * @brief selectAct 처리 * - * select의 경우 특정 페이지의 목록을 가져오는 것을 편하게 하기 위해\n - * navigation이라는 method를 제공 + * override + * @param $queryObject + */ + function getSelectSql($query){ + $select = $query->getSelectString(); + if($select == '') return new Object(-1, "Invalid query"); + $select = 'SELECT ' .$select; + + $from = $query->getFromString(); + if($from == '') return new Object(-1, "Invalid query"); + $from = ' FROM '.$from; + + $where = $query->getWhereString(); + if($where != '') $where = ' WHERE ' . $where; + + $groupBy = $query->getGroupByString(); + if($groupBy != '') $groupBy = ' GROUP BY ' . $groupBy; + + $orderBy = $query->getOrderByString(); + if($orderBy != '') $orderBy = ' ORDER BY ' . $orderBy; + + $limit = $query->getLimitString(); + $limitObject = $query->getLimit(); + if($limit != '') $limit = ' LIMIT ' . $limitObject->getLimit() . ' OFFSET ' . $limitObject->getOffset(); + + return $select . ' ' . $from . ' ' . $where . ' ' . $groupBy . ' ' . $orderBy . ' ' . $limit; + } + + /** + * @brief Handle selectAct + * + * In order to get a list of pages easily when selecting \n + * it supports a method as navigation **/ - function _executeSelectAct($output) + function _executeSelectAct($queryObject, $connection) { - // 테이블 정리 - $table_list = array(); - foreach ($output->tables as $key => $val) { - $table_list[] = $this->prefix . $val . ' as ' . $key; - } + $query = $this->getSelectSql($queryObject); - $left_join = array(); - // why??? - $left_tables = (array )$output->left_tables; + if(is_a($query, 'Object')) return; - foreach ($left_tables as $key => $val) { - $condition = $this->_getCondition($output->left_conditions[$key], $output-> - column_type); - if ($condition) { - $left_join[] = $val . ' ' . $this->prefix . $output->_tables[$key] . ' as ' . $key . - ' on (' . $condition . ')'; - } - } + $query .= (__DEBUG_QUERY__&1 && $queryObject->query_id)?sprintf(' '.$this->comment_syntax,$this->query_id):''; - $click_count = array(); - if(!$output->columns){ - $output->columns = array(array('name'=>'*')); - } + // TODO Add support for click count + // TODO Add code for pagination - $column_list = array(); - foreach ($output->columns as $key => $val) { - $name = $val['name']; - $alias = $val['alias']; - if($val['click_count']) $click_count[] = $val['name']; - - if (substr($name, -1) == '*') { - $column_list[] = $name; - } elseif (strpos($name, '.') === false && strpos($name, '(') === false) { - if ($alias) - $column_list[$alias] = sprintf('%s as %s', $name, $alias); - else - $column_list[] = sprintf('%s', $name); - } else { - if ($alias) - $column_list[$alias] = sprintf('%s as %s', $name, $alias); - else - $column_list[] = sprintf('%s', $name); + $result = $this->_query ($query, $connection); + if ($this->isError ()) { + if ($limit && $output->limit->isPageHandler()){ + $buff = new Object (); + $buff->total_count = 0; + $buff->total_page = 0; + $buff->page = 1; + $buff->data = array (); + $buff->page_navigation = new PageHandler (/*$total_count*/0, /*$total_page*/1, /*$page*/1, /*$page_count*/10);//default page handler values + return $buff; + }else + return; } - } - $columns = implode(',', $column_list); - $condition = $this->getCondition($output); + $limit = $queryObject->getLimit(); + if ($limit && $limit->isPageHandler()) { + // Total count + $temp_where = $queryObject->getWhereString(true, false); + $count_query = sprintf('select count(*) as "count" %s %s', 'FROM ' . $queryObject->getFromString(), ($temp_where === '' ? '' : ' WHERE '. $temp_where)); + if ($queryObject->getGroupByString() != '') { + $count_query = sprintf('select count(*) as "count" from (%s) xet', $count_query); + } - $output->column_list = $column_list; - if ($output->list_count && $output->page) - return $this->_getNavigationData($table_list, $columns, $left_join, $condition, - $output); + $count_query .= (__DEBUG_QUERY__&1 && $output->query_id)?sprintf (' '.$this->comment_syntax, $this->query_id):''; + $result_count = $this->_query($count_query, $connection); + $count_output = $this->_fetch($result_count); + $total_count = (int)$count_output->count; - // list_order, update_order 로 정렬시에 인덱스 사용을 위해 condition에 쿼리 추가 - if ($output->order) { - $conditions = $this->getConditionList($output); - if (!in_array('list_order', $conditions) && !in_array('update_order', $conditions)) { - foreach ($output->order as $key => $val) { - $col = $val[0]; - if (!in_array($col, array('list_order', 'update_order'))) - continue; - if ($condition) - $condition .= sprintf(' and %s < 2100000000 ', $col); - else - $condition = sprintf(' where %s < 2100000000 ', $col); - } - } - } + // Total pages + if ($total_count) { + $total_page = (int) (($total_count - 1) / $limit->list_count) + 1; + } else $total_page = 1; - if (count($output->groups)) { - /* - var_dump("= column output start = "); - var_dump(sizeof ($output->columns) . " = end length == "); - var_dump($output->columns); - var_dump("= column output end = " . "\n"); - var_dump($output->groups); - var_dump("=== " . "\n"); - var_dump(debug_backtrace()); - - foreach($output->columns as $key => $val) { - $name = $val['name']; - $alias = $val['alias']; - } */ - $group_list = array(); - foreach ($output->groups as $gkey => $gval) { - foreach ($output->columns as $key => $val) { - $name = $val['name']; - $alias = $val['alias']; - if (trim($name) == trim($gval)) { - $group_list[] = $alias; - break; - } - } + $virtual_no = $total_count - ($limit->page - 1) * $limit->list_count; + $data = $this->_fetch($result, $virtual_no); - if($column_list[$gval]) $output->arg_columns[] = $column_list[$gval]; + $buff = new Object (); + $buff->total_count = $total_count; + $buff->total_page = $total_page; + $buff->page = $limit->page->getValue(); + $buff->data = $data; + $buff->page_navigation = new PageHandler($total_count, $total_page, $limit->page->getValue(), $limit->page_count); + }else{ + $data = $this->_fetch($result); + $buff = new Object (); + $buff->data = $data; + } - } - $groupby_query = sprintf(' group by %s', implode(',', $group_list)); - // var_dump($query); - } - - if ($output->order) { - foreach ($output->order as $key => $val) { - $index_list[] = sprintf('%s %s', $val[0], $val[1]); - if(count($output->arg_columns) && $column_list[$val[0]]) $output->arg_columns[] = $column_list[$val[0]]; - } - if (count($index_list)) $orderby_query = ' order by ' . implode(',', $index_list); - } - - if(count($output->arg_columns)) - { - $columns = join(',',$output->arg_columns); - } - - $query = sprintf("select %s from %s %s %s %s", $columns, implode(',', $table_list), implode(' ', $left_join), $condition, $groupby_query.$orderby_query); - $query .= (__DEBUG_QUERY__&1 && $output->query_id)?sprintf(' '.$this->comment_syntax,$this->query_id):''; - $result = $this->_query($query); - if ($this->isError()) - return; - - if(count($click_count)>0 && count($output->conditions)>0){ - $_query = ''; - foreach($click_count as $k => $c) $_query .= sprintf(',%s=%s+1 ',$c,$c); - $_query = sprintf('update %s set %s %s',implode(',',$table_list), substr($_query,1), $condition); - $this->_query($_query); - } - - - $data = $this->_fetch($result); - - $buff = new Object(); - $buff->data = $data; - return $buff; + return $buff; } - /** - * @brief query xml에 navigation 정보가 있을 경우 페이징 관련 작업을 처리한다 - * - * 그닥 좋지는 않은 구조이지만 편리하다.. -_-; - **/ - function _getNavigationData($table_list, $columns, $left_join, $condition, $output) - { - require_once (_XE_PATH_ . 'classes/page/PageHandler.class.php'); - - $column_list = $output->column_list; - /* - // group by 절이 포함된 SELECT 쿼리의 전체 갯수를 구하기 위한 수정 - // 정상적인 동작이 확인되면 주석으로 막아둔 부분으로 대체합니다. - // - $count_condition = count($output->groups) ? sprintf('%s group by %s', $condition, implode(', ', $output->groups)) : $condition; - $total_count = $this->getCountCache($output->tables, $count_condition); - if ($total_count === false) { - $count_query = sprintf('select count(*) as count from %s %s %s', implode(', ', $table_list), implode(' ', $left_join), $count_condition); - if (count($output->groups)) - $count_query = sprintf('select count(*) as count from (%s) xet', $count_query); - $result = $this->_query($count_query); - $count_output = $this->_fetch($result); - $total_count = (int)$count_output->count; - $this->putCountCache($output->tables, $count_condition, $total_count); - } - */ - - // 전체 개수를 구함 - $count_query = sprintf("select count(*) as count from %s %s %s", implode(',', $table_list), implode(' ', $left_join), $condition); - $count_query .= (__DEBUG_QUERY__&1 && $output->query_id)?sprintf(' '.$this->comment_syntax,$this->query_id . ' count(*)'):''; - $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; - - // list_order, update_order 로 정렬시에 인덱스 사용을 위해 condition에 쿼리 추가 - if ($output->order) { - $conditions = $this->getConditionList($output); - if (!in_array('list_order', $conditions) && !in_array('update_order', $conditions)) { - foreach ($output->order as $key => $val) { - $col = $val[0]; - if (!in_array($col, array('list_order', 'update_order'))) - continue; - if ($condition) - $condition .= sprintf(' and %s < 2100000000 ', $col); - else - $condition = sprintf(' where %s < 2100000000 ', $col); - } - } - } - - if (count($output->groups)) { - /* - var_dump("= column output start = "); - var_dump(sizeof ($output->columns) . " = end length == "); - var_dump($output->columns); - var_dump("= column output end = " . "\n"); - var_dump($output->groups); - var_dump("=== " . "\n"); - var_dump(debug_backtrace()); - - foreach($output->columns as $key => $val) { - $name = $val['name']; - $alias = $val['alias']; - } */ - $group_list = array(); - foreach ($output->groups as $gkey => $gval) { - foreach ($output->columns as $key => $val) { - $name = $val['name']; - $alias = $val['alias']; - if (trim($name) == trim($gval)) { - $group_list[] = $alias; - break; - } - } - - if($column_list[$gval]) $output->arg_columns[] = $column_list[$gval]; - - } - $groupby_query = sprintf(' group by %s', implode(',', $group_list)); - // var_dump($query); - } - - if ($output->order) { - foreach ($output->order as $key => $val) { - $index_list[] = sprintf('%s %s', $val[0], $val[1]); - if(count($output->arg_columns) && $column_list[$val[0]]) $output->arg_columns[] = $column_list[$val[0]]; - } - if (count($index_list)) $orderby_query = ' order by ' . implode(',', $index_list); - } - - if(count($output->arg_columns)) - { - $columns = join(',',$output->arg_columns); - } - - $query = sprintf("select %s from %s %s %s", $columns, implode(',', $table_list), implode(' ', $left_join), $condition); - $query = sprintf('%s offset %d limit %d', $query, $start_count, $list_count); - $query .= (__DEBUG_QUERY__&1 && $output->query_id)?sprintf(' '.$this->comment_syntax,$this->query_id):''; - - $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 = pg_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; + function getParser(){ + return new DBParser('"', '"', $this->prefix); } } diff --git a/classes/db/DBSqlite2.class.php b/classes/db/DBSqlite2.class.php index 51a5664ab..01e607f25 100644 --- a/classes/db/DBSqlite2.class.php +++ b/classes/db/DBSqlite2.class.php @@ -1,739 +1,718 @@ - 'INTEGER', - 'number' => 'INTEGER', - 'varchar' => 'VARCHAR', - 'char' => 'CHAR', - 'text' => 'TEXT', - 'bigtext' => 'TEXT', - 'date' => 'VARCHAR(14)', - 'float' => 'FLOAT', - ); - - /** - * @brief constructor - **/ - function DBSqlite2() { - $this->_setDBInfo(); - $this->_connect(); - } - - /** - * @brief create an instance of this class - */ - function create() - { - return new DBSqlite2; - } - - /** - * @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; - $this->password = md5($this->password); - } - - /** - * @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(version_compare(PHP_VERSION, "5.9.0", "<") && 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); - if($sequence % 10000 == 0) { - $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 제거 - **/ - function dropColumn($table_name, $column_name) { - $query = sprintf("alter table %s%s drop column %s ", $this->prefix, $table_name, $column_name); - $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 특정 테이블에 특정 인덱스 추가 - * $target_columns = array(col1, col2) - * $is_unique? unique : none - **/ - function addIndex($table_name, $index_name, $target_columns, $is_unique = false) { - if(!is_array($target_columns)) $target_columns = array($target_columns); - - $key_name = sprintf('%s%s_%s', $this->prefix, $table_name, $index_name); - $query = sprintf("pragma table_info(%s%s)", $this->prefix, $table_name); - - $query = sprintf('CREATE %s INDEX %s ON %s%s (%s)', $is_unique?'UNIQUE':'', $key_name, $this->prefix, $table_name, implode(',',$target_columns)); - return $this->_query($query); - } - - /** - * @brief 특정 테이블의 특정 인덱스 삭제 - **/ - function dropIndex($table_name, $index_name, $is_unique = false) { - $key_name = sprintf('%s%s_%s', $this->prefix, $table_name, $index_name); - $query = sprintf("DROP INDEX %s", $this->prefix, $table_name, $key_name); - $this->_query($query); - } - - /** - * @brief 특정 테이블의 index 정보를 return - **/ - function isIndexExists($table_name, $index_name) { - $key_name = sprintf('%s%s_%s', $this->prefix, $table_name, $index_name); - $query = sprintf("pragma index_info(%s)", $key_name); - $result = $this->_query($query); - $output = $this->_fetch($result); - if(!$output) return false; - return true; - } - - /** - * @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':'', - isset($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; - $condition = $this->_getCondition($output->conditions,$output->column_type); - if($condition) $condition = ' where '.$condition; - return $condition; - } - - function getLeftCondition($conditions,$column_type){ - return $this->_getCondition($conditions,$column_type); - } - - - function _getCondition($conditions,$column_type) { - $condition = ''; - foreach($conditions as $val) { - $sub_condition = ''; - foreach($val['condition'] as $v) { - if(!isset($v['value'])) continue; - if($v['value'] === '') continue; - if(!in_array(gettype($v['value']), array('string', 'integer', 'double'))) continue; - - $name = $v['column']; - $operation = $v['operation']; - $value = $v['value']; - $type = $this->getColumnType($column_type,$name); - $pipe = $v['pipe']; - - $value = $this->getConditionValue($name, $value, $operation, $type, $column_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.')'; - } - } - return $condition; - } - - /** - * @brief insertAct 처리 - **/ - function _executeInsertAct($output) { - // 테이블 정리 - foreach($output->tables as $key => $val) { - $table_list[] = $this->prefix.$val; - } - - // 컬럼 정리 - 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'; - } - // sql injection 문제로 xml 선언이 number인 경우이면서 넘어온 값이 숫자형이 아니면 숫자형으로 강제 형변환 - // elseif(!$value || is_numeric($value)) $value = (int)$value; - else $this->_filterNumber(&$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_values($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)."'"; - // sql injection 문제로 xml 선언이 number인 경우이면서 넘어온 값이 숫자형이 아니면 숫자형으로 강제 형변환 - else $this->_filterNumber(&$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.$key; - } - 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.$val; - } - - // 조건절 정리 - $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.$val.' as '.$key; - } - - $left_join = array(); - // why??? - $left_tables= (array)$output->left_tables; - - foreach($left_tables as $key => $val) { - $condition = $this->_getCondition($output->left_conditions[$key],$output->column_type); - if($condition){ - $left_join[] = $val . ' '.$this->prefix.$output->_tables[$key].' as '.$key . ' on ' . $condition . ''; - } - } - - if(!$output->columns) { - $columns = '*'; - } else { - $column_list = array(); - foreach($output->columns as $key => $val) { - $name = $val['name']; - $alias = $val['alias']; - if($val['click_count']) $click_count[] = $val['name']; - - if(substr($name,-1) == '*') { - $column_list[] = $name; - } 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); - - $output->column_list = $column_list; - if($output->list_count && $output->page) return $this->_getNavigationData($table_list, $columns, $left_join, $condition, $output); - - // list_order, update_order 로 정렬시에 인덱스 사용을 위해 condition에 쿼리 추가 - if($output->order) { - $conditions = $this->getConditionList($output); - if(!in_array('list_order', $conditions) && !in_array('update_order', $conditions)) { - foreach($output->order as $key => $val) { - $col = $val[0]; - if(!in_array($col, array('list_order','update_order'))) continue; - if($condition) $condition .= sprintf(' and %s < 2100000000 ', $col); - else $condition = sprintf(' where %s < 2100000000 ', $col); - } - } - } - - $query = sprintf("select %s from %s %s %s", $columns, implode(',',$table_list),implode(' ',$left_join), $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); - } - - // list_count를 사용할 경우 적용 - if($output->list_count['value']) $query = sprintf('%s limit %d', $query, $output->list_count['value']); - - $query .= (__DEBUG_QUERY__&1 && $output->query_id)?sprintf(' '.$this->comment_syntax,$this->query_id):''; - $result = $this->_query($query); - if($this->isError()) return; - - if(count($click_count)>0 && count($output->conditions)>0){ - $_query = ''; - foreach($click_count as $k => $c) $_query .= sprintf(',%s=%s+1 ',$c,$c); - $_query = sprintf('update %s set %s %s',implode(',',$table_list), substr($_query,1), $condition); - $this->_query($_query); - } - - $data = $this->_fetch($result); - - $buff = new Object(); - $buff->data = $data; - return $buff; - } - - /** - * @brief query xml에 navigation 정보가 있을 경우 페이징 관련 작업을 처리한다 - * - * 그닥 좋지는 않은 구조이지만 편리하다.. -_-; - **/ - function _getNavigationData($table_list, $columns, $left_join, $condition, $output) { - require_once(_XE_PATH_.'classes/page/PageHandler.class.php'); - - $column_list = $output->column_list; - /* - // group by 절이 포함된 SELECT 쿼리의 전체 갯수를 구하기 위한 수정 - // 정상적인 동작이 확인되면 주석으로 막아둔 부분으로 대체합니다. - // - $count_condition = count($output->groups) ? sprintf('%s group by %s', $condition, implode(', ', $output->groups)) : $condition; - $total_count = $this->getCountCache($output->tables, $count_condition); - if($total_count === false) { - $count_query = sprintf("select count(*) as count from %s %s %s", implode(', ', $table_list), implode(' ', $left_join), $count_condition); - if (count($output->groups)) - $count_query = sprintf('select count(*) as count from (%s) xet', $count_query); - $result = $this->_query($count_query); - $count_output = $this->_fetch($result); - $total_count = (int)$count_output->count; - $this->putCountCache($output->tables, $count_condition, $total_count); - } - */ - - // 전체 개수를 구함 - $count_query = sprintf("select count(*) as count from %s %s %s", implode(',',$table_list),implode(' ',$left_join), $condition); - $count_query .= (__DEBUG_QUERY__&1 && $output->query_id)?sprintf(' '.$this->comment_syntax,$this->query_id . ' count(*)'):''; - $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; - - // list_order, update_order 로 정렬시에 인덱스 사용을 위해 condition에 쿼리 추가 - if($output->order) { - $conditions = $this->getConditionList($output); - if(!in_array('list_order', $conditions) && !in_array('update_order', $conditions)) { - foreach($output->order as $key => $val) { - $col = $val[0]; - if(!in_array($col, array('list_order','update_order'))) continue; - if($condition) $condition .= sprintf(' and %s < 2100000000 ', $col); - else $condition = sprintf(' where %s < 2100000000 ', $col); - } - } - } - - $query = sprintf("select %s from %s %s %s", $columns, implode(',',$table_list), implode(' ',$left_join), $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); - $query .= (__DEBUG_QUERY__&1 && $output->query_id)?sprintf(' '.$this->comment_syntax,$this->query_id):''; - - $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; - } - } - -return new DBSqlite2; -?> + 'INTEGER', + 'number' => 'INTEGER', + 'varchar' => 'VARCHAR', + 'char' => 'CHAR', + 'text' => 'TEXT', + 'bigtext' => 'TEXT', + 'date' => 'VARCHAR(14)', + 'float' => 'FLOAT', + ); + + /** + * @brief constructor + **/ + function DBSqlite2() { + $this->_setDBInfo(); + $this->_connect(); + } + + /** + * @brief create an instance of this class + */ + function create() + { + return new DBSqlite2; + } + + /** + * @brief Return if it is installable + **/ + function isSupported() { + if(!function_exists('sqlite_open')) return false; + return true; + } + + /** + * @brief DB settings and 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 Connection + **/ + function _connect() { + // Ignore if no DB information exists + if(!$this->database) return; + // Attempt to access the database file + $this->fd = sqlite_open($this->database, 0666, $error); + if(!file_exists($this->database) || $error) { + $this->setError(-1,$error); + $this->is_connected = false; + return; + } + // Check connections + $this->is_connected = true; + $this->password = md5($this->password); + } + + /** + * @brief DB disconnection + **/ + function close() { + if(!$this->isConnected()) return; + sqlite_close($this->fd); + } + + /** + * @brief Begin transaction + **/ + function begin() { + if(!$this->is_connected || $this->transaction_started) return; + if($this->_query("BEGIN;")) $this->transaction_started = true; + } + + /** + * @brief Rollback + **/ + function rollback() { + if(!$this->is_connected || !$this->transaction_started) return; + $this->_query("ROLLBACK;"); + $this->transaction_started = false; + } + + /** + * @brief Commits + **/ + 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 Add quotes on the string variables in a query + **/ + function addQuotes($string) { + if(version_compare(PHP_VERSION, "5.9.0", "<") && get_magic_quotes_gpc()) $string = stripslashes(str_replace("\\","\\\\",$string)); + if(!is_numeric($string)) $string = str_replace("'","''", $string); + return $string; + } + + /** + * @brief : Run a query and fetch the result + * + * query: run a query and return the result \n + * fetch: NULL if no value is returned \n + * array object if rows are returned \n + * object if a row is returned \n + * return\n + **/ + function _query($query) { + if(!$this->isConnected()) return; + // Notify to start a query execution + $this->actStart($query); + // Run the query statement + $result = @sqlite_query($query, $this->fd); + // Error Check + if(sqlite_last_error($this->fd)) $this->setError(sqlite_last_error($this->fd), sqlite_error_string(sqlite_last_error($this->fd))); + // Notify to complete a query execution + $this->actFinish(); + + return $result; + } + + /** + * @brief Fetch results + **/ + 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 Return the sequence value is incremented by 1 + **/ + function getNextSequence() { + $query = sprintf("insert into %ssequence (seq) values ('')", $this->prefix); + $this->_query($query); + $sequence = sqlite_last_insert_rowid($this->fd); + if($sequence % 10000 == 0) { + $query = sprintf("delete from %ssequence where seq < %d", $this->prefix, $sequence); + $this->_query($query); + } + + return $sequence; + } + + /** + * @brief Return if a table already exists + **/ + 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 Add a column to a table + **/ + 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 Delete a column from a table + **/ + function dropColumn($table_name, $column_name) { + $query = sprintf("alter table %s%s drop column %s ", $this->prefix, $table_name, $column_name); + $this->_query($query); + } + + /** + * @brief Return column information of a table + **/ + 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 Add an index to a table + * $target_columns = array(col1, col2) + * $is_unique? unique : none + **/ + function addIndex($table_name, $index_name, $target_columns, $is_unique = false) { + if(!is_array($target_columns)) $target_columns = array($target_columns); + + $key_name = sprintf('%s%s_%s', $this->prefix, $table_name, $index_name); + $query = sprintf("pragma table_info(%s%s)", $this->prefix, $table_name); + + $query = sprintf('CREATE %s INDEX %s ON %s%s (%s)', $is_unique?'UNIQUE':'', $key_name, $this->prefix, $table_name, implode(',',$target_columns)); + return $this->_query($query); + } + + /** + * @brief Drop an index from a table + **/ + function dropIndex($table_name, $index_name, $is_unique = false) { + $key_name = sprintf('%s%s_%s', $this->prefix, $table_name, $index_name); + $query = sprintf("DROP INDEX %s", $this->prefix, $table_name, $key_name); + $this->_query($query); + } + + /** + * @brief Return index information of a table + **/ + function isIndexExists($table_name, $index_name) { + $key_name = sprintf('%s%s_%s', $this->prefix, $table_name, $index_name); + $query = sprintf("pragma index_info(%s)", $key_name); + $result = $this->_query($query); + $output = $this->_fetch($result); + if(!$output) return false; + return true; + } + + /** + * @brief Create a table by using xml file + **/ + function createTableByXml($xml_doc) { + return $this->_createTable($xml_doc); + } + + /** + * @brief Create a table by using xml file + **/ + function createTableByXmlFile($file_name) { + if(!file_exists($file_name)) return; + // read xml file + $buff = FileHandler::readFile($file_name); + return $this->_createTable($buff); + } + + /** + * @brief generate a query statement to create a table by using schema xml + * + * 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); + // Create a table 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':'', + isset($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 conditional clause + **/ + function getCondition($output) { + if(!$output->conditions) return; + $condition = $this->_getCondition($output->conditions,$output->column_type); + if($condition) $condition = ' where '.$condition; + return $condition; + } + + function getLeftCondition($conditions,$column_type){ + return $this->_getCondition($conditions,$column_type); + } + + + function _getCondition($conditions,$column_type) { + $condition = ''; + foreach($conditions as $val) { + $sub_condition = ''; + foreach($val['condition'] as $v) { + if(!isset($v['value'])) continue; + if($v['value'] === '') continue; + if(!in_array(gettype($v['value']), array('string', 'integer', 'double'))) continue; + + $name = $v['column']; + $operation = $v['operation']; + $value = $v['value']; + $type = $this->getColumnType($column_type,$name); + $pipe = $v['pipe']; + + $value = $this->getConditionValue($name, $value, $operation, $type, $column_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.')'; + } + } + return $condition; + } + + /** + * @brief Handle the insertAct + **/ + function _executeInsertAct($output) { + // List tables + foreach($output->tables as $key => $val) { + $table_list[] = $this->prefix.$val; + } + // List columns + 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'; + } + // sql injection 문제로 xml 선언이 number인 경우이면서 넘어온 값이 숫자형이 아니면 숫자형으로 강제 형변환 + // elseif(!$value || is_numeric($value)) $value = (int)$value; + else $this->_filterNumber(&$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 Handle updateAct + **/ + function _executeUpdateAct($output) { + $table_count = count(array_values($output->tables)); + // If one day the destination table + if($table_count == 1) { + // List tables + list($target_table) = array_values($output->tables); + $target_table = $this->prefix.$target_table; + // List columns + 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)."'"; + // sql injection 문제로 xml 선언이 number인 경우이면서 넘어온 값이 숫자형이 아니면 숫자형으로 강제 형변환 + else $this->_filterNumber(&$value); + + $column_list[] = sprintf("%s = %s", $name, $value); + } + } + // List the conditional clause + $condition = $this->getCondition($output); + + $query = sprintf("update %s set %s %s", $target_table, implode(',',$column_list), $condition); + // trick to handle if targt table to update is more than one (sqlite doesn't support update to multi-tables) + } elseif($table_count == 2) { + // List tables + foreach($output->tables as $key => $val) { + $table_list[$val] = $this->prefix.$key; + } + list($source_table, $target_table) = array_values($table_list); + // List the conditional clause + $condition = $this->getCondition($output); + foreach($table_list as $key => $val) { + $condition = eregi_replace($key.'\\.', $val.'.', $condition); + } + // List columns + 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 Handle deleteAct + **/ + function _executeDeleteAct($output) { + // List tables + foreach($output->tables as $key => $val) { + $table_list[] = $this->prefix.$val; + } + // List the conditional clause + $condition = $this->getCondition($output); + + $query = sprintf("delete from %s %s", implode(',',$table_list), $condition); + + return $this->_query($query); + } + + /** + * @brief Handle selectAct + * + * In order to get a list of pages easily when selecting \n + * it supports a method as navigation + **/ + function _executeSelectAct($output) { + // List tables + $table_list = array(); + foreach($output->tables as $key => $val) { + $table_list[] = $this->prefix.$val.' as '.$key; + } + + $left_join = array(); + // why??? + $left_tables= (array)$output->left_tables; + + foreach($left_tables as $key => $val) { + $condition = $this->_getCondition($output->left_conditions[$key],$output->column_type); + if($condition){ + $left_join[] = $val . ' '.$this->prefix.$output->_tables[$key].' as '.$key . ' on ' . $condition . ''; + } + } + + if(!$output->columns) { + $columns = '*'; + } else { + $column_list = array(); + foreach($output->columns as $key => $val) { + $name = $val['name']; + $alias = $val['alias']; + if($val['click_count']) $click_count[] = $val['name']; + + if(substr($name,-1) == '*') { + $column_list[] = $name; + } 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); + + $output->column_list = $column_list; + if($output->list_count && $output->page) return $this->_getNavigationData($table_list, $columns, $left_join, $condition, $output); + // Add a condition to use an index when sorting in order by list_order, update_order + if($output->order) { + $conditions = $this->getConditionList($output); + if(!in_array('list_order', $conditions) && !in_array('update_order', $conditions)) { + foreach($output->order as $key => $val) { + $col = $val[0]; + if(!in_array($col, array('list_order','update_order'))) continue; + if($condition) $condition .= sprintf(' and %s < 2100000000 ', $col); + else $condition = sprintf(' where %s < 2100000000 ', $col); + } + } + } + + $query = sprintf("select %s from %s %s %s", $columns, implode(',',$table_list),implode(' ',$left_join), $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); + } + // Apply when using list_count + if($output->list_count['value']) $query = sprintf('%s limit %d', $query, $output->list_count['value']); + + $query .= (__DEBUG_QUERY__&1 && $output->query_id)?sprintf(' '.$this->comment_syntax,$this->query_id):''; + $result = $this->_query($query); + if($this->isError()) return; + + if(count($click_count)>0 && count($output->conditions)>0){ + $_query = ''; + foreach($click_count as $k => $c) $_query .= sprintf(',%s=%s+1 ',$c,$c); + $_query = sprintf('update %s set %s %s',implode(',',$table_list), substr($_query,1), $condition); + $this->_query($_query); + } + + $data = $this->_fetch($result); + + $buff = new Object(); + $buff->data = $data; + return $buff; + } + + /** + * @brief Paging is handled if navigation information exists in the query xml + * + * It is quite convenient although its structure is not good at all .. -_-; + **/ + function _getNavigationData($table_list, $columns, $left_join, $condition, $output) { + require_once(_XE_PATH_.'classes/page/PageHandler.class.php'); + + $column_list = $output->column_list; + /* + // Modified to find total number of SELECT queries having group by clause + // If it works correctly, uncomment the following codes + // + $count_condition = count($output->groups) ? sprintf('%s group by %s', $condition, implode(', ', $output->groups)) : $condition; + $total_count = $this->getCountCache($output->tables, $count_condition); + if($total_count === false) { + $count_query = sprintf("select count(*) as count from %s %s %s", implode(', ', $table_list), implode(' ', $left_join), $count_condition); + if (count($output->groups)) + $count_query = sprintf('select count(*) as count from (%s) xet', $count_query); + $result = $this->_query($count_query); + $count_output = $this->_fetch($result); + $total_count = (int)$count_output->count; + $this->putCountCache($output->tables, $count_condition, $total_count); + } + */ + // Get a total count + $count_query = sprintf("select count(*) as count from %s %s %s", implode(',',$table_list),implode(' ',$left_join), $condition); + $count_query .= (__DEBUG_QUERY__&1 && $output->query_id)?sprintf(' '.$this->comment_syntax,$this->query_id . ' count(*)'):''; + $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; + // Get a total page + if($total_count) $total_page = (int)( ($total_count-1) / $list_count) + 1; + else $total_page = 1; + // Check Page variables + if($page > $total_page) $page = $total_page; + $start_count = ($page-1)*$list_count; + // Add a condition to use an index when sorting in order by list_order, update_order + if($output->order) { + $conditions = $this->getConditionList($output); + if(!in_array('list_order', $conditions) && !in_array('update_order', $conditions)) { + foreach($output->order as $key => $val) { + $col = $val[0]; + if(!in_array($col, array('list_order','update_order'))) continue; + if($condition) $condition .= sprintf(' and %s < 2100000000 ', $col); + else $condition = sprintf(' where %s < 2100000000 ', $col); + } + } + } + + $query = sprintf("select %s from %s %s %s", $columns, implode(',',$table_list), implode(' ',$left_join), $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); + $query .= (__DEBUG_QUERY__&1 && $output->query_id)?sprintf(' '.$this->comment_syntax,$this->query_id):''; + + $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; + } + } + +return new DBSqlite2; +?> diff --git a/classes/db/DBSqlite3_pdo.class.php b/classes/db/DBSqlite3_pdo.class.php index 96301a1a2..e8e7e1d0f 100644 --- a/classes/db/DBSqlite3_pdo.class.php +++ b/classes/db/DBSqlite3_pdo.class.php @@ -1,819 +1,628 @@ - 'INTEGER', - 'number' => 'INTEGER', - 'varchar' => 'VARCHAR', - 'char' => 'CHAR', - 'text' => 'TEXT', - 'bigtext' => 'TEXT', - 'date' => 'VARCHAR(14)', - 'float' => 'REAL', - ); - - /** - * @brief constructor - **/ - function DBSqlite3_pdo() { - $this->_setDBInfo(); - $this->_connect(); - } - - /** - * @brief create an instance of this class - */ - function create() - { - return new DBSqlite3_pdo; - } - - /** - * @brief 설치 가능 여부를 return - **/ - function isSupported() { - return class_exists('PDO'); - } - - /** - * @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; - - // 데이터 베이스 파일 접속 시도 - try { - // PDO is only supported with PHP5, - // so it is allowed to use try~catch statment in this class. - $this->handler = new PDO('sqlite:'.$this->database); - } catch (PDOException $e) { - $this->setError(-1, 'Connection failed: '.$e->getMessage()); - $this->is_connected = false; - return; - } - - // 접속체크 - $this->is_connected = true; - $this->password = md5($this->password); - } - - /** - * @brief DB접속 해제 - **/ - function close() { - if(!$this->is_connected) return; - $this->commit(); - } - - /** - * @brief 트랜잭션 시작 - **/ - function begin() { - if(!$this->is_connected || $this->transaction_started) return; - if($this->handler->beginTransaction()) $this->transaction_started = true; - } - - /** - * @brief 롤백 - **/ - function rollback() { - if(!$this->is_connected || !$this->transaction_started) return; - $this->handler->rollBack(); - $this->transaction_started = false; - } - - /** - * @brief 커밋 - **/ - function commit($force = false) { - if(!$force && (!$this->is_connected || !$this->transaction_started)) return; - $this->handler->commit(); - $this->transaction_started = false; - } - - /** - * @brief 쿼리에서 입력되는 문자열 변수들의 quotation 조절 - **/ - function addQuotes($string) { - if(version_compare(PHP_VERSION, "5.9.0", "<") && 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->is_connected) 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->is_connected || !$this->stmt) return; - - $this->bind_idx ++; - $this->bind_vars[] = $val; - $this->stmt->bindParam($this->bind_idx, $val); - } - - /** - * @brief : prepare된 쿼리의 execute - **/ - function _execute() { - if(!$this->is_connected || !$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(); - if($sequence % 10000 == 0) { - $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 제거 - **/ - function dropColumn($table_name, $column_name) { - $query = sprintf("alter table %s%s drop column %s ", $this->prefix, $table_name, $column_name); - $this->_query($query); - } - - /** - * @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 특정 테이블에 특정 인덱스 추가 - * $target_columns = array(col1, col2) - * $is_unique? unique : none - **/ - function addIndex($table_name, $index_name, $target_columns, $is_unique = false) { - if(!is_array($target_columns)) $target_columns = array($target_columns); - - $key_name = sprintf('%s%s_%s', $this->prefix, $table_name, $index_name); - - $query = sprintf('CREATE %s INDEX %s ON %s%s (%s)', $is_unique?'UNIQUE':'', $key_name, $this->prefix, $table_name, implode(',',$target_columns)); - $this->_prepare($query); - $this->_execute(); - } - - /** - * @brief 특정 테이블의 특정 인덱스 삭제 - **/ - function dropIndex($table_name, $index_name, $is_unique = false) { - $key_name = sprintf('%s%s_%s', $this->prefix, $table_name, $index_name); - $query = sprintf("DROP INDEX %s", $this->prefix, $table_name, $key_name); - $this->_query($query); - } - - /** - * @brief 특정 테이블의 index 정보를 return - **/ - function isIndexExists($table_name, $index_name) { - $key_name = sprintf('%s%s_%s', $this->prefix, $table_name, $index_name); - - $query = sprintf("pragma index_info(%s)", $key_name); - $this->_prepare($query); - $output = $this->_execute(); - if(!$output) return false; - return true; - } - - /** - * @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':'', - isset($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; - $condition = $this->_getCondition($output->conditions,$output->column_type); - if($condition) $condition = ' where '.$condition; - return $condition; - } - - function getLeftCondition($conditions,$column_type){ - return $this->_getCondition($conditions,$column_type); - } - - - function _getCondition($conditions,$column_type) { - $condition = ''; - foreach($conditions as $val) { - $sub_condition = ''; - foreach($val['condition'] as $v) { - if(!isset($v['value'])) continue; - if($v['value'] === '') continue; - if(!in_array(gettype($v['value']), array('string', 'integer', 'double', 'array'))) continue; - - $name = $v['column']; - $operation = $v['operation']; - $value = $v['value']; - $type = $this->getColumnType($column_type,$name); - $pipe = $v['pipe']; - - $value = $this->getConditionValue($name, $value, $operation, $type, $column_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.')'; - } - } - return $condition; - } - - /** - * @brief insertAct 처리 - **/ - function _executeInsertAct($output) { - // 테이블 정리 - foreach($output->tables as $key => $val) { - $table_list[] = $this->prefix.$val; - } - - // 컬럼 정리 - 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 { - $this->_filterNumber(&$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_values($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)."'"; - else $this->_filterNumber(&$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.$key; - } - 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.$val; - } - - // 조건절 정리 - $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.$val.' as '.$key; - } - - $left_join = array(); - // why??? - $left_tables= (array)$output->left_tables; - - foreach($left_tables as $key => $val) { - $condition = $this->_getCondition($output->left_conditions[$key],$output->column_type); - if($condition){ - $left_join[] = $val . ' '.$this->prefix.$output->_tables[$key].' as '.$key . ' on (' . $condition . ')'; - } - } - - - $click_count = array(); - if(!$output->columns){ - $output->columns = array(array('name'=>'*')); - } - - $column_list = array(); - foreach($output->columns as $key => $val) { - $name = $val['name']; - $alias = $val['alias']; - if($val['click_count']) $click_count[] = $val['name']; - - if(substr($name,-1) == '*') { - $column_list[] = $name; - } elseif(strpos($name,'.')===false && strpos($name,'(')===false) { - if($alias) $column_list[$alias] = sprintf('%s as %s', $name, $alias); - else $column_list[] = sprintf('%s',$name); - } else { - if($alias) $column_list[$alias] = sprintf('%s as %s', $name, $alias); - else $column_list[] = sprintf('%s',$name); - } - } - $columns = implode(',',$column_list); - - $condition = $this->getCondition($output); - - $output->column_list = $column_list; - if($output->list_count && $output->page) return $this->_getNavigationData($table_list, $columns, $left_join, $condition, $output); - - // list_order, update_order 로 정렬시에 인덱스 사용을 위해 condition에 쿼리 추가 - if($output->order) { - $conditions = $this->getConditionList($output); - if(!in_array('list_order', $conditions) && !in_array('update_order', $conditions)) { - foreach($output->order as $key => $val) { - $col = $val[0]; - if(!in_array($col, array('list_order','update_order'))) continue; - if($condition) $condition .= sprintf(' and %s < 2100000000 ', $col); - else $condition = sprintf(' where %s < 2100000000 ', $col); - } - } - } - - if(count($output->groups)){ - $groupby_query = sprintf(' group by %s', implode(',',$output->groups)); - if(count($output->arg_columns)) - { - foreach($output->groups as $group) - { - if($column_list[$group]) $output->arg_columns[] = $column_list[$group]; - } - } - } - - if($output->order) { - foreach($output->order as $key => $val) { - $index_list[] = sprintf('%s %s', $val[0], $val[1]); - if(count($output->arg_columns) && $column_list[$val[0]]) $output->arg_columns[] = $column_list[$val[0]]; - } - if(count($index_list)) $orderby_query = ' order by '.implode(',',$index_list); - } - - if(count($output->arg_columns)) - { - $columns = join(',',$output->arg_columns); - } - - $query = sprintf("select %s from %s %s %s %s", $columns, implode(',',$table_list),implode(' ',$left_join), $condition, $groupby_query.$orderby_query); - // list_count를 사용할 경우 적용 - if($output->list_count['value']) $query = sprintf('%s limit %d', $query, $output->list_count['value']); - - $query .= (__DEBUG_QUERY__&1 && $output->query_id)?sprintf(' '.$this->comment_syntax,$this->query_id):''; - $this->_prepare($query); - $data = $this->_execute(); - if($this->isError()) return; - - if(count($click_count)>0 && count($output->conditions)>0){ - $_query = ''; - foreach($click_count as $k => $c) $_query .= sprintf(',%s=%s+1 ',$c,$c); - $_query = sprintf('update %s set %s %s',implode(',',$table_list), substr($_query,1), $condition); - $this->_query($_query); - } - - $buff = new Object(); - $buff->data = $data; - return $buff; - } - - /** - * @brief query xml에 navigation 정보가 있을 경우 페이징 관련 작업을 처리한다 - * - * 그닥 좋지는 않은 구조이지만 편리하다.. -_-; - **/ - function _getNavigationData($table_list, $columns, $left_join, $condition, $output) { - require_once(_XE_PATH_.'classes/page/PageHandler.class.php'); - - $column_list = $output->column_list; - /* - // group by 절이 포함된 SELECT 쿼리의 전체 갯수를 구하기 위한 수정 - // 정상적인 동작이 확인되면 주석으로 막아둔 부분으로 대체합니다. - // - $count_condition = count($output->groups) ? sprintf('%s group by %s', $condition, implode(', ', $output->groups)) : $condition; - $total_count = $this->getCountCache($output->tables, $count_condition); - if($total_count === false) { - $count_query = sprintf("select count(*) as count from %s %s %s", implode(', ', $table_list), implode(' ', $left_join), $count_condition); - if (count($output->groups)) - $count_query = sprintf('select count(*) as count from (%s) xet', $count_query); - $result = $this->_query($count_query); - $count_output = $this->_fetch($result); - $total_count = (int)$count_output->count; - $this->putCountCache($output->tables, $count_condition, $total_count); - } - */ - - // 전체 개수를 구함 - $count_query = sprintf("select count(*) as count from %s %s %s", implode(',',$table_list),implode(' ',$left_join), $condition); - $count_query .= (__DEBUG_QUERY__&1 && $output->query_id)?sprintf(' '.$this->comment_syntax,$this->query_id . ' count(*)'):''; - $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; - - // list_order, update_order 로 정렬시에 인덱스 사용을 위해 condition에 쿼리 추가 - if($output->order) { - $conditions = $this->getConditionList($output); - if(!in_array('list_order', $conditions) && !in_array('update_order', $conditions)) { - foreach($output->order as $key => $val) { - $col = $val[0]; - if(!in_array($col, array('list_order','update_order'))) continue; - if($condition) $condition .= sprintf(' and %s < 2100000000 ', $col); - else $condition = sprintf(' where %s < 2100000000 ', $col); - } - } - } - - if(count($output->groups)){ - $groupby_query = sprintf(' group by %s', implode(',',$output->groups)); - if(count($output->arg_columns)) - { - foreach($output->groups as $group) - { - if($column_list[$group]) $output->arg_columns[] = $column_list[$group]; - } - } - } - - if($output->order) { - foreach($output->order as $key => $val) { - $index_list[] = sprintf('%s %s', $val[0], $val[1]); - if(count($output->arg_columns) && $column_list[$val[0]]) $output->arg_columns[] = $column_list[$val[0]]; - } - if(count($index_list)) $orderby_query = ' order by '.implode(',',$index_list); - } - - if(count($output->arg_columns)) - { - $columns = join(',',$output->arg_columns); - } - - // 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("select %s from %s %s %s %s", $columns, implode(',',$table_list),implode(' ',$left_join), $condition, $groupby_query.$orderby_query); - $query = sprintf('%s limit %d, %d', $query, $start_count, $list_count); - $query .= (__DEBUG_QUERY__&1 && $output->query_id)?sprintf(' '.$this->comment_syntax,$this->query_id):''; - - $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; - } - } - -return new DBSqlite3_pdo; -?> + 'INTEGER', + 'number' => 'INTEGER', + 'varchar' => 'VARCHAR', + 'char' => 'CHAR', + 'text' => 'TEXT', + 'bigtext' => 'TEXT', + 'date' => 'VARCHAR(14)', + 'float' => 'REAL', + ); + + /** + * @brief constructor + **/ + function DBSqlite3_pdo() { + $this->_setDBInfo(); + $this->_connect(); + } + + /** + * @brief create an instance of this class + */ + function create() + { + return new DBSqlite3_pdo; + } + + /** + * @brief Return if installable + **/ + function isSupported() { + return class_exists('PDO'); + } + + function isConnected() { + return $this->is_connected; + } + + /** + * @brief DB settings and connect/close + **/ + function _setDBInfo() { + $db_info = Context::getDBInfo(); + $this->database = $db_info->master_db["db_database"]; + $this->prefix = $db_info->master_db["db_table_prefix"]; + //if(!substr($this->prefix,-1)!='_') $this->prefix .= '_'; + } + + /** + * @brief DB Connection + **/ + function _connect() { + // override if db information not exists + if(!$this->database) return; + + // Attempt to access the database file + try { + // PDO is only supported with PHP5, + // so it is allowed to use try~catch statment in this class. + $this->handler = new PDO('sqlite:'.$this->database); + } catch (PDOException $e) { + $this->setError(-1, 'Connection failed: '.$e->getMessage()); + $this->is_connected = false; + return; + } + + // Check connections + $this->is_connected = true; + $this->password = md5($this->password); + } + + /** + * @brief disconnect to DB + **/ + function close() { + if(!$this->is_connected) return; + $this->commit(); + } + + /** + * @brief Begin a transaction + **/ + function begin() { + if(!$this->is_connected || $this->transaction_started) return; + if($this->handler->beginTransaction()) $this->transaction_started = true; + } + + /** + * @brief Rollback + **/ + function rollback() { + if(!$this->is_connected || !$this->transaction_started) return; + $this->handler->rollBack(); + $this->transaction_started = false; + } + + /** + * @brief Commit + **/ + function commit($force = false) { + if(!$force && (!$this->is_connected || !$this->transaction_started)) return; + try { + $this->handler->commit(); + } + catch(PDOException $e){ + // There was no transaction started, so just continue. + error_log($e->getMessage()); + } + $this->transaction_started = false; + } + + /** + * @brief Add or change quotes to the query string variables + **/ + function addQuotes($string) { + if(version_compare(PHP_VERSION, "5.9.0", "<") && get_magic_quotes_gpc()) $string = stripslashes(str_replace("\\","\\\\",$string)); + if(!is_numeric($string)) $string = str_replace("'","''",$string); + return $string; + } + + /** + * @brief : Prepare a query statement + **/ + function _prepare($query) { + if(!$this->is_connected) return; + + // notify to start a query execution + $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 : Binding params in stmt + **/ + function _bind($val) { + if(!$this->is_connected || !$this->stmt) return; + + $this->bind_idx ++; + $this->bind_vars[] = $val; + $this->stmt->bindParam($this->bind_idx, $val); + } + + /** + * @brief : execute the prepared statement + **/ + function _execute() { + if(!$this->is_connected || !$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 Return the sequence value incremented by 1 + **/ + function getNextSequence() { + $query = sprintf("insert into %ssequence (seq) values (NULL)", $this->prefix); + $this->_prepare($query); + $result = $this->_execute(); + $sequence = $this->handler->lastInsertId(); + if($sequence % 10000 == 0) { + $query = sprintf("delete from %ssequence where seq < %d", $this->prefix, $sequence); + $this->_prepare($query); + $result = $this->_execute(); + } + + return $sequence; + } + + /** + * @brief return if the table already exists + **/ + 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 Add a column to a table + **/ + 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 Remove a column from a table + **/ + function dropColumn($table_name, $column_name) { + $query = sprintf("alter table %s%s drop column %s ", $this->prefix, $table_name, $column_name); + $this->_query($query); + } + + /** + * @brief Return column information of a table + **/ + 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 Add an index to a table + * $target_columns = array(col1, col2) + * $is_unique? unique : none + **/ + function addIndex($table_name, $index_name, $target_columns, $is_unique = false) { + if(!is_array($target_columns)) $target_columns = array($target_columns); + + $key_name = sprintf('%s%s_%s', $this->prefix, $table_name, $index_name); + + $query = sprintf('CREATE %s INDEX %s ON %s%s (%s)', $is_unique?'UNIQUE':'', $key_name, $this->prefix, $table_name, implode(',',$target_columns)); + $this->_prepare($query); + $this->_execute(); + } + + /** + * @brief Drop an index from a table + **/ + function dropIndex($table_name, $index_name, $is_unique = false) { + $key_name = sprintf('%s%s_%s', $this->prefix, $table_name, $index_name); + $query = sprintf("DROP INDEX %s", $this->prefix, $table_name, $key_name); + $this->_query($query); + } + + /** + * @brief Return index information of a table + **/ + function isIndexExists($table_name, $index_name) { + $key_name = sprintf('%s%s_%s', $this->prefix, $table_name, $index_name); + + $query = sprintf("pragma index_info(%s)", $key_name); + $this->_prepare($query); + $output = $this->_execute(); + if(!$output) return false; + return true; + } + + /** + * @brief create a table from xml file + **/ + function createTableByXml($xml_doc) { + return $this->_createTable($xml_doc); + } + + /** + * @brief create a table from xml file + **/ + function createTableByXmlFile($file_name) { + if(!file_exists($file_name)) return; + // read xml file + $buff = FileHandler::readFile($file_name); + return $this->_createTable($buff); + } + + /** + * @brief generate a query to create a table using the schema xml + * + * 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); + // Create a table 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':'', + isset($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(); + } + } + } + + function _getConnection($type = null){ + return null; + } + + /** + * @brief insertAct + * */ + function _executeInsertAct($queryObject) { + $query = $this->getInsertSql($queryObject); + if (is_a($query, 'Object')) + return; + + $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($queryObject) { + $query = $this->getUpdateSql($queryObject); + if (is_a($query, 'Object')) + return; + + $this->_prepare($query); + return $this->_execute(); + } + + /** + * @brief deleteAct + * */ + function _executeDeleteAct($queryObject) { + $query = $this->getDeleteSql($queryObject); + if (is_a($query, 'Object')) + return; + + $this->_prepare($query); + return $this->_execute(); + } + + /** + * @brief selectAct + * + * To fetch a list of the page conveniently when selecting, \n + * navigation method supported + * */ + function _executeSelectAct($queryObject) { + $query = $this->getSelectSql($queryObject); + if (is_a($query, 'Object')) + return; + + $this->_prepare($query); + $data = $this->_execute(); + // TODO isError is called twice + if ($this->isError()) + return; + + if ($this->isError()) + return $this->queryError($queryObject); + else + return $this->queryPageLimit($queryObject, $data); + } + + function queryError($queryObject) { + if ($queryObject->getLimit() && $queryObject->getLimit()->isPageHandler()) { + $buff = new Object (); + $buff->total_count = 0; + $buff->total_page = 0; + $buff->page = 1; + $buff->data = array(); + $buff->page_navigation = new PageHandler(/* $total_count */0, /* $total_page */1, /* $page */1, /* $page_count */10); //default page handler values + return $buff; + }else + return; + } + + function queryPageLimit($queryObject, $data) { + if ($queryObject->getLimit() && $queryObject->getLimit()->isPageHandler()) { + // Total count + $temp_where = $queryObject->getWhereString(true, false); + $count_query = sprintf('select count(*) as "count" %s %s', 'FROM ' . $queryObject->getFromString(), ($temp_where === '' ? '' : ' WHERE ' . $temp_where)); + if ($queryObject->getGroupByString() != '') { + $count_query = sprintf('select count(*) as "count" from (%s) xet', $count_query); + } + + $count_query .= ( __DEBUG_QUERY__ & 1 && $output->query_id) ? sprintf(' ' . $this->comment_syntax, $this->query_id) : ''; + $this->_prepare($count_query); + $count_output = $this->_execute(); + $total_count = (int) $count_output->count; + + $list_count = $queryObject->getLimit()->list_count->getValue(); + if (!$list_count) $list_count = 20; + $page_count = $queryObject->getLimit()->page_count->getValue(); + if (!$page_count) $page_count = 10; + $page = $queryObject->getLimit()->page->getValue(); + if (!$page) $page = 1; + // Total pages + if ($total_count) { + $total_page = (int) (($total_count - 1) / $list_count) + 1; + } else + $total_page = 1; + + // check the page variables + if ($page > $total_page) $page = $total_page; + $start_count = ($page - 1) * $list_count; + + $this->_prepare($this->getSelectPageSql($queryObject, true, $start_count, $list_count)); + $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; + //$data = $this->_fetch($result, $virtual_no); + 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; + } + $datatemp[$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 = $datatemp; + $buff->page_navigation = new PageHandler($total_count, $total_page, $page, $page_count); + }else { + //$data = $this->_fetch($result); + $buff = new Object (); + $buff->data = $data; + } + return $buff; + } + + function getSelectPageSql($query, $with_values = true, $start_count = 0, $list_count = 0) { + + $select = $query->getSelectString($with_values); + if ($select == '') + return new Object(-1, "Invalid query"); + $select = 'SELECT ' . $select; + + $from = $query->getFromString($with_values); + if ($from == '') + return new Object(-1, "Invalid query"); + $from = ' FROM ' . $from; + + $where = $query->getWhereString($with_values); + if ($where != '') + $where = ' WHERE ' . $where; + + $groupBy = $query->getGroupByString(); + if ($groupBy != '') + $groupBy = ' GROUP BY ' . $groupBy; + + $orderBy = $query->getOrderByString(); + if ($orderBy != '') + $orderBy = ' ORDER BY ' . $orderBy; + + $limit = $query->getLimitString(); + if ($limit != '' && $query->getLimit()) { + $limit = sprintf(' LIMIT %d, %d',$start_count, $list_count); + } + + return $select . ' ' . $from . ' ' . $where . ' ' . $groupBy . ' ' . $orderBy . ' ' . $limit; + } + + function getParser() { + return new DBParser('"', '"', $this->prefix); + } + + function getUpdateSql($query, $with_values = true, $with_priority = false){ + $columnsList = $query->getUpdateString($with_values); + if($columnsList == '') return new Object(-1, "Invalid query"); + + $tableName = $query->getFirstTableName(); + if($tableName == '') return new Object(-1, "Invalid query"); + + $where = $query->getWhereString($with_values); + if($where != '') $where = ' WHERE ' . $where; + + $priority = $with_priority?$query->getPriority():''; + + return "UPDATE $priority $tableName SET $columnsList ".$where; + } + + function getDeleteSql($query, $with_values = true, $with_priority = false){ + $sql = 'DELETE '; + + $tables = $query->getTables(); + $from = $tables[0]->getName(); + $sql .= ' FROM '.$from; + + $where = $query->getWhereString($with_values); + if($where != '') $sql .= ' WHERE ' . $where; + + return $sql; + } +} + +return new DBSqlite3_pdo; +?> diff --git a/classes/db/queryparts/Query.class.php b/classes/db/queryparts/Query.class.php new file mode 100644 index 000000000..729ca46e4 --- /dev/null +++ b/classes/db/queryparts/Query.class.php @@ -0,0 +1,327 @@ +queryID = $queryID; + $this->action = $action; + $this->priority = $priority; + + if(!isset($tables)) return; + $this->columns = $this->setColumns($columns); + $this->tables = $this->setTables($tables); + $this->conditions = $this->setConditions($conditions); + $this->groups = $this->setGroups($groups); + $this->orderby = $this->setOrder($orderby); + $this->limit = $this->setLimit($limit); + } + + function show(){ + return true; + } + + function setQueryId($queryID){ + $this->queryID = $queryID; + } + + function setAction($action){ + $this->action = $action; + } + + function setPriority($priority){ + $this->priority = $priority; + } + + function setColumnList($columnList){ + $this->columnList = $columnList; + if(count($this->columnList) > 0) { + $selectColumns = array(); + $dbParser = DB::getParser(); + + foreach($this->columnList as $columnName){ + $columnName = $dbParser->escapeColumn($columnName); + $selectColumns[] = new SelectExpression($columnName); + } + unset($this->columns); + $this->columns = $selectColumns; + } + } + + function setColumns($columns){ + if(!isset($columns) || count($columns) === 0){ + $this->columns = array(new StarExpression()); + return; + } + + if(!is_array($columns)) $columns = array($columns); + + $this->columns = $columns; + } + + function setTables($tables){ + if(!isset($tables) || count($tables) === 0){ + $this->setError(true); + $this->setMessage("You must provide at least one table for the query."); + return; + } + + if(!is_array($tables)) $tables = array($tables); + + $this->tables = $tables; + } + + function setConditions($conditions){ + $this->conditions = array(); + if(!isset($conditions) || count($conditions) === 0) return; + if(!is_array($conditions)) $conditions = array($conditions); + + foreach($conditions as $conditionGroup){ + if($conditionGroup->show()) $this->conditions[] = $conditionGroup; + } + } + + function setGroups($groups){ + if(!isset($groups) || count($groups) === 0) return; + if(!is_array($groups)) $groups = array($groups); + + $this->groups = $groups; + } + + function setOrder($order){ + if(!isset($order) || count($order) === 0) return; + if(!is_array($order)) $order = array($order); + + $this->orderby = $order; + } + + function setLimit($limit = NULL){ + if(!isset($limit)) return; + $this->limit = $limit; + } + + // START Fluent interface + function select($columns= null){ + $this->action = 'select'; + $this->setColumns($columns); + return $this; + } + + function from($tables){ + $this->setTables($tables); + return $this; + } + + function where($conditions){ + $this->setConditions($conditions); + return $this; + } + + function groupBy($groups){ + $this->setGroups($groups); + return $this; + } + + function orderBy($order){ + $this->setOrder($order); + return $this; + } + + function limit($limit){ + $this->setLimit($limit); + return $this; + } + // END Fluent interface + + function getAction(){ + return $this->action; + } + + function getPriority(){ + return $this->priority?'LOW_PRIORITY':''; + } + + function getSelectString($with_values = true){ + foreach($this->columns as $column){ + if($column->show()) + if($column->isSubquery()){ + $select[] = $column->toString($with_values) . ' as '. $column->getAlias(); + } + else + $select[] = $column->getExpression($with_values); + } + return trim(implode($select, ', ')); + } + + function getUpdateString($with_values = true){ + foreach($this->columns as $column){ + if($column->show()) + $update[] = $column->getExpression($with_values); + } + return trim(implode($update, ', ')); + } + + function getInsertString($with_values = true){ + $columnsList = ''; + $valuesList = ''; + foreach($this->columns as $column){ + if($column->show()){ + $columnsList .= $column->getColumnName() . ', '; + $valuesList .= $column->getValue($with_values) . ', '; + } + } + $columnsList = substr($columnsList, 0, -2); + $valuesList = substr($valuesList, 0, -2); + + return "($columnsList) \n VALUES ($valuesList)"; + } + + function getTables(){ + return $this->tables; + } + + // from table_a + // from table_a inner join table_b on x=y + // from (select * from table a) as x + // from (select * from table t) as x inner join table y on y.x + function getFromString($with_values = true){ + $from = ''; + $simple_table_count = 0; + foreach($this->tables as $table){ + if($table->isJoinTable() || !$simple_table_count) $from .= $table->toString($with_values) . ' '; + else $from .= ', '.$table->toString($with_values) . ' '; + + if(is_a($table, 'Subquery')) $from .= $table->getAlias() ? ' as ' . $table->getAlias() . ' ' : ' '; + + $simple_table_count++; + } + if(trim($from) == '') return ''; + return $from; + } + + function getWhereString($with_values = true, $with_optimization = true){ + $where = ''; + $condition_count = 0; + + foreach($this->conditions as $conditionGroup){ + if($condition_count === 0){ + $conditionGroup->setPipe(""); + } + $condition_string = $conditionGroup->toString($with_values); + $where .= $condition_string; + $condition_count++; + } + + if($with_optimization && + (strstr($this->getOrderByString(), 'list_order') || strstr($this->getOrderByString(), 'update_order'))){ + + if($condition_count !== 0) $where = '(' . $where .') '; + + foreach($this->orderby as $order){ + $colName = $order->getColumnName(); + if(strstr($colName, 'list_order') || strstr($colName, 'update_order')){ + $opt_condition = new ConditionWithoutArgument($colName, 2100000000, 'less', 'and'); + if ($condition_count === 0) $opt_condition->setPipe(""); + $where .= $opt_condition->toString($with_values).' '; + $condition_count++; + } + } + } + + return trim($where); + } + + function getGroupByString(){ + $groupBy = ''; + if($this->groups) if($this->groups[0] !== "") + $groupBy = implode(', ', $this->groups); + return $groupBy; + } + + function getOrderByString(){ + if(!$this->_orderByString){ + if(count($this->orderby) === 0) return ''; + $orderBy = ''; + foreach($this->orderby as $order){ + $orderBy .= $order->toString() .', '; + } + $orderBy = substr($orderBy, 0, -2); + $this->_orderByString = $orderBy; + } + return $this->_orderByString; + } + + function getLimit(){ + return $this->limit; + } + + function getLimitString(){ + $limit = ''; + if(count($this->limit) > 0){ + $limit = ''; + $limit .= $this->limit->toString(); + } + return $limit; + } + + function getFirstTableName(){ + return $this->tables[0]->getName(); + } + + function getArguments(){ + if(!isset($this->arguments)){ + $this->arguments = array(); + + // Column arguments + if(count($this->columns) > 0){ // The if is for delete statements, all others must have columns + foreach($this->columns as $column){ + if($column->show()){ + $arg = $column->getArgument(); + if($arg) $this->arguments[] = $arg; + } + } + } + + // Condition arguments + if(count($this->conditions) > 0) + foreach($this->conditions as $conditionGroup){ + $args = $conditionGroup->getArguments(); + if(count($args) > 0) $this->arguments = array_merge($this->arguments, $args); + } + + // Navigation arguments + if(count($this->orderby) > 0) + foreach($this->orderby as $order){ + $args = $order->getArguments(); + if(count($args) > 0) $this->arguments = array_merge($this->arguments, $args); + } + } + return $this->arguments; + } + } + + + +?> \ No newline at end of file diff --git a/classes/db/queryparts/Subquery.class.php b/classes/db/queryparts/Subquery.class.php new file mode 100644 index 000000000..2f49c34ef --- /dev/null +++ b/classes/db/queryparts/Subquery.class.php @@ -0,0 +1,42 @@ +alias = $alias; + + $this->queryID = null; + $this->action = "select"; + + $this->columns = $columns; + $this->tables = $tables; + $this->conditions = $conditions; + $this->groups = $groups; + $this->orderby = $orderby; + $this->limit = $limit; + $this->join_type = $join_type; + } + + function getAlias(){ + return $this->alias; + } + + function isJoinTable(){ + if($this->join_type) return true; + return false; + } + + function toString($with_values = true){ + $oDB = &DB::getInstance(); + return '(' .$oDB->getSelectSql($this, $with_values) . ')'; + + } + + function isSubquery(){ + return true; + } + } + +?> \ No newline at end of file diff --git a/classes/db/queryparts/condition/Condition.class.php b/classes/db/queryparts/condition/Condition.class.php new file mode 100644 index 000000000..c482626f5 --- /dev/null +++ b/classes/db/queryparts/condition/Condition.class.php @@ -0,0 +1,152 @@ +column_name = $column_name; + $this->argument = $argument; + $this->operation = $operation; + $this->pipe = $pipe; + + } + + function getArgument(){ + return null; + } + + function toString($withValue = true){ + if(!isset($this->_value_to_string)){ + if(!$this->show()) { $this->_value_to_string = ''; } + else if($withValue) + $this->_value_to_string = $this->toStringWithValue(); + else $this->_value_to_string = $this->toStringWithoutValue(); + } + return $this->_value_to_string; + } + + function toStringWithoutValue(){ + return $this->pipe . ' ' . $this->getConditionPart($this->_value); + } + + function toStringWithValue(){ + return $this->pipe . ' ' . $this->getConditionPart($this->_value); + } + + function setPipe($pipe){ + $this->pipe = $pipe; + } + + function show(){ + if(!isset($this->_show)){ + if(is_array($this->_value) && count($this->_value) === 1 && $this->_value[0] === '') { + $this->_show = false; + } + else { + $this->_show = true; + switch($this->operation) { + case 'equal' : + case 'more' : + case 'excess' : + case 'less' : + case 'below' : + case 'like_tail' : + case 'like_prefix' : + case 'like' : + case 'notlike_tail' : + case 'notlike_prefix' : + case 'notlike' : + case 'in' : + case 'notin' : + case 'and': + case 'or': + case 'xor': + case 'not': + case 'notequal' : + // if variable is not set or is not string or number, return + if(!isset($this->_value)) { $this->_show = false; break;} + if($this->_value === '') { $this->_show = false; break; } + if(!in_array(gettype($this->_value), array('string', 'integer'))) {$this->_show = false; break; } + break; + case 'between' : + if(!is_array($this->_value)) { $this->_show = false; break;} + if(count($this->_value)!=2) {$this->_show = false; break;} + } + } + } + return $this->_show; + } + + function getConditionPart($value) { + $name = $this->column_name; + $operation = $this->operation; + + switch($operation) { + case 'equal' : + return $name.' = '.$value; + break; + case 'more' : + return $name.' >= '.$value; + break; + case 'excess' : + return $name.' > '.$value; + break; + case 'less' : + return $name.' <= '.$value; + break; + case 'below' : + return $name.' < '.$value; + break; + case 'like_tail' : + case 'like_prefix' : + case 'like' : + return $name.' like '.$value; + break; + case 'notlike_tail' : + case 'notlike_prefix' : + case 'notlike' : + return $name.' not like '.$value; + break; + case 'in' : + return $name.' in '.$value; + break; + case 'notin' : + return $name.' not in '.$value; + break; + case 'notequal' : + return $name.' <> '.$value; + break; + case 'notnull' : + return $name.' is not null'; + break; + case 'null' : + return $name.' is null'; + break; + case 'and' : + return $name.' & '.$value; + break; + case 'or' : + return $name.' | '.$value; + break; + case 'xor' : + return $name.' ^ '.$value; + break; + case 'not' : + return $name.' ~ '.$value; + break; + case 'between' : + return $name.' between ' . $value[0] . ' and ' . $value[1]; + break; + } + } + } + +?> diff --git a/classes/db/queryparts/condition/ConditionGroup.class.php b/classes/db/queryparts/condition/ConditionGroup.class.php new file mode 100644 index 000000000..201c8c8bf --- /dev/null +++ b/classes/db/queryparts/condition/ConditionGroup.class.php @@ -0,0 +1,60 @@ +conditions = array(); + foreach($conditions as $condition){ + if($condition->show()) + $this->conditions[] = $condition; + } + if(count($this->conditions) === 0) $this->_show = false; + else $this->_show = true; + + $this->pipe = $pipe; + } + + function show(){ + return $this->_show; + } + + function setPipe($pipe){ + if($this->pipe !== $pipe) $this->_group = null; + $this->pipe = $pipe; + } + + function toString($with_value = true){ + if(!isset($this->_group)){ + $cond_indx = 0; + $group = ''; + + foreach($this->conditions as $condition){ + if($cond_indx === 0) $condition->setPipe(""); + $group .= $condition->toString($with_value) . ' '; + $cond_indx++; + } + + if($this->pipe !== "" && trim($group) !== ''){ + $group = $this->pipe . ' (' . $group . ')'; + } + + $this->_group = $group; + } + return $this->_group; + } + + function getArguments(){ + $args = array(); + foreach($this->conditions as $condition){ + $arg = $condition->getArgument(); + if($arg) $args[] = $arg; + } + return $args; + } + } +?> \ No newline at end of file diff --git a/classes/db/queryparts/condition/ConditionSubquery.class.php b/classes/db/queryparts/condition/ConditionSubquery.class.php new file mode 100644 index 000000000..c515e814b --- /dev/null +++ b/classes/db/queryparts/condition/ConditionSubquery.class.php @@ -0,0 +1,11 @@ +_value = $this->argument->toString(); + } + } + +?> diff --git a/classes/db/queryparts/condition/ConditionWithArgument.class.php b/classes/db/queryparts/condition/ConditionWithArgument.class.php new file mode 100644 index 000000000..5a00153f3 --- /dev/null +++ b/classes/db/queryparts/condition/ConditionWithArgument.class.php @@ -0,0 +1,41 @@ +_show = false; return; } + parent::Condition($column_name, $argument, $operation, $pipe); + $this->_value = $argument->getValue(); + } + + function getArgument(){ + if(!$this->show()) return; + return $this->argument; + } + + function toStringWithoutValue(){ + $value = $this->argument->getUnescapedValue(); + + if(is_array($value)){ + $q = ''; + foreach ($value as $v) $q .= '?,'; + if($q !== '') $q = substr($q, 0, -1); + $q = '(' . $q . ')'; + } + else $q = '?'; + return $this->pipe . ' ' . $this->getConditionPart($q); + } + + function show(){ + if(!isset($this->_show)){ + if(!$this->argument->isValid()) $this->_show = false; + if($this->_value === '\'\'') $this->_show = false; + if(!isset($this->_show)){ + return parent::show(); + } + } + return $this->_show; + } + } + +?> diff --git a/classes/db/queryparts/condition/ConditionWithoutArgument.class.php b/classes/db/queryparts/condition/ConditionWithoutArgument.class.php new file mode 100644 index 000000000..68ee35103 --- /dev/null +++ b/classes/db/queryparts/condition/ConditionWithoutArgument.class.php @@ -0,0 +1,14 @@ +_value = '('. $argument .')'; + else + $this->_value = $argument; + + } + } + +?> diff --git a/classes/db/queryparts/expression/ClickCountExpression.class.php b/classes/db/queryparts/expression/ClickCountExpression.class.php new file mode 100644 index 000000000..a112368e8 --- /dev/null +++ b/classes/db/queryparts/expression/ClickCountExpression.class.php @@ -0,0 +1,33 @@ +click_count = false; + return; + } + $this->click_count = $click_count; + } + + function show() { + return $this->click_count; + } + + function getExpression(){ + return "$this->column_name = $this->column_name + 1"; + } + } + +?> \ No newline at end of file diff --git a/classes/db/queryparts/expression/DeleteExpression.class.php b/classes/db/queryparts/expression/DeleteExpression.class.php new file mode 100644 index 000000000..611510109 --- /dev/null +++ b/classes/db/queryparts/expression/DeleteExpression.class.php @@ -0,0 +1,35 @@ +value = $value; + } + + function getExpression(){ + return "$this->column_name = $this->value"; + } + + function getValue(){ + // TODO Escape value according to column type instead of variable type + if(!is_numeric($this->value)) return "'".$this->value."'"; + return $this->value; + } + + function show(){ + if(!$this->value) return false; + return true; + } + } + + +?> \ No newline at end of file diff --git a/classes/db/queryparts/expression/Expression.class.php b/classes/db/queryparts/expression/Expression.class.php new file mode 100644 index 000000000..bd11929f6 --- /dev/null +++ b/classes/db/queryparts/expression/Expression.class.php @@ -0,0 +1,30 @@ +column_name = $column_name; + } + + function getColumnName(){ + return $this->column_name; + } + + function show() { + return false; + } + + function getExpression() { + } + } \ No newline at end of file diff --git a/classes/db/queryparts/expression/InsertExpression.class.php b/classes/db/queryparts/expression/InsertExpression.class.php new file mode 100644 index 000000000..89b0754b7 --- /dev/null +++ b/classes/db/queryparts/expression/InsertExpression.class.php @@ -0,0 +1,36 @@ +argument = $argument; + } + + function getValue($with_values = true){ + if($with_values) + return $this->argument->getValue(); + return '?'; + } + + function show(){ + if(!$this->argument) return false; + $value = $this->argument->getValue(); + if(!isset($value)) return false; + return true; + } + + function getArgument(){ + return $this->argument; + } + } + +?> \ No newline at end of file diff --git a/classes/db/queryparts/expression/SelectExpression.class.php b/classes/db/queryparts/expression/SelectExpression.class.php new file mode 100644 index 000000000..1f7e54625 --- /dev/null +++ b/classes/db/queryparts/expression/SelectExpression.class.php @@ -0,0 +1,40 @@ +column_alias = $alias; + } + + function getExpression() { + return sprintf("%s%s", $this->column_name, $this->column_alias ? " as ".$this->column_alias : ""); + } + + function show() { + return true; + } + + function getArgument(){ + return null; + } + + function isSubquery(){ + return false; + } + } +?> \ No newline at end of file diff --git a/classes/db/queryparts/expression/StarExpression.class.php b/classes/db/queryparts/expression/StarExpression.class.php new file mode 100644 index 000000000..eb8d35030 --- /dev/null +++ b/classes/db/queryparts/expression/StarExpression.class.php @@ -0,0 +1,20 @@ + \ No newline at end of file diff --git a/classes/db/queryparts/expression/UpdateExpression.class.php b/classes/db/queryparts/expression/UpdateExpression.class.php new file mode 100644 index 000000000..067239e34 --- /dev/null +++ b/classes/db/queryparts/expression/UpdateExpression.class.php @@ -0,0 +1,58 @@ +argument = $argument; + } + + function getExpression($with_value = true){ + if($with_value) + return $this->getExpressionWithValue(); + return $this->getExpressionWithoutValue(); + } + + function getExpressionWithValue(){ + $value = $this->argument->getValue(); + $operation = $this->argument->getColumnOperation(); + if(isset($operation)) + return "$this->column_name = $this->column_name $operation $value"; + return "$this->column_name = $value"; + } + + function getExpressionWithoutValue(){ + $operation = $this->argument->getColumnOperation(); + if(isset($operation)) + return "$this->column_name = $this->column_name $operation ?"; + return "$this->column_name = ?"; + } + + function getValue(){ + // TODO Escape value according to column type instead of variable type + $value = $this->argument->getValue(); + if(!is_numeric($value)) return "'".$value."'"; + return $value; + } + + function show(){ + if(!$this->argument) return false; + $value = $this->argument->getValue(); + if(!isset($value)) return false; + return true; + } + + function getArgument(){ + return $this->argument; + } + } + + +?> \ No newline at end of file diff --git a/classes/db/queryparts/expression/UpdateExpressionWithoutArgument.class.php b/classes/db/queryparts/expression/UpdateExpressionWithoutArgument.class.php new file mode 100644 index 000000000..2ee8c019b --- /dev/null +++ b/classes/db/queryparts/expression/UpdateExpressionWithoutArgument.class.php @@ -0,0 +1,41 @@ +argument = $argument; + } + + function getExpression($with_value = true){ + return "$this->column_name = $this->argument"; + } + + function getValue(){ + // TODO Escape value according to column type instead of variable type + $value = $this->argument; + if(!is_numeric($value)) return "'".$value."'"; + return $value; + } + + function show(){ + if(!$this->argument) return false; + $value = $this->argument; + if(!isset($value)) return false; + return true; + } + + function getArgument(){ + return null; + } + } + + +?> \ No newline at end of file diff --git a/classes/db/queryparts/limit/Limit.class.php b/classes/db/queryparts/limit/Limit.class.php new file mode 100644 index 000000000..4cc372434 --- /dev/null +++ b/classes/db/queryparts/limit/Limit.class.php @@ -0,0 +1,37 @@ +list_count = $list_count; + if ($page){ + $list_count_value = $list_count->getValue(); + $page_value = $page->getValue(); + $this->start = ($page_value - 1) * $list_count_value; + $this->page_count = $page_count; + $this->page = $page; + } + } + + function isPageHandler(){//in case you choose to use query limit in other cases than page select + if ($this->page)return true; + else return false; + } + + function getOffset(){ + return $this->start; + } + + function getLimit(){ + return $this->list_count->getValue(); + } + + function toString(){ + if ($this->page) return $this->start . ' , ' . $this->list_count->getValue(); + else return $this->list_count->getValue(); + } + } +?> \ No newline at end of file diff --git a/classes/db/queryparts/order/OrderByColumn.class.php b/classes/db/queryparts/order/OrderByColumn.class.php new file mode 100644 index 000000000..445eaae64 --- /dev/null +++ b/classes/db/queryparts/order/OrderByColumn.class.php @@ -0,0 +1,31 @@ +column_name = $column_name; + $this->sort_order = $sort_order; + } + + function toString(){ + $result = $this->getColumnName(); + $result .= ' '; + $result .= is_a($this->sort_order, 'Argument') ? $this->sort_order->getValue() : $this->sort_order; + return $result; + } + + function getColumnName(){ + return is_a($this->column_name, 'Argument') ? $this->column_name->getValue() : $this->column_name; + } + + function getArguments(){ + $args = array(); + if(is_a($this->column_name, 'Argument')) + $args[]= $this->column_name; + if(is_a($this->sort_order, 'Argument')) + $args[] = $this->sort_order; + } + } + +?> \ No newline at end of file diff --git a/classes/db/queryparts/table/CubridTableWithHint.class.php b/classes/db/queryparts/table/CubridTableWithHint.class.php new file mode 100644 index 000000000..ae1e7a1fe --- /dev/null +++ b/classes/db/queryparts/table/CubridTableWithHint.class.php @@ -0,0 +1,35 @@ +index_hints_list = $index_hints_list; + } + + function getIndexHintString(){ + $result = ''; + + // Retrieve table prefix, to add it to index name + $db_info = Context::getDBInfo(); + $prefix = $db_info->master_db["db_table_prefix"]; + + foreach($this->index_hints_list as $index_hint){ + $index_hint_type = $index_hint->getIndexHintType(); + if($index_hint_type !== 'IGNORE'){ + $result .= $this->alias . '.' + . '"' . $prefix . substr($index_hint->getIndexName(), 1) + . ($index_hint_type == 'FORCE' ? '(+)' : '') + . ', '; + } + + } + $result = substr($result, 0, -2); + return $result; + } + } + +?> \ No newline at end of file diff --git a/classes/db/queryparts/table/IndexHint.class.php b/classes/db/queryparts/table/IndexHint.class.php new file mode 100644 index 000000000..6f24a954b --- /dev/null +++ b/classes/db/queryparts/table/IndexHint.class.php @@ -0,0 +1,21 @@ +index_name = $index_name; + $this->index_hint_type = $index_hint_type; + } + + function getIndexName(){ + return $this->index_name; + } + + function getIndexHintType() { + return $this->index_hint_type; + } + } + +?> \ No newline at end of file diff --git a/classes/db/queryparts/table/JoinTable.class.php b/classes/db/queryparts/table/JoinTable.class.php new file mode 100644 index 000000000..7738667d1 --- /dev/null +++ b/classes/db/queryparts/table/JoinTable.class.php @@ -0,0 +1,37 @@ +join_type = $join_type; + $this->conditions = $conditions; + } + + function toString($with_value = true){ + $part = $this->join_type . ' ' . $this->name ; + $part .= $this->alias ? ' as ' . $this->alias : ''; + $part .= ' on '; + foreach($this->conditions as $conditionGroup) + $part .= $conditionGroup->toString($with_value); + return $part; + } + + function isJoinTable(){ + return true; + } + } + +?> \ No newline at end of file diff --git a/classes/db/queryparts/table/MssqlTableWithHint.class.php b/classes/db/queryparts/table/MssqlTableWithHint.class.php new file mode 100644 index 000000000..a3c2e46e6 --- /dev/null +++ b/classes/db/queryparts/table/MssqlTableWithHint.class.php @@ -0,0 +1,29 @@ +index_hints_list = $index_hints_list; + } + + function toString(){ + $result = parent::toString(); + + $index_hint_string = ''; + foreach($this->index_hints_list as $index_hint){ + $index_hint_type = $index_hint->getIndexHintType(); + if(in_array($index_hint_type, array('USE', 'FORCE'))) + $index_hint_string .= 'INDEX(' . $index_hint->getIndexName() . '), '; + } + if($index_hint_string != ''){ + $result .= ' WITH(' . substr($index_hint_string, 0, -2) . ') '; + } + return $result; + } + } + +?> \ No newline at end of file diff --git a/classes/db/queryparts/table/MysqlTableWithHint.class.php b/classes/db/queryparts/table/MysqlTableWithHint.class.php new file mode 100644 index 000000000..58bdcac73 --- /dev/null +++ b/classes/db/queryparts/table/MysqlTableWithHint.class.php @@ -0,0 +1,36 @@ +index_hints_list = $index_hints_list; + } + + function toString(){ + $result = parent::toString(); + + $use_index_hint = ''; $force_index_hint = ''; $ignore_index_hint = ''; + foreach($this->index_hints_list as $index_hint){ + $index_hint_type = $index_hint->getIndexHintType(); + if($index_hint_type == 'USE') $use_index_hint .= $index_hint->getIndexName() . ', '; + else if($index_hint_type == 'FORCE') $force_index_hint .= $index_hint->getIndexName() . ', '; + else if($index_hint_type == 'IGNORE') $ignore_index_hint .= $index_hint->getIndexName() . ', '; + } + if($use_index_hint != ''){ + $result .= ' USE INDEX (' . substr($use_index_hint, 0, -2) . ') '; + } + if($force_index_hint != ''){ + $result .= ' FORCE INDEX (' . substr($force_index_hint, 0, -2) . ') '; + } + if($ignore_index_hint != ''){ + $result .= ' IGNORE INDEX (' . substr($ignore_index_hint, 0, -2) . ') '; + } + return $result; + } + } + +?> \ No newline at end of file diff --git a/classes/db/queryparts/table/Table.class.php b/classes/db/queryparts/table/Table.class.php new file mode 100644 index 000000000..3a19d3a97 --- /dev/null +++ b/classes/db/queryparts/table/Table.class.php @@ -0,0 +1,30 @@ +name = $name; + $this->alias = $alias; + } + + function toString(){ + //return $this->name; + return sprintf("%s%s", $this->name, $this->alias ? ' as ' . $this->alias : ''); + } + + function getName(){ + return $this->name; + } + + function getAlias(){ + return $this->alias; + } + + function isJoinTable(){ + return false; + } + } + +?> \ No newline at end of file diff --git a/classes/display/DisplayHandler.class.php b/classes/display/DisplayHandler.class.php index 8ad991f7a..0327211b9 100644 --- a/classes/display/DisplayHandler.class.php +++ b/classes/display/DisplayHandler.class.php @@ -1,278 +1,270 @@ -gz_enabled = true; - - // request method에 따른 컨텐츠 결과물 추출 - if(Context::get('xeVirtualRequestMethod')=='xml') { - require_once("./classes/display/VirtualXMLDisplayHandler.php"); - $handler = new VirtualXMLDisplayHandler(); - } - else if(Context::getRequestMethod() == 'XMLRPC') { - require_once("./classes/display/XMLDisplayHandler.php"); - $handler = new XMLDisplayHandler(); - if(strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== false) $this->gz_enabled = false; - } - else if(Context::getRequestMethod() == 'JSON') { - require_once("./classes/display/JSONDisplayHandler.php"); - $handler = new JSONDisplayHandler(); - } - else { - require_once("./classes/display/HTMLDisplayHandler.php"); - $handler = new HTMLDisplayHandler(); - } - - $output = $handler->toDoc($oModule); - - // 출력하기 전에 trigger 호출 (before) - ModuleHandler::triggerCall('display', 'before', $output); - - // 애드온 실행 - $called_position = 'before_display_content'; - $oAddonController = &getController('addon'); - $addon_file = $oAddonController->getCacheFilePath(Mobile::isFromMobilePhone()?"mobile":"pc"); - @include($addon_file); - - if(method_exists($handler, "prepareToPrint")) $handler->prepareToPrint($output); - - // header 출력 - if($this->gz_enabled) header("Content-Encoding: gzip"); - if(Context::getResponseMethod() == 'JSON') $this->_printJSONHeader(); - else if(Context::getResponseMethod() != 'HTML') $this->_printXMLHeader(); - else $this->_printHTMLHeader(); - - // debugOutput 출력 - $this->content_size = strlen($output); - $output .= $this->_debugOutput(); - - // 결과물 직접 출력 - if($this->gz_enabled) print ob_gzhandler($output, 5); - else print $output; - - // 출력 후 trigger 호출 (after) - ModuleHandler::triggerCall('display', 'after', $content); - } - - - /** - * @brief Print debugging message to designated output source depending on the value set to __DEBUG_OUTPUT_. \n - * This method only functions when __DEBUG__ variable is set to 1. - * __DEBUG_OUTPUT__ == 0, messages are written in ./files/_debug_message.php - **/ - function _debugOutput() { - if(!__DEBUG__) return; - - $end = getMicroTime(); - - // Firebug 콘솔 출력 - if(__DEBUG_OUTPUT__ == 2 && version_compare(PHP_VERSION, '6.0.0') === -1) { - static $firephp; - if(!isset($firephp)) $firephp = FirePHP::getInstance(true); - - if(__DEBUG_PROTECT__ == 1 && __DEBUG_PROTECT_IP__ != $_SERVER['REMOTE_ADDR']) { - $firephp->fb('Change the value of __DEBUG_PROTECT_IP__ into your IP address in config/config.user.inc.php or config/config.inc.php', 'The IP address is not allowed.'); - return; - } - - // 전체 실행 시간 출력, Request/Response info 출력 - if(__DEBUG__ & 2) { - $firephp->fb( - array('Request / Response info >>> '.$_SERVER['REQUEST_METHOD'].' / '.Context::getResponseMethod(), - array( - array('Request URI', 'Request method', 'Response method', 'Response contents size'), - array( - sprintf("%s:%s%s%s%s", $_SERVER['SERVER_NAME'], $_SERVER['SERVER_PORT'], $_SERVER['PHP_SELF'], $_SERVER['QUERY_STRING']?'?':'', $_SERVER['QUERY_STRING']), - $_SERVER['REQUEST_METHOD'], - Context::getResponseMethod(), - $this->content_size.' byte' - ) - ) - ), - 'TABLE' - ); - $firephp->fb( - array('Elapsed time >>> Total : '.sprintf('%0.5f sec', $end - __StartTime__), - array(array('DB queries', 'class file load', 'Template compile', 'XmlParse compile', 'PHP', 'Widgets', 'Trans Content'), - array( - sprintf('%0.5f sec', $GLOBALS['__db_elapsed_time__']), - sprintf('%0.5f sec', $GLOBALS['__elapsed_class_load__']), - sprintf('%0.5f sec (%d called)', $GLOBALS['__template_elapsed__'], $GLOBALS['__TemplateHandlerCalled__']), - sprintf('%0.5f sec', $GLOBALS['__xmlparse_elapsed__']), - sprintf('%0.5f sec', $end-__StartTime__-$GLOBALS['__template_elapsed__']-$GLOBALS['__xmlparse_elapsed__']-$GLOBALS['__db_elapsed_time__']-$GLOBALS['__elapsed_class_load__']), - sprintf('%0.5f sec', $GLOBALS['__widget_excute_elapsed__']), - sprintf('%0.5f sec', $GLOBALS['__trans_content_elapsed__']) - ) - ) - ), - 'TABLE' - ); - } - - // DB 쿼리 내역 출력 - if((__DEBUG__ & 4) && $GLOBALS['__db_queries__']) { - $queries_output = array(array('Query', 'Elapsed time', 'Result')); - foreach($GLOBALS['__db_queries__'] as $query) { - array_push($queries_output, array($query['query'], sprintf('%0.5f', $query['elapsed_time']), $query['result'])); - } - $firephp->fb( - array( - 'DB Queries >>> '.count($GLOBALS['__db_queries__']).' Queries, '.sprintf('%0.5f sec', $GLOBALS['__db_elapsed_time__']), - $queries_output - ), - 'TABLE' - ); - } - - - // 파일 및 HTML 주석으로 출력 - } else { - - // 전체 실행 시간 출력, Request/Response info 출력 - if(__DEBUG__ & 2) { - if(__DEBUG_PROTECT__ == 1 && __DEBUG_PROTECT_IP__ != $_SERVER['REMOTE_ADDR']) { - return; - } - - // 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->content_size); - - // 전체 실행 시간 - $buff .= sprintf("\n- Total elapsed time : %0.5f sec\n", $end-__StartTime__); - - $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 Content \t\t\t: %0.5f sec\n", $GLOBALS['__trans_content_elapsed__']); - } - - // DB 로그 작성 - if(__DEBUG__ & 4) { - if(__DEBUG_PROTECT__ == 1 && __DEBUG_PROTECT_IP__ != $_SERVER['REMOTE_ADDR']) { - return; - } - - if($GLOBALS['__db_queries__']) { - $buff .= sprintf("\n- DB Queries : %d Queries. %0.5f sec\n", count($GLOBALS['__db_queries__']), $GLOBALS['__db_elapsed_time__']); - $num = 0; - - foreach($GLOBALS['__db_queries__'] as $query) { - $buff .= sprintf("\t%02d. %s\n\t\t%0.6f sec. ", ++$num, $query['query'], $query['elapsed_time']); - if($query['result'] == 'Success') { - $buff .= "Query Success\n"; - } else { - $buff .= sprintf("Query $s : %d\n\t\t\t %s\n", $query['result'], $query['errno'], $query['errstr']); - } - } - } - } - - // HTML 주석으로 출력 - if($buff && __DEBUG_OUTPUT__ == 1 && Context::getResponseMethod() == 'HTML') { - $buff = sprintf("[%s %s:%d]\n%s\n", date('Y-m-d H:i:s'), $file_name, $line_num, print_r($buff, true)); - - if(__DEBUG_PROTECT__ == 1 && __DEBUG_PROTECT_IP__ != $_SERVER['REMOTE_ADDR']) { - $buff = 'The IP address is not allowed. Change the value of __DEBUG_PROTECT_IP__ into your IP address in config/config.user.inc.php or config/config.inc.php'; - } - - return ""; - } - - // 파일에 출력 - if($buff && __DEBUG_OUTPUT__ == 0) { - $debug_file = _XE_PATH_.'files/_debug_message.php'; - $buff = sprintf("[%s %s:%d]\n%s\n", date('Y-m-d H:i:s'), $file_name, $line_num, print_r($buff, true)); - - $buff = str_repeat('=', 40)."\n".$buff.str_repeat('-', 40); - $buff = "\n\n"; - - if(@!$fp = fopen($debug_file, 'a')) return; - fwrite($fp, $buff); - fclose($fp); - } - } - } - - /** - * @brief print a HTTP HEADER for XML, which is encoded in UTF-8 - **/ - 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 print a HTTP HEADER for HTML, which is encoded in UTF-8 - **/ - 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"); - } - - - /** - * @brief print a HTTP HEADER for JSON, which is encoded in UTF-8 - **/ - function _printJSONHeader() { - 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"); - } - - - - - } -?> +gz_enabled = true; + // Extract contents to display by the request method + if(Context::get('xeVirtualRequestMethod')=='xml') { + require_once("./classes/display/VirtualXMLDisplayHandler.php"); + $handler = new VirtualXMLDisplayHandler(); + } + else if(Context::getRequestMethod() == 'XMLRPC') { + require_once("./classes/display/XMLDisplayHandler.php"); + $handler = new XMLDisplayHandler(); + if(strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== false) $this->gz_enabled = false; + } + else if(Context::getRequestMethod() == 'JSON') { + require_once("./classes/display/JSONDisplayHandler.php"); + $handler = new JSONDisplayHandler(); + } + else { + require_once("./classes/display/HTMLDisplayHandler.php"); + $handler = new HTMLDisplayHandler(); + } + + $output = $handler->toDoc($oModule); + // call a trigger before display + ModuleHandler::triggerCall('display', 'before', $output); + // execute add-on + $called_position = 'before_display_content'; + $oAddonController = &getController('addon'); + $addon_file = $oAddonController->getCacheFilePath(Mobile::isFromMobilePhone()?"mobile":"pc"); + @include($addon_file); + + if(method_exists($handler, "prepareToPrint")) $handler->prepareToPrint($output); + // header output + if($this->gz_enabled) header("Content-Encoding: gzip"); + + $httpStatusCode = $oModule->getHttpStatusCode(); + if($httpStatusCode && $httpStatusCode != 200) $this->_printHttpStatusCode($httpStatusCode); + else + { + if(Context::getResponseMethod() == 'JSON') $this->_printJSONHeader(); + else if(Context::getResponseMethod() != 'HTML') $this->_printXMLHeader(); + else $this->_printHTMLHeader(); + } + + // debugOutput output + $this->content_size = strlen($output); + $output .= $this->_debugOutput(); + // results directly output + if($this->gz_enabled) print ob_gzhandler($output, 5); + else print $output; + // call a trigger after display + ModuleHandler::triggerCall('display', 'after', $content); + } + + + /** + * @brief Print debugging message to designated output source depending on the value set to __DEBUG_OUTPUT_. \n + * This method only functions when __DEBUG__ variable is set to 1. + * __DEBUG_OUTPUT__ == 0, messages are written in ./files/_debug_message.php + **/ + function _debugOutput() { + if(!__DEBUG__) return; + + $end = getMicroTime(); + // Firebug console output + if(__DEBUG_OUTPUT__ == 2 && version_compare(PHP_VERSION, '6.0.0') === -1) { + static $firephp; + if(!isset($firephp)) $firephp = FirePHP::getInstance(true); + + if(__DEBUG_PROTECT__ == 1 && __DEBUG_PROTECT_IP__ != $_SERVER['REMOTE_ADDR']) { + $firephp->fb('Change the value of __DEBUG_PROTECT_IP__ into your IP address in config/config.user.inc.php or config/config.inc.php', 'The IP address is not allowed.'); + return; + } + // display total execution time and Request/Response info + if(__DEBUG__ & 2) { + $firephp->fb( + array('Request / Response info >>> '.$_SERVER['REQUEST_METHOD'].' / '.Context::getResponseMethod(), + array( + array('Request URI', 'Request method', 'Response method', 'Response contents size'), + array( + sprintf("%s:%s%s%s%s", $_SERVER['SERVER_NAME'], $_SERVER['SERVER_PORT'], $_SERVER['PHP_SELF'], $_SERVER['QUERY_STRING']?'?':'', $_SERVER['QUERY_STRING']), + $_SERVER['REQUEST_METHOD'], + Context::getResponseMethod(), + $this->content_size.' byte' + ) + ) + ), + 'TABLE' + ); + $firephp->fb( + array('Elapsed time >>> Total : '.sprintf('%0.5f sec', $end - __StartTime__), + array(array('DB queries', 'class file load', 'Template compile', 'XmlParse compile', 'PHP', 'Widgets', 'Trans Content'), + array( + sprintf('%0.5f sec', $GLOBALS['__db_elapsed_time__']), + sprintf('%0.5f sec', $GLOBALS['__elapsed_class_load__']), + sprintf('%0.5f sec (%d called)', $GLOBALS['__template_elapsed__'], $GLOBALS['__TemplateHandlerCalled__']), + sprintf('%0.5f sec', $GLOBALS['__xmlparse_elapsed__']), + sprintf('%0.5f sec', $end-__StartTime__-$GLOBALS['__template_elapsed__']-$GLOBALS['__xmlparse_elapsed__']-$GLOBALS['__db_elapsed_time__']-$GLOBALS['__elapsed_class_load__']), + sprintf('%0.5f sec', $GLOBALS['__widget_excute_elapsed__']), + sprintf('%0.5f sec', $GLOBALS['__trans_content_elapsed__']) + ) + ) + ), + 'TABLE' + ); + } + // display DB query history + if((__DEBUG__ & 4) && $GLOBALS['__db_queries__']) { + $queries_output = array(array('Query', 'Elapsed time', 'Result')); + foreach($GLOBALS['__db_queries__'] as $query) { + array_push($queries_output, array($query['query'], sprintf('%0.5f', $query['elapsed_time']), $query['result'])); + } + $firephp->fb( + array( + 'DB Queries >>> '.count($GLOBALS['__db_queries__']).' Queries, '.sprintf('%0.5f sec', $GLOBALS['__db_elapsed_time__']), + $queries_output + ), + 'TABLE' + ); + } + // dislpay the file and HTML comments + } else { + // display total execution time and Request/Response info + if(__DEBUG__ & 2) { + if(__DEBUG_PROTECT__ == 1 && __DEBUG_PROTECT_IP__ != $_SERVER['REMOTE_ADDR']) { + return; + } + // Request/Response information + $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->content_size); + // total execution time + $buff .= sprintf("\n- Total elapsed time : %0.5f sec\n", $end-__StartTime__); + + $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("\tDB class elapsed time \t\t: %0.5f sec\n", $GLOBALS['__dbclass_elapsed_time__'] -$GLOBALS['__db_elapsed_time__']); + // widget execution time + $buff .= sprintf("\n\tWidgets elapsed time \t\t: %0.5f sec", $GLOBALS['__widget_excute_elapsed__']); + // layout execution time + $buff .= sprintf("\n\tLayout compile elapsed time \t: %0.5f sec", $GLOBALS['__layout_compile_elapsed__']); + // Widgets, the editor component replacement time + $buff .= sprintf("\n\tTrans Content \t\t\t: %0.5f sec\n", $GLOBALS['__trans_content_elapsed__']); + } + // DB Logging + if(__DEBUG__ & 4) { + if(__DEBUG_PROTECT__ == 1 && __DEBUG_PROTECT_IP__ != $_SERVER['REMOTE_ADDR']) { + return; + } + + if($GLOBALS['__db_queries__']) { + $buff .= sprintf("\n- DB Queries : %d Queries. %0.5f sec\n", count($GLOBALS['__db_queries__']), $GLOBALS['__db_elapsed_time__']); + $num = 0; + + foreach($GLOBALS['__db_queries__'] as $query) { + $buff .= sprintf("\t%02d. %s\n\t\t%0.6f sec. ", ++$num, $query['query'], $query['elapsed_time']); + if($query['result'] == 'Success') { + $buff .= "Query Success\n"; + } else { + $buff .= sprintf("Query $s : %d\n\t\t\t %s\n", $query['result'], $query['errno'], $query['errstr']); + } + $buff .= sprintf("\t\tConnection: %s\n", $query['connection']); + } + } + } + // Output in HTML comments + if($buff && __DEBUG_OUTPUT__ == 1 && Context::getResponseMethod() == 'HTML') { + $buff = sprintf("[%s %s:%d]\n%s\n", date('Y-m-d H:i:s'), $file_name, $line_num, print_r($buff, true)); + + if(__DEBUG_PROTECT__ == 1 && __DEBUG_PROTECT_IP__ != $_SERVER['REMOTE_ADDR']) { + $buff = 'The IP address is not allowed. Change the value of __DEBUG_PROTECT_IP__ into your IP address in config/config.user.inc.php or config/config.inc.php'; + } + + return ""; + } + // Output to a file + if($buff && __DEBUG_OUTPUT__ == 0) { + $debug_file = _XE_PATH_.'files/_debug_message.php'; + $buff = sprintf("[%s %s:%d]\n%s\n", date('Y-m-d H:i:s'), $file_name, $line_num, print_r($buff, true)); + + $buff = str_repeat('=', 40)."\n".$buff.str_repeat('-', 40); + $buff = "\n\n"; + + if(@!$fp = fopen($debug_file, 'a')) return; + fwrite($fp, $buff); + fclose($fp); + } + } + } + + /** + * @brief print a HTTP HEADER for XML, which is encoded in UTF-8 + **/ + 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 print a HTTP HEADER for HTML, which is encoded in UTF-8 + **/ + 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"); + } + + + /** + * @brief print a HTTP HEADER for JSON, which is encoded in UTF-8 + **/ + function _printJSONHeader() { + 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"); + } + + + /** + * @brief print a HTTP HEADER for HTML, which is encoded in UTF-8 + **/ + function _printHttpStatusCode($code) { + $statusMessage = Context::get('http_status_message'); + header("HTTP/1.0 $code $statusMessage"); + } + } +?> diff --git a/classes/display/HTMLDisplayHandler.php b/classes/display/HTMLDisplayHandler.php index 577f98163..5b35a2dde 100644 --- a/classes/display/HTMLDisplayHandler.php +++ b/classes/display/HTMLDisplayHandler.php @@ -5,19 +5,35 @@ class HTMLDisplayHandler { * @brief Produce HTML compliant content given a module object.\n * @param[in] $oModule the module object **/ - function toDoc(&$oModule) + function toDoc(&$oModule) { $oTemplate = &TemplateHandler::getInstance(); // compile module tpl - $template_path = $oModule->getTemplatePath(); + if ($oModule->module_info->module == $oModule->module) + $skin = $oModule->origin_module_info->skin; + else + $skin = $oModule->module_config->skin; + + if(Context::get('module')!='admin' && strpos(Context::get('act'),'Admin') === false){ + if ($skin && is_string($skin)){ + $theme_skin = explode('.', $skin); + if (count($theme_skin) == 2) + $template_path = sprintf('./themes/%s/modules/%s/', $theme_skin[0], $theme_skin[1]); + else + $template_path = $oModule->getTemplatePath(); + }else{ + $template_path = $oModule->getTemplatePath(); + } + }else $template_path = $oModule->getTemplatePath(); $tpl_file = $oModule->getTemplateFile(); + $output = $oTemplate->compile($template_path, $tpl_file); - // add #xeAdmin div for adminitration pages + // add .x div for adminitration pages if(Context::getResponseMethod() == 'HTML') { - if(Context::get('module')!='admin' && strpos(Context::get('act'),'Admin')>0) $output = '
'.$output.'
'; - + if(Context::get('module')!='admin' && strpos(Context::get('act'),'Admin')>0) $output = '
'.$output.'
'; + if(Context::get('layout') != 'none') { if(__DEBUG__==3) $start = getMicroTime(); @@ -28,32 +44,32 @@ class HTMLDisplayHandler { $edited_layout_file = $oModule->getEditedLayoutFile(); - // 현재 요청된 레이아웃 정보를 구함 + // get the layout information currently requested $oLayoutModel = &getModel('layout'); $layout_info = Context::get('layout_info'); $layout_srl = $layout_info->layout_srl; - // 레이아웃과 연결되어 있으면 레이아웃 컴파일 + // compile if connected to the layout if($layout_srl > 0){ - // faceoff 레이아웃일 경우 별도 처리 + // handle separately if the layout is faceoff if($layout_info && $layout_info->type == 'faceoff') { $oLayoutModel->doActivateFaceOff($layout_info); Context::set('layout_info', $layout_info); } - // 관리자 레이아웃 수정화면에서 변경된 CSS가 있는지 조사 + // search if the changes CSS exists in the admin layout edit window $edited_layout_css = $oLayoutModel->getUserLayoutCss($layout_srl); - if(file_exists($edited_layout_css)) Context::addCSSFile($edited_layout_css,true,'all','',100); + if(file_exists($edited_layout_css)) Context::loadFile(array($edited_layout_css,'all','',100)); } - if(!$layout_path) $layout_path = "./common/tpl"; - if(!$layout_file) $layout_file = "default_layout"; + if(!$layout_path) $layout_path = './common/tpl'; + if(!$layout_file) $layout_file = 'default_layout'; $output = $oTemplate->compile($layout_path, $layout_file, $edited_layout_file); if(__DEBUG__==3) $GLOBALS['__layout_compile_elapsed__'] = getMicroTime()-$start; - if(preg_match('/MSIE/i',$_SERVER['HTTP_USER_AGENT']) && (Context::get("_use_ssl")=='optional'||Context::get("_use_ssl")=="always")) { + if(preg_match('/MSIE/i',$_SERVER['HTTP_USER_AGENT']) && (Context::get('_use_ssl') == 'optional' || Context::get('_use_ssl') == 'always')) { Context::addHtmlFooter(''); } } @@ -63,16 +79,16 @@ class HTMLDisplayHandler { function prepareToPrint(&$output) { if(Context::getResponseMethod() != 'HTML') return; - + if(__DEBUG__==3) $start = getMicroTime(); - // body 내의 를 header로 이동 + // move in body to the header $output = preg_replace_callback('!!is', array($this,'_moveStyleToHeader'), $output); - // 메타 파일 변경 (캐싱기능등으로 인해 위젯등에서 태그를 content에 넣는 경우가 있음 + // change a meta fine(widget often put the tag like to the content because of caching) $output = preg_replace_callback('//is', array($this,'_transMeta'), $output); - // rewrite module 사용시 생기는 상대경로에 대한 처리를 함 + // handles a relative path generated by using the rewrite module if(Context::isAllowRewrite()) { $url = parse_url(Context::getRequestUri()); $real_path = $url['path']; @@ -89,15 +105,31 @@ class HTMLDisplayHandler { } } - // 간혹 background-image에 url(none) 때문에 request가 한번 더 일어나는 경우가 생기는 것을 방지 + // prevent the 2nd request due to url(none) of the background-image $output = preg_replace('/url\((["\']?)none(["\']?)\)/is', 'none', $output); + if(is_array(Context::get('INPUT_ERROR'))) + { + $INPUT_ERROR = Context::get('INPUT_ERROR'); + $keys = array_keys($INPUT_ERROR); + $keys = '('.implode('|', $keys).')'; + + $output = preg_replace_callback('@(]*?)\sname="'.$keys.'"([^>]*?)/?>@is', array(&$this, '_preserveValue'), $output); + } + if(__DEBUG__==3) $GLOBALS['__trans_content_elapsed__'] = getMicroTime()-$start; - // 불필요한 정보 제거 + // Remove unnecessary information $output = preg_replace('/member\_\-([0-9]+)/s','member_0',$output); - // 최종 레이아웃 변환 + // set icon + $oAdminModel = &getAdminModel('admin'); + $favicon_url = $oAdminModel->getFaviconUrl(); + $mobicon_url = $oAdminModel->getMobileIconUrl(); + Context::set('favicon_url', $favicon_url); + Context::set('mobicon_url', $mobicon_url); + + // convert the final layout Context::set('content', $output); $oTemplate = &TemplateHandler::getInstance(); if(Mobile::isFromMobilePhone()) { @@ -106,14 +138,45 @@ class HTMLDisplayHandler { else { $this->_loadJSCSS(); + $this->_addMetaTag(); $output = $oTemplate->compile('./common/tpl', 'common_layout'); } - // 사용자 정의 언어 변환 + // replace the user-defined-language $oModuleController = &getController('module'); $oModuleController->replaceDefinedLangCode($output); } + function _preserveValue($match) + { + $INPUT_ERROR = Context::get('INPUT_ERROR'); + + $str = $match[1].$match[2].' name="'.$match[3].'"'.$match[4]; + + // get type + $type = 'text'; + if(preg_match('/\stype="([a-z]+)"/i', $str, $m)) $type = strtolower($m[1]); + + switch($type){ + case 'text': + case 'hidden': + $str = preg_replace('@\svalue="[^"]*?"@', ' ', $str).' value="'.$INPUT_ERROR[$match[3]].'"'; + break; + case 'password': + $str = preg_replace('@\svalue="[^"]*?"@', ' ', $str); + break; + case 'radio': + case 'checkbox': + $str = preg_replace('@\schecked(="[^"]*?")?@', ' ', $str); + if(preg_match('@\s(?i:value)="'.$INPUT_ERROR[$match[3]].'"@', $str)) { + $str .= ' checked="checked"'; + } + break; + } + + return $str.' />'; + } + /** * @brief add html style code extracted from html body to Context, which will be * printed inside
later. @@ -124,33 +187,53 @@ class HTMLDisplayHandler { } /** - * @brief add given .css or .js file names in widget code to Context + * @brief add given .css or .js file names in widget code to Context * @param[in] $oModule the module object **/ function _transMeta($matches) { if($matches[1]) return ''; - if(substr($matches[2],'-4')=='.css') Context::addCSSFile($matches[2]); - elseif(substr($matches[2],'-3')=='.js') Context::addJSFile($matches[2]); + Context::loadFile($matches[2]); } function _loadJSCSS() { - $oContext =& Context::getInstance(); + $oContext =& Context::getInstance(); + $lang_type = Context::getLangType(); + // add common JS/CSS files - $oContext->addJsFile('./common/js/jquery.js', false, '', -100000); - $oContext->addJsFile('./common/js/x.js', false, '', -100000); - $oContext->addJsFile('./common/js/common.js', false, '', -100000); - $oContext->addJsFile('./common/js/js_app.js', false, '', -100000); - $oContext->addJsFile('./common/js/xml_handler.js', false, '', -100000); - $oContext->addJsFile('./common/js/xml_js_filter.js', false, '', -100000); - $oContext->addCSSFile('./common/css/default.css', false, 'all', '', -100000); - $oContext->addCSSFile('./common/css/button.css', false, 'all', '', -100000); + if(__DEBUG__) { + $oContext->loadFile(array('./common/js/jquery.js', 'head', '', -100000), true); + $oContext->loadFile(array('./common/js/x.js', 'head', '', -100000), true); + $oContext->loadFile(array('./common/js/common.js', 'head', '', -100000), true); + $oContext->loadFile(array('./common/js/js_app.js', 'head', '', -100000), true); + $oContext->loadFile(array('./common/js/xml_handler.js', 'head', '', -100000), true); + $oContext->loadFile(array('./common/js/xml_js_filter.js', 'head', '', -100000), true); + $oContext->loadFile(array('./common/css/xe.css', 'all', '', -100000), true); + } else { + $oContext->loadFile(array('./common/js/jquery.min.js', 'head', '', -100000), true); + $oContext->loadFile(array('./common/js/x.min.js', 'head', '', -100000), true); + $oContext->loadFile(array('./common/js/xe.min.js', 'head', '', -100000), true); + $oContext->loadFile(array('./common/css/xe.min.css', 'all', '', -100000), true); + } // for admin page, add admin css if(Context::get('module')=='admin' || strpos(Context::get('act'),'Admin')>0){ - $oContext->addCSSFile('./modules/admin/tpl/css/font.css', false, 'all', '',10000); - $oContext->addCSSFile('./modules/admin/tpl/css/pagination.css', false, 'all', '', 100001); - $oContext->addCSSFile('./modules/admin/tpl/css/admin.css', false, 'all', '', 100002); + if(__DEBUG__) { + $oContext->loadFile(array('./modules/admin/tpl/css/admin.css', 'all', '', 100000), true); + $oContext->loadFile(array("./modules/admin/tpl/css/admin_{$lang_type}.css", 'all', '', 100000), true); + $oContext->loadFile('./modules/admin/tpl/js/admin.js', true); + } else { + $oContext->loadFile(array('./modules/admin/tpl/css/admin.min.css', 'all', '', 100000), true); + $oContext->loadFile(array("./modules/admin/tpl/css/admin_{$lang_type}.css", 'all', '',10000), true); + $oContext->loadFile('./modules/admin/tpl/js/admin.min.js', true); + } } } + + function _addMetaTag() + { + $oContext =& Context::getInstance(); + $oContext->addMetaTag('Content-Type', 'text/html; charset=UTF-8', true); + $oContext->addMetaTag('imagetoolbar', 'no'); + } } diff --git a/classes/editor/EditorHandler.class.php b/classes/editor/EditorHandler.class.php index 930139792..0db5f451e 100644 --- a/classes/editor/EditorHandler.class.php +++ b/classes/editor/EditorHandler.class.php @@ -1,27 +1,27 @@ -extra_vars) return; - - foreach($info->extra_vars as $key => $val) { - $this->{$key} = trim($val->value); - } - } - - } - -?> +extra_vars) return; + + foreach($info->extra_vars as $key => $val) { + $this->{$key} = trim($val->value); + } + } + + } + +?> diff --git a/classes/extravar/Extravar.class.php b/classes/extravar/Extravar.class.php index bf3d83389..db16ac8ba 100644 --- a/classes/extravar/Extravar.class.php +++ b/classes/extravar/Extravar.class.php @@ -1,311 +1,304 @@ -module_srl = $module_srl; - } - - /** - * @brief 확장변수 키를 등록 - * @param module_srl, idx, name, type, default, desc, is_required, search, value - **/ - function setExtraVarKeys($extra_keys) { - if(!is_array($extra_keys) || !count($extra_keys)) return; - foreach($extra_keys as $key => $val) { - $obj = null; - $obj = new ExtraItem($val->module_srl, $val->idx, $val->name, $val->type, $val->default, $val->desc, $val->is_required, $val->search, $val->value, $val->eid); - $this->keys[$val->idx] = $obj; - } - } - - /** - * @brief 확장변수 객체 배열 return - **/ - function getExtraVars() { - return $this->keys; - } - } - - /** - * @class ExtraItem - * @author NHN (developers@xpressengine.com) - * @brief 확장변수의 개별 값 - **/ - class ExtraItem { - var $module_srl = 0; - var $idx = 0; - var $name = 0; - var $type = 'text'; - var $default = null; - var $desc = ''; - var $is_required = 'N'; - var $search = 'N'; - var $value = null; - var $eid = ''; - - /** - * @brief constructor - **/ - function ExtraItem($module_srl, $idx, $name, $type = 'text', $default = null, $desc = '', $is_required = 'N', $search = 'N', $value = null, $eid = '') { - if(!$idx) return; - $this->module_srl = $module_srl; - $this->idx = $idx; - $this->name = $name; - $this->type = $type; - $this->default = $default; - $this->desc = $desc; - $this->is_required = $is_required; - $this->search = $search; - $this->value = $value; - $this->eid = $eid; - } - - /** - * @brief 값 지정 - **/ - function setValue($value) { - $this->value = $value; - } - - /** - * @brief type에 따라서 주어진 값을 변형하여 원형 값을 return - **/ - function _getTypeValue($type, $value) { - $value = trim($value); - if(!isset($value)) return; - switch($type) { - case 'homepage' : - if($value && !preg_match('/^([a-z]+):\/\//i',$value)) $value = 'http://'.$value; - return htmlspecialchars($value); - break; - case 'tel' : - if(is_array($value)) $values = $value; - elseif(strpos($value,'|@|')!==false) $values = explode('|@|', $value); - elseif(strpos($value,',')!==false) $values = explode(',', $value); - $values[0] = $values[0]; - $values[1] = $values[1]; - $values[2] = $values[2]; - return $values; - break; - break; - case 'checkbox' : - case 'radio' : - case 'select' : - if(is_array($value)) $values = $value; - elseif(strpos($value,'|@|')!==false) $values = explode('|@|', $value); - elseif(strpos($value,',')!==false) $values = explode(',', $value); - else $values = array($value); - for($i=0;$i_getTypeValue($this->type, $this->value); - switch($this->type) { - case 'homepage' : - return ($value)?(sprintf('
%s', $value, strlen($value)>60?substr($value,0,40).'...'.substr($value,-10):$value)):""; - case 'email_address' : - return ($value)?sprintf('%s', $value, $value):""; - break; - case 'tel' : - return sprintf('%s - %s - %s', $value[0],$value[1],$value[2]); - break; - case 'textarea' : - return nl2br($value); - break; - case 'checkbox' : - if(is_array($value)) return implode(', ',$value); - else return $value; - break; - case 'date' : - return zdate($value,"Y-m-d"); - break; - case 'select' : - case 'radio' : - if(is_array($value)) return implode(', ',$value); - else return $value; - break; - case 'kr_zip' : - if(is_array($value)) return implode(' ',$value); - else return $value; - break; - // case 'text' : - default : - return $value; - } - } - - /** - * @brief type에 따른 form을 리턴 - **/ - function getFormHTML() { - static $id_num = 1000; - - $type = $this->type; - $name = $this->name; - $value = $this->_getTypeValue($this->type, $this->value); - $default = $this->_getTypeValue($this->type, $this->default); - $column_name = 'extra_vars'.$this->idx; - $tmp_id = $column_name.'-'.$id_num++; - - $buff = ''; - switch($type) { - // 홈페이지 주소 - case 'homepage' : - $buff .= ''; - break; - - // Email 주소 - case 'email_address' : - $buff .= ''; - break; - - // 전화번호 - case 'tel' : - $buff .= - ''. - ''. - ''; - break; - - // textarea - case 'textarea' : - $buff .= ''; - break; - - // 다중 선택 - case 'checkbox' : - $buff .= '
    '; - foreach($default as $v) { - if($value && in_array($v, $value)) $checked = ' checked="checked"'; - else $checked = ''; - - // Temporary ID for labeling - $tmp_id = $column_name.'-'.$id_num++; - - $buff .='
  • '; - } - $buff .= '
'; - break; - - // 단일 선택 - case 'select' : - $buff .= ''; - break; - - // radio - case 'radio' : - $buff .= '
    '; - foreach($default as $v) { - if($value && in_array($v,$value)) $checked = ' checked="checked"'; - else $checked = ''; - - // Temporary ID for labeling - $tmp_id = $column_name.'-'.$id_num++; - - $buff .= '
  • '; - } - $buff .= '
'; - break; - - // 날짜 입력 - case 'date' : - // datepicker javascript plugin load - Context::loadJavascriptPlugin('ui.datepicker'); - - $buff .= - ''. - ''."\n". - ''; - break; - - // 주소 입력 - case "kr_zip" : - // krzip address javascript plugin load - Context::loadJavascriptPlugin('ui.krzip'); - - $buff .= - ''. - - ''. - - ''. - - ''. - ''; - break; - - // 일반 text - default : - $buff .=' '; - break; - } - if($this->desc) $buff .= '

'.$this->desc.'

'; - return $buff; - } - } -?> +module_srl = $module_srl; + } + + /** + * @brief register a key of extra variable + * @param module_srl, idx, name, type, default, desc, is_required, search, value + **/ + function setExtraVarKeys($extra_keys) { + if(!is_array($extra_keys) || !count($extra_keys)) return; + foreach($extra_keys as $key => $val) { + $obj = null; + $obj = new ExtraItem($val->module_srl, $val->idx, $val->name, $val->type, $val->default, $val->desc, $val->is_required, $val->search, $val->value, $val->eid); + $this->keys[$val->idx] = $obj; + } + } + + /** + * @brief Return an array of extra vars + **/ + function getExtraVars() { + return $this->keys; + } + } + + /** + * @class ExtraItem + * @author NHN (developers@xpressengine.com) + * @brief each value of the extra vars + **/ + class ExtraItem { + var $module_srl = 0; + var $idx = 0; + var $name = 0; + var $type = 'text'; + var $default = null; + var $desc = ''; + var $is_required = 'N'; + var $search = 'N'; + var $value = null; + var $eid = ''; + + /** + * @brief constructor + **/ + function ExtraItem($module_srl, $idx, $name, $type = 'text', $default = null, $desc = '', $is_required = 'N', $search = 'N', $value = null, $eid = '') { + if(!$idx) return; + $this->module_srl = $module_srl; + $this->idx = $idx; + $this->name = $name; + $this->type = $type; + $this->default = $default; + $this->desc = $desc; + $this->is_required = $is_required; + $this->search = $search; + $this->value = $value; + $this->eid = $eid; + } + + /** + * @brief Values + **/ + function setValue($value) { + $this->value = $value; + } + + /** + * @brief return a given value converted based on its type + **/ + function _getTypeValue($type, $value) { + $value = trim($value); + if(!isset($value)) return; + switch($type) { + case 'homepage' : + if($value && !preg_match('/^([a-z]+):\/\//i',$value)) $value = 'http://'.$value; + return htmlspecialchars($value); + break; + case 'tel' : + if(is_array($value)) $values = $value; + elseif(strpos($value,'|@|')!==false) $values = explode('|@|', $value); + elseif(strpos($value,',')!==false) $values = explode(',', $value); + $values[0] = $values[0]; + $values[1] = $values[1]; + $values[2] = $values[2]; + return $values; + break; + break; + case 'checkbox' : + case 'radio' : + case 'select' : + if(is_array($value)) $values = $value; + elseif(strpos($value,'|@|')!==false) $values = explode('|@|', $value); + elseif(strpos($value,',')!==false) $values = explode(',', $value); + else $values = array($value); + for($i=0;$i_getTypeValue($this->type, $this->value); + switch($this->type) { + case 'homepage' : + return ($value)?(sprintf('%s', $value, strlen($value)>60?substr($value,0,40).'...'.substr($value,-10):$value)):""; + case 'email_address' : + return ($value)?sprintf('%s', $value, $value):""; + break; + case 'tel' : + return sprintf('%s - %s - %s', $value[0],$value[1],$value[2]); + break; + case 'textarea' : + return nl2br($value); + break; + case 'checkbox' : + if(is_array($value)) return implode(', ',$value); + else return $value; + break; + case 'date' : + return zdate($value,"Y-m-d"); + break; + case 'select' : + case 'radio' : + if(is_array($value)) return implode(', ',$value); + else return $value; + break; + case 'kr_zip' : + if(is_array($value)) return implode(' ',$value); + else return $value; + break; + // case 'text' : + default : + return $value; + } + } + + /** + * @brief return a form based on its type + **/ + function getFormHTML() { + static $id_num = 1000; + + $type = $this->type; + $name = $this->name; + $value = $this->_getTypeValue($this->type, $this->value); + $default = $this->_getTypeValue($this->type, $this->default); + $column_name = 'extra_vars'.$this->idx; + $tmp_id = $column_name.'-'.$id_num++; + + $buff = ''; + switch($type) { + // Homepage + case 'homepage' : + $buff .= ''; + break; + // Email Address + case 'email_address' : + $buff .= ''; + break; + // Phone Number + case 'tel' : + $buff .= + ''. + ''. + ''; + break; + + // textarea + case 'textarea' : + $buff .= ''; + break; + // multiple choice + case 'checkbox' : + $buff .= '
    '; + foreach($default as $v) { + if($value && in_array($v, $value)) $checked = ' checked="checked"'; + else $checked = ''; + + // Temporary ID for labeling + $tmp_id = $column_name.'-'.$id_num++; + + $buff .='
  • '; + } + $buff .= '
'; + break; + // single choice + case 'select' : + $buff .= ''; + break; + + // radio + case 'radio' : + $buff .= '
    '; + foreach($default as $v) { + if($value && in_array($v,$value)) $checked = ' checked="checked"'; + else $checked = ''; + + // Temporary ID for labeling + $tmp_id = $column_name.'-'.$id_num++; + + $buff .= '
  • '; + } + $buff .= '
'; + break; + // date + case 'date' : + // datepicker javascript plugin load + Context::loadJavascriptPlugin('ui.datepicker'); + + $buff .= + ''. + ''."\n". + ''; + break; + // address + case "kr_zip" : + // krzip address javascript plugin load + Context::loadJavascriptPlugin('ui.krzip'); + + $buff .= + ''. + + ''. + + ''. + + ''. + ''; + break; + // General text + default : + $buff .=' '; + break; + } + if($this->desc) $buff .= '

'.$this->desc.'

'; + return $buff; + } + } +?> diff --git a/classes/file/FileHandler.class.php b/classes/file/FileHandler.class.php index ee87578a9..72772089b 100644 --- a/classes/file/FileHandler.class.php +++ b/classes/file/FileHandler.class.php @@ -1,677 +1,680 @@ read()) { - if(substr($file,0,1)=='.') continue; - if($filter && preg_match($filter, $file)) continue; - if(is_dir($source_dir.$file)){ - FileHandler::copyDir($source_dir.$file,$target_dir.$file,$type); - }else{ - if($type == 'force'){ - @unlink($target_dir.$file); - }else{ - if(!file_exists($target_dir.$file)) @copy($source_dir.$file,$target_dir.$file); - } - } - } - } - - /** - * @brief copy a file to target - * @param[in] $source path of source file - * @param[in] $target path of target file - * @param[in] $force Y: overwrite - * @return none - **/ - function copyFile($source, $target, $force='Y'){ - setlocale(LC_CTYPE, 'en_US.UTF8', 'ko_KR.UTF8'); - $source = FileHandler::getRealPath($source); - $target_dir = FileHandler::getRealPath(dirname($target)); - $target = basename($target); - if(!file_exists($target_dir)) FileHandler::makeDir($target_dir); - if($force=='Y') @unlink($target_dir.'/'.$target); - @copy($source, $target_dir.'/'.$target); - } - - /** - * @brief returns the content of the file - * @param[in] $file_name path of target file - * @return the content of the file. if target file does not exist, this function returns nothing. - **/ - function readFile($file_name) { - $file_name = FileHandler::getRealPath($file_name); - - if(!file_exists($file_name)) return; - $filesize = filesize($file_name); - if($filesize<1) return; - - if(function_exists('file_get_contents')) return file_get_contents($file_name); - - $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 write $buff into the specified file - * @param[in] $file_name path of target file - * @param[in] $buff content to be writeen - * @param[in] $mode a(append) / w(write) - * @return none - **/ - function writeFile($file_name, $buff, $mode = "w") { - $file_name = FileHandler::getRealPath($file_name); - - $pathinfo = pathinfo($file_name); - $path = $pathinfo['dirname']; - if(!is_dir($path)) FileHandler::makeDir($path); - - $mode = strtolower($mode); - if($mode != "a") $mode = "w"; - if(@!$fp = fopen($file_name,$mode)) return false; - fwrite($fp, $buff); - fclose($fp); - @chmod($file_name, 0644); - } - - /** - * @brief remove a file - * @param[in] $file_name path of target file - * @return returns true on success or false on failure. - **/ - function removeFile($file_name) { - $file_name = FileHandler::getRealPath($file_name); - return (file_exists($file_name) && @unlink($file_name)); - } - - /** - * @brief rename a file - * @param[in] $source path of source file - * @param[in] $target path of target file - * @remarks In order to move a file, use this function. - * @return returns true on success or false on failure. - **/ - function rename($source, $target) { - $source = FileHandler::getRealPath($source); - $target = FileHandler::getRealPath($target); - return @rename($source, $target); - } - - /** - * @brief Move a file - * @param[in] $source path of source file - * @param[in] $target path of target file - * @return returns true on success or false on failure. - */ - function moveFile($source, $target) { - $source = FileHandler::getRealPath($source); - return (file_exists($source) && FileHandler::removeFile($target) && FileHandler::rename($source, $target)); - } - - /** - * @brief move a directory - * @param[in] $source_dir path of source directory - * @param[in] $target_dir path of target directory - * @remarks this function just wraps rename function. - * @return none - **/ - function moveDir($source_dir, $target_dir) { - FileHandler::rename($source_dir, $target_dir); - } - - /** - * @brief return list of the files in the path - * @param[in] $path path of target directory - * @param[in] $filter if specified, return only files matching with the filter - * @param[in] $to_lower if true, file names will be changed into lower case. - * @param[in] $concat_prefix if true, return file name as absolute path - * @remarks the array does not contain files, such as '.', '..', and files starting with '.' - * @return array of the filenames in the path - **/ - function readDir($path, $filter = '', $to_lower = false, $concat_prefix = false) { - $path = FileHandler::getRealPath($path); - - 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 = sprintf('%s%s', str_replace(_XE_PATH_, '', $path), $file); - } - - $output[] = $file; - } - if(!$output) return array(); - - return $output; - } - - /** - * @brief creates a directory - * @param[in] $path_string path of target directory - * @return true if success. it might return nothing when ftp is used and connection to the ftp address failed. - * @remarks This function creates directories recursively, which means that if ancestors of the target directory does not exist, they will be created too. - **/ - function makeDir($path_string) { - static $oFtp = null; - - // if safe_mode is on, use FTP - if(ini_get('safe_mode')) { - $ftp_info = Context::getFTPInfo(); - if($oFtp == null) { - if(!Context::isFTPRegisted()) return; - - require_once(_XE_PATH_.'libs/ftp.class.php'); - $oFtp = new ftp(); - if(!$ftp_info->ftp_host) $ftp_info->ftp_host = "127.0.0.1"; - if(!$ftp_info->ftp_port) $ftp_info->ftp_port = 21; - if(!$oFtp->ftp_connect($ftp_info->ftp_host, $ftp_info->ftp_port)) return; - if(!$oFtp->ftp_login($ftp_info->ftp_user, $ftp_info->ftp_password)) { - $oFtp->ftp_quit(); - return; - } + $oDir = dir($source_dir); + while($file = $oDir->read()) { + if(substr($file,0,1)=='.') continue; + if($filter && preg_match($filter, $file)) continue; + if(is_dir($source_dir.$file)){ + FileHandler::copyDir($source_dir.$file,$target_dir.$file,$type); + }else{ + if($type == 'force'){ + @unlink($target_dir.$file); + }else{ + if(!file_exists($target_dir.$file)) @copy($source_dir.$file,$target_dir.$file); } - $ftp_path = $ftp_info->ftp_root_path; - if(!$ftp_path) $ftp_path = "/"; - } - - $path_string = str_replace(_XE_PATH_,'',$path_string); - $path_list = explode('/', $path_string); - - $path = _XE_PATH_; - for($i=0;$iftp_mkdir($ftp_path); - $oFtp->ftp_site("CHMOD 777 ".$ftp_path); - } else { - @mkdir($path, 0755); - @chmod($path, 0755); - } - } - } - - return is_dir($path_string); - } - - /** - * @brief remove all files under the path - * @param[in] $path path of the target directory - * @return none - **/ - function removeDir($path) { - $path = FileHandler::getRealPath($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 remove a directory only if it is empty - * @param[in] $path path of the target directory - * @return none - **/ - function removeBlankDir($path) { - $item_cnt = 0; - - $path = FileHandler::getRealPath($path); - if(!is_dir($path)) return; - $directory = dir($path); - while($entry = $directory->read()) { - if ($entry == "." || $entry == "..") continue; - if (is_dir($path."/".$entry)) $item_cnt = FileHandler::removeBlankDir($path.'/'.$entry); - } - $directory->close(); - - if($item_cnt < 1) @rmdir($path); - } - - - /** - * @biref remove files in the target directory. - * @param[in] $path path of the target directory - * @remarks This function keeps the directory structure. - * @return none - **/ - function removeFilesInDir($path) { - $path = FileHandler::getRealPath($path); - if(!is_dir($path)) return; - $directory = dir($path); - while($entry = $directory->read()) { - if ($entry != "." && $entry != "..") { - if (is_dir($path."/".$entry)) { - FileHandler::removeFilesInDir($path."/".$entry); - } else { - @unlink($path."/".$entry); - } - } - } - $directory->close(); - } - - /** - * @brief makes file size byte into KB, MB according to the size - * @param[in] $size number of the size - * @return file size string - **/ - 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 return remote file's content via HTTP - * @param[in] $url the address of the target file - * @param[in] $body HTTP request body - * @param[in] $timeout connection timeout - * @param[in] $method GET/POST - * @param[in] $content_type content type header of HTTP request - * @param[in] $headers headers key vaule array. - * @param[in] $cookies cookies key value array. - * @param[in] $post_data request arguments array for POST method - * @return if success, the content of the target file. otherwise: none - * @remarks if the target is moved (when return code is 300~399), this function follows the location specified response header. - **/ - function getRemoteResource($url, $body = null, $timeout = 3, $method = 'GET', $content_type = null, $headers = array(), $cookies = array(), $post_data = array()) { - requirePear(); - require_once('HTTP/Request.php'); - - $parsed_url = parse_url(__PROXY_SERVER__); - if($parsed_url["host"]) { - $oRequest = new HTTP_Request(__PROXY_SERVER__); - $oRequest->setMethod('POST'); - $oRequest->_timeout = $timeout; - $oRequest->addPostData('arg', serialize(array('Destination'=>$url, 'method'=>$method, 'body'=>$body, 'content_type'=>$content_type, "headers"=>$headers, "post_data"=>$post_data))); - } else { - $oRequest = new HTTP_Request($url); - if(count($headers)) { - foreach($headers as $key => $val) { - $oRequest->addHeader($key, $val); - } - } - if($cookies[$host]) { - foreach($cookies[$host] as $key => $val) { - $oRequest->addCookie($key, $val); - } - } - if(count($post_data)) { - foreach($post_data as $key => $val) { - $oRequest->addPostData($key, $val); - } - } - if(!$content_type) $oRequest->addHeader('Content-Type', 'text/html'); - else $oRequest->addHeader('Content-Type', $content_type); - $oRequest->setMethod($method); - if($body) $oRequest->setBody($body); - - $oRequest->_timeout = $timeout; - } - - $oResponse = $oRequest->sendRequest(); - - $code = $oRequest->getResponseCode(); - $header = $oRequest->getResponseHeader(); - $response = $oRequest->getResponseBody(); - if($c = $oRequest->getResponseCookies()) { - foreach($c as $k => $v) { - $cookies[$host][$v['name']] = $v['value']; - } - } - - if($code > 300 && $code < 399 && $header['location']) { - return FileHandler::getRemoteResource($header['location'], $body, $timeout, $method, $content_type, $headers, $cookies, $post_data); - } - - if($code != 200) return; - - return $response; - } - - /** - * @brief retrieves remote file, then stores it into target path. - * @param[in] $url the address of the target file - * @param[in] $target_file the location to store - * @param[in] $body HTTP request body - * @param[in] $timeout connection timeout - * @param[in] $method GET/POST - * @param[in] $content_type content type header of HTTP request - * @param[in] $headers headers key vaule array. - * @return true: success, false: failed - **/ - function getRemoteFile($url, $target_filename, $body = null, $timeout = 3, $method = 'GET', $content_type = null, $headers = array()) { - $body = FileHandler::getRemoteResource($url, $body, $timeout, $method, $content_type, $headers); - if(!$body) return false; - $target_filename = FileHandler::getRealPath($target_filename); - FileHandler::writeFile($target_filename, $body); - return true; - } - - /** - * @brief convert size in string into numeric value - * @param[in] $val size in string (ex., 10, 10K, 10M, 10G ) - * @return converted size - */ - function returnBytes($val) - { - $val = trim($val); - $last = strtolower(substr($val, -1)); - if($last == 'g') $val *= 1024*1024*1024; - else if($last == 'm') $val *= 1024*1024; - else if($last == 'k') $val *= 1024; - - return $val; - } - - /** - * @brief check available memory to load image file - * @param[in] $imageInfo image info retrieved by getimagesize function - * @return true: it's ok, false: otherwise - */ - function checkMemoryLoadImage(&$imageInfo) - { - if(!function_exists('memory_get_usage')) return true; - $K64 = 65536; - $TWEAKFACTOR = 2.0; - $channels = $imageInfo['channels']; - if(!$channels) $channels = 6; //for png - $memoryNeeded = round( ($imageInfo[0] * $imageInfo[1] * $imageInfo['bits'] * $channels / 8 + $K64 ) * $TWEAKFACTOR ); - $availableMemory = FileHandler::returnBytes(ini_get('memory_limit')) - memory_get_usage(); - if($availableMemory < $memoryNeeded) return false; - return true; - } - - /** - * @brief moves an image file (resizing is possible) - * @param[in] $source_file path of the source file - * @param[in] $target_file path of the target file - * @param[in] $resize_width width to resize - * @param[in] $resize_height height to resize - * @param[in] $target_type if $target_type is set (gif, jpg, png, bmp), result image will be saved as target type - * @param[in] $thumbnail_type thumbnail type(crop, ratio) - * @return true: success, false: failed - **/ - function createImageFile($source_file, $target_file, $resize_width = 0, $resize_height = 0, $target_type = '', $thumbnail_type = 'crop') { - $source_file = FileHandler::getRealPath($source_file); - $target_file = FileHandler::getRealPath($target_file); - - if(!file_exists($source_file)) return; - if(!$resize_width) $resize_width = 100; - if(!$resize_height) $resize_height = $resize_width; - - // retrieve source image's information - $imageInfo = getimagesize($source_file); - if(!FileHandler::checkMemoryLoadImage($imageInfo)) return false; - list($width, $height, $type, $attrs) = $imageInfo; - - 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; - } - - // if original image is larger than specified size to resize, calculate the ratio - if($resize_width > 0 && $width >= $resize_width) $width_per = $resize_width / $width; - else $width_per = 1; - - if($resize_height>0 && $height >= $resize_height) $height_per = $resize_height / $height; - else $height_per = 1; - - if($thumbnail_type == 'ratio') { - if($width_per>$height_per) $per = $height_per; - else $per = $width_per; - $resize_width = $width * $per; - $resize_height = $height * $per; - } else { - if($width_per < $height_per) $per = $height_per; - else $per = $width_per; - } - - if(!$per) $per = 1; - - // get type of target file - if(!$target_type) $target_type = $type; - $target_type = strtolower($target_type); - - // create temporary image with target size - if(function_exists('imagecreatetruecolor')) $thumb = imagecreatetruecolor($resize_width, $resize_height); - else if(function_exists('imagecreate')) $thumb = imagecreate($resize_width, $resize_height); - else return false; - if(!$thumb) return false; - - $white = imagecolorallocate($thumb, 255,255,255); - imagefilledrectangle($thumb,0,0,$resize_width-1,$resize_height-1,$white); - - // create temporary image having original type - switch($type) { - case 'gif' : - if(!function_exists('imagecreatefromgif')) return false; - $source = imagecreatefromgif($source_file); - break; - // jpg - case 'jpeg' : - case 'jpg' : - if(!function_exists('imagecreatefromjpeg')) return false; - $source = imagecreatefromjpeg($source_file); - break; - // png - case 'png' : - if(!function_exists('imagecreatefrompng')) return false; - $source = imagecreatefrompng($source_file); - break; - // bmp - case 'wbmp' : - case 'bmp' : - if(!function_exists('imagecreatefromwbmp')) return false; - $source = imagecreatefromwbmp($source_file); - break; - default : - return; - } - - // resize original image and put it into temporary image - $new_width = (int)($width * $per); - $new_height = (int)($height * $per); - - if($thumbnail_type == 'crop') { - $x = (int)($resize_width/2 - $new_width/2); - $y = (int)($resize_height/2 - $new_height/2); - } else { - $x = 0; - $y = 0; - } - - if($source) { - 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 return false; - - // create directory - $path = dirname($target_file); - if(!is_dir($path)) FileHandler::makeDir($path); - - // write into the file - switch($target_type) { - case 'gif' : - if(!function_exists('imagegif')) return false; - $output = imagegif($thumb, $target_file); - break; - case 'jpeg' : - case 'jpg' : - if(!function_exists('imagejpeg')) return false; - $output = imagejpeg($thumb, $target_file, 100); - break; - case 'png' : - if(!function_exists('imagepng')) return false; - $output = imagepng($thumb, $target_file, 9); - break; - case 'wbmp' : - case 'bmp' : - if(!function_exists('imagewbmp')) return false; - $output = imagewbmp($thumb, $target_file, 100); - break; - } - - imagedestroy($thumb); - imagedestroy($source); - - if(!$output) return false; - @chmod($target_file, 0644); - - return true; - } - - /** - * @brief reads ini file, and puts result into array - * @param[in] $filename path of the ini file - * @return ini array (if the target file does not exist, it returns false) - **/ - function readIniFile($filename){ - $filename = FileHandler::getRealPath($filename); - if(!file_exists($filename)) return false; - $arr = parse_ini_file($filename, true); - if(is_array($arr) && count($arr)>0) return $arr; - else return array(); - } - - - /** - * @brief write array into ini file - * @param[in] $filename target ini file name - * @param[in] $arr array - * @return if array contains nothing it returns false, otherwise true - **/ - function writeIniFile($filename, $arr){ - if(count($arr)==0) return false; - FileHandler::writeFile($filename, FileHandler::_makeIniBuff($arr)); - return true; - } - - function _makeIniBuff($arr){ - $return = ''; - foreach($arr as $key => $val){ - // section - if(is_array($val)){ - $return .= sprintf("[%s]\n",$key); - foreach($val as $k => $v){ - $return .= sprintf("%s=\"%s\"\n",$k,$v); - } - // value - }else if(is_string($val) || is_int($val)){ - $return .= sprintf("%s=\"%s\"\n",$key,$val); - } - } - return $return; - } - - /** - * @brief return file object - * @param[in] $filename target file name - * @param[in] $mode file mode for fopen - * @remarks if the directory of the file does not exist, create it. - * @return file object - **/ - function openFile($filename, $mode) - { - $pathinfo = pathinfo($filename); - $path = $pathinfo['dirname']; - if(!is_dir($path)) FileHandler::makeDir($path); - - require_once("FileObject.class.php"); - $file_object = new FileObject($file_name, $mode); - return $file_object; - } - - /** - * @brief check whether the given file has the content. - * @param[in] $file_name target file name - * @return return true if the file exists and contains something. - */ - function hasContent($filename) - { - return (is_readable($filename) && !!filesize($filename)); + } } - } -?> \ No newline at end of file + } + + /** + * @brief copy a file to target + * @param[in] $source path of source file + * @param[in] $target path of target file + * @param[in] $force Y: overwrite + * @return none + **/ + function copyFile($source, $target, $force='Y'){ + setlocale(LC_CTYPE, 'en_US.UTF8', 'ko_KR.UTF8'); + $source = FileHandler::getRealPath($source); + $target_dir = FileHandler::getRealPath(dirname($target)); + $target = basename($target); + if(!file_exists($target_dir)) FileHandler::makeDir($target_dir); + if($force=='Y') @unlink($target_dir.'/'.$target); + @copy($source, $target_dir.'/'.$target); + } + + /** + * @brief returns the content of the file + * @param[in] $file_name path of target file + * @return the content of the file. if target file does not exist, this function returns nothing. + **/ + function readFile($file_name) { + $file_name = FileHandler::getRealPath($file_name); + + if(!file_exists($file_name)) return; + $filesize = filesize($file_name); + if($filesize<1) return; + + if(function_exists('file_get_contents')) return file_get_contents($file_name); + + $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 write $buff into the specified file + * @param[in] $file_name path of target file + * @param[in] $buff content to be writeen + * @param[in] $mode a(append) / w(write) + * @return none + **/ + function writeFile($file_name, $buff, $mode = "w") { + $file_name = FileHandler::getRealPath($file_name); + + $pathinfo = pathinfo($file_name); + $path = $pathinfo['dirname']; + if(!is_dir($path)) FileHandler::makeDir($path); + + $mode = strtolower($mode); + if($mode != "a") $mode = "w"; + if(@!$fp = fopen($file_name,$mode)) return false; + fwrite($fp, $buff); + fclose($fp); + @chmod($file_name, 0644); + } + + /** + * @brief remove a file + * @param[in] $file_name path of target file + * @return returns true on success or false on failure. + **/ + function removeFile($file_name) { + $file_name = FileHandler::getRealPath($file_name); + return (file_exists($file_name) && @unlink($file_name)); + } + + /** + * @brief rename a file + * @param[in] $source path of source file + * @param[in] $target path of target file + * @remarks In order to move a file, use this function. + * @return returns true on success or false on failure. + **/ + function rename($source, $target) { + $source = FileHandler::getRealPath($source); + $target = FileHandler::getRealPath($target); + return @rename($source, $target); + } + + /** + * @brief Move a file + * @param[in] $source path of source file + * @param[in] $target path of target file + * @return returns true on success or false on failure. + */ + function moveFile($source, $target) { + $source = FileHandler::getRealPath($source); + return (file_exists($source) && FileHandler::removeFile($target) && FileHandler::rename($source, $target)); + } + + /** + * @brief move a directory + * @param[in] $source_dir path of source directory + * @param[in] $target_dir path of target directory + * @remarks this function just wraps rename function. + * @return none + **/ + function moveDir($source_dir, $target_dir) { + FileHandler::rename($source_dir, $target_dir); + } + + /** + * @brief return list of the files in the path + * @param[in] $path path of target directory + * @param[in] $filter if specified, return only files matching with the filter + * @param[in] $to_lower if true, file names will be changed into lower case. + * @param[in] $concat_prefix if true, return file name as absolute path + * @remarks the array does not contain files, such as '.', '..', and files starting with '.' + * @return array of the filenames in the path + **/ + function readDir($path, $filter = '', $to_lower = false, $concat_prefix = false) { + $path = FileHandler::getRealPath($path); + + 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 = sprintf('%s%s', str_replace(_XE_PATH_, '', $path), $file); + } + + $output[] = $file; + } + if(!$output) return array(); + + return $output; + } + + /** + * @brief creates a directory + * @param[in] $path_string path of target directory + * @return true if success. it might return nothing when ftp is used and connection to the ftp address failed. + * @remarks This function creates directories recursively, which means that if ancestors of the target directory does not exist, they will be created too. + **/ + function makeDir($path_string) { + static $oFtp = null; + + // if safe_mode is on, use FTP + if(ini_get('safe_mode')) { + $ftp_info = Context::getFTPInfo(); + if($oFtp == null) { + if(!Context::isFTPRegisted()) return; + + require_once(_XE_PATH_.'libs/ftp.class.php'); + $oFtp = new ftp(); + if(!$ftp_info->ftp_host) $ftp_info->ftp_host = "127.0.0.1"; + if(!$ftp_info->ftp_port) $ftp_info->ftp_port = 21; + if(!$oFtp->ftp_connect($ftp_info->ftp_host, $ftp_info->ftp_port)) return; + if(!$oFtp->ftp_login($ftp_info->ftp_user, $ftp_info->ftp_password)) { + $oFtp->ftp_quit(); + return; + } + } + $ftp_path = $ftp_info->ftp_root_path; + if(!$ftp_path) $ftp_path = "/"; + } + + $path_string = str_replace(_XE_PATH_,'',$path_string); + $path_list = explode('/', $path_string); + + $path = _XE_PATH_; + for($i=0;$iftp_mkdir($ftp_path); + $oFtp->ftp_site("CHMOD 777 ".$ftp_path); + } else { + @mkdir($path, 0755); + @chmod($path, 0755); + } + } + } + + return is_dir($path_string); + } + + /** + * @brief remove all files under the path + * @param[in] $path path of the target directory + * @return none + **/ + function removeDir($path) { + $path = FileHandler::getRealPath($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 remove a directory only if it is empty + * @param[in] $path path of the target directory + * @return none + **/ + function removeBlankDir($path) { + $item_cnt = 0; + + $path = FileHandler::getRealPath($path); + if(!is_dir($path)) return; + $directory = dir($path); + while($entry = $directory->read()) { + if ($entry == "." || $entry == "..") continue; + if (is_dir($path."/".$entry)) $item_cnt = FileHandler::removeBlankDir($path.'/'.$entry); + } + $directory->close(); + + if($item_cnt < 1) @rmdir($path); + } + + + /** + * @biref remove files in the target directory. + * @param[in] $path path of the target directory + * @remarks This function keeps the directory structure. + * @return none + **/ + function removeFilesInDir($path) { + $path = FileHandler::getRealPath($path); + if(!is_dir($path)) return; + $directory = dir($path); + while($entry = $directory->read()) { + if ($entry != "." && $entry != "..") { + if (is_dir($path."/".$entry)) { + FileHandler::removeFilesInDir($path."/".$entry); + } else { + @unlink($path."/".$entry); + } + } + } + $directory->close(); + } + + /** + * @brief makes file size byte into KB, MB according to the size + * @param[in] $size number of the size + * @return file size string + **/ + function filesize($size) { + if(!$size) return '0Byte'; + if($size === 1) return '1Byte'; + if($size < 1024) return $size.'Bytes'; + if($size >= 1024 && $size < 1024*1024) return sprintf("%0.1fKB",$size / 1024); + return sprintf("%0.2fMB",$size / (1024*1024)); + } + + /** + * @brief return remote file's content via HTTP + * @param[in] $url the address of the target file + * @param[in] $body HTTP request body + * @param[in] $timeout connection timeout + * @param[in] $method GET/POST + * @param[in] $content_type content type header of HTTP request + * @param[in] $headers headers key vaule array. + * @param[in] $cookies cookies key value array. + * @param[in] $post_data request arguments array for POST method + * @return if success, the content of the target file. otherwise: none + * @remarks if the target is moved (when return code is 300~399), this function follows the location specified response header. + **/ + function getRemoteResource($url, $body = null, $timeout = 3, $method = 'GET', $content_type = null, $headers = array(), $cookies = array(), $post_data = array()) { + requirePear(); + require_once('HTTP/Request.php'); + + $parsed_url = parse_url(__PROXY_SERVER__); + if($parsed_url["host"]) { + $oRequest = new HTTP_Request(__PROXY_SERVER__); + $oRequest->setMethod('POST'); + $oRequest->_timeout = $timeout; + $oRequest->addPostData('arg', serialize(array('Destination'=>$url, 'method'=>$method, 'body'=>$body, 'content_type'=>$content_type, "headers"=>$headers, "post_data"=>$post_data))); + } else { + $oRequest = new HTTP_Request($url); + if(count($headers)) { + foreach($headers as $key => $val) { + $oRequest->addHeader($key, $val); + } + } + if($cookies[$host]) { + foreach($cookies[$host] as $key => $val) { + $oRequest->addCookie($key, $val); + } + } + if(count($post_data)) { + foreach($post_data as $key => $val) { + $oRequest->addPostData($key, $val); + } + } + if(!$content_type) $oRequest->addHeader('Content-Type', 'text/html'); + else $oRequest->addHeader('Content-Type', $content_type); + $oRequest->setMethod($method); + if($body) $oRequest->setBody($body); + + $oRequest->_timeout = $timeout; + } + + $oResponse = $oRequest->sendRequest(); + + $code = $oRequest->getResponseCode(); + $header = $oRequest->getResponseHeader(); + $response = $oRequest->getResponseBody(); + if($c = $oRequest->getResponseCookies()) { + foreach($c as $k => $v) { + $cookies[$host][$v['name']] = $v['value']; + } + } + + if($code > 300 && $code < 399 && $header['location']) { + return FileHandler::getRemoteResource($header['location'], $body, $timeout, $method, $content_type, $headers, $cookies, $post_data); + } + + if($code != 200) return; + + return $response; + } + + /** + * @brief retrieves remote file, then stores it into target path. + * @param[in] $url the address of the target file + * @param[in] $target_file the location to store + * @param[in] $body HTTP request body + * @param[in] $timeout connection timeout + * @param[in] $method GET/POST + * @param[in] $content_type content type header of HTTP request + * @param[in] $headers headers key vaule array. + * @return true: success, false: failed + **/ + function getRemoteFile($url, $target_filename, $body = null, $timeout = 3, $method = 'GET', $content_type = null, $headers = array()) { + $body = FileHandler::getRemoteResource($url, $body, $timeout, $method, $content_type, $headers); + if(!$body) return false; + $target_filename = FileHandler::getRealPath($target_filename); + FileHandler::writeFile($target_filename, $body); + return true; + } + + /** + * @brief convert size in string into numeric value + * @param[in] $val size in string (ex., 10, 10K, 10M, 10G ) + * @return converted size + */ + function returnBytes($val) + { + $val = trim($val); + $last = strtolower(substr($val, -1)); + if($last == 'g') $val *= 1024*1024*1024; + else if($last == 'm') $val *= 1024*1024; + else if($last == 'k') $val *= 1024; + else $val *= 1; + + return $val; + } + + /** + * @brief check available memory to load image file + * @param[in] $imageInfo image info retrieved by getimagesize function + * @return true: it's ok, false: otherwise + */ + function checkMemoryLoadImage(&$imageInfo) + { + if(!function_exists('memory_get_usage')) return true; + $K64 = 65536; + $TWEAKFACTOR = 2.0; + $channels = $imageInfo['channels']; + if(!$channels) $channels = 6; //for png + $memoryNeeded = round( ($imageInfo[0] * $imageInfo[1] * $imageInfo['bits'] * $channels / 8 + $K64 ) * $TWEAKFACTOR ); + $availableMemory = FileHandler::returnBytes(ini_get('memory_limit')) - memory_get_usage(); + if($availableMemory < $memoryNeeded) return false; + return true; + } + + /** + * @brief moves an image file (resizing is possible) + * @param[in] $source_file path of the source file + * @param[in] $target_file path of the target file + * @param[in] $resize_width width to resize + * @param[in] $resize_height height to resize + * @param[in] $target_type if $target_type is set (gif, jpg, png, bmp), result image will be saved as target type + * @param[in] $thumbnail_type thumbnail type(crop, ratio) + * @return true: success, false: failed + **/ + function createImageFile($source_file, $target_file, $resize_width = 0, $resize_height = 0, $target_type = '', $thumbnail_type = 'crop') { + $source_file = FileHandler::getRealPath($source_file); + $target_file = FileHandler::getRealPath($target_file); + + if(!file_exists($source_file)) return; + if(!$resize_width) $resize_width = 100; + if(!$resize_height) $resize_height = $resize_width; + + // retrieve source image's information + $imageInfo = getimagesize($source_file); + if(!FileHandler::checkMemoryLoadImage($imageInfo)) return false; + list($width, $height, $type, $attrs) = $imageInfo; + + 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; + } + + // if original image is larger than specified size to resize, calculate the ratio + if($resize_width > 0 && $width >= $resize_width) $width_per = $resize_width / $width; + else $width_per = 1; + + if($resize_height>0 && $height >= $resize_height) $height_per = $resize_height / $height; + else $height_per = 1; + + if($thumbnail_type == 'ratio') { + if($width_per>$height_per) $per = $height_per; + else $per = $width_per; + $resize_width = $width * $per; + $resize_height = $height * $per; + } else { + if($width_per < $height_per) $per = $height_per; + else $per = $width_per; + } + + if(!$per) $per = 1; + + // get type of target file + if(!$target_type) $target_type = $type; + $target_type = strtolower($target_type); + + // create temporary image with target size + if(function_exists('imagecreatetruecolor')) $thumb = imagecreatetruecolor($resize_width, $resize_height); + else if(function_exists('imagecreate')) $thumb = imagecreate($resize_width, $resize_height); + else return false; + if(!$thumb) return false; + + $white = imagecolorallocate($thumb, 255,255,255); + imagefilledrectangle($thumb,0,0,$resize_width-1,$resize_height-1,$white); + + // create temporary image having original type + switch($type) { + case 'gif' : + if(!function_exists('imagecreatefromgif')) return false; + $source = imagecreatefromgif($source_file); + break; + // jpg + case 'jpeg' : + case 'jpg' : + if(!function_exists('imagecreatefromjpeg')) return false; + $source = imagecreatefromjpeg($source_file); + break; + // png + case 'png' : + if(!function_exists('imagecreatefrompng')) return false; + $source = imagecreatefrompng($source_file); + break; + // bmp + case 'wbmp' : + case 'bmp' : + if(!function_exists('imagecreatefromwbmp')) return false; + $source = imagecreatefromwbmp($source_file); + break; + default : + return; + } + + // resize original image and put it into temporary image + $new_width = (int)($width * $per); + $new_height = (int)($height * $per); + + if($thumbnail_type == 'crop') { + $x = (int)($resize_width/2 - $new_width/2); + $y = (int)($resize_height/2 - $new_height/2); + } else { + $x = 0; + $y = 0; + } + + if($source) { + 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 return false; + + // create directory + $path = dirname($target_file); + if(!is_dir($path)) FileHandler::makeDir($path); + + // write into the file + switch($target_type) { + case 'gif' : + if(!function_exists('imagegif')) return false; + $output = imagegif($thumb, $target_file); + break; + case 'jpeg' : + case 'jpg' : + if(!function_exists('imagejpeg')) return false; + $output = imagejpeg($thumb, $target_file, 100); + break; + case 'png' : + if(!function_exists('imagepng')) return false; + $output = imagepng($thumb, $target_file, 9); + break; + case 'wbmp' : + case 'bmp' : + if(!function_exists('imagewbmp')) return false; + $output = imagewbmp($thumb, $target_file, 100); + break; + } + + imagedestroy($thumb); + imagedestroy($source); + + if(!$output) return false; + @chmod($target_file, 0644); + + return true; + } + + /** + * @brief reads ini file, and puts result into array + * @param[in] $filename path of the ini file + * @return ini array (if the target file does not exist, it returns false) + **/ + function readIniFile($filename){ + $filename = FileHandler::getRealPath($filename); + if(!file_exists($filename)) return false; + $arr = parse_ini_file($filename, true); + if(is_array($arr) && count($arr)>0) return $arr; + else return array(); + } + + + /** + * @brief write array into ini file + * @param[in] $filename target ini file name + * @param[in] $arr array + * @return if array contains nothing it returns false, otherwise true + **/ + function writeIniFile($filename, $arr){ + if(count($arr)==0) return false; + FileHandler::writeFile($filename, FileHandler::_makeIniBuff($arr)); + return true; + } + + function _makeIniBuff($arr){ + $return = ''; + foreach($arr as $key => $val){ + // section + if(is_array($val)){ + $return .= sprintf("[%s]\n",$key); + foreach($val as $k => $v){ + $return .= sprintf("%s=\"%s\"\n",$k,$v); + } + // value + }else if(is_string($val) || is_int($val)){ + $return .= sprintf("%s=\"%s\"\n",$key,$val); + } + } + return $return; + } + + /** + * @brief return file object + * @param[in] $filename target file name + * @param[in] $mode file mode for fopen + * @remarks if the directory of the file does not exist, create it. + * @return file object + **/ + function openFile($filename, $mode) + { + $pathinfo = pathinfo($filename); + $path = $pathinfo['dirname']; + if(!is_dir($path)) FileHandler::makeDir($path); + + require_once("FileObject.class.php"); + $file_object = new FileObject($file_name, $mode); + return $file_object; + } + + /** + * @brief check whether the given file has the content. + * @param[in] $file_name target file name + * @return return true if the file exists and contains something. + */ + function hasContent($filename) + { + return (is_readable($filename) && !!filesize($filename)); + } +} + +/* End of file FileObject.class.php */ +/* Location: ./classes/file/FileObject.class.php */ diff --git a/classes/file/FileObject.class.php b/classes/file/FileObject.class.php index cbe1ae2b0..fd83b127c 100644 --- a/classes/file/FileObject.class.php +++ b/classes/file/FileObject.class.php @@ -1,128 +1,129 @@ -Open($path, $mode); - } - - /** - * @brief append target file's content to current file - * @param[in] $file_name path of target file - * @return none - **/ - function append($file_name) - { - $target = new FileObject($file_name, "r"); - while(!$target->feof()) - { - $readstr = $target->read(); - $this->write($readstr); - } - $target->close(); - } - - /** - * @brief check current file meets eof - * @return true: if eof. false: otherwise - **/ - function feof() - { - return feof($this->fp); - } - - /** - * @brief read from current file - * @param[in] $size size to read - * @return read bytes - **/ - function read($size = 1024) - { - return fread($this->fp, $size); - } - - - /** - * @brief write string to current file - * @param[in] $str string to write - * @return written bytes. if failed, it returns false - **/ - function write($str) - { - $len = strlen($str); - if(!$str || $len <= 0) return false; - if(!$this->fp) return false; - $written = fwrite($this->fp, $str); - return $written; - } - - /** - * @brief open a file - * @param[in] $path path of target file - * @param[in] $mode file open mode - * @remarks if file is opened, close it and open the new path - * @return true if succeed, false otherwise. - */ - function open($path, $mode) - { - if($this->fp != null) - { - $this->close(); - } - $this->fp = fopen($path, $mode); - if(! is_resource($this->fp) ) - { - $this->fp = null; - return false; - } - $this->path = $path; - return true; - } - - /** - * @brief return current file's path - * @return file path - **/ - function getPath() - { - if($this->fp != null) - { - return $this->path; - } - else - { - return null; - } - } - - /** - * @brief close file - * @return none - **/ - function close() - { - if($this->fp != null) - { - fclose($this->fp); - $this->fp = null; - } - } - - } -?> +Open($path, $mode); + } + + /** + * @brief append target file's content to current file + * @param[in] $file_name path of target file + * @return none + **/ + function append($file_name) + { + $target = new FileObject($file_name, "r"); + while(!$target->feof()) + { + $readstr = $target->read(); + $this->write($readstr); + } + $target->close(); + } + + /** + * @brief check current file meets eof + * @return true: if eof. false: otherwise + **/ + function feof() + { + return feof($this->fp); + } + + /** + * @brief read from current file + * @param[in] $size size to read + * @return read bytes + **/ + function read($size = 1024) + { + return fread($this->fp, $size); + } + + + /** + * @brief write string to current file + * @param[in] $str string to write + * @return written bytes. if failed, it returns false + **/ + function write($str) + { + $len = strlen($str); + if(!$str || $len <= 0) return false; + if(!$this->fp) return false; + $written = fwrite($this->fp, $str); + return $written; + } + + /** + * @brief open a file + * @param[in] $path path of target file + * @param[in] $mode file open mode + * @remarks if file is opened, close it and open the new path + * @return true if succeed, false otherwise. + */ + function open($path, $mode) + { + if($this->fp != null) + { + $this->close(); + } + $this->fp = fopen($path, $mode); + if(! is_resource($this->fp) ) + { + $this->fp = null; + return false; + } + $this->path = $path; + return true; + } + + /** + * @brief return current file's path + * @return file path + **/ + function getPath() + { + if($this->fp != null) + { + return $this->path; + } + else + { + return null; + } + } + + /** + * @brief close file + * @return none + **/ + function close() + { + if($this->fp != null) + { + fclose($this->fp); + $this->fp = null; + } + } +} + +/* End of file FileObject.class.php */ +/* Location: ./classes/file/FileObject.class.php */ diff --git a/classes/frontendfile/FrontEndFileHandler.class.php b/classes/frontendfile/FrontEndFileHandler.class.php new file mode 100644 index 000000000..b54c8d4b4 --- /dev/null +++ b/classes/frontendfile/FrontEndFileHandler.class.php @@ -0,0 +1,262 @@ +fileName = $pathInfo['basename']; + $file->filePath = $this->_getAbsFileUrl($pathInfo['dirname']); + $file->fileExtension = strtolower($pathInfo['extension']); + + if (strpos($file->filePath, '://') == false) + { + $file->useCdn = $useCdn; + $file->cdnPath = $this->_normalizeFilePath($pathInfo['dirname']); + $file->cdnPrefix = $cdnPrefix; + $file->cdnVersion = $cdnVersion; + } + + $availableExtension = array('css', 'js'); + if (!in_array($file->fileExtension, $availableExtension)) return; + + $file->targetIe = $args[2]; + $file->index = (int)$args[3]; + + if ($file->fileExtension == 'css') + { + $file->media = $args[1]; + if (!$file->media) $file->media = 'all'; + $map = &$this->cssMap; + $mapIndex = &$this->cssMapIndex; + $key = $file->filePath . $file->fileName . "\t" . $file->targetIe . "\t" . $file->media; + } + else if ($file->fileExtension == 'js') + { + $type = $args[1]; + if ($type == 'body') + { + $map = &$this->jsBodyMap; + $mapIndex = &$this->jsBodyMapIndex; + } + else + { + $map = &$this->jsHeadMap; + $mapIndex = &$this->jsHeadMapIndex; + } + $key = $file->filePath . $file->fileName . "\t" . $file->targetIe; + } + + if (!isset($map[$key]) || $mapIndex[$key] != $file->index) + { + $this->unloadFile($args[0], $args[2], $args[1]); + $map[$file->index][$key] = $file; + $mapIndex[$key] = $file->index; + } + } + + function unloadFile($fileName, $targetIe = '', $media = 'all') + { + $pathInfo = pathinfo($fileName); + $fileName = $pathInfo['basename']; + $filePath = $this->_getAbsFileUrl($pathInfo['dirname']); + $fileExtension = strtolower($pathInfo['extension']); + $key = $filePath . $fileName . "\t" . $targetIe; + + if ($fileExtension == 'css') + { + $key .= "\t" . $media; + if (isset($this->cssMapIndex[$key])) + { + $index = $this->cssMapIndex[$key]; + unset($this->cssMap[$index][$key]); + unset($this->cssMapIndex[$key]); + } + } + else + { + if (isset($this->jsHeadMapIndex[$key])) + { + $index = $this->jsHeadMapIndex[$key]; + unset($this->jsHeadMap[$index][$key]); + unset($this->jsHeadMapIndex[$key]); + } + if (isset($this->jsBodyMapIndex[$key])) + { + $index = $this->jsBodyMapIndex[$key]; + unset($this->jsBodyMap[$index][$key]); + unset($this->jsBodyMapIndex[$key]); + } + } + } + + function unloadAllFiles($type = 'all') + { + if ($type == 'css' || $type == 'all') + { + $cssMap = array(); + $cssMapIndex = array(); + } + + if ($type == 'js' || $type == 'all') + { + $jsHeadMap = array(); + $jsBodyMap = array(); + $jsHeadMapIndex = array(); + $jsBodyMapIndex = array(); + } + } + + function getCssFileList() + { + $map = &$this->cssMap; + $mapIndex = &$this->cssMapIndex; + + $this->_sortMap($map, $mapIndex); + + $dbInfo = Context::getDBInfo(); + $useCdn = $dbInfo->use_cdn; + + $result = array(); + foreach($map as $indexedMap) + { + foreach($indexedMap as $file) + { + if ($this->isSsl() == false && $useCdn == 'Y' && $file->useCdn && $file->cdnVersion != '%__XE_CDN_VERSION__%') + { + $fullFilePath = $file->cdnPrefix . $file->cdnVersion . '/' . substr($file->cdnPath, 2) . '/' . $file->fileName; + } + else + { + $fullFilePath = $file->filePath . '/' . $file->fileName; + } + $result[] = array('file' => $fullFilePath, 'media' => $file->media, 'targetie' => $file->targetIe); + } + } + + return $result; + } + + function getJsFileList($type = 'head') + { + if ($type == 'head') + { + $map = &$this->jsHeadMap; + $mapIndex = &$this->jsHeadMapIndex; + } + else + { + $map = &$this->jsBodyMap; + $mapIndex = &$this->jsBodyMapIndex; + } + + $this->_sortMap($map, $mapIndex); + + $dbInfo = Context::getDBInfo(); + $useCdn = $dbInfo->use_cdn; + + $result = array(); + foreach($map as $indexedMap) + { + foreach($indexedMap as $file) + { + if ($this->isSsl() == false && $useCdn == 'Y' && $file->useCdn && $file->cdnVersion != '%__XE_CDN_VERSION__%') + { + $fullFilePath = $file->cdnPrefix . $file->cdnVersion . '/' . substr($file->cdnPath, 2) . '/' . $file->fileName; + } + else + { + $fullFilePath = $file->filePath . '/' . $file->fileName; + } + $result[] = array('file' => $fullFilePath, 'targetie' => $file->targetIe); + } + } + + return $result; + } + + function _sortMap(&$map, &$index) + { + ksort($map); + } + + function _normalizeFilePath($path) + { + if (strpos($path, '://') === false && $path{0} != '/' && $path{0} != '.') + { + $path = './' . $path; + } + + $path = preg_replace('@/\./|(?_normalizeFilePath($path); + + if(strpos($path, './') === 0) + { + if (dirname($_SERVER['SCRIPT_NAME']) == '/' || dirname($_SERVER['SCRIPT_NAME']) == '\\') + { + $path = '/' . substr($path, 2); + } + else + { + $path = dirname($_SERVER['SCRIPT_NAME']) . '/' . substr($path, 2); + } + } + else if(strpos($file, '../') === 0) + { + $path= $this->_normalizeFilePath(dirname($_SERVER['SCRIPT_NAME']) . "/{$path}"); + } + + return $path; + } + } diff --git a/classes/handler/Handler.class.php b/classes/handler/Handler.class.php index 375dfe281..1454f733e 100644 --- a/classes/handler/Handler.class.php +++ b/classes/handler/Handler.class.php @@ -1,11 +1,11 @@ - + diff --git a/classes/httprequest/XEHttpRequest.class.php b/classes/httprequest/XEHttpRequest.class.php index cf09444ff..621676cf5 100644 --- a/classes/httprequest/XEHttpRequest.class.php +++ b/classes/httprequest/XEHttpRequest.class.php @@ -1,85 +1,184 @@ -m_host = $host; - $this->m_port = $port; - $this->m_headers = array(); - } - - /** - * @brief mether to add key/value pair to the HTTP request header - * @param[in] key HTTP header element - * @param[in] value value string for HTTP header element - */ - function AddToHeader($key, $value) - { - $this->m_headers[$key] = $value; - } - - /** - * @brief send HTTP message to the host - * @param[in] target ip or url of the external server - * @param[in] method HTTP method such as GET and POST - * @param[in] timeout time out value for HTTP request expiration - * @return Returns an object containing HTTP Response body and HTTP response code - */ - function Send($target, $method="GET", $timeout = 3) - { - $socket = @fsockopen($this->m_host, $this->m_port, $errno, $errstr, $timeout); - if(!$socket) - { - return new Object(-1, "socket_connect_failed"); - } - - $this->AddToHeader('Host', $this->m_host); - $this->AddToHeader('Connection', "close"); - - $crlf = "\r\n"; - $request = "$method $target HTTP/1.1$crlf"; - - foreach($this->m_headers as $equiv => $content) - { - $request .= "$equiv: $content$crlf"; - } - $request .= $crlf; - fwrite($socket, $request); - - list($httpver, $code, $status) = split(' +', rtrim(fgets($socket))); - // read response header - while(strlen(trim($line = fgets($socket)))) - { - list($equiv, $content) = split(' *: *', rtrim($line)); - } - $body = ''; - while(!feof($socket)) - { - $body .= fgets($socket, 128); - } - fclose($socket); - - $ret->result_code = $code; - $ret->body = $body; - - return $ret; - } - } -?> +m_host = $host; + $this->m_port = $port; + $this->m_headers = array(); + } + + /** + * @brief mether to add key/value pair to the HTTP request header + * @param[in] key HTTP header element + * @param[in] value value string for HTTP header element + */ + function addToHeader($key, $value) + { + $this->m_headers[$key] = $value; + } + + /** + * @brief send HTTP message to the host + * @param[in] target ip or url of the external server + * @param[in] method HTTP method such as GET and POST + * @param[in] timeout time out value for HTTP request expiration + * @param[in] post variables to send + * @return Returns an object containing HTTP Response body and HTTP response code + */ + function send($target='/', $method='GET', $timeout=3, $post_vars=null) + { + static $allow_methods=null; + + $this->addToHeader('Host', $this->m_host); + $this->addToHeader('Connection', 'close'); + + $method = strtoupper($method); + if(!$allow_methods) $allow_methods = explode(' ', 'GET POST PUT'); + if(!in_array($method, $allow_methods)) $method = $allow_methods[0]; + + // $timeout should be an integer that is bigger than zero + $timout = max((int)$timeout, 0); + + // list of post variables + if(!is_array($post_vars)) $post_vars = array(); + + if(false && is_callable('curl_init')) { + return $this->sendWithCurl($target, $method, $timeout, $post_vars); + } else { + return $this->sendWithSock($target, $method, $timeout, $post_vars); + } + } + + /** + * @brief Send a request with the file socket + * @private + */ + function sendWithSock($target, $method, $timeout, $post_vars) + { + static $crlf = "\r\n"; + + $sock = @fsockopen($this->m_host, $this->m_port, $errno, $errstr, $timeout); + if(!$sock) { + return new Object(-1, 'socket_connect_failed'); + } + + $headers = $this->m_headers + array(); + if(!isset($headers['Accept-Encoding'])) $headers['Accept-Encoding'] = 'identity'; + + // post body + $post_body = ''; + if($method == 'POST' && count($post_vars)) { + foreach($post_vars as $key=>$value) { + $post_body .= urlencode($key).'='.urlencode($value).'&'; + } + $post_body = substr($post_body, 0, -1); + + $headers['Content-Length'] = strlen($post_body); + $headers['Content-Type'] = 'application/x-www-form-urlencoded'; + } + + $request = "$method $target HTTP/1.1$crlf"; + foreach($headers as $equiv=>$content) { + $request .= "$equiv: $content$crlf"; + } + $request .= $crlf.$post_body; + fwrite($sock, $request); + + list($httpver, $code, $status) = preg_split('/ +/', rtrim(fgets($sock)), 3); + + // read response headers + $is_chunked = false; + while(strlen(trim($line = fgets($sock)))) { + list($equiv, $content) = preg_split('/ *: */', rtrim($line), 1); + if(!strcasecmp($equiv, 'Transfer-Encoding') && $content == 'chunked') { + $is_chunked = true; + } + } + + $body = ''; + while(!feof($sock)) { + if ($is_chunked) { + $chunk_size = hexdec(fgets($sock)); + if($chunk_size) $body .= fread($sock, $chunk_size); + } else { + $body .= fgets($sock, 512); + } + } + fclose($sock); + + $ret = new stdClass; + $ret->result_code = $code; + $ret->body = $body; + + return $ret; + } + + /** + * @brief Send a request with the curl library + * @private + */ + function sendWithCurl($target, $method, $timeout, $post_vars) + { + $headers = $this->m_headers + array(); + + // creat a new cURL resource + $ch = curl_init(); + + $headers['Expect'] = ''; + + // set URL and other appropriate options + curl_setopt($ch, CURLOPT_URL, "http://{$this->m_host}{$target}"); + curl_setopt($ch, CURLOPT_HEADER, false); + curl_setopt($ch, CURLOPT_PORT, $this->m_port); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout); + curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + + switch($method) { + case 'GET': curl_setopt($ch, CURLOPT_HTTPGET, true); break; + case 'PUT': curl_setopt($ch, CURLOPT_PUT, true); break; + case 'POST': + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $post_vars); + break; + } + + $arr_headers = array(); + foreach($headers as $key=>$value){ + $arr_headers[] = "$key: $value"; + } + + curl_setopt($ch, CURLOPT_HTTPHEADER, $arr_headers); + + $body = curl_exec($ch); + if(curl_errno($ch)) { + return new Object(-1, 'socket_connect_failed'); + } + + $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); + + $ret = new stdClass; + $ret->result_code = $code; + $ret->body = $body; + + return $ret; + } +} +?> diff --git a/classes/mail/Mail.class.php b/classes/mail/Mail.class.php index ad5b3d19a..9605670ba 100644 --- a/classes/mail/Mail.class.php +++ b/classes/mail/Mail.class.php @@ -1,353 +1,353 @@ -additional_params = $additional_params; - } - - function addAttachment($filename, $orgfilename) - { - $this->attachments[$orgfilename] = $filename; - } - - function addCidAttachment($filename, $cid) - { - $this->cidAttachments[$cid] = $filename; - } - - function setSender($name, $email) { - $this->sender_name = $name; - $this->sender_email = $email; - } - - function getSender() { - if(!stristr(PHP_OS, 'win') && $this->sender_name) return sprintf("%s <%s>", '=?utf-8?b?'.base64_encode($this->sender_name).'?=', $this->sender_email); - return $this->sender_email; - } - - function setReceiptor($name, $email) { - $this->receiptor_name = $name; - $this->receiptor_email = $email; - } - - function getReceiptor() { - if(!stristr(PHP_OS, 'win') && $this->receiptor_name && $this->receiptor_name != $this->receiptor_email) return sprintf("%s <%s>", '=?utf-8?b?'.base64_encode($this->receiptor_name).'?=', $this->receiptor_email); - return $this->receiptor_email; - } - - function setTitle($title) { - $this->title = $title; - } - - function getTitle() { - return '=?utf-8?b?'.base64_encode($this->title).'?='; - } - - function setBCC($bcc) - { - $this->bcc = $bcc; - } - - function setMessageID($messageId) { - $this->messageId = $messageId; - } - - function setReferences($references) { - $this->references = $references; - } - - function setReplyTo($replyTo) - { - $this->replyTo = $replyTo; - } - - function setContent($content) { - $content = preg_replace_callback('/]+)>/i',array($this,'replaceResourceRealPath'), $content); - $this->content = $content; - } - - function replaceResourceRealPath($matches) { - return preg_replace('/src=(["\']?)files/i','src=$1'.Context::getRequestUri().'files', $matches[0]); - } - - function getPlainContent() { - return chunk_split(base64_encode(str_replace(array("<",">","&"), array("<",">","&"), $this->content))); - } - - function getHTMLContent() { - return chunk_split(base64_encode($this->content_type!='html'?nl2br($this->content):$this->content)); - } - - function setContentType($mode = 'html') { - $this->content_type = $mode=='html'?'html':''; - } - - function procAttachments() - { - if(count($this->attachments) > 0) - { - $this->body = $this->header.$this->body; - $boundary = '----=='.uniqid(rand(),true); - $this->header = "Content-Type: multipart/mixed;".$this->eol."\tboundary=\"".$boundary."\"".$this->eol.$this->eol; - $this->body = "--".$boundary.$this->eol.$this->body.$this->eol.$this->eol; - $res = array(); - $res[] = $this->body; - foreach($this->attachments as $filename => $attachment) - { - $type = $this->returnMIMEType($filename); - $file_str = FileHandler::readFile($attachment); - $chunks = chunk_split(base64_encode($file_str)); - $tempBody = sprintf( - "--".$boundary.$this->eol. - "Content-Type: %s;".$this->eol. - "\tname=\"%s\"".$this->eol. - "Content-Transfer-Encoding: base64".$this->eol. - "Content-Description: %s".$this->eol. - "Content-Disposition: attachment;".$this->eol. - "\tfilename=\"%s\"".$this->eol.$this->eol. - "%s".$this->eol.$this->eol, - $type, - $filename, - $filename, - $filename, - $chunks); - $res[] = $tempBody; - } - $this->body = implode("", $res); - $this->body .= "--".$boundary."--"; - } - } - - function procCidAttachments() - { - if(count($this->cidAttachments) > 0) - { - $this->body = $this->header.$this->body; - $boundary = '----=='.uniqid(rand(),true); - $this->header = "Content-Type: multipart/relative;".$this->eol."\ttype=\"multipart/alternative\";".$this->eol."\tboundary=\"".$boundary."\"".$this->eol.$this->eol; - $this->body = "--".$boundary.$this->eol.$this->body.$this->eol.$this->eol; - $res = array(); - $res[] = $this->body; - foreach($this->cidAttachments as $cid => $attachment) - { - $filename = basename($attachment); - $type = $this->returnMIMEType(FileHandler::getRealPath($attachment)); - $file_str = FileHandler::readFile($attachment); - $chunks = chunk_split(base64_encode($file_str)); - $tempBody = sprintf( - "--".$boundary.$this->eol. - "Content-Type: %s;".$this->eol. - "\tname=\"%s\"".$this->eol. - "Content-Transfer-Encoding: base64".$this->eol. - "Content-ID: <%s>".$this->eol. - "Content-Description: %s".$this->eol. - "Content-Location: %s".$this->eol.$this->eol. - "%s".$this->eol.$this->eol, - $type, - $filename, - $cid, - $filename, - $filename, - $chunks); - $res[] = $tempBody; - } - $this->body = implode("", $res); - $this->body .= "--".$boundary."--"; - } - } - - - function send() { - $boundary = '----=='.uniqid(rand(),true); - $this->eol = $GLOBALS['_qmail_compatibility'] == "Y" ? "\n" : "\r\n"; - - $this->header = "Content-Type: multipart/alternative;".$this->eol."\tboundary=\"".$boundary."\"".$this->eol.$this->eol; - $this->body = sprintf( - "--%s".$this->eol. - "Content-Type: text/plain; charset=utf-8; format=flowed".$this->eol. - "Content-Transfer-Encoding: base64".$this->eol. - "Content-Disposition: inline".$this->eol.$this->eol. - "%s". - "--%s".$this->eol. - "Content-Type: text/html; charset=utf-8".$this->eol. - "Content-Transfer-Encoding: base64".$this->eol. - "Content-Disposition: inline".$this->eol.$this->eol. - "%s". - "--%s--". - "", - $boundary, - $this->getPlainContent(), - $boundary, - $this->getHTMLContent(), - $boundary - ); - - $this->procCidAttachments(); - $this->procAttachments(); - - $headers = sprintf( - "From: %s".$this->eol. - "%s". - "%s". - "%s". - "%s". - "MIME-Version: 1.0".$this->eol."", - $this->getSender(), - $this->messageId?("Message-ID: <".$this->messageId.">".$this->eol):"", - $this->replyTo?("Reply-To: <".$this->replyTo.">".$this->eol):"", - $this->bcc?("Bcc: ".$this->bcc.$this->eol):"", - $this->references?("References: <".$this->references.">".$this->eol."In-Reply-To: <".$this->references.">".$this->eol):"" - ); - $headers .= $this->header; - if($this->additional_params) return mail($this->getReceiptor(), $this->getTitle(), $this->body, $headers, $this->additional_params); - return mail($this->getReceiptor(), $this->getTitle(), $this->body, $headers); - } - - function checkMailMX($email_address) { - if(!Mail::isVaildMailAddress($email_address)) return false; - list($user, $host) = explode("@", $email_address); - if(function_exists('checkdnsrr')) { - if (checkdnsrr($host, "MX") or checkdnsrr($host, "A")) return true; - else return false; - } - return true; - } - - function isVaildMailAddress($email_address) { - if( preg_match("/([a-z0-9\_\-\.]+)@([a-z0-9\_\-\.]+)/i", $email_address) ) return $email_address; - else return ''; - } - - function returnMIMEType($filename) - { - preg_match("|\.([a-z0-9]{2,4})$|i", $filename, $fileSuffix); - - switch(strtolower($fileSuffix[1])) - { - case "js" : - return "application/x-javascript"; - - case "json" : - return "application/json"; - - case "jpg" : - case "jpeg" : - case "jpe" : - return "image/jpg"; - - case "png" : - case "gif" : - case "bmp" : - case "tiff" : - return "image/".strtolower($fileSuffix[1]); - - case "css" : - return "text/css"; - - case "xml" : - return "application/xml"; - - case "doc" : - case "docx" : - return "application/msword"; - - case "xls" : - case "xlt" : - case "xlm" : - case "xld" : - case "xla" : - case "xlc" : - case "xlw" : - case "xll" : - return "application/vnd.ms-excel"; - - case "ppt" : - case "pps" : - return "application/vnd.ms-powerpoint"; - - case "rtf" : - return "application/rtf"; - - case "pdf" : - return "application/pdf"; - - case "html" : - case "htm" : - case "php" : - return "text/html"; - - case "txt" : - return "text/plain"; - - case "mpeg" : - case "mpg" : - case "mpe" : - return "video/mpeg"; - - case "mp3" : - return "audio/mpeg3"; - - case "wav" : - return "audio/wav"; - - case "aiff" : - case "aif" : - return "audio/aiff"; - - case "avi" : - return "video/msvideo"; - - case "wmv" : - return "video/x-ms-wmv"; - - case "mov" : - return "video/quicktime"; - - case "zip" : - return "application/zip"; - - case "tar" : - return "application/x-tar"; - - case "swf" : - return "application/x-shockwave-flash"; - - default : - if(function_exists("mime_content_type")) - { - $fileSuffix = mime_content_type($filename); - } - - return "unknown/" . trim($fileSuffix[0], "."); - } - } - } -?> +additional_params = $additional_params; + } + + function addAttachment($filename, $orgfilename) + { + $this->attachments[$orgfilename] = $filename; + } + + function addCidAttachment($filename, $cid) + { + $this->cidAttachments[$cid] = $filename; + } + + function setSender($name, $email) { + $this->sender_name = $name; + $this->sender_email = $email; + } + + function getSender() { + if(!stristr(PHP_OS, 'win') && $this->sender_name) return sprintf("%s <%s>", '=?utf-8?b?'.base64_encode($this->sender_name).'?=', $this->sender_email); + return $this->sender_email; + } + + function setReceiptor($name, $email) { + $this->receiptor_name = $name; + $this->receiptor_email = $email; + } + + function getReceiptor() { + if(!stristr(PHP_OS, 'win') && $this->receiptor_name && $this->receiptor_name != $this->receiptor_email) return sprintf("%s <%s>", '=?utf-8?b?'.base64_encode($this->receiptor_name).'?=', $this->receiptor_email); + return $this->receiptor_email; + } + + function setTitle($title) { + $this->title = $title; + } + + function getTitle() { + return '=?utf-8?b?'.base64_encode($this->title).'?='; + } + + function setBCC($bcc) + { + $this->bcc = $bcc; + } + + function setMessageID($messageId) { + $this->messageId = $messageId; + } + + function setReferences($references) { + $this->references = $references; + } + + function setReplyTo($replyTo) + { + $this->replyTo = $replyTo; + } + + function setContent($content) { + $content = preg_replace_callback('/]+)>/i',array($this,'replaceResourceRealPath'), $content); + $this->content = $content; + } + + function replaceResourceRealPath($matches) { + return preg_replace('/src=(["\']?)files/i','src=$1'.Context::getRequestUri().'files', $matches[0]); + } + + function getPlainContent() { + return chunk_split(base64_encode(str_replace(array("<",">","&"), array("<",">","&"), $this->content))); + } + + function getHTMLContent() { + return chunk_split(base64_encode($this->content_type!='html'?nl2br($this->content):$this->content)); + } + + function setContentType($mode = 'html') { + $this->content_type = $mode=='html'?'html':''; + } + + function procAttachments() + { + if(count($this->attachments) > 0) + { + $this->body = $this->header.$this->body; + $boundary = '----=='.uniqid(rand(),true); + $this->header = "Content-Type: multipart/mixed;".$this->eol."\tboundary=\"".$boundary."\"".$this->eol.$this->eol; + $this->body = "--".$boundary.$this->eol.$this->body.$this->eol.$this->eol; + $res = array(); + $res[] = $this->body; + foreach($this->attachments as $filename => $attachment) + { + $type = $this->returnMIMEType($filename); + $file_str = FileHandler::readFile($attachment); + $chunks = chunk_split(base64_encode($file_str)); + $tempBody = sprintf( + "--".$boundary.$this->eol. + "Content-Type: %s;".$this->eol. + "\tname=\"%s\"".$this->eol. + "Content-Transfer-Encoding: base64".$this->eol. + "Content-Description: %s".$this->eol. + "Content-Disposition: attachment;".$this->eol. + "\tfilename=\"%s\"".$this->eol.$this->eol. + "%s".$this->eol.$this->eol, + $type, + $filename, + $filename, + $filename, + $chunks); + $res[] = $tempBody; + } + $this->body = implode("", $res); + $this->body .= "--".$boundary."--"; + } + } + + function procCidAttachments() + { + if(count($this->cidAttachments) > 0) + { + $this->body = $this->header.$this->body; + $boundary = '----=='.uniqid(rand(),true); + $this->header = "Content-Type: multipart/relative;".$this->eol."\ttype=\"multipart/alternative\";".$this->eol."\tboundary=\"".$boundary."\"".$this->eol.$this->eol; + $this->body = "--".$boundary.$this->eol.$this->body.$this->eol.$this->eol; + $res = array(); + $res[] = $this->body; + foreach($this->cidAttachments as $cid => $attachment) + { + $filename = basename($attachment); + $type = $this->returnMIMEType(FileHandler::getRealPath($attachment)); + $file_str = FileHandler::readFile($attachment); + $chunks = chunk_split(base64_encode($file_str)); + $tempBody = sprintf( + "--".$boundary.$this->eol. + "Content-Type: %s;".$this->eol. + "\tname=\"%s\"".$this->eol. + "Content-Transfer-Encoding: base64".$this->eol. + "Content-ID: <%s>".$this->eol. + "Content-Description: %s".$this->eol. + "Content-Location: %s".$this->eol.$this->eol. + "%s".$this->eol.$this->eol, + $type, + $filename, + $cid, + $filename, + $filename, + $chunks); + $res[] = $tempBody; + } + $this->body = implode("", $res); + $this->body .= "--".$boundary."--"; + } + } + + + function send() { + $boundary = '----=='.uniqid(rand(),true); + $this->eol = $GLOBALS['_qmail_compatibility'] == "Y" ? "\n" : "\r\n"; + + $this->header = "Content-Type: multipart/alternative;".$this->eol."\tboundary=\"".$boundary."\"".$this->eol.$this->eol; + $this->body = sprintf( + "--%s".$this->eol. + "Content-Type: text/plain; charset=utf-8; format=flowed".$this->eol. + "Content-Transfer-Encoding: base64".$this->eol. + "Content-Disposition: inline".$this->eol.$this->eol. + "%s". + "--%s".$this->eol. + "Content-Type: text/html; charset=utf-8".$this->eol. + "Content-Transfer-Encoding: base64".$this->eol. + "Content-Disposition: inline".$this->eol.$this->eol. + "%s". + "--%s--". + "", + $boundary, + $this->getPlainContent(), + $boundary, + $this->getHTMLContent(), + $boundary + ); + + $this->procCidAttachments(); + $this->procAttachments(); + + $headers = sprintf( + "From: %s".$this->eol. + "%s". + "%s". + "%s". + "%s". + "MIME-Version: 1.0".$this->eol."", + $this->getSender(), + $this->messageId?("Message-ID: <".$this->messageId.">".$this->eol):"", + $this->replyTo?("Reply-To: <".$this->replyTo.">".$this->eol):"", + $this->bcc?("Bcc: ".$this->bcc.$this->eol):"", + $this->references?("References: <".$this->references.">".$this->eol."In-Reply-To: <".$this->references.">".$this->eol):"" + ); + $headers .= $this->header; + if($this->additional_params) return mail($this->getReceiptor(), $this->getTitle(), $this->body, $headers, $this->additional_params); + return mail($this->getReceiptor(), $this->getTitle(), $this->body, $headers); + } + + function checkMailMX($email_address) { + if(!Mail::isVaildMailAddress($email_address)) return false; + list($user, $host) = explode("@", $email_address); + if(function_exists('checkdnsrr')) { + if (checkdnsrr($host, "MX") or checkdnsrr($host, "A")) return true; + else return false; + } + return true; + } + + function isVaildMailAddress($email_address) { + if( preg_match("/([a-z0-9\_\-\.]+)@([a-z0-9\_\-\.]+)/i", $email_address) ) return $email_address; + else return ''; + } + + function returnMIMEType($filename) + { + preg_match("|\.([a-z0-9]{2,4})$|i", $filename, $fileSuffix); + + switch(strtolower($fileSuffix[1])) + { + case "js" : + return "application/x-javascript"; + + case "json" : + return "application/json"; + + case "jpg" : + case "jpeg" : + case "jpe" : + return "image/jpg"; + + case "png" : + case "gif" : + case "bmp" : + case "tiff" : + return "image/".strtolower($fileSuffix[1]); + + case "css" : + return "text/css"; + + case "xml" : + return "application/xml"; + + case "doc" : + case "docx" : + return "application/msword"; + + case "xls" : + case "xlt" : + case "xlm" : + case "xld" : + case "xla" : + case "xlc" : + case "xlw" : + case "xll" : + return "application/vnd.ms-excel"; + + case "ppt" : + case "pps" : + return "application/vnd.ms-powerpoint"; + + case "rtf" : + return "application/rtf"; + + case "pdf" : + return "application/pdf"; + + case "html" : + case "htm" : + case "php" : + return "text/html"; + + case "txt" : + return "text/plain"; + + case "mpeg" : + case "mpg" : + case "mpe" : + return "video/mpeg"; + + case "mp3" : + return "audio/mpeg3"; + + case "wav" : + return "audio/wav"; + + case "aiff" : + case "aif" : + return "audio/aiff"; + + case "avi" : + return "video/msvideo"; + + case "wmv" : + return "video/x-ms-wmv"; + + case "mov" : + return "video/quicktime"; + + case "zip" : + return "application/zip"; + + case "tar" : + return "application/x-tar"; + + case "swf" : + return "application/x-shockwave-flash"; + + default : + if(function_exists("mime_content_type")) + { + $fileSuffix = mime_content_type($filename); + } + + return "unknown/" . trim($fileSuffix[0], "."); + } + } + } +?> diff --git a/classes/mobile/Mobile.class.php b/classes/mobile/Mobile.class.php index 574fa9c10..694038406 100644 --- a/classes/mobile/Mobile.class.php +++ b/classes/mobile/Mobile.class.php @@ -16,44 +16,37 @@ class Mobile { function _isFromMobilePhone() { if($this->ismobile !== null) return $this->ismobile; - $db_info = Context::getDBInfo(); - if($db_info->use_mobile_view != "Y" || Context::get('full_browse') || $_COOKIE["FullBrowse"]) - { - $this->ismobile = false; - } - else - { - $xe_web_path = Context::pathToUrl(_XE_PATH_); - $m = Context::get('m'); - if(strlen($m)==1) { - if($m == "1") { - $_COOKIE["mobile"] = 'true'; - setcookie("mobile", 'true', 0, $xe_web_path); - $this->ismobile = true; - } - else if($m == "0") { - $_COOKIE["mobile"] = 'false'; - setcookie("mobile", 'false', 0, $xe_web_path); - $this->ismobile = false; - } + $db_info = Context::getDBInfo(); + if($db_info->use_mobile_view != "Y" || Context::get('full_browse') || $_COOKIE["FullBrowse"]) { + return ($this->ismobile = false); + } + + $xe_web_path = Context::pathToUrl(_XE_PATH_); + + $m = Context::get('m'); + if(strlen($m)==1) { + if($m == "1") { + $_COOKIE['mobile'] = 'true'; + setcookie('mobile', 'true', 0, $xe_web_path); + $this->ismobile = true; + } elseif($m == "0") { + $_COOKIE['mobile'] = 'false'; + setcookie('mobile', 'false', 0, $xe_web_path); + $this->ismobile = false; } - else if(isset($_COOKIE["mobile"])) { - if($_COOKIE['mobile'] == 'true') { - $this->ismobile = true; - } - else { - $_COOKIE["mobile"] = 'false'; - setcookie("mobile", 'false', 0, $xe_web_path); - $this->ismobile = false; - } - } - else { - if(preg_match('/(iPod|iPhone|Android|BlackBerry|SymbianOS|SCH\-M[0-9]+)/',$_SERVER['HTTP_USER_AGENT'])) - { - setcookie("mobile", 'true', 0, $xe_web_path); - $this->ismobile = true; - } + } elseif(isset($_COOKIE['mobile'])) { + if($_COOKIE['mobile'] == 'true') { + $this->ismobile = true; + } else { + $_COOKIE['mobile'] = 'false'; + setcookie('mobile', 'false', 0, $xe_web_path); + $this->ismobile = false; + } + } else { + if($this->isMobileCheckByAgent()) { + setcookie("mobile", 'true', 0, $xe_web_path); + $this->ismobile = true; } } @@ -62,10 +55,7 @@ class Mobile { function isMobileCheckByAgent() { - if(preg_match('/(iPod|iPhone|Android|BlackBerry|SymbianOS|SCH\-M[0-9]+)/',$_SERVER['HTTP_USER_AGENT'])) - return true; - else - return false; + return !!preg_match('/(iPod|iPhone|Android|BlackBerry|SymbianOS|SCH-M\d+|SPH-M\d+|Windows Phone|Dorothy Browser|Googlebot-Mobile)/',$_SERVER['HTTP_USER_AGENT']); } function setMobile($ismobile) diff --git a/classes/module/ModuleHandler.class.php b/classes/module/ModuleHandler.class.php index 7d70d6b9f..788a3ba30 100644 --- a/classes/module/ModuleHandler.class.php +++ b/classes/module/ModuleHandler.class.php @@ -1,559 +1,779 @@ -module = 'install'; - $this->act = Context::get('act'); - return; - } - - // Set variables from request arguments - $this->module = $module?$module:Context::get('module'); - $this->act = $act?$act:Context::get('act'); - $this->mid = $mid?$mid:Context::get('mid'); - $this->document_srl = $document_srl?(int)$document_srl:(int)Context::get('document_srl'); - $this->module_srl = $module_srl?(int)$module_srl:(int)Context::get('module_srl'); - $this->entry = Context::convertEncodingStr(Context::get('entry')); - - // Validate variables to prevent XSS - if($this->module && !preg_match("/^([a-z0-9\_\-]+)$/i",$this->module)) die(Context::getLang("msg_invalid_request")); - if($this->mid && !preg_match("/^([a-z0-9\_\-]+)$/i",$this->mid)) die(Context::getLang("msg_invalid_request")); - if($this->act && !preg_match("/^([a-z0-9\_\-]+)$/i",$this->act)) die(Context::getLang("msg_invalid_request")); - - // execute addon (before module initialization) - $called_position = 'before_module_init'; - $oAddonController = &getController('addon'); - $addon_file = $oAddonController->getCacheFilePath(Mobile::isFromMobilePhone()?'mobile':'pc'); - @include($addon_file); - } - - /** - * @brief Initialization. It finds the target module based on module, mid, document_srl, and prepares to execute an action - * @return true: OK, false: redirected - **/ - function init() { - $oModuleModel = &getModel('module'); - $site_module_info = Context::get('site_module_info'); - - if(!$this->document_srl && $this->mid && $this->entry) { - $oDocumentModel = &getModel('document'); - $this->document_srl = $oDocumentModel->getDocumentSrlByAlias($this->mid, $this->entry); - if($this->document_srl) Context::set('document_srl', $this->document_srl); - } - - // Get module's information based on document_srl, if it's specified - if($this->document_srl && !$this->module) { - $module_info = $oModuleModel->getModuleInfoByDocumentSrl($this->document_srl); - - // If the document does not exist, remove document_srl - if(!$module_info) { - unset($this->document_srl); - } else { - // If it exists, compare mid based on the module information - // if mids are not matching, set it as the document's mid - if($this->mid != $module_info->mid) { - $this->mid = $module_info->mid; - Context::set('mid', $module_info->mid, true); - } - } - // if requested module is different from one of the document, remove the module information retrieved based on the document number - if($this->module && $module_info->module != $this->module) unset($module_info); - } - - // If module_info is not set yet, and there exists mid information, get module information based on the mid - if(!$module_info && $this->mid) { - $module_info = $oModuleModel->getModuleInfoByMid($this->mid, $site_module_info->site_srl); - //if($this->module && $module_info->module != $this->module) unset($module_info); - } - - // redirect, if module_site_srl and site_srl are different - if(!$this->module && !$module_info && $site_module_info->site_srl == 0 && $site_module_info->module_site_srl > 0) { - $site_info = $oModuleModel->getSiteInfo($site_module_info->module_site_srl); - header("location:".getNotEncodedSiteUrl($site_info->domain,'mid',$site_module_info->mid)); - return false; - } - - // If module_info is not set still, and $module does not exist, find the default module - if(!$module_info && !$this->module) $module_info = $site_module_info; - - if(!$module_info && !$this->module && $site_module_info->module_site_srl) $module_info = $site_module_info; - - // redirect, if site_srl of module_info is different from one of site's module_info - if($module_info && $module_info->site_srl != $site_module_info->site_srl && !isCrawler()) { - // If the module is of virtual site - if($module_info->site_srl) { - $site_info = $oModuleModel->getSiteInfo($module_info->site_srl); - $redirect_url = getNotEncodedSiteUrl($site_info->domain, 'mid',Context::get('mid'),'document_srl',Context::get('document_srl'),'module_srl',Context::get('module_srl'),'entry',Context::get('entry')); - // If it's called from a virtual site, though it's not a module of the virtual site - } else { - $db_info = Context::getDBInfo(); - if(!$db_info->default_url) return Context::getLang('msg_default_url_is_not_defined'); - else $redirect_url = getNotEncodedSiteUrl($db_info->default_url, 'mid',Context::get('mid'),'document_srl',Context::get('document_srl'),'module_srl',Context::get('module_srl'),'entry',Context::get('entry')); - } - header("location:".$redirect_url); - return false; - } - - // If module info was set, retrieve variables from the module information - if($module_info) { - $this->module = $module_info->module; - $this->mid = $module_info->mid; - $this->module_info = $module_info; - Context::setBrowserTitle($module_info->browser_title); - $part_config= $oModuleModel->getModulePartConfig('layout',$module_info->layout_srl); - Context::addHtmlHeader($part_config->header_script); - } - - // Set module and mid into module_info - $this->module_info->module = $this->module; - $this->module_info->mid = $this->mid; - - // Still no module? it's an error - if(!$this->module) $this->error = 'msg_module_does_not_exist'; - - // If mid exists, set mid into context - if($this->mid) Context::set('mid', $this->mid, true); - - // Call a trigger after moduleHandler init - $output = ModuleHandler::triggerCall('moduleHandler.init', 'after', $this->module_info); - if(!$output->toBool()) { - $this->error = $output->getMessage(); - return false; - } - - // Set current module info into context - Context::set('current_module_info', $this->module_info); - - return true; - } - - /** - * @brief get a module instance and execute an action - * @return executed module instance - **/ - function procModule() { - // If error occurred while preparation, return a message instance - if($this->error) { - $type = Mobile::isFromMobilePhone() ? 'mobile' : 'view'; - $oMessageObject = &ModuleHandler::getModuleInstance('message',$type); - $oMessageObject->setError(-1); - $oMessageObject->setMessage($this->error); - $oMessageObject->dispMessage(); - return $oMessageObject; - } - - $oModuleModel = &getModel('module'); - - // Get action information with conf/action.xml - $xml_info = $oModuleModel->getModuleActionXml($this->module); - - // If not installed yet, modify act - if($this->module=="install") { - if(!$this->act || !$xml_info->action->{$this->act}) $this->act = $xml_info->default_index_act; - } - - // if act exists, find type of the action, if not use default index act - if(!$this->act) $this->act = $xml_info->default_index_act; - - // still no act means error - if(!$this->act) { - $this->error = 'msg_module_does_not_exist'; - return; - } - - // get type, kind - $type = $xml_info->action->{$this->act}->type; - $kind = strpos(strtolower($this->act),'admin')!==false?'admin':''; - if(!$kind && $this->module == 'admin') $kind = 'admin'; - if($this->module_info->use_mobile != "Y") Mobile::setMobile(false); - - // if(type == view, and case for using mobilephone) - if($type == "view" && Mobile::isFromMobilePhone() && Context::isInstalled()) - { - $orig_type = "view"; - $type = "mobile"; - // create a module instance - $oModule = &$this->getModuleInstance($this->module, $type, $kind); - if(!is_object($oModule) || !method_exists($oModule, $this->act)) { - $type = $orig_type; - Mobile::setMobile(false); - $oModule = &$this->getModuleInstance($this->module, $type, $kind); - } - } - else - { - // create a module instance - $oModule = &$this->getModuleInstance($this->module, $type, $kind); - } - - if(!is_object($oModule)) { - $this->error = 'msg_module_does_not_exist'; - return; - } - - // If there is no such action in the module object - if(!isset($xml_info->action->{$this->act}) || !method_exists($oModule, $this->act)) - { - if(!Context::isInstalled()) - { - $this->error = 'msg_invalid_request'; - return; - } - - $forward = null; - // 1. Look for the module with action name - if(preg_match('/^([a-z]+)([A-Z])([a-z0-9\_]+)(.*)$/', $this->act, $matches)) { - $module = strtolower($matches[2].$matches[3]); - $xml_info = $oModuleModel->getModuleActionXml($module); - if($xml_info->action->{$this->act}) { - $forward->module = $module; - $forward->type = $xml_info->action->{$this->act}->type; - $forward->act = $this->act; - } - } - - if(!$forward) - { - $forward = $oModuleModel->getActionForward($this->act); - } - - if($forward->module && $forward->type && $forward->act && $forward->act == $this->act) { - $kind = strpos(strtolower($forward->act),'admin')!==false?'admin':''; - $type = $forward->type; - $tpl_path = $oModule->getTemplatePath(); - $orig_module = $oModule; - - if($type == "view" && Mobile::isFromMobilePhone()) - { - $orig_type = "view"; - $type = "mobile"; - // create a module instance - $oModule = &$this->getModuleInstance($forward->module, $type, $kind); - if(!is_object($oModule) || !method_exists($oModule, $this->act)) { - $type = $orig_type; - Mobile::setMobile(false); - $oModule = &$this->getModuleInstance($forward->module, $type, $kind); - } - } - else - { - $oModule = &$this->getModuleInstance($forward->module, $type, $kind); - } - $xml_info = $oModuleModel->getModuleActionXml($forward->module); - $oMemberModel = &getModel('member'); - $logged_info = $oMemberModel->getLoggedInfo(); - - if($this->module == "admin" && $type == "view") - { - if($logged_info->is_admin=='Y') { - $orig_module->loadSideBar(); - $oModule->setLayoutPath("./modules/admin/tpl"); - $oModule->setLayoutFile("layout.html"); - } - else{ - $this->error = 'msg_is_not_administrator'; - $oMessageObject = &ModuleHandler::getModuleInstance('message',$type); - $oMessageObject->setError(-1); - $oMessageObject->setMessage($this->error); - $oMessageObject->dispMessage(); - return $oMessageObject; - } - } - if ($kind == 'admin'){ - $grant = $oModuleModel->getGrant($this->module_info, $logged_info); - if(!$grant->is_admin && !$grant->manager) { - $this->error = 'msg_is_not_manager'; - $oMessageObject = &ModuleHandler::getModuleInstance('message',$type); - $oMessageObject->setError(-1); - $oMessageObject->setMessage($this->error); - $oMessageObject->dispMessage(); - return $oMessageObject; - } - - } - } - else if($xml_info->default_index_act && method_exists($oModule, $xml_info->default_index_act)) - { - $this->act = $xml_info->default_index_act; - } - else - { - $this->error = 'msg_invalid_request'; - return; - } - } - - $oModule->setAct($this->act); - - $this->module_info->module_type = $type; - $oModule->setModuleInfo($this->module_info, $xml_info); - - if($type == "view" && $this->module_info->use_mobile == "Y" && Mobile::isMobileCheckByAgent()) - { - global $lang; - $footer = '

'.$lang->msg_pc_to_mobile.' '.$lang->cmd_move.'

'; - Context::addHtmlFooter($footer); - } - - // execute the action, and if failed, set error - if(!$oModule->proc()) $this->error = $oModule->getMessage(); - - return $oModule; - } - - /** - * @brief display contents from executed module - * @param[in] $oModule module instance - * @return none - **/ - function displayContent($oModule = NULL) { - // If the module is not set or not an object, set error - if(!$oModule || !is_object($oModule)) { - $this->error = 'msg_module_does_not_exists'; - } - - // If connection to DB has a problem even though it's not install module, set error - if($this->module != 'install' && $GLOBALS['__DB__'][Context::getDBType()]->is_connected == false) { - $this->error = 'msg_dbconnect_failed'; - } - - // Call trigger after moduleHandler proc - $output = ModuleHandler::triggerCall('moduleHandler.proc', 'after', $oModule); - if(!$output->toBool()) $this->error = $output->getMessage(); - - // Use message view object, if HTML call - if(!in_array(Context::getRequestMethod(),array('XMLRPC','JSON'))) { - // If error occurred, handle it - if($this->error) { - // display content with message module instance - $type = Mobile::isFromMobilePhone() ? 'mobile' : 'view'; - $oMessageObject = &ModuleHandler::getModuleInstance('message',$type); - $oMessageObject->setError(-1); - $oMessageObject->setMessage($this->error); - $oMessageObject->dispMessage(); - - // If module was called normally, change the templates of the module into ones of the message view module - if($oModule) { - $oModule->setTemplatePath($oMessageObject->getTemplatePath()); - $oModule->setTemplateFile($oMessageObject->getTemplateFile()); - - // Otherwise, set message instance as the target module - } else { - $oModule = $oMessageObject; - } - } - - // Check if layout_srl exists for the module - if(Mobile::isFromMobilePhone()) - { - $layout_srl = $oModule->module_info->mlayout_srl; - } - else - { - $layout_srl = $oModule->module_info->layout_srl; - } - - if($layout_srl && !$oModule->getLayoutFile()) { - - // If layout_srl exists, get information of the layout, and set the location of layout_path/ layout_file - $oLayoutModel = &getModel('layout'); - $layout_info = $oLayoutModel->getLayout($layout_srl); - if($layout_info) { - - // Input extra_vars into $layout_info - if($layout_info->extra_var_count) { - - foreach($layout_info->extra_var as $var_id => $val) { - if($val->type == 'image') { - if(preg_match('/^\.\/files\/attach\/images\/(.+)/i',$val->value)) $val->value = Context::getRequestUri().substr($val->value,2); - } - $layout_info->{$var_id} = $val->value; - } - } - // Set menus into context - 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); - } - } - - // Set layout information into context - Context::set('layout_info', $layout_info); - - $oModule->setLayoutPath($layout_info->path); - $oModule->setLayoutFile('layout'); - - // If layout was modified, use the modified version - $edited_layout = $oLayoutModel->getUserLayoutHtml($layout_info->layout_srl); - if(file_exists($edited_layout)) $oModule->setEditedLayoutFile($edited_layout); - } - } - } - - // Display contents - $oDisplayHandler = new DisplayHandler(); - $oDisplayHandler->printContent($oModule); - } - - /** - * @brief returns module's path - * @param[in] $module module name - * @return path of the module - **/ - function getModulePath($module) { - return sprintf('./modules/%s/', $module); - } - - /** - * @brief It creates a module instance - * @param[in] $module module name - * @param[in] $type instance type, (e.g., view, controller, model) - * @param[in] $kind admin or svc - * @return module instance (if failed it returns null) - * @remarks if there exists a module instance created before, returns it. - **/ - function &getModuleInstance($module, $type = 'view', $kind = '') { - - if(__DEBUG__==3) $start_time = getMicroTime(); - - $kind = strtolower($kind); - $type = strtolower($type); - - $kinds = explode(' ', 'svc admin'); - if(!in_array($kind, $kinds)) $kind = $kinds[0]; - - $key = $module.'.'.($kind!='admin'?'':'admin').'.'.$type; - - if(is_array($GLOBALS['__MODULE_EXTEND__']) && array_key_exists($key, $GLOBALS['__MODULE_EXTEND__'])) { - $module = $extend_module = $GLOBALS['__MODULE_EXTEND__'][$key]; - }else{ - unset($parent_module); - } - - // if there is no instance of the module in global variable, create a new one - if(!$GLOBALS['_loaded_module'][$module][$type][$kind]) { - $parent_module = $module; - - $class_path = ModuleHandler::getModulePath($module); - if(!is_dir(FileHandler::getRealPath($class_path))) return NULL; - - // Get base class name and load the file contains it - if(!class_exists($module)) { - $high_class_file = sprintf('%s%s%s.class.php', _XE_PATH_,$class_path, $module); - if(!file_exists($high_class_file)) return NULL; - require_once($high_class_file); - } - - // Get the object's name - $types = explode(' ', 'view controller model api wap mobile class'); - if(!in_array($type, $types)) $type = $types[0]; - if($type == 'class') { - $instance_name = '%s'; - $class_file = '%s%s.%s.php'; - } elseif($kind == 'admin' && array_search($type, $types) < 3) { - $instance_name = '%sAdmin%s'; - $class_file = '%s%s.admin.%s.php'; - } else{ - $instance_name = '%s%s'; - $class_file = '%s%s.%s.php'; - } - $instance_name = sprintf($instance_name, $module, ucfirst($type)); - $class_file = sprintf($class_file, $class_path, $module, $type); - $class_file = FileHandler::getRealPath($class_file); - - // Get the name of the class file - if(!is_readable($class_file)) return NULL; - - // Create an instance with eval function - require_once($class_file); - if(!class_exists($instance_name)) return NULL; - $tmp_fn = create_function('', "return new {$instance_name}();"); - $oModule = $tmp_fn(); - if(!is_object($oModule)) return NULL; - - // Load language files for the class - Context::loadLang($class_path.'lang'); - if($extend_module) { - Context::loadLang(ModuleHandler::getModulePath($parent_module).'lang'); - } - - // Set variables to the instance - $oModule->setModule($module); - $oModule->setModulePath($class_path); - - // If the module has a constructor, run it. - if(!isset($GLOBALS['_called_constructor'][$instance_name])) { - $GLOBALS['_called_constructor'][$instance_name] = true; - if(@method_exists($oModule, $instance_name)) $oModule->{$instance_name}(); - } - - // Store the created instance into GLOBALS variable - $GLOBALS['_loaded_module'][$module][$type][$kind] = $oModule; - } - - if(__DEBUG__==3) $GLOBALS['__elapsed_class_load__'] += getMicroTime() - $start_time; - - // return the instance - return $GLOBALS['_loaded_module'][$module][$type][$kind]; - } - - /** - * @brief call a trigger - * @param[in] $trigger_name trigger's name to call - * @param[in] $called_position called position - * @param[in] $obj an object as a parameter to trigger - * @return Object - **/ - function triggerCall($trigger_name, $called_position, &$obj) { - // skip if not installed - if(!Context::isInstalled()) return new Object(); - - $oModuleModel = &getModel('module'); - $triggers = $oModuleModel->getTriggers($trigger_name, $called_position); - if(!$triggers || !count($triggers)) return new Object(); - - foreach($triggers as $item) { - $module = $item->module; - $type = $item->type; - $called_method = $item->called_method; - - $oModule = null; - $oModule = &getModule($module, $type); - if(!$oModule || !method_exists($oModule, $called_method)) continue; - - $output = $oModule->{$called_method}($obj); - if(is_object($output) && method_exists($output, 'toBool') && !$output->toBool()) return $output; - unset($oModule); - } - - return new Object(); - } - } -?> +module = 'install'; + $this->act = Context::get('act'); + return; + } + // Set variables from request arguments + $this->module = $module?$module:Context::get('module'); + $this->act = $act?$act:Context::get('act'); + $this->mid = $mid?$mid:Context::get('mid'); + $this->document_srl = $document_srl?(int)$document_srl:(int)Context::get('document_srl'); + $this->module_srl = $module_srl?(int)$module_srl:(int)Context::get('module_srl'); + $this->entry = Context::convertEncodingStr(Context::get('entry')); + + // Validate variables to prevent XSS + $isInvalid = null; + if($this->module && !preg_match("/^([a-z0-9\_\-]+)$/i",$this->module)) $isInvalid = true; + if($this->mid && !preg_match("/^([a-z0-9\_\-]+)$/i",$this->mid)) $isInvalid = true; + if($this->act && !preg_match("/^([a-z0-9\_\-]+)$/i",$this->act)) $isInvalid = true; + if ($isInvalid) + { + htmlHeader(); + echo Context::getLang("msg_invalid_request"); + htmlFooter(); + Context::close(); + exit; + } + + // execute addon (before module initialization) + $called_position = 'before_module_init'; + $oAddonController = &getController('addon'); + $addon_file = $oAddonController->getCacheFilePath(Mobile::isFromMobilePhone()?'mobile':'pc'); + @include($addon_file); + } + + /** + * @brief Initialization. It finds the target module based on module, mid, document_srl, and prepares to execute an action + * @return true: OK, false: redirected + **/ + function init() { + $oModuleModel = &getModel('module'); + $site_module_info = Context::get('site_module_info'); + + if(!$this->document_srl && $this->mid && $this->entry) { + $oDocumentModel = &getModel('document'); + $this->document_srl = $oDocumentModel->getDocumentSrlByAlias($this->mid, $this->entry); + if($this->document_srl) Context::set('document_srl', $this->document_srl); + } + + // Get module's information based on document_srl, if it's specified + if($this->document_srl && !$this->module) { + $module_info = $oModuleModel->getModuleInfoByDocumentSrl($this->document_srl); + + // If the document does not exist, remove document_srl + if(!$module_info) { + unset($this->document_srl); + } else { + // If it exists, compare mid based on the module information + // if mids are not matching, set it as the document's mid + if($this->mid != $module_info->mid) { + $this->mid = $module_info->mid; + Context::set('mid', $module_info->mid, true); + } + } + // if requested module is different from one of the document, remove the module information retrieved based on the document number + if($this->module && $module_info->module != $this->module) unset($module_info); + } + + // If module_info is not set yet, and there exists mid information, get module information based on the mid + if(!$module_info && $this->mid) { + $module_info = $oModuleModel->getModuleInfoByMid($this->mid, $site_module_info->site_srl); + //if($this->module && $module_info->module != $this->module) unset($module_info); + } + + // redirect, if module_site_srl and site_srl are different + if(!$this->module && !$module_info && $site_module_info->site_srl == 0 && $site_module_info->module_site_srl > 0) { + $site_info = $oModuleModel->getSiteInfo($site_module_info->module_site_srl); + header("location:".getNotEncodedSiteUrl($site_info->domain,'mid',$site_module_info->mid)); + return false; + } + + // If module_info is not set still, and $module does not exist, find the default module + if(!$module_info && !$this->module && !$this->mid) $module_info = $site_module_info; + + if(!$module_info && !$this->module && $site_module_info->module_site_srl) $module_info = $site_module_info; + + // redirect, if site_srl of module_info is different from one of site's module_info + if($module_info && $module_info->site_srl != $site_module_info->site_srl && !isCrawler()) { + // If the module is of virtual site + if($module_info->site_srl) { + $site_info = $oModuleModel->getSiteInfo($module_info->site_srl); + $redirect_url = getNotEncodedSiteUrl($site_info->domain, 'mid',Context::get('mid'),'document_srl',Context::get('document_srl'),'module_srl',Context::get('module_srl'),'entry',Context::get('entry')); + // If it's called from a virtual site, though it's not a module of the virtual site + } else { + $db_info = Context::getDBInfo(); + if(!$db_info->default_url) return Context::getLang('msg_default_url_is_not_defined'); + else $redirect_url = getNotEncodedSiteUrl($db_info->default_url, 'mid',Context::get('mid'),'document_srl',Context::get('document_srl'),'module_srl',Context::get('module_srl'),'entry',Context::get('entry')); + } + header("location:".$redirect_url); + return false; + } + + // If module info was set, retrieve variables from the module information + if($module_info) { + $this->module = $module_info->module; + $this->mid = $module_info->mid; + $this->module_info = $module_info; + Context::setBrowserTitle($module_info->browser_title); + $part_config= $oModuleModel->getModulePartConfig('layout',$module_info->layout_srl); + Context::addHtmlHeader($part_config->header_script); + } + + // Set module and mid into module_info + $this->module_info->module = $this->module; + $this->module_info->mid = $this->mid; + + // Set site_srl add 2011 08 09 + $this->module_info->site_srl = $site_module_info->site_srl; + + // Still no module? it's an error + if(!$this->module) + { + $this->error = 'msg_module_is_not_exists'; + $this->httpStatusCode = '404'; + } + + // If mid exists, set mid into context + if($this->mid) Context::set('mid', $this->mid, true); + + // Call a trigger after moduleHandler init + $output = ModuleHandler::triggerCall('moduleHandler.init', 'after', $this->module_info); + if(!$output->toBool()) { + $this->error = $output->getMessage(); + return false; + } + + // Set current module info into context + Context::set('current_module_info', $this->module_info); + + return true; + } + + /** + * @brief get a module instance and execute an action + * @return executed module instance + **/ + function procModule() { + $oModuleModel = &getModel('module'); + + // If error occurred while preparation, return a message instance + if($this->error) { + $type = Mobile::isFromMobilePhone() ? 'mobile' : 'view'; + $oMessageObject = &ModuleHandler::getModuleInstance('message',$type); + $oMessageObject->setError(-1); + $oMessageObject->setMessage($this->error); + $oMessageObject->dispMessage(); + if($this->httpStatusCode) + { + $oMessageObject->setHttpStatusCode($this->httpStatusCode); + } + return $oMessageObject; + } + + // Get action information with conf/module.xml + $xml_info = $oModuleModel->getModuleActionXml($this->module); + + // If not installed yet, modify act + if($this->module=="install") { + if(!$this->act || !$xml_info->action->{$this->act}) $this->act = $xml_info->default_index_act; + } + + // if act exists, find type of the action, if not use default index act + if(!$this->act) $this->act = $xml_info->default_index_act; + + // still no act means error + if(!$this->act) { + $this->error = 'msg_module_is_not_exists'; + $this->httpStatusCode = '404'; + return; + } + + // get type, kind + $type = $xml_info->action->{$this->act}->type; + $ruleset = $xml_info->action->{$this->act}->ruleset; + $kind = strpos(strtolower($this->act),'admin')!==false?'admin':''; + if(!$kind && $this->module == 'admin') $kind = 'admin'; + if($this->module_info->use_mobile != "Y") Mobile::setMobile(false); + + // admin menu check + if(Context::isInstalled()) + { + $oMenuAdminModel = &getAdminModel('menu'); + $output = $oMenuAdminModel->getMenuByTitle('__XE_ADMIN__'); + + if(!$output->menu_srl) + { + $oAdminClass = &getClass('admin'); + $oAdminClass->createXeAdminMenu(); + } + else if(!is_readable($output->php_file)) + { + $oMenuAdminController = &getAdminController('menu'); + $oMenuAdminController->makeXmlFile($output->menu_srl); + } + } + + // Admin ip + $logged_info = Context::get('logged_info'); + if($kind == 'admin' && $_SESSION['denied_admin'] == 'Y'){ + $this->error = "msg_not_permitted_act"; + $oMessageObject = &ModuleHandler::getModuleInstance('message',$type); + $oMessageObject->setError(-1); + $oMessageObject->setMessage($this->error); + $oMessageObject->dispMessage(); + return $oMessageObject; + } + + // if(type == view, and case for using mobilephone) + if($type == "view" && Mobile::isFromMobilePhone() && Context::isInstalled()) + { + $orig_type = "view"; + $type = "mobile"; + // create a module instance + $oModule = &$this->getModuleInstance($this->module, $type, $kind); + if(!is_object($oModule) || !method_exists($oModule, $this->act)) { + $type = $orig_type; + Mobile::setMobile(false); + $oModule = &$this->getModuleInstance($this->module, $type, $kind); + } + } + else + { + // create a module instance + $oModule = &$this->getModuleInstance($this->module, $type, $kind); + } + + if(!is_object($oModule)) { + $this->error = 'msg_module_is_not_exists'; + $this->httpStatusCode = '404'; + return; + } + + // If there is no such action in the module object + if(!isset($xml_info->action->{$this->act}) || !method_exists($oModule, $this->act)) + { + + if(!Context::isInstalled()) + { + $this->error = 'msg_invalid_request'; + return; + } + + $forward = null; + // 1. Look for the module with action name + if(preg_match('/^([a-z]+)([A-Z])([a-z0-9\_]+)(.*)$/', $this->act, $matches)) { + $module = strtolower($matches[2].$matches[3]); + $xml_info = $oModuleModel->getModuleActionXml($module); + if($xml_info->action->{$this->act}) { + $forward->module = $module; + $forward->type = $xml_info->action->{$this->act}->type; + $forward->ruleset = $xml_info->action->{$this->act}->ruleset; + $forward->act = $this->act; + } + } + + if(!$forward) + { + $forward = $oModuleModel->getActionForward($this->act); + } + + if($forward->module && $forward->type && $forward->act && $forward->act == $this->act) { + $kind = strpos(strtolower($forward->act),'admin')!==false?'admin':''; + $type = $forward->type; + $ruleset = $forward->ruleset; + $tpl_path = $oModule->getTemplatePath(); + $orig_module = $oModule; + + if($type == "view" && Mobile::isFromMobilePhone()) + { + $orig_type = "view"; + $type = "mobile"; + // create a module instance + $oModule = &$this->getModuleInstance($forward->module, $type, $kind); + if(!is_object($oModule) || !method_exists($oModule, $this->act)) { + $type = $orig_type; + Mobile::setMobile(false); + $oModule = &$this->getModuleInstance($forward->module, $type, $kind); + } + } + else + { + $oModule = &$this->getModuleInstance($forward->module, $type, $kind); + } + $xml_info = $oModuleModel->getModuleActionXml($forward->module); + $oMemberModel = &getModel('member'); + + if($this->module == "admin" && $type == "view") + { + if($logged_info->is_admin=='Y'){ + if ($this->act != 'dispLayoutAdminLayoutModify') + { + $oAdminView = &getAdminView('admin'); + $oAdminView->makeGnbUrl($forward->module); + $oModule->setLayoutPath("./modules/admin/tpl"); + $oModule->setLayoutFile("layout.html"); + } + }else{ + $this->error = 'msg_is_not_administrator'; + $oMessageObject = &ModuleHandler::getModuleInstance('message',$type); + $oMessageObject->setError(-1); + $oMessageObject->setMessage($this->error); + $oMessageObject->dispMessage(); + return $oMessageObject; + } + } + if ($kind == 'admin'){ + $grant = $oModuleModel->getGrant($this->module_info, $logged_info); + if(!$grant->is_admin && !$grant->manager) { + $this->error = 'msg_is_not_manager'; + $oMessageObject = &ModuleHandler::getModuleInstance('message','view'); + $oMessageObject->setError(-1); + $oMessageObject->setMessage($this->error); + $oMessageObject->dispMessage(); + return $oMessageObject; + } + + } + } + else if($xml_info->default_index_act && method_exists($oModule, $xml_info->default_index_act)) + { + $this->act = $xml_info->default_index_act; + } + else + { + $this->error = 'msg_invalid_request'; + return; + } + } + + // ruleset check... + if(!empty($ruleset)) + { + $rulesetModule = $forward->module ? $forward->module : $this->module; + $rulesetFile = $oModuleModel->getValidatorFilePath($rulesetModule, $ruleset); + if(!empty($rulesetFile)) + { + $Validator = new Validator($rulesetFile); + $result = $Validator->validate(); + if(!$result) + { + $lastError = $Validator->getLastError(); + $returnUrl = Context::get('error_return_url'); + $errorMsg = $lastError['msg'] ? $lastError['msg'] : 'validation error'; + + //for xml response + $oModule->setError(-1); + $oModule->setMessage($errorMsg); + //for html redirect + $this->error = $errorMsg; + $_SESSION['XE_VALIDATOR_ERROR'] = -1; + $_SESSION['XE_VALIDATOR_MESSAGE'] = $this->error; + $_SESSION['XE_VALIDATOR_MESSAGE_TYPE'] = 'error'; + $_SESSION['XE_VALIDATOR_RETURN_URL'] = $returnUrl; + $this->_setInputValueToSession(); + return $oModule; + } + } + } + + $oModule->setAct($this->act); + + $this->module_info->module_type = $type; + $oModule->setModuleInfo($this->module_info, $xml_info); + + if($type == "view" && $this->module_info->use_mobile == "Y" && Mobile::isMobileCheckByAgent()) + { + global $lang; + $footer = ''; + Context::addHtmlFooter($footer); + } + + if($type == "view" && $kind != 'admin'){ + $module_config= $oModuleModel->getModuleConfig('module'); + if($module_config->htmlFooter){ + Context::addHtmlFooter($module_config->htmlFooter); + } + } + + + // if failed message exists in session, set context + $this->_setInputErrorToContext(); + + $procResult = $oModule->proc(); + + if(!in_array(Context::getRequestMethod(),array('XMLRPC','JSON'))) + { + $error = $oModule->getError(); + $message = $oModule->getMessage(); + $messageType = $oModule->getMessageType(); + $redirectUrl = $oModule->getRedirectUrl(); + + if (!$procResult) + { + $this->error = $message; + if (!$redirectUrl && Context::get('error_return_url')) $redirectUrl = Context::get('error_return_url'); + $this->_setInputValueToSession(); + + } + else + { + if(count($_SESSION['INPUT_ERROR'])) + { + Context::set('INPUT_ERROR', $_SESSION['INPUT_ERROR']); + $_SESSION['INPUT_ERROR'] = ''; + } + } + + $_SESSION['XE_VALIDATOR_ERROR'] = $error; + if ($message != 'success') $_SESSION['XE_VALIDATOR_MESSAGE'] = $message; + $_SESSION['XE_VALIDATOR_MESSAGE_TYPE'] = $messageType; + $_SESSION['XE_VALIDATOR_RETURN_URL'] = $redirectUrl; + } + + unset($logged_info); + return $oModule; + } + + function _setInputErrorToContext() + { + if($_SESSION['XE_VALIDATOR_ERROR'] && !Context::get('XE_VALIDATOR_ERROR')) Context::set('XE_VALIDATOR_ERROR', $_SESSION['XE_VALIDATOR_ERROR']); + if($_SESSION['XE_VALIDATOR_MESSAGE'] && !Context::get('XE_VALIDATOR_MESSAGE')) Context::set('XE_VALIDATOR_MESSAGE', $_SESSION['XE_VALIDATOR_MESSAGE']); + if($_SESSION['XE_VALIDATOR_MESSAGE_TYPE'] && !Context::get('XE_VALIDATOR_MESSAGE_TYPE')) Context::set('XE_VALIDATOR_MESSAGE_TYPE', $_SESSION['XE_VALIDATOR_MESSAGE_TYPE']); + if($_SESSION['XE_VALIDATOR_RETURN_URL'] && !Context::get('XE_VALIDATOR_RETURN_URL')) Context::set('XE_VALIDATOR_RETURN_URL', $_SESSION['XE_VALIDATOR_RETURN_URL']); + + $this->_clearErrorSession(); + } + + function _clearErrorSession() + { + $_SESSION['XE_VALIDATOR_ERROR'] = ''; + $_SESSION['XE_VALIDATOR_MESSAGE'] = ''; + $_SESSION['XE_VALIDATOR_MESSAGE_TYPE'] = ''; + $_SESSION['XE_VALIDATOR_RETURN_URL'] = ''; + } + + function _setInputValueToSession() + { + $requestVars = Context::getRequestVars(); + unset($requestVars->act, $requestVars->mid, $requestVars->vid, $requestVars->success_return_url, $requestVars->error_return_url); + foreach($requestVars AS $key=>$value) $_SESSION['INPUT_ERROR'][$key] = $value; + } + + /** + * @brief display contents from executed module + * @param[in] $oModule module instance + * @return none + **/ + function displayContent($oModule = NULL) { + // If the module is not set or not an object, set error + if(!$oModule || !is_object($oModule)) { + $this->error = 'msg_module_is_not_exists'; + $this->httpStatusCode = '404'; + } + + // If connection to DB has a problem even though it's not install module, set error + if($this->module != 'install' && $GLOBALS['__DB__'][Context::getDBType()]->isConnected() == false) { + $this->error = 'msg_dbconnect_failed'; + } + + // Call trigger after moduleHandler proc + $output = ModuleHandler::triggerCall('moduleHandler.proc', 'after', $oModule); + if(!$output->toBool()) $this->error = $output->getMessage(); + + // Use message view object, if HTML call + if(!in_array(Context::getRequestMethod(),array('XMLRPC','JSON'))) { + + if($_SESSION['XE_VALIDATOR_RETURN_URL']) + { + header('location:'.$_SESSION['XE_VALIDATOR_RETURN_URL']); + return; + } + + // If error occurred, handle it + if($this->error) { + // display content with message module instance + $type = Mobile::isFromMobilePhone() ? 'mobile' : 'view'; + $oMessageObject = &ModuleHandler::getModuleInstance('message',$type); + $oMessageObject->setError(-1); + $oMessageObject->setMessage($this->error); + $oMessageObject->dispMessage(); + + if($oMessageObject->getHttpStatusCode() && $oMessageObject->getHttpStatusCode() != '200') + { + $this->_setHttpStatusMessage($oMessageObject->getHttpStatusCode()); + $oMessageObject->setTemplateFile('http_status_code'); + } + + // If module was called normally, change the templates of the module into ones of the message view module + if($oModule) { + $oModule->setTemplatePath($oMessageObject->getTemplatePath()); + $oModule->setTemplateFile($oMessageObject->getTemplateFile()); + // Otherwise, set message instance as the target module + } else { + $oModule = $oMessageObject; + } + + $this->_clearErrorSession(); + } + + // Check if layout_srl exists for the module + if(Mobile::isFromMobilePhone()) + { + $layout_srl = $oModule->module_info->mlayout_srl; + } + else + { + $layout_srl = $oModule->module_info->layout_srl; + } + + if($layout_srl && !$oModule->getLayoutFile()) { + + // If layout_srl exists, get information of the layout, and set the location of layout_path/ layout_file + $oLayoutModel = &getModel('layout'); + $layout_info = $oLayoutModel->getLayout($layout_srl); + if($layout_info) { + + // Input extra_vars into $layout_info + if($layout_info->extra_var_count) { + + foreach($layout_info->extra_var as $var_id => $val) { + if($val->type == 'image') { + if(preg_match('/^\.\/files\/attach\/images\/(.+)/i',$val->value)) $val->value = Context::getRequestUri().substr($val->value,2); + } + $layout_info->{$var_id} = $val->value; + } + } + // Set menus into context + 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); + } + } + + // Set layout information into context + Context::set('layout_info', $layout_info); + + $oModule->setLayoutPath($layout_info->path); + $oModule->setLayoutFile('layout'); + + // If layout was modified, use the modified version + $edited_layout = $oLayoutModel->getUserLayoutHtml($layout_info->layout_srl); + if(file_exists($edited_layout)) $oModule->setEditedLayoutFile($edited_layout); + } + } + } + + // Display contents + $oDisplayHandler = new DisplayHandler(); + $oDisplayHandler->printContent($oModule); + } + + /** + * @brief returns module's path + * @param[in] $module module name + * @return path of the module + **/ + function getModulePath($module) { + return sprintf('./modules/%s/', $module); + } + + /** + * @brief It creates a module instance + * @param[in] $module module name + * @param[in] $type instance type, (e.g., view, controller, model) + * @param[in] $kind admin or svc + * @return module instance (if failed it returns null) + * @remarks if there exists a module instance created before, returns it. + **/ + function &getModuleInstance($module, $type = 'view', $kind = '') { + + if(__DEBUG__==3) $start_time = getMicroTime(); + + $kind = strtolower($kind); + $type = strtolower($type); + + $kinds = explode(' ', 'svc admin'); + if(!in_array($kind, $kinds)) $kind = $kinds[0]; + + $key = $module.'.'.($kind!='admin'?'':'admin').'.'.$type; + + if(is_array($GLOBALS['__MODULE_EXTEND__']) && array_key_exists($key, $GLOBALS['__MODULE_EXTEND__'])) { + $module = $extend_module = $GLOBALS['__MODULE_EXTEND__'][$key]; + }else{ + unset($parent_module); + } + + // if there is no instance of the module in global variable, create a new one + if(!$GLOBALS['_loaded_module'][$module][$type][$kind]) { + $parent_module = $module; + + $class_path = ModuleHandler::getModulePath($module); + if(!is_dir(FileHandler::getRealPath($class_path))) return NULL; + + // Get base class name and load the file contains it + if(!class_exists($module)) { + $high_class_file = sprintf('%s%s%s.class.php', _XE_PATH_,$class_path, $module); + if(!file_exists($high_class_file)) return NULL; + require_once($high_class_file); + } + + // Get the object's name + $types = explode(' ', 'view controller model api wap mobile class'); + if(!in_array($type, $types)) $type = $types[0]; + if($type == 'class') { + $instance_name = '%s'; + $class_file = '%s%s.%s.php'; + } elseif($kind == 'admin' && array_search($type, $types) < 3) { + $instance_name = '%sAdmin%s'; + $class_file = '%s%s.admin.%s.php'; + } else{ + $instance_name = '%s%s'; + $class_file = '%s%s.%s.php'; + } + $instance_name = sprintf($instance_name, $module, ucfirst($type)); + $class_file = sprintf($class_file, $class_path, $module, $type); + $class_file = FileHandler::getRealPath($class_file); + + // Get the name of the class file + if(!is_readable($class_file)) return NULL; + + // Create an instance with eval function + require_once($class_file); + if(!class_exists($instance_name)) return NULL; + $tmp_fn = create_function('', "return new {$instance_name}();"); + $oModule = $tmp_fn(); + if(!is_object($oModule)) return NULL; + + // Load language files for the class + Context::loadLang($class_path.'lang'); + if($extend_module) { + Context::loadLang(ModuleHandler::getModulePath($parent_module).'lang'); + } + + // Set variables to the instance + $oModule->setModule($module); + $oModule->setModulePath($class_path); + + // If the module has a constructor, run it. + if(!isset($GLOBALS['_called_constructor'][$instance_name])) { + $GLOBALS['_called_constructor'][$instance_name] = true; + if(@method_exists($oModule, $instance_name)) $oModule->{$instance_name}(); + } + + // Store the created instance into GLOBALS variable + $GLOBALS['_loaded_module'][$module][$type][$kind] = $oModule; + } + + if(__DEBUG__==3) $GLOBALS['__elapsed_class_load__'] += getMicroTime() - $start_time; + + // return the instance + return $GLOBALS['_loaded_module'][$module][$type][$kind]; + } + + /** + * @brief call a trigger + * @param[in] $trigger_name trigger's name to call + * @param[in] $called_position called position + * @param[in] $obj an object as a parameter to trigger + * @return Object + **/ + function triggerCall($trigger_name, $called_position, &$obj) { + // skip if not installed + if(!Context::isInstalled()) return new Object(); + + $oModuleModel = &getModel('module'); + $triggers = $oModuleModel->getTriggers($trigger_name, $called_position); + if(!$triggers || !count($triggers)) return new Object(); + + foreach($triggers as $item) { + $module = $item->module; + $type = $item->type; + $called_method = $item->called_method; + + $oModule = null; + $oModule = &getModule($module, $type); + if(!$oModule || !method_exists($oModule, $called_method)) continue; + + $output = $oModule->{$called_method}($obj); + if(is_object($output) && method_exists($output, 'toBool') && !$output->toBool()) return $output; + unset($oModule); + } + + return new Object(); + } + + /** + * @brief get http status message by http status code + **/ + function _setHttpStatusMessage($code) { + $statusMessageList = array( + '100'=>'Continue', + '101'=>'Switching Protocols', + '201'=>'OK', + '201'=>'Created', + '202'=>'Accepted', + '203'=>'Non-Authoritative Information', + '204'=>'No Content', + '205'=>'Reset Content', + '206'=>'Partial Content', + '300'=>'Multiple Choices', + '301'=>'Moved Permanently', + '302'=>'Found', + '303'=>'See Other', + '304'=>'Not Modified', + '305'=>'Use Proxy', + '307'=>'Temporary Redirect', + '400'=>'Bad Request', + '401'=>'Unauthorized', + '402'=>'Payment Required', + '403'=>'Forbidden', + '404'=>'Not Found', + '405'=>'Method Not Allowed', + '406'=>'Not Acceptable', + '407'=>'Proxy Authentication Required', + '408'=>'Request Timeout', + '409'=>'Conflict', + '410'=>'Gone', + '411'=>'Length Required', + '412'=>'Precondition Failed', + '413'=>'Request Entity Too Large', + '414'=>'Request-URI Too Long', + '415'=>'Unsupported Media Type', + '416'=>'Requested Range Not Satisfiable', + '417'=>'Expectation Failed', + '500'=>'Internal Server Error', + '501'=>'Not Implemented', + '502'=>'Bad Gateway', + '503'=>'Service Unavailable', + '504'=>'Gateway Timeout', + '505'=>'HTTP Version Not Supported', + ); + $statusMessage = $statusMessageList[$code]; + if(!$statusMessage) $statusMessage = 'OK'; + + Context::set('http_status_code', $code); + Context::set('http_status_message', $statusMessage); + } + } +?> diff --git a/classes/module/ModuleObject.class.php b/classes/module/ModuleObject.class.php index 35f0fba01..68409c7d0 100644 --- a/classes/module/ModuleObject.class.php +++ b/classes/module/ModuleObject.class.php @@ -1,311 +1,345 @@ -module = $module; - } - - /** - * @brief setter to set the name of module path - * @param the directory path to a module directory - **/ - function setModulePath($path) { - if(substr($path,-1)!='/') $path.='/'; - $this->module_path = $path; - } - - /** - * @brief setter to set an url for redirection - * @param $url url for redirection - * @remark redirect_url is used only for ajax requests - **/ - function setRedirectUrl($url='./') { - $this->add('redirect_url', $url); - } - - /** - * @brief sett to set the template path for refresh.html - * @remark refresh.html is executed as a result of method execution - * 공통 tpl중 refresh.html을 실행할 뿐.. - **/ - function setRefreshPage() { - $this->setTemplatePath('./common/tpl'); - $this->setTemplateFile('refresh'); - } - - - /** - * @brief sett to set the action name - **/ - function setAct($act) { - $this->act = $act; - } - - /** - * @brief sett to set module information - * @param[in] $module_info object containing module information - * @param[in] $xml_info object containing module description - **/ - 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'); - - // module model 객체 생성 - $oModuleModel = &getModel('module'); - - // XE에서 access, manager (== is_admin) 는 고정된 권한명이며 이와 관련된 권한 설정 - $module_srl = Context::get('module_srl'); - if(!$module_info->mid && preg_match('/^([0-9]+)$/',$module_srl)) { - $request_module = $oModuleModel->getModuleInfoByModuleSrl($module_srl); - if($request_module->module_srl == $module_srl) { - $grant = $oModuleModel->getGrant($request_module, $logged_info); - } - } else { - $grant = $oModuleModel->getGrant($module_info, $logged_info, $xml_info); - } - - // 현재 모듈의 access 권한이 없으면 권한 없음 표시 - //if(!$grant->access) return $this->stop("msg_not_permitted"); - - // 관리 권한이 없으면 permision, action 확인 - if(!$grant->manager) { - // 현재 요청된 action의 퍼미션 type(guest, member, manager, root)를 구함 - $permission_target = $xml_info->permission->{$this->act}; - - // module.xml에 명시된 퍼미션이 없을때 action명에 Admin이 있으면 manager로 체크 - if(!$permission_target && substr_count($this->act, 'Admin')) $permission_target = 'manager'; - - // 권한 체크 - switch($permission_target) { - case 'root' : - $this->stop('msg_not_permitted_act'); - break; - case 'manager' : - if(!$grant->manager) $this->stop('msg_not_permitted_act'); - break; - case 'member' : - if(!$is_logged) $this->stop('msg_not_permitted_act'); - break; - } - } - - // 권한변수 설정 - $this->grant = $grant; - Context::set('grant', $grant); - - if(method_exists($this, 'init')) $this->init(); - } - - /** - * @brief set the stop_proc and approprate message for msg_code - * @param $msg_code an error code - **/ - function stop($msg_code) { - // proc 수행을 중지 시키기 위한 플래그 세팅 - $this->stop_proc = true; - - // 에러 처리 - $this->setError(-1); - $this->setMessage($msg_code); - - // message 모듈의 에러 표시 - $type = Mobile::isFromMobilePhone() ? 'mobile' : 'view'; - $oMessageObject = &ModuleHandler::getModuleInstance('message',$type); - $oMessageObject->setError(-1); - $oMessageObject->setMessage($msg_code); - $oMessageObject->dispMessage(); - - $this->setTemplatePath($oMessageObject->getTemplatePath()); - $this->setTemplateFile($oMessageObject->getTemplateFile()); - - return $this; - } - - /** - * @brief set the file name of the template file - **/ - function setTemplateFile($filename) { - if(substr($filename,-5)!='.html') $filename .= '.html'; - $this->template_file = $filename; - } - - /** - * @brief retrieve the directory path of the template directory - **/ - function getTemplateFile() { - return $this->template_file; - } - - /** - * @brief set the directory path of the template directory - **/ - function setTemplatePath($path) { - if(substr($path,0,1)!='/' && substr($path,0,2)!='./') $path = './'.$path; - if(substr($path,-1)!='/') $path .= '/'; - $this->template_path = $path; - } - - /** - - * @brief retrieve the directory path of the template directory - **/ - function getTemplatePath() { - return $this->template_path; - } - - /** - * @brief set the file name of the temporarily modified by admin - **/ - function setEditedLayoutFile($filename) { - if(substr($filename,-5)!='.html') $filename .= '.html'; - $this->edited_layout_file = $filename; - } - - /** - * @brief retreived the file name of edited_layout_file - **/ - function getEditedLayoutFile() { - return $this->edited_layout_file; - } - - /** - * @brief set the file name of the layout file - **/ - function setLayoutFile($filename) { - if(substr($filename,-5)!='.html') $filename .= '.html'; - $this->layout_file = $filename; - } - - /** - * @brief get the file name of the layout file - **/ - function getLayoutFile() { - return $this->layout_file; - } - - /** - * @brief set the directory path of the layout directory - **/ - function setLayoutPath($path) { - if(substr($path,0,1)!='/' && substr($path,0,2)!='./') $path = './'.$path; - if(substr($path,-1)!='/') $path .= '/'; - $this->layout_path = $path; - } - - /** - * @brief set the directory path of the layout directory - **/ - function getLayoutPath() { - return $this->layout_path; - } - - /** - * @brief excute the member method specified by $act variable - * - **/ - function proc() { - // stop_proc==true이면 그냥 패스 - if($this->stop_proc) return false; - - // trigger call - $triggerOutput = ModuleHandler::triggerCall('moduleObject.proc', 'before', $this); - if(!$triggerOutput->toBool()) { - $this->setError($triggerOutput->getError()); - $this->setMessage($triggerOutput->getMessage()); - return false; - } - - // addon 실행(called_position 를 before_module_proc로 하여 호출) - $called_position = 'before_module_proc'; - $oAddonController = &getController('addon'); - $addon_file = $oAddonController->getCacheFilePath(Mobile::isFromMobilePhone()?"mobile":"pc"); - @include($addon_file); - - if(isset($this->xml_info->action->{$this->act}) && method_exists($this, $this->act)) { - - // 권한 체크 - if(!$this->grant->access) return $this->stop("msg_not_permitted_act"); - - // 모듈의 스킨 정보를 연동 (스킨 정보의 테이블 분리로 동작대상 모듈에만 스킨 정보를 싱크시키도록 변경) - $oModuleModel = &getModel('module'); - $oModuleModel->syncSkinInfoToModuleInfo($this->module_info); - Context::set('module_info', $this->module_info); - - // 실행 - $output = $this->{$this->act}(); - } - else { - return false; - } - - // trigger call - $triggerOutput = ModuleHandler::triggerCall('moduleObject.proc', 'after', $this); - if(!$triggerOutput->toBool()) { - $this->setError($triggerOutput->getError()); - $this->setMessage($triggerOutput->getMessage()); - return false; - } - - // addon 실행(called_position 를 after_module_proc로 하여 호출) - $called_position = 'after_module_proc'; - $oAddonController = &getController('addon'); - $addon_file = $oAddonController->getCacheFilePath(Mobile::isFromMobilePhone()?"mobile":"pc"); - @include($addon_file); - - if(is_a($output, 'Object') || is_subclass_of($output, 'Object')) { - $this->setError($output->getError()); - $this->setMessage($output->getMessage()); - return false; - } - - // view action이고 결과 출력이 XMLRPC 또는 JSON일 경우 해당 모듈의 api method를 실행 - if($this->module_info->module_type == 'view'){ - if(Context::getResponseMethod() == 'XMLRPC' || Context::getResponseMethod() == 'JSON') { - $oAPI = getAPI($this->module_info->module, 'api'); - if(method_exists($oAPI, $this->act)) { - $oAPI->{$this->act}($this); - } - } - } - - return true; - } - } -?> +module = $module; + } + + /** + * @brief setter to set the name of module path + * @param the directory path to a module directory + **/ + function setModulePath($path) { + if(substr($path,-1)!='/') $path.='/'; + $this->module_path = $path; + } + + /** + * @brief setter to set an url for redirection + * @param $url url for redirection + * @remark redirect_url is used only for ajax requests + **/ + function setRedirectUrl($url='./') { + $this->add('redirect_url', $url); + } + + /** + * @brief get url for redirection + **/ + function getRedirectUrl(){ + return $this->get('redirect_url'); + } + + /** + * @brief set message + * @param $message a message string + * @param $type type of message (error, info, update) + **/ + function setMessage($message, $type = null){ + parent::setMessage($message); + $this->setMessageType($type); + } + + /** + * @brief set type of message + * @param $type type of message (error, info, update) + **/ + function setMessageType($type){ + $this->add('message_type', $type); + } + + /** + * @brief get type of message + **/ + function getMessageType(){ + $type = $this->get('message_type'); + if (!in_array($type, array('error', 'info', 'update'))){ + $type = $this->getError()?'error':'info'; + } + return $type; + } + + /** + * @brief sett to set the template path for refresh.html + * @remark refresh.html is executed as a result of method execution + * Tpl as the common run of the refresh.html .. + **/ + function setRefreshPage() { + $this->setTemplatePath('./common/tpl'); + $this->setTemplateFile('refresh'); + } + + + /** + * @brief sett to set the action name + **/ + function setAct($act) { + $this->act = $act; + } + + /** + * @brief sett to set module information + * @param[in] $module_info object containing module information + * @param[in] $xml_info object containing module description + **/ + function setModuleInfo($module_info, $xml_info) { + // The default variable settings + $this->mid = $module_info->mid; + $this->module_srl = $module_info->module_srl; + $this->module_info = $module_info; + $this->origin_module_info = $module_info; + $this->xml_info = $xml_info; + $this->skin_vars = $module_info->skin_vars; + // validate certificate info and permission settings necessary in Web-services + $is_logged = Context::get('is_logged'); + $logged_info = Context::get('logged_info'); + // module model create an object + $oModuleModel = &getModel('module'); + // permission settings. access, manager(== is_admin) are fixed and privilege name in XE + $module_srl = Context::get('module_srl'); + if(!$module_info->mid && preg_match('/^([0-9]+)$/',$module_srl)) { + $request_module = $oModuleModel->getModuleInfoByModuleSrl($module_srl); + if($request_module->module_srl == $module_srl) { + $grant = $oModuleModel->getGrant($request_module, $logged_info); + } + } else { + $grant = $oModuleModel->getGrant($module_info, $logged_info, $xml_info); + // have at least access grant + if( substr_count($this->act, 'Member') || substr_count($this->act, 'Communication')) + $grant->access = 1; + } + // display no permission if the current module doesn't have an access privilege + //if(!$grant->access) return $this->stop("msg_not_permitted"); + // checks permission and action if you don't have an admin privilege + if(!$grant->manager) { + // get permission types(guest, member, manager, root) of the currently requested action + $permission_target = $xml_info->permission->{$this->act}; + // check manager if a permission in module.xml otherwise action if no permission + if(!$permission_target && substr_count($this->act, 'Admin')) $permission_target = 'manager'; + // Check permissions + switch($permission_target) { + case 'root' : + $this->stop('msg_not_permitted_act'); + break; + case 'manager' : + if(!$grant->manager) $this->stop('msg_not_permitted_act'); + break; + case 'member' : + if(!$is_logged) $this->stop('msg_not_permitted_act'); + break; + } + } + // permission variable settings + $this->grant = $grant; + + Context::set('grant', $grant); + + $this->module_config = $oModuleModel->getModuleConfig($this->module, $module_info->site_srl); + + if(method_exists($this, 'init')) $this->init(); + } + + /** + * @brief set the stop_proc and approprate message for msg_code + * @param $msg_code an error code + **/ + function stop($msg_code) { + // flag setting to stop the proc processing + $this->stop_proc = true; + // Error handling + $this->setError(-1); + $this->setMessage($msg_code); + // Error message display by message module + $type = Mobile::isFromMobilePhone() ? 'mobile' : 'view'; + $oMessageObject = &ModuleHandler::getModuleInstance('message',$type); + $oMessageObject->setError(-1); + $oMessageObject->setMessage($msg_code); + $oMessageObject->dispMessage(); + + $this->setTemplatePath($oMessageObject->getTemplatePath()); + $this->setTemplateFile($oMessageObject->getTemplateFile()); + + return $this; + } + + /** + * @brief set the file name of the template file + **/ + function setTemplateFile($filename) { + if(substr($filename,-5)!='.html') $filename .= '.html'; + $this->template_file = $filename; + } + + /** + * @brief retrieve the directory path of the template directory + **/ + function getTemplateFile() { + return $this->template_file; + } + + /** + * @brief set the directory path of the template directory + **/ + function setTemplatePath($path) { + if(substr($path,0,1)!='/' && substr($path,0,2)!='./') $path = './'.$path; + if(substr($path,-1)!='/') $path .= '/'; + $this->template_path = $path; + } + + /** + + * @brief retrieve the directory path of the template directory + **/ + function getTemplatePath() { + return $this->template_path; + } + + /** + * @brief set the file name of the temporarily modified by admin + **/ + function setEditedLayoutFile($filename) { + if(substr($filename,-5)!='.html') $filename .= '.html'; + $this->edited_layout_file = $filename; + } + + /** + * @brief retreived the file name of edited_layout_file + **/ + function getEditedLayoutFile() { + return $this->edited_layout_file; + } + + /** + * @brief set the file name of the layout file + **/ + function setLayoutFile($filename) { + if(substr($filename,-5)!='.html') $filename .= '.html'; + $this->layout_file = $filename; + } + + /** + * @brief get the file name of the layout file + **/ + function getLayoutFile() { + return $this->layout_file; + } + + /** + * @brief set the directory path of the layout directory + **/ + function setLayoutPath($path) { + if(substr($path,0,1)!='/' && substr($path,0,2)!='./') $path = './'.$path; + if(substr($path,-1)!='/') $path .= '/'; + $this->layout_path = $path; + } + + /** + * @brief set the directory path of the layout directory + **/ + function getLayoutPath() { + return $this->layout_path; + } + + /** + * @brief excute the member method specified by $act variable + * + **/ + function proc() { + // pass if stop_proc is true + if($this->stop_proc) return false; + + // trigger call + $triggerOutput = ModuleHandler::triggerCall('moduleObject.proc', 'before', $this); + if(!$triggerOutput->toBool()) { + $this->setError($triggerOutput->getError()); + $this->setMessage($triggerOutput->getMessage()); + return false; + } + + // execute an addon(call called_position as before_module_proc) + $called_position = 'before_module_proc'; + $oAddonController = &getController('addon'); + $addon_file = $oAddonController->getCacheFilePath(Mobile::isFromMobilePhone()?"mobile":"pc"); + @include($addon_file); + + if(isset($this->xml_info->action->{$this->act}) && method_exists($this, $this->act)) { + // Check permissions + if(!$this->grant->access){ + return $this->stop("msg_not_permitted_act"); + } + // integrate skin information of the module(change to sync skin info with the target module only by seperating its table) + $oModuleModel = &getModel('module'); + $oModuleModel->syncSkinInfoToModuleInfo($this->module_info); + Context::set('module_info', $this->module_info); + // Run + $output = $this->{$this->act}(); + } + else { + return false; + } + + // trigger call + $triggerOutput = ModuleHandler::triggerCall('moduleObject.proc', 'after', $this); + if(!$triggerOutput->toBool()) { + $this->setError($triggerOutput->getError()); + $this->setMessage($triggerOutput->getMessage()); + return false; + } + + // execute an addon(call called_position as after_module_proc) + $called_position = 'after_module_proc'; + $oAddonController = &getController('addon'); + $addon_file = $oAddonController->getCacheFilePath(Mobile::isFromMobilePhone()?"mobile":"pc"); + @include($addon_file); + + if(is_a($output, 'Object') || is_subclass_of($output, 'Object')) { + $this->setError($output->getError()); + $this->setMessage($output->getMessage()); + + if (!$output->toBool()) return false; + } + // execute api methos of the module if view action is and result is XMLRPC or JSON + if($this->module_info->module_type == 'view'){ + if(Context::getResponseMethod() == 'XMLRPC' || Context::getResponseMethod() == 'JSON') { + $oAPI = getAPI($this->module_info->module, 'api'); + if(method_exists($oAPI, $this->act)) { + $oAPI->{$this->act}($this); + } + } + } + return true; + } + } +?> diff --git a/classes/object/Object.class.php b/classes/object/Object.class.php index 4cf8bab11..36cefe90d 100644 --- a/classes/object/Object.class.php +++ b/classes/object/Object.class.php @@ -1,137 +1,149 @@ -setError($error); - $this->setMessage($message); - } - - /** - * @brief Setter to set error code - * @param[in] $error error code - **/ - function setError($error = 0) { - $this->error = $error; - } - - /** - * @brief Getter to retrieve error code - **/ - function getError() { - return $this->error; - } - - /** - * @brief Setter to set set the error message - * @param[in] $message a messge string - * @return return True - * @remark this method always returns True. We'd better remove it - **/ - function setMessage($message = 'success') { - if(Context::getLang($message)) $message = Context::getLang($message); - $this->message = $message; - return true; - } - - /** - * @brief getter to retrieve an error message - **/ - function getMessage() { - return $this->message; - } - - /** - * @brief setter to set a key/value pair as an additional variable - * @param[in] $key a variable name - * @param[in] $val a value for the variable - **/ - function add($key, $val) { - $this->variables[$key] = $val; - } - - /** - * @brief method to set multiple key/value pairs as an additional variables - * @param[in] $object either object or array containg key/value pairs to be added - **/ - 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 method to retrieve a corresponding value to a given key - **/ - function get($key) { - return $this->variables[$key]; - } - - - /** - * @brief method to retrieve an object containing a key/value paris - * @return Returns an object containing key/value pairs - **/ - 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->get($key); - } - return $output; - } - - /** - * @brief method to retrieve an array of key/value pairs - * @return Return a list of key/value pairs - **/ - function getVariables() { - return $this->variables; - } - - /** - * @brief method to retrieve an object of key/value pairs - * @return Return an object of key/value pairs - **/ - function getObjectVars() { - foreach($this->variables as $key => $val) $output->{$key} = $val; - return $output; - } - - /** - * @brief method to return either true or false depnding on the value in a 'error' variable - * @remark this method is misleading in that it returns true if error is 0, which should be true in - * boolean representation. - **/ - function toBool() { - return $this->error==0?true:false; - } - - - /** - * @brief method to return either true or false depnding on the value in a 'error' variable - **/ - function toBoolean() { - return $this->toBool(); - } - } -?> +setError($error); + $this->setMessage($message); + } + + /** + * @brief Setter to set error code + * @param[in] $error error code + **/ + function setError($error = 0) { + $this->error = $error; + } + + /** + * @brief Getter to retrieve error code + **/ + function getError() { + return $this->error; + } + + function setHttpStatusCode($code = '200') + { + $this->httpStatusCode = $code; + } + + function getHttpStatusCode() + { + return $this->httpStatusCode; + } + + /** + * @brief Setter to set set the error message + * @param[in] $message a messge string + * @return return True + * @remark this method always returns True. We'd better remove it + **/ + function setMessage($message = 'success') { + if(Context::getLang($message)) $message = Context::getLang($message); + $this->message = $message; + return true; + } + + /** + * @brief getter to retrieve an error message + **/ + function getMessage() { + return $this->message; + } + + /** + * @brief setter to set a key/value pair as an additional variable + * @param[in] $key a variable name + * @param[in] $val a value for the variable + **/ + function add($key, $val) { + $this->variables[$key] = $val; + } + + /** + * @brief method to set multiple key/value pairs as an additional variables + * @param[in] $object either object or array containg key/value pairs to be added + **/ + 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 method to retrieve a corresponding value to a given key + **/ + function get($key) { + return $this->variables[$key]; + } + + + /** + * @brief method to retrieve an object containing a key/value paris + * @return Returns an object containing key/value pairs + **/ + 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->get($key); + } + return $output; + } + + /** + * @brief method to retrieve an array of key/value pairs + * @return Return a list of key/value pairs + **/ + function getVariables() { + return $this->variables; + } + + /** + * @brief method to retrieve an object of key/value pairs + * @return Return an object of key/value pairs + **/ + function getObjectVars() { + foreach($this->variables as $key => $val) $output->{$key} = $val; + return $output; + } + + /** + * @brief method to return either true or false depnding on the value in a 'error' variable + * @remark this method is misleading in that it returns true if error is 0, which should be true in + * boolean representation. + **/ + function toBool() { + return $this->error==0?true:false; + } + + + /** + * @brief method to return either true or false depnding on the value in a 'error' variable + **/ + function toBoolean() { + return $this->toBool(); + } +} + +/* End of file Object.class.php */ +/* Location: ./classes/object/Object.class.php */ diff --git a/classes/page/PageHandler.class.php b/classes/page/PageHandler.class.php index 4f65e4bed..5ba96bfc5 100644 --- a/classes/page/PageHandler.class.php +++ b/classes/page/PageHandler.class.php @@ -1,62 +1,62 @@ -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 request next page - * @return next page number - **/ - function getNextPage() { - $page = $this->first_page+$this->point++; - if($this->point > $this->page_count || $page > $this->last_page) $page = 0; - return $page; - } - - function getPage($offset) - { - return max(min($this->cur_page + $offset, $this->total_page), ''); - } - } -?> +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 request next page + * @return next page number + **/ + function getNextPage() { + $page = $this->first_page+$this->point++; + if($this->point > $this->page_count || $page > $this->last_page) $page = 0; + return $page; + } + + function getPage($offset) + { + return max(min($this->cur_page + $offset, $this->total_page), ''); + } + } +?> diff --git a/classes/security/Security.class.php b/classes/security/Security.class.php index ab0387b38..dcbe91546 100644 --- a/classes/security/Security.class.php +++ b/classes/security/Security.class.php @@ -47,19 +47,22 @@ class Security $varName0 = array_shift($varName); if($use_context) { $var = Context::get($varName0); - } else { + } elseif($varName0) { $var = $is_object ? $this->_targetVar->{$varName0} : $this->_targetVar[$varName0]; + } else { + $var = $this->_targetVar; } $var = $this->_encodeHTML($var, $varName); - if($var !== false) { - if($use_context) { - Context::set($varName0, $var); - } elseif($is_object) { - $this->_targetVar->{$varName0} = $var; - } else { - $this->_targetVar[$varName0] = $var; - } + if($var === false) continue; + + if($use_context) { + Context::set($varName0, $var); + } elseif($varName0) { + if($is_object) $this->_targetVar->{$varName0} = $var; + else $this->_targetVar[$varName0] = $var; + } else { + $this->_targetVar = $var; } } diff --git a/classes/template/TemplateHandler.class.php b/classes/template/TemplateHandler.class.php index a56d0f2db..6ee48ba09 100644 --- a/classes/template/TemplateHandler.class.php +++ b/classes/template/TemplateHandler.class.php @@ -1,914 +1,567 @@ -path = preg_replace('/^\.\//','',$info['dirname']).'/'; - $this->path = $tpl_path; - $this->filename = $tpl_filename; - $this->file = $tpl_file; - - $this->xe_path = preg_replace('/([^\.^\/]+)\.php$/i','',$_SERVER['SCRIPT_NAME']); - $this->web_path = $this->xe_path.str_replace(_XE_PATH_,'',$this->path); - - // get compiled file name - $this->compiled_file = sprintf('%s%s.compiled.php',$this->compiled_path, md5($this->file)); - - // compare various file's modified time for check changed - $this->handler_mtime = filemtime(_XE_PATH_.'classes/template/TemplateHandler.class.php'); - - $this->buff = null; - } - - /** - * @brief compiles specified tpl file and execution result in Context into resultant content - * @param[in] $tpl_path path of the directory containing target template file - * @param[in] $tpl_filename target template file's name - * @param[in] $tpl_file if specified use it as template file's full path - * @return Returns compiled result in case of success, NULL otherwise - */ - function compile($tpl_path, $tpl_filename, $tpl_file = '') { - // store the starting time for debug information - if(__DEBUG__==3 ) $start = getMicroTime(); - - // initiation - $this->init($tpl_path, $tpl_filename, $tpl_file); - - // if target file does not exist exit - if(!$this->file || !file_exists($this->file)) return sprintf('Err : "%s" template file does not exists.', $this->file); - - $source_template_mtime = filemtime($this->file); - $latest_mtime = $source_template_mtime>$this->handler_mtime?$source_template_mtime:$this->handler_mtime; - - // cache controll - $oCacheHandler = &CacheHandler::getInstance('template'); - - // get cached buff - if($oCacheHandler->isSupport()){ - $cache_key = 'template:'.$this->file; - $this->buff = $oCacheHandler->get($cache_key, $latest_mtime); - } else { - if(file_exists($this->compiled_file) && filemtime($this->compiled_file)>$latest_mtime) { - $this->buff = FileHandler::readFile($this->compiled_file); - } - } - - if(!$this->buff) { - $this->parse(); - if($oCacheHandler->isSupport()) $oCacheHandler->put($cache_key, $this->buff); - else FileHandler::writeFile($this->compiled_file, $this->buff); - } - - $output = $this->_fetch(); - - // store the ending time for debug information - if(__DEBUG__==3 ) $GLOBALS['__template_elapsed__'] += getMicroTime() - $start; - - return $output; - } - - /** - * @brief compile specified file and immediately return - * @param[in] $tpl_path path of the directory containing target template file - * @param[in] $tpl_filename target template file's name - * @return Returns compiled content in case of success or NULL in case of failure - **/ - function compileDirect($tpl_path, $tpl_filename) { - $this->init($tpl_path, $tpl_filename, null); - - // if target file does not exist exit - if(!$this->file || !file_exists($this->file)) { - Context::close(); - printf('"%s" template file is not exists.', $this->file); - exit(); - } - - $this->parse(); - return $this->buff; - } - - /** - * @brief compile a template file specified in $tpl_file and - * @pre files specified by $tpl_file exists. - * @param[in] $tpl_file path of tpl file - * @param[in] $compiled_tpl_file if specified, write compiled result into the file - * @return compiled result in case of success or NULL in case of error - **/ - function parse() { - if(!file_exists($this->file)) return; - - // read tpl file - $buff = FileHandler::readFile($this->file); - - // replace value of src in img/input/script tag - $buff = preg_replace_callback('/<(img|input|script)([^>]*)src="([^"]*?)"/is', array($this, '_replacePath'), $buff); - - // loop 템플릿 문법을 변환 - $buff = $this->_replaceLoop($buff); - - // cond 템플릿 문법을 변환 - $buff = $this->_replaceCond($buff); - - // |cond 템플릿 문법을 변환 - $buff = preg_replace_callback("/<\/?(\w+)((\s+\w+(\s*=\s*(?:\".*?\"|'.*?'|[^'\">\s]+))?)+\s*|\s*)\/?>/i", array($this, '_replacePipeCond'), $buff); - - // include 태그의 변환 - $buff = preg_replace_callback('!]+)>!is', array($this, '_replaceInclude'), $buff); - - // unload/ load 태그의 변환 - $buff = preg_replace_callback('!<(unload|load) ([^>]+)>!is', array($this, '_replaceLoad'), $buff); - - // 가상 태그인 block의 변환 - $buff = preg_replace('/|<\/block>/is','',$buff); - - // replace include - $buff = preg_replace_callback('!<\!--#include\(([^\)]*?)\)-->!is', array($this, '_compileIncludeToCode'), $buff); - - // replace - $buff = preg_replace_callback('!<\!--@(.*?)-->!is', array($this, '_compileFuncToCode'), $buff); - - // remove comments - $buff = preg_replace('!(\n?)( *?)<\!--//(.*?)-->!is', '', $buff); - - // import xml filter/ css/ js/ files (media is applied to only css) - $buff = preg_replace_callback('!<\!--%import\(\"([^\"]*?)\"(,optimized\=(true|false))?(,media\=\"([^\"]*)\")?(,targetie=\"([^\"]*)\")?(,index=\"([^\"]*)\")?(,type=\"([^\"]*)\")?\)-->!is', array($this, '_compileImportCode'), $buff); - - // unload css/ js (media is applied to only css) - $buff = preg_replace_callback('!<\!--%unload\(\"([^\"]*?)\"(,optimized\=(true|false))?(,media\=\"([^\"]*)\")?(,targetie=\"([^\"]*)\")?\)-->!is', array($this, '_compileUnloadCode'), $buff); - - // javascript plugin import - $buff = preg_replace_callback('!<\!--%load_js_plugin\(\"([^\"]*?)\"\)-->!is', array($this, '_compileLoadJavascriptPlugin'), $buff); - - // replace variables - $buff = preg_replace_callback('/\{[^@^ ]([^\{\}\n]+)\}/i', array($this, '_compileVarToContext'), $buff); - - // PHP 변수형의 변환 ($문자등을 공유 context로 변환) - $buff = $this->_replaceVarInPHP($buff); - - // replace parts not displaying results - $buff = preg_replace_callback('/\{\@([^\{\}]+)\}/i', array($this, '_compileVarToSilenceExecute'), $buff); - - // prevent from calling directly before writing into file - $this->buff = ''.$buff; - } - - /** - * @brief fetch using ob_* function - * @param[in] $compiled_tpl_file path of compiled template file - * @param[in] $buff if buff is not null, eval it instead of including compiled template file - * @param[in] $tpl_path set context's tpl path - * @return result string - **/ - function _fetch() { - if(!$this->buff) return; - - $__Context = &$GLOBALS['__Context__']; - $__Context->tpl_path = $this->path; - - if($_SESSION['is_logged']) $__Context->logged_info = $_SESSION['logged_info']; - - ob_start(); - $eval_str = "?>".$this->buff; - eval($eval_str); - return ob_get_clean(); - } - - /** - * @brief change image path - * @pre $matches is an array containg three elements - * @param[in] $matches match - * @return changed result - **/ - function _replacePath($matches) - { - preg_match_all('/src="([^"]*?)"/is', $matches[0], $m); - for($i=0,$c=count($m[0]);$i<$c;$i++) { - $path = trim($m[1][$i]); - if(substr($path,0,1)=='/' || substr($path,0,1)=='{' || strpos($path,'://')!==false) continue; - if(substr($path,0,2)=='./') $path = substr($path,2); - $target = $this->web_path.$path; - while(strpos($target,'/../')!==false) - { - $target = preg_replace('/\/([^\/]+)\/\.\.\//','/',$target); - } - $target = str_replace('/./','/',$target); - $matches[0] = str_replace($m[0][$i], 'src="'.$target.'"', $matches[0]); - } - return $matches[0]; - } - - /** - * @brief loop 문법의 변환 - **/ - function _replaceLoop($buff) - { - while(false !== $pos = strpos($buff, ' loop="')) - { - $pre = substr($buff,0,$pos); - $next = substr($buff,$pos); - - $pre_pos = strrpos($pre, '<'); - - preg_match('/^ loop="([^"]+)"/i',$next,$m); - $orgTag = $tag = substr($next,0,strlen($m[0])); - $next = substr($next,strlen($m[0])); - $next_pos = strpos($next, '<'); - - $tag = substr($pre, $pre_pos). $tag. substr($next, 0, $next_pos); - // search end tag - /* tag as '
blahblah' to be '
' */ - preg_match('/\/>(\w+)/',$tag, $mm); - if ($mm[1]){ - $next_pos = strpos($next, $mm[1]); - $tag = substr($pre, $pre_pos). $orgTag. substr($next, 0, $next_pos); - } - $pre = substr($pre, 0, $pre_pos); - $next = substr($next, $next_pos); - - $tag_name = trim(substr($tag,1,strpos($tag,' '))); - $tag_head = $tag_tail = ''; - - if(!preg_match('/ loop="([^"]+)"/is',$tag)) { - print "Invalid XpressEngine Template Syntax
"; - print "File : ".$this->file."
"; - print "Code : ".htmlspecialchars($tag); - exit(); - } - - preg_match_all('/ loop="([^"]+)"/is',$tag,$m); - $tag = preg_replace('/ loop="([^"]+)"/is','', $tag); - - for($i=0,$c=count($m[0]);$i<$c;$i++) - { - $loop = $m[1][$i]; - if(false!== $fpos = strpos($loop,'=>')) - { - $target = trim(substr($loop,0,$fpos)); - if(substr($target, 0, 1) == '$') $target = sprintf('$__Context->%s ', substr($target, 1)); - - $vars = trim(substr($loop,$fpos+2)); - if(false===strpos($vars,',')) - { - if(substr($vars, 0, 1) == '$') $vars = sprintf('$__Context->%s ', substr($vars, 1)); - - $tag_head .= ''; - $tag_tail .= ''; - } - else - { - $t = explode(',',$vars); - foreach($t as $key => $val){ - if(substr(trim($val), 0, 1) == '$') $val = sprintf('$__Context->%s ', substr(trim($val), 1)); - $t[$key] = trim($val); - } - $tag_head .= ' '.trim($t[1]).') { ?>'; - $tag_tail .= ''; - } - } - elseif(false!==strpos($loop,';')) - { - $loop = preg_replace('/\$(\w+)/', '$__Context->$1', $loop); - $tag_head .= ''; - $tag_tail .= ''; - } - else - { - $t = explode('=',$loop); - if(count($t)==2) - { - $tag_head .= ''; - $tag_tail .= ''; - } - } - } - - if(substr(trim($tag),-2)!='/>') - { - while(false !== $close_pos = strpos($next, '')); - $tag .= $tmp_buff; - $next = substr($next, strlen($tmp_buff)); - if(substr_count($tag, '<'.$tag_name) == substr_count($tag,'$1', $m[3]); - $matches[0] = str_replace($m[0], sprintf(' %s="%s"', $m[3], $m[1], $m[2]), $matches[0]); - } - } - } - - return $matches[0]; - } - - /** - * @brief cond 문법의 변환 - **/ - function _replaceCond($buff) - { - while(false !== ($pos = strpos($buff, ' cond="'))) - { - $pre = substr($buff,0,$pos); - $next = substr($buff,$pos); - - $pre_pos = strrpos($pre, '<'); - - preg_match('/<(\/|[a-z])/i',$next,$m); - if(!$m[0]) return $buff; - $next_pos = strpos($next, $m[0]); - - $tag = substr($pre, $pre_pos). substr($next, 0, $next_pos); - - // search end tag - /* tag as '
blahblah' to be '
' */ - preg_match('/\/>(\w+)/',$tag, $mm); - if ($mm[1]){ - $next_pos = strpos($next, $mm[1]); - $tag = substr($pre, $pre_pos). substr($next, 0, $next_pos); - } - $pre = substr($pre, 0, $pre_pos); - $next = substr($next, $next_pos); - $tag_name = trim(substr($tag,1,strpos($tag,' '))); - $tag_head = $tag_tail = ''; - - if(preg_match_all('/ cond=\"([^\"]+)"/is',$tag,$m)) - { - for($i=0,$c=count($m[0]);$i<$c;$i++) - { - $m[1][$i] = preg_replace('/^\$(\w+)/', '$__Context->$1', $m[1][$i]); - $tag_head .= ''; - $tag_tail .= ''; - } - } - - if(!preg_match('/ cond="([^"]+)"/is',$tag)) { - print "Invalid XpressEngine Template Syntax
"; - print "File : ".$this->file."
"; - print "Code : ".htmlspecialchars($tag); - exit(); - } - - $tag = preg_replace('/ cond="([^"]+)"/is','', $tag); - if(substr(trim($tag),-2)=='/>') - { - $buff = $pre.$tag_head.$tag.$tag_tail.$next; - } - else - { - while(false !== $close_pos = strpos($next, '')); - $tag .= $tmp_buff; - $next = substr($next, strlen($tmp_buff)); - - if(substr_count($tag, '<'.$tag_name) == substr_count($tag,'path.substr($target,0,$pos); - } - - return sprintf( - 'compile(\'%s\',\'%s\');%s'. - '?>%s', - "\n", - "\n", - $path, $filename, "\n", - "\n" - ); - } - - /** - * @brief load 태그의 변환 - **/ - function _replaceLoad($matches) { - $output = $matches[0]; - if(!preg_match_all('/ ([^=]+)=\"([^\"]+)\"/is',$matches[0], $m)) return $matches[0]; - - $type = $matches[1]; - for($i=0,$c=count($m[1]);$i<$c;$i++) - { - if(!trim($m[1][$i])) continue; - $attrs[trim($m[1][$i])] = trim($m[2][$i]); - } - - if(!$attrs['target']) return $matches[0]; - - $web_path = $this->web_path; - $base_path = $this->path; - - $target = $attrs['target']; - if(!preg_match('/^(http|https)/i',$target)) - { - if(substr($target,0,2)=='./') $target = substr($target,2); - if(substr($target,0,1)!='/') $target = $web_path.$target; - } - - if(!$attrs['index']) $attrs['index'] = 'null'; - if($attrs['type']!='body') $attrs['type'] = 'head'; - - // if target ends with lang, load language pack - if(substr($target, -4)=='lang') { - if(substr($target,0,2)=='./') $target = substr($target, 2); - $lang_dir = $base_path.$target; - if(is_dir($lang_dir)) $output = sprintf('', $lang_dir); - - // otherwise try to load xml, css, js file - } else { - if(substr($target,0,1)!='/' && !preg_match('/^(http|https)/i',$target)) $source_filename = $base_path.$target; - else $source_filename = $target; - - if(!preg_match('/^(http|https)/i',$source_filename)) - $source_filename = str_replace(array('/./','//'),'/',$source_filename); - - // get filename and path - $tmp_arr = explode("/",$source_filename); - $filename = array_pop($tmp_arr); - - //$base_path = implode("/",$tmp_arr)."/"; - - // get the ext - $tmp_arr = explode(".",$filename); - $ext = strtolower(array_pop($tmp_arr)); - - // according to ext., import the file - switch($ext) { - // xml js filter - case 'xml' : - if(preg_match('/^(http|https)/i',$source_filename)) return; - // create an instance of XmlJSFilter class, then create js and handle Context::addJsFile - $output = sprintf( - 'compile();%s'. - '?>%s', - "\n", - "\n", - dirname($base_path . $attrs['target']).'/', - $filename, - "\n", - "\n", - "\n" - ); - break; - // css file - case 'css' : - if($type == 'unload') { - $output = ''; - } else { - $meta_file = $source_filename; - $output = ''; - } - break; - // js file - case 'js' : - if($type == 'unload') { - $output = ''; - } else { - $meta_file = $source_filename; - $output = ''; - } - break; - } - } - - if($meta_file) $output = ''.$output; - return $output; - } - - /** - * @brief $문자 의 PHP 변수 변환 - **/ - function _replaceVarInPHP($buff) { - $head = $tail = ''; - while(false !== $pos = strpos($buff, ''); - $body = substr($buff,0,$pos+2); - $head .= preg_replace_callback('/(.?)\$(\w+[a-z0-9\_\-\[\]\'\"]+)/is',array($this, '_replaceVarString'), $body); - - $buff = substr($buff,$pos+2); - } - return $head.$buff; - } - - - /** - * @brief php5의 class::$변수명의 경우 context를 사용하지 않아야 하기에 함수로 대체 - **/ - function _replaceVarString($matches) - { - if($matches[1]==':') return $matches[0]; - if(substr($matches[2],0,1)=='_') return $matches[0]; - return $matches[1].'$__Context->'.$matches[2]; - } - - /** - * @brief replace with php code - * @param[in] $matches match - * @return replaced result - **/ - function _compileIncludeToCode($matches) { - // if target string to include contains variables handle them - $arg = str_replace(array('"','\''), '', $matches[1]); - if(!$arg) return; - - $tmp_arr = explode("/", $arg); - for($i=0;$ifile), $arg); - $path = substr($this->path,-1)=='/'?substr($this->path,0,-1):$this->path; - $source_filename = sprintf("%s/%s", $path, $arg); - - // step2: check path from root - if(!file_exists($source_filename)) $source_filename = './'.$arg; - if(!file_exists($source_filename)) return; - - // split into path and filename - $tmp_arr = explode('/', $source_filename); - $filename = array_pop($tmp_arr); - $path = implode('/', $tmp_arr).'/'; - - // try to include - $output = sprintf( - 'compile(\'%s\',\'%s\');%s'. - '?>%s', - "\n", - "\n", - $path, $filename, "\n", - "\n" - ); - - return $output; - } - - /** - * @brief replace $... variables in { } with Context::get(...) - * @param[in] $matches match - * @return replaced result in case of success or NULL in case of error - **/ - function _compileVarToContext($matches) { - $str = trim(substr($matches[0],1,strlen($matches[0])-2)); - if(!$str) return $matches[0]; - if(!in_array(substr($str,0,1),array('(','$','\'','"'))) { - if(preg_match('/^([^\( \.]+)(\(| \.)/i',$str,$m)) { - $func = trim($m[1]); - if(strpos($func,'::')===false) { - if(!function_exists($func)) { - return $matches[0]; - } - } else { - list($class, $method) = explode('::',$func); - // FIXME regardless of whether class/func name is case-sensitive, it is safe - // to assume names are case sensitive. We don't have compare twice. - if(!class_exists($class) || !in_array($method, get_class_methods($class))) { - // In some environment, the name of classes and methods may be case-sensitive - list($class, $method) = explode('::',strtolower($func)); - if(!class_exists($class) || !in_array($method, get_class_methods($class))) { - return $matches[0]; - } - } - } - } else { - if(!defined($str)) return $matches[0]; - } - } - return ']+)/i','$__Context->\\1', $str).');?>'; - } - - /** - * @brief replace @... function in { } into print func(..) - * @param[in] $matches match - * @return replaced result - **/ - function _compileVarToSilenceExecute($matches) { - if(strtolower(trim(str_replace(array(';',' '),'', $matches[1])))=='return') return ''; - return ']+)/i','$__Context->\\1', trim($matches[1])).';?>'; - } - - /** - * @brief replace code in with php code - * @param[in] $matches match - * @return changed result - **/ - function _compileFuncToCode($matches) { - static $idx = 0; - $code = trim($matches[1]); - if(!$code) return; - - switch(strtolower($code)) { - case 'else' : - $output = '}else{'; - break; - case 'end' : - case 'endif' : - case 'endfor' : - case 'endforeach' : - case 'endswitch' : - $output = '}'; - break; - case 'break' : - $output = 'break;'; - break; - case 'default' : - $output = 'default :'; - break; - case 'break@default' : - $output = 'break; default :'; - break; - default : - $suffix = '{'; - - 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]; - $prefix = '$Context->__idx['.$idx.']=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); - } - $idx++; - $suffix .= '$__idx['.$idx.']=($__idx['.$idx.']+1)%2; $cycle_idx = $__idx['.$idx.']+1;'; - } elseif(substr($code, 0, 4) == 'case') { - $suffix = ':'; - } elseif(substr($code, 0, 10) == 'break@case') { - $code = 'break; case'.substr($code, 10); - $suffix = ':'; - } - $output = preg_replace('/\$([a-zA-Z0-9\_\-]+)/i', '$__Context->\\1', $code.$suffix); - break; - } - - return sprintf('', $prefix, $output); - } - - - /** - * @brief replace xe specific code, "" with appropriate php code - * @param[in] $matches match - * @return Returns modified result or NULL in case of error - **/ - function _compileImportCode($matches) { - // find xml file - $base_path = $this->path; - $given_file = trim($matches[1]); - if(!$given_file) return; - if(isset($matches[3])) $optimized = strtolower(trim($matches[3])); - if(!$optimized) $optimized = 'true'; - if(isset($matches[5])) $media = trim($matches[5]); - if(!$media) $media = 'all'; - if(isset($matches[7])) $targetie = trim($matches[7]); - if(!$targetie) $targetie = ''; - else $optimized = 'false'; - - if(isset($matches[9])) $index = intval($matches[9]); - if(!$index) $index = 'null'; - if(isset($matches[11])) $type = strtolower(trim($matches[11])); - if($type!='body') $type = 'head'; - - // if given_file ends with lang, load language pack - if(substr($given_file, -4)=='lang') { - if(substr($given_file,0,2)=='./') $given_file = substr($given_file, 2); - $lang_dir = $base_path.$given_file; - if(is_dir($lang_dir)) $output = sprintf('', $lang_dir); - - // otherwise try to load xml, css, js file - } else { - if(preg_match('/^(http|https):/i',$given_file)) $source_filename = $given_file; - elseif(substr($given_file,0,1)!='/') $source_filename = sprintf("%s%s",$base_path, $given_file); - else $source_filename = $given_file; - - // get filename and path - $tmp_arr = explode("/",$source_filename); - $filename = array_pop($tmp_arr); - - $base_path = implode("/",$tmp_arr)."/"; - - // get the ext - $tmp_arr = explode(".",$filename); - $ext = strtolower(array_pop($tmp_arr)); - - // according to ext., import the file - switch($ext) { - // xml js filter - case 'xml' : - // create an instance of XmlJSFilter class, then create js and handle Context::addJsFile - $output = sprintf( - 'compile();%s'. - '?>%s', - "\n", - "\n", - $base_path, - $filename, - "\n", - "\n", - "\n" - ); - break; - // css file - case 'css' : - if(preg_match('/^(http|\/)/i',$source_filename)) { - $output = sprintf('', $source_filename, 'false', $media, $targetie, $index); - } else { - $meta_file = $base_path.$filename; - $output = sprintf('', $base_path, $filename, $optimized, $media, $targetie, $index); - } - break; - // js file - case 'js' : - if(preg_match('/^(http|\/)/i',$source_filename)) { - $output = sprintf('', $source_filename, 'false', $targetie, $index, $type); - } else { - $meta_file = $base_path.$filename; - $output = sprintf('', $base_path, $filename, $optimized, $targetie, $index, $type); - } - break; - } - } - - if($meta_file) $output = ''.$output; - return $output; - } - - /** - * @brief import javascript plugin - * @param[in] $matches match - * @return result loading the plugin - * @remarks javascript plugin works as optimized = false - **/ - function _compileLoadJavascriptPlugin($matches) { - $base_path = $this->path; - $plugin = trim($matches[1]); - return sprintf('', $plugin); - } - - /** - * @brief remove loading part of css/ js file - * @param[in] $matches match - * @return removed result - **/ - function _compileUnloadCode($matches) { - // find xml file - $base_path = $this->path; - $given_file = trim($matches[1]); - if(!$given_file) return; - if(isset($matches[3])) $optimized = strtolower(trim($matches[3])); - if(!$optimized) $optimized = 'true'; - if(isset($matches[5])) $media = trim($matches[5]); - if(!$media) $media = 'all'; - if(isset($matches[7])) $targetie = trim($matches[7]); - if(!$targetie) $targetie = ''; - else $optimized = 'false'; - - if(substr($given_file,0,1)!='/') $source_filename = sprintf("%s%s",$base_path, $given_file); - else $source_filename = $given_file; - - // get path and file nam - $tmp_arr = explode("/",$source_filename); - $filename = array_pop($tmp_arr); - - $base_path = implode("/",$tmp_arr)."/"; - - // get an ext. - $tmp_arr = explode(".",$filename); - $ext = strtolower(array_pop($tmp_arr)); - - switch($ext) { - // css file - case 'css' : - if(preg_match('/^(http|https|\/)/i',$source_filename)) { - $output = sprintf('', $source_filename, 'false', $media, $targetie); - } else { - $output = sprintf('', $base_path, $filename, $optimized, $media, $targetie); - } - break; - // js file - case 'js' : - if(preg_match('/^(http|https|\/)/i',$source_filename)) { - $output = sprintf('', $source_filename, 'false', $targetie); - } else { - $output = sprintf('', $base_path, $filename, $optimized, $targetie); - } - break; - } - - return $output; - } - } -?> +xe_path = rtrim(preg_replace('/([^\.^\/]+)\.php$/i','',$_SERVER['SCRIPT_NAME']),'/'); + } + + /** + * @brief returns TemplateHandler's singleton object + * @return TemplateHandler instance + **/ + function &getInstance() + { + static $oTemplate = null; + + if(__DEBUG__==3 ) { + if(!isset($GLOBALS['__TemplateHandlerCalled__'])) $GLOBALS['__TemplateHandlerCalled__']=1; + else $GLOBALS['__TemplateHandlerCalled__']++; + } + + if(!$oTemplate) $oTemplate = new TemplateHandler(); + + return $oTemplate; + } + + /** + * @brief set variables for template compile + **/ + function init($tpl_path, $tpl_filename, $tpl_file='') + { + // verify arguments + if(substr($tpl_path,-1)!='/') $tpl_path .= '/'; + if(!file_exists($tpl_path.$tpl_filename)&&file_exists($tpl_path.$tpl_filename.'.html')) $tpl_filename .= '.html'; + + // create tpl_file variable + if(!$tpl_file) $tpl_file = $tpl_path.$tpl_filename; + + // set template file infos. + $this->path = $tpl_path; + $this->filename = $tpl_filename; + $this->file = $tpl_file; + + $this->web_path = $this->xe_path.'/'.ltrim(preg_replace('@^'.preg_quote(_XE_PATH_,'@').'|\./@','',$this->path),'/'); + + // get compiled file name + $hash = md5($this->file . __ZBXE_VERSION__); + $this->compiled_file = "{$this->compiled_path}{$hash}.compiled.php"; + + // compare various file's modified time for check changed + $this->handler_mtime = filemtime(__FILE__); + + $skip = array(''); + } + + /** + * @brief compiles specified tpl file and execution result in Context into resultant content + * @param[in] $tpl_path path of the directory containing target template file + * @param[in] $tpl_filename target template file's name + * @param[in] $tpl_file if specified use it as template file's full path + * @return Returns compiled result in case of success, NULL otherwise + */ + function compile($tpl_path, $tpl_filename, $tpl_file='') { + global $__templatehandler_root_tpl; + + $buff = ''; + + // store the starting time for debug information + if(__DEBUG__==3 ) $start = getMicroTime(); + + // initiation + $this->init($tpl_path, $tpl_filename, $tpl_file); + + // if target file does not exist exit + if(!$this->file || !file_exists($this->file)) return "Err : '{$this->file}' template file does not exists."; + + // for backward compatibility + if(is_null($__templatehandler_root_tpl)) { + $__templatehandler_root_tpl = $this->file; + } + + $source_template_mtime = filemtime($this->file); + $latest_mtime = $source_template_mtime>$this->handler_mtime?$source_template_mtime:$this->handler_mtime; + + // cache control + $oCacheHandler = &CacheHandler::getInstance('template'); + + // get cached buff + if($oCacheHandler->isSupport()){ + $cache_key = 'template:'.$this->file; + $buff = $oCacheHandler->get($cache_key, $latest_mtime); + } else { + if(is_readable($this->compiled_file) && filemtime($this->compiled_file)>$latest_mtime && filesize($this->compiled_file)) { + $buff = 'file://'.$this->compiled_file; + } + } + + if(!$buff) { + $buff = $this->parse(); + if($oCacheHandler->isSupport()) $oCacheHandler->put($cache_key, $buff); + else FileHandler::writeFile($this->compiled_file, $buff); + } + + $output = $this->_fetch($buff); + + if($__templatehandler_root_tpl == $this->file) { + $__templatehandler_root_tpl = null; + } + + // store the ending time for debug information + if(__DEBUG__==3 ) $GLOBALS['__template_elapsed__'] += getMicroTime() - $start; + + return $output; + } + + /** + * @brief compile specified file and immediately return + * @param[in] $tpl_path path of the directory containing target template file + * @param[in] $tpl_filename target template file's name + * @return Returns compiled content in case of success or NULL in case of failure + **/ + function compileDirect($tpl_path, $tpl_filename) { + $this->init($tpl_path, $tpl_filename, null); + + // if target file does not exist exit + if(!$this->file || !file_exists($this->file)) { + Context::close(); + exit("Cannot find the template file: '{$this->file}'"); + } + + return $this->parse(); + } + + /** + * @brief compile a template file specified in $tpl_file and + * @pre files specified by $tpl_file exists. + * @param[in] $tpl_file path of tpl file + * @param[in] $compiled_tpl_file if specified, write compiled result into the file + * @return compiled result in case of success or NULL in case of error + **/ + function parse($buff=null) { + if(is_null($buff)) { + if(!is_readable($this->file)) return; + + // read tpl file + $buff = FileHandler::readFile($this->file); + } + + // HTML tags to skip + if(is_null($this->skipTags)) { + $this->skipTags = array('marquee'); + } + + // replace comments + $buff = preg_replace('@@s', '', $buff); + + // replace value of src in img/input/script tag + $buff = preg_replace_callback('/<(?:img|input|script)(?:(?!["\'\/]\s*>).)* src="(?!https?:\/\/|[\/\{])([^"]+)"/is', array($this, '_replacePath'), $buff); + + // replace loop and cond template syntax + $buff = $this->_parseInline($buff); + + // include, unload/load, import + $buff = preg_replace_callback('/{(@[\s\S]+?|(?=\$\w+|_{1,2}[A-Z]+|[!\(+-]|\w+(?:\(|::)|\d+|[\'"].*?[\'"]).+?)}|<(!--[#%])?(include|import|(un)?load(?(4)|(?:_js_plugin)?))(?(2)\("([^"]+)")(.*?)(?(2)\)--|\/)>|(\s*)/', array($this, '_parseResource'), $buff); + + // remove block which is a virtual tag and remove comments + $buff = preg_replace('@|\s?@is','',$buff); + + // form auto generation + $buff = preg_replace_callback('/(|[^<>]+)*?>)(.*?)(<\/form>)/is', array($this, '_compileFormAuthGeneration'), $buff); + + // prevent from calling directly before writing into file + $buff = ''.$buff; + + return $buff; + } + + /** + * @brief 1. remove ruleset from form tag + * 2. add hidden tag with ruleset value + * 3. if empty default hidden tag, generate hidden tag (ex:mid, vid, act...) + * 4. generate return url, return url use in server side validator + **/ + function _compileFormAuthGeneration($matches) + { + // form ruleset attribute move to hidden tag + if($matches[1]) + { + preg_match('/ruleset="([^"]*?)"/is', $matches[1], $m); + if($m[0]) + { + $matches[1] = preg_replace('/'.$m[0].'/i', '', $matches[1]); + $matches[2] = ''.$matches[2]; + + if (strpos($m[1],'@') !== false){ + $path = str_replace('@', '', $m[1]); + $path = './files/ruleset/'.$path.'.xml'; + }else if(preg_match('@(?:^|\.?/)(modules/[\w-]+)@', $this->path, $mm)) { + $module_path = $mm[1]; + $path = $module_path.'/ruleset/'.$m[1].'.xml'; + } + //assign to addJsFile method for js dynamic recache + $matches[1] = ''.$matches[1]; + } + } + + // if not exists default hidden tag, generate hidden tag + preg_match_all('/]* name="(act|mid|vid)"/is', $matches[2], $m2); + $checkVar = array('act', 'mid', 'vid'); + $resultArray = array_diff($checkVar, $m2[1]); + if(is_array($resultArray)) + { + $generatedHidden = ''; + foreach($resultArray AS $key=>$value) + { + $generatedHidden .= ''; + } + $matches[2] = $generatedHidden.$matches[2]; + } + + // return url generate + if (!preg_match('/no-error-return-url="true"/i', $matches[1])) + { + preg_match('/]*name="error_return_url"[^>]*>/is', $matches[2], $m3); + if(!$m3[0]) $matches[2] = ''.$matches[2]; + } + + $matches[0] = ''; + return implode($matches); + } + + /** + * @brief fetch using ob_* function + * @param[in] $compiled_tpl_file path of compiled template file + * @param[in] $buff if buff is not null, eval it instead of including compiled template file + * @param[in] $tpl_path set context's tpl path + * @return result string + **/ + function _fetch($buff) { + if(!$buff) return; + + $__Context = &$GLOBALS['__Context__']; + $__Context->tpl_path = $this->path; + + if($_SESSION['is_logged']) { + $__Context->logged_info = Context::get('logged_info'); + } + + ob_start(); + if(substr($buff, 0, 7) == 'file://') { + include(substr($buff, 7)); + } else { + $eval_str = "?>".$buff; + eval($eval_str); + } + + return ob_get_clean(); + } + + /** + * @brief change image path + * @pre $matches is an array containg three elements + * @param[in] $matches match + * @return changed result + **/ + function _replacePath($match) + { + $src = preg_replace('@^(\./)+@', '', trim($match[1])); + + $src = $this->web_path.$src; + $src = str_replace('/./', '/', $src); + + // for backward compatibility + $src = preg_replace('@((?:[\w-]+/)+)\1@', '\1', $src); + + while(($tmp=preg_replace('@[^/]+/\.\./@', '', $src))!==$src) $src = $tmp; + + return substr($match[0],0,-strlen($match[1])-6)."src=\"{$src}\""; + } + + function _parseInline($buff) + { + if(preg_match_all('/<([a-zA-Z]+\d?)(?>(?!<[a-z]+\d?[\s>]).)*?(?:[ \|]cond| loop)="/s', $buff, $match) === false) return $buff; + + $tags = array_diff(array_unique($match[1]), $this->skipTags); + + if(!count($tags)) return $buff; + + $tags = '(?:'.implode('|',$tags).')'; + $split_regex = "@(<(?>/?{$tags})(?>[^<>\{\}\"']+||{[^}]+}|\".*?\"|'.*?'|.)*?>)@s"; + + $nodes = preg_split($split_regex, $buff, -1, PREG_SPLIT_DELIM_CAPTURE); + + // list of self closing tags + $self_closing = explode(',', 'area,base,basefont,br,hr,input,img,link,meta,param,frame,col'); + + for($idx=1,$node_len=count($nodes); $idx < $node_len; $idx+=2) { + if(!($node=$nodes[$idx])) continue; + + if(preg_match_all('@\s(loop|cond)="([^"]+)"@', $node, $matches)) { + // this tag + $tag = substr($node, 1, strpos($node, ' ')-1); + + // if the vale of $closing is 0, it means 'skipping' + $closing = 0; + + // process opening tag + foreach($matches[1] as $n=>$stmt) { + $expr = $matches[2][$n]; + $expr = $this->_replaceVar($expr); + $closing++; + + switch($stmt) { + case 'cond': + $nodes[$idx-1] .= ""; + break; + case 'loop': + if(!preg_match('@^(?:(.+?)=>(.+?)(?:,(.+?))?|(.*?;.*?;.*?)|(.+?)\s*=\s*(.+?))$@', $expr, $expr_m)) break; + if($expr_m[1]) { + $expr_m[1] = trim($expr_m[1]); + $expr_m[2] = trim($expr_m[2]); + if($expr_m[3]) $expr_m[2] .= '=>'.trim($expr_m[3]); + $nodes[$idx-1] .= ""; + }elseif($expr_m[4]) { + $nodes[$idx-1] .= ""; + }elseif($expr_m[5]) { + $nodes[$idx-1] .= ""; + } + break; + } + } + $node = preg_replace('@\s(loop|cond)="([^"]+)"@', '', $node); + + // find closing tag + $close_php = ''; + if($node{1} == '!' || substr($node,-2,1) == '/' || in_array($tag, $self_closing)) { // self closing tag + $nodes[$idx+1] = $close_php.$nodes[$idx+1]; + } else { + $depth = 1; + for($i=$idx+2; $i < $node_len; $i+=2) { + $nd = $nodes[$i]; + if(strpos($nd, $tag) === 1) { + $depth++; + } elseif(strpos($nd, '/'.$tag) === 1) { + $depth--; + if(!$depth) { + $nodes[$i-1] .= $nodes[$i].$close_php; + $nodes[$i] = ''; + break; + } + } + } + } + } + + if(strpos($node, '|cond="') !== false) { + $node = preg_replace('@(\s[\w:]+="[^"]+?")\|cond="(.+?)"@s', '$1', $node); + $node = $this->_replaceVar($node); + } + + if($nodes[$idx] != $node) $nodes[$idx] = $node; + } + + $buff = implode('', $nodes); + + return $buff; + } + + function _parseResource($m) + { + // {@ ... } or {$var} or {func(...)} + if($m[1]) + { + if(preg_match('@^(\w+)\(@', $m[1], $mm) && !function_exists($mm[1])) return $m[0]; + + $echo = 'echo '; + if($m[1]{0} == '@') { + $echo = ''; + $m[1] = substr($m[1], 1); + } + return '_replaceVar($m[1]).' ?>'; + } + + if($m[3]) + { + $attr = array(); + if($m[5]) { + if(preg_match_all('@,(\w+)="([^"]+)"@', $m[6], $mm)) { + foreach($mm[1] as $idx=>$name) { + $attr[$name] = $mm[2][$idx]; + } + } + $attr['target'] = $m[5]; + } else { + if(!preg_match_all('@ (\w+)="([^"]+)"@', $m[6], $mm)) return $m[0]; + foreach($mm[1] as $idx=>$name) { + $attr[$name] = $mm[2][$idx]; + } + } + + switch($m[3]) + { + // or + case 'include': + if(!$this->file || !$attr['target']) return ''; + + $pathinfo = pathinfo($attr['target']); + $fileDir = $this->_getRelativeDir($pathinfo['dirname']); + + if(!$fileDir) return ''; + + return "compile('{$fileDir}','{$pathinfo['basename']}') ?>"; + + // + case 'load_js_plugin': + $plugin = $this->_replaceVar($m[5]); + if(strpos($plugin, '$__Context') === false) $plugin = "'{$plugin}'"; + + return ""; + + // or or or + case 'import': + case 'load': + case 'unload': + $metafile = ''; + $pathinfo = pathinfo($attr['target']); + $doUnload = ($m[3] === 'unload'); + $isRemote = !!preg_match('@^https?://@i', $attr['target']); + + if(!$isRemote) { + if(!preg_match('@^\.?/@',$attr['target'])) $attr['target'] = './'.$attr['target']; + if(substr($attr['target'], -5) == '/lang') { + $pathinfo['dirname'] .= '/lang'; + $pathinfo['basename'] = ''; + $pathinfo['extension'] = 'xml'; + } + + $relativeDir = $this->_getRelativeDir($pathinfo['dirname']); + + $attr['target'] = $relativeDir.'/'.$pathinfo['basename']; + } + + switch($pathinfo['extension']) + { + case 'xml': + if($isRemote || $doUnload) return ''; + // language file? + if($pathinfo['basename'] == 'lang.xml' || substr($pathinfo['dirname'],-5) == '/lang') { + $result = "Context::loadLang('{$relativeDir}');"; + } else { + $result = "require_once('./classes/xml/XmlJsFilter.class.php');\$__xmlFilter=new XmlJsFilter('{$relativeDir}','{$pathinfo['basename']}');\$__xmlFilter->compile();"; + } + break; + case 'js': + if($doUnload) { + $result = "Context::unloadFile('{$attr['target']}','{$attr['targetie']}');"; + } else { + $metafile = $attr['target']; + $result = "\$__tmp=array('{$attr['target']}','{$attr['type']}','{$attr['targetie']}','{$attr['index']}');Context::loadFile(\$__tmp,'{$attr['usecdn']}','{$attr['cdnprefix']}','{$attr['cdnversion']}');unset(\$__tmp);"; + } + break; + case 'css': + if($doUnload) { + $result = "Context::unloadFile('{$attr['target']}','{$attr['targetie']}','{$attr['media']}');"; + } else { + $metafile = $attr['target']; + $result = "\$__tmp=array('{$attr['target']}','{$attr['media']}','{$attr['targetie']}','{$attr['index']}');Context::loadFile(\$__tmp,'{$attr['usecdn']}','{$attr['cdnprefix']}','{$attr['cdnversion']}');unset(\$__tmp);"; + } + break; + } + + $result = ""; + if($metafile) $result = "".$result; + + return $result; + } + } + + // such as , , + if($m[7]) + { + $m[7] = substr($m[7],1); + if(!preg_match('/^(?:((?:end)?(?:if|switch|for(?:each)?|while)|end)|(else(?:if)?)|(break@)?(case|default)|(break))$/', $m[7], $mm)) return ''; + if($mm[1]) { + if($mm[1]{0} == 'e') return ''.$m[9]; + + $precheck = ''; + if($mm[1] == 'switch') { + $m[9] = ''; + } elseif($mm[1] == 'foreach') { + $var = preg_replace('/^\s*\(\s*(.+?) .*$/', '$1', $m[8]); + $precheck = "if({$var}&&count({$var}))"; + } + return '_replaceVar($precheck.$m[7].$m[8]).'{ ?>'.$m[9]; + } + if($mm[2]) return "_replaceVar($m[8])."{ ?>".$m[9]; + if($mm[4]) return "".$m[9]; + if($mm[5]) return ""; + return ''; + } + + return $m[0]; + } + + function _getRelativeDir($path) + { + $_path = $path; + + $fileDir = strtr(realpath($this->path),'\\','/'); + if($path{0} != '/') $path = strtr(realpath($fileDir.'/'.$path),'\\','/'); + + // for backward compatibility + if(!$path) { + $dirs = explode('/', $fileDir); + $paths = explode('/', $_path); + $idx = array_search($paths[0], $dirs); + + if($idx !== false) { + while($dirs[$idx] && $dirs[$idx] === $paths[0]) { + array_splice($dirs, $idx, 1); + array_shift($paths); + } + $path = strtr(realpath($fileDir.'/'.implode('/', $paths)),'\\','/'); + } + } + + $path = preg_replace('/^'.preg_quote(_XE_PATH_,'/').'/', '', $path); + + return $path; + } + + /** + * @brief replace PHP variables of $ character + **/ + function _replaceVar($php) { + if(!strlen($php)) return ''; + return preg_replace('@(?$1', $php); + } +} + +/* End of File: TemplateHandler.class.php */ diff --git a/classes/validator/Validator.class.php b/classes/validator/Validator.class.php new file mode 100644 index 000000000..aa07afa2e --- /dev/null +++ b/classes/validator/Validator.class.php @@ -0,0 +1,499 @@ +__construct($xml_path); + } + + function __construct($xml_path='') { + $this->_rules = array(); + $this->_filters = array(); + $this->_xml_ruleset = null; + + if($xml_path) $this->load($xml_path); + + // predefined rules + $this->addRule(array( + 'email' => '/^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/', + 'userid' => '/^[a-z]+[\w-]*[a-z0-9_]+$/i', + 'url' => '/^(https?|ftp|mms):\/\/[0-9a-z-]+(\.[_0-9a-z-]+)+(:\d+)?/', + 'alpha' => '/^[a-z]*$/i', + 'alpha_number' => '/^[a-z][a-z0-9_]*$/i', + 'number' => '/^(?:[1-9]\\d*|0)$/' + )); + + $this->_has_mb_func = is_callable('mb_strlen'); + $this->setCacheDir('./files/cache'); + } + + function __destruct() { + $this->_rules = null; + $this->_filters = null; + } + + /** + * Load a xml file + * @param[in] string $xml_path A file name to be loaded + */ + function load($xml_path) { + $this->_xml_ruleset = null; + + $xml_path = realpath($xml_path); + if(!is_readable($xml_path)) return false; + + $parser = new XmlParser(); + $xml = $parser->loadXmlFile($xml_path); + if(!isset($xml->ruleset) || !isset($xml->ruleset->fields) || !isset($xml->ruleset->fields->field)) return false; + + // custom rules + if(isset($xml->ruleset->customrules) && isset($xml->ruleset->customrules->rule)) { + $customrules = $xml->ruleset->customrules->rule; + if(!is_array($customrules)) $customrules = array($customrules); + + $rules = array(); + foreach($customrules as $rule) { + if(!isset($rule->attrs) || !isset($rule->attrs->name)) continue; + + $rule = (array)$rule->attrs; + $name = $rule['name']; + unset($rule['name']); + + $rules[$name] = $rule; + } + if(count($rules)) $this->addRule($rules); + } + + // filters + $fields = $xml->ruleset->fields->field; + if(!is_array($fields)) $fields = array($fields); + + $filters = array(); + foreach($fields as $field) { + $name = ''; + $filter = array(); + + if(!isset($field->attrs) || !isset($field->attrs->name)) continue; + $filter = (array)$field->attrs; + + $name = $filter['name']; + unset($filter['name']); + + // conditional statement + if(isset($field->if)) { + $if = $field->if; + if(!is_array($if)) $if = array($if); + foreach($if as $idx=>$cond) { + $if[$idx] = (array)$cond->attrs; + } + $filter['if'] = $if; + } + + $filters[$name] = $filter; + } + + $this->_xml_ruleset = $xml->ruleset; + $this->_filters = $filters; + $this->_xml_path = $xml_path; + + return true; + } + + /** + * Set root cache directory + * @param[in] string $cache_dir Root cache directory + */ + function setCacheDir($cache_dir){ + if(is_dir($cache_dir)) { + $this->_cache_dir = preg_replace('@/$@', '', $cache_dir); + } + } + + /** + * Validate the fields. If the fields aren't passed, validation will be execute on the Context variables. + * @param[in] (optional) array $fields Target fields. The keys of the array represents field's name, its values represents field's value. + * @return bool True if it is valid, FALSE otherwise. + */ + function validate($fields_=null) { + if(is_array($fields_)) { + $fields = $fields_; + } else { + $args = array_keys($this->_filters); + $fields = (array)Context::getRequestVars(); + } + + if(!is_array($fields)) return true; + + $filter_default = array( + 'required' => 'false', + 'default' => '', + 'modifiers' => array(), + 'length' => 0, + 'equalto' => 0, + 'rule' => 0, + 'if' => array() + ); + + $fields = array_map(array($this, 'arrayTrim'), $fields); + $field_names = array_keys($fields); + + $filters = $this->_filters; + + // get field names matching patterns + foreach($this->_filters as $key=>$filter) { + $names = array(); + if($key{0} == '^') { + $names = preg_grep('/^'.preg_quote(substr($key,1)).'/', $field_names); + }elseif(substr($key,-2) == '[]'){ + $filters[substr($key,0,-2)] = $filter; + unset($filters[$key]); + } + + if(!count($names)) continue; + + foreach($names as $name) { + $filters[$name] = $filter; + } + + unset($filters[$key]); + } + + foreach($filters as $key=>$filter) { + $fname = preg_replace('/\[\]$/', '', $key); + $exists = array_key_exists($key, $fields); + $filter = array_merge($filter_default, $filter); + $value = $exists ? $fields[$fname] : null; + + if(is_array($value)) $value = implode('', $value); + + // conditional statement + foreach($filter['if'] as $cond) { + if(!isset($cond['test']) || !isset($cond['attr'])) continue; + + $func_body = preg_replace('/\\$(\w+)/', '$c[\'$1\']', $cond['test']); + $func = create_function('$c', "return !!({$func_body});"); + + if($func($fields)) $filter[$cond['attr']] = $cond['value']; + } + + // attr : default + if(!$value && strlen($default=trim($filter['default']))) { + $value = $default; + if(is_null($fields_)) Context::set($fname, $value); + else $fields_[$fname] = $value; + } + $value_len = strlen($value); + + // attr : modifier + if(is_string($modifiers=$filter['modifiers'])) $modifiers = explode(',', trim($modifiers)); + + // attr : required + if($filter['required'] === 'true' && !$value_len) return $this->error($key, 'isnull'); + + // if the field wasn't passed, ignore this value + if(!$exists && !$value_len) continue; + + // attr : length + if($length=$filter['length']){ + list($min, $max) = explode(':', trim($length)); + $is_min_b = (substr($min, -1) === 'b'); + $is_max_b = (substr($max, -1) === 'b'); + list($min, $max) = array((int)$min, (int)$max); + + $strbytes = strlen($value); + if(!$is_min_b || !$is_max_b) { + $strlength = $this->_has_mb_func?mb_strlen($value,'utf-8'):$this->mbStrLen($value); + } + + if(($min && $min > ($is_min_b?$strbytes:$strlength)) || ($max && $max < ($is_max_b?$strbytes:$strlength))) return $this->error($key, 'outofrange'); + } + + // equalto + if($equalto=$filter['equalto']){ + if(!array_key_exists($equalto, $fields) || trim($fields[$equalto]) !== $value) return $this->error($key, 'equalto'); + } + + // rules + if($rules=$filter['rule']){ + $rules = explode(',', $rules); + foreach($rules as $rule) { + $result = $this->applyRule($rule, $value); + // apply the 'not' modifier + if(in_array('not', $modifiers)) $result = !$result; + if(!$result) return $this->error($key, 'invalid_'.$rule); + } + } + } + + return true; + } + + /** + * apply trim recursive + */ + function arrayTrim($array) + { + if (!is_array($array)) return trim($array); + + foreach($array as $key => $value) + { + $array[$key] = $this->arrayTrim($value); + } + + return $array; + } + + /** + * Log an error + * @param[in] $msg error message + * @return always false + */ + function error($field, $msg){ + $lang_filter = Context::getLang('filter'); + $msg = isset($lang_filter->{$msg})?$lang_filter->{$msg}:$lang_filter->invalid; + $msg = sprintf($msg, Context::getLang($field)); + + $this->_last_error = array('field'=>$field, 'msg'=>$msg); + + return false; + } + + /** + * Returns the last error infomation including a field name and an error message. + * @return array The last error infomation + */ + function getLastError(){ + return $this->_last_error; + } + + /** + * Add a new rule + * @param[in] string $name rule name + * @param[in] mixed $rule + */ + function addRule($name, $rule=''){ + if(is_array($name)) $args = $name; + else $args = array($name=>$rule); + + foreach($args as $name=>$rule){ + if(!$rule) continue; + if(is_string($rule)) $rule = array('type'=>'regex', 'test'=>$rule); + + if($rule['type'] == 'enum') { + $delim = isset($rule['delim'])?$rule['delim']:','; + $rule['test'] = explode($delim, $rule['test']); + } + + $this->_rules[$name] = $rule; + } + } + + /** + * Remove a rule + * @param[in] string $name rule name + */ + function removeRule($name){ + unset($this->_rules[$name]); + } + + function addFilter($name, $filter='') { + if(is_array($name)) $args = $name; + else $args = array($name=>$filter); + + foreach($args as $name=>$filter) { + if(!$filter) continue; + + if(isset($filter['if'])) { + if(is_array($filter['if']) && count($filter['if'])) { + $key = key($filter['if']); + if(!is_int($key)) $filter['if'] = array($filter['if']); + } else { + unset($filter['if']); + } + } + + $this->_filters[$name] = $filter; + } + } + + function removeFilter($name) { + unset($this->_filters[$name]); + } + + /** + * Find whether the field is valid with the rule + * @param[in] string $name rule name + * @param[in] string $value a value to be validated + * @return bool TRUE if the field is valid, FALSE otherwise. + */ + function applyRule($name, $value){ + $rule = $this->_rules[$name]; + + if (is_array($value) && isset($value['tmp_name'])) + { + $value = $value['name']; + } + + switch($rule['type']) { + case 'regex': + return (preg_match($rule['test'], $value) > 0); + case 'enum': + return in_array($value, $rule['test']); + case 'expr': + if(!$rule['func_test']) { + $rule['func_test'] = create_function('$a', 'return ('.preg_replace('/\$\$/', '$a', $rule['test']).');'); + } + return $rule['func_test']($value); + } + + return true; + } + + /** + * Return + */ + function mbStrLen($str){ + $arr = count_chars($str); + for($i=0x80; $i < 0xc0; $i++) { + unset($arr[$i]); + } + return array_sum($arr); + } + + /** + * Returns compiled javascript file path. The path begins from XE root directory. + * @return string Compiled JavaScript file path + */ + function getJsPath(){ + if(!$this->_cache_dir) return false; + + $dir = $this->_cache_dir.'/ruleset'; + if(!is_dir($dir) && !mkdir($dir)) return false; + if(!$this->_xml_path) return false; + + // current language + $lang_type = class_exists('Context')?Context::getLangType():'en'; + + // check the file + $filepath = $dir.'/'.md5($this->_version.' '.$this->_xml_path).".{$lang_type}.js"; + if(is_readable($filepath) && filemtime($filepath) > filemtime($this->_xml_path)) return $filepath; + + $content = $this->_compile2js(); + if($content === false) return false; + + if(is_callable('file_put_contents')) { + @file_put_contents($filepath, $content); + } else { + $fp = @fopen($filepath, 'w'); + if(is_resource($fp)) { + fwrite($fp, $content); + fclose($fp); + } + } + + return $filepath; + } + + /** + * Compile a ruleset to a javascript file + * @private + */ + function _compile2js() { + global $lang; + + $ruleset = basename($this->_xml_path,'.xml'); + $content = array(); + + if(preg_match('@(^|/)files/ruleset/\w+\.xml$@i', $this->_xml_path)) $ruleset = '@'.$ruleset; + + // current language + $lang_type = class_exists('Context')?Context::getLangType():'en'; + + // custom rulesets + $addrules = array(); + foreach($this->_rules as $name=>$rule) { + if(strpos('email,userid,url,alpha,alpha_number,number,', $name.',') !== false) continue; + switch($rule['type']) { + case 'regex': + $content[] = "v.cast('ADD_RULE', ['{$name}', {$rule['test']}]);"; + break; + case 'enum': + $enums = '"'.implode('","', $rule['test']).'"'; + $content[] = "v.cast('ADD_RULE', ['{$name}', function($$){ return ($.inArray($$,[{$enums}]) > -1); }]);"; + break; + case 'expr': + $content[] = "v.cast('ADD_RULE', ['{$name}', function($$){ return ({$rule['test']}); }]);"; + break; + } + } + $addrules = implode('', $addrules); + + // filters + $content = array(); + $messages = array(); + foreach($this->_filters as $name=>$filter) { + $field = array(); + + // form filed name + if(isset($lang->{$name})) { + $field_lang = addslashes($lang->{$name}); + $messages[] = "v.cast('ADD_MESSAGE',['{$name}','{$field_lang}']);"; + } + + if($filter['required'] == 'true') $field[] = 'required:true'; + if($filter['rule']) $field[] = "rule:'{$filter['rule']}'"; + if($filter['default']) $field[] = "default:'{$filter['default']}'"; + if($filter['modifier']) $field[] = "modifier:'{$filter['modifier']}'"; + if($filter['length']) { + list($min, $max) = explode(':', $filter['length']); + if($min) $field[] = "minlength:'{$min}'"; + if($max) $field[] = "maxlength:'{$max}'"; + } + if($filter['if']) { + $ifs = array(); + if(!isset($filter['if'][0])) $filter['if'] = array($filter['if']); + foreach($filter['if'] as $if) { + $ifs[] = "{test:'".addslashes($if['test'])."', attr:'{$if['attr']}', value:'".addslashes($if['value'])."'}"; + } + $field[] = "'if':[".implode(',', $ifs)."]"; + } + if(count($field)) { + $field = '{'.implode(',', $field).'}'; + $content[] = "'{$name}':{$field}"; + } + } + + if(!$content) return '/* Error : empty ruleset */'; + + // error messages + foreach($lang->filter as $key=>$text) { + if($text) { + $text = preg_replace('@\r?\n@', '\\n', addslashes($text)); + $messages[] = "v.cast('ADD_MESSAGE',['{$key}','{$text}']);"; + } + } + + $content = implode(',', $content); + $messages = implode("\n", $messages); + + return "(function($,v){\nv=xe.getApp('validator')[0];if(!v)return;\n{$addrules}\nv.cast('ADD_FILTER',['{$ruleset}', {{$content}}]);\n{$messages}\n})(jQuery);"; + } +} + +/* End of file Validator.class.php */ +/* Location: ./classes/validator/Validator.class.php */ diff --git a/classes/widget/WidgetHandler.class.php b/classes/widget/WidgetHandler.class.php index c35e4a188..8acf4abb4 100644 --- a/classes/widget/WidgetHandler.class.php +++ b/classes/widget/WidgetHandler.class.php @@ -1,14 +1,14 @@ - + diff --git a/classes/xml/GeneralXmlParser.class.php b/classes/xml/GeneralXmlParser.class.php index 18b938ab0..becf581d4 100644 --- a/classes/xml/GeneralXmlParser.class.php +++ b/classes/xml/GeneralXmlParser.class.php @@ -1,83 +1,83 @@ -output)) return; - $this->output = array_shift($this->output); - - return $this->output; - } - - /** - * @brief start element handler - * @param[in] $parse an instance of parser - * @param[in] $node_name a name of node - * @param[in] $attrs attributes to be set - */ - function _tagOpen($parser, $node_name, $attrs) { - $obj->node_name = strtolower($node_name); - $obj->attrs = $attrs; - $obj->childNodes = array(); - - array_push($this->output, $obj); - } - - /** - * @brief character data handler - * variable in the last element of this->output - * @param[in] $parse an instance of parser - * @param[in] $body a data to be added - */ - function _tagBody($parser, $body) { - //if(!trim($body)) return; - $this->output[count($this->output)-1]->body .= $body; - } - - - /** - * @brief end element handler - * @param[in] $parse an instance of parser - * @param[in] $node_name name of xml node - */ - 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($parent_obj->childNodes[$node_name]) - { - $tmp_obj = $parent_obj->childNodes[$node_name]; - if(is_array($tmp_obj)) { - array_push($parent_obj->childNodes[$node_name], $cur_obj); - } else { - $parent_obj->childNodes[$node_name] = array(); - array_push($parent_obj->childNodes[$node_name], $tmp_obj); - array_push($parent_obj->childNodes[$node_name], $cur_obj); - } - } else { - $parent_obj->childNodes[$node_name] = $cur_obj; - } - } - - } -?> +output)) return; + $this->output = array_shift($this->output); + + return $this->output; + } + + /** + * @brief start element handler + * @param[in] $parse an instance of parser + * @param[in] $node_name a name of node + * @param[in] $attrs attributes to be set + */ + function _tagOpen($parser, $node_name, $attrs) { + $obj->node_name = strtolower($node_name); + $obj->attrs = $attrs; + $obj->childNodes = array(); + + array_push($this->output, $obj); + } + + /** + * @brief character data handler + * variable in the last element of this->output + * @param[in] $parse an instance of parser + * @param[in] $body a data to be added + */ + function _tagBody($parser, $body) { + //if(!trim($body)) return; + $this->output[count($this->output)-1]->body .= $body; + } + + + /** + * @brief end element handler + * @param[in] $parse an instance of parser + * @param[in] $node_name name of xml node + */ + 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($parent_obj->childNodes[$node_name]) + { + $tmp_obj = $parent_obj->childNodes[$node_name]; + if(is_array($tmp_obj)) { + array_push($parent_obj->childNodes[$node_name], $cur_obj); + } else { + $parent_obj->childNodes[$node_name] = array(); + array_push($parent_obj->childNodes[$node_name], $tmp_obj); + array_push($parent_obj->childNodes[$node_name], $cur_obj); + } + } else { + $parent_obj->childNodes[$node_name] = $cur_obj; + } + } + + } +?> diff --git a/classes/xml/XmlJsFilter.class.php b/classes/xml/XmlJsFilter.class.php index a97a8eceb..db1bae032 100644 --- a/classes/xml/XmlJsFilter.class.php +++ b/classes/xml/XmlJsFilter.class.php @@ -1,290 +1,292 @@ - - *
<-- code to validate data in the form - * - * - * <-- 폼 항목을 조합하여 key=val 의 js array로 return, act는 필수 - * - * - * <-- 서버에 ajax로 전송하여 받을 결과값 - * <-- error이름의 결과값을 받겠다는 것 - * - * - * } - * - * @detail { - * - syntax description of
node - * target = name of for element - * required = flag indicating whether a field is mandatory or not - * minlength, maxlength = mininum, maxinum length of string allowed for the field - * filter = name of filter to be used for javascript validation. Following is the description of filter available - * 1) email : validate the confirmance of the value against an email format - * 2) userid : validate the confirmance of the value against the format of user id. (combination of number[0-9],alphabet(lower case) and '_', underscore starting with an alphatic character) - * 3) alpha : check if the value is consists of alphabatic characters. - * 4) number : check if the value is consists of numerical digits - * 5) equalto = target : indicate that values in the form should be equal to those in target - * 6) pattern_id/regex pattern/[i] : check the value using custom regular expression. - * - * - parameter - param - * name = key : indicate that a new array, 'key' will be created and a value will be assigned to it - * target = target_name : target form element의 값을 가져옴 - * - * - response - * tag = key : name of variable that will contain the result of the execution - * } - **/ - - class XmlJsFilter extends XmlParser { - var $version = '0.2.5'; - 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) { - if(substr($path,-1)!=='/') $path .= '/'; - $this->xml_file = sprintf("%s%s",$path, $xml_file); - $this->js_file = $this->_getCompiledFileName($this->xml_file); - } - - /** - * @brief compile a xml_file only when a corresponding js file does not exists or is outdated - * @return Returns NULL regardless of the success of failure of the operation - **/ - function compile() { - if(!file_exists($this->xml_file)) return; - if(!file_exists($this->js_file)) $this->_compile(); - else if(filemtime($this->xml_file)>filemtime($this->js_file)) $this->_compile(); - Context::addJsFile($this->js_file, false, '',null,'body'); - } - - /** - * @brief compile a xml_file into js_file - **/ - function _compile() { - global $lang; - - // xml 파일을 읽음 - $buff = FileHandler::readFile($this->xml_file); - - // xml parsing - $xml_obj = parent::parse($buff); - - $attrs = $xml_obj->filter->attrs; - $rules = $xml_obj->filter->rules; - - // XmlJsFilter는 filter_name, field, parameter 3개의 데이터를 핸들링 - $filter_name = $attrs->name; - $confirm_msg_code = $attrs->confirm_msg_code; - $module = $attrs->module; - $act = $attrs->act; - $extend_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 = $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; - } - } - - } - } - - // 언어 입력을 위한 사용되는 필드 조사 - $target_list = array(); - $target_type_list = array(); - - // javascript contents - $js_rules = array(); - $js_messages = array(); - - $fields = array(); - - // create custom rule - if ($rules && $rules->rule) { - if (!is_array($rules->rule)) $rules->rule = array($rules->rule); - foreach($rules->rule as $r) { - if ($r->attrs->type == 'regex') { - $js_rules[] = "v.cast('ADD_RULE', ['{$r->attrs->name}', {$r->body}]);"; - } - } - } - - // 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; - - $rule = trim($attrs->rule?$attrs->rule:$attrs->filter); - $equalto = trim($attrs->equalto); - - $field = array(); - - if($attrs->required == 'true') $field[] = 'required:true'; - if($attrs->minlength > 0) $field[] = 'minlength:'.$attrs->minlength; - if($attrs->maxlength > 0) $field[] = 'maxlength:'.$attrs->maxlength; - if($equalto) $field[] = "equalto:'{$attrs->equalto}'"; - if($rule) $field[] = "rule:'{$rule}'"; - - $fields[] = "'{$target}': {".implode(',', $field)."}"; - - if(!in_array($target, $target_list)) $target_list[] = $target; - if(!$target_type_list[$target]) $target_type_list[$target] = $filter; - } - } - - // extend_filter_item 체크 - $rule_types = array('homepage'=>'homepage', 'email_address'=>'email'); - - for($i=0;$i<$extend_filter_count;$i++) { - $filter_item = $extend_filter_list[$i]; - $target = trim($filter_item->name); - - if(!$target) continue; - - // extend filter item의 type으로 rule을 구함 - $type = $filter_item->type; - $rule = $rule_types[$type]?$rule_types[$type]:''; - $required = ($filter_item->required == 'true'); - - $field = array(); - if($required) $field[] = 'required:true'; - if($rule) $field[] = "rule:'{$rule}'"; - $fields[] = "\t\t'{$target}' : {".implode(',', $field)."}"; - - if(!in_array($target, $target_list)) $target_list[] = $target; - if(!$target_type_list[$target]) $target_type_list[$target] = $type; - } - - // 데이터를 만들기 위한 parameter script 생성 - $rename_params = array(); - $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 && ($name != $target)) $js_doc[] = "\t\tparams['{$name}'] = params['{$target}']; delete params['{$target}'];"; - if($name && $target && ($name != $target)) $rename_params[] = "'{$target}':'{$name}'"; - if($name && !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; - - if(!in_array($name, $target_list)) $target_list[] = $name; - } - } - - // response script 생성 - $response_count = count($response_tag); - $responses = array(); - for($i=0;$i<$response_count;$i++) { - $attrs = $response_tag[$i]->attrs; - $name = $attrs->name; - $responses[] = "'{$name}'"; - } - - // lang : form field description - $target_count = count($target_list); - for($i=0;$i<$target_count;$i++) { - $target = $target_list[$i]; - if(!$lang->{$target}) $lang->{$target} = $target; - $js_messages[] = sprintf("v.cast('ADD_MESSAGE',['%s','%s']);", $target, addslashes($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); - } - } - */ - - // lang : error message - foreach($lang->filter as $key => $val) { - if(!$val) $val = $key; - $js_messages[] = sprintf("v.cast('ADD_MESSAGE',['%s','%s']);", $key, $val); - } - - $callback_func = $xml_obj->filter->response->attrs->callback_func; - if(!$callback_func) $callback_func = "filterAlertMessage"; - - $confirm_msg = ''; - if ($confirm_msg_code) $confirm_msg = $lang->{$confirm_msg_code}; - - $jsdoc = array(); - $jsdoc[] = "function {$filter_name}(form){ return legacy_filter('{$filter_name}', form, '{$module}', '{$act}', {$callback_func}, [".implode(',', $responses)."], '".addslashes($confirm_msg)."', {".implode(',', $rename_params)."}) };"; - $jsdoc[] = '(function($){'; - $jsdoc[] = "\tvar v=xe.getApp('validator')[0];if(!v)return false;"; - $jsdoc[] = "\t".'v.cast("ADD_FILTER", ["'.$filter_name.'", {'.implode(',', $fields).'}]);'; - $jsdoc[] = "\t".implode("\n\t", $js_rules); - $jsdoc[] = "\t".implode("\n\t", $js_messages); - $jsdoc[] = '})(jQuery);'; - $jsdoc = implode("\n", $jsdoc); - - // js파일 생성 - FileHandler::writeFile($this->js_file, $jsdoc); - } - - /** - * @brief return a file name of js file corresponding to the xml file - **/ - function _getCompiledFileName($xml_file) { - return sprintf('%s%s.%s.compiled.js',$this->compiled_path, md5($this->version.$xml_file),Context::getLangType()); - } - } -?> + + * <-- code to validate data in the form + * + * + * "- A form of key = val combination of items to js array return, act required + * + * + * "- Result to get by sending ajax to the server + * <- get the result of error name + * + * + * } + * + * @detail { + * - syntax description of
node + * target = name of for element + * required = flag indicating whether a field is mandatory or not + * minlength, maxlength = mininum, maxinum length of string allowed for the field + * filter = name of filter to be used for javascript validation. Following is the description of filter available + * 1) email : validate the confirmance of the value against an email format + * 2) userid : validate the confirmance of the value against the format of user id. (combination of number[0-9],alphabet(lower case) and '_', underscore starting with an alphatic character) + * 3) alpha : check if the value is consists of alphabatic characters. + * 4) number : check if the value is consists of numerical digits + * 5) equalto = target : indicate that values in the form should be equal to those in target + * 6) pattern_id/regex pattern/[i] : check the value using custom regular expression. + * + * - parameter - param + * name = key : indicate that a new array, 'key' will be created and a value will be assigned to it + * target = target_name: get the value of the target form element + * + * - response + * tag = key : name of variable that will contain the result of the execution + * } + **/ + + class XmlJsFilter extends XmlParser { + var $version = '0.2.5'; + var $compiled_path = './files/cache/js_filter_compiled/'; // / directory path for compiled cache file + var $xml_file = NULL; // / Target xml file + var $js_file = NULL; // / Compiled js file + + /** + * @brief constructor + **/ + function XmlJsFilter($path, $xml_file) { + if(substr($path,-1)!=='/') $path .= '/'; + $this->xml_file = sprintf("%s%s",$path, $xml_file); + $this->js_file = $this->_getCompiledFileName($this->xml_file); + } + + /** + * @brief compile a xml_file only when a corresponding js file does not exists or is outdated + * @return Returns NULL regardless of the success of failure of the operation + **/ + function compile() { + if(!file_exists($this->xml_file)) return; + if(!file_exists($this->js_file)) $this->_compile(); + else if(filemtime($this->xml_file)>filemtime($this->js_file)) $this->_compile(); + Context::loadFile(array($this->js_file, 'body', '',null)); + } + + /** + * @brief compile a xml_file into js_file + **/ + function _compile() { + global $lang; + + // read xml file + $buff = FileHandler::readFile($this->xml_file); + + // xml parsing + $xml_obj = parent::parse($buff); + + $attrs = $xml_obj->filter->attrs; + $rules = $xml_obj->filter->rules; + + // XmlJsFilter handles three data; filter_name, field, and parameter + $filter_name = $attrs->name; + $confirm_msg_code = $attrs->confirm_msg_code; + $module = $attrs->module; + $act = $attrs->act; + $extend_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); + + // If extend_filter exists, result returned by calling the method + if($extend_filter) { + + // If extend_filter exists, it changes the name of cache not to use cache + $this->js_file .= '.nocache.js'; + + // Separate the extend_filter + list($module_name, $method) = explode('.',$extend_filter); + + // contibue if both module_name and methos exist. + if($module_name&&$method) { + // get model object of the module + $oExtendFilter = &getModel($module_name); + + // execute if method exists + if(method_exists($oExtendFilter, $method)) { + // get the result + $extend_filter_list = $oExtendFilter->{$method}(true); + $extend_filter_count = count($extend_filter_list); + + // apply lang_value from the result to the variable + 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; + } + } + + } + } + + // search the field to be used for entering language + $target_list = array(); + $target_type_list = array(); + + // javascript contents + $js_rules = array(); + $js_messages = array(); + + $fields = array(); + + // create custom rule + if ($rules && $rules->rule) { + if (!is_array($rules->rule)) $rules->rule = array($rules->rule); + foreach($rules->rule as $r) { + if ($r->attrs->type == 'regex') { + $js_rules[] = "v.cast('ADD_RULE', ['{$r->attrs->name}', {$r->body}]);"; + } + } + } + + // generates a field, which is a script of the checked item + $node_count = count($field_node); + if($node_count) { + foreach($field_node as $key =>$node) { + $attrs = $node->attrs; + $target = trim($attrs->target); + + if(!$target) continue; + + $rule = trim($attrs->rule?$attrs->rule:$attrs->filter); + $equalto = trim($attrs->equalto); + + $field = array(); + + if($attrs->required == 'true') $field[] = 'required:true'; + if($attrs->minlength > 0) $field[] = 'minlength:'.$attrs->minlength; + if($attrs->maxlength > 0) $field[] = 'maxlength:'.$attrs->maxlength; + if($equalto) $field[] = "equalto:'{$attrs->equalto}'"; + if($rule) $field[] = "rule:'{$rule}'"; + + $fields[] = "'{$target}': {".implode(',', $field)."}"; + + if(!in_array($target, $target_list)) $target_list[] = $target; + if(!$target_type_list[$target]) $target_type_list[$target] = $filter; + } + } + + // Check extend_filter_item + $rule_types = array('homepage'=>'homepage', 'email_address'=>'email'); + + for($i=0;$i<$extend_filter_count;$i++) { + $filter_item = $extend_filter_list[$i]; + $target = trim($filter_item->name); + + if(!$target) continue; + + // get the filter from the type of extend filter item + $type = $filter_item->type; + $rule = $rule_types[$type]?$rule_types[$type]:''; + $required = ($filter_item->required == 'true'); + + $field = array(); + if($required) $field[] = 'required:true'; + if($rule) $field[] = "rule:'{$rule}'"; + $fields[] = "\t\t'{$target}' : {".implode(',', $field)."}"; + + if(!in_array($target, $target_list)) $target_list[] = $target; + if(!$target_type_list[$target]) $target_type_list[$target] = $type; + } + + // generates parameter script to create dbata + $rename_params = array(); + $parameter_count = count($parameter_param); + if($parameter_count) { + // contains parameter of the default filter contents + foreach($parameter_param as $key =>$param) { + $attrs = $param->attrs; + $name = trim($attrs->name); + $target = trim($attrs->target); + + //if($name && $target && ($name != $target)) $js_doc[] = "\t\tparams['{$name}'] = params['{$target}']; delete params['{$target}'];"; + if($name && $target && ($name != $target)) $rename_params[] = "'{$target}':'{$name}'"; + if($name && !in_array($name, $target_list)) $target_list[] = $name; + } + + // Check 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; + + if(!in_array($name, $target_list)) $target_list[] = $name; + } + } + + // generates the response script + $response_count = count($response_tag); + $responses = array(); + for($i=0;$i<$response_count;$i++) { + $attrs = $response_tag[$i]->attrs; + $name = $attrs->name; + $responses[] = "'{$name}'"; + } + + // writes lang values of the form field + $target_count = count($target_list); + for($i=0;$i<$target_count;$i++) { + $target = $target_list[$i]; + if(!$lang->{$target}) $lang->{$target} = $target; + $text = preg_replace('@\r?\n@', '\\n', addslashes($lang->{$target})); + $js_messages[] = "v.cast('ADD_MESSAGE',['{$target}','{$text}']);"; + } + + // writes the 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); + } + } + */ + + // writes error messages + foreach($lang->filter as $key => $val) { + if(!$val) $val = $key; + $val = preg_replace('@\r?\n@', '\\n', addslashes($val)); + $js_messages[] = sprintf("v.cast('ADD_MESSAGE',['%s','%s']);", $key, $val); + } + + $callback_func = $xml_obj->filter->response->attrs->callback_func; + if(!$callback_func) $callback_func = "filterAlertMessage"; + + $confirm_msg = ''; + if ($confirm_msg_code) $confirm_msg = $lang->{$confirm_msg_code}; + + $jsdoc = array(); + $jsdoc[] = "function {$filter_name}(form){ return legacy_filter('{$filter_name}', form, '{$module}', '{$act}', {$callback_func}, [".implode(',', $responses)."], '".addslashes($confirm_msg)."', {".implode(',', $rename_params)."}) };"; + $jsdoc[] = '(function($){'; + $jsdoc[] = "\tvar v=xe.getApp('validator')[0];if(!v)return false;"; + $jsdoc[] = "\t".'v.cast("ADD_FILTER", ["'.$filter_name.'", {'.implode(',', $fields).'}]);'; + $jsdoc[] = "\t".implode("\n\t", $js_rules); + $jsdoc[] = "\t".implode("\n\t", $js_messages); + $jsdoc[] = '})(jQuery);'; + $jsdoc = implode("\n", $jsdoc); + + // generates the js file + FileHandler::writeFile($this->js_file, $jsdoc); + } + + /** + * @brief return a file name of js file corresponding to the xml file + **/ + function _getCompiledFileName($xml_file) { + return sprintf('%s%s.%s.compiled.js',$this->compiled_path, md5($this->version.$xml_file),Context::getLangType()); + } + } +?> diff --git a/classes/xml/XmlLangParser.class.php b/classes/xml/XmlLangParser.class.php new file mode 100644 index 000000000..9a780a4d6 --- /dev/null +++ b/classes/xml/XmlLangParser.class.php @@ -0,0 +1,152 @@ +lang_type = $lang_type; + $this->xml_file = $xml_file; + $this->php_file = $this->_getCompiledFileName($lang_type); + } + + /** + * @brief compile a xml_file only when a corresponding php lang file does not exists or is outdated + * @return Returns compiled php file. + **/ + function compile() { + if(!file_exists($this->xml_file)) return false; + if(!file_exists($this->php_file)){ + $this->_compile(); + } else { + if(filemtime($this->xml_file)>filemtime($this->php_file)) $this->_compile(); + else return $this->php_file; + } + + return $this->_writefile() ? $this->php_file : false; + } + + function getCompileContent() { + if(!file_exists($this->xml_file)) return false; + $this->_compile(); + + return $this->code; + } + + /** + * @brief compile a xml_file + **/ + function _compile() { + $lang_selected = Context::loadLangSelected(); + $this->lang_types = array_keys($lang_selected); + + // read xml file + $buff = FileHandler::readFile($this->xml_file); + $buff = str_replace('xml:lang','xml_lang',$buff); + + // xml parsing + $xml_obj = parent::parse($buff); + + $item = $xml_obj->lang->item; + if(!is_array($item)) $item = array($item); + foreach($item as $i){ + $this->_parseItem($i, $var='$lang->%s'); + } + } + + /** + * @brief writing cache file + **/ + function _writeFile(){ + if(!$this->code) return; + FileHandler::writeFile($this->php_file, "code); + return false; + } + + /** + * @brief Parsing item node + **/ + function _parseItem($item, $var){ + $name = $item->attrs->name; + $value = $item->value; + $var = sprintf($var, $name); + + if($item->item) { + $type = $item->attrs->type; + + if($type == 'array'){ + $this->code .= $var."=array();\n"; + $var .= '[\'%s\']'; + }else{ + $this->code .= $var."=new stdClass;\n"; + $var .= '->%s'; + } + + $items = $item->item; + if(!is_array($items)) $items = array($items); + foreach($items as $item){ + $this->_parseItem($item, $var); + } + + } else { + $code = $this->_parseValues($value, $var); + $this->code .= $code; + } + } + + /** + * @brief Parsing value nodes + **/ + function _parseValues($nodes, $var) { + if(!is_array($nodes)) $nodes = array($nodes); + + $value = array(); + foreach($nodes as $node){ + $return = $this->_parseValue($node, $var); + if($return && is_array($return)) $value = array_merge($value, $return); + } + + if($value[$this->lang_type]) return $value[$this->lang_type]; + else if($value['en']) return $value['en']; + else if($value['ko']) return $value['ko']; + + foreach($this->lang_types as $lang_type) { + if($lang_type == 'en' || $lang_type == 'ko' || $lang_type == $this->lang_type) continue; + if($value[$lang_type]) return $value[$lang_type]; + } + + return ''; + } + + /** + * @brief Parsing value node + **/ + function _parseValue($node, $var) { + $lang_type = $node->attrs->xml_lang; + $value = $node->body; + if(!$value) return false; + + $var .= '=\'' . str_replace("'","\'",$value) . "';\n"; + return array($lang_type=>$var); + } + + /** + * @brief get cache file name + **/ + function _getCompiledFileName($lang_type, $type='php') { + return sprintf('%s%s.%s.php',$this->compiled_path, md5($this->xml_file), $lang_type); + } + } diff --git a/classes/xml/XmlParser.class.php b/classes/xml/XmlParser.class.php index 4e68a2abf..b9326fec8 100644 --- a/classes/xml/XmlParser.class.php +++ b/classes/xml/XmlParser.class.php @@ -1,154 +1,153 @@ -parse($buff); - } - - /** - * @brief parse xml data to extract values from it and construct data object - * @param[in] a data buffer containing xml data - * @return Returns a resultant data object or NULL in case of error - **/ - function parse($input = '') { - // 디버그를 위한 컴파일 시작 시간 저장 - if(__DEBUG__==3) $start = getMicroTime(); - - $this->lang = Context::getLangType(); - - $this->input = $input?$input:$GLOBALS['HTTP_RAW_POST_DATA']; - $this->input = str_replace(array('',''),array('',''),$this->input); - - // 지원언어 종류를 뽑음 - 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('UTF-8'); - - 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 start element handler. - * @param[in] $parse an instance of parser - * @param[in] $node_name a name of node - * @param[in] $attrs attributes to be set - */ - function _tagOpen($parser, $node_name, $attrs) { - $obj->node_name = strtolower($node_name); - $obj->attrs = $this->_arrToObj($attrs); - - array_push($this->output, $obj); - } - - - /** - * @brief character data handler - * variable in the last element of this->output - * @param[in] $parse an instance of parser - * @param[in] $body a data to be added - * @remark the first parameter, $parser ought to be remove since it is not used. - */ - function _tagBody($parser, $body) { - //if(!trim($body)) return; - $this->output[count($this->output)-1]->body .= $body; - } - - /** - * @brief end element handler - * @param[in] $parse an instance of parser - * @param[in] $node_name name of xml node - */ - 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 method to transfer values in an array to a data object - * @param[in] $arr data array - **/ - function _arrToObj($arr) { - if(!count($arr)) return; - foreach($arr as $key => $val) { - $key = strtolower($key); - $output->{$key} = $val; - } - return $output; - } - } -?> +parse($buff); + } + + /** + * @brief parse xml data to extract values from it and construct data object + * @param[in] a data buffer containing xml data + * @return Returns a resultant data object or NULL in case of error + **/ + function parse($input = '') { + // Save the compile starting time for debugging + if(__DEBUG__==3) $start = getMicroTime(); + + $this->lang = Context::getLangType(); + + $this->input = $input?$input:$GLOBALS['HTTP_RAW_POST_DATA']; + $this->input = str_replace(array('',''),array('',''),$this->input); + + // extracts a supported language + preg_match_all("/xml:lang=\"([^\"].+)\"/i", $this->input, $matches); + + // extracts the supported lanuage when xml:lang is used + if(count($matches[1]) && $supported_lang = array_unique($matches[1])) { + // if lang of the first log-in user doesn't exist, apply en by default if exists. Otherwise apply the first lang. + if(!in_array($this->lang, $supported_lang)) { + if(in_array('en', $supported_lang)) { + $this->lang = 'en'; + } else { + $this->lang = array_shift($supported_lang); + } + } + // uncheck the language if no specific language is set. + } else { + $this->lang = ''; + } + + $this->oParser = xml_parser_create('UTF-8'); + + 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); + // Save compile starting time for debugging + if(__DEBUG__==3) $GLOBALS['__xmlparse_elapsed__'] += getMicroTime() - $start; + + return $output; + } + + + /** + * @brief start element handler. + * @param[in] $parse an instance of parser + * @param[in] $node_name a name of node + * @param[in] $attrs attributes to be set + */ + function _tagOpen($parser, $node_name, $attrs) { + $obj->node_name = strtolower($node_name); + $obj->attrs = $this->_arrToObj($attrs); + + array_push($this->output, $obj); + } + + + /** + * @brief character data handler + * variable in the last element of this->output + * @param[in] $parse an instance of parser + * @param[in] $body a data to be added + * @remark the first parameter, $parser ought to be remove since it is not used. + */ + function _tagBody($parser, $body) { + //if(!trim($body)) return; + $this->output[count($this->output)-1]->body .= $body; + } + + /** + * @brief end element handler + * @param[in] $parse an instance of parser + * @param[in] $node_name name of xml node + */ + 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(isset($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 method to transfer values in an array to a data object + * @param[in] $arr data array + **/ + function _arrToObj($arr) { + if(!count($arr)) return; + foreach($arr as $key => $val) { + $key = strtolower($key); + $output->{$key} = $val; + } + return $output; + } + } +?> diff --git a/classes/xml/XmlQueryParser.150.class.php b/classes/xml/XmlQueryParser.150.class.php new file mode 100644 index 000000000..f42920447 --- /dev/null +++ b/classes/xml/XmlQueryParser.150.class.php @@ -0,0 +1,82 @@ +getXmlFileContent($xml_file); + + // insert, update, delete, select action + $action = strtolower($xml_obj->query->attrs->action); + if(!$action) return; + + $parser = new QueryParser($xml_obj->query); + + FileHandler::writeFile($cache_file, $parser->toString()); + } + + function getXmlFileContent($xml_file){ + $buff = FileHandler::readFile($xml_file); + $xml_obj = parent::parse($buff); + if(!$xml_obj) return; + unset($buff); + return $xml_obj; + } + } +?> diff --git a/classes/xml/XmlQueryParser.class.php b/classes/xml/XmlQueryParser.class.php index 15cccc026..bf6d291fd 100644 --- a/classes/xml/XmlQueryParser.class.php +++ b/classes/xml/XmlQueryParser.class.php @@ -1,542 +1,542 @@ -query->attrs->action); - if(!$action) return; - - // 테이블 정리 (배열코드로 변환) - $tables = $xml_obj->query->tables->table; - $output->left_tables = array(); - - $left_conditions = array(); - - 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[$alias] = $table_name; - - if(in_array($val->attrs->type,array('left join','left outer join','right join','right outer join')) && count($val->conditions)){ - $output->left_tables[$alias] = $val->attrs->type; - $left_conditions[$alias] = $val->conditions; - } - - // 테이블을 찾아서 컬럼의 속성을 구함 - $table_file = sprintf('%s%s/%s/schemas/%s.xml', _XE_PATH_, 'modules', $module, $table_name); - if(!file_exists($table_file)) { - $searched_list = FileHandler::readDir(_XE_PATH_.'modules'); - $searched_count = count($searched_list); - for($i=0;$i<$searched_count;$i++) { - $table_file = sprintf('%s%s/%s/schemas/%s.xml', _XE_PATH_, 'modules', $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) { - if(isset($table_obj->table->column) && !is_array($table_obj->table->column)) - { - $table_obj->table->column = array($table_obj->table->column); - } - - 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; - $out = $this->_setColumn($columns); - $output->columns = $out->columns; - - $conditions = $xml_obj->query->conditions; - $out = $this->_setConditions($conditions); - $output->conditions = $out->conditions; - - foreach($output->left_tables as $key => $val){ - if($left_conditions[$key]){ - $out = $this->_setConditions($left_conditions[$key]); - $output->left_conditions[$key] = $out->conditions; - } - } - - $group_list = $xml_obj->query->groups->group; - $out = $this->_setGroup($group_list); - $output->groups = $out->groups; - - // 네비게이션 정리 - $out = $this->_setNavigation($xml_obj); - $output->order = $out->order; - $output->list_count = $out->list_count; - $output->page_count = $out->page_count; - $output->page = $out->page; - - $column_count = count($output->columns); - $condition_count = count($output->conditions); - - $buff .= '$output->tables = array( '; - foreach($output->tables as $key => $val) { - if(!array_key_exists($key,$output->left_tables)){ - $buff .= sprintf('"%s"=>"%s",', $key, $val); - } - } - $buff .= ' );'."\n"; - - // php script 생성 - $buff .= '$output->_tables = array( '; - foreach($output->tables as $key => $val) { - $buff .= sprintf('"%s"=>"%s",', $key, $val); - } - $buff .= ' );'."\n"; - - if(count($output->left_tables)){ - $buff .= '$output->left_tables = array( '; - foreach($output->left_tables as $key => $val) { - $buff .= sprintf('"%s"=>"%s",', $key, $val); - } - $buff .= ' );'."\n"; - } - - // column 정리 - if($column_count) { - $buff .= '$output->columns = array ( '; - $buff .= $this->_getColumn($output->columns); - $buff .= ' );'."\n"; - } - - // conditions 정리 - if($condition_count) { - $buff .= '$output->conditions = array ( '; - $buff .= $this->_getConditions($output->conditions); - $buff .= ' );'."\n"; - } - - // conditions 정리 - if(count($output->left_conditions)) { - $buff .= '$output->left_conditions = array ( '; - foreach($output->left_conditions as $key => $val){ - $buff .= "'{$key}' => array ( "; - $buff .= $this->_getConditions($val); - $buff .= "),\n"; - } - $buff .= ' );'."\n"; - } - - // args 변수 확인 - $arg_list = $this->getArguments(); - if($arg_list) - { - foreach($arg_list as $arg) - { - $pre_buff .= 'if(is_object($args->'.$arg.')){ $args->'.$arg.' = array_values(get_method_vars($args->'.$arg.')); }'. "\n"; - $pre_buff .= 'if(is_array($args->'.$arg.') && count($args->'.$arg.')==0){ unset($args->'.$arg.'); };'."\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"); - } - - // 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($this->filter_list)) { - foreach($this->filter_list as $key => $val) { - $pre_buff .= sprintf('if(isset($args->%s)) { unset($_output); $_output = $this->checkFilter("%s",$args->%s,"%s"); if(!$_output->toBool()) return $_output; } %s',$val->var, $val->var,$val->var,$val->filter,"\n"); - } - } - - // default check - if(count($this->default_list)) { - foreach($this->default_list as $key => $val) { - $pre_buff .= 'if(!isset($args->'.$key.')) $args->'.$key.' = '.$val.';'."\n"; - } - } - - // not null check - if(count($this->notnull_list)) { - foreach($this->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"; - } - } - - $buff = "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 transfer given column information to object->columns - * @param[in] column information - * @result Returns $object - */ - - function _setColumn($columns){ - 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, - "click_count" => $val->attrs->click_count, - ); - } - } - return $output; - } - - /** - * @brief transfer condition information to $object->conditions - * @param[in] SQL condition information - * @result Returns $output - */ - function _setConditions($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; - } - } - return $output; - } - - /** - * @brief transfer condition information to $object->groups - * @param[in] SQL group information - * @result Returns $output - */ - function _setGroup($group_list){ - // group 정리 - - if($group_list) { - if(!is_array($group_list)) $group_list = array($group_list); - for($i=0;$iattrs->column); - if(!$column) continue; - $group_column_list[] = $column; - } - if(count($group_column_list)) $output->groups = $group_column_list; - } - return $output; - } - - - /** - * @brief transfer pagnation information to $output - * @param[in] $xml_obj xml object containing Navigation information - * @result Returns $output - */ - function _setNavigation($xml_obj){ - $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 ; - } - return $output; - } - - /** - * @brief retrieve column information from $output->colums to generate corresponding php code - * @param[in] $column - * @remarks the name of this method is misleading. - * @result Returns string buffer containing php code - */ - function _getColumn($columns){ - $buff = ''; - $str = ''; - $print_vars = array(); - - foreach($columns as $key => $val) { - $str = 'array("name"=>"%s","alias"=>"%s"'; - $print_vars = array(); - $print_vars[] = $val['name']; - $print_vars[] = $val['alias']; - - $val['default'] = $this->getDefault($val['name'], $val['default']); - if($val['var'] && strpos($val['var'],'.')===false) { - - if($val['default']){ - $str .= ',"value"=>$args->%s?$args->%s:%s'; - $print_vars[] = $val['var']; - $print_vars[] = $val['var']; - $print_vars[] = $val['default']; - }else{ - $str .= ',"value"=>$args->%s'; - $print_vars[] = $val['var']; - } - - } else { - if($val['default']){ - $str .= ',"value"=>%s'; - $print_vars[] = $val['default']; - } - } - - if($val['click_count']){ - $str .= ',"click_count"=>$args->%s'; - $print_vars[] = $val['click_count']; - } - - $str .= '),%s'; - $print_vars[] = "\n"; - - $buff .= vsprintf($str, $print_vars); - } - return $buff; - } - - /** - * @brief retrieve condition information from $output->condition to generate corresponding php code - * @param[in] $conditions array containing Query conditions - * @remarks the name of this method is misleading. - * @return Returns string buffer containing php code - */ - function _getConditions($conditions){ - $buff = ''; - foreach($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) $this->default_list[$v->var] = $v->default; - if($v->filter) $this->filter_list[] = $v; - if($v->notnull) $this->notnull_list[] = $v->var; - 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"); - - $this->addArguments($v->var); - } 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"; - } - return $buff; - } - - function addArguments($args_name) - { - $this->args[] = $args_name; - } - - function getArguments() - { - return $this->args; - } - - /** - * @brief returns predefined default values correspoding to given parameters - * @param[in] $name - * @param[in] $value - * @return Returns a default value for specified field - */ - function getDefault($name, $value) { - $db_info = Context::getDBInfo (); - if(!isset($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); - if ($db_info->db_type == 'cubrid') { - $val = sprintf ('"\\"%s\\"+%d"', $name, $args); - } else { - $val = sprintf('"%s+%d"', $name, $args); - } - break; - case 'minus' : - $args = abs($args); - if ($db_info->db_type == 'cubrid') { - $val = sprintf ('"\\"%s\\"-%d"', $name, $args); - } else { - $val = sprintf('"%s-%d"', $name, $args); - } - break; - case 'multiply' : - $args = intval($args); - if ($db_info->db_type == 'cubrid') { - $val = sprintf ('"\\"%s\\"*%d"', $name, $args); - } else { - $val = sprintf('"%s*%d"', $name, $args); - } - break; - } - - return $val; - } - } -?> +query->attrs->action); + if(!$action) return; + + // 테이블 정리 (배열코드로 변환) + $tables = $xml_obj->query->tables->table; + $output->left_tables = array(); + + $left_conditions = array(); + + 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[$alias] = $table_name; + + if(in_array($val->attrs->type,array('left join','left outer join','right join','right outer join')) && count($val->conditions)){ + $output->left_tables[$alias] = $val->attrs->type; + $left_conditions[$alias] = $val->conditions; + } + + // 테이블을 찾아서 컬럼의 속성을 구함 + $table_file = sprintf('%s%s/%s/schemas/%s.xml', _XE_PATH_, 'modules', $module, $table_name); + if(!file_exists($table_file)) { + $searched_list = FileHandler::readDir(_XE_PATH_.'modules'); + $searched_count = count($searched_list); + for($i=0;$i<$searched_count;$i++) { + $table_file = sprintf('%s%s/%s/schemas/%s.xml', _XE_PATH_, 'modules', $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) { + if(isset($table_obj->table->column) && !is_array($table_obj->table->column)) + { + $table_obj->table->column = array($table_obj->table->column); + } + + 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; + $out = $this->_setColumn($columns); + $output->columns = $out->columns; + + $conditions = $xml_obj->query->conditions; + $out = $this->_setConditions($conditions); + $output->conditions = $out->conditions; + + foreach($output->left_tables as $key => $val){ + if($left_conditions[$key]){ + $out = $this->_setConditions($left_conditions[$key]); + $output->left_conditions[$key] = $out->conditions; + } + } + + $group_list = $xml_obj->query->groups->group; + $out = $this->_setGroup($group_list); + $output->groups = $out->groups; + + // 네비게이션 정리 + $out = $this->_setNavigation($xml_obj); + $output->order = $out->order; + $output->list_count = $out->list_count; + $output->page_count = $out->page_count; + $output->page = $out->page; + + $column_count = count($output->columns); + $condition_count = count($output->conditions); + + $buff .= '$output->tables = array( '; + foreach($output->tables as $key => $val) { + if(!array_key_exists($key,$output->left_tables)){ + $buff .= sprintf('"%s"=>"%s",', $key, $val); + } + } + $buff .= ' );'."\n"; + + // php script 생성 + $buff .= '$output->_tables = array( '; + foreach($output->tables as $key => $val) { + $buff .= sprintf('"%s"=>"%s",', $key, $val); + } + $buff .= ' );'."\n"; + + if(count($output->left_tables)){ + $buff .= '$output->left_tables = array( '; + foreach($output->left_tables as $key => $val) { + $buff .= sprintf('"%s"=>"%s",', $key, $val); + } + $buff .= ' );'."\n"; + } + + // column 정리 + if($column_count) { + $buff .= '$output->columns = array ( '; + $buff .= $this->_getColumn($output->columns); + $buff .= ' );'."\n"; + } + + // conditions 정리 + if($condition_count) { + $buff .= '$output->conditions = array ( '; + $buff .= $this->_getConditions($output->conditions); + $buff .= ' );'."\n"; + } + + // conditions 정리 + if(count($output->left_conditions)) { + $buff .= '$output->left_conditions = array ( '; + foreach($output->left_conditions as $key => $val){ + $buff .= "'{$key}' => array ( "; + $buff .= $this->_getConditions($val); + $buff .= "),\n"; + } + $buff .= ' );'."\n"; + } + + // args 변수 확인 + $arg_list = $this->getArguments(); + if($arg_list) + { + foreach($arg_list as $arg) + { + $pre_buff .= 'if(is_object($args->'.$arg.')){ $args->'.$arg.' = array_values(get_method_vars($args->'.$arg.')); }'. "\n"; + $pre_buff .= 'if(is_array($args->'.$arg.') && count($args->'.$arg.')==0){ unset($args->'.$arg.'); };'."\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->page_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"); + } + + // 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($this->filter_list)) { + foreach($this->filter_list as $key => $val) { + $pre_buff .= sprintf('if(isset($args->%s)) { unset($_output); $_output = $this->checkFilter("%s",$args->%s,"%s"); if(!$_output->toBool()) return $_output; } %s',$val->var, $val->var,$val->var,$val->filter,"\n"); + } + } + + // default check + if(count($this->default_list)) { + foreach($this->default_list as $key => $val) { + $pre_buff .= 'if(!isset($args->'.$key.')) $args->'.$key.' = '.$val.';'."\n"; + } + } + + // not null check + if(count($this->notnull_list)) { + foreach($this->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"; + } + } + + $buff = "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 transfer given column information to object->columns + * @param[in] column information + * @result Returns $object + */ + + function _setColumn($columns){ + 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, + "click_count" => $val->attrs->click_count, + ); + } + } + return $output; + } + + /** + * @brief transfer condition information to $object->conditions + * @param[in] SQL condition information + * @result Returns $output + */ + function _setConditions($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; + } + } + return $output; + } + + /** + * @brief transfer condition information to $object->groups + * @param[in] SQL group information + * @result Returns $output + */ + function _setGroup($group_list){ + // group 정리 + + if($group_list) { + if(!is_array($group_list)) $group_list = array($group_list); + for($i=0;$iattrs->column); + if(!$column) continue; + $group_column_list[] = $column; + } + if(count($group_column_list)) $output->groups = $group_column_list; + } + return $output; + } + + + /** + * @brief transfer pagnation information to $output + * @param[in] $xml_obj xml object containing Navigation information + * @result Returns $output + */ + function _setNavigation($xml_obj){ + $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 ; + } + return $output; + } + + /** + * @brief retrieve column information from $output->colums to generate corresponding php code + * @param[in] $column + * @remarks the name of this method is misleading. + * @result Returns string buffer containing php code + */ + function _getColumn($columns){ + $buff = ''; + $str = ''; + $print_vars = array(); + + foreach($columns as $key => $val) { + $str = 'array("name"=>"%s","alias"=>"%s"'; + $print_vars = array(); + $print_vars[] = $val['name']; + $print_vars[] = $val['alias']; + + $val['default'] = $this->getDefault($val['name'], $val['default']); + if($val['var'] && strpos($val['var'],'.')===false) { + + if($val['default']){ + $str .= ',"value"=>$args->%s?$args->%s:%s'; + $print_vars[] = $val['var']; + $print_vars[] = $val['var']; + $print_vars[] = $val['default']; + }else{ + $str .= ',"value"=>$args->%s'; + $print_vars[] = $val['var']; + } + + } else { + if($val['default']){ + $str .= ',"value"=>%s'; + $print_vars[] = $val['default']; + } + } + + if($val['click_count']){ + $str .= ',"click_count"=>$args->%s'; + $print_vars[] = $val['click_count']; + } + + $str .= '),%s'; + $print_vars[] = "\n"; + + $buff .= vsprintf($str, $print_vars); + } + return $buff; + } + + /** + * @brief retrieve condition information from $output->condition to generate corresponding php code + * @param[in] $conditions array containing Query conditions + * @remarks the name of this method is misleading. + * @return Returns string buffer containing php code + */ + function _getConditions($conditions){ + $buff = ''; + foreach($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) $this->default_list[$v->var] = $v->default; + if($v->filter) $this->filter_list[] = $v; + if($v->notnull) $this->notnull_list[] = $v->var; + 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"); + + $this->addArguments($v->var); + } 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"; + } + return $buff; + } + + function addArguments($args_name) + { + $this->args[] = $args_name; + } + + function getArguments() + { + return $this->args; + } + + /** + * @brief returns predefined default values correspoding to given parameters + * @param[in] $name + * @param[in] $value + * @return Returns a default value for specified field + */ + function getDefault($name, $value) { + $db_info = Context::getDBInfo (); + if(!isset($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); + if ($db_info->db_type == 'cubrid') { + $val = sprintf ('"\\"%s\\"+%d"', $name, $args); + } else { + $val = sprintf('"%s+%d"', $name, $args); + } + break; + case 'minus' : + $args = abs($args); + if ($db_info->db_type == 'cubrid') { + $val = sprintf ('"\\"%s\\"-%d"', $name, $args); + } else { + $val = sprintf('"%s-%d"', $name, $args); + } + break; + case 'multiply' : + $args = intval($args); + if ($db_info->db_type == 'cubrid') { + $val = sprintf ('"\\"%s\\"*%d"', $name, $args); + } else { + $val = sprintf('"%s*%d"', $name, $args); + } + break; + } + + return $val; + } + } +?> diff --git a/classes/xml/xmlquery/DBParser.class.php b/classes/xml/xmlquery/DBParser.class.php new file mode 100644 index 000000000..54591f3a2 --- /dev/null +++ b/classes/xml/xmlquery/DBParser.class.php @@ -0,0 +1,110 @@ +escape_char_left = $escape_char_left; + if ($escape_char_right !== "")$this->escape_char_right = $escape_char_right; + else $this->escape_char_right = $escape_char_left; + $this->table_prefix = $table_prefix; + } + + function getEscapeChar($leftOrRight){ + if ($leftOrRight === 'left')return $this->escape_char_left; + else return $this->escape_char_right; + } + + function escape($name){ + return $this->escape_char_left . $name . $this->escape_char_right; + } + + function escapeString($name){ + return "'".$this->escapeStringValue($name)."'"; + } + + function escapeStringValue($value){ + if($value == "*") return $value; + if (is_string($value)) return $value = str_replace("'","''",$value); + return $value; + } + + function parseTableName($name){ + return $this->table_prefix . $name; + } + + function parseColumnName($name){ + return $this->escapeColumn($name); + } + + function escapeColumn($column_name){ + if($this->isUnqualifiedColumnName($column_name)) + return $this->escape($column_name); + if($this->isQualifiedColumnName($column_name)){ + list($table, $column) = explode('.', $column_name); + // $table can also be an alias, so the prefix should not be added + return $this->escape($table).'.'.$this->escape($column); + //return $this->escape($this->parseTableName($table)).'.'.$this->escape($column); + } + } + + function isUnqualifiedColumnName($column_name){ + if(strpos($column_name,'.')===false && strpos($column_name,'(')===false) return true; + return false; + } + + function isQualifiedColumnName($column_name){ + if(strpos($column_name,'.')!==false && strpos($column_name,'(')===false) return true; + return false; + } + + function parseExpression($column_name){ + $functions = preg_split('/([\+\-\*\/\ ])/', $column_name, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY); + foreach($functions as $k => $v){ + $function = &$functions[$k]; + if(strlen($function)==1) continue; // skip delimiters + $pos = strrpos("(", $function); + $matches = preg_split('/([a-zA-Z0-9_*]+)/', $function, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY); + $total_brackets = substr_count($function, "("); + $brackets = 0; + foreach($matches as $i => $j){ + $match = &$matches[$i]; + if($match == '(') {$brackets++; continue;} + if(strpos($match,')') !== false) continue; + if(in_array($match, array(',', '.'))) continue; + if($brackets == $total_brackets){ + if(!is_numeric($match)) { + $match = $this->escapeColumnExpression($match); + } + } + } + $function = implode('', $matches); + } + return implode('', $functions); + } + + function isStar($column_name){ + if(substr($column_name,-1) == '*') return true; + return false; + } + + /* + * Checks to see if expression is an aggregate star function + * like count(*) + */ + function isStarFunction($column_name){ + if(strpos($column_name, "(*)")!==false) return true; + return false; + } + + function escapeColumnExpression($column_name){ + if($this->isStar($column_name)) return $column_name; + if($this->isStarFunction($column_name)){ + return $column_name; + } + return $this->escapeColumn($column_name); + } + } + + \ No newline at end of file diff --git a/classes/xml/xmlquery/QueryParser.class.php b/classes/xml/xmlquery/QueryParser.class.php new file mode 100644 index 000000000..2f1a3f20c --- /dev/null +++ b/classes/xml/xmlquery/QueryParser.class.php @@ -0,0 +1,65 @@ +queryTag = new QueryTag($query, $isSubQuery); + } + + function getTableInfo($query_id, $table_name) { + $column_type = array(); + + $id_args = explode('.', $query_id); + if (count($id_args) == 2) { + $target = 'modules'; + $module = $id_args[0]; + $id = $id_args[1]; + } elseif (count($id_args) == 3) { + $target = $id_args[0]; + if (!in_array($target, array('modules', 'addons', 'widgets'))) + return; + $module = $id_args[1]; + $id = $id_args[2]; + } + + // get column properties from the table + $table_file = sprintf('%s%s/%s/schemas/%s.xml', _XE_PATH_, 'modules', $module, $table_name); + if (!file_exists($table_file)) { + $searched_list = FileHandler::readDir(_XE_PATH_ . 'modules'); + $searched_count = count($searched_list); + for ($i = 0; $i < $searched_count; $i++) { + $table_file = sprintf('%s%s/%s/schemas/%s.xml', _XE_PATH_, 'modules', $searched_list[$i], $table_name); + if (file_exists($table_file)) + break; + } + } + + if (file_exists($table_file)) { + $table_xml = FileHandler::readFile($table_file); + $xml_parser = new XmlParser(); + $table_obj = $xml_parser->parse($table_xml); + if ($table_obj->table) { + if (isset($table_obj->table->column) && !is_array($table_obj->table->column)) { + $table_obj->table->column = array($table_obj->table->column); + } + + foreach ($table_obj->table->column as $k => $v) { + $column_type[$v->attrs->name] = $v->attrs->type; + } + } + } + + return $column_type; + } + + function toString() { + return "queryTag->toString() + . 'return $query; ?>'; + } + + } + +?> \ No newline at end of file diff --git a/classes/xml/xmlquery/argument/Argument.class.php b/classes/xml/xmlquery/argument/Argument.class.php new file mode 100644 index 000000000..69d0f2800 --- /dev/null +++ b/classes/xml/xmlquery/argument/Argument.class.php @@ -0,0 +1,186 @@ +value = $value; + $this->name = $name; + $this->isValid = true; + } + + function getType(){ + if(isset($this->type)) return $this->type; + if(is_string($this->value)) return 'column_name'; + return 'number'; + } + + function setColumnType($value){ + $this->type = $value; + } + + function setColumnOperation($operation){ + $this->column_operation = $operation; + } + + function getName(){ + return $this->name; + } + + function getValue(){ + if(!isset($this->_value)){ + $value = $this->getEscapedValue(); + $this->_value = $this->toString($value); + } + return $this->_value; + } + + function getColumnOperation(){ + return $this->column_operation; + } + + function getEscapedValue(){ + return $this->escapeValue($this->value); + } + + function getUnescapedValue(){ + return $this->value; + } + + function toString($value){ + if(is_array($value)){ + if(count($value) === 0) return ''; + if(count($value) === 1 && $value[0] === '') return ''; + return '('.implode(',', $value).')'; + } + return $value; + } + + function escapeValue($value){ + if($this->getType() == 'column_name'){ + $dbParser = DB::getParser(); + return $dbParser->parseExpression($value); + } + if(!isset($value)) return null; + if(in_array($this->getType(), array('date', 'varchar', 'char','text', 'bigtext'))){ + if(!is_array($value)) + $value = $this->_escapeStringValue ($value); + else { + $total = count($value); + for($i = 0; $i < $total; $i++) + $value[$i] = $this->_escapeStringValue($value[$i]); + //$value[$i] = '\''.$value[$i].'\''; + } + } + return $value; + } + + function _escapeStringValue($value){ + $db = &DB::getInstance(); + $value = $db->addQuotes($value); + return '\''.$value.'\''; + + } + + function isValid(){ + return $this->isValid; + } + + function getErrorMessage(){ + return $this->errorMessage; + } + + function ensureDefaultValue($default_value){ + if(!isset($this->value) || $this->value == '') + $this->value = $default_value; + } + + function checkFilter($filter_type){ + if(isset($this->value) && $this->value != ''){ + global $lang; + $val = $this->value; + $key = $this->name; + switch($filter_type) { + case 'email' : + case 'email_address' : + if(!preg_match('/^[_0-9a-z-]+(\.[_0-9a-z-]+)*@[0-9a-z-]+(\.[0-9a-z-]+)*$/is', $val)) { + $this->isValid = false; + $this->errorMessage = new Object(-1, sprintf($lang->filter->invalid_email, $lang->{$key} ? $lang->{$key} : $key)); + } + break; + case 'homepage' : + if(!preg_match('/^(http|https)+(:\/\/)+[0-9a-z_-]+\.[^ ]+$/is', $val)) { + $this->isValid = false; + $this->errorMessage = new Object(-1, sprintf($lang->filter->invalid_homepage, $lang->{$key} ? $lang->{$key} : $key)); + } + break; + case 'userid' : + case 'user_id' : + if(!preg_match('/^[a-zA-Z]+([_0-9a-zA-Z]+)*$/is', $val)) { + $this->isValid = false; + $this->errorMessage = new Object(-1, sprintf($lang->filter->invalid_userid, $lang->{$key} ? $lang->{$key} : $key)); + } + break; + case 'number' : + case 'numbers' : + if(is_array($val)) $val = join(',', $val); + if(!preg_match('/^(-?)[0-9]+(,\-?[0-9]+)*$/is', $val)){ + $this->isValid = false; + $this->errorMessage = new Object(-1, sprintf($lang->filter->invalid_number, $lang->{$key} ? $lang->{$key} : $key)); + } + break; + case 'alpha' : + if(!preg_match('/^[a-z]+$/is', $val)) { + $this->isValid = false; + $this->errorMessage = new Object(-1, sprintf($lang->filter->invalid_alpha, $lang->{$key} ? $lang->{$key} : $key)); + } + break; + case 'alpha_number' : + if(!preg_match('/^[0-9a-z]+$/is', $val)) { + $this->isValid = false; + $this->errorMessage = new Object(-1, sprintf($lang->filter->invalid_alpha_number, $lang->{$key} ? $lang->{$key} : $key)); + } + break; + } + } + } + + function checkMaxLength($length){ + if($this->value && (strlen($this->value) > $length)){ + global $lang; + $this->isValid = false; + $key = $this->name; + $this->errorMessage = new Object(-1, sprintf($lang->filter->outofrange, $lang->{$key} ? $lang->{$key} : $key)); + } + } + + function checkMinLength($length){ + if($this->value && (strlen($this->value) < $length)){ + global $lang; + $this->isValid = false; + $key = $this->name; + $this->errorMessage = new Object(-1, sprintf($lang->filter->outofrange, $lang->{$key} ? $lang->{$key} : $key)); + } + } + + function checkNotNull(){ + if(!isset($this->value)){ + global $lang; + $this->isValid = false; + $key = $this->name; + $this->errorMessage = new Object(-1, sprintf($lang->filter->isnull, $lang->{$key} ? $lang->{$key} : $key)); + } + } + } + + +?> \ No newline at end of file diff --git a/classes/xml/xmlquery/argument/ConditionArgument.class.php b/classes/xml/xmlquery/argument/ConditionArgument.class.php new file mode 100644 index 000000000..cdbebfdf0 --- /dev/null +++ b/classes/xml/xmlquery/argument/ConditionArgument.class.php @@ -0,0 +1,76 @@ +operation = $operation; + } + + function createConditionValue(){ + if(!isset($this->value)) return; + + $name = $this->column_name; + $operation = $this->operation; + $value = $this->value; + + switch($operation) { + case 'like_prefix' : + $this->value = $value.'%'; + break; + case 'like_tail' : + $this->value = '%'.$value; + break; + case 'like' : + $this->value = '%'.$value.'%'; + break; + case 'notlike' : + $this->value = '%'.$value.'%'; + break; + case 'notlike_prefix' : + $this->value = $value.'%'; + break; + case 'notlike_tail' : + $this->value = '%'.$value; + break; + case 'in': + if(!is_array($value)) $this->value = array($value); + break; + case 'notin': + if(!is_array($value)) $this->value = array($value); + break; + } + } + + /** + * Since ConditionArgument is used in WHERE clause, + * where the argument value is compared to a table column, + * it is assumed that all arguments have type. There are cases though + * where the column does not have any type - if it was removed from + * the XML schema for example - see the is_secret column in xe_documents table. + * In this case, the column type is retrieved according to argument + * value type (using the PHP function is_numeric). + * + * @return type string + */ + function getType(){ + return $this->type ? $this->type : (!is_numeric($this->value) ? "varchar" : ""); + } + + function setColumnType($column_type){ + if(!isset($this->value)) return; + if($column_type === '') return; + + $this->type = $column_type; + } + + } + +?> \ No newline at end of file diff --git a/classes/xml/xmlquery/argument/SortArgument.class.php b/classes/xml/xmlquery/argument/SortArgument.class.php new file mode 100644 index 000000000..639b10a06 --- /dev/null +++ b/classes/xml/xmlquery/argument/SortArgument.class.php @@ -0,0 +1,11 @@ +getUnescapedValue(); + } + + } + +?> diff --git a/classes/xml/xmlquery/queryargument/DefaultValue.class.php b/classes/xml/xmlquery/queryargument/DefaultValue.class.php new file mode 100644 index 000000000..05f2bd69b --- /dev/null +++ b/classes/xml/xmlquery/queryargument/DefaultValue.class.php @@ -0,0 +1,110 @@ +column_name = $dbParser->parseColumnName($column_name); + $this->value = $value; + $this->value = $this->_setValue(); + } + + function isString(){ + return $this->_is_string; + $str_pos = strpos($this->value, '('); + if($str_pos===false) return true; + return false; + } + + function isStringFromFunction(){ + return $this->_is_string_from_function; + } + + function isSequence(){ + return $this->is_sequence; + } + + function isOperation(){ + return $this->is_operation; + } + + function getOperation(){ + return $this->operation; + } + + function _setValue(){ + if(!isset($this->value)) return; + + // If value contains comma separated values and does not contain paranthesis + // -> default value is an array + if(strpos($this->value, ',') !== false && strpos($this->value, '(') === false) { + return sprintf('array(%s)', $this->value); + } + + $str_pos = strpos($this->value, '('); + // // TODO Replace this with parseExpression + if($str_pos===false) { + $this->_is_string = true; + return '\''.$this->value.'\''; + } + //if($str_pos===false) return $this->value; + + $func_name = substr($this->value, 0, $str_pos); + $args = substr($this->value, $str_pos+1, strlen($value)-1); + + switch($func_name) { + case 'ipaddress' : + $val = '$_SERVER[\'REMOTE_ADDR\']'; + $this->_is_string_from_function = true; + break; + case 'unixtime' : + $val = 'time()'; + break; + case 'curdate' : + $val = 'date("YmdHis")'; + $this->_is_string_from_function = true; + break; + case 'sequence' : + $this->is_sequence = true; + $val = '$sequence'; + break; + case 'plus' : + $args = abs($args); + $this->is_operation = true; + $this->operation = '+'; + $val = sprintf('%d', $args); + break; + case 'minus' : + $args = abs($args); + $this->is_operation = true; + $this->operation = '-'; + $val = sprintf('%d', $args); + break; + case 'multiply' : + $args = intval($args); + $this->is_operation = true; + $this->operation = '*'; + $val = sprintf('%d', $args); + break; + default : + $val = '\'' . $this->value . '\''; + //$val = $this->value; + } + + return $val; + } + + function toString(){ + return $this->value; + } + } + +?> \ No newline at end of file diff --git a/classes/xml/xmlquery/queryargument/QueryArgument.class.php b/classes/xml/xmlquery/queryargument/QueryArgument.class.php new file mode 100644 index 000000000..e4082403f --- /dev/null +++ b/classes/xml/xmlquery/queryargument/QueryArgument.class.php @@ -0,0 +1,104 @@ +argument_name = $tag->attrs->var; + if(!$this->argument_name) $this->argument_name = str_replace('.', '_',$tag->attrs->name); + if(!$this->argument_name) $this->argument_name = str_replace('.', '_',$tag->attrs->column); + + $this->variable_name = $this->argument_name; + + $number_of_arguments++; + $this->argument_name .= $number_of_arguments; + + $name = $tag->attrs->name; + if(!$name) $name = $tag->attrs->column; + if(strpos($name, '.') === false) $this->column_name = $name; + else { + list($prefix, $name) = explode('.', $name); + $this->column_name = $name; + } + + if($tag->attrs->operation) $this->operation = $tag->attrs->operation; + + $this->argument_validator = new QueryArgumentValidator($tag, $this); + $this->ignore_value = $ignore_value; + } + + function getArgumentName(){ + return $this->argument_name; + } + + function getColumnName(){ + return $this->column_name; + } + + function getValidatorString(){ + return $this->argument_validator->toString(); + } + + function isConditionArgument(){ + if($this->operation) return true; + return false; + } + + function toString(){ + if($this->isConditionArgument()){ + // Instantiation + $arg = sprintf("\n$%s_argument = new ConditionArgument('%s', %s, '%s');\n" + , $this->argument_name + , $this->variable_name + , '$args->'.$this->variable_name + , $this->operation + ); + // Call methods to validate argument and ensure default value + $arg .= $this->argument_validator->toString(); + + // Prepare condition string + $arg .= sprintf("$%s_argument->createConditionValue();\n" + , $this->argument_name + ); + + // Check that argument passed validation, else return + $arg .= sprintf("if(!$%s_argument->isValid()) return $%s_argument->getErrorMessage();\n" + , $this->argument_name + , $this->argument_name + ); + } + else { + $arg = sprintf("\n$%s_argument = new Argument('%s', %s);\n" + , $this->argument_name + , $this->variable_name + , $this->ignore_value ? 'null' : '$args->'.$this->variable_name); + + $arg .= $this->argument_validator->toString(); + + $arg .= sprintf("if(!$%s_argument->isValid()) return $%s_argument->getErrorMessage();\n" + , $this->argument_name + , $this->argument_name + ); + } + + // If the argument is null, skip it + if($this->argument_validator->isIgnorable()){ + $arg = sprintf("if(isset(%s)) {", '$args->'.$this->variable_name) + . $arg + . sprintf("} else \n$%s_argument = null;", $this->argument_name); + } + + return $arg; + } + + } + +?> \ No newline at end of file diff --git a/classes/xml/xmlquery/queryargument/SortQueryArgument.class.php b/classes/xml/xmlquery/queryargument/SortQueryArgument.class.php new file mode 100644 index 000000000..60b068f2d --- /dev/null +++ b/classes/xml/xmlquery/queryargument/SortQueryArgument.class.php @@ -0,0 +1,20 @@ +argument_name + , $this->argument_name + , '$args->'.$this->variable_name); + + + $arg .= $this->argument_validator->toString(); + + $arg .= sprintf("if(!$%s_argument->isValid()) return $%s_argument->getErrorMessage();\n" + , $this->argument_name + , $this->argument_name + ); + return $arg; + } + } +?> diff --git a/classes/xml/xmlquery/queryargument/validator/QueryArgumentValidator.class.php b/classes/xml/xmlquery/queryargument/validator/QueryArgumentValidator.class.php new file mode 100644 index 000000000..e54fce76d --- /dev/null +++ b/classes/xml/xmlquery/queryargument/validator/QueryArgumentValidator.class.php @@ -0,0 +1,73 @@ +argument = $argument; + $this->argument_name = $this->argument->getArgumentName(); + + $this->default_value = $tag->attrs->default; + $this->notnull = $tag->attrs->notnull; + $this->filter = $tag->attrs->filter; + $this->min_length = $tag->attrs->min_length; + $this->max_length = $tag->attrs->max_length; + } + + function isIgnorable(){ + if(isset($this->default_value) || isset($this->notnull)) return false; + return true; + } + + function toString(){ + $validator = ''; + if($this->filter){ + $validator .= sprintf("$%s_argument->checkFilter('%s');\n" + , $this->argument_name + , $this->filter + ); + } + if($this->min_length){ + $validator .= sprintf("$%s_argument->checkMinLength(%s);\n" + , $this->argument_name + , $this->min_length + ); + } + if($this->max_length){ + $validator .= sprintf("$%s_argument->checkMaxLength(%s);\n" + , $this->argument_name + , $this->max_length + ); + } + if(isset($this->default_value)){ + $this->default_value = new DefaultValue($this->argument_name, $this->default_value); + if($this->default_value->isSequence()) + $validator .= '$db = &DB::getInstance(); $sequence = $db->getNextSequence(); '; + if($this->default_value->isOperation()) + $validator .= sprintf("$%s_argument->setColumnOperation('%s');\n" + , $this->argument_name + , $this->default_value->getOperation() + ); + $validator .= sprintf("$%s_argument->ensureDefaultValue(%s);\n" + , $this->argument_name + , $this->default_value->toString() + ); + } + if($this->notnull){ + $validator .= sprintf("$%s_argument->checkNotNull();\n" + , $this->argument_name + ); + } + return $validator; + } + } + +?> \ No newline at end of file diff --git a/classes/xml/xmlquery/tags/column/ColumnTag.class.php b/classes/xml/xmlquery/tags/column/ColumnTag.class.php new file mode 100644 index 000000000..8ffe77373 --- /dev/null +++ b/classes/xml/xmlquery/tags/column/ColumnTag.class.php @@ -0,0 +1,19 @@ + tag inside an XML Query file + * + * Since the tag supports different attributes depending on + * the type of query (select, update, insert, delete) this is only + * the base class for the classes that will model each type tag. + * + **/ + + class ColumnTag { + var $name; + + function ColumnTag($name){ + $this->name = $name; + } + } \ No newline at end of file diff --git a/classes/xml/xmlquery/tags/column/InsertColumnTag.class.php b/classes/xml/xmlquery/tags/column/InsertColumnTag.class.php new file mode 100644 index 000000000..586125f42 --- /dev/null +++ b/classes/xml/xmlquery/tags/column/InsertColumnTag.class.php @@ -0,0 +1,31 @@ + tag inside an XML Query file whose action is 'insert' + * + **/ + + + class InsertColumnTag extends ColumnTag { + var $argument; + + function InsertColumnTag($column) { + parent::ColumnTag($column->attrs->name); + $dbParser = DB::getParser(); + $this->name = $dbParser->parseColumnName($this->name); + $this->argument = new QueryArgument($column); + } + + function getExpressionString(){ + return sprintf('new InsertExpression(\'%s\', $%s_argument)' + , $this->name + , $this->argument->argument_name); + } + + function getArgument(){ + return $this->argument; + } + + } +?> \ No newline at end of file diff --git a/classes/xml/xmlquery/tags/column/InsertColumnsTag.class.php b/classes/xml/xmlquery/tags/column/InsertColumnsTag.class.php new file mode 100644 index 000000000..88ed19ccb --- /dev/null +++ b/classes/xml/xmlquery/tags/column/InsertColumnsTag.class.php @@ -0,0 +1,46 @@ + tag inside an XML Query file whose action is 'insert' + * + **/ + + class InsertColumnsTag{ + var $columns; + + function InsertColumnsTag($xml_columns) { + $this->columns = array(); + + if(!$xml_columns) + return; + + if(!is_array($xml_columns)) $xml_columns = array($xml_columns); + + foreach($xml_columns as $column){ + if($column->name === 'query') $this->columns[] = new QueryTag($column, true); + else $this->columns[] = new InsertColumnTag($column); + } + } + + function toString(){ + $output_columns = 'array(' . PHP_EOL; + foreach($this->columns as $column){ + $output_columns .= $column->getExpressionString() . PHP_EOL . ','; + } + $output_columns = substr($output_columns, 0, -1); + $output_columns .= ')'; + return $output_columns; + } + + function getArguments(){ + $arguments = array(); + foreach($this->columns as $column){ + $arguments[] = $column->getArgument(); + } + return $arguments; + } + + } + +?> \ No newline at end of file diff --git a/classes/xml/xmlquery/tags/column/SelectColumnTag.class.php b/classes/xml/xmlquery/tags/column/SelectColumnTag.class.php new file mode 100644 index 000000000..53012641c --- /dev/null +++ b/classes/xml/xmlquery/tags/column/SelectColumnTag.class.php @@ -0,0 +1,34 @@ + tag inside an XML Query file whose action is 'select' + * + **/ + + class SelectColumnTag extends ColumnTag{ + var $alias; + var $click_count; + + function SelectColumnTag($column){ + parent::ColumnTag($column->attrs->name); + if(!$this->name) $this->name = "*"; + if($this->name != "*") { + $dbParser = DB::getParser(); + $this->name = $dbParser->parseExpression($this->name); + } + + $this->alias = $column->attrs->alias; + $this->click_count = $column->attrs->click_count; + } + + function getExpressionString(){ + if($this->name == '*') return "new StarExpression()"; + if($this->click_count) + return sprintf('new ClickCountExpression(%s, %s, $args->%s)', $this->name, $this->alias,$this->click_count); + $dbParser = DB::getParser(); + return sprintf('new SelectExpression(\'%s\'%s)', $this->name, $this->alias ? ', \''.$dbParser->escape($this->alias) .'\'': ''); + } + } +?> \ No newline at end of file diff --git a/classes/xml/xmlquery/tags/column/SelectColumnsTag.class.php b/classes/xml/xmlquery/tags/column/SelectColumnsTag.class.php new file mode 100644 index 000000000..404107a2f --- /dev/null +++ b/classes/xml/xmlquery/tags/column/SelectColumnsTag.class.php @@ -0,0 +1,57 @@ +column; + $xml_queries = $xml_columns_tag->query; + + $this->columns = array(); + + if(!$xml_columns) { + $this->columns[] = new SelectColumnTag("*"); + return; + } + + if(!is_array($xml_columns)) $xml_columns = array($xml_columns); + + foreach($xml_columns as $column){ + $this->columns[] = new SelectColumnTag($column); + } + + + if(!$xml_queries) { + return; + } + + if(!is_array($xml_queries)) $xml_queries = array($xml_queries); + + foreach($xml_queries as $column){ + $this->columns[] = new QueryTag($column, true); + } + } + + function toString(){ + $output_columns = 'array(' . PHP_EOL; + foreach($this->columns as $column){ + if(is_a($column, 'QueryTag')) + $output_columns .= $column->toString() . PHP_EOL . ','; + else + $output_columns .= $column->getExpressionString() . PHP_EOL . ','; + } + $output_columns = substr($output_columns, 0, -1); + $output_columns .= ')'; + return $output_columns; + } + + function getArguments(){ + $arguments = array(); + foreach($this->columns as $column){ + if(is_a($column, 'QueryTag')) + $arguments = array_merge($arguments, $column->getArguments()); + } + return $arguments; + } + } +?> \ No newline at end of file diff --git a/classes/xml/xmlquery/tags/column/UpdateColumnTag.class.php b/classes/xml/xmlquery/tags/column/UpdateColumnTag.class.php new file mode 100644 index 000000000..3ba648727 --- /dev/null +++ b/classes/xml/xmlquery/tags/column/UpdateColumnTag.class.php @@ -0,0 +1,62 @@ + tag inside an XML Query file whose action is 'update' + * + **/ + + + + class UpdateColumnTag extends ColumnTag { + var $argument; + var $default_value; + + function UpdateColumnTag($column) { + parent::ColumnTag($column->attrs->name); + $dbParser = DB::getParser(); + $this->name = $dbParser->parseColumnName($this->name); + if($column->attrs->var) + $this->argument = new QueryArgument($column); + else { + if(strpos($column->attrs->default, '.') !== false) + $this->default_value = "'" . $dbParser->parseColumnName($column->attrs->default) . "'"; + else { + $default_value = new DefaultValue($this->name, $column->attrs->default); + if($default_value->isOperation()) + $this->argument = new QueryArgument($column, true); + //else $this->default_value = $dbParser->parseColumnName($column->attrs->default); + else { + $this->default_value = $default_value->toString(); + if($default_value->isStringFromFunction()){ + $this->default_value = '"\'".' . $this->default_value . '."\'"'; + } + if($default_value->isString()){ + $this->default_value = '"' . $this->default_value . '"'; + } + } + + + } + } + } + + function getExpressionString(){ + if($this->argument) + return sprintf('new UpdateExpression(\'%s\', $%s_argument)' + , $this->name + , $this->argument->argument_name); + else { + return sprintf('new UpdateExpressionWithoutArgument(\'%s\', %s)' + , $this->name + , $this->default_value); + } + } + + function getArgument(){ + return $this->argument; + } + } + +?> \ No newline at end of file diff --git a/classes/xml/xmlquery/tags/column/UpdateColumnsTag.class.php b/classes/xml/xmlquery/tags/column/UpdateColumnsTag.class.php new file mode 100644 index 000000000..eb6ca94ea --- /dev/null +++ b/classes/xml/xmlquery/tags/column/UpdateColumnsTag.class.php @@ -0,0 +1,44 @@ + tag inside an XML Query file whose action is 'update' + * + **/ + + class UpdateColumnsTag{ + var $columns; + + function UpdateColumnsTag($xml_columns) { + $this->columns = array(); + + if(!is_array($xml_columns)) $xml_columns = array($xml_columns); + + foreach($xml_columns as $column){ + if($column->name === 'query') $this->columns[] = new QueryTag($column, true); + else $this->columns[] = new UpdateColumnTag($column); + } + } + + function toString(){ + $output_columns = 'array(' . PHP_EOL; + foreach($this->columns as $column){ + $output_columns .= $column->getExpressionString() . PHP_EOL . ','; + } + $output_columns = substr($output_columns, 0, -1); + $output_columns .= ')'; + return $output_columns; + } + + function getArguments(){ + $arguments = array(); + foreach($this->columns as $column){ + $arguments[] = $column->getArgument(); + } + return $arguments; + } + + } + +?> \ No newline at end of file diff --git a/classes/xml/xmlquery/tags/condition/ConditionGroupTag.class.php b/classes/xml/xmlquery/tags/condition/ConditionGroupTag.class.php new file mode 100644 index 000000000..2fa3609db --- /dev/null +++ b/classes/xml/xmlquery/tags/condition/ConditionGroupTag.class.php @@ -0,0 +1,42 @@ +pipe = $pipe; + + if(!is_array($conditions)) $conditions = array($conditions); + + foreach($conditions as $condition){ + //if($condition->node_name === 'query') $this->conditions[] = new QueryTag($condition, true); + $this->conditions[] = new ConditionTag($condition); + } + } + + function getConditions(){ + return $this->conditions; + } + + function getConditionGroupString(){ + $conditions_string = 'array('.PHP_EOL; + foreach($this->conditions as $condition){ + $conditions_string .= $condition->getConditionString() . PHP_EOL . ','; + } + $conditions_string = substr($conditions_string, 0, -2);//remove ',' + $conditions_string .= ')'; + + return sprintf("new ConditionGroup(%s%s)", $conditions_string, $this->pipe ? ',\''.$this->pipe . '\'': ''); + } + + function getArguments(){ + $arguments = array(); + foreach($this->conditions as $condition){ + $arguments = array_merge($arguments, $condition->getArguments()); + } + return $arguments; + } + + } +?> \ No newline at end of file diff --git a/classes/xml/xmlquery/tags/condition/ConditionTag.class.php b/classes/xml/xmlquery/tags/condition/ConditionTag.class.php new file mode 100644 index 000000000..c2eedd66d --- /dev/null +++ b/classes/xml/xmlquery/tags/condition/ConditionTag.class.php @@ -0,0 +1,110 @@ + tag inside an XML Query file. Base class. + * + */ + + class ConditionTag { + var $operation; + var $column_name; + + var $pipe; + var $argument_name; + var $argument; + var $default_column; + + var $query; + function ConditionTag($condition){ + $this->operation = $condition->attrs->operation; + $this->pipe = $condition->attrs->pipe; + $dbParser = DB::getParser(); + $this->column_name = $dbParser->parseExpression($condition->attrs->column); + + // If default value is column name, it should be escaped + if($isColumnName = strpos($condition->attrs->default, '.')){ + $condition->attrs->default = $dbParser->parseColumnName($condition->attrs->default); + } + + if($condition->node_name == 'query'){ + $this->query = new QueryTag($condition, true); + $this->default_column = $this->query->toString(); + } + else if($condition->attrs->var && !strpos($condition->attrs->var, '.')){ + $this->argument = new QueryArgument($condition); + $this->argument_name = $this->argument->getArgumentName(); + } + else { + if(isset($condition->attrs->default)){ + if(in_array($this->operation, array('in', 'between', 'not in'))){ + $default_value = $condition->attrs->default; + if(strpos($default_value, "'") !== false) + $default_value = "\"" . $default_value . "\""; + else + $default_value = "'" . $default_value . "'"; + } + else { + $default_value_object = new DefaultValue($this->column_name, $condition->attrs->default); + $default_value = $default_value_object->toString(); + + if($default_value_object->isStringFromFunction()){ + $default_value = '"\'".' . $default_value . '."\'"'; + } + + if($default_value_object->isString() && !$isColumnName && !is_numeric( $condition->attrs->default)){ + if(strpos($default_value, "'") !== false) + $default_value = "\"" . $default_value . "\""; + else + $default_value = "'" . $default_value . "'"; + } + } + $this->default_column = $default_value; + } + else + $this->default_column = "'" . $dbParser->parseColumnName($condition->attrs->var) . "'" ; + } + } + + function setPipe($pipe){ + $this->pipe = $pipe; + } + + function getArguments(){ + $arguments = array(); + if($this->query) + $arguments = array_merge($arguments, $this->query->getArguments()); + if($this->argument) + $arguments[] = $this->argument; + return $arguments; + } + + function getConditionString(){ + if($this->query){ + return sprintf("new ConditionSubquery('%s',%s,%s%s)" + , $this->column_name + , $this->default_column + , '"'.$this->operation.'"' + , $this->pipe ? ", '" . $this->pipe . "'" : '' + ); + } + else if(isset($this->default_column)){ + return sprintf("new ConditionWithoutArgument('%s',%s,%s%s)" + , $this->column_name + , $this->default_column + , '"'.$this->operation.'"' + , $this->pipe ? ", '" . $this->pipe . "'" : '' + ); + } + else{ + return sprintf("new ConditionWithArgument('%s',%s,%s%s)" + , $this->column_name + , '$' . $this->argument_name . '_argument' + , '"'.$this->operation.'"' + , $this->pipe ? ", '" . $this->pipe . "'" : '' + ); + } + } + } +?> \ No newline at end of file diff --git a/classes/xml/xmlquery/tags/condition/ConditionsTag.class.php b/classes/xml/xmlquery/tags/condition/ConditionsTag.class.php new file mode 100644 index 000000000..dcd676494 --- /dev/null +++ b/classes/xml/xmlquery/tags/condition/ConditionsTag.class.php @@ -0,0 +1,48 @@ +condition_groups = array(); + + $xml_condition_list = array(); + if($xml_conditions->condition) + $xml_condition_list = $xml_conditions->condition; + + if($xml_conditions->query){ + if(!is_array($xml_condition_list)) $xml_condition_list = array($xml_condition_list); + if(!is_array($xml_conditions->query)) $xml_conditions->query = array($xml_conditions->query); + $xml_condition_list = array_merge($xml_condition_list, $xml_conditions->query); + } + if($xml_condition_list){ + $this->condition_groups[] = new ConditionGroupTag($xml_condition_list); + } + + $xml_groups = $xml_conditions->group; + if($xml_groups){ + if(!is_array($xml_groups)) $xml_groups = array($xml_groups); + foreach($xml_groups as $group){ + $this->condition_groups[] = new ConditionGroupTag($group->condition, $group->attrs->pipe); + } + } + } + + function toString(){ + $output_conditions = 'array(' . PHP_EOL; + foreach($this->condition_groups as $condition){ + $output_conditions .= $condition->getConditionGroupString() . PHP_EOL . ','; + } + $output_conditions = substr($output_conditions, 0, -1); + $output_conditions .= ')'; + return $output_conditions; + } + + function getArguments(){ + $arguments = array(); + foreach($this->condition_groups as $condition){ + $arguments = array_merge($arguments, $condition->getArguments()); + } + return $arguments; + } + } +?> \ No newline at end of file diff --git a/classes/xml/xmlquery/tags/condition/JoinConditionsTag.class.php b/classes/xml/xmlquery/tags/condition/JoinConditionsTag.class.php new file mode 100644 index 000000000..3b59989f6 --- /dev/null +++ b/classes/xml/xmlquery/tags/condition/JoinConditionsTag.class.php @@ -0,0 +1,11 @@ +condition_groups[0]->conditions[0]->setPipe(""); + } + } + +?> \ No newline at end of file diff --git a/classes/xml/xmlquery/tags/group/GroupsTag.class.php b/classes/xml/xmlquery/tags/group/GroupsTag.class.php new file mode 100644 index 000000000..620f3c5f6 --- /dev/null +++ b/classes/xml/xmlquery/tags/group/GroupsTag.class.php @@ -0,0 +1,35 @@ +groups = array(); + + if($xml_groups) { + if(!is_array($xml_groups)) $xml_groups = array($xml_groups); + + $dbParser = DB::getParser(); + for($i=0;$iattrs->column); + if(!$column) continue; + + $column = $dbParser->parseExpression($column); + $this->groups[] = $column; + } + } + } + + function toString(){ + $output = 'array(' . PHP_EOL; + foreach($this->groups as $group){ + $output .= "'" . $group . "' ,"; + } + $output = substr($output, 0, -1); + $output .= ')'; + return $output; + } + } + +?> \ No newline at end of file diff --git a/classes/xml/xmlquery/tags/navigation/IndexTag.class.php b/classes/xml/xmlquery/tags/navigation/IndexTag.class.php new file mode 100644 index 000000000..86408de2a --- /dev/null +++ b/classes/xml/xmlquery/tags/navigation/IndexTag.class.php @@ -0,0 +1,43 @@ +argument_name = $index->attrs->var; + + // Sort index - column by which to sort + //$dbParser = DB::getParser(); + //$index->attrs->default = $dbParser->parseExpression($index->attrs->default); + $this->default = $index->attrs->default; + $this->argument = new QueryArgument($index); + + // Sort order - asc / desc + $this->sort_order = $index->attrs->order; + if(!in_array($this->sort_order, array("asc", "desc"))){ + $arg->attrs->var = $this->sort_order; + $arg->attrs->default = 'asc'; + $this->sort_order_argument = new SortQueryArgument($arg); + $this->sort_order = '$'.$this->sort_order_argument->getArgumentName().'_argument'; + } + else $this->sort_order = '"'.$this->sort_order.'"'; + } + + function toString(){ + return sprintf("new OrderByColumn(\$%s_argument, %s)", $this->argument->getArgumentName(), $this->sort_order); + } + + function getArguments(){ + $arguments = array(); + $arguments[] = $this->argument; + if($this->sort_order_argument) + $arguments[] = $this->sort_order_argument; + return $arguments; + } + } + +?> \ No newline at end of file diff --git a/classes/xml/xmlquery/tags/navigation/LimitTag.class.php b/classes/xml/xmlquery/tags/navigation/LimitTag.class.php new file mode 100644 index 000000000..fc9350675 --- /dev/null +++ b/classes/xml/xmlquery/tags/navigation/LimitTag.class.php @@ -0,0 +1,30 @@ +page->attrs && $index->page_count->attrs){ + $this->page = new QueryArgument($index->page); + $this->page_count = new QueryArgument($index->page_count); + $this->arguments[] = $this->page; + $this->arguments[] = $this->page_count; + } + + $this->list_count = new QueryArgument($index->list_count); + $this->arguments[] = $this->list_count; + } + + function toString(){ + if ($this->page)return sprintf("new Limit(\$%s_argument, \$%s_argument, \$%s_argument)", $this->list_count->getArgumentName(), $this->page->getArgumentName(), $this->page_count->getArgumentName()); + else return sprintf("new Limit(\$%s_argument)", $this->list_count->getArgumentName()); + } + + function getArguments(){ + return $this->arguments; + } + } +?> \ No newline at end of file diff --git a/classes/xml/xmlquery/tags/navigation/NavigationTag.class.php b/classes/xml/xmlquery/tags/navigation/NavigationTag.class.php new file mode 100644 index 000000000..062ae6ad3 --- /dev/null +++ b/classes/xml/xmlquery/tags/navigation/NavigationTag.class.php @@ -0,0 +1,59 @@ +order = array(); + if($xml_navigation) { + $order = $xml_navigation->index; + if($order) { + if(!is_array($order)) $order = array($order); + foreach($order as $order_info) { + $this->order[] = new IndexTag($order_info); + } + } + + if($xml_navigation->page->attrs || $xml_navigation->list_count->attrs) + $this->limit = new LimitTag($xml_navigation); + + $list_count = $xml_navigation->list_count->attrs; + $this->list_count = $list_count; + + $page_count = $xml_navigation->page_count->attrs; + $this->page_count = $page_count; + + $page = $xml_navigation->page->attrs; + $this->page = $page ; + } + } + + function getOrderByString(){ + $output = 'array(' . PHP_EOL; + foreach($this->order as $order){ + $output .= $order->toString() . PHP_EOL . ','; + } + $output = substr($output, 0, -1); + $output .= ')'; + return $output; + } + + function getLimitString(){ + if ($this->limit) return $this->limit->toString(); + else return ""; + } + + function getArguments(){ + $arguments = array(); + foreach($this->order as $order){ + $arguments = array_merge($order->getArguments(), $arguments); + } + if($this->limit) $arguments = array_merge($this->limit->getArguments(), $arguments); + return $arguments; + } + } + +?> \ No newline at end of file diff --git a/classes/xml/xmlquery/tags/query/QueryTag.class.php b/classes/xml/xmlquery/tags/query/QueryTag.class.php new file mode 100644 index 000000000..ac9a2a6c4 --- /dev/null +++ b/classes/xml/xmlquery/tags/query/QueryTag.class.php @@ -0,0 +1,198 @@ +action = $query->attrs->action; + $this->query_id = $query->attrs->id; + $this->priority = $query->attrs->priority; + $this->query = $query; + $this->isSubQuery = $isSubQuery; + if($this->isSubQuery) $this->action = 'select'; + if($query->attrs->alias){ + $dbParser = DB::getParser(); + $this->alias = $dbParser->escape($query->attrs->alias); + } + $this->join_type = $query->attrs->join_type; + + $this->getColumns(); + $tables = $this->getTables(); + $this->setTableColumnTypes($tables); + $this->getConditions(); + $this->getGroups(); + $this->getNavigation(); + $this->getPrebuff(); + $this->getBuff(); + } + + function show(){ + return true; + } + + function getQueryId(){ + return $this->query->attrs->query_id ? $this->query->attrs->query_id : $this->query->attrs->id; + } + + function getPriority(){ + return $this->query->attrs->priority; + } + + function getAction(){ + return $this->query->attrs->action; + } + + function setTableColumnTypes($tables){ + $query_id = $this->getQueryId(); + if(!isset($this->column_type[$query_id])){ + $table_tags = $tables->getTables(); + $column_type = array(); + foreach($table_tags as $table_tag){ + if(is_a($table_tag, 'TableTag')){ + $tag_column_type = QueryParser::getTableInfo($query_id, $table_tag->getTableName()); + $column_type = array_merge($column_type, $tag_column_type); + } + } + $this->column_type[$query_id] = $column_type; + } + } + + function getColumns(){ + if($this->action == 'select'){ + return $this->columns = new SelectColumnsTag($this->query->columns); + }else if($this->action == 'insert'){ + return $this->columns = new InsertColumnsTag($this->query->columns->column); + }else if($this->action == 'update') { + return $this->columns = new UpdateColumnsTag($this->query->columns->column); + }else if($this->action == 'delete') { + return $this->columns = null; + } + } + + function getPrebuff(){ + // TODO Check if this work with arguments in join clause + $arguments = $this->getArguments(); + + $prebuff = ''; + foreach($arguments as $argument){ + if(isset($argument)){ + $arg_name = $argument->getArgumentName(); + if($arg_name){ + $prebuff .= $argument->toString(); + $column_type = $this->column_type[$this->getQueryId()][$argument->getColumnName()]; + if(isset($column_type)) + $prebuff .= sprintf("if($%s_argument !== null) $%s_argument->setColumnType('%s');\n" + , $arg_name + , $arg_name + , $column_type ); + } + } + } + $prebuff .= "\n"; + + return $this->preBuff = $prebuff; + } + + function getBuff(){ + $buff = ''; + if($this->isSubQuery){ + $buff = 'new Subquery('; + $buff .= "'" . $this->alias . '\', '; + $buff .= ($this->columns ? $this->columns->toString() : 'null' ). ', '.PHP_EOL; + $buff .= $this->tables->toString() .','.PHP_EOL; + $buff .= $this->conditions->toString() .',' .PHP_EOL; + $buff .= $this->groups->toString() . ',' .PHP_EOL; + $buff .= $this->navigation->getOrderByString() .','.PHP_EOL; + $limit = $this->navigation->getLimitString() ; + $buff .= $limit ? $limit : 'null' . PHP_EOL; + $buff .= $this->join_type ? "'" . $this->join_type . "'" : ''; + $buff .= ')'; + + $this->buff = $buff; + return $this->buff; + } + + $buff .= '$query = new Query();'.PHP_EOL; + $buff .= sprintf('$query->setQueryId("%s");%s', $this->query_id, "\n"); + $buff .= sprintf('$query->setAction("%s");%s', $this->action, "\n"); + $buff .= sprintf('$query->setPriority("%s");%s', $this->priority, "\n"); + $buff .= $this->preBuff; + if($this->columns) + $buff .= '$query->setColumns(' . $this->columns->toString() . ');'.PHP_EOL; + + $buff .= '$query->setTables(' . $this->tables->toString() .');'.PHP_EOL; + $buff .= '$query->setConditions('.$this->conditions->toString() .');'.PHP_EOL; + $buff .= '$query->setGroups(' . $this->groups->toString() . ');'.PHP_EOL; + $buff .= '$query->setOrder(' . $this->navigation->getOrderByString() .');'.PHP_EOL; + $buff .= '$query->setLimit(' . $this->navigation->getLimitString() .');'.PHP_EOL; + + $this->buff = $buff; + return $this->buff; + } + + function getTables(){ + if($this->query->index_hint->attrs->for == 'ALL' || Context::getDBType() == strtolower($this->query->index_hint->attrs->for)) + return $this->tables = new TablesTag($this->query->tables, $this->query->index_hint); + else + return $this->tables = new TablesTag($this->query->tables); + } + + function getConditions(){ + return $this->conditions = new ConditionsTag($this->query->conditions); + } + + function getGroups(){ + return $this->groups = new GroupsTag($this->query->groups->group); + } + + function getNavigation(){ + return $this->navigation = new NavigationTag($this->query->navigation); + } + + function toString(){ + return $this->buff; + } + + function getTableString(){ + return $this->buff; + } + + function getConditionString(){ + return $this->buff; + } + + function getExpressionString(){ + return $this->buff; + } + + + function getArguments(){ + $arguments = array(); + if($this->columns) + $arguments = array_merge($arguments, $this->columns->getArguments()); + $arguments = array_merge($arguments, $this->tables->getArguments()); + $arguments = array_merge($arguments, $this->conditions->getArguments()); + $arguments = array_merge($arguments, $this->navigation->getArguments()); + return $arguments; + } + +} +?> diff --git a/classes/xml/xmlquery/tags/table/HintTableTag.class.php b/classes/xml/xmlquery/tags/table/HintTableTag.class.php new file mode 100644 index 000000000..ce76a63bb --- /dev/null +++ b/classes/xml/xmlquery/tags/table/HintTableTag.class.php @@ -0,0 +1,48 @@ + tag inside an XML Query file + * and the corresponding tag + * + */ + + class HintTableTag extends TableTag { + var $index; + + /** + * @brief Initialises Table Tag properties + * @param XML tag $table + */ + function HintTableTag($table, $index){ + parent::TableTag($table); + $this->index = $index; + } + + function getTableString(){ + $dbParser = DB::getParser(); + $dbType = ucfirst(Context::getDBType()); + + $result = sprintf('new %sTableWithHint(\'%s\'%s, array(' + , $dbType + , $dbParser->escape($this->name) + , $this->alias ? ', \'' . $dbParser->escape($this->alias) .'\'' : ', null' + //, ', \'' . $dbParser->escape($this->index->name) .'\', \'' . $this->index->type .'\'' + ); + foreach($this->index as $indx){ + $result .= "new IndexHint("; + $result .= '\'' . $dbParser->escape($indx->name) .'\', \'' . $indx->type .'\'' . ') , '; + } + $result = substr($result, 0, -2); + $result .= '))'; + return $result; + } + + function getArguments(){ + if(!isset($this->conditionsTag)) return array(); + return $this->conditionsTag->getArguments(); + } + } + +?> \ No newline at end of file diff --git a/classes/xml/xmlquery/tags/table/TableTag.class.php b/classes/xml/xmlquery/tags/table/TableTag.class.php new file mode 100644 index 000000000..0fe59c23d --- /dev/null +++ b/classes/xml/xmlquery/tags/table/TableTag.class.php @@ -0,0 +1,88 @@ + tag inside an XML Query file + * + * @abstract + * Example + *
+ *
+ * Attributes + * name - name of the table - table prefix will be automatically added + * alias - table alias. If no value is specified, the table name will be set as default alias + * join_type - in case the table is part of a join clause, this specifies the type of join: left, right etc. + * - permitted values: 'left join','left outer join','right join','right outer join' + * Children + * Can have children of type + */ + + class TableTag { + var $unescaped_name; + var $name; + var $alias; + var $join_type; + var $conditions; + var $conditionsTag; + /** + * @brief Initialises Table Tag properties + * @param XML
tag $table + */ + function TableTag($table){ + $dbParser = DB::getParser(); + + $this->unescaped_name = $table->attrs->name; + $this->name = $dbParser->parseTableName($table->attrs->name); + + $this->alias = $table->attrs->alias; + if(!$this->alias) $this->alias = $table->attrs->name; + + $this->join_type = $table->attrs->type; + + $this->conditions = $table->conditions; + + if($this->isJoinTable()) + $this->conditionsTag = new JoinConditionsTag($this->conditions); + } + + function isJoinTable(){ + if(in_array($this->join_type,array('left join','left outer join','right join','right outer join')) + && count($this->conditions)) return true; + return false; + } + + function getTableAlias(){ + return $this->alias; + } + + function getTableName(){ + return $this->unescaped_name; + } + + /** + * @brief Returns string for printing in PHP query cache file + * The string contains code for instantiation of either + * a Table or a JoinTable object + * @return string + */ + function getTableString(){ + $dbParser = DB::getParser(); + if($this->isJoinTable()){ + return sprintf('new JoinTable(\'%s\', \'%s\', "%s", %s)' + , $dbParser->escape($this->name) + , $dbParser->escape($this->alias) + , $this->join_type, $this->conditionsTag->toString()); + } + return sprintf('new Table(\'%s\'%s)' + , $dbParser->escape($this->name) + , $this->alias ? ', \'' . $dbParser->escape($this->alias) .'\'' : ''); + } + + function getArguments(){ + if(!isset($this->conditionsTag)) return array(); + return $this->conditionsTag->getArguments(); + } + } + +?> \ No newline at end of file diff --git a/classes/xml/xmlquery/tags/table/TablesTag.class.php b/classes/xml/xmlquery/tags/table/TablesTag.class.php new file mode 100644 index 000000000..ca0842c3a --- /dev/null +++ b/classes/xml/xmlquery/tags/table/TablesTag.class.php @@ -0,0 +1,76 @@ + tag inside an XML Query file + * + * @abstract + * Example + * + *
+ * + * Attributes + * None. + * Children + * Can have children of type
or + */ + + class TablesTag { + var $tables; + + function TablesTag($xml_tables_tag, $xml_index_hints_tag = NULL){ + $this->tables = array(); + + $xml_tables = $xml_tables_tag->table; + if(!is_array($xml_tables)) $xml_tables = array($xml_tables); + + if($xml_index_hints_tag){ + $index_nodes = $xml_index_hints_tag->index; + if(!is_array($index_nodes)) $index_nodes = array($index_nodes); + foreach($index_nodes as $index_node) { + if(!$indexes[$index_node->attrs->table]) $indexes[$index_node->attrs->table] = array(); + $count = count($indexes[$index_node->attrs->table]); + $indexes[$index_node->attrs->table][$count]->name = $index_node->attrs->name; + $indexes[$index_node->attrs->table][$count]->type = $index_node->attrs->type; + } + } + + foreach($xml_tables as $tag){ + if($tag->attrs->query == 'true'){ + $this->tables[] = new QueryTag($tag, true); + } + else { + if($indexes && $indexes[$tag->attrs->name]) + $this->tables[] = new HintTableTag($tag, $indexes[$tag->attrs->name]); + else + $this->tables[] = new TableTag($tag); + } + } + } + + function getTables(){ + return $this->tables; + } + + function toString(){ + $output_tables = 'array(' . PHP_EOL; + foreach($this->tables as $table){ + if(is_a($table, 'QueryTag')) + $output_tables .= $table->toString() . PHP_EOL . ','; + else + $output_tables .= $table->getTableString() . PHP_EOL . ','; + } + $output_tables = substr($output_tables, 0, -1); + $output_tables .= ')'; + return $output_tables; + } + + function getArguments(){ + $arguments = array(); + foreach($this->tables as $table) + $arguments = array_merge($arguments, $table->getArguments()); + return $arguments; + } + } +?> \ No newline at end of file diff --git a/common/css/button.css b/common/css/button.css deleted file mode 100644 index fb9661cd9..000000000 --- a/common/css/button.css +++ /dev/null @@ -1,91 +0,0 @@ -/* NHN (developers@xpressengine.com) */ - -/* Anchor Button */ -a.button, -a.button span { position:relative; display:inline-block; text-decoration:none !important; background:url(../tpl/images/buttonWhite.gif) no-repeat; cursor:pointer; white-space:nowrap; vertical-align:middle;} -a.button { padding:0; background-position:left top; overflow:visible;} -a.button span { left:2px; padding:6px 10px 5px 8px; color:#000; font:12px/12px Sans-serif; background-position:right top; *vertical-align:top;} -/* Large Size */ -a.button.large { background-position:left -30px; } -a.button.large span { padding:7px 10px 6px 8px; font:16px/16px Sans-serif; background-position:right -30px;} -/* xLarge Size */ -a.button.xLarge { background-position:left -65px; } -a.button.xLarge span { padding:8px 10px 7px 8px; font:20px/20px Sans-serif; background-position:right -65px;} -/* Small Size */ -a.button.small { background-position:left -107px; } -a.button.small span { padding:4px 6px 3px 4px; font:11px/11px Sans-serif; background-position:right -107px;} - -/* Control Button + Submit Button */ -span.button, -span.button button, -span.button input { position:relative; margin:0; display:inline-block; border:0; font:12px Sans-serif; white-space:nowrap; background:url(../tpl/images/buttonWhite.gif) no-repeat; vertical-align:middle;} -span.button { padding:0; background-position:left top;} -span.button button, -span.button input { height:23px; left:2px; *vertical-align:top; padding:0 10px 0 8px; line-height:24px; background-position:right top; cursor:pointer; *overflow:visible;} -/* Large Size */ -span.button.large { background-position:left -30px;} -span.button.large button, -span.button.large input { height:30px; padding:0 10px 0 8px; font:16px/30px Sans-serif; background-position:right -30px;} -/* xLarge Size */ -span.button.xLarge { background-position:left -65px;} -span.button.xLarge button, -span.button.xLarge input { height:35px; padding:0 10px 0 8px; font:20px/36px Sans-serif; background-position:right -65px;} -/* Small Size */ -span.button.small { background-position:left -107px;} -span.button.small button, -span.button.small input { height:18px; padding:0 6px 0 4px; font:11px/18px Sans-serif; background-position:right -107px;} - -/* Strong Button */ -a.button.strong *, -span.button.strong * { font-weight:bold !important;} - -/* Icon Add */ -a.button .icon { position:relative; border:0; vertical-align:middle;} -span.button .icon { position:relative; left:10px; margin-right:8px; vertical-align:middle;} - -/* Color Preset */ -a.button.green, -a.button.green span, -span.button.green, -span.button.green button, -span.button.green input { background-image:url(../tpl/images/buttonGreen.gif); color:#fff;} -a.button.black, -a.button.black span, -span.button.black, -span.button.black button, -span.button.black input { background-image:url(../tpl/images/buttonBlack.gif); color:#fff;} -a.button.red, -a.button.red span, -span.button.red, -span.button.red button, -span.button.red input { background-image:url(../tpl/images/buttonRed.gif); color:#fff;} -a.button.blue, -a.button.blue span, -span.button.blue, -span.button.blue button, -span.button.blue input { background-image:url(../tpl/images/buttonBlue.gif); color:#fff;} - -/* Offset Debug */ -a.button, -span.button{ margin-right:2px;} - -/* Button Set */ -.buttonSet{ width:16px; height:16px; background-image:url(../tpl/images/buttonSet.gif); background-repeat:no-repeat; border:0; background-color:transparent; vertical-align:middle; display:inline-block; text-decoration:none;} -.buttonSet span{ z-index:-1; font-size:0; line-height:0; visibility:hidden;} -.buttonSet.buttonUp{ background-position:0 0;} -.buttonSet.buttonDown{ background-position:0 -16px;} -.buttonSet.buttonLeft{ background-position:0 -32px;} -.buttonSet.buttonRight{ background-position:0 -48px;} -.buttonSet.buttonInfo{ background-position:0 -64px;} -.buttonSet.buttonCopy{ background-position:0 -80px;} -.buttonSet.buttonSetting{ background-position:0 -96px;} -.buttonSet.buttonActive{ background-position:0 -112px;} -.buttonSet.buttonDisable{ background-position:0 -128px;} -.buttonSet.buttonDelete{ background-position:0 -144px;} -.buttonSet.buttonHomepage{ background-position:0 -160px;} -.buttonSet.buttonBlog{ background-position:0 -176px;} -.buttonSet.buttonCalendar{ background-position:0 -192px;} -.buttonSet.buttonLayoutEditor{ background-position:0 -208px;} -.buttonSet.buttonAddWidget{ background-position:0 -224px;} -.buttonSet.buttonAddContent{ background-position:0 -240px;} - diff --git a/common/css/default.css b/common/css/default.css deleted file mode 100644 index dba8b421f..000000000 --- a/common/css/default.css +++ /dev/null @@ -1,91 +0,0 @@ -/* default.css - Type Selector Definition */ -body { margin:0;padding:0; font-size:.75em;} - -img { border:none; } -label { cursor:pointer; } -form { margin:0; padding:0; } - -/* 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; } -.nowrap { white-space:nowrap; } - -.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; _font-size:9pt; } -*:first-child+html .inputTypeText { font-size:9pt; } -.inputTypeText:hover, -.inputTypeText:focus { background:#f4f4f4; } -.inputTypeTextArea { border:1px solid !important; border-color:#a6a6a6 #d8d8d8 #d8d8d8 #a6a6a6 !important; background:#ffffff; font-size:1em; _font-size:9pt; height:100px;} -*:first-child+html .inputTypeTextArea { font-size:9pt; } - -.w40 { width:40px; } -.w60 { width:60px; } -.w70 { width:70px; } -.w80 { width:80px; } -.w90 { width:90px; } -.w100 { width:100px; } -.w110 { width:110px; } -.w120 { width:120px; } -.w130 { width:130px; } -.w140 { width:140px; } -.w150 { width:150px; } -.w160 { width:160px; } -.w170 { width:170px; } -.w180 { width:180px; } -.w190 { width:190px; } -.w200 { width:200px; } -.w210 { width:210px; } -.w220 { width:220px; } -.w230 { width:230px; } -.w240 { width:240px; } -.w250 { width:250px; } -.w260 { width:260px; } -.w270 { width:270px; } -.w280 { width:280px; } -.w290 { width:290px; } -.w300 { width:300px; } -.w400 { width:400px; } - -/* editor style */ -a.bold { font-weight:bold; } - -.editor_blue_text { color: #145ff9 !important; text-decoration:underline !important; } -.editor_blue_text a { color: #145ff9 !important; text-decoration:underline !important; } -.editor_red_text { color: #f42126 !important; text-decoration:underline !important; } -.editor_red_text a { color: #f42126 !important; text-decoration:underline !important; } -.editor_yellow_text { color: #c9bd00 !important; text-decoration:underline !important; } -.editor_yellow_text a { color: #c9bd00 !important; text-decoration:underline !important; } -.editor_green_text { color: #08830B !important; text-decoration:underline !important; } -.editor_green_text a { color: #08830B !important; text-decoration:underline !important; } - -.folder_opener { display: block; } -.folder_closer { display: none; } -.folder_area { display: none; } - -.xe_content { line-height:1.6; overflow:hidden; } - -.zbxe_widget_output { background:url(../tpl/images/widget_text.gif) no-repeat center bottom; display:block;} - -/* xe layer */ -#waitingforserverresponse { border:2px solid #444444; font-weight:bold; color:#444444; padding: 7px 5px 5px 25px; background:#FFFFFF url("../tpl/images/loading.gif") no-repeat 5px 5px; top:40px; left:40px; position:absolute; z-index:100; visibility:hidden; } - -#popup_menu_area{ position:absolute; background:#fff; border:2px solid #eee; -moz-border-radius:5px; -webkit-border-radius:5px; margin:0; padding:0;} -#popup_menu_area *{ margin:0; padding:0; list-style:none; font-size:12px; line-height:normal;} -#popup_menu_area ul{ border:1px solid #ddd; -moz-border-radius:5px; -webkit-border-radius:5px; padding:10px 10px 5px 10px;} -#popup_menu_area li{ padding:2px 0 2px 20px; background-repeat:no-repeat; background-position:left center; margin-bottom:3px; white-space:nowrap;} -#popup_menu_area li a{ text-decoration:none; color:#000;} -#popup_menu_area li a:hover, -#popup_menu_area li a:active, -#popup_menu_area li a:focus{ font-weight:bold; letter-spacing:-1px;} - -/* xe faceoff */ -.faceOffManager { position:fixed; _position:absolute; right:3px; top:3px; height:23px; } diff --git a/common/css/popup.css b/common/css/popup.css deleted file mode 100644 index 6b558e507..000000000 --- a/common/css/popup.css +++ /dev/null @@ -1,2 +0,0 @@ -body { overflow:hidden; } -table.colTable { margin:0; } diff --git a/common/css/xe.css b/common/css/xe.css new file mode 100644 index 000000000..f668cac6e --- /dev/null +++ b/common/css/xe.css @@ -0,0 +1,64 @@ +@charset "utf-8"; +/* Element Reset */ +body,table,input,textarea,select,button{font-family:Tahoma,Geneva,sans-serif;font-size:12px} +img{border:0} +/* Button */ +.btn{position:relative;display:inline-block;vertical-align:middle} +.btn *{display:inline-block;padding:0 8px;font-size:12px;height:24px;line-height:22px;margin:0;font-weight:bold !important;color:#fff;text-decoration:none !important;border:1px solid;cursor:pointer;overflow:visible;border-radius:3px;box-shadow:inset 0 0 1px #fff;background-color:#666;text-shadow:0 -1px 0 #333;zoom:1} +.btn *[type=submit][disabled=disabled], +.btn *[type=button][disabled=disabled]{opacity:.5;*filter:alpha(opacity=50)} +.btn a, +.btn button[type=button]{border-color:#ccc;color:#333 !important;background:#eee -webkit-gradient(linear,0% 0%,0% 100%,from(#fff),to(#ddd));background:#eee -moz-linear-gradient(top,#fff,#ddd);background-color:#eee;text-shadow:1px 1px 0 #fff;filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#ffffff, endColorStr=#dddddd)} +.btn input, +.btn button[type=submit]{border-color:#666;background:#333 -webkit-gradient(linear,0% 0%,0% 100%,from(#777),to(#777),color-stop(0.5,#333),color-stop(0.5,#000)) !important;background:#333 -moz-linear-gradient(top,#777,#000) !important;background-color:#333 !important;color:#ffc !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#777777, endColorStr=#333333)} +.btn a{height:22px} +.btn.medium *{padding:0 12px;font-size:16px;height:30px;line-height:28px} +.btn.medium a{height:28px} +.btn.large *{padding:0 18px;font-size:22px;height:36px;line-height:34px} +.btn.large a{height:34px} +/* Button - Regucy */ +span.button, +a.button{position:relative;display:inline-block;vertical-align:top} +span.button *, +a.button *{display:inline-block;padding:0 8px;font-size:12px;height:24px;line-height:22px;margin:0;font-weight:bold !important;color:#fff;text-decoration:none !important;border:1px solid;cursor:pointer;overflow:visible;border-radius:3px;box-shadow:inset 0 0 1px #fff;background-color:#666;text-shadow:0 -1px 0 #333;zoom:1} +span.button *[type=submit][disabled=disabled], +span.button *[type=button][disabled=disabled]{opacity:.5;*filter:alpha(opacity=50)} +a.button span, +span.button button[type=button]{border-color:#ccc;color:#333 !important;background:#eee -webkit-gradient(linear,0% 0%,0% 100%,from(#fff),to(#ddd));background:#eee -moz-linear-gradient(top,#fff,#ddd);background-color:#eee;text-shadow:1px 1px 0 #fff;filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#ffffff, endColorStr=#dddddd)} +span.button input, +span.button button[type=submit]{border-color:#666;background:#333 -webkit-gradient(linear,0% 0%,0% 100%,from(#777),to(#777),color-stop(0.5,#333),color-stop(0.5,#000));background:#333 -moz-linear-gradient(top,#777,#000);background-color:#333;color:#ffc !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#777777, endColorStr=#333333)} +a.button span{height:22px} +/* Button Area */ +.btnArea{margin:1em 0;text-align:right;zoom:1} +.btnArea:after{content:"";display:block;clear:both} +.btnArea .etc{float:left} +/* Text Button */ +input[type=submit].text, +input[type=button].text, +button[type=submit].text, +button[type=button].text{border:0;overflow:visible;padding:0;margin:0 4px 0 0;color:#33a !important;background:none;text-decoration:underline;cursor:pointer} +/* Popup Menu Area */ +#popup_menu_area{position:absolute;background:#fff;border:1px solid #e9e9e9;border-radius:5px;padding:10px;line-height:1.3;box-shadow:0 0 6px #666;font-size:12px;filter:progid:DXImageTransform.Microsoft.Shadow(color=#999999,direction=135, strength=5)} +#popup_menu_area ul{list-style:none;margin:0;padding:0} +#popup_menu_area li{margin:0;padding:0} +#popup_menu_area a{text-decoration:none;color:#333} +#popup_menu_area a:hover, +#popup_menu_area a:avtive, +#popup_menu_area a:focus{text-decoration:underline} +/* Message */ +.message{border:1px solid #ddd;background:#f8f8f8;margin:1em 0;padding:0 1em;border-radius:5px;line-height:1.4;font-size:12px} +body>.message{margin:1em} +.message p{margin:1em 0 !important} +.message em{font-style:normal;color:#e00} +.message.info, +.message.error, +.message.update{padding-left:55px} +.message.info{border-color:#E0E8EC;background:#EDF9FF url(../../common/img/msg.Info.png) no-repeat 1em .5em} +.message.error{border-color:#EFDCDC;background:#FFECEC url(../../common/img/msg.error.png) no-repeat 1em .5em} +.message.update{border-color:#EAE9DC;background:#FFFDEF url(../../common/img/msg.update.png) no-repeat 1em .5em} +/* Waiting for server response */ +.wfsr{display:none;position:absolute;position:fixed;left:0;top:0;z-index:100; border:1px solid #EAE9DC;background:#FFFDEF url(../../common/img/msg.loading.gif) no-repeat 1em .5em;margin:1em;padding:1em 1em 1em 55px;border-radius:5px;line-height:1.4;font-size:12px;font-weight:bold} +/* Waiting for server response - Modal Window */ +.wfsr_fog{position:absolute;top:0;left:0;width:100%;_height:100%;min-height:100%;z-index:100} +.wfsr_fog .bg{position:absolute;position:fixed;background:#000;_background:none;width:100%;height:100%;opacity:.5;z-index:2;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=50);zoom:1} +.wfsr_fog .ie6{position:absolute;left:0;top:0;width:100%;height:100%;border:0;opacity:0;filter:alpha(opacity=0);z-index:1} diff --git a/common/css/xe.min.css b/common/css/xe.min.css new file mode 100644 index 000000000..896e7492c --- /dev/null +++ b/common/css/xe.min.css @@ -0,0 +1 @@ +@charset "utf-8";/* Element Reset */body,table,input,textarea,select,button{font-family:Tahoma,Geneva,sans-serif;font-size:12px}img{border:0}/* Button */.btn{position:relative;display:inline-block;vertical-align:middle}.btn *{display:inline-block;padding:0 8px;font-size:12px;height:24px;line-height:22px;margin:0;font-weight:bold !important;color:#fff;text-decoration:none !important;border:1px solid;cursor:pointer;overflow:visible;border-radius:3px;box-shadow:inset 0 0 1px #fff;background-color:#666;text-shadow:0 -1px 0 #333;zoom:1}.btn *[type=submit][disabled=disabled],.btn *[type=button][disabled=disabled]{opacity:.5;*filter:alpha(opacity=50)}.btn a, .btn button[type=button]{border-color:#ccc;color:#333 !important;background:#eee -webkit-gradient(linear,0% 0%,0% 100%,from(#fff),to(#ddd));background:#eee -moz-linear-gradient(top,#fff,#ddd);background-color:#eee;text-shadow:1px 1px 0 #fff;filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#ffffff, endColorStr=#dddddd)}.btn input, .btn button[type=submit]{border-color:#666;background:#333 -webkit-gradient(linear,0% 0%,0% 100%,from(#777),to(#777),color-stop(0.5,#333),color-stop(0.5,#000)) !important;background:#333 -moz-linear-gradient(top,#777,#000) !important;background-color:#333 !important;color:#ffc !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#777777, endColorStr=#333333)}.btn a{height:22px}.btn.medium *{padding:0 12px;font-size:16px;height:30px;line-height:28px}.btn.medium a{height:28px}.btn.large *{padding:0 18px;font-size:22px;height:36px;line-height:34px}.btn.large a{height:34px}/* Button - Regucy */span.button,a.button{position:relative;display:inline-block;vertical-align:top}span.button *,a.button *{display:inline-block;padding:0 8px;font-size:12px;height:24px;line-height:22px;margin:0;font-weight:bold !important;color:#fff;text-decoration:none !important;border:1px solid;cursor:pointer;overflow:visible;border-radius:3px;box-shadow:inset 0 0 1px #fff;background-color:#666;text-shadow:0 -1px 0 #333;zoom:1}span.button *[type=submit][disabled=disabled],span.button *[type=button][disabled=disabled]{opacity:.5;*filter:alpha(opacity=50)}a.button span, span.button button[type=button]{border-color:#ccc;color:#333 !important;background:#eee -webkit-gradient(linear,0% 0%,0% 100%,from(#fff),to(#ddd));background:#eee -moz-linear-gradient(top,#fff,#ddd);background-color:#eee;text-shadow:1px 1px 0 #fff;filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#ffffff, endColorStr=#dddddd)}span.button input, span.button button[type=submit]{border-color:#666;background:#333 -webkit-gradient(linear,0% 0%,0% 100%,from(#777),to(#777),color-stop(0.5,#333),color-stop(0.5,#000));background:#333 -moz-linear-gradient(top,#777,#000);background-color:#333;color:#ffc !important;filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#777777, endColorStr=#333333)}a.button span{height:22px}/* Button Area */.btnArea{margin:1em 0;text-align:right;zoom:1}.btnArea:after{content:"";display:block;clear:both}.btnArea .etc{float:left}/* Text Button */input[type=submit].text,input[type=button].text, button[type=submit].text,button[type=button].text{border:0;overflow:visible;padding:0;margin:0 4px 0 0;color:#33a !important;background:none;text-decoration:underline;cursor:pointer}/* Popup Menu Area */#popup_menu_area{position:absolute;background:#fff;border:1px solid #e9e9e9;border-radius:5px;padding:10px;line-height:1.3;box-shadow:0 0 6px #666;font-size:12px;filter:progid:DXImageTransform.Microsoft.Shadow(color=#999999,direction=135, strength=5)}#popup_menu_area ul{list-style:none;margin:0;padding:0}#popup_menu_area li{margin:0;padding:0}#popup_menu_area a{text-decoration:none;color:#333}#popup_menu_area a:hover,#popup_menu_area a:avtive,#popup_menu_area a:focus{text-decoration:underline}/* Message */.message{border:1px solid #ddd;background:#f8f8f8;margin:1em 0;padding:0 1em;border-radius:5px;line-height:1.4;font-size:12px}body>.message{margin:1em}.message p{margin:1em 0 !important}.message em{font-style:normal;color:#e00}.message.info,.message.error,.message.update{padding-left:55px}.message.info{border-color:#E0E8EC;background:#EDF9FF url(../../common/img/msg.Info.png) no-repeat 1em .5em}.message.error{border-color:#EFDCDC;background:#FFECEC url(../../common/img/msg.error.png) no-repeat 1em .5em}.message.update{border-color:#EAE9DC;background:#FFFDEF url(../../common/img/msg.update.png) no-repeat 1em .5em}/* Waiting for server response */.wfsr{display:none;position:absolute;position:fixed;left:0;top:0;z-index:100; border:1px solid #EAE9DC;background:#FFFDEF url(../../common/img/msg.loading.gif) no-repeat 1em .5em;margin:1em;padding:1em 1em 1em 55px;border-radius:5px;line-height:1.4;font-size:12px;font-weight:bold}/* Waiting for server response - Modal Window */.wfsr_fog{position:absolute;top:0;left:0;width:100%;_height:100%;min-height:100%;z-index:100}.wfsr_fog .bg{position:absolute;position:fixed;background:#000;_background:none;width:100%;height:100%;opacity:.5;z-index:2;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=50);zoom:1}.wfsr_fog .ie6{position:absolute;left:0;top:0;width:100%;height:100%;border:0;opacity:0;filter:alpha(opacity=0);z-index:1} \ No newline at end of file diff --git a/common/tpl/images/blank.gif b/common/img/blank.gif similarity index 100% rename from common/tpl/images/blank.gif rename to common/img/blank.gif diff --git a/common/tpl/images/flvplayer.swf b/common/img/flvplayer.swf similarity index 100% rename from common/tpl/images/flvplayer.swf rename to common/img/flvplayer.swf diff --git a/common/img/icon.bubble.png b/common/img/icon.bubble.png new file mode 100644 index 000000000..82f6f3aad Binary files /dev/null and b/common/img/icon.bubble.png differ diff --git a/common/img/msg.Info.png b/common/img/msg.Info.png new file mode 100644 index 000000000..a6f3565c5 Binary files /dev/null and b/common/img/msg.Info.png differ diff --git a/common/img/msg.error.png b/common/img/msg.error.png new file mode 100644 index 000000000..7d9c1a68b Binary files /dev/null and b/common/img/msg.error.png differ diff --git a/common/img/msg.loading.gif b/common/img/msg.loading.gif new file mode 100644 index 000000000..c97ec6ea9 Binary files /dev/null and b/common/img/msg.loading.gif differ diff --git a/common/img/msg.update.png b/common/img/msg.update.png new file mode 100644 index 000000000..598afd0a3 Binary files /dev/null and b/common/img/msg.update.png differ diff --git a/common/js/common.js b/common/js/common.js index e1c795c4d..3d7ed4db1 100644 --- a/common/js/common.js +++ b/common/js/common.js @@ -1,47 +1,977 @@ -/** - * @file common.js - * @author NHN (developers@xpressengine.com) - * @brief 몇가지 유용한 & 기본적으로 자주 사용되는 자바스크립트 함수들 모음 +/** + * @file common.js + * @author NHN (developers@xpressengine.com) + * @brief 몇가지 유용한 & 기본적으로 자주 사용되는 자바스크립트 함수들 모음 **/ -if(jQuery)jQuery.noConflict();(function($){var UA=navigator.userAgent.toLowerCase();$.os={Linux:/linux/.test(UA),Unix:/x11/.test(UA),Mac:/mac/.test(UA),Windows:/win/.test(UA)};$.os.name=($.os.Windows)?'Windows':($.os.Linux)?'Linux':($.os.Unix)?'Unix':($.os.Mac)?'Mac':'';window.XE={loaded_popup_menus:new Array(),addedDocument:new Array(),checkboxToggleAll:function(){var itemName='cart',options={wrap:null,checked:'toggle',doClick:false};switch(arguments.length){case 1:if(typeof (arguments[0])=="string"){itemName=arguments[0]}else $.extend(options,arguments[0]||{});break;case 2:itemName=arguments[0];$.extend(options,arguments[1]||{})};if(options.doClick==true)options.checked=null;if(typeof (options.wrap)=="string")options.wrap='#'+options.wrap;if(options.wrap){var obj=$(options.wrap).find('input[name='+itemName+']:checkbox')}else var obj=$('input[name='+itemName+']:checkbox');if(options.checked=='toggle'){obj.each(function(){$(this).attr('checked',($(this).attr('checked'))?false:true)})}else (options.doClick==true)?obj.click():obj.attr('checked',options.checked)},displayPopupMenu:function(ret_obj,response_tags,params){var target_srl=params.target_srl,menu_id=params.menu_id,menus=ret_obj.menus,html="";if(this.loaded_popup_menus[menu_id]){html=this.loaded_popup_menus[menu_id]}else{if(menus){var item=menus.item;if(typeof (item.length)=='undefined'||item.length<1)item=new Array(item);if(item.length)for(var i=0;i'+str+' '}};this.loaded_popup_menus[menu_id]=html};if(html){var area=$('#popup_menu_area').html('
    '+html+'
'),areaOffset={top:params.page_y,left:params.page_x};if(area.outerHeight()+areaOffset.top>$(window).height()+$(window).scrollTop())areaOffset.top=$(window).height()-area.outerHeight()+$(window).scrollTop();if(area.outerWidth()+areaOffset.left>$(window).width()+$(window).scrollLeft())areaOffset.left=$(window).width()-area.outerWidth()+$(window).scrollLeft();area.css({top:areaOffset.top,left:areaOffset.left}).show()}}}})(jQuery);jQuery(function($){if(!$('#popup_menu_area').length){var menuObj=$('
').attr('id','popup_menu_area').css({display:'none',zIndex:9999});$(document.body).append(menuObj)};$(document).click(function(evt){var area=$('#popup_menu_area');if(!area.length)return;area.hide();var targetObj=$(evt.target);if(!targetObj.length)return;if(targetObj.length&&$.inArray(targetObj.attr('nodeName'),['DIV','SPAN','A'])==-1)targetObj=targetObj.parent();if(!targetObj.length||$.inArray(targetObj.attr('nodeName'),['DIV','SPAN','A'])==-1)return;var class_name=targetObj.attr('className');if(class_name.indexOf('_')<=0)return;var class_name_list=class_name.split(' '),menu_id='',menu_id_regx=/^([a-zA-Z]+)_([0-9]+)$/;for(var i=0,c=class_name_list.length;i-1)?first_enable[i]:j;if(!disabled_exists)return;sels.oldonchange=sels.onchange;sels.onchange=function(){if(this.options[this.selectedIndex].disabled){this.selectedIndex=first_enable[i]}else if(this.oldonchange)this.oldonchange()};if(sels.selectedIndex>=0&&sels.options[sels.selectedIndex].disabled)sels.onchange()});var drEditorFold=$('.xe_content .fold_button');if(drEditorFold.size()){var fold_container=$('div.fold_container',drEditorFold);$('button.more',drEditorFold).click(function(){$(this).hide().next('button').show().parent().next(fold_container).show()});$('button.less',drEditorFold).click(function(){$(this).hide().prev('button').show().parent().next(fold_container).hide()})}});String.prototype.getQuery=function(key){var idx=this.indexOf('?');if(idx==-1)return null;var query_string=this.substr(idx+1,this.length),args={};query_string.replace(/([^=]+)=([^&]*)(&|$)/g,function(){args[arguments[1]]=arguments[2]});var q=args[key];if(typeof (q)=="undefined")q="";return q};String.prototype.setQuery=function(key,val){var idx=this.indexOf('?'),uri=this.replace(/#$/,'');if(idx!=-1){var query_string=uri.substr(idx+1,this.length),args={},q_list=[];uri=this.substr(0,idx);query_string.replace(/([^=]+)=([^&]*)(&|$)/g,function(all,key,val){args[key]=val});args[key]=val;jQuery.each(args,function(key,val){if(!jQuery.trim(val))return;q_list.push(decodeURIComponent(key)+'='+decodeURIComponent(val))});query_string=q_list.join('&');uri=uri+(query_string?'?'+query_string:'')}else if(val.toString().trim())uri=uri+"?"+decodeURIComponent(key)+"="+decodeURIComponent(val);var re=/https:\/\/([^:\/]+)(:\d+|)/i,check=re.exec(uri);if(check){var toReplace="http://"+check[1];if(typeof (http_port)!='undefined'&&http_port!=80)toReplace+=":"+http_port;uri=uri.replace(re,toReplace)};var bUseSSL=false;if(typeof (enforce_ssl)!='undefined'&&enforce_ssl){bUseSSL=true}else if(typeof (ssl_actions)!='undefined'&&typeof (ssl_actions.length)!='undefined'&&uri.getQuery('act')){var act=uri.getQuery('act');for(i=0;i-1&&!url.getQuery('vid'))url=url.setQuery('vid',xeVid);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} -function popopen(url,target){if(typeof (target)=="undefined")target="_blank";if(typeof (xeVid)!='undefined'&&url.indexOf(request_uri)>-1&&!url.getQuery('vid'))url=url.setQuery('vid',xeVid);winopen(url,target,"left=10,top=10,width=10,height=10,scrollbars=no,resizable=yes,toolbars=no")} -function sendMailTo(to){location.href="mailto:"+to} -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} -function displayMultimedia(src,width,height,options){var html=_displayMultimedia(src,width,height,options);if(html)document.writeln(html)} -function _displayMultimedia(src,width,height,options){if(src.indexOf('files')==0)src=request_uri+src;var defaults={wmode:'transparent',allowScriptAccess:'sameDomain',quality:'high',flashvars:'',autostart:false},params=jQuery.extend(defaults,options||{}),autostart=(params.autostart&¶ms.autostart!='false')?'true':'false';delete (params.autostart);var clsid="",codebase="",html="";if(/\.(gif|jpg|jpeg|bmp|png)$/i.test(src)){html=''}else if(/\.flv$/i.test(src)||/\.mov$/i.test(src)||/\.moov$/i.test(src)||/\.m4v$/i.test(src)){html=''}else if(/\.swf/i.test(src)){clsid='clsid:D27CDB6E-AE6D-11cf-96B8-444553540000';if(typeof (enforce_ssl)!='undefined'&&enforce_ssl){codebase="https://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,28,0"}else codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,28,0";html='';html+='';for(var name in params)if(params[name]!='undefined'&¶ms[name]!='')html+='';html+=''}else{if(jQuery.browser.mozilla||jQuery.browser.opera)autostart=(params.autostart&¶ms.autostart!='false')?'1':'0';html='400)$body.css({overflow:'auto',overflowX:'hidden',height:400+'px'});var $win=$(window),$pc=$('#popup_content'),w=Math.max($pc[0].offsetWidth,600),h=$pc[0].offsetHeight,dw=$win.width(),dh=$win.height(),_w=0,_h=0;if(w!=dw)_w=w-dw;if(h!=dh)_h=h-dh;if(_w||_h)window.resizeBy(_w,_h);if(!arguments.callee.executed){setTimeout(setFixedPopupSize,300);arguments.callee.executed=true}} -function doCallModuleAction(module,action,target_srl){var params=new Array();params.target_srl=target_srl;params.cur_mid=current_mid;exec_xml(module,action,params,completeCallModuleAction)} -function completeCallModuleAction(ret_obj,response_tags){if(ret_obj.message!='success')alert(ret_obj.message);location.reload()} -function completeMessage(ret_obj){alert(ret_obj.message);location.reload()} -function doChangeLangType(obj){if(typeof (obj)=="string"){setLangType(obj)}else{var val=obj.options[obj.selectedIndex].value;setLangType(val)};location.href=location.href.setQuery('l','')} -function setLangType(lang_type){var expire=new Date();expire.setTime(expire.getTime()+(7000*24*3600000));setCookie('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 editor_sequence=fo_obj.getAttribute('editor_sequence'),content=editorGetContent(editor_sequence),win=window.open("","previewDocument","toolbars=no,width=700px;height=800px,scrollbars=yes,resizable=yes"),dummy_obj=jQuery("#previewDocument");if(!dummy_obj.length)dummy_obj=jQuery('').appendTo(document.body);dummy_obj.find('input[name="content"]').val(content).end().submit()} -function doDocumentSave(obj){var editor_sequence=obj.form.getAttribute('editor_sequence'),prev_content=editorRelKeys[editor_sequence]['content'].value;if(typeof (editor_sequence)!='undefined'&&editor_sequence&&typeof (editorRelKeys)!='undefined'&&typeof (editorGetContent)=='function'){var content=editorGetContent(editor_sequence);editorRelKeys[editor_sequence]['content'].value=content};var params={},responses=['error','message','document_srl'],elms=obj.form.elements,data=jQuery(obj.form).serializeArray();jQuery.each(data,function(i,field){var val=jQuery.trim(field.value);if(!val)return true;if(/\[\]$/.test(field.name))field.name=field.name.replace(/\[\]$/,'');if(params[field.name]){params[field.name]+='|@|'+val}else params[field.name]=field.value});exec_xml('member','procMemberSaveDocument',params,completeDocumentSave,responses,params,obj.form);editorRelKeys[editor_sequence]['content'].value=prev_content;return false} -function completeDocumentSave(ret_obj){jQuery('input[name=document_srl]').eq(0).val(ret_obj.document_srl);alert(ret_obj.message)};var objForSavedDoc=null -function doDocumentLoad(obj){objForSavedDoc=obj.form;popopen(request_uri.setQuery('module','member').setQuery('act','dispSavedDocumentList'))} -function doDocumentSelect(document_srl){if(!opener||!opener.objForSavedDoc){window.close();return};opener.location.href=opener.current_url.setQuery('document_srl',document_srl).setQuery('act','dispBoardWrite');window.close()} -function viewSkinInfo(module,skin){popopen("./?module=module&act=dispModuleSkinInfo&selected_module="+module+"&skin="+skin,'SkinInfo')};var addedDocument=new Array() -function doAddDocumentCart(obj){var srl=obj.value;addedDocument[addedDocument.length]=srl;setTimeout(function(){callAddDocumentCart(addedDocument.length)},100)} -function callAddDocumentCart(document_length){if(addedDocument.length<1||document_length!=addedDocument.length)return;var params=new Array();params.srls=addedDocument.join(",");exec_xml("document","procDocumentAddCart",params,null);addedDocument=new Array()} -function transRGB2Hex(value){if(!value)return value;if(value.indexOf('#')>-1)return value.replace(/^#/,'');if(value.toLowerCase().indexOf('rgb')<0)return value;value=value.replace(/^rgb\(/i,'').replace(/\)$/,'');value_list=value.split(',');var hex='';for(var i=0;i>2;enc2=((chr1&3)<<4)|(chr2>>4);enc3=((chr2&15)<<2)|(chr3>>6);enc4=chr3&63;if(isNaN(chr2)){enc3=enc4=64}else if(isNaN(chr3))enc4=64;output=output+this._keyStr.charAt(enc1)+this._keyStr.charAt(enc2)+this._keyStr.charAt(enc3)+this._keyStr.charAt(enc4)};return output},decode:function(input){var output="",chr1,chr2,chr3,enc1,enc2,enc3,enc4,i=0;input=input.replace(/[^A-Za-z0-9\+\/\=]/g,"");while(i>4);chr2=((enc2&15)<<4)|(enc3>>2);chr3=((enc3&3)<<6)|enc4;output=output+String.fromCharCode(chr1);if(enc3!=64)output=output+String.fromCharCode(chr2);if(enc4!=64)output=output+String.fromCharCode(chr3)};output=Base64._utf8_decode(output);return output},_utf8_encode:function(string){string=string.replace(/\r\n/g,"\n");var utftext="";for(var n=0;n127)&&(c<2048)){utftext+=String.fromCharCode((c>>6)|192);utftext+=String.fromCharCode((c&63)|128)}else{utftext+=String.fromCharCode((c>>12)|224);utftext+=String.fromCharCode(((c>>6)&63)|128);utftext+=String.fromCharCode((c&63)|128)}};return utftext},_utf8_decode:function(utftext){var string="",i=0,c=c1=c2=0;while(i191)&&(c<224)){c2=utftext.charCodeAt(i+1);string+=String.fromCharCode(((c&31)<<6)|(c2&63));i+=2}else{c2=utftext.charCodeAt(i+1);c3=utftext.charCodeAt(i+2);string+=String.fromCharCode(((c&15)<<12)|((c2&63)<<6)|(c3&63));i+=3}};return string}};if(typeof (resizeImageContents)=='undefined')function resizeImageContents(){};if(typeof (activateOptionDisabled)=='undefined')function activateOptionDisabled(){};objectExtend=jQuery.extend -function toggleDisplay(objId){jQuery('#'+objId).toggle()} -function checkboxSelectAll(formObj,name,checked){var itemName=name,option={};if(typeof (formObj)!="undefined")option.wrap=formObj;if(typeof (checked)!="undefined")option.checked=checked;XE.checkboxToggleAll(itemName,option)} -function clickCheckBoxAll(formObj,name){var itemName=name,option={doClick:true};if(typeof (formObj)!="undefined")option.wrap=formObj;XE.checkboxToggleAll(itemName,option)} -function svc_folder_open(id){jQuery("#_folder_open_"+id).hide();jQuery("#_folder_close_"+id).show();jQuery("#_folder_"+id).show()} -function svc_folder_close(id){jQuery("#_folder_open_"+id).show();jQuery("#_folder_close_"+id).hide();jQuery("#_folder_"+id).hide()} -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')};var loaded_popup_menus=XE.loaded_popup_menus -function createPopupMenu(){} -function chkPopupMenu(){} -function displayPopupMenu(ret_obj,response_tags,params){XE.displayPopupMenu(ret_obj,response_tags,params)} -function GetObjLeft(obj){return jQuery(obj).offset().left} -function GetObjTop(obj){return jQuery(obj).offset().top} -function replaceOuterHTML(obj,html){jQuery(obj).replaceWith(html)} -function getOuterHTML(obj){return jQuery(obj).html().trim()} -function setCookie(name,value,expire,path){var s_cookie=name+"="+escape(value)+((!expire)?"":("; expires="+expire.toGMTString()))+"; path="+((!path)?"/":path);document.cookie=s_cookie};jQuery(function(){jQuery(".lang_code").each(function(){var objText=jQuery(this),targetName=objText.attr("id");if(typeof (targetName)=="undefined")targetName=objText.attr("name");if(typeof (targetName)=="undefined")return;objText.after("find_langcode")})}) \ No newline at end of file + +/* jQuery 참조변수($) 제거 */ +if(jQuery) jQuery.noConflict(); + +(function($) { + /* OS check */ + var UA = navigator.userAgent.toLowerCase(); + $.os = { + Linux: /linux/.test(UA), + Unix: /x11/.test(UA), + Mac: /mac/.test(UA), + Windows: /win/.test(UA) + }; + $.os.name = ($.os.Windows) ? 'Windows' : + ($.os.Linux) ? 'Linux' : + ($.os.Unix) ? 'Unix' : + ($.os.Mac) ? 'Mac' : ''; + + /** + * @brief XE 공용 유틸리티 함수 + * @namespace XE + */ + window.XE = { + loaded_popup_menus : new Array(), + addedDocument : new Array(), + /** + * @brief 특정 name을 가진 체크박스들의 checked 속성 변경 + * @param [itemName='cart',][options={}] + */ + checkboxToggleAll : function(itemName) { + if(!is_def(itemName)) itemName='cart'; + var options = { + wrap : null, + checked : 'toggle', + doClick : false + }; + + switch(arguments.length) { + case 1: + if(typeof(arguments[0]) == "string") { + itemName = arguments[0]; + } else { + $.extend(options, arguments[0] || {}); + itemName = 'cart'; + } + break; + case 2: + itemName = arguments[0]; + $.extend(options, arguments[1] || {}); + } + + if(options.doClick == true) options.checked = null; + if(typeof(options.wrap) == "string") options.wrap ='#'+options.wrap; + + if(options.wrap) { + var obj = $(options.wrap).find('input[name='+itemName+']:checkbox'); + } else { + var obj = $('input[name='+itemName+']:checkbox'); + } + + if(options.checked == 'toggle') { + obj.each(function() { + $(this).attr('checked', ($(this).attr('checked')) ? false : true); + }); + } else { + (options.doClick == true) ? obj.click() : obj.attr('checked', options.checked); + } + }, + + /** + * @brief 문서/회원 등 팝업 메뉴 출력 + */ + displayPopupMenu : function(ret_obj, response_tags, params) { + var target_srl = params["target_srl"]; + var menu_id = params["menu_id"]; + var menus = ret_obj['menus']; + var html = ""; + + if(this.loaded_popup_menus[menu_id]) { + html = this.loaded_popup_menus[menu_id]; + + } else { + if(menus) { + var item = menus['item']; + if(typeof(item.length)=='undefined' || item.length<1) item = new Array(item); + if(item.length) { + for(var i=0;i'+str+' '; + } + } + } + this.loaded_popup_menus[menu_id] = html; + } + + /* 레이어 출력 */ + if(html) { + var area = $('#popup_menu_area').html('
    '+html+'
'); + var areaOffset = {top:params['page_y'], left:params['page_x']}; + + if(area.outerHeight()+areaOffset.top > $(window).height()+$(window).scrollTop()) + areaOffset.top = $(window).height() - area.outerHeight() + $(window).scrollTop(); + if(area.outerWidth()+areaOffset.left > $(window).width()+$(window).scrollLeft()) + areaOffset.left = $(window).width() - area.outerWidth() + $(window).scrollLeft(); + + area.css({ top:areaOffset.top, left:areaOffset.left }).show(); + } + } + } + +}) (jQuery); + + + +/* jQuery(document).ready() */ +jQuery(function($) { + + /* select - option의 disabled=disabled 속성을 IE에서도 체크하기 위한 함수 */ + if($.browser.msie) { + $('select').each(function(i, sels) { + var disabled_exists = false; + var first_enable = new Array(); + + for(var j=0; j < sels.options.length; j++) { + if(sels.options[j].disabled) { + sels.options[j].style.color = '#CCCCCC'; + disabled_exists = true; + }else{ + first_enable[i] = (first_enable[i] > -1) ? first_enable[i] : j; + } + } + + if(!disabled_exists) return; + + sels.oldonchange = sels.onchange; + sels.onchange = function() { + if(this.options[this.selectedIndex].disabled) { + + this.selectedIndex = first_enable[i]; + /* + if(this.options.length<=1) this.selectedIndex = -1; + else if(this.selectedIndex < this.options.length - 1) this.selectedIndex++; + else this.selectedIndex--; + */ + + } else { + if(this.oldonchange) this.oldonchange(); + } + }; + + if(sels.selectedIndex >= 0 && sels.options[ sels.selectedIndex ].disabled) sels.onchange(); + + }); + } + + /* 단락에디터 fold 컴포넌트 펼치기/접기 */ + var drEditorFold = $('.xe_content .fold_button'); + if(drEditorFold.size()) { + var fold_container = $('div.fold_container', drEditorFold); + $('button.more', drEditorFold).click(function() { + $(this).hide().next('button').show().parent().next(fold_container).show(); + }); + $('button.less', drEditorFold).click(function() { + $(this).hide().prev('button').show().parent().next(fold_container).hide(); + }); + } + +}); + + +/** + * @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.replace(/#$/, ''); + + if(idx != -1) { + var query_string = uri.substr(idx+1, this.length), args = {}, q_list = []; + uri = this.substr(0, idx); + query_string.replace(/([^=]+)=([^&]*)(&|$)/g, function(all,key,val) { args[key] = val; }); + + args[key] = val; + + jQuery.each(args, function(key,val){ + if (!jQuery.trim(val)) return; + q_list.push(key+'='+decodeURI(val)); + }); + + query_string = q_list.join('&'); + uri = uri+(query_string?'?'+query_string:''); + } else { + if(val.toString().trim()) uri = uri+"?"+key+"="+val; + } + + var re = /https:\/\/([^:\/]+)(:\d+|)/i; + var check = re.exec(uri); + if(check) + { + var toReplace = "http://"+check[1]; + if(typeof(http_port)!='undefined' && http_port != 80) + { + toReplace += ":" + http_port; + } + uri = uri.replace(re,toReplace); + } + var bUseSSL = false; + if(typeof(enforce_ssl)!='undefined' && enforce_ssl) + { + bUseSSL = true; + } + else if(typeof(ssl_actions)!='undefined' && typeof(ssl_actions.length)!='undefined' && uri.getQuery('act')) { + var act = uri.getQuery('act'); + for(i=0;i-1 && !url.getQuery('vid')) url = url.setQuery('vid',xeVid); + 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"; + if(typeof(xeVid)!='undefined' && url.indexOf(request_uri)>-1 && !url.getQuery('vid')) url = url.setQuery('vid',xeVid); + winopen(url, target, "width=650,height=500,scrollbars=yes,resizable=yes,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 멀티미디어 출력용 (IE에서 플래쉬/동영상 주변에 점선 생김 방지용) + **/ +function displayMultimedia(src, width, height, options) { + var html = _displayMultimedia(src, width, height, options); + if(html) document.writeln(html); +} +function _displayMultimedia(src, width, height, options) { + if(src.indexOf('files') == 0) src = request_uri + src; + + var defaults = { + wmode : 'transparent', + allowScriptAccess : 'sameDomain', + quality : 'high', + flashvars : '', + autostart : false + }; + + var params = jQuery.extend(defaults, options || {}); + var autostart = (params.autostart && params.autostart != 'false') ? 'true' : 'false'; + delete(params.autostart); + + var clsid = ""; + var codebase = ""; + var html = ""; + + if(/\.(gif|jpg|jpeg|bmp|png)$/i.test(src)){ + html = ''; + } else if(/\.flv$/i.test(src) || /\.mov$/i.test(src) || /\.moov$/i.test(src) || /\.m4v$/i.test(src)) { + html = ''; + } else if(/\.swf/i.test(src)) { + clsid = 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000'; + + if(typeof(enforce_ssl)!='undefined' && enforce_ssl){ codebase = "https://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,28,0"; } + else { codebase = "http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,28,0"; } + html = ''; + html += ''; + for(var name in params) { + if(params[name] != 'undefined' && params[name] != '') { + html += ''; + } + } + html += '' + + '' + + ''; + } else { + if (jQuery.browser.mozilla || jQuery.browser.opera) { + // firefox and opera uses 0 or 1 for autostart parameter. + autostart = (params.autostart && params.autostart != 'false') ? '1' : '0'; + } + + html = '.popup'), w, h, dw, dh, offset; + + offset = $pc.css({overflow:'scroll'}).offset(); + + w = $pc.width(10).height(10000).get(0).scrollWidth + offset.left*2; + h = $pc.height(10).width(10000).get(0).scrollHeight + offset.top*2; + + if(w < 600) w = 600 + offset.left*2; + + dw = $win.width(); + dh = $win.height(); + + if(w != dw) window.resizeBy(w - dw, 0); + if(h != dh) window.resizeBy(0, h - dh); + + $pc.width(w-offset.left*2).css({overflow:'',height:''}); +} + +/** + * @brief 추천/비추천,스크랩,신고기능등 특정 srl에 대한 특정 module/action을 호출하는 함수 + **/ +function doCallModuleAction(module, action, target_srl) { + var params = { + target_srl : target_srl, + cur_mid : current_mid, + mid : current_mid + }; + exec_xml(module, action, params, completeCallModuleAction); +} + +function completeCallModuleAction(ret_obj, response_tags) { + if(ret_obj['message']!='success') alert(ret_obj['message']); + location.reload(); +} + +function completeMessage(ret_obj) { + alert(ret_obj['message']); + location.reload(); +} + + + +/* 언어코드 (lang_type) 쿠키값 변경 */ +function doChangeLangType(obj) { + if(typeof(obj) == "string") { + setLangType(obj); + } else { + var val = obj.options[obj.selectedIndex].value; + setLangType(val); + } + location.href = location.href.setQuery('l', ''); +} +function setLangType(lang_type) { + var expire = new Date(); + expire.setTime(expire.getTime()+ (7000 * 24 * 3600000)); + setCookie('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 editor_sequence = fo_obj.getAttribute('editor_sequence'); + + var content = editorGetContent(editor_sequence); + + var win = window.open("", "previewDocument","toolbars=no,width=700px;height=800px,scrollbars=yes,resizable=yes"); + + var dummy_obj = jQuery("#previewDocument"); + + if(!dummy_obj.length) { + jQuery( + '
'+ + ''+ + ''+ + ''+ + '' + ).appendTo(document.body); + + dummy_obj = jQuery("#previewDocument")[0]; + } + + if(dummy_obj) { + dummy_obj.content.value = content; + dummy_obj.submit(); + } +} + +/* 게시글 저장 */ +function doDocumentSave(obj) { + var editor_sequence = obj.form.getAttribute('editor_sequence'); + var prev_content = editorRelKeys[editor_sequence]['content'].value; + if(typeof(editor_sequence)!='undefined' && editor_sequence && typeof(editorRelKeys)!='undefined' && typeof(editorGetContent)=='function') { + var content = editorGetContent(editor_sequence); + editorRelKeys[editor_sequence]['content'].value = content; + } + + var params={}, responses=['error','message','document_srl'], elms=obj.form.elements, data=jQuery(obj.form).serializeArray();; + jQuery.each(data, function(i, field){ + var val = jQuery.trim(field.value); + if(!val) return true; + if(/\[\]$/.test(field.name)) field.name = field.name.replace(/\[\]$/, ''); + if(params[field.name]) params[field.name] += '|@|'+val; + else params[field.name] = field.value; + }); + + exec_xml('document','procDocumentTempSave', params, completeDocumentSave, responses, params, obj.form); + + editorRelKeys[editor_sequence]['content'].value = prev_content; + return false; +} + +function completeDocumentSave(ret_obj) { + jQuery('input[name=document_srl]').eq(0).val(ret_obj['document_srl']); + alert(ret_obj['message']); +} + +/* 저장된 게시글 불러오기 */ +var objForSavedDoc = null; +function doDocumentLoad(obj) { + // 저장된 게시글 목록 불러오기 + objForSavedDoc = obj.form; + popopen(request_uri.setQuery('module','document').setQuery('act','dispTempSavedList')); +} + +/* 저장된 게시글의 선택 */ +function doDocumentSelect(document_srl) { + if(!opener || !opener.objForSavedDoc) { + window.close(); + return; + } + + // 게시글을 가져와서 등록하기 + opener.location.href = opener.current_url.setQuery('document_srl', document_srl).setQuery('act', 'dispBoardWrite'); + window.close(); +} + + +/* 스킨 정보 */ +function viewSkinInfo(module, skin) { + popopen("./?module=module&act=dispModuleSkinInfo&selected_module="+module+"&skin="+skin, 'SkinInfo'); +} + + +/* 관리자가 문서를 관리하기 위해서 선택시 세션에 넣음 */ +var addedDocument = new Array(); +function doAddDocumentCart(obj) { + var srl = obj.value; + addedDocument[addedDocument.length] = srl; + setTimeout(function() { callAddDocumentCart(addedDocument.length); }, 100); +} + +function callAddDocumentCart(document_length) { + if(addedDocument.length<1 || document_length != addedDocument.length) return; + var params = new Array(); + params["srls"] = addedDocument.join(","); + exec_xml("document","procDocumentAddCart", params, null); + addedDocument = new Array(); +} + +/* ff의 rgb(a,b,c)를 #... 로 변경 */ +function transRGB2Hex(value) { + if(!value) return value; + if(value.indexOf('#') > -1) return value.replace(/^#/, ''); + + if(value.toLowerCase().indexOf('rgb') < 0) return value; + value = value.replace(/^rgb\(/i, '').replace(/\)$/, ''); + value_list = value.split(','); + + var hex = ''; + for(var i = 0; i < value_list.length; i++) { + var color = parseInt(value_list[i], 10).toString(16); + if(color.length == 1) color = '0'+color; + hex += color; + } + return hex; +} + +/* 보안 로그인 모드로 전환 */ +function toggleSecuritySignIn() { + var href = location.href; + if(/https:\/\//i.test(href)) location.href = href.replace(/^https/i,'http'); + else location.href = href.replace(/^http/i,'https'); +} + +function reloadDocument() { + location.reload(); +} + + +/** +* +* Base64 encode / decode +* http://www.webtoolkit.info/ +* +**/ + +var Base64 = { + + // private property + _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", + + // public method for encoding + encode : function (input) { + var output = ""; + var chr1, chr2, chr3, enc1, enc2, enc3, enc4; + var i = 0; + + input = Base64._utf8_encode(input); + + while (i < input.length) { + + chr1 = input.charCodeAt(i++); + chr2 = input.charCodeAt(i++); + chr3 = input.charCodeAt(i++); + + enc1 = chr1 >> 2; + enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); + enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); + enc4 = chr3 & 63; + + if (isNaN(chr2)) { + enc3 = enc4 = 64; + } else if (isNaN(chr3)) { + enc4 = 64; + } + + output = output + + this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + + this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4); + + } + + return output; + }, + + // public method for decoding + decode : function (input) { + var output = ""; + var chr1, chr2, chr3; + var enc1, enc2, enc3, enc4; + var i = 0; + + input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); + + while (i < input.length) { + + enc1 = this._keyStr.indexOf(input.charAt(i++)); + enc2 = this._keyStr.indexOf(input.charAt(i++)); + enc3 = this._keyStr.indexOf(input.charAt(i++)); + enc4 = this._keyStr.indexOf(input.charAt(i++)); + + chr1 = (enc1 << 2) | (enc2 >> 4); + chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); + chr3 = ((enc3 & 3) << 6) | enc4; + + output = output + String.fromCharCode(chr1); + + if (enc3 != 64) { + output = output + String.fromCharCode(chr2); + } + if (enc4 != 64) { + output = output + String.fromCharCode(chr3); + } + + } + + output = Base64._utf8_decode(output); + + return output; + + }, + + // private method for UTF-8 encoding + _utf8_encode : function (string) { + string = string.replace(/\r\n/g,"\n"); + var utftext = ""; + + for (var n = 0; n < string.length; n++) { + + var c = string.charCodeAt(n); + + if (c < 128) { + utftext += String.fromCharCode(c); + } + else if((c > 127) && (c < 2048)) { + utftext += String.fromCharCode((c >> 6) | 192); + utftext += String.fromCharCode((c & 63) | 128); + } + else { + utftext += String.fromCharCode((c >> 12) | 224); + utftext += String.fromCharCode(((c >> 6) & 63) | 128); + utftext += String.fromCharCode((c & 63) | 128); + } + + } + + return utftext; + }, + + // private method for UTF-8 decoding + _utf8_decode : function (utftext) { + var string = ""; + var i = 0; + var c = c1 = c2 = 0; + + while ( i < utftext.length ) { + + c = utftext.charCodeAt(i); + + if (c < 128) { + string += String.fromCharCode(c); + i++; + } + else if((c > 191) && (c < 224)) { + c2 = utftext.charCodeAt(i+1); + string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); + i += 2; + } + else { + c2 = utftext.charCodeAt(i+1); + c3 = utftext.charCodeAt(i+2); + string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); + i += 3; + } + + } + + return string; + } + +} + + + + + + +/* ---------------------------------------------- + * DEPRECATED + * 하위호환용으로 남겨 놓음 + * ------------------------------------------- */ + +if(typeof(resizeImageContents) == 'undefined') { + function resizeImageContents() {} +} + +if(typeof(activateOptionDisabled) == 'undefined') { + function activateOptionDisabled() {} +} + +objectExtend = jQuery.extend; + +/** + * @brief 특정 Element의 display 옵션 토글 + **/ +function toggleDisplay(objId) { + jQuery('#'+objId).toggle(); +} + +/* 체크박스 선택 */ +function checkboxSelectAll(formObj, name, checked) { + var itemName = name; + var option = {}; + if(typeof(formObj) != "undefined") option.wrap = formObj; + if(typeof(checked) != "undefined") option.checked = checked; + + XE.checkboxToggleAll(itemName, option); +} + +/* 체크박스를 실행 */ +function clickCheckBoxAll(formObj, name) { + var itemName = name; + var option = { doClick:true }; + if(typeof(formObj) != "undefined") option.wrap = formObj; + + XE.checkboxToggleAll(itemName, option); +} + +/** + * @brief 에디터에서 사용하되 내용 여닫는 코드 (zb5beta beta 호환용으로 남겨 놓음) + **/ +function svc_folder_open(id) { + jQuery("#_folder_open_"+id).hide(); + jQuery("#_folder_close_"+id).show(); + jQuery("#_folder_"+id).show(); +} +function svc_folder_close(id) { + jQuery("#_folder_open_"+id).show(); + jQuery("#_folder_close_"+id).hide(); + jQuery("#_folder_"+id).hide(); +} + +/** + * @brief 날짜 선택 (달력 열기) + **/ +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'); +} + +var loaded_popup_menus = XE.loaded_popup_menus; +function createPopupMenu() {} +function chkPopupMenu() {} +function displayPopupMenu(ret_obj, response_tags, params) { + XE.displayPopupMenu(ret_obj, response_tags, params); +} + +function GetObjLeft(obj) { + return jQuery(obj).offset().left; +} +function GetObjTop(obj) { + return jQuery(obj).offset().top; +} + +function replaceOuterHTML(obj, html) { + jQuery(obj).replaceWith(html); +} + +function getOuterHTML(obj) { + return jQuery(obj).html().trim(); +} + +function setCookie(name, value, expire, path) { + var s_cookie = name + "=" + escape(value) + + ((!expire) ? "" : ("; expires=" + expire.toGMTString())) + + "; path=" + ((!path) ? "/" : path); + + document.cookie = s_cookie; +} + +function getCookie(name) { + var match = document.cookie.match(new RegExp(name+'=(.*?)(?:;|$)')); + if(match) return unescape(match[1]); +} + +function is_def(v) { + return (typeof(v)!='undefined'); +} + +function ucfirst(str) { + return str.charAt(0).toUpperCase() + str.slice(1); +} + +function get_by_id(id) { + return document.getElementById(id); +} + +jQuery(function($){ + $('.lang_code').each( + function() + { + var objText = $(this); + var targetName = objText.attr("id"); + if(typeof(targetName) == "undefined") targetName = objText.attr("name"); + if(typeof(targetName) == "undefined") return; + objText.after("find_langcode"); + } + ); + + // display popup menu that contains member actions and document actions + $(document).click(function(evt) { + var $area = $('#popup_menu_area'); + if(!$area.length) $area = $('
a";var f=d.getElementsByTagName("*"),h=d.getElementsByTagName("a")[0],l=t.createElement("select"), -k=l.appendChild(t.createElement("option"));if(!(!f||!f.length||!h)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(h.getAttribute("style")),hrefNormalized:h.getAttribute("href")==="/a",opacity:/^0.55$/.test(h.style.opacity),cssFloat:!!h.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:k.selected,deleteExpando:true,optDisabled:false,checkClone:false, -scriptEval:false,noCloneEvent:true,boxModel:null,inlineBlockNeedsLayout:false,shrinkWrapBlocks:false,reliableHiddenOffsets:true};l.disabled=true;c.support.optDisabled=!k.disabled;b.type="text/javascript";try{b.appendChild(t.createTextNode("window."+e+"=1;"))}catch(o){}a.insertBefore(b,a.firstChild);if(E[e]){c.support.scriptEval=true;delete E[e]}try{delete b.test}catch(x){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function r(){c.support.noCloneEvent= -false;d.detachEvent("onclick",r)});d.cloneNode(true).fireEvent("onclick")}d=t.createElement("div");d.innerHTML="";a=t.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var r=t.createElement("div");r.style.width=r.style.paddingLeft="1px";t.body.appendChild(r);c.boxModel=c.support.boxModel=r.offsetWidth===2;if("zoom"in r.style){r.style.display="inline";r.style.zoom= -1;c.support.inlineBlockNeedsLayout=r.offsetWidth===2;r.style.display="";r.innerHTML="
";c.support.shrinkWrapBlocks=r.offsetWidth!==2}r.innerHTML="
t
";var A=r.getElementsByTagName("td");c.support.reliableHiddenOffsets=A[0].offsetHeight===0;A[0].style.display="";A[1].style.display="none";c.support.reliableHiddenOffsets=c.support.reliableHiddenOffsets&&A[0].offsetHeight===0;r.innerHTML="";t.body.removeChild(r).style.display= -"none"});a=function(r){var A=t.createElement("div");r="on"+r;var C=r in A;if(!C){A.setAttribute(r,"return;");C=typeof A[r]==="function"}return C};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=f=h=null}})();var ra={},Ja=/^(?:\{.*\}|\[.*\])$/;c.extend({cache:{},uuid:0,expando:"jQuery"+c.now(),noData:{embed:true,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:true},data:function(a,b,d){if(c.acceptData(a)){a=a==E?ra:a;var e=a.nodeType,f=e?a[c.expando]:null,h= -c.cache;if(!(e&&!f&&typeof b==="string"&&d===B)){if(e)f||(a[c.expando]=f=++c.uuid);else h=a;if(typeof b==="object")if(e)h[f]=c.extend(h[f],b);else c.extend(h,b);else if(e&&!h[f])h[f]={};a=e?h[f]:h;if(d!==B)a[b]=d;return typeof b==="string"?a[b]:a}}},removeData:function(a,b){if(c.acceptData(a)){a=a==E?ra:a;var d=a.nodeType,e=d?a[c.expando]:a,f=c.cache,h=d?f[e]:e;if(b){if(h){delete h[b];d&&c.isEmptyObject(h)&&c.removeData(a)}}else if(d&&c.support.deleteExpando)delete a[c.expando];else if(a.removeAttribute)a.removeAttribute(c.expando); -else if(d)delete f[e];else for(var l in a)delete a[l]}},acceptData:function(a){if(a.nodeName){var b=c.noData[a.nodeName.toLowerCase()];if(b)return!(b===true||a.getAttribute("classid")!==b)}return true}});c.fn.extend({data:function(a,b){var d=null;if(typeof a==="undefined"){if(this.length){var e=this[0].attributes,f;d=c.data(this[0]);for(var h=0,l=e.length;h-1)return true;return false},val:function(a){if(!arguments.length){var b=this[0];if(b){if(c.nodeName(b,"option")){var d=b.attributes.value;return!d||d.specified?b.value:b.text}if(c.nodeName(b,"select")){var e=b.selectedIndex;d=[];var f=b.options;b=b.type==="select-one"; -if(e<0)return null;var h=b?e:0;for(e=b?e+1:f.length;h=0;else if(c.nodeName(this,"select")){var A=c.makeArray(r);c("option",this).each(function(){this.selected=c.inArray(c(this).val(),A)>=0});if(!A.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true}, -attr:function(a,b,d,e){if(!a||a.nodeType===3||a.nodeType===8)return B;if(e&&b in c.attrFn)return c(a)[b](d);e=a.nodeType!==1||!c.isXMLDoc(a);var f=d!==B;b=e&&c.props[b]||b;var h=Ta.test(b);if((b in a||a[b]!==B)&&e&&!h){if(f){b==="type"&&Ua.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");if(d===null)a.nodeType===1&&a.removeAttribute(b);else a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&& -b.specified?b.value:Va.test(a.nodeName)||Wa.test(a.nodeName)&&a.href?0:B;return a[b]}if(!c.support.style&&e&&b==="style"){if(f)a.style.cssText=""+d;return a.style.cssText}f&&a.setAttribute(b,""+d);if(!a.attributes[b]&&a.hasAttribute&&!a.hasAttribute(b))return B;a=!c.support.hrefNormalized&&e&&h?a.getAttribute(b,2):a.getAttribute(b);return a===null?B:a}});var X=/\.(.*)$/,ia=/^(?:textarea|input|select)$/i,La=/\./g,Ma=/ /g,Xa=/[^\w\s.|`]/g,Ya=function(a){return a.replace(Xa,"\\$&")},ua={focusin:0,focusout:0}; -c.event={add:function(a,b,d,e){if(!(a.nodeType===3||a.nodeType===8)){if(c.isWindow(a)&&a!==E&&!a.frameElement)a=E;if(d===false)d=U;else if(!d)return;var f,h;if(d.handler){f=d;d=f.handler}if(!d.guid)d.guid=c.guid++;if(h=c.data(a)){var l=a.nodeType?"events":"__events__",k=h[l],o=h.handle;if(typeof k==="function"){o=k.handle;k=k.events}else if(!k){a.nodeType||(h[l]=h=function(){});h.events=k={}}if(!o)h.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem, -arguments):B};o.elem=a;b=b.split(" ");for(var x=0,r;l=b[x++];){h=f?c.extend({},f):{handler:d,data:e};if(l.indexOf(".")>-1){r=l.split(".");l=r.shift();h.namespace=r.slice(0).sort().join(".")}else{r=[];h.namespace=""}h.type=l;if(!h.guid)h.guid=d.guid;var A=k[l],C=c.event.special[l]||{};if(!A){A=k[l]=[];if(!C.setup||C.setup.call(a,e,r,o)===false)if(a.addEventListener)a.addEventListener(l,o,false);else a.attachEvent&&a.attachEvent("on"+l,o)}if(C.add){C.add.call(a,h);if(!h.handler.guid)h.handler.guid= -d.guid}A.push(h);c.event.global[l]=true}a=null}}},global:{},remove:function(a,b,d,e){if(!(a.nodeType===3||a.nodeType===8)){if(d===false)d=U;var f,h,l=0,k,o,x,r,A,C,J=a.nodeType?"events":"__events__",w=c.data(a),I=w&&w[J];if(w&&I){if(typeof I==="function"){w=I;I=I.events}if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(f in I)c.event.remove(a,f+b)}else{for(b=b.split(" ");f=b[l++];){r=f;k=f.indexOf(".")<0;o=[];if(!k){o=f.split(".");f=o.shift();x=RegExp("(^|\\.)"+ -c.map(o.slice(0).sort(),Ya).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(A=I[f])if(d){r=c.event.special[f]||{};for(h=e||0;h=0){a.type=f=f.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[f]&&c.each(c.cache,function(){this.events&&this.events[f]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType=== -8)return B;a.result=B;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(e=d.nodeType?c.data(d,"handle"):(c.data(d,"__events__")||{}).handle)&&e.apply(d,b);e=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+f]&&d["on"+f].apply(d,b)===false){a.result=false;a.preventDefault()}}catch(h){}if(!a.isPropagationStopped()&&e)c.event.trigger(a,b,e,true);else if(!a.isDefaultPrevented()){var l;e=a.target;var k=f.replace(X,""),o=c.nodeName(e,"a")&&k=== -"click",x=c.event.special[k]||{};if((!x._default||x._default.call(d,a)===false)&&!o&&!(e&&e.nodeName&&c.noData[e.nodeName.toLowerCase()])){try{if(e[k]){if(l=e["on"+k])e["on"+k]=null;c.event.triggered=true;e[k]()}}catch(r){}if(l)e["on"+k]=l;c.event.triggered=false}}},handle:function(a){var b,d,e,f;d=[];var h=c.makeArray(arguments);a=h[0]=c.event.fix(a||E.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;if(!b){e=a.type.split(".");a.type=e.shift();d=e.slice(0).sort();e=RegExp("(^|\\.)"+ -d.join("\\.(?:.*\\.)?")+"(\\.|$)")}a.namespace=a.namespace||d.join(".");f=c.data(this,this.nodeType?"events":"__events__");if(typeof f==="function")f=f.events;d=(f||{})[a.type];if(f&&d){d=d.slice(0);f=0;for(var l=d.length;f-1?c.map(a.options,function(e){return e.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},Z=function(a,b){var d=a.target,e,f;if(!(!ia.test(d.nodeName)||d.readOnly)){e=c.data(d,"_change_data");f=xa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",f);if(!(e===B||f===e))if(e!=null||f){a.type="change";a.liveFired= -B;return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:Z,beforedeactivate:Z,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return Z.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return Z.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,"_change_data",xa(a))}},setup:function(){if(this.type=== -"file")return false;for(var a in V)c.event.add(this,a+".specialChange",V[a]);return ia.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return ia.test(this.nodeName)}};V=c.event.special.change.filters;V.focus=V.beforeactivate}t.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(e){e=c.event.fix(e);e.type=b;return c.event.trigger(e,null,e.target)}c.event.special[b]={setup:function(){ua[b]++===0&&t.addEventListener(a,d,true)},teardown:function(){--ua[b]=== -0&&t.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,e,f){if(typeof d==="object"){for(var h in d)this[b](h,e,d[h],f);return this}if(c.isFunction(e)||e===false){f=e;e=B}var l=b==="one"?c.proxy(f,function(o){c(this).unbind(o,l);return f.apply(this,arguments)}):f;if(d==="unload"&&b!=="one")this.one(d,e,f);else{h=0;for(var k=this.length;h0?this.bind(b,d,e):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});E.attachEvent&&!E.addEventListener&&c(E).bind("unload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}}); -(function(){function a(g,i,n,m,p,q){p=0;for(var u=m.length;p0){F=y;break}}y=y[g]}m[p]=F}}}var d=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,e=0,f=Object.prototype.toString,h=false,l=true;[0,0].sort(function(){l=false;return 0});var k=function(g,i,n,m){n=n||[];var p=i=i||t;if(i.nodeType!==1&&i.nodeType!==9)return[];if(!g||typeof g!=="string")return n;var q,u,y,F,M,N=true,O=k.isXML(i),D=[],R=g;do{d.exec("");if(q=d.exec(R)){R=q[3];D.push(q[1]);if(q[2]){F=q[3]; -break}}}while(q);if(D.length>1&&x.exec(g))if(D.length===2&&o.relative[D[0]])u=L(D[0]+D[1],i);else for(u=o.relative[D[0]]?[i]:k(D.shift(),i);D.length;){g=D.shift();if(o.relative[g])g+=D.shift();u=L(g,u)}else{if(!m&&D.length>1&&i.nodeType===9&&!O&&o.match.ID.test(D[0])&&!o.match.ID.test(D[D.length-1])){q=k.find(D.shift(),i,O);i=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]}if(i){q=m?{expr:D.pop(),set:C(m)}:k.find(D.pop(),D.length===1&&(D[0]==="~"||D[0]==="+")&&i.parentNode?i.parentNode:i,O);u=q.expr?k.filter(q.expr, -q.set):q.set;if(D.length>0)y=C(u);else N=false;for(;D.length;){q=M=D.pop();if(o.relative[M])q=D.pop();else M="";if(q==null)q=i;o.relative[M](y,q,O)}}else y=[]}y||(y=u);y||k.error(M||g);if(f.call(y)==="[object Array]")if(N)if(i&&i.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&k.contains(i,y[g])))n.push(u[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&n.push(u[g]);else n.push.apply(n,y);else C(y,n);if(F){k(F,p,n,m);k.uniqueSort(n)}return n};k.uniqueSort=function(g){if(w){h= -l;g.sort(w);if(h)for(var i=1;i0};k.find=function(g,i,n){var m;if(!g)return[];for(var p=0,q=o.order.length;p":function(g,i){var n,m=typeof i==="string",p=0,q=g.length;if(m&&!/\W/.test(i))for(i=i.toLowerCase();p=0))n||m.push(u);else if(n)i[q]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},CHILD:function(g){if(g[1]==="nth"){var i=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=i[1]+(i[2]||1)-0;g[3]=i[3]-0}g[0]=e++;return g},ATTR:function(g,i,n, -m,p,q){i=g[1].replace(/\\/g,"");if(!q&&o.attrMap[i])g[1]=o.attrMap[i];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,i,n,m,p){if(g[1]==="not")if((d.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,i);else{g=k.filter(g[3],i,n,true^p);n||m.push.apply(m,g);return false}else if(o.match.POS.test(g[0])||o.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled=== -true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,i,n){return!!k(n[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"=== -g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},setFilters:{first:function(g,i){return i===0},last:function(g,i,n,m){return i===m.length-1},even:function(g,i){return i%2===0},odd:function(g,i){return i%2===1},lt:function(g,i,n){return in[3]-0},nth:function(g,i,n){return n[3]- -0===i},eq:function(g,i,n){return n[3]-0===i}},filter:{PSEUDO:function(g,i,n,m){var p=i[1],q=o.filters[p];if(q)return q(g,n,i,m);else if(p==="contains")return(g.textContent||g.innerText||k.getText([g])||"").indexOf(i[3])>=0;else if(p==="not"){i=i[3];n=0;for(m=i.length;n=0}},ID:function(g,i){return g.nodeType===1&&g.getAttribute("id")===i},TAG:function(g,i){return i==="*"&&g.nodeType===1||g.nodeName.toLowerCase()=== -i},CLASS:function(g,i){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(i)>-1},ATTR:function(g,i){var n=i[1];n=o.attrHandle[n]?o.attrHandle[n](g):g[n]!=null?g[n]:g.getAttribute(n);var m=n+"",p=i[2],q=i[4];return n==null?p==="!=":p==="="?m===q:p==="*="?m.indexOf(q)>=0:p==="~="?(" "+m+" ").indexOf(q)>=0:!q?m&&n!==false:p==="!="?m!==q:p==="^="?m.indexOf(q)===0:p==="$="?m.substr(m.length-q.length)===q:p==="|="?m===q||m.substr(0,q.length+1)===q+"-":false},POS:function(g,i,n,m){var p=o.setFilters[i[2]]; -if(p)return p(g,n,i,m)}}},x=o.match.POS,r=function(g,i){return"\\"+(i-0+1)},A;for(A in o.match){o.match[A]=RegExp(o.match[A].source+/(?![^\[]*\])(?![^\(]*\))/.source);o.leftMatch[A]=RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[A].source.replace(/\\(\d+)/g,r))}var C=function(g,i){g=Array.prototype.slice.call(g,0);if(i){i.push.apply(i,g);return i}return g};try{Array.prototype.slice.call(t.documentElement.childNodes,0)}catch(J){C=function(g,i){var n=0,m=i||[];if(f.call(g)==="[object Array]")Array.prototype.push.apply(m, -g);else if(typeof g.length==="number")for(var p=g.length;n";n.insertBefore(g,n.firstChild);if(t.getElementById(i)){o.find.ID=function(m,p,q){if(typeof p.getElementById!=="undefined"&&!q)return(p=p.getElementById(m[1]))?p.id===m[1]||typeof p.getAttributeNode!=="undefined"&&p.getAttributeNode("id").nodeValue===m[1]?[p]:B:[]};o.filter.ID=function(m,p){var q=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&q&&q.nodeValue===p}}n.removeChild(g); -n=g=null})();(function(){var g=t.createElement("div");g.appendChild(t.createComment(""));if(g.getElementsByTagName("*").length>0)o.find.TAG=function(i,n){var m=n.getElementsByTagName(i[1]);if(i[1]==="*"){for(var p=[],q=0;m[q];q++)m[q].nodeType===1&&p.push(m[q]);m=p}return m};g.innerHTML="";if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")o.attrHandle.href=function(i){return i.getAttribute("href",2)};g=null})();t.querySelectorAll&& -function(){var g=k,i=t.createElement("div");i.innerHTML="

";if(!(i.querySelectorAll&&i.querySelectorAll(".TEST").length===0)){k=function(m,p,q,u){p=p||t;m=m.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!u&&!k.isXML(p))if(p.nodeType===9)try{return C(p.querySelectorAll(m),q)}catch(y){}else if(p.nodeType===1&&p.nodeName.toLowerCase()!=="object"){var F=p.getAttribute("id"),M=F||"__sizzle__";F||p.setAttribute("id",M);try{return C(p.querySelectorAll("#"+M+" "+m),q)}catch(N){}finally{F|| -p.removeAttribute("id")}}return g(m,p,q,u)};for(var n in g)k[n]=g[n];i=null}}();(function(){var g=t.documentElement,i=g.matchesSelector||g.mozMatchesSelector||g.webkitMatchesSelector||g.msMatchesSelector,n=false;try{i.call(t.documentElement,"[test!='']:sizzle")}catch(m){n=true}if(i)k.matchesSelector=function(p,q){q=q.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(p))try{if(n||!o.match.PSEUDO.test(q)&&!/!=/.test(q))return i.call(p,q)}catch(u){}return k(q,null,null,[p]).length>0}})();(function(){var g= -t.createElement("div");g.innerHTML="
";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){o.order.splice(1,0,"CLASS");o.find.CLASS=function(i,n,m){if(typeof n.getElementsByClassName!=="undefined"&&!m)return n.getElementsByClassName(i[1])};g=null}}})();k.contains=t.documentElement.contains?function(g,i){return g!==i&&(g.contains?g.contains(i):true)}:t.documentElement.compareDocumentPosition? -function(g,i){return!!(g.compareDocumentPosition(i)&16)}:function(){return false};k.isXML=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false};var L=function(g,i){for(var n,m=[],p="",q=i.nodeType?[i]:i;n=o.match.PSEUDO.exec(g);){p+=n[0];g=g.replace(o.match.PSEUDO,"")}g=o.relative[g]?g+"*":g;n=0;for(var u=q.length;n0)for(var h=d;h0},closest:function(a,b){var d=[],e,f,h=this[0];if(c.isArray(a)){var l,k={},o=1;if(h&&a.length){e=0;for(f=a.length;e-1:c(h).is(e))d.push({selector:l,elem:h,level:o})}h= -h.parentNode;o++}}return d}l=cb.test(a)?c(a,b||this.context):null;e=0;for(f=this.length;e-1:c.find.matchesSelector(h,a)){d.push(h);break}else{h=h.parentNode;if(!h||!h.ownerDocument||h===b)break}d=d.length>1?c.unique(d):d;return this.pushStack(d,"closest",a)},index:function(a){if(!a||typeof a==="string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var d=typeof a==="string"?c(a,b||this.context): -c.makeArray(a),e=c.merge(this.get(),d);return this.pushStack(!d[0]||!d[0].parentNode||d[0].parentNode.nodeType===11||!e[0]||!e[0].parentNode||e[0].parentNode.nodeType===11?e:c.unique(e))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a, -2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a, -b){c.fn[a]=function(d,e){var f=c.map(this,b,d);Za.test(a)||(e=d);if(e&&typeof e==="string")f=c.filter(e,f);f=this.length>1?c.unique(f):f;if((this.length>1||ab.test(e))&&$a.test(a))f=f.reverse();return this.pushStack(f,a,bb.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return b.length===1?c.find.matchesSelector(b[0],a)?[b[0]]:[]:c.find.matches(a,b)},dir:function(a,b,d){var e=[];for(a=a[b];a&&a.nodeType!==9&&(d===B||a.nodeType!==1||!c(a).is(d));){a.nodeType===1&& -e.push(a);a=a[b]}return e},nth:function(a,b,d){b=b||1;for(var e=0;a;a=a[d])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var za=/ jQuery\d+="(?:\d+|null)"/g,$=/^\s+/,Aa=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Ba=/<([\w:]+)/,db=/
","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]};P.optgroup=P.option;P.tbody=P.tfoot=P.colgroup=P.caption=P.thead;P.th=P.td;if(!c.support.htmlSerialize)P._default=[1,"div

","
"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d= -c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==B)return this.empty().append((this[0]&&this[0].ownerDocument||t).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this}, -wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})}, -prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b, -this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,e;(e=this[d])!=null;d++)if(!a||c.filter(a,[e]).length){if(!b&&e.nodeType===1){c.cleanData(e.getElementsByTagName("*"));c.cleanData([e])}e.parentNode&&e.parentNode.removeChild(e)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild); -return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,e=this.ownerDocument;if(!d){d=e.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(za,"").replace(fb,'="$1">').replace($,"")],e)[0]}else return this.cloneNode(true)});if(a===true){na(this,b);na(this.find("*"),b.find("*"))}return b},html:function(a){if(a===B)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(za,""):null; -else if(typeof a==="string"&&!Ca.test(a)&&(c.support.leadingWhitespace||!$.test(a))&&!P[(Ba.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Aa,"<$1>");try{for(var b=0,d=this.length;b0||e.cacheable||this.length>1?h.cloneNode(true):h)}k.length&&c.each(k,Oa)}return this}});c.buildFragment=function(a,b,d){var e,f,h;b=b&&b[0]?b[0].ownerDocument||b[0]:t;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===t&&!Ca.test(a[0])&&(c.support.checkClone||!Da.test(a[0]))){f=true;if(h=c.fragments[a[0]])if(h!==1)e=h}if(!e){e=b.createDocumentFragment();c.clean(a,b,e,d)}if(f)c.fragments[a[0]]=h?e:1;return{fragment:e,cacheable:f}};c.fragments={};c.each({appendTo:"append", -prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var e=[];d=c(d);var f=this.length===1&&this[0].parentNode;if(f&&f.nodeType===11&&f.childNodes.length===1&&d.length===1){d[b](this[0]);return this}else{f=0;for(var h=d.length;f0?this.clone(true):this).get();c(d[f])[b](l);e=e.concat(l)}return this.pushStack(e,a,d.selector)}}});c.extend({clean:function(a,b,d,e){b=b||t;if(typeof b.createElement==="undefined")b=b.ownerDocument|| -b[0]&&b[0].ownerDocument||t;for(var f=[],h=0,l;(l=a[h])!=null;h++){if(typeof l==="number")l+="";if(l){if(typeof l==="string"&&!eb.test(l))l=b.createTextNode(l);else if(typeof l==="string"){l=l.replace(Aa,"<$1>");var k=(Ba.exec(l)||["",""])[1].toLowerCase(),o=P[k]||P._default,x=o[0],r=b.createElement("div");for(r.innerHTML=o[1]+l+o[2];x--;)r=r.lastChild;if(!c.support.tbody){x=db.test(l);k=k==="table"&&!x?r.firstChild&&r.firstChild.childNodes:o[1]===""&&!x?r.childNodes:[];for(o=k.length- -1;o>=0;--o)c.nodeName(k[o],"tbody")&&!k[o].childNodes.length&&k[o].parentNode.removeChild(k[o])}!c.support.leadingWhitespace&&$.test(l)&&r.insertBefore(b.createTextNode($.exec(l)[0]),r.firstChild);l=r.childNodes}if(l.nodeType)f.push(l);else f=c.merge(f,l)}}if(d)for(h=0;f[h];h++)if(e&&c.nodeName(f[h],"script")&&(!f[h].type||f[h].type.toLowerCase()==="text/javascript"))e.push(f[h].parentNode?f[h].parentNode.removeChild(f[h]):f[h]);else{f[h].nodeType===1&&f.splice.apply(f,[h+1,0].concat(c.makeArray(f[h].getElementsByTagName("script")))); -d.appendChild(f[h])}return f},cleanData:function(a){for(var b,d,e=c.cache,f=c.event.special,h=c.support.deleteExpando,l=0,k;(k=a[l])!=null;l++)if(!(k.nodeName&&c.noData[k.nodeName.toLowerCase()]))if(d=k[c.expando]){if((b=e[d])&&b.events)for(var o in b.events)f[o]?c.event.remove(k,o):c.removeEvent(k,o,b.handle);if(h)delete k[c.expando];else k.removeAttribute&&k.removeAttribute(c.expando);delete e[d]}}});var Ea=/alpha\([^)]*\)/i,gb=/opacity=([^)]*)/,hb=/-([a-z])/ig,ib=/([A-Z])/g,Fa=/^-?\d+(?:px)?$/i, -jb=/^-?\d/,kb={position:"absolute",visibility:"hidden",display:"block"},Pa=["Left","Right"],Qa=["Top","Bottom"],W,Ga,aa,lb=function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){if(arguments.length===2&&b===B)return this;return c.access(this,a,b,true,function(d,e,f){return f!==B?c.style(d,e,f):c.css(d,e)})};c.extend({cssHooks:{opacity:{get:function(a,b){if(b){var d=W(a,"opacity","opacity");return d===""?"1":d}else return a.style.opacity}}},cssNumber:{zIndex:true,fontWeight:true,opacity:true, -zoom:true,lineHeight:true},cssProps:{"float":c.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,d,e){if(!(!a||a.nodeType===3||a.nodeType===8||!a.style)){var f,h=c.camelCase(b),l=a.style,k=c.cssHooks[h];b=c.cssProps[h]||h;if(d!==B){if(!(typeof d==="number"&&isNaN(d)||d==null)){if(typeof d==="number"&&!c.cssNumber[h])d+="px";if(!k||!("set"in k)||(d=k.set(a,d))!==B)try{l[b]=d}catch(o){}}}else{if(k&&"get"in k&&(f=k.get(a,false,e))!==B)return f;return l[b]}}},css:function(a,b,d){var e,f=c.camelCase(b), -h=c.cssHooks[f];b=c.cssProps[f]||f;if(h&&"get"in h&&(e=h.get(a,true,d))!==B)return e;else if(W)return W(a,b,f)},swap:function(a,b,d){var e={},f;for(f in b){e[f]=a.style[f];a.style[f]=b[f]}d.call(a);for(f in b)a.style[f]=e[f]},camelCase:function(a){return a.replace(hb,lb)}});c.curCSS=c.css;c.each(["height","width"],function(a,b){c.cssHooks[b]={get:function(d,e,f){var h;if(e){if(d.offsetWidth!==0)h=oa(d,b,f);else c.swap(d,kb,function(){h=oa(d,b,f)});if(h<=0){h=W(d,b,b);if(h==="0px"&&aa)h=aa(d,b,b); -if(h!=null)return h===""||h==="auto"?"0px":h}if(h<0||h==null){h=d.style[b];return h===""||h==="auto"?"0px":h}return typeof h==="string"?h:h+"px"}},set:function(d,e){if(Fa.test(e)){e=parseFloat(e);if(e>=0)return e+"px"}else return e}}});if(!c.support.opacity)c.cssHooks.opacity={get:function(a,b){return gb.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var d=a.style;d.zoom=1;var e=c.isNaN(b)?"":"alpha(opacity="+b*100+")",f= -d.filter||"";d.filter=Ea.test(f)?f.replace(Ea,e):d.filter+" "+e}};if(t.defaultView&&t.defaultView.getComputedStyle)Ga=function(a,b,d){var e;d=d.replace(ib,"-$1").toLowerCase();if(!(b=a.ownerDocument.defaultView))return B;if(b=b.getComputedStyle(a,null)){e=b.getPropertyValue(d);if(e===""&&!c.contains(a.ownerDocument.documentElement,a))e=c.style(a,d)}return e};if(t.documentElement.currentStyle)aa=function(a,b){var d,e,f=a.currentStyle&&a.currentStyle[b],h=a.style;if(!Fa.test(f)&&jb.test(f)){d=h.left; -e=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;h.left=b==="fontSize"?"1em":f||0;f=h.pixelLeft+"px";h.left=d;a.runtimeStyle.left=e}return f===""?"auto":f};W=Ga||aa;if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=a.offsetHeight;return a.offsetWidth===0&&b===0||!c.support.reliableHiddenOffsets&&(a.style.display||c.css(a,"display"))==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var mb=c.now(),nb=/)<[^<]*)*<\/script>/gi, -ob=/^(?:select|textarea)/i,pb=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,qb=/^(?:GET|HEAD)$/,Ra=/\[\]$/,T=/\=\?(&|$)/,ja=/\?/,rb=/([?&])_=[^&]*/,sb=/^(\w+:)?\/\/([^\/?#]+)/,tb=/%20/g,ub=/#.*$/,Ha=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!=="string"&&Ha)return Ha.apply(this,arguments);else if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var f=a.slice(e,a.length);a=a.slice(0,e)}e="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b=== -"object"){b=c.param(b,c.ajaxSettings.traditional);e="POST"}var h=this;c.ajax({url:a,type:e,dataType:"html",data:b,complete:function(l,k){if(k==="success"||k==="notmodified")h.html(f?c("
").append(l.responseText.replace(nb,"")).find(f):l.responseText);d&&h.each(d,[l.responseText,k,l])}});return this},serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&& -!this.disabled&&(this.checked||ob.test(this.nodeName)||pb.test(this.type))}).map(function(a,b){var d=c(this).val();return d==null?null:c.isArray(d)?c.map(d,function(e){return{name:b.name,value:e}}):{name:b.name,value:d}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,e){if(c.isFunction(b)){e=e||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:e})}, -getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,e){if(c.isFunction(b)){e=e||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:e})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return new E.XMLHttpRequest},accepts:{xml:"application/xml, text/xml",html:"text/html", -script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},ajax:function(a){var b=c.extend(true,{},c.ajaxSettings,a),d,e,f,h=b.type.toUpperCase(),l=qb.test(h);b.url=b.url.replace(ub,"");b.context=a&&a.context!=null?a.context:b;if(b.data&&b.processData&&typeof b.data!=="string")b.data=c.param(b.data,b.traditional);if(b.dataType==="jsonp"){if(h==="GET")T.test(b.url)||(b.url+=(ja.test(b.url)?"&":"?")+(b.jsonp||"callback")+"=?");else if(!b.data|| -!T.test(b.data))b.data=(b.data?b.data+"&":"")+(b.jsonp||"callback")+"=?";b.dataType="json"}if(b.dataType==="json"&&(b.data&&T.test(b.data)||T.test(b.url))){d=b.jsonpCallback||"jsonp"+mb++;if(b.data)b.data=(b.data+"").replace(T,"="+d+"$1");b.url=b.url.replace(T,"="+d+"$1");b.dataType="script";var k=E[d];E[d]=function(m){if(c.isFunction(k))k(m);else{E[d]=B;try{delete E[d]}catch(p){}}f=m;c.handleSuccess(b,w,e,f);c.handleComplete(b,w,e,f);r&&r.removeChild(A)}}if(b.dataType==="script"&&b.cache===null)b.cache= -false;if(b.cache===false&&l){var o=c.now(),x=b.url.replace(rb,"$1_="+o);b.url=x+(x===b.url?(ja.test(b.url)?"&":"?")+"_="+o:"")}if(b.data&&l)b.url+=(ja.test(b.url)?"&":"?")+b.data;b.global&&c.active++===0&&c.event.trigger("ajaxStart");o=(o=sb.exec(b.url))&&(o[1]&&o[1].toLowerCase()!==location.protocol||o[2].toLowerCase()!==location.host);if(b.dataType==="script"&&h==="GET"&&o){var r=t.getElementsByTagName("head")[0]||t.documentElement,A=t.createElement("script");if(b.scriptCharset)A.charset=b.scriptCharset; -A.src=b.url;if(!d){var C=false;A.onload=A.onreadystatechange=function(){if(!C&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){C=true;c.handleSuccess(b,w,e,f);c.handleComplete(b,w,e,f);A.onload=A.onreadystatechange=null;r&&A.parentNode&&r.removeChild(A)}}}r.insertBefore(A,r.firstChild);return B}var J=false,w=b.xhr();if(w){b.username?w.open(h,b.url,b.async,b.username,b.password):w.open(h,b.url,b.async);try{if(b.data!=null&&!l||a&&a.contentType)w.setRequestHeader("Content-Type", -b.contentType);if(b.ifModified){c.lastModified[b.url]&&w.setRequestHeader("If-Modified-Since",c.lastModified[b.url]);c.etag[b.url]&&w.setRequestHeader("If-None-Match",c.etag[b.url])}o||w.setRequestHeader("X-Requested-With","XMLHttpRequest");w.setRequestHeader("Accept",b.dataType&&b.accepts[b.dataType]?b.accepts[b.dataType]+", */*; q=0.01":b.accepts._default)}catch(I){}if(b.beforeSend&&b.beforeSend.call(b.context,w,b)===false){b.global&&c.active--===1&&c.event.trigger("ajaxStop");w.abort();return false}b.global&& -c.triggerGlobal(b,"ajaxSend",[w,b]);var L=w.onreadystatechange=function(m){if(!w||w.readyState===0||m==="abort"){J||c.handleComplete(b,w,e,f);J=true;if(w)w.onreadystatechange=c.noop}else if(!J&&w&&(w.readyState===4||m==="timeout")){J=true;w.onreadystatechange=c.noop;e=m==="timeout"?"timeout":!c.httpSuccess(w)?"error":b.ifModified&&c.httpNotModified(w,b.url)?"notmodified":"success";var p;if(e==="success")try{f=c.httpData(w,b.dataType,b)}catch(q){e="parsererror";p=q}if(e==="success"||e==="notmodified")d|| -c.handleSuccess(b,w,e,f);else c.handleError(b,w,e,p);d||c.handleComplete(b,w,e,f);m==="timeout"&&w.abort();if(b.async)w=null}};try{var g=w.abort;w.abort=function(){w&&Function.prototype.call.call(g,w);L("abort")}}catch(i){}b.async&&b.timeout>0&&setTimeout(function(){w&&!J&&L("timeout")},b.timeout);try{w.send(l||b.data==null?null:b.data)}catch(n){c.handleError(b,w,null,n);c.handleComplete(b,w,e,f)}b.async||L();return w}},param:function(a,b){var d=[],e=function(h,l){l=c.isFunction(l)?l():l;d[d.length]= -encodeURIComponent(h)+"="+encodeURIComponent(l)};if(b===B)b=c.ajaxSettings.traditional;if(c.isArray(a)||a.jquery)c.each(a,function(){e(this.name,this.value)});else for(var f in a)da(f,a[f],b,e);return d.join("&").replace(tb,"+")}});c.extend({active:0,lastModified:{},etag:{},handleError:function(a,b,d,e){a.error&&a.error.call(a.context,b,d,e);a.global&&c.triggerGlobal(a,"ajaxError",[b,a,e])},handleSuccess:function(a,b,d,e){a.success&&a.success.call(a.context,e,d,b);a.global&&c.triggerGlobal(a,"ajaxSuccess", -[b,a])},handleComplete:function(a,b,d){a.complete&&a.complete.call(a.context,b,d);a.global&&c.triggerGlobal(a,"ajaxComplete",[b,a]);a.global&&c.active--===1&&c.event.trigger("ajaxStop")},triggerGlobal:function(a,b,d){(a.context&&a.context.url==null?c(a.context):c.event).trigger(b,d)},httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===1223}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"), -e=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(e)c.etag[b]=e;return a.status===304},httpData:function(a,b,d){var e=a.getResponseHeader("content-type")||"",f=b==="xml"||!b&&e.indexOf("xml")>=0;a=f?a.responseXML:a.responseText;f&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b==="json"||!b&&e.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&e.indexOf("javascript")>=0)c.globalEval(a);return a}}); -if(E.ActiveXObject)c.ajaxSettings.xhr=function(){if(E.location.protocol!=="file:")try{return new E.XMLHttpRequest}catch(a){}try{return new E.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}};c.support.ajax=!!c.ajaxSettings.xhr();var ea={},vb=/^(?:toggle|show|hide)$/,wb=/^([+\-]=)?([\d+.\-]+)(.*)$/,ba,pa=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b,d){if(a||a===0)return this.animate(S("show", -3),a,b,d);else{d=0;for(var e=this.length;d=0;e--)if(d[e].elem===this){b&&d[e](true);d.splice(e,1)}});b||this.dequeue();return this}});c.each({slideDown:S("show",1),slideUp:S("hide",1),slideToggle:S("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){c.fn[a]=function(d,e,f){return this.animate(b, -d,e,f)}});c.extend({speed:function(a,b,d){var e=a&&typeof a==="object"?c.extend({},a):{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};e.duration=c.fx.off?0:typeof e.duration==="number"?e.duration:e.duration in c.fx.speeds?c.fx.speeds[e.duration]:c.fx.speeds._default;e.old=e.complete;e.complete=function(){e.queue!==false&&c(this).dequeue();c.isFunction(e.old)&&e.old.call(this)};return e},easing:{linear:function(a,b,d,e){return d+e*a},swing:function(a,b,d,e){return(-Math.cos(a* -Math.PI)/2+0.5)*e+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||c.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a=parseFloat(c.css(this.elem,this.prop));return a&&a>-1E4?a:0},custom:function(a,b,d){function e(l){return f.step(l)} -var f=this,h=c.fx;this.startTime=c.now();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;this.pos=this.state=0;e.elem=this.elem;if(e()&&c.timers.push(e)&&!ba)ba=setInterval(h.tick,h.interval)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true; -this.custom(this.cur(),0)},step:function(a){var b=c.now(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var e in this.options.curAnim)if(this.options.curAnim[e]!==true)d=false;if(d){if(this.options.overflow!=null&&!c.support.shrinkWrapBlocks){var f=this.elem,h=this.options;c.each(["","X","Y"],function(k,o){f.style["overflow"+o]=h.overflow[k]})}this.options.hide&&c(this.elem).hide();if(this.options.hide|| -this.options.show)for(var l in this.options.curAnim)c.style(this.elem,l,this.options.orig[l]);this.options.complete.call(this.elem)}return false}else{a=b-this.startTime;this.state=a/this.options.duration;b=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||b](this.state,a,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a= -c.timers,b=0;b-1;e={};var x={};if(o)x=f.position();l=o?x.top:parseInt(l,10)||0;k=o?x.left:parseInt(k,10)||0;if(c.isFunction(b))b=b.call(a,d,h);if(b.top!=null)e.top=b.top-h.top+l;if(b.left!=null)e.left=b.left-h.left+k;"using"in b?b.using.call(a, -e):f.css(e)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),e=Ia.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.css(a,"marginTop"))||0;d.left-=parseFloat(c.css(a,"marginLeft"))||0;e.top+=parseFloat(c.css(b[0],"borderTopWidth"))||0;e.left+=parseFloat(c.css(b[0],"borderLeftWidth"))||0;return{top:d.top-e.top,left:d.left-e.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||t.body;a&&!Ia.test(a.nodeName)&& -c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(e){var f=this[0],h;if(!f)return null;if(e!==B)return this.each(function(){if(h=fa(this))h.scrollTo(!a?e:c(h).scrollLeft(),a?e:c(h).scrollTop());else this[d]=e});else return(h=fa(f))?"pageXOffset"in h?h[a?"pageYOffset":"pageXOffset"]:c.support.boxModel&&h.document.documentElement[d]||h.document.body[d]:f[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase(); -c.fn["inner"+b]=function(){return this[0]?parseFloat(c.css(this[0],d,"padding")):null};c.fn["outer"+b]=function(e){return this[0]?parseFloat(c.css(this[0],d,e?"margin":"border")):null};c.fn[d]=function(e){var f=this[0];if(!f)return e==null?null:this;if(c.isFunction(e))return this.each(function(l){var k=c(this);k[d](e.call(this,l,k[d]()))});if(c.isWindow(f))return f.document.compatMode==="CSS1Compat"&&f.document.documentElement["client"+b]||f.document.body["client"+b];else if(f.nodeType===9)return Math.max(f.documentElement["client"+ -b],f.body["scroll"+b],f.documentElement["scroll"+b],f.body["offset"+b],f.documentElement["offset"+b]);else if(e===B){f=c.css(f,d);var h=parseFloat(f);return c.isNaN(h)?f:h}else return this.css(d,typeof e==="string"?e:e+"px")}})})(window); \ No newline at end of file +(function( window, undefined ) { + +// Use the correct document accordingly with window argument (sandbox) +var document = window.document, + navigator = window.navigator, + location = window.location; +var jQuery = (function() { + +// Define a local copy of jQuery +var jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context, rootjQuery ); + }, + + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + + // Map over the $ in case of overwrite + _$ = window.$, + + // A central reference to the root jQuery(document) + rootjQuery, + + // A simple way to check for HTML strings or ID strings + // (both of which we optimize for) + quickExpr = /^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/, + + // Check if a string has a non-whitespace character in it + rnotwhite = /\S/, + + // Used for trimming whitespace + trimLeft = /^\s+/, + trimRight = /\s+$/, + + // Check for digits + rdigit = /\d/, + + // Match a standalone tag + rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/, + + // JSON RegExp + rvalidchars = /^[\],:{}\s]*$/, + rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, + rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, + rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, + + // Useragent RegExp + rwebkit = /(webkit)[ \/]([\w.]+)/, + ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/, + rmsie = /(msie) ([\w.]+)/, + rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/, + + // Matches dashed string for camelizing + rdashAlpha = /-([a-z])/ig, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return letter.toUpperCase(); + }, + + // Keep a UserAgent string for use with jQuery.browser + userAgent = navigator.userAgent, + + // For matching the engine and version of the browser + browserMatch, + + // The deferred used on DOM ready + readyList, + + // The ready event handler + DOMContentLoaded, + + // Save a reference to some core methods + toString = Object.prototype.toString, + hasOwn = Object.prototype.hasOwnProperty, + push = Array.prototype.push, + slice = Array.prototype.slice, + trim = String.prototype.trim, + indexOf = Array.prototype.indexOf, + + // [[Class]] -> type pairs + class2type = {}; + +jQuery.fn = jQuery.prototype = { + constructor: jQuery, + init: function( selector, context, rootjQuery ) { + var match, elem, ret, doc; + + // Handle $(""), $(null), or $(undefined) + if ( !selector ) { + return this; + } + + // Handle $(DOMElement) + if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + } + + // The body element only exists once, optimize finding it + if ( selector === "body" && !context && document.body ) { + this.context = document; + this[0] = document.body; + this.selector = selector; + this.length = 1; + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + // Are we dealing with HTML string or an ID? + if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = quickExpr.exec( selector ); + } + + // Verify a match, and that no context was specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; + doc = (context ? context.ownerDocument || context : document); + + // If a single string is passed in and it's a single tag + // just do a createElement and skip the rest + ret = rsingleTag.exec( selector ); + + if ( ret ) { + if ( jQuery.isPlainObject( context ) ) { + selector = [ document.createElement( ret[1] ) ]; + jQuery.fn.attr.call( selector, context, true ); + + } else { + selector = [ doc.createElement( ret[1] ) ]; + } + + } else { + ret = jQuery.buildFragment( [ match[1] ], [ doc ] ); + selector = (ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment).childNodes; + } + + return jQuery.merge( this, selector ); + + // HANDLE: $("#id") + } else { + elem = document.getElementById( match[2] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[2] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return (context || rootjQuery).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return rootjQuery.ready( selector ); + } + + if (selector.selector !== undefined) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }, + + // Start with an empty selector + selector: "", + + // The current version of jQuery being used + jquery: "1.6.2", + + // The default length of a jQuery object is 0 + length: 0, + + // The number of elements contained in the matched element set + size: function() { + return this.length; + }, + + toArray: function() { + return slice.call( this, 0 ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num == null ? + + // Return a 'clean' array + this.toArray() : + + // Return just the object + ( num < 0 ? this[ this.length + num ] : this[ num ] ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems, name, selector ) { + // Build a new jQuery matched element set + var ret = this.constructor(); + + if ( jQuery.isArray( elems ) ) { + push.apply( ret, elems ); + + } else { + jQuery.merge( ret, elems ); + } + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + ret.context = this.context; + + if ( name === "find" ) { + ret.selector = this.selector + (this.selector ? " " : "") + selector; + } else if ( name ) { + ret.selector = this.selector + "." + name + "(" + selector + ")"; + } + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + ready: function( fn ) { + // Attach the listeners + jQuery.bindReady(); + + // Add the callback + readyList.done( fn ); + + return this; + }, + + eq: function( i ) { + return i === -1 ? + this.slice( i ) : + this.slice( i, +i + 1 ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ), + "slice", slice.call(arguments).join(",") ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + end: function() { + return this.prevObject || this.constructor(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: [].sort, + splice: [].splice +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( length === i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray(src) ? src : []; + + } else { + clone = src && jQuery.isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend({ + noConflict: function( deep ) { + if ( window.$ === jQuery ) { + window.$ = _$; + } + + if ( deep && window.jQuery === jQuery ) { + window.jQuery = _jQuery; + } + + return jQuery; + }, + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + // Either a released hold or an DOMready/load event and not yet ready + if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( !document.body ) { + return setTimeout( jQuery.ready, 1 ); + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.trigger ) { + jQuery( document ).trigger( "ready" ).unbind( "ready" ); + } + } + }, + + bindReady: function() { + if ( readyList ) { + return; + } + + readyList = jQuery._Deferred(); + + // Catch cases where $(document).ready() is called after the + // browser event has already occurred. + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + return setTimeout( jQuery.ready, 1 ); + } + + // Mozilla, Opera and webkit nightlies currently support this event + if ( document.addEventListener ) { + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", jQuery.ready, false ); + + // If IE event model is used + } else if ( document.attachEvent ) { + // ensure firing before onload, + // maybe late but safe also for iframes + document.attachEvent( "onreadystatechange", DOMContentLoaded ); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", jQuery.ready ); + + // If IE and not a frame + // continually check to see if the document is ready + var toplevel = false; + + try { + toplevel = window.frameElement == null; + } catch(e) {} + + if ( document.documentElement.doScroll && toplevel ) { + doScrollCheck(); + } + } + }, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray || function( obj ) { + return jQuery.type(obj) === "array"; + }, + + // A crude way of determining if an object is a window + isWindow: function( obj ) { + return obj && typeof obj === "object" && "setInterval" in obj; + }, + + isNaN: function( obj ) { + return obj == null || !rdigit.test( obj ) || isNaN( obj ); + }, + + type: function( obj ) { + return obj == null ? + String( obj ) : + class2type[ toString.call(obj) ] || "object"; + }, + + isPlainObject: function( obj ) { + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + // Not own constructor property must be Object + if ( obj.constructor && + !hasOwn.call(obj, "constructor") && + !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + return false; + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + + var key; + for ( key in obj ) {} + + return key === undefined || hasOwn.call( obj, key ); + }, + + isEmptyObject: function( obj ) { + for ( var name in obj ) { + return false; + } + return true; + }, + + error: function( msg ) { + throw msg; + }, + + parseJSON: function( data ) { + if ( typeof data !== "string" || !data ) { + return null; + } + + // Make sure leading/trailing whitespace is removed (IE can't handle it) + data = jQuery.trim( data ); + + // Attempt to parse using the native JSON parser first + if ( window.JSON && window.JSON.parse ) { + return window.JSON.parse( data ); + } + + // Make sure the incoming data is actual JSON + // Logic borrowed from http://json.org/json2.js + if ( rvalidchars.test( data.replace( rvalidescape, "@" ) + .replace( rvalidtokens, "]" ) + .replace( rvalidbraces, "")) ) { + + return (new Function( "return " + data ))(); + + } + jQuery.error( "Invalid JSON: " + data ); + }, + + // Cross-browser xml parsing + // (xml & tmp used internally) + parseXML: function( data , xml , tmp ) { + + if ( window.DOMParser ) { // Standard + tmp = new DOMParser(); + xml = tmp.parseFromString( data , "text/xml" ); + } else { // IE + xml = new ActiveXObject( "Microsoft.XMLDOM" ); + xml.async = "false"; + xml.loadXML( data ); + } + + tmp = xml.documentElement; + + if ( ! tmp || ! tmp.nodeName || tmp.nodeName === "parsererror" ) { + jQuery.error( "Invalid XML: " + data ); + } + + return xml; + }, + + noop: function() {}, + + // Evaluates a script in a global context + // Workarounds based on findings by Jim Driscoll + // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context + globalEval: function( data ) { + if ( data && rnotwhite.test( data ) ) { + // We use execScript on Internet Explorer + // We use an anonymous function so that context is window + // rather than jQuery in Firefox + ( window.execScript || function( data ) { + window[ "eval" ].call( window, data ); + } )( data ); + } + }, + + // Converts a dashed string to camelCased string; + // Used by both the css and data modules + camelCase: function( string ) { + return string.replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); + }, + + // args is for internal usage only + each: function( object, callback, args ) { + var name, i = 0, + length = object.length, + isObj = length === undefined || jQuery.isFunction( object ); + + if ( args ) { + if ( isObj ) { + for ( name in object ) { + if ( callback.apply( object[ name ], args ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.apply( object[ i++ ], args ) === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isObj ) { + for ( name in object ) { + if ( callback.call( object[ name ], name, object[ name ] ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) { + break; + } + } + } + } + + return object; + }, + + // Use native String.trim function wherever possible + trim: trim ? + function( text ) { + return text == null ? + "" : + trim.call( text ); + } : + + // Otherwise use our own trimming functionality + function( text ) { + return text == null ? + "" : + text.toString().replace( trimLeft, "" ).replace( trimRight, "" ); + }, + + // results is for internal usage only + makeArray: function( array, results ) { + var ret = results || []; + + if ( array != null ) { + // The window, strings (and functions) also have 'length' + // The extra typeof function check is to prevent crashes + // in Safari 2 (See: #3039) + // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930 + var type = jQuery.type( array ); + + if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) { + push.call( ret, array ); + } else { + jQuery.merge( ret, array ); + } + } + + return ret; + }, + + inArray: function( elem, array ) { + + if ( indexOf ) { + return indexOf.call( array, elem ); + } + + for ( var i = 0, length = array.length; i < length; i++ ) { + if ( array[ i ] === elem ) { + return i; + } + } + + return -1; + }, + + merge: function( first, second ) { + var i = first.length, + j = 0; + + if ( typeof second.length === "number" ) { + for ( var l = second.length; j < l; j++ ) { + first[ i++ ] = second[ j ]; + } + + } else { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, inv ) { + var ret = [], retVal; + inv = !!inv; + + // Go through the array, only saving the items + // that pass the validator function + for ( var i = 0, length = elems.length; i < length; i++ ) { + retVal = !!callback( elems[ i ], i ); + if ( inv !== retVal ) { + ret.push( elems[ i ] ); + } + } + + return ret; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var value, key, ret = [], + i = 0, + length = elems.length, + // jquery objects are treated as arrays + isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ; + + // Go through the array, translating each of the items to their + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + + // Go through every key on the object, + } else { + for ( key in elems ) { + value = callback( elems[ key ], key, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + } + + // Flatten any nested arrays + return ret.concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + if ( typeof context === "string" ) { + var tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + var args = slice.call( arguments, 2 ), + proxy = function() { + return fn.apply( context, args.concat( slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++; + + return proxy; + }, + + // Mutifunctional method to get and set values to a collection + // The value/s can optionally be executed if it's a function + access: function( elems, key, value, exec, fn, pass ) { + var length = elems.length; + + // Setting many attributes + if ( typeof key === "object" ) { + for ( var k in key ) { + jQuery.access( elems, k, key[k], exec, fn, value ); + } + return elems; + } + + // Setting one attribute + if ( value !== undefined ) { + // Optionally, function values get executed if exec is true + exec = !pass && exec && jQuery.isFunction(value); + + for ( var i = 0; i < length; i++ ) { + fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); + } + + return elems; + } + + // Getting an attribute + return length ? fn( elems[0], key ) : undefined; + }, + + now: function() { + return (new Date()).getTime(); + }, + + // Use of jQuery.browser is frowned upon. + // More details: http://docs.jquery.com/Utilities/jQuery.browser + uaMatch: function( ua ) { + ua = ua.toLowerCase(); + + var match = rwebkit.exec( ua ) || + ropera.exec( ua ) || + rmsie.exec( ua ) || + ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) || + []; + + return { browser: match[1] || "", version: match[2] || "0" }; + }, + + sub: function() { + function jQuerySub( selector, context ) { + return new jQuerySub.fn.init( selector, context ); + } + jQuery.extend( true, jQuerySub, this ); + jQuerySub.superclass = this; + jQuerySub.fn = jQuerySub.prototype = this(); + jQuerySub.fn.constructor = jQuerySub; + jQuerySub.sub = this.sub; + jQuerySub.fn.init = function init( selector, context ) { + if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) { + context = jQuerySub( context ); + } + + return jQuery.fn.init.call( this, selector, context, rootjQuerySub ); + }; + jQuerySub.fn.init.prototype = jQuerySub.fn; + var rootjQuerySub = jQuerySub(document); + return jQuerySub; + }, + + browser: {} +}); + +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); + +browserMatch = jQuery.uaMatch( userAgent ); +if ( browserMatch.browser ) { + jQuery.browser[ browserMatch.browser ] = true; + jQuery.browser.version = browserMatch.version; +} + +// Deprecated, use jQuery.browser.webkit instead +if ( jQuery.browser.webkit ) { + jQuery.browser.safari = true; +} + +// IE doesn't match non-breaking spaces with \s +if ( rnotwhite.test( "\xA0" ) ) { + trimLeft = /^[\s\xA0]+/; + trimRight = /[\s\xA0]+$/; +} + +// All jQuery objects should point back to these +rootjQuery = jQuery(document); + +// Cleanup functions for the document ready method +if ( document.addEventListener ) { + DOMContentLoaded = function() { + document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + jQuery.ready(); + }; + +} else if ( document.attachEvent ) { + DOMContentLoaded = function() { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( document.readyState === "complete" ) { + document.detachEvent( "onreadystatechange", DOMContentLoaded ); + jQuery.ready(); + } + }; +} + +// The DOM ready check for Internet Explorer +function doScrollCheck() { + if ( jQuery.isReady ) { + return; + } + + try { + // If IE is used, use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + document.documentElement.doScroll("left"); + } catch(e) { + setTimeout( doScrollCheck, 1 ); + return; + } + + // and execute any waiting functions + jQuery.ready(); +} + +return jQuery; + +})(); + + +var // Promise methods + promiseMethods = "done fail isResolved isRejected promise then always pipe".split( " " ), + // Static reference to slice + sliceDeferred = [].slice; + +jQuery.extend({ + // Create a simple deferred (one callbacks list) + _Deferred: function() { + var // callbacks list + callbacks = [], + // stored [ context , args ] + fired, + // to avoid firing when already doing so + firing, + // flag to know if the deferred has been cancelled + cancelled, + // the deferred itself + deferred = { + + // done( f1, f2, ...) + done: function() { + if ( !cancelled ) { + var args = arguments, + i, + length, + elem, + type, + _fired; + if ( fired ) { + _fired = fired; + fired = 0; + } + for ( i = 0, length = args.length; i < length; i++ ) { + elem = args[ i ]; + type = jQuery.type( elem ); + if ( type === "array" ) { + deferred.done.apply( deferred, elem ); + } else if ( type === "function" ) { + callbacks.push( elem ); + } + } + if ( _fired ) { + deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] ); + } + } + return this; + }, + + // resolve with given context and args + resolveWith: function( context, args ) { + if ( !cancelled && !fired && !firing ) { + // make sure args are available (#8421) + args = args || []; + firing = 1; + try { + while( callbacks[ 0 ] ) { + callbacks.shift().apply( context, args ); + } + } + finally { + fired = [ context, args ]; + firing = 0; + } + } + return this; + }, + + // resolve with this as context and given arguments + resolve: function() { + deferred.resolveWith( this, arguments ); + return this; + }, + + // Has this deferred been resolved? + isResolved: function() { + return !!( firing || fired ); + }, + + // Cancel + cancel: function() { + cancelled = 1; + callbacks = []; + return this; + } + }; + + return deferred; + }, + + // Full fledged deferred (two callbacks list) + Deferred: function( func ) { + var deferred = jQuery._Deferred(), + failDeferred = jQuery._Deferred(), + promise; + // Add errorDeferred methods, then and promise + jQuery.extend( deferred, { + then: function( doneCallbacks, failCallbacks ) { + deferred.done( doneCallbacks ).fail( failCallbacks ); + return this; + }, + always: function() { + return deferred.done.apply( deferred, arguments ).fail.apply( this, arguments ); + }, + fail: failDeferred.done, + rejectWith: failDeferred.resolveWith, + reject: failDeferred.resolve, + isRejected: failDeferred.isResolved, + pipe: function( fnDone, fnFail ) { + return jQuery.Deferred(function( newDefer ) { + jQuery.each( { + done: [ fnDone, "resolve" ], + fail: [ fnFail, "reject" ] + }, function( handler, data ) { + var fn = data[ 0 ], + action = data[ 1 ], + returned; + if ( jQuery.isFunction( fn ) ) { + deferred[ handler ](function() { + returned = fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise().then( newDefer.resolve, newDefer.reject ); + } else { + newDefer[ action ]( returned ); + } + }); + } else { + deferred[ handler ]( newDefer[ action ] ); + } + }); + }).promise(); + }, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + if ( obj == null ) { + if ( promise ) { + return promise; + } + promise = obj = {}; + } + var i = promiseMethods.length; + while( i-- ) { + obj[ promiseMethods[i] ] = deferred[ promiseMethods[i] ]; + } + return obj; + } + }); + // Make sure only one callback list will be used + deferred.done( failDeferred.cancel ).fail( deferred.cancel ); + // Unexpose cancel + delete deferred.cancel; + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + return deferred; + }, + + // Deferred helper + when: function( firstParam ) { + var args = arguments, + i = 0, + length = args.length, + count = length, + deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ? + firstParam : + jQuery.Deferred(); + function resolveFunc( i ) { + return function( value ) { + args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value; + if ( !( --count ) ) { + // Strange bug in FF4: + // Values changed onto the arguments object sometimes end up as undefined values + // outside the $.when method. Cloning the object into a fresh array solves the issue + deferred.resolveWith( deferred, sliceDeferred.call( args, 0 ) ); + } + }; + } + if ( length > 1 ) { + for( ; i < length; i++ ) { + if ( args[ i ] && jQuery.isFunction( args[ i ].promise ) ) { + args[ i ].promise().then( resolveFunc(i), deferred.reject ); + } else { + --count; + } + } + if ( !count ) { + deferred.resolveWith( deferred, args ); + } + } else if ( deferred !== firstParam ) { + deferred.resolveWith( deferred, length ? [ firstParam ] : [] ); + } + return deferred.promise(); + } +}); + + + +jQuery.support = (function() { + + var div = document.createElement( "div" ), + documentElement = document.documentElement, + all, + a, + select, + opt, + input, + marginDiv, + support, + fragment, + body, + testElementParent, + testElement, + testElementStyle, + tds, + events, + eventName, + i, + isSupported; + + // Preliminary tests + div.setAttribute("className", "t"); + div.innerHTML = "
a"; + + all = div.getElementsByTagName( "*" ); + a = div.getElementsByTagName( "a" )[ 0 ]; + + // Can't get basic test support + if ( !all || !all.length || !a ) { + return {}; + } + + // First batch of supports tests + select = document.createElement( "select" ); + opt = select.appendChild( document.createElement("option") ); + input = div.getElementsByTagName( "input" )[ 0 ]; + + support = { + // IE strips leading whitespace when .innerHTML is used + leadingWhitespace: ( div.firstChild.nodeType === 3 ), + + // Make sure that tbody elements aren't automatically inserted + // IE will insert them into empty tables + tbody: !div.getElementsByTagName( "tbody" ).length, + + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + htmlSerialize: !!div.getElementsByTagName( "link" ).length, + + // Get the style information from getAttribute + // (IE uses .cssText instead) + style: /top/.test( a.getAttribute("style") ), + + // Make sure that URLs aren't manipulated + // (IE normalizes it by default) + hrefNormalized: ( a.getAttribute( "href" ) === "/a" ), + + // Make sure that element opacity exists + // (IE uses filter instead) + // Use a regex to work around a WebKit issue. See #5145 + opacity: /^0.55$/.test( a.style.opacity ), + + // Verify style float existence + // (IE uses styleFloat instead of cssFloat) + cssFloat: !!a.style.cssFloat, + + // Make sure that if no value is specified for a checkbox + // that it defaults to "on". + // (WebKit defaults to "" instead) + checkOn: ( input.value === "on" ), + + // Make sure that a selected-by-default option has a working selected property. + // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) + optSelected: opt.selected, + + // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) + getSetAttribute: div.className !== "t", + + // Will be defined later + submitBubbles: true, + changeBubbles: true, + focusinBubbles: false, + deleteExpando: true, + noCloneEvent: true, + inlineBlockNeedsLayout: false, + shrinkWrapBlocks: false, + reliableMarginRight: true + }; + + // Make sure checked status is properly cloned + input.checked = true; + support.noCloneChecked = input.cloneNode( true ).checked; + + // Make sure that the options inside disabled selects aren't marked as disabled + // (WebKit marks them as disabled) + select.disabled = true; + support.optDisabled = !opt.disabled; + + // Test to see if it's possible to delete an expando from an element + // Fails in Internet Explorer + try { + delete div.test; + } catch( e ) { + support.deleteExpando = false; + } + + if ( !div.addEventListener && div.attachEvent && div.fireEvent ) { + div.attachEvent( "onclick", function() { + // Cloning a node shouldn't copy over any + // bound event handlers (IE does this) + support.noCloneEvent = false; + }); + div.cloneNode( true ).fireEvent( "onclick" ); + } + + // Check if a radio maintains it's value + // after being appended to the DOM + input = document.createElement("input"); + input.value = "t"; + input.setAttribute("type", "radio"); + support.radioValue = input.value === "t"; + + input.setAttribute("checked", "checked"); + div.appendChild( input ); + fragment = document.createDocumentFragment(); + fragment.appendChild( div.firstChild ); + + // WebKit doesn't clone checked state correctly in fragments + support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; + + div.innerHTML = ""; + + // Figure out if the W3C box model works as expected + div.style.width = div.style.paddingLeft = "1px"; + + body = document.getElementsByTagName( "body" )[ 0 ]; + // We use our own, invisible, body unless the body is already present + // in which case we use a div (#9239) + testElement = document.createElement( body ? "div" : "body" ); + testElementStyle = { + visibility: "hidden", + width: 0, + height: 0, + border: 0, + margin: 0 + }; + if ( body ) { + jQuery.extend( testElementStyle, { + position: "absolute", + left: -1000, + top: -1000 + }); + } + for ( i in testElementStyle ) { + testElement.style[ i ] = testElementStyle[ i ]; + } + testElement.appendChild( div ); + testElementParent = body || documentElement; + testElementParent.insertBefore( testElement, testElementParent.firstChild ); + + // Check if a disconnected checkbox will retain its checked + // value of true after appended to the DOM (IE6/7) + support.appendChecked = input.checked; + + support.boxModel = div.offsetWidth === 2; + + if ( "zoom" in div.style ) { + // Check if natively block-level elements act like inline-block + // elements when setting their display to 'inline' and giving + // them layout + // (IE < 8 does this) + div.style.display = "inline"; + div.style.zoom = 1; + support.inlineBlockNeedsLayout = ( div.offsetWidth === 2 ); + + // Check if elements with layout shrink-wrap their children + // (IE 6 does this) + div.style.display = ""; + div.innerHTML = "
"; + support.shrinkWrapBlocks = ( div.offsetWidth !== 2 ); + } + + div.innerHTML = "
t
"; + tds = div.getElementsByTagName( "td" ); + + // Check if table cells still have offsetWidth/Height when they are set + // to display:none and there are still other visible table cells in a + // table row; if so, offsetWidth/Height are not reliable for use when + // determining if an element has been hidden directly using + // display:none (it is still safe to use offsets if a parent element is + // hidden; don safety goggles and see bug #4512 for more information). + // (only IE 8 fails this test) + isSupported = ( tds[ 0 ].offsetHeight === 0 ); + + tds[ 0 ].style.display = ""; + tds[ 1 ].style.display = "none"; + + // Check if empty table cells still have offsetWidth/Height + // (IE < 8 fail this test) + support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); + div.innerHTML = ""; + + // Check if div with explicit width and no margin-right incorrectly + // gets computed margin-right based on width of container. For more + // info see bug #3333 + // Fails in WebKit before Feb 2011 nightlies + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + if ( document.defaultView && document.defaultView.getComputedStyle ) { + marginDiv = document.createElement( "div" ); + marginDiv.style.width = "0"; + marginDiv.style.marginRight = "0"; + div.appendChild( marginDiv ); + support.reliableMarginRight = + ( parseInt( ( document.defaultView.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0; + } + + // Remove the body element we added + testElement.innerHTML = ""; + testElementParent.removeChild( testElement ); + + // Technique from Juriy Zaytsev + // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/ + // We only care about the case where non-standard event systems + // are used, namely in IE. Short-circuiting here helps us to + // avoid an eval call (in setAttribute) which can cause CSP + // to go haywire. See: https://developer.mozilla.org/en/Security/CSP + if ( div.attachEvent ) { + for( i in { + submit: 1, + change: 1, + focusin: 1 + } ) { + eventName = "on" + i; + isSupported = ( eventName in div ); + if ( !isSupported ) { + div.setAttribute( eventName, "return;" ); + isSupported = ( typeof div[ eventName ] === "function" ); + } + support[ i + "Bubbles" ] = isSupported; + } + } + + // Null connected elements to avoid leaks in IE + testElement = fragment = select = opt = body = marginDiv = div = input = null; + + return support; +})(); + +// Keep track of boxModel +jQuery.boxModel = jQuery.support.boxModel; + + + + +var rbrace = /^(?:\{.*\}|\[.*\])$/, + rmultiDash = /([a-z])([A-Z])/g; + +jQuery.extend({ + cache: {}, + + // Please use with caution + uuid: 0, + + // Unique for each copy of jQuery on the page + // Non-digits removed to match rinlinejQuery + expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ), + + // The following elements throw uncatchable exceptions if you + // attempt to add expando properties to them. + noData: { + "embed": true, + // Ban all objects except for Flash (which handle expandos) + "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", + "applet": true + }, + + hasData: function( elem ) { + elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; + + return !!elem && !isEmptyDataObject( elem ); + }, + + data: function( elem, name, data, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var internalKey = jQuery.expando, getByName = typeof name === "string", thisCache, + + // We have to handle DOM nodes and JS objects differently because IE6-7 + // can't GC object references properly across the DOM-JS boundary + isNode = elem.nodeType, + + // Only DOM nodes need the global jQuery cache; JS object data is + // attached directly to the object so GC can occur automatically + cache = isNode ? jQuery.cache : elem, + + // Only defining an ID for JS objects if its cache already exists allows + // the code to shortcut on the same path as a DOM node with no cache + id = isNode ? elem[ jQuery.expando ] : elem[ jQuery.expando ] && jQuery.expando; + + // Avoid doing any more work than we need to when trying to get data on an + // object that has no data at all + if ( (!id || (pvt && id && !cache[ id ][ internalKey ])) && getByName && data === undefined ) { + return; + } + + if ( !id ) { + // Only DOM nodes need a new unique ID for each element since their data + // ends up in the global cache + if ( isNode ) { + elem[ jQuery.expando ] = id = ++jQuery.uuid; + } else { + id = jQuery.expando; + } + } + + if ( !cache[ id ] ) { + cache[ id ] = {}; + + // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery + // metadata on plain JS objects when the object is serialized using + // JSON.stringify + if ( !isNode ) { + cache[ id ].toJSON = jQuery.noop; + } + } + + // An object can be passed to jQuery.data instead of a key/value pair; this gets + // shallow copied over onto the existing cache + if ( typeof name === "object" || typeof name === "function" ) { + if ( pvt ) { + cache[ id ][ internalKey ] = jQuery.extend(cache[ id ][ internalKey ], name); + } else { + cache[ id ] = jQuery.extend(cache[ id ], name); + } + } + + thisCache = cache[ id ]; + + // Internal jQuery data is stored in a separate object inside the object's data + // cache in order to avoid key collisions between internal data and user-defined + // data + if ( pvt ) { + if ( !thisCache[ internalKey ] ) { + thisCache[ internalKey ] = {}; + } + + thisCache = thisCache[ internalKey ]; + } + + if ( data !== undefined ) { + thisCache[ jQuery.camelCase( name ) ] = data; + } + + // TODO: This is a hack for 1.5 ONLY. It will be removed in 1.6. Users should + // not attempt to inspect the internal events object using jQuery.data, as this + // internal data object is undocumented and subject to change. + if ( name === "events" && !thisCache[name] ) { + return thisCache[ internalKey ] && thisCache[ internalKey ].events; + } + + return getByName ? + // Check for both converted-to-camel and non-converted data property names + thisCache[ jQuery.camelCase( name ) ] || thisCache[ name ] : + thisCache; + }, + + removeData: function( elem, name, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var internalKey = jQuery.expando, isNode = elem.nodeType, + + // See jQuery.data for more information + cache = isNode ? jQuery.cache : elem, + + // See jQuery.data for more information + id = isNode ? elem[ jQuery.expando ] : jQuery.expando; + + // If there is already no cache entry for this object, there is no + // purpose in continuing + if ( !cache[ id ] ) { + return; + } + + if ( name ) { + var thisCache = pvt ? cache[ id ][ internalKey ] : cache[ id ]; + + if ( thisCache ) { + delete thisCache[ name ]; + + // If there is no data left in the cache, we want to continue + // and let the cache object itself get destroyed + if ( !isEmptyDataObject(thisCache) ) { + return; + } + } + } + + // See jQuery.data for more information + if ( pvt ) { + delete cache[ id ][ internalKey ]; + + // Don't destroy the parent cache unless the internal data object + // had been the only thing left in it + if ( !isEmptyDataObject(cache[ id ]) ) { + return; + } + } + + var internalCache = cache[ id ][ internalKey ]; + + // Browsers that fail expando deletion also refuse to delete expandos on + // the window, but it will allow it on all other JS objects; other browsers + // don't care + if ( jQuery.support.deleteExpando || cache != window ) { + delete cache[ id ]; + } else { + cache[ id ] = null; + } + + // We destroyed the entire user cache at once because it's faster than + // iterating through each key, but we need to continue to persist internal + // data if it existed + if ( internalCache ) { + cache[ id ] = {}; + // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery + // metadata on plain JS objects when the object is serialized using + // JSON.stringify + if ( !isNode ) { + cache[ id ].toJSON = jQuery.noop; + } + + cache[ id ][ internalKey ] = internalCache; + + // Otherwise, we need to eliminate the expando on the node to avoid + // false lookups in the cache for entries that no longer exist + } else if ( isNode ) { + // IE does not allow us to delete expando properties from nodes, + // nor does it have a removeAttribute function on Document nodes; + // we must handle all of these cases + if ( jQuery.support.deleteExpando ) { + delete elem[ jQuery.expando ]; + } else if ( elem.removeAttribute ) { + elem.removeAttribute( jQuery.expando ); + } else { + elem[ jQuery.expando ] = null; + } + } + }, + + // For internal use only. + _data: function( elem, name, data ) { + return jQuery.data( elem, name, data, true ); + }, + + // A method for determining if a DOM node can handle the data expando + acceptData: function( elem ) { + if ( elem.nodeName ) { + var match = jQuery.noData[ elem.nodeName.toLowerCase() ]; + + if ( match ) { + return !(match === true || elem.getAttribute("classid") !== match); + } + } + + return true; + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + var data = null; + + if ( typeof key === "undefined" ) { + if ( this.length ) { + data = jQuery.data( this[0] ); + + if ( this[0].nodeType === 1 ) { + var attr = this[0].attributes, name; + for ( var i = 0, l = attr.length; i < l; i++ ) { + name = attr[i].name; + + if ( name.indexOf( "data-" ) === 0 ) { + name = jQuery.camelCase( name.substring(5) ); + + dataAttr( this[0], name, data[ name ] ); + } + } + } + } + + return data; + + } else if ( typeof key === "object" ) { + return this.each(function() { + jQuery.data( this, key ); + }); + } + + var parts = key.split("."); + parts[1] = parts[1] ? "." + parts[1] : ""; + + if ( value === undefined ) { + data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]); + + // Try to fetch any internally stored data first + if ( data === undefined && this.length ) { + data = jQuery.data( this[0], key ); + data = dataAttr( this[0], key, data ); + } + + return data === undefined && parts[1] ? + this.data( parts[0] ) : + data; + + } else { + return this.each(function() { + var $this = jQuery( this ), + args = [ parts[0], value ]; + + $this.triggerHandler( "setData" + parts[1] + "!", args ); + jQuery.data( this, key, value ); + $this.triggerHandler( "changeData" + parts[1] + "!", args ); + }); + } + }, + + removeData: function( key ) { + return this.each(function() { + jQuery.removeData( this, key ); + }); + } +}); + +function dataAttr( elem, key, data ) { + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + var name = "data-" + key.replace( rmultiDash, "$1-$2" ).toLowerCase(); + + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + !jQuery.isNaN( data ) ? parseFloat( data ) : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + jQuery.data( elem, key, data ); + + } else { + data = undefined; + } + } + + return data; +} + +// TODO: This is a hack for 1.5 ONLY to allow objects with a single toJSON +// property to be considered empty objects; this property always exists in +// order to make sure JSON.stringify does not expose internal metadata +function isEmptyDataObject( obj ) { + for ( var name in obj ) { + if ( name !== "toJSON" ) { + return false; + } + } + + return true; +} + + + + +function handleQueueMarkDefer( elem, type, src ) { + var deferDataKey = type + "defer", + queueDataKey = type + "queue", + markDataKey = type + "mark", + defer = jQuery.data( elem, deferDataKey, undefined, true ); + if ( defer && + ( src === "queue" || !jQuery.data( elem, queueDataKey, undefined, true ) ) && + ( src === "mark" || !jQuery.data( elem, markDataKey, undefined, true ) ) ) { + // Give room for hard-coded callbacks to fire first + // and eventually mark/queue something else on the element + setTimeout( function() { + if ( !jQuery.data( elem, queueDataKey, undefined, true ) && + !jQuery.data( elem, markDataKey, undefined, true ) ) { + jQuery.removeData( elem, deferDataKey, true ); + defer.resolve(); + } + }, 0 ); + } +} + +jQuery.extend({ + + _mark: function( elem, type ) { + if ( elem ) { + type = (type || "fx") + "mark"; + jQuery.data( elem, type, (jQuery.data(elem,type,undefined,true) || 0) + 1, true ); + } + }, + + _unmark: function( force, elem, type ) { + if ( force !== true ) { + type = elem; + elem = force; + force = false; + } + if ( elem ) { + type = type || "fx"; + var key = type + "mark", + count = force ? 0 : ( (jQuery.data( elem, key, undefined, true) || 1 ) - 1 ); + if ( count ) { + jQuery.data( elem, key, count, true ); + } else { + jQuery.removeData( elem, key, true ); + handleQueueMarkDefer( elem, type, "mark" ); + } + } + }, + + queue: function( elem, type, data ) { + if ( elem ) { + type = (type || "fx") + "queue"; + var q = jQuery.data( elem, type, undefined, true ); + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !q || jQuery.isArray(data) ) { + q = jQuery.data( elem, type, jQuery.makeArray(data), true ); + } else { + q.push( data ); + } + } + return q || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + fn = queue.shift(), + defer; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + } + + if ( fn ) { + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift("inprogress"); + } + + fn.call(elem, function() { + jQuery.dequeue(elem, type); + }); + } + + if ( !queue.length ) { + jQuery.removeData( elem, type + "queue", true ); + handleQueueMarkDefer( elem, type, "queue" ); + } + } +}); + +jQuery.fn.extend({ + queue: function( type, data ) { + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + } + + if ( data === undefined ) { + return jQuery.queue( this[0], type ); + } + return this.each(function() { + var queue = jQuery.queue( this, type, data ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); + }, + dequeue: function( type ) { + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }, + // Based off of the plugin by Clint Helfers, with permission. + // http://blindsignals.com/index.php/2009/07/jquery-delay/ + delay: function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[time] || time : time; + type = type || "fx"; + + return this.queue( type, function() { + var elem = this; + setTimeout(function() { + jQuery.dequeue( elem, type ); + }, time ); + }); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, object ) { + if ( typeof type !== "string" ) { + object = type; + type = undefined; + } + type = type || "fx"; + var defer = jQuery.Deferred(), + elements = this, + i = elements.length, + count = 1, + deferDataKey = type + "defer", + queueDataKey = type + "queue", + markDataKey = type + "mark", + tmp; + function resolve() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + } + while( i-- ) { + if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) || + ( jQuery.data( elements[ i ], queueDataKey, undefined, true ) || + jQuery.data( elements[ i ], markDataKey, undefined, true ) ) && + jQuery.data( elements[ i ], deferDataKey, jQuery._Deferred(), true ) )) { + count++; + tmp.done( resolve ); + } + } + resolve(); + return defer.promise(); + } +}); + + + + +var rclass = /[\n\t\r]/g, + rspace = /\s+/, + rreturn = /\r/g, + rtype = /^(?:button|input)$/i, + rfocusable = /^(?:button|input|object|select|textarea)$/i, + rclickable = /^a(?:rea)?$/i, + rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i, + rinvalidChar = /\:|^on/, + formHook, boolHook; + +jQuery.fn.extend({ + attr: function( name, value ) { + return jQuery.access( this, name, value, true, jQuery.attr ); + }, + + removeAttr: function( name ) { + return this.each(function() { + jQuery.removeAttr( this, name ); + }); + }, + + prop: function( name, value ) { + return jQuery.access( this, name, value, true, jQuery.prop ); + }, + + removeProp: function( name ) { + name = jQuery.propFix[ name ] || name; + return this.each(function() { + // try/catch handles cases where IE balks (such as removing a property on window) + try { + this[ name ] = undefined; + delete this[ name ]; + } catch( e ) {} + }); + }, + + addClass: function( value ) { + var classNames, i, l, elem, + setClass, c, cl; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).addClass( value.call(this, j, this.className) ); + }); + } + + if ( value && typeof value === "string" ) { + classNames = value.split( rspace ); + + for ( i = 0, l = this.length; i < l; i++ ) { + elem = this[ i ]; + + if ( elem.nodeType === 1 ) { + if ( !elem.className && classNames.length === 1 ) { + elem.className = value; + + } else { + setClass = " " + elem.className + " "; + + for ( c = 0, cl = classNames.length; c < cl; c++ ) { + if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) { + setClass += classNames[ c ] + " "; + } + } + elem.className = jQuery.trim( setClass ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classNames, i, l, elem, className, c, cl; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).removeClass( value.call(this, j, this.className) ); + }); + } + + if ( (value && typeof value === "string") || value === undefined ) { + classNames = (value || "").split( rspace ); + + for ( i = 0, l = this.length; i < l; i++ ) { + elem = this[ i ]; + + if ( elem.nodeType === 1 && elem.className ) { + if ( value ) { + className = (" " + elem.className + " ").replace( rclass, " " ); + for ( c = 0, cl = classNames.length; c < cl; c++ ) { + className = className.replace(" " + classNames[ c ] + " ", " "); + } + elem.className = jQuery.trim( className ); + + } else { + elem.className = ""; + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isBool = typeof stateVal === "boolean"; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( i ) { + jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); + }); + } + + return this.each(function() { + if ( type === "string" ) { + // toggle individual class names + var className, + i = 0, + self = jQuery( this ), + state = stateVal, + classNames = value.split( rspace ); + + while ( (className = classNames[ i++ ]) ) { + // check each className given, space seperated list + state = isBool ? state : !self.hasClass( className ); + self[ state ? "addClass" : "removeClass" ]( className ); + } + + } else if ( type === "undefined" || type === "boolean" ) { + if ( this.className ) { + // store className if set + jQuery._data( this, "__className__", this.className ); + } + + // toggle whole className + this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; + } + }); + }, + + hasClass: function( selector ) { + var className = " " + selector + " "; + for ( var i = 0, l = this.length; i < l; i++ ) { + if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { + return true; + } + } + + return false; + }, + + val: function( value ) { + var hooks, ret, + elem = this[0]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.nodeName.toLowerCase() ] || jQuery.valHooks[ elem.type ]; + + if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { + return ret; + } + + ret = elem.value; + + return typeof ret === "string" ? + // handle most common string cases + ret.replace(rreturn, "") : + // handle cases where value is null/undef or number + ret == null ? "" : ret; + } + + return undefined; + } + + var isFunction = jQuery.isFunction( value ); + + return this.each(function( i ) { + var self = jQuery(this), val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( isFunction ) { + val = value.call( this, i, self.val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + } else if ( typeof val === "number" ) { + val += ""; + } else if ( jQuery.isArray( val ) ) { + val = jQuery.map(val, function ( value ) { + return value == null ? "" : value + ""; + }); + } + + hooks = jQuery.valHooks[ this.nodeName.toLowerCase() ] || jQuery.valHooks[ this.type ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + }); + } +}); + +jQuery.extend({ + valHooks: { + option: { + get: function( elem ) { + // attributes.value is undefined in Blackberry 4.7 but + // uses .value. See #6932 + var val = elem.attributes.value; + return !val || val.specified ? elem.value : elem.text; + } + }, + select: { + get: function( elem ) { + var value, + index = elem.selectedIndex, + values = [], + options = elem.options, + one = elem.type === "select-one"; + + // Nothing was selected + if ( index < 0 ) { + return null; + } + + // Loop through all the selected options + for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) { + var option = options[ i ]; + + // Don't return options that are disabled or in a disabled optgroup + if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) && + (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + // Fixes Bug #2551 -- select.val() broken in IE after form.reset() + if ( one && !values.length && options.length ) { + return jQuery( options[ index ] ).val(); + } + + return values; + }, + + set: function( elem, value ) { + var values = jQuery.makeArray( value ); + + jQuery(elem).find("option").each(function() { + this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; + }); + + if ( !values.length ) { + elem.selectedIndex = -1; + } + return values; + } + } + }, + + attrFn: { + val: true, + css: true, + html: true, + text: true, + data: true, + width: true, + height: true, + offset: true + }, + + attrFix: { + // Always normalize to ensure hook usage + tabindex: "tabIndex" + }, + + attr: function( elem, name, value, pass ) { + var nType = elem.nodeType; + + // don't get/set attributes on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return undefined; + } + + if ( pass && name in jQuery.attrFn ) { + return jQuery( elem )[ name ]( value ); + } + + // Fallback to prop when attributes are not supported + if ( !("getAttribute" in elem) ) { + return jQuery.prop( elem, name, value ); + } + + var ret, hooks, + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + // Normalize the name if needed + if ( notxml ) { + name = jQuery.attrFix[ name ] || name; + + hooks = jQuery.attrHooks[ name ]; + + if ( !hooks ) { + // Use boolHook for boolean attributes + if ( rboolean.test( name ) ) { + + hooks = boolHook; + + // Use formHook for forms and if the name contains certain characters + } else if ( formHook && name !== "className" && + (jQuery.nodeName( elem, "form" ) || rinvalidChar.test( name )) ) { + + hooks = formHook; + } + } + } + + if ( value !== undefined ) { + + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return undefined; + + } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + elem.setAttribute( name, "" + value ); + return value; + } + + } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + + ret = elem.getAttribute( name ); + + // Non-existent attributes return null, we normalize to undefined + return ret === null ? + undefined : + ret; + } + }, + + removeAttr: function( elem, name ) { + var propName; + if ( elem.nodeType === 1 ) { + name = jQuery.attrFix[ name ] || name; + + if ( jQuery.support.getSetAttribute ) { + // Use removeAttribute in browsers that support it + elem.removeAttribute( name ); + } else { + jQuery.attr( elem, name, "" ); + elem.removeAttributeNode( elem.getAttributeNode( name ) ); + } + + // Set corresponding property to false for boolean attributes + if ( rboolean.test( name ) && (propName = jQuery.propFix[ name ] || name) in elem ) { + elem[ propName ] = false; + } + } + }, + + attrHooks: { + type: { + set: function( elem, value ) { + // We can't allow the type property to be changed (since it causes problems in IE) + if ( rtype.test( elem.nodeName ) && elem.parentNode ) { + jQuery.error( "type property can't be changed" ); + } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { + // Setting the type on a radio button after the value resets the value in IE6-9 + // Reset value to it's default in case type is set after value + // This is for element creation + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + }, + tabIndex: { + get: function( elem ) { + // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set + // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + var attributeNode = elem.getAttributeNode("tabIndex"); + + return attributeNode && attributeNode.specified ? + parseInt( attributeNode.value, 10 ) : + rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? + 0 : + undefined; + } + }, + // Use the value property for back compat + // Use the formHook for button elements in IE6/7 (#1954) + value: { + get: function( elem, name ) { + if ( formHook && jQuery.nodeName( elem, "button" ) ) { + return formHook.get( elem, name ); + } + return name in elem ? + elem.value : + null; + }, + set: function( elem, value, name ) { + if ( formHook && jQuery.nodeName( elem, "button" ) ) { + return formHook.set( elem, value, name ); + } + // Does not return so that setAttribute is also used + elem.value = value; + } + } + }, + + propFix: { + tabindex: "tabIndex", + readonly: "readOnly", + "for": "htmlFor", + "class": "className", + maxlength: "maxLength", + cellspacing: "cellSpacing", + cellpadding: "cellPadding", + rowspan: "rowSpan", + colspan: "colSpan", + usemap: "useMap", + frameborder: "frameBorder", + contenteditable: "contentEditable" + }, + + prop: function( elem, name, value ) { + var nType = elem.nodeType; + + // don't get/set properties on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return undefined; + } + + var ret, hooks, + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + if ( notxml ) { + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + return (elem[ name ] = value); + } + + } else { + if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== undefined ) { + return ret; + + } else { + return elem[ name ]; + } + } + }, + + propHooks: {} +}); + +// Hook for boolean attributes +boolHook = { + get: function( elem, name ) { + // Align boolean attributes with corresponding properties + return jQuery.prop( elem, name ) ? + name.toLowerCase() : + undefined; + }, + set: function( elem, value, name ) { + var propName; + if ( value === false ) { + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + // value is true since we know at this point it's type boolean and not false + // Set boolean attributes to the same name and set the DOM property + propName = jQuery.propFix[ name ] || name; + if ( propName in elem ) { + // Only set the IDL specifically if it already exists on the element + elem[ propName ] = true; + } + + elem.setAttribute( name, name.toLowerCase() ); + } + return name; + } +}; + +// IE6/7 do not support getting/setting some attributes with get/setAttribute +if ( !jQuery.support.getSetAttribute ) { + + // propFix is more comprehensive and contains all fixes + jQuery.attrFix = jQuery.propFix; + + // Use this for any attribute on a form in IE6/7 + formHook = jQuery.attrHooks.name = jQuery.attrHooks.title = jQuery.valHooks.button = { + get: function( elem, name ) { + var ret; + ret = elem.getAttributeNode( name ); + // Return undefined if nodeValue is empty string + return ret && ret.nodeValue !== "" ? + ret.nodeValue : + undefined; + }, + set: function( elem, value, name ) { + // Check form objects in IE (multiple bugs related) + // Only use nodeValue if the attribute node exists on the form + var ret = elem.getAttributeNode( name ); + if ( ret ) { + ret.nodeValue = value; + return value; + } + } + }; + + // Set width and height to auto instead of 0 on empty string( Bug #8150 ) + // This is for removals + jQuery.each([ "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + set: function( elem, value ) { + if ( value === "" ) { + elem.setAttribute( name, "auto" ); + return value; + } + } + }); + }); +} + + +// Some attributes require a special call on IE +if ( !jQuery.support.hrefNormalized ) { + jQuery.each([ "href", "src", "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + get: function( elem ) { + var ret = elem.getAttribute( name, 2 ); + return ret === null ? undefined : ret; + } + }); + }); +} + +if ( !jQuery.support.style ) { + jQuery.attrHooks.style = { + get: function( elem ) { + // Return undefined in the case of empty string + // Normalize to lowercase since IE uppercases css property names + return elem.style.cssText.toLowerCase() || undefined; + }, + set: function( elem, value ) { + return (elem.style.cssText = "" + value); + } + }; +} + +// Safari mis-reports the default selected property of an option +// Accessing the parent's selectedIndex property fixes it +if ( !jQuery.support.optSelected ) { + jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, { + get: function( elem ) { + var parent = elem.parentNode; + + if ( parent ) { + parent.selectedIndex; + + // Make sure that it also works with optgroups, see #5701 + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + } + }); +} + +// Radios and checkboxes getter/setter +if ( !jQuery.support.checkOn ) { + jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + get: function( elem ) { + // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified + return elem.getAttribute("value") === null ? "on" : elem.value; + } + }; + }); +} +jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], { + set: function( elem, value ) { + if ( jQuery.isArray( value ) ) { + return (elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0); + } + } + }); +}); + + + + +var rnamespaces = /\.(.*)$/, + rformElems = /^(?:textarea|input|select)$/i, + rperiod = /\./g, + rspaces = / /g, + rescape = /[^\w\s.|`]/g, + fcleanup = function( nm ) { + return nm.replace(rescape, "\\$&"); + }; + +/* + * A number of helper functions used for managing events. + * Many of the ideas behind this code originated from + * Dean Edwards' addEvent library. + */ +jQuery.event = { + + // Bind an event to an element + // Original by Dean Edwards + add: function( elem, types, handler, data ) { + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + if ( handler === false ) { + handler = returnFalse; + } else if ( !handler ) { + // Fixes bug #7229. Fix recommended by jdalton + return; + } + + var handleObjIn, handleObj; + + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + } + + // Make sure that the function being executed has a unique ID + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure + var elemData = jQuery._data( elem ); + + // If no elemData is found then we must be trying to bind to one of the + // banned noData elements + if ( !elemData ) { + return; + } + + var events = elemData.events, + eventHandle = elemData.handle; + + if ( !events ) { + elemData.events = events = {}; + } + + if ( !eventHandle ) { + elemData.handle = eventHandle = function( e ) { + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ? + jQuery.event.handle.apply( eventHandle.elem, arguments ) : + undefined; + }; + } + + // Add elem as a property of the handle function + // This is to prevent a memory leak with non-native events in IE. + eventHandle.elem = elem; + + // Handle multiple events separated by a space + // jQuery(...).bind("mouseover mouseout", fn); + types = types.split(" "); + + var type, i = 0, namespaces; + + while ( (type = types[ i++ ]) ) { + handleObj = handleObjIn ? + jQuery.extend({}, handleObjIn) : + { handler: handler, data: data }; + + // Namespaced event handlers + if ( type.indexOf(".") > -1 ) { + namespaces = type.split("."); + type = namespaces.shift(); + handleObj.namespace = namespaces.slice(0).sort().join("."); + + } else { + namespaces = []; + handleObj.namespace = ""; + } + + handleObj.type = type; + if ( !handleObj.guid ) { + handleObj.guid = handler.guid; + } + + // Get the current list of functions bound to this event + var handlers = events[ type ], + special = jQuery.event.special[ type ] || {}; + + // Init the event handler queue + if ( !handlers ) { + handlers = events[ type ] = []; + + // Check for a special event handler + // Only use addEventListener/attachEvent if the special + // events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + // Bind the global event handler to the element + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add the function to the element's handler list + handlers.push( handleObj ); + + // Keep track of which events have been used, for event optimization + jQuery.event.global[ type ] = true; + } + + // Nullify elem to prevent memory leaks in IE + elem = null; + }, + + global: {}, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, pos ) { + // don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + if ( handler === false ) { + handler = returnFalse; + } + + var ret, type, fn, j, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType, + elemData = jQuery.hasData( elem ) && jQuery._data( elem ), + events = elemData && elemData.events; + + if ( !elemData || !events ) { + return; + } + + // types is actually an event object here + if ( types && types.type ) { + handler = types.handler; + types = types.type; + } + + // Unbind all events for the element + if ( !types || typeof types === "string" && types.charAt(0) === "." ) { + types = types || ""; + + for ( type in events ) { + jQuery.event.remove( elem, type + types ); + } + + return; + } + + // Handle multiple events separated by a space + // jQuery(...).unbind("mouseover mouseout", fn); + types = types.split(" "); + + while ( (type = types[ i++ ]) ) { + origType = type; + handleObj = null; + all = type.indexOf(".") < 0; + namespaces = []; + + if ( !all ) { + // Namespaced event handlers + namespaces = type.split("."); + type = namespaces.shift(); + + namespace = new RegExp("(^|\\.)" + + jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)"); + } + + eventType = events[ type ]; + + if ( !eventType ) { + continue; + } + + if ( !handler ) { + for ( j = 0; j < eventType.length; j++ ) { + handleObj = eventType[ j ]; + + if ( all || namespace.test( handleObj.namespace ) ) { + jQuery.event.remove( elem, origType, handleObj.handler, j ); + eventType.splice( j--, 1 ); + } + } + + continue; + } + + special = jQuery.event.special[ type ] || {}; + + for ( j = pos || 0; j < eventType.length; j++ ) { + handleObj = eventType[ j ]; + + if ( handler.guid === handleObj.guid ) { + // remove the given handler for the given type + if ( all || namespace.test( handleObj.namespace ) ) { + if ( pos == null ) { + eventType.splice( j--, 1 ); + } + + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + + if ( pos != null ) { + break; + } + } + } + + // remove generic event handler if no more handlers exist + if ( eventType.length === 0 || pos != null && eventType.length === 1 ) { + if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); + } + + ret = null; + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + var handle = elemData.handle; + if ( handle ) { + handle.elem = null; + } + + delete elemData.events; + delete elemData.handle; + + if ( jQuery.isEmptyObject( elemData ) ) { + jQuery.removeData( elem, undefined, true ); + } + } + }, + + // Events that are safe to short-circuit if no handlers are attached. + // Native DOM events should not be added, they may have inline handlers. + customEvent: { + "getData": true, + "setData": true, + "changeData": true + }, + + trigger: function( event, data, elem, onlyHandlers ) { + // Event object or event type + var type = event.type || event, + namespaces = [], + exclusive; + + if ( type.indexOf("!") >= 0 ) { + // Exclusive events trigger only for the exact event (no namespaces) + type = type.slice(0, -1); + exclusive = true; + } + + if ( type.indexOf(".") >= 0 ) { + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split("."); + type = namespaces.shift(); + namespaces.sort(); + } + + if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) { + // No jQuery handlers for this event type, and it can't have inline handlers + return; + } + + // Caller can pass in an Event, Object, or just an event type string + event = typeof event === "object" ? + // jQuery.Event object + event[ jQuery.expando ] ? event : + // Object literal + new jQuery.Event( type, event ) : + // Just the event type (string) + new jQuery.Event( type ); + + event.type = type; + event.exclusive = exclusive; + event.namespace = namespaces.join("."); + event.namespace_re = new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)"); + + // triggerHandler() and global events don't bubble or run the default action + if ( onlyHandlers || !elem ) { + event.preventDefault(); + event.stopPropagation(); + } + + // Handle a global trigger + if ( !elem ) { + // TODO: Stop taunting the data cache; remove global events and always attach to document + jQuery.each( jQuery.cache, function() { + // internalKey variable is just used to make it easier to find + // and potentially change this stuff later; currently it just + // points to jQuery.expando + var internalKey = jQuery.expando, + internalCache = this[ internalKey ]; + if ( internalCache && internalCache.events && internalCache.events[ type ] ) { + jQuery.event.trigger( event, data, internalCache.handle.elem ); + } + }); + return; + } + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // Clean up the event in case it is being reused + event.result = undefined; + event.target = elem; + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data != null ? jQuery.makeArray( data ) : []; + data.unshift( event ); + + var cur = elem, + // IE doesn't like method names with a colon (#3533, #8272) + ontype = type.indexOf(":") < 0 ? "on" + type : ""; + + // Fire event on the current element, then bubble up the DOM tree + do { + var handle = jQuery._data( cur, "handle" ); + + event.currentTarget = cur; + if ( handle ) { + handle.apply( cur, data ); + } + + // Trigger an inline bound script + if ( ontype && jQuery.acceptData( cur ) && cur[ ontype ] && cur[ ontype ].apply( cur, data ) === false ) { + event.result = false; + event.preventDefault(); + } + + // Bubble up to document, then to window + cur = cur.parentNode || cur.ownerDocument || cur === event.target.ownerDocument && window; + } while ( cur && !event.isPropagationStopped() ); + + // If nobody prevented the default action, do it now + if ( !event.isDefaultPrevented() ) { + var old, + special = jQuery.event.special[ type ] || {}; + + if ( (!special._default || special._default.call( elem.ownerDocument, event ) === false) && + !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name name as the event. + // Can't use an .isFunction)() check here because IE6/7 fails that test. + // IE<9 dies on focus to hidden element (#1486), may want to revisit a try/catch. + try { + if ( ontype && elem[ type ] ) { + // Don't re-trigger an onFOO event when we call its FOO() method + old = elem[ ontype ]; + + if ( old ) { + elem[ ontype ] = null; + } + + jQuery.event.triggered = type; + elem[ type ](); + } + } catch ( ieError ) {} + + if ( old ) { + elem[ ontype ] = old; + } + + jQuery.event.triggered = undefined; + } + } + + return event.result; + }, + + handle: function( event ) { + event = jQuery.event.fix( event || window.event ); + // Snapshot the handlers list since a called handler may add/remove events. + var handlers = ((jQuery._data( this, "events" ) || {})[ event.type ] || []).slice(0), + run_all = !event.exclusive && !event.namespace, + args = Array.prototype.slice.call( arguments, 0 ); + + // Use the fix-ed Event rather than the (read-only) native event + args[0] = event; + event.currentTarget = this; + + for ( var j = 0, l = handlers.length; j < l; j++ ) { + var handleObj = handlers[ j ]; + + // Triggered event must 1) be non-exclusive and have no namespace, or + // 2) have namespace(s) a subset or equal to those in the bound event. + if ( run_all || event.namespace_re.test( handleObj.namespace ) ) { + // Pass in a reference to the handler function itself + // So that we can later remove it + event.handler = handleObj.handler; + event.data = handleObj.data; + event.handleObj = handleObj; + + var ret = handleObj.handler.apply( this, args ); + + if ( ret !== undefined ) { + event.result = ret; + if ( ret === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + + if ( event.isImmediatePropagationStopped() ) { + break; + } + } + } + return event.result; + }, + + props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "), + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // store a copy of the original event object + // and "clone" to set read-only properties + var originalEvent = event; + event = jQuery.Event( originalEvent ); + + for ( var i = this.props.length, prop; i; ) { + prop = this.props[ --i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Fix target property, if necessary + if ( !event.target ) { + // Fixes #1925 where srcElement might not be defined either + event.target = event.srcElement || document; + } + + // check if target is a textnode (safari) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && event.fromElement ) { + event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement; + } + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && event.clientX != null ) { + var eventDocument = event.target.ownerDocument || document, + doc = eventDocument.documentElement, + body = eventDocument.body; + + event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0); + event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0); + } + + // Add which for key events + if ( event.which == null && (event.charCode != null || event.keyCode != null) ) { + event.which = event.charCode != null ? event.charCode : event.keyCode; + } + + // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs) + if ( !event.metaKey && event.ctrlKey ) { + event.metaKey = event.ctrlKey; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && event.button !== undefined ) { + event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) )); + } + + return event; + }, + + // Deprecated, use jQuery.guid instead + guid: 1E8, + + // Deprecated, use jQuery.proxy instead + proxy: jQuery.proxy, + + special: { + ready: { + // Make sure the ready event is setup + setup: jQuery.bindReady, + teardown: jQuery.noop + }, + + live: { + add: function( handleObj ) { + jQuery.event.add( this, + liveConvert( handleObj.origType, handleObj.selector ), + jQuery.extend({}, handleObj, {handler: liveHandler, guid: handleObj.handler.guid}) ); + }, + + remove: function( handleObj ) { + jQuery.event.remove( this, liveConvert( handleObj.origType, handleObj.selector ), handleObj ); + } + }, + + beforeunload: { + setup: function( data, namespaces, eventHandle ) { + // We only want to do this special case on windows + if ( jQuery.isWindow( this ) ) { + this.onbeforeunload = eventHandle; + } + }, + + teardown: function( namespaces, eventHandle ) { + if ( this.onbeforeunload === eventHandle ) { + this.onbeforeunload = null; + } + } + } + } +}; + +jQuery.removeEvent = document.removeEventListener ? + function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } + } : + function( elem, type, handle ) { + if ( elem.detachEvent ) { + elem.detachEvent( "on" + type, handle ); + } + }; + +jQuery.Event = function( src, props ) { + // Allow instantiation without the 'new' keyword + if ( !this.preventDefault ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = (src.defaultPrevented || src.returnValue === false || + src.getPreventDefault && src.getPreventDefault()) ? returnTrue : returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // timeStamp is buggy for some events on Firefox(#3843) + // So we won't rely on the native value + this.timeStamp = jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +function returnFalse() { + return false; +} +function returnTrue() { + return true; +} + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + preventDefault: function() { + this.isDefaultPrevented = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + + // if preventDefault exists run it on the original event + if ( e.preventDefault ) { + e.preventDefault(); + + // otherwise set the returnValue property of the original event to false (IE) + } else { + e.returnValue = false; + } + }, + stopPropagation: function() { + this.isPropagationStopped = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + // if stopPropagation exists run it on the original event + if ( e.stopPropagation ) { + e.stopPropagation(); + } + // otherwise set the cancelBubble property of the original event to true (IE) + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + this.isImmediatePropagationStopped = returnTrue; + this.stopPropagation(); + }, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse +}; + +// Checks if an event happened on an element within another element +// Used in jQuery.event.special.mouseenter and mouseleave handlers +var withinElement = function( event ) { + + // Check if mouse(over|out) are still within the same parent element + var related = event.relatedTarget, + inside = false, + eventType = event.type; + + event.type = event.data; + + if ( related !== this ) { + + if ( related ) { + inside = jQuery.contains( this, related ); + } + + if ( !inside ) { + + jQuery.event.handle.apply( this, arguments ); + + event.type = eventType; + } + } +}, + +// In case of event delegation, we only need to rename the event.type, +// liveHandler will take care of the rest. +delegate = function( event ) { + event.type = event.data; + jQuery.event.handle.apply( this, arguments ); +}; + +// Create mouseenter and mouseleave events +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + setup: function( data ) { + jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig ); + }, + teardown: function( data ) { + jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement ); + } + }; +}); + +// submit delegation +if ( !jQuery.support.submitBubbles ) { + + jQuery.event.special.submit = { + setup: function( data, namespaces ) { + if ( !jQuery.nodeName( this, "form" ) ) { + jQuery.event.add(this, "click.specialSubmit", function( e ) { + var elem = e.target, + type = elem.type; + + if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) { + trigger( "submit", this, arguments ); + } + }); + + jQuery.event.add(this, "keypress.specialSubmit", function( e ) { + var elem = e.target, + type = elem.type; + + if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) { + trigger( "submit", this, arguments ); + } + }); + + } else { + return false; + } + }, + + teardown: function( namespaces ) { + jQuery.event.remove( this, ".specialSubmit" ); + } + }; + +} + +// change delegation, happens here so we have bind. +if ( !jQuery.support.changeBubbles ) { + + var changeFilters, + + getVal = function( elem ) { + var type = elem.type, val = elem.value; + + if ( type === "radio" || type === "checkbox" ) { + val = elem.checked; + + } else if ( type === "select-multiple" ) { + val = elem.selectedIndex > -1 ? + jQuery.map( elem.options, function( elem ) { + return elem.selected; + }).join("-") : + ""; + + } else if ( jQuery.nodeName( elem, "select" ) ) { + val = elem.selectedIndex; + } + + return val; + }, + + testChange = function testChange( e ) { + var elem = e.target, data, val; + + if ( !rformElems.test( elem.nodeName ) || elem.readOnly ) { + return; + } + + data = jQuery._data( elem, "_change_data" ); + val = getVal(elem); + + // the current data will be also retrieved by beforeactivate + if ( e.type !== "focusout" || elem.type !== "radio" ) { + jQuery._data( elem, "_change_data", val ); + } + + if ( data === undefined || val === data ) { + return; + } + + if ( data != null || val ) { + e.type = "change"; + e.liveFired = undefined; + jQuery.event.trigger( e, arguments[1], elem ); + } + }; + + jQuery.event.special.change = { + filters: { + focusout: testChange, + + beforedeactivate: testChange, + + click: function( e ) { + var elem = e.target, type = jQuery.nodeName( elem, "input" ) ? elem.type : ""; + + if ( type === "radio" || type === "checkbox" || jQuery.nodeName( elem, "select" ) ) { + testChange.call( this, e ); + } + }, + + // Change has to be called before submit + // Keydown will be called before keypress, which is used in submit-event delegation + keydown: function( e ) { + var elem = e.target, type = jQuery.nodeName( elem, "input" ) ? elem.type : ""; + + if ( (e.keyCode === 13 && !jQuery.nodeName( elem, "textarea" ) ) || + (e.keyCode === 32 && (type === "checkbox" || type === "radio")) || + type === "select-multiple" ) { + testChange.call( this, e ); + } + }, + + // Beforeactivate happens also before the previous element is blurred + // with this event you can't trigger a change event, but you can store + // information + beforeactivate: function( e ) { + var elem = e.target; + jQuery._data( elem, "_change_data", getVal(elem) ); + } + }, + + setup: function( data, namespaces ) { + if ( this.type === "file" ) { + return false; + } + + for ( var type in changeFilters ) { + jQuery.event.add( this, type + ".specialChange", changeFilters[type] ); + } + + return rformElems.test( this.nodeName ); + }, + + teardown: function( namespaces ) { + jQuery.event.remove( this, ".specialChange" ); + + return rformElems.test( this.nodeName ); + } + }; + + changeFilters = jQuery.event.special.change.filters; + + // Handle when the input is .focus()'d + changeFilters.focus = changeFilters.beforeactivate; +} + +function trigger( type, elem, args ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + // Don't pass args or remember liveFired; they apply to the donor event. + var event = jQuery.extend( {}, args[ 0 ] ); + event.type = type; + event.originalEvent = {}; + event.liveFired = undefined; + jQuery.event.handle.call( elem, event ); + if ( event.isDefaultPrevented() ) { + args[ 0 ].preventDefault(); + } +} + +// Create "bubbling" focus and blur events +if ( !jQuery.support.focusinBubbles ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler while someone wants focusin/focusout + var attaches = 0; + + jQuery.event.special[ fix ] = { + setup: function() { + if ( attaches++ === 0 ) { + document.addEventListener( orig, handler, true ); + } + }, + teardown: function() { + if ( --attaches === 0 ) { + document.removeEventListener( orig, handler, true ); + } + } + }; + + function handler( donor ) { + // Donor event is always a native one; fix it and switch its type. + // Let focusin/out handler cancel the donor focus/blur event. + var e = jQuery.event.fix( donor ); + e.type = fix; + e.originalEvent = {}; + jQuery.event.trigger( e, null, e.target ); + if ( e.isDefaultPrevented() ) { + donor.preventDefault(); + } + } + }); +} + +jQuery.each(["bind", "one"], function( i, name ) { + jQuery.fn[ name ] = function( type, data, fn ) { + var handler; + + // Handle object literals + if ( typeof type === "object" ) { + for ( var key in type ) { + this[ name ](key, data, type[key], fn); + } + return this; + } + + if ( arguments.length === 2 || data === false ) { + fn = data; + data = undefined; + } + + if ( name === "one" ) { + handler = function( event ) { + jQuery( this ).unbind( event, handler ); + return fn.apply( this, arguments ); + }; + handler.guid = fn.guid || jQuery.guid++; + } else { + handler = fn; + } + + if ( type === "unload" && name !== "one" ) { + this.one( type, data, fn ); + + } else { + for ( var i = 0, l = this.length; i < l; i++ ) { + jQuery.event.add( this[i], type, handler, data ); + } + } + + return this; + }; +}); + +jQuery.fn.extend({ + unbind: function( type, fn ) { + // Handle object literals + if ( typeof type === "object" && !type.preventDefault ) { + for ( var key in type ) { + this.unbind(key, type[key]); + } + + } else { + for ( var i = 0, l = this.length; i < l; i++ ) { + jQuery.event.remove( this[i], type, fn ); + } + } + + return this; + }, + + delegate: function( selector, types, data, fn ) { + return this.live( types, data, fn, selector ); + }, + + undelegate: function( selector, types, fn ) { + if ( arguments.length === 0 ) { + return this.unbind( "live" ); + + } else { + return this.die( types, null, fn, selector ); + } + }, + + trigger: function( type, data ) { + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }, + + triggerHandler: function( type, data ) { + if ( this[0] ) { + return jQuery.event.trigger( type, data, this[0], true ); + } + }, + + toggle: function( fn ) { + // Save reference to arguments for access in closure + var args = arguments, + guid = fn.guid || jQuery.guid++, + i = 0, + toggler = function( event ) { + // Figure out which function to execute + var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i; + jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 ); + + // Make sure that clicks stop + event.preventDefault(); + + // and execute the function + return args[ lastToggle ].apply( this, arguments ) || false; + }; + + // link all the functions, so any of them can unbind this click handler + toggler.guid = guid; + while ( i < args.length ) { + args[ i++ ].guid = guid; + } + + return this.click( toggler ); + }, + + hover: function( fnOver, fnOut ) { + return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); + } +}); + +var liveMap = { + focus: "focusin", + blur: "focusout", + mouseenter: "mouseover", + mouseleave: "mouseout" +}; + +jQuery.each(["live", "die"], function( i, name ) { + jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) { + var type, i = 0, match, namespaces, preType, + selector = origSelector || this.selector, + context = origSelector ? this : jQuery( this.context ); + + if ( typeof types === "object" && !types.preventDefault ) { + for ( var key in types ) { + context[ name ]( key, data, types[key], selector ); + } + + return this; + } + + if ( name === "die" && !types && + origSelector && origSelector.charAt(0) === "." ) { + + context.unbind( origSelector ); + + return this; + } + + if ( data === false || jQuery.isFunction( data ) ) { + fn = data || returnFalse; + data = undefined; + } + + types = (types || "").split(" "); + + while ( (type = types[ i++ ]) != null ) { + match = rnamespaces.exec( type ); + namespaces = ""; + + if ( match ) { + namespaces = match[0]; + type = type.replace( rnamespaces, "" ); + } + + if ( type === "hover" ) { + types.push( "mouseenter" + namespaces, "mouseleave" + namespaces ); + continue; + } + + preType = type; + + if ( liveMap[ type ] ) { + types.push( liveMap[ type ] + namespaces ); + type = type + namespaces; + + } else { + type = (liveMap[ type ] || type) + namespaces; + } + + if ( name === "live" ) { + // bind live handler + for ( var j = 0, l = context.length; j < l; j++ ) { + jQuery.event.add( context[j], "live." + liveConvert( type, selector ), + { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } ); + } + + } else { + // unbind live handler + context.unbind( "live." + liveConvert( type, selector ), fn ); + } + } + + return this; + }; +}); + +function liveHandler( event ) { + var stop, maxLevel, related, match, handleObj, elem, j, i, l, data, close, namespace, ret, + elems = [], + selectors = [], + events = jQuery._data( this, "events" ); + + // Make sure we avoid non-left-click bubbling in Firefox (#3861) and disabled elements in IE (#6911) + if ( event.liveFired === this || !events || !events.live || event.target.disabled || event.button && event.type === "click" ) { + return; + } + + if ( event.namespace ) { + namespace = new RegExp("(^|\\.)" + event.namespace.split(".").join("\\.(?:.*\\.)?") + "(\\.|$)"); + } + + event.liveFired = this; + + var live = events.live.slice(0); + + for ( j = 0; j < live.length; j++ ) { + handleObj = live[j]; + + if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) { + selectors.push( handleObj.selector ); + + } else { + live.splice( j--, 1 ); + } + } + + match = jQuery( event.target ).closest( selectors, event.currentTarget ); + + for ( i = 0, l = match.length; i < l; i++ ) { + close = match[i]; + + for ( j = 0; j < live.length; j++ ) { + handleObj = live[j]; + + if ( close.selector === handleObj.selector && (!namespace || namespace.test( handleObj.namespace )) && !close.elem.disabled ) { + elem = close.elem; + related = null; + + // Those two events require additional checking + if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) { + event.type = handleObj.preType; + related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0]; + + // Make sure not to accidentally match a child element with the same selector + if ( related && jQuery.contains( elem, related ) ) { + related = elem; + } + } + + if ( !related || related !== elem ) { + elems.push({ elem: elem, handleObj: handleObj, level: close.level }); + } + } + } + } + + for ( i = 0, l = elems.length; i < l; i++ ) { + match = elems[i]; + + if ( maxLevel && match.level > maxLevel ) { + break; + } + + event.currentTarget = match.elem; + event.data = match.handleObj.data; + event.handleObj = match.handleObj; + + ret = match.handleObj.origHandler.apply( match.elem, arguments ); + + if ( ret === false || event.isPropagationStopped() ) { + maxLevel = match.level; + + if ( ret === false ) { + stop = false; + } + if ( event.isImmediatePropagationStopped() ) { + break; + } + } + } + + return stop; +} + +function liveConvert( type, selector ) { + return (type && type !== "*" ? type + "." : "") + selector.replace(rperiod, "`").replace(rspaces, "&"); +} + +jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + + "change select submit keydown keypress keyup error").split(" "), function( i, name ) { + + // Handle event binding + jQuery.fn[ name ] = function( data, fn ) { + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; + + if ( jQuery.attrFn ) { + jQuery.attrFn[ name ] = true; + } +}); + + + +/*! + * Sizzle CSS Selector Engine + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){ + +var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, + done = 0, + toString = Object.prototype.toString, + hasDuplicate = false, + baseHasDuplicate = true, + rBackslash = /\\/g, + rNonWord = /\W/; + +// Here we check if the JavaScript engine is using some sort of +// optimization where it does not always call our comparision +// function. If that is the case, discard the hasDuplicate value. +// Thus far that includes Google Chrome. +[0, 0].sort(function() { + baseHasDuplicate = false; + return 0; +}); + +var Sizzle = function( selector, context, results, seed ) { + results = results || []; + context = context || document; + + var origContext = context; + + if ( context.nodeType !== 1 && context.nodeType !== 9 ) { + return []; + } + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + var m, set, checkSet, extra, ret, cur, pop, i, + prune = true, + contextXML = Sizzle.isXML( context ), + parts = [], + soFar = selector; + + // Reset the position of the chunker regexp (start from head) + do { + chunker.exec( "" ); + m = chunker.exec( soFar ); + + if ( m ) { + soFar = m[3]; + + parts.push( m[1] ); + + if ( m[2] ) { + extra = m[3]; + break; + } + } + } while ( m ); + + if ( parts.length > 1 && origPOS.exec( selector ) ) { + + if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { + set = posProcess( parts[0] + parts[1], context ); + + } else { + set = Expr.relative[ parts[0] ] ? + [ context ] : + Sizzle( parts.shift(), context ); + + while ( parts.length ) { + selector = parts.shift(); + + if ( Expr.relative[ selector ] ) { + selector += parts.shift(); + } + + set = posProcess( selector, set ); + } + } + + } else { + // Take a shortcut and set the context if the root selector is an ID + // (but not if it'll be faster if the inner selector is an ID) + if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && + Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { + + ret = Sizzle.find( parts.shift(), context, contextXML ); + context = ret.expr ? + Sizzle.filter( ret.expr, ret.set )[0] : + ret.set[0]; + } + + if ( context ) { + ret = seed ? + { expr: parts.pop(), set: makeArray(seed) } : + Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); + + set = ret.expr ? + Sizzle.filter( ret.expr, ret.set ) : + ret.set; + + if ( parts.length > 0 ) { + checkSet = makeArray( set ); + + } else { + prune = false; + } + + while ( parts.length ) { + cur = parts.pop(); + pop = cur; + + if ( !Expr.relative[ cur ] ) { + cur = ""; + } else { + pop = parts.pop(); + } + + if ( pop == null ) { + pop = context; + } + + Expr.relative[ cur ]( checkSet, pop, contextXML ); + } + + } else { + checkSet = parts = []; + } + } + + if ( !checkSet ) { + checkSet = set; + } + + if ( !checkSet ) { + Sizzle.error( cur || selector ); + } + + if ( toString.call(checkSet) === "[object Array]" ) { + if ( !prune ) { + results.push.apply( results, checkSet ); + + } else if ( context && context.nodeType === 1 ) { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) { + results.push( set[i] ); + } + } + + } else { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && checkSet[i].nodeType === 1 ) { + results.push( set[i] ); + } + } + } + + } else { + makeArray( checkSet, results ); + } + + if ( extra ) { + Sizzle( extra, origContext, results, seed ); + Sizzle.uniqueSort( results ); + } + + return results; +}; + +Sizzle.uniqueSort = function( results ) { + if ( sortOrder ) { + hasDuplicate = baseHasDuplicate; + results.sort( sortOrder ); + + if ( hasDuplicate ) { + for ( var i = 1; i < results.length; i++ ) { + if ( results[i] === results[ i - 1 ] ) { + results.splice( i--, 1 ); + } + } + } + } + + return results; +}; + +Sizzle.matches = function( expr, set ) { + return Sizzle( expr, null, null, set ); +}; + +Sizzle.matchesSelector = function( node, expr ) { + return Sizzle( expr, null, null, [node] ).length > 0; +}; + +Sizzle.find = function( expr, context, isXML ) { + var set; + + if ( !expr ) { + return []; + } + + for ( var i = 0, l = Expr.order.length; i < l; i++ ) { + var match, + type = Expr.order[i]; + + if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { + var left = match[1]; + match.splice( 1, 1 ); + + if ( left.substr( left.length - 1 ) !== "\\" ) { + match[1] = (match[1] || "").replace( rBackslash, "" ); + set = Expr.find[ type ]( match, context, isXML ); + + if ( set != null ) { + expr = expr.replace( Expr.match[ type ], "" ); + break; + } + } + } + } + + if ( !set ) { + set = typeof context.getElementsByTagName !== "undefined" ? + context.getElementsByTagName( "*" ) : + []; + } + + return { set: set, expr: expr }; +}; + +Sizzle.filter = function( expr, set, inplace, not ) { + var match, anyFound, + old = expr, + result = [], + curLoop = set, + isXMLFilter = set && set[0] && Sizzle.isXML( set[0] ); + + while ( expr && set.length ) { + for ( var type in Expr.filter ) { + if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) { + var found, item, + filter = Expr.filter[ type ], + left = match[1]; + + anyFound = false; + + match.splice(1,1); + + if ( left.substr( left.length - 1 ) === "\\" ) { + continue; + } + + if ( curLoop === result ) { + result = []; + } + + if ( Expr.preFilter[ type ] ) { + match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); + + if ( !match ) { + anyFound = found = true; + + } else if ( match === true ) { + continue; + } + } + + if ( match ) { + for ( var i = 0; (item = curLoop[i]) != null; i++ ) { + if ( item ) { + found = filter( item, match, i, curLoop ); + var pass = not ^ !!found; + + if ( inplace && found != null ) { + if ( pass ) { + anyFound = true; + + } else { + curLoop[i] = false; + } + + } else if ( pass ) { + result.push( item ); + anyFound = true; + } + } + } + } + + if ( found !== undefined ) { + if ( !inplace ) { + curLoop = result; + } + + expr = expr.replace( Expr.match[ type ], "" ); + + if ( !anyFound ) { + return []; + } + + break; + } + } + } + + // Improper expression + if ( expr === old ) { + if ( anyFound == null ) { + Sizzle.error( expr ); + + } else { + break; + } + } + + old = expr; + } + + return curLoop; +}; + +Sizzle.error = function( msg ) { + throw "Syntax error, unrecognized expression: " + msg; +}; + +var Expr = Sizzle.selectors = { + order: [ "ID", "NAME", "TAG" ], + + match: { + ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/, + ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/, + TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/, + CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/, + POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/, + PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/ + }, + + leftMatch: {}, + + attrMap: { + "class": "className", + "for": "htmlFor" + }, + + attrHandle: { + href: function( elem ) { + return elem.getAttribute( "href" ); + }, + type: function( elem ) { + return elem.getAttribute( "type" ); + } + }, + + relative: { + "+": function(checkSet, part){ + var isPartStr = typeof part === "string", + isTag = isPartStr && !rNonWord.test( part ), + isPartStrNotTag = isPartStr && !isTag; + + if ( isTag ) { + part = part.toLowerCase(); + } + + for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { + if ( (elem = checkSet[i]) ) { + while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} + + checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? + elem || false : + elem === part; + } + } + + if ( isPartStrNotTag ) { + Sizzle.filter( part, checkSet, true ); + } + }, + + ">": function( checkSet, part ) { + var elem, + isPartStr = typeof part === "string", + i = 0, + l = checkSet.length; + + if ( isPartStr && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + + for ( ; i < l; i++ ) { + elem = checkSet[i]; + + if ( elem ) { + var parent = elem.parentNode; + checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; + } + } + + } else { + for ( ; i < l; i++ ) { + elem = checkSet[i]; + + if ( elem ) { + checkSet[i] = isPartStr ? + elem.parentNode : + elem.parentNode === part; + } + } + + if ( isPartStr ) { + Sizzle.filter( part, checkSet, true ); + } + } + }, + + "": function(checkSet, part, isXML){ + var nodeCheck, + doneName = done++, + checkFn = dirCheck; + + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML ); + }, + + "~": function( checkSet, part, isXML ) { + var nodeCheck, + doneName = done++, + checkFn = dirCheck; + + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML ); + } + }, + + find: { + ID: function( match, context, isXML ) { + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [m] : []; + } + }, + + NAME: function( match, context ) { + if ( typeof context.getElementsByName !== "undefined" ) { + var ret = [], + results = context.getElementsByName( match[1] ); + + for ( var i = 0, l = results.length; i < l; i++ ) { + if ( results[i].getAttribute("name") === match[1] ) { + ret.push( results[i] ); + } + } + + return ret.length === 0 ? null : ret; + } + }, + + TAG: function( match, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( match[1] ); + } + } + }, + preFilter: { + CLASS: function( match, curLoop, inplace, result, not, isXML ) { + match = " " + match[1].replace( rBackslash, "" ) + " "; + + if ( isXML ) { + return match; + } + + for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { + if ( elem ) { + if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) { + if ( !inplace ) { + result.push( elem ); + } + + } else if ( inplace ) { + curLoop[i] = false; + } + } + } + + return false; + }, + + ID: function( match ) { + return match[1].replace( rBackslash, "" ); + }, + + TAG: function( match, curLoop ) { + return match[1].replace( rBackslash, "" ).toLowerCase(); + }, + + CHILD: function( match ) { + if ( match[1] === "nth" ) { + if ( !match[2] ) { + Sizzle.error( match[0] ); + } + + match[2] = match[2].replace(/^\+|\s*/g, ''); + + // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' + var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec( + match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" || + !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); + + // calculate the numbers (first)n+(last) including if they are negative + match[2] = (test[1] + (test[2] || 1)) - 0; + match[3] = test[3] - 0; + } + else if ( match[2] ) { + Sizzle.error( match[0] ); + } + + // TODO: Move to normal caching system + match[0] = done++; + + return match; + }, + + ATTR: function( match, curLoop, inplace, result, not, isXML ) { + var name = match[1] = match[1].replace( rBackslash, "" ); + + if ( !isXML && Expr.attrMap[name] ) { + match[1] = Expr.attrMap[name]; + } + + // Handle if an un-quoted value was used + match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" ); + + if ( match[2] === "~=" ) { + match[4] = " " + match[4] + " "; + } + + return match; + }, + + PSEUDO: function( match, curLoop, inplace, result, not ) { + if ( match[1] === "not" ) { + // If we're dealing with a complex expression, or a simple one + if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { + match[3] = Sizzle(match[3], null, null, curLoop); + + } else { + var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); + + if ( !inplace ) { + result.push.apply( result, ret ); + } + + return false; + } + + } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { + return true; + } + + return match; + }, + + POS: function( match ) { + match.unshift( true ); + + return match; + } + }, + + filters: { + enabled: function( elem ) { + return elem.disabled === false && elem.type !== "hidden"; + }, + + disabled: function( elem ) { + return elem.disabled === true; + }, + + checked: function( elem ) { + return elem.checked === true; + }, + + selected: function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + parent: function( elem ) { + return !!elem.firstChild; + }, + + empty: function( elem ) { + return !elem.firstChild; + }, + + has: function( elem, i, match ) { + return !!Sizzle( match[3], elem ).length; + }, + + header: function( elem ) { + return (/h\d/i).test( elem.nodeName ); + }, + + text: function( elem ) { + var attr = elem.getAttribute( "type" ), type = elem.type; + // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) + // use getAttribute instead to test this case + return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null ); + }, + + radio: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type; + }, + + checkbox: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type; + }, + + file: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "file" === elem.type; + }, + + password: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "password" === elem.type; + }, + + submit: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && "submit" === elem.type; + }, + + image: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "image" === elem.type; + }, + + reset: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && "reset" === elem.type; + }, + + button: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && "button" === elem.type || name === "button"; + }, + + input: function( elem ) { + return (/input|select|textarea|button/i).test( elem.nodeName ); + }, + + focus: function( elem ) { + return elem === elem.ownerDocument.activeElement; + } + }, + setFilters: { + first: function( elem, i ) { + return i === 0; + }, + + last: function( elem, i, match, array ) { + return i === array.length - 1; + }, + + even: function( elem, i ) { + return i % 2 === 0; + }, + + odd: function( elem, i ) { + return i % 2 === 1; + }, + + lt: function( elem, i, match ) { + return i < match[3] - 0; + }, + + gt: function( elem, i, match ) { + return i > match[3] - 0; + }, + + nth: function( elem, i, match ) { + return match[3] - 0 === i; + }, + + eq: function( elem, i, match ) { + return match[3] - 0 === i; + } + }, + filter: { + PSEUDO: function( elem, match, i, array ) { + var name = match[1], + filter = Expr.filters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + + } else if ( name === "contains" ) { + return (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0; + + } else if ( name === "not" ) { + var not = match[3]; + + for ( var j = 0, l = not.length; j < l; j++ ) { + if ( not[j] === elem ) { + return false; + } + } + + return true; + + } else { + Sizzle.error( name ); + } + }, + + CHILD: function( elem, match ) { + var type = match[1], + node = elem; + + switch ( type ) { + case "only": + case "first": + while ( (node = node.previousSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + if ( type === "first" ) { + return true; + } + + node = elem; + + case "last": + while ( (node = node.nextSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + return true; + + case "nth": + var first = match[2], + last = match[3]; + + if ( first === 1 && last === 0 ) { + return true; + } + + var doneName = match[0], + parent = elem.parentNode; + + if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) { + var count = 0; + + for ( node = parent.firstChild; node; node = node.nextSibling ) { + if ( node.nodeType === 1 ) { + node.nodeIndex = ++count; + } + } + + parent.sizcache = doneName; + } + + var diff = elem.nodeIndex - last; + + if ( first === 0 ) { + return diff === 0; + + } else { + return ( diff % first === 0 && diff / first >= 0 ); + } + } + }, + + ID: function( elem, match ) { + return elem.nodeType === 1 && elem.getAttribute("id") === match; + }, + + TAG: function( elem, match ) { + return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match; + }, + + CLASS: function( elem, match ) { + return (" " + (elem.className || elem.getAttribute("class")) + " ") + .indexOf( match ) > -1; + }, + + ATTR: function( elem, match ) { + var name = match[1], + result = Expr.attrHandle[ name ] ? + Expr.attrHandle[ name ]( elem ) : + elem[ name ] != null ? + elem[ name ] : + elem.getAttribute( name ), + value = result + "", + type = match[2], + check = match[4]; + + return result == null ? + type === "!=" : + type === "=" ? + value === check : + type === "*=" ? + value.indexOf(check) >= 0 : + type === "~=" ? + (" " + value + " ").indexOf(check) >= 0 : + !check ? + value && result !== false : + type === "!=" ? + value !== check : + type === "^=" ? + value.indexOf(check) === 0 : + type === "$=" ? + value.substr(value.length - check.length) === check : + type === "|=" ? + value === check || value.substr(0, check.length + 1) === check + "-" : + false; + }, + + POS: function( elem, match, i, array ) { + var name = match[2], + filter = Expr.setFilters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + } + } + } +}; + +var origPOS = Expr.match.POS, + fescape = function(all, num){ + return "\\" + (num - 0 + 1); + }; + +for ( var type in Expr.match ) { + Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) ); + Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) ); +} + +var makeArray = function( array, results ) { + array = Array.prototype.slice.call( array, 0 ); + + if ( results ) { + results.push.apply( results, array ); + return results; + } + + return array; +}; + +// Perform a simple check to determine if the browser is capable of +// converting a NodeList to an array using builtin methods. +// Also verifies that the returned array holds DOM nodes +// (which is not the case in the Blackberry browser) +try { + Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType; + +// Provide a fallback method if it does not work +} catch( e ) { + makeArray = function( array, results ) { + var i = 0, + ret = results || []; + + if ( toString.call(array) === "[object Array]" ) { + Array.prototype.push.apply( ret, array ); + + } else { + if ( typeof array.length === "number" ) { + for ( var l = array.length; i < l; i++ ) { + ret.push( array[i] ); + } + + } else { + for ( ; array[i]; i++ ) { + ret.push( array[i] ); + } + } + } + + return ret; + }; +} + +var sortOrder, siblingCheck; + +if ( document.documentElement.compareDocumentPosition ) { + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { + return a.compareDocumentPosition ? -1 : 1; + } + + return a.compareDocumentPosition(b) & 4 ? -1 : 1; + }; + +} else { + sortOrder = function( a, b ) { + // The nodes are identical, we can exit early + if ( a === b ) { + hasDuplicate = true; + return 0; + + // Fallback to using sourceIndex (in IE) if it's available on both nodes + } else if ( a.sourceIndex && b.sourceIndex ) { + return a.sourceIndex - b.sourceIndex; + } + + var al, bl, + ap = [], + bp = [], + aup = a.parentNode, + bup = b.parentNode, + cur = aup; + + // If the nodes are siblings (or identical) we can do a quick check + if ( aup === bup ) { + return siblingCheck( a, b ); + + // If no parents were found then the nodes are disconnected + } else if ( !aup ) { + return -1; + + } else if ( !bup ) { + return 1; + } + + // Otherwise they're somewhere else in the tree so we need + // to build up a full list of the parentNodes for comparison + while ( cur ) { + ap.unshift( cur ); + cur = cur.parentNode; + } + + cur = bup; + + while ( cur ) { + bp.unshift( cur ); + cur = cur.parentNode; + } + + al = ap.length; + bl = bp.length; + + // Start walking down the tree looking for a discrepancy + for ( var i = 0; i < al && i < bl; i++ ) { + if ( ap[i] !== bp[i] ) { + return siblingCheck( ap[i], bp[i] ); + } + } + + // We ended someplace up the tree so do a sibling check + return i === al ? + siblingCheck( a, bp[i], -1 ) : + siblingCheck( ap[i], b, 1 ); + }; + + siblingCheck = function( a, b, ret ) { + if ( a === b ) { + return ret; + } + + var cur = a.nextSibling; + + while ( cur ) { + if ( cur === b ) { + return -1; + } + + cur = cur.nextSibling; + } + + return 1; + }; +} + +// Utility function for retreiving the text value of an array of DOM nodes +Sizzle.getText = function( elems ) { + var ret = "", elem; + + for ( var i = 0; elems[i]; i++ ) { + elem = elems[i]; + + // Get the text from text nodes and CDATA nodes + if ( elem.nodeType === 3 || elem.nodeType === 4 ) { + ret += elem.nodeValue; + + // Traverse everything else, except comment nodes + } else if ( elem.nodeType !== 8 ) { + ret += Sizzle.getText( elem.childNodes ); + } + } + + return ret; +}; + +// Check to see if the browser returns elements by name when +// querying by getElementById (and provide a workaround) +(function(){ + // We're going to inject a fake input element with a specified name + var form = document.createElement("div"), + id = "script" + (new Date()).getTime(), + root = document.documentElement; + + form.innerHTML = ""; + + // Inject it into the root element, check its status, and remove it quickly + root.insertBefore( form, root.firstChild ); + + // The workaround has to do additional checks after a getElementById + // Which slows things down for other browsers (hence the branching) + if ( document.getElementById( id ) ) { + Expr.find.ID = function( match, context, isXML ) { + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + + return m ? + m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? + [m] : + undefined : + []; + } + }; + + Expr.filter.ID = function( elem, match ) { + var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); + + return elem.nodeType === 1 && node && node.nodeValue === match; + }; + } + + root.removeChild( form ); + + // release memory in IE + root = form = null; +})(); + +(function(){ + // Check to see if the browser returns only elements + // when doing getElementsByTagName("*") + + // Create a fake element + var div = document.createElement("div"); + div.appendChild( document.createComment("") ); + + // Make sure no comments are found + if ( div.getElementsByTagName("*").length > 0 ) { + Expr.find.TAG = function( match, context ) { + var results = context.getElementsByTagName( match[1] ); + + // Filter out possible comments + if ( match[1] === "*" ) { + var tmp = []; + + for ( var i = 0; results[i]; i++ ) { + if ( results[i].nodeType === 1 ) { + tmp.push( results[i] ); + } + } + + results = tmp; + } + + return results; + }; + } + + // Check to see if an attribute returns normalized href attributes + div.innerHTML = ""; + + if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && + div.firstChild.getAttribute("href") !== "#" ) { + + Expr.attrHandle.href = function( elem ) { + return elem.getAttribute( "href", 2 ); + }; + } + + // release memory in IE + div = null; +})(); + +if ( document.querySelectorAll ) { + (function(){ + var oldSizzle = Sizzle, + div = document.createElement("div"), + id = "__sizzle__"; + + div.innerHTML = "

"; + + // Safari can't handle uppercase or unicode characters when + // in quirks mode. + if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { + return; + } + + Sizzle = function( query, context, extra, seed ) { + context = context || document; + + // Only use querySelectorAll on non-XML documents + // (ID selectors don't work in non-HTML documents) + if ( !seed && !Sizzle.isXML(context) ) { + // See if we find a selector to speed up + var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query ); + + if ( match && (context.nodeType === 1 || context.nodeType === 9) ) { + // Speed-up: Sizzle("TAG") + if ( match[1] ) { + return makeArray( context.getElementsByTagName( query ), extra ); + + // Speed-up: Sizzle(".CLASS") + } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) { + return makeArray( context.getElementsByClassName( match[2] ), extra ); + } + } + + if ( context.nodeType === 9 ) { + // Speed-up: Sizzle("body") + // The body element only exists once, optimize finding it + if ( query === "body" && context.body ) { + return makeArray( [ context.body ], extra ); + + // Speed-up: Sizzle("#ID") + } else if ( match && match[3] ) { + var elem = context.getElementById( match[3] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id === match[3] ) { + return makeArray( [ elem ], extra ); + } + + } else { + return makeArray( [], extra ); + } + } + + try { + return makeArray( context.querySelectorAll(query), extra ); + } catch(qsaError) {} + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + var oldContext = context, + old = context.getAttribute( "id" ), + nid = old || id, + hasParent = context.parentNode, + relativeHierarchySelector = /^\s*[+~]/.test( query ); + + if ( !old ) { + context.setAttribute( "id", nid ); + } else { + nid = nid.replace( /'/g, "\\$&" ); + } + if ( relativeHierarchySelector && hasParent ) { + context = context.parentNode; + } + + try { + if ( !relativeHierarchySelector || hasParent ) { + return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra ); + } + + } catch(pseudoError) { + } finally { + if ( !old ) { + oldContext.removeAttribute( "id" ); + } + } + } + } + + return oldSizzle(query, context, extra, seed); + }; + + for ( var prop in oldSizzle ) { + Sizzle[ prop ] = oldSizzle[ prop ]; + } + + // release memory in IE + div = null; + })(); +} + +(function(){ + var html = document.documentElement, + matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector; + + if ( matches ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9 fails this) + var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ), + pseudoWorks = false; + + try { + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( document.documentElement, "[test!='']:sizzle" ); + + } catch( pseudoError ) { + pseudoWorks = true; + } + + Sizzle.matchesSelector = function( node, expr ) { + // Make sure that attribute selectors are quoted + expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']"); + + if ( !Sizzle.isXML( node ) ) { + try { + if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) { + var ret = matches.call( node, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || !disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9, so check for that + node.document && node.document.nodeType !== 11 ) { + return ret; + } + } + } catch(e) {} + } + + return Sizzle(expr, null, null, [node]).length > 0; + }; + } +})(); + +(function(){ + var div = document.createElement("div"); + + div.innerHTML = "
"; + + // Opera can't find a second classname (in 9.6) + // Also, make sure that getElementsByClassName actually exists + if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) { + return; + } + + // Safari caches class attributes, doesn't catch changes (in 3.2) + div.lastChild.className = "e"; + + if ( div.getElementsByClassName("e").length === 1 ) { + return; + } + + Expr.order.splice(1, 0, "CLASS"); + Expr.find.CLASS = function( match, context, isXML ) { + if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { + return context.getElementsByClassName(match[1]); + } + }; + + // release memory in IE + div = null; +})(); + +function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + + if ( elem ) { + var match = false; + + elem = elem[dir]; + + while ( elem ) { + if ( elem.sizcache === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 && !isXML ){ + elem.sizcache = doneName; + elem.sizset = i; + } + + if ( elem.nodeName.toLowerCase() === cur ) { + match = elem; + break; + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + + if ( elem ) { + var match = false; + + elem = elem[dir]; + + while ( elem ) { + if ( elem.sizcache === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 ) { + if ( !isXML ) { + elem.sizcache = doneName; + elem.sizset = i; + } + + if ( typeof cur !== "string" ) { + if ( elem === cur ) { + match = true; + break; + } + + } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { + match = elem; + break; + } + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +if ( document.documentElement.contains ) { + Sizzle.contains = function( a, b ) { + return a !== b && (a.contains ? a.contains(b) : true); + }; + +} else if ( document.documentElement.compareDocumentPosition ) { + Sizzle.contains = function( a, b ) { + return !!(a.compareDocumentPosition(b) & 16); + }; + +} else { + Sizzle.contains = function() { + return false; + }; +} + +Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; + + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +var posProcess = function( selector, context ) { + var match, + tmpSet = [], + later = "", + root = context.nodeType ? [context] : context; + + // Position selectors must be done after the filter + // And so must :not(positional) so we move all PSEUDOs to the end + while ( (match = Expr.match.PSEUDO.exec( selector )) ) { + later += match[0]; + selector = selector.replace( Expr.match.PSEUDO, "" ); + } + + selector = Expr.relative[selector] ? selector + "*" : selector; + + for ( var i = 0, l = root.length; i < l; i++ ) { + Sizzle( selector, root[i], tmpSet ); + } + + return Sizzle.filter( later, tmpSet ); +}; + +// EXPOSE +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.filters; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + +})(); + + +var runtil = /Until$/, + rparentsprev = /^(?:parents|prevUntil|prevAll)/, + // Note: This RegExp should be improved, or likely pulled from Sizzle + rmultiselector = /,/, + isSimple = /^.[^:#\[\.,]*$/, + slice = Array.prototype.slice, + POS = jQuery.expr.match.POS, + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend({ + find: function( selector ) { + var self = this, + i, l; + + if ( typeof selector !== "string" ) { + return jQuery( selector ).filter(function() { + for ( i = 0, l = self.length; i < l; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + }); + } + + var ret = this.pushStack( "", "find", selector ), + length, n, r; + + for ( i = 0, l = this.length; i < l; i++ ) { + length = ret.length; + jQuery.find( selector, this[i], ret ); + + if ( i > 0 ) { + // Make sure that the results are unique + for ( n = length; n < ret.length; n++ ) { + for ( r = 0; r < length; r++ ) { + if ( ret[r] === ret[n] ) { + ret.splice(n--, 1); + break; + } + } + } + } + } + + return ret; + }, + + has: function( target ) { + var targets = jQuery( target ); + return this.filter(function() { + for ( var i = 0, l = targets.length; i < l; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + not: function( selector ) { + return this.pushStack( winnow(this, selector, false), "not", selector); + }, + + filter: function( selector ) { + return this.pushStack( winnow(this, selector, true), "filter", selector ); + }, + + is: function( selector ) { + return !!selector && ( typeof selector === "string" ? + jQuery.filter( selector, this ).length > 0 : + this.filter( selector ).length > 0 ); + }, + + closest: function( selectors, context ) { + var ret = [], i, l, cur = this[0]; + + // Array + if ( jQuery.isArray( selectors ) ) { + var match, selector, + matches = {}, + level = 1; + + if ( cur && selectors.length ) { + for ( i = 0, l = selectors.length; i < l; i++ ) { + selector = selectors[i]; + + if ( !matches[ selector ] ) { + matches[ selector ] = POS.test( selector ) ? + jQuery( selector, context || this.context ) : + selector; + } + } + + while ( cur && cur.ownerDocument && cur !== context ) { + for ( selector in matches ) { + match = matches[ selector ]; + + if ( match.jquery ? match.index( cur ) > -1 : jQuery( cur ).is( match ) ) { + ret.push({ selector: selector, elem: cur, level: level }); + } + } + + cur = cur.parentNode; + level++; + } + } + + return ret; + } + + // String + var pos = POS.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( i = 0, l = this.length; i < l; i++ ) { + cur = this[i]; + + while ( cur ) { + if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) { + ret.push( cur ); + break; + + } else { + cur = cur.parentNode; + if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) { + break; + } + } + } + } + + ret = ret.length > 1 ? jQuery.unique( ret ) : ret; + + return this.pushStack( ret, "closest", selectors ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + if ( !elem || typeof elem === "string" ) { + return jQuery.inArray( this[0], + // If it receives a string, the selector is used + // If it receives nothing, the siblings are used + elem ? jQuery( elem ) : this.parent().children() ); + } + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[0] : elem, this ); + }, + + add: function( selector, context ) { + var set = typeof selector === "string" ? + jQuery( selector, context ) : + jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), + all = jQuery.merge( this.get(), set ); + + return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ? + all : + jQuery.unique( all ) ); + }, + + andSelf: function() { + return this.add( this.prevObject ); + } +}); + +// A painfully simple check to see if an element is disconnected +// from a document (should be improved, where feasible). +function isDisconnected( node ) { + return !node || !node.parentNode || node.parentNode.nodeType === 11; +} + +jQuery.each({ + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return jQuery.nth( elem, 2, "nextSibling" ); + }, + prev: function( elem ) { + return jQuery.nth( elem, 2, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( elem.parentNode.firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return jQuery.nodeName( elem, "iframe" ) ? + elem.contentDocument || elem.contentWindow.document : + jQuery.makeArray( elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var ret = jQuery.map( this, fn, until ), + // The variable 'args' was introduced in + // https://github.com/jquery/jquery/commit/52a0238 + // to work around a bug in Chrome 10 (Dev) and should be removed when the bug is fixed. + // http://code.google.com/p/v8/issues/detail?id=1050 + args = slice.call(arguments); + + if ( !runtil.test( name ) ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + ret = jQuery.filter( selector, ret ); + } + + ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret; + + if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) { + ret = ret.reverse(); + } + + return this.pushStack( ret, name, args.join(",") ); + }; +}); + +jQuery.extend({ + filter: function( expr, elems, not ) { + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 ? + jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] : + jQuery.find.matches(expr, elems); + }, + + dir: function( elem, dir, until ) { + var matched = [], + cur = elem[ dir ]; + + while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { + if ( cur.nodeType === 1 ) { + matched.push( cur ); + } + cur = cur[dir]; + } + return matched; + }, + + nth: function( cur, result, dir, elem ) { + result = result || 1; + var num = 0; + + for ( ; cur; cur = cur[dir] ) { + if ( cur.nodeType === 1 && ++num === result ) { + break; + } + } + + return cur; + }, + + sibling: function( n, elem ) { + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + r.push( n ); + } + } + + return r; + } +}); + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, keep ) { + + // Can't pass null or undefined to indexOf in Firefox 4 + // Set to 0 to skip string check + qualifier = qualifier || 0; + + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep(elements, function( elem, i ) { + var retVal = !!qualifier.call( elem, i, elem ); + return retVal === keep; + }); + + } else if ( qualifier.nodeType ) { + return jQuery.grep(elements, function( elem, i ) { + return (elem === qualifier) === keep; + }); + + } else if ( typeof qualifier === "string" ) { + var filtered = jQuery.grep(elements, function( elem ) { + return elem.nodeType === 1; + }); + + if ( isSimple.test( qualifier ) ) { + return jQuery.filter(qualifier, filtered, !keep); + } else { + qualifier = jQuery.filter( qualifier, filtered ); + } + } + + return jQuery.grep(elements, function( elem, i ) { + return (jQuery.inArray( elem, qualifier ) >= 0) === keep; + }); +} + + + + +var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g, + rleadingWhitespace = /^\s+/, + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig, + rtagName = /<([\w:]+)/, + rtbody = /
", "
" ], + tr: [ 2, "", "
" ], + td: [ 3, "", "
" ], + col: [ 2, "", "
" ], + area: [ 1, "", "" ], + _default: [ 0, "", "" ] + }; + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// IE can't serialize and - - - -

A

B

- + + + + +JSSpec results + + + + + +

A

B

+ diff --git a/common/js/unittest/css/JSSpec.css b/common/js/unittest/css/JSSpec.css index b03804ba9..0eac8183a 100644 --- a/common/js/unittest/css/JSSpec.css +++ b/common/js/unittest/css/JSSpec.css @@ -1,224 +1,224 @@ -@CHARSET "UTF-8"; - -/* -------------------- - * @Layout - */ - -html { - overflow: hidden; -} - -body, #jsspec_container { - overflow: hidden; - padding: 0; - margin: 0; - width: 100%; - height: 100%; - background-color: white; -} - -#title { - padding: 0; - margin: 0; - position: absolute; - top: 0px; - left: 0px; - width: 100%; - height: 40px; - overflow: hidden; -} - -#list { - padding: 0; - margin: 0; - position: absolute; - top: 40px; - left: 0px; - bottom: 0px; - overflow: auto; - width: 250px; - _height:expression(document.body.clientHeight-40); -} - -#log { - padding: 0; - margin: 0; - position: absolute; - top: 40px; - left: 250px; - right: 0px; - bottom: 0px; - overflow: auto; - _height:expression(document.body.clientHeight-40); - _width:expression(document.body.clientWidth-250); -} - - - -/* -------------------- - * @Decorations and colors - */ -* { - padding: 0; - margin: 0; - font-family: "Lucida Grande", Helvetica, sans-serif; -} - -li { - list-style: none; -} - -/* hiding subtitles */ -h2 { - display: none; -} - -/* title section */ -div#title { - padding: 0em 0.5em; -} - -div#title h1 { - font-size: 1.5em; - float: left; -} - -div#title ul li { - float: left; - padding: 0.5em 0em 0.5em 0.75em; -} - -div#title p { - float:right; - margin-right:1em; - font-size: 0.75em; -} - -/* spec container */ -ul.specs { - margin: 0.5em; -} -ul.specs li { - margin-bottom: 0.1em; -} - -/* spec title */ -ul.specs li h3 { - font-weight: bold; - font-size: 0.75em; - padding: 0.2em 1em; - cursor: pointer; - _cursor: hand; -} - -/* example container */ -ul.examples li { - border-style: solid; - border-width: 0px 0px 1px 5px; - margin: 0.2em 0em 0.2em 1em; -} - -/* example title */ -ul.examples li h4 { - font-weight: normal; - font-size: 0.75em; - margin-left: 1em; -} - -pre.examples-code { - margin: 0.5em 2em; - padding: 0.5em; - background: white; - border: solid 1px #CCC; -} - -/* example explaination */ -ul.examples li div { - padding: 1em 2em; - font-size: 0.75em; -} - -/* styles for ongoing, success, failure, error */ -div.success, div.success a { - color: #FFFFFF; - background-color: #65C400; -} - -ul.specs li.success h3, ul.specs li.success h3 a { - color: #FFFFFF; - background-color: #65C400; -} - -ul.examples li.success, ul.examples li.success a { - color: #3D7700; - background-color: #DBFFB4; - border-color: #65C400; -} - -div.exception, div.exception a { - color: #FFFFFF; - background-color: #C20000; -} - -ul.specs li.exception h3, ul.specs li.exception h3 a { - color: #FFFFFF; - background-color: #C20000; -} - -ul.examples li.exception, ul.examples li.exception a { - color: #C20000; - background-color: #FFFBD3; - border-color: #C20000; -} - -div.ongoing, div.ongoing a { - color: #000000; - background-color: #FFFF80; -} - -ul.specs li.ongoing h3, ul.specs li.ongoing h3 a { - color: #000000; - background-color: #FFFF80; -} - -ul.examples li.ongoing, ul.examples li.ongoing a { - color: #000000; - background-color: #FFFF80; - border-color: #DDDD00; -} - - - -/* -------------------- - * values - */ -.number_value, .string_value, .regexp_value, .boolean_value, .dom_value { - font-family: monospace; - color: blue; -} -.object_value, .array_value { - line-height: 2em; - padding: 0.1em 0.2em; - margin: 0.1em 0; -} -.date_value { - font-family: monospace; - color: olive; -} -.undefined_value, .null_value { - font-style: italic; - color: blue; -} -.dom_attr_name { -} -.dom_attr_value { - color: red; -} -.dom_path { - font-size: 0.75em; - color: gray; -} -strong { - font-weight: normal; - background-color: #FFC6C6; +@CHARSET "UTF-8"; + +/* -------------------- + * @Layout + */ + +html { + overflow: hidden; +} + +body, #jsspec_container { + overflow: hidden; + padding: 0; + margin: 0; + width: 100%; + height: 100%; + background-color: white; +} + +#title { + padding: 0; + margin: 0; + position: absolute; + top: 0px; + left: 0px; + width: 100%; + height: 40px; + overflow: hidden; +} + +#list { + padding: 0; + margin: 0; + position: absolute; + top: 40px; + left: 0px; + bottom: 0px; + overflow: auto; + width: 250px; + _height:expression(document.body.clientHeight-40); +} + +#log { + padding: 0; + margin: 0; + position: absolute; + top: 40px; + left: 250px; + right: 0px; + bottom: 0px; + overflow: auto; + _height:expression(document.body.clientHeight-40); + _width:expression(document.body.clientWidth-250); +} + + + +/* -------------------- + * @Decorations and colors + */ +* { + padding: 0; + margin: 0; + font-family: "Lucida Grande", Helvetica, sans-serif; +} + +li { + list-style: none; +} + +/* hiding subtitles */ +h2 { + display: none; +} + +/* title section */ +div#title { + padding: 0em 0.5em; +} + +div#title h1 { + font-size: 1.5em; + float: left; +} + +div#title ul li { + float: left; + padding: 0.5em 0em 0.5em 0.75em; +} + +div#title p { + float:right; + margin-right:1em; + font-size: 0.75em; +} + +/* spec container */ +ul.specs { + margin: 0.5em; +} +ul.specs li { + margin-bottom: 0.1em; +} + +/* spec title */ +ul.specs li h3 { + font-weight: bold; + font-size: 0.75em; + padding: 0.2em 1em; + cursor: pointer; + _cursor: hand; +} + +/* example container */ +ul.examples li { + border-style: solid; + border-width: 0px 0px 1px 5px; + margin: 0.2em 0em 0.2em 1em; +} + +/* example title */ +ul.examples li h4 { + font-weight: normal; + font-size: 0.75em; + margin-left: 1em; +} + +pre.examples-code { + margin: 0.5em 2em; + padding: 0.5em; + background: white; + border: solid 1px #CCC; +} + +/* example explaination */ +ul.examples li div { + padding: 1em 2em; + font-size: 0.75em; +} + +/* styles for ongoing, success, failure, error */ +div.success, div.success a { + color: #FFFFFF; + background-color: #65C400; +} + +ul.specs li.success h3, ul.specs li.success h3 a { + color: #FFFFFF; + background-color: #65C400; +} + +ul.examples li.success, ul.examples li.success a { + color: #3D7700; + background-color: #DBFFB4; + border-color: #65C400; +} + +div.exception, div.exception a { + color: #FFFFFF; + background-color: #C20000; +} + +ul.specs li.exception h3, ul.specs li.exception h3 a { + color: #FFFFFF; + background-color: #C20000; +} + +ul.examples li.exception, ul.examples li.exception a { + color: #C20000; + background-color: #FFFBD3; + border-color: #C20000; +} + +div.ongoing, div.ongoing a { + color: #000000; + background-color: #FFFF80; +} + +ul.specs li.ongoing h3, ul.specs li.ongoing h3 a { + color: #000000; + background-color: #FFFF80; +} + +ul.examples li.ongoing, ul.examples li.ongoing a { + color: #000000; + background-color: #FFFF80; + border-color: #DDDD00; +} + + + +/* -------------------- + * values + */ +.number_value, .string_value, .regexp_value, .boolean_value, .dom_value { + font-family: monospace; + color: blue; +} +.object_value, .array_value { + line-height: 2em; + padding: 0.1em 0.2em; + margin: 0.1em 0; +} +.date_value { + font-family: monospace; + color: olive; +} +.undefined_value, .null_value { + font-style: italic; + color: blue; +} +.dom_attr_name { +} +.dom_attr_value { + color: red; +} +.dom_path { + font-size: 0.75em; + color: gray; +} +strong { + font-weight: normal; + background-color: #FFC6C6; } \ No newline at end of file diff --git a/common/js/unittest/unittest_common.html b/common/js/unittest/unittest_common.html index c2b27eb69..f073a9c8f 100644 --- a/common/js/unittest/unittest_common.html +++ b/common/js/unittest/unittest_common.html @@ -1,74 +1,74 @@ - - - - -JSSpec results - - - - - - - -

A

B

- + + + + +JSSpec results + + + + + + + +

A

B

+ diff --git a/common/js/x.js b/common/js/x.js index 95d2ee04c..71fed6d3e 100644 --- a/common/js/x.js +++ b/common/js/x.js @@ -3,172 +3,674 @@ * Distributed by GNU LGPL. For copyrights, license, documentation and more visit Cross-Browser.com * Copyright 2001-2005 Michael Foster (Cross-Browser.com) **/ -function xDeprecate(funcName){var msg='DEPRECATED : '+funcName+'() is deprecated function.';if(typeof console=='object'&&typeof console.log=='function'){console.log(msg);}} -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;} +function xDeprecate(funcName) { + var msg = 'DEPRECATED : '+funcName+'() is deprecated function.'; + if (typeof console == 'object' && typeof console.log == 'function') { + console.log(msg); + } +} + +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;var xFF=xUA.indexOf('firefox')!=-1;function xAddEventListener(e,eT,eL,cap) -{xDeprecate('xAddEventListener');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);} +var xMac=xUA.indexOf('mac')!=-1; +var xFF=xUA.indexOf('firefox')!=-1; + +// (element, event(without 'on'), event listener(function name)[, caption]) +function xAddEventListener(e,eT,eL,cap) +{ + xDeprecate('xAddEventListener'); + 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() -{xDeprecate('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();}} +{ + xDeprecate('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() -{xDeprecate('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) -{xDeprecate('xAppendChild');if(oParent.appendChild)return oParent.appendChild(oChild);else return null;} +{ + xDeprecate('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) +{ + xDeprecate('xAppendChild'); + if (oParent.appendChild) return oParent.appendChild(oChild); + else return null; +} + function xClientHeight() -{xDeprecate('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;} +{ + xDeprecate('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() -{xDeprecate('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;} +{ + xDeprecate('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) -{xDeprecate('xCreateElement');if(document.createElement)return document.createElement(sTag);else return null;} +{ + xDeprecate('xCreateElement'); + if (document.createElement) return document.createElement(sTag); + else return null; +} + function xDef() -{xDeprecate('xDef');for(var i=0;i0){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;} +{ + xDeprecate('xGetCookie'); + 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) -{xDeprecate('xGetElementById');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) -{xDeprecate('xGetElementsByAttribute');var a,list,found=new Array(),re=new RegExp(sRE,'i');list=xGetElementsByTagName(sTag);for(var i=0;i=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);} -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) -{xDeprecate('xHex');var p='';var n=Math.ceil(sn);if(prefix)p=prefix;n=n.toString(16);for(var i=0;i=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) +{ + xDeprecate('xHex'); + 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){ xDeprecate('xHide'); return xVisibility(e,0);} + function xInnerHtml(e,h) -{xDeprecate('xInnerHtml');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) -{xDeprecate('xLeft');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;} +{ + xDeprecate('xInnerHtml'); + 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) +{ + xDeprecate('xLeft'); + 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) -{xDeprecate('xMoveTo');xLeft(e,x);xTop(e,y);} +{ + xDeprecate('xMoveTo'); + xLeft(e,x); + xTop(e,y); +} + function xName(e) -{xDeprecate('xName');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;} +{ + xDeprecate('xName'); + 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) -{xDeprecate('xNextSib');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;} +{ + xDeprecate('xNextSib'); + 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() -{xDeprecate('xNum');for(var i=0;i=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);} -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;} +{ + xDeprecate('xWidth'); + 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) -{xDeprecate('xZIndex');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;} +{ + xDeprecate('xZIndex'); + 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) -{xDeprecate('xStopPropagation');if(evt&&evt.stopPropagation)evt.stopPropagation();else if(window.event)window.event.cancelBubble=true;} \ No newline at end of file +{ + xDeprecate('xStopPropagation'); + if (evt && evt.stopPropagation) evt.stopPropagation(); + else if (window.event) window.event.cancelBubble = true; +} + diff --git a/common/js/x.min.js b/common/js/x.min.js new file mode 100644 index 000000000..09d7283e3 --- /dev/null +++ b/common/js/x.min.js @@ -0,0 +1,38 @@ +/** + * 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) + **/ +function xDeprecate(a){typeof console=="object"&&typeof console.log=="function"&&console.log("DEPRECATED : "+a+"() is deprecated function.")}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 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):document.layers&&(xNN4=true);var xMac=xUA.indexOf("mac")!=-1,xFF=xUA.indexOf("firefox")!=-1; +function xAddEventListener(a,b,d,e){xDeprecate("xAddEventListener");if(a=xGetElementById(a)){b=b.toLowerCase();if(!xIE4Up&&!xOp7Up&&a==window){if(b=="resize"){window.xPCW=xClientWidth();window.xPCH=xClientHeight();window.xREL=d;xResizeEvent();return}if(b=="scroll"){window.xPSL=xScrollLeft();window.xPST=xScrollTop();window.xSEL=d;xScrollEvent();return}}var f="e.on"+b+"=eL";a.addEventListener?a.addEventListener(b,d,e):a.attachEvent?a.attachEvent("on"+b,d):eval(f)}} +function xResizeEvent(){xDeprecate("xResizeEvent");window.xREL&&setTimeout("xResizeEvent()",250);var a=xClientWidth(),b=xClientHeight();if(window.xPCW!=a||window.xPCH!=b)window.xPCW=a,window.xPCH=b,window.xREL&&window.xREL()}function xScrollEvent(){xDeprecate("xScrollEvent");window.xSEL&&setTimeout("xScrollEvent()",250);var a=xScrollLeft(),b=xScrollTop();if(window.xPSL!=a||window.xPST!=b)window.xPSL=a,window.xPST=b,window.xSEL&&window.xSEL()} +function xAppendChild(a,b){xDeprecate("xAppendChild");return a.appendChild?a.appendChild(b):null} +function xClientHeight(){xDeprecate("xClientHeight");var a=0;if(xOp6Dn)a=window.innerHeight;else if(document.compatMode=="CSS1Compat"&&!window.opera&&document.documentElement&&document.documentElement.clientHeight)a=document.documentElement.clientHeight;else if(document.body&&document.body.clientHeight)a=document.body.clientHeight;else if(xDef(window.innerWidth,window.innerHeight,document.width))a=window.innerHeight,document.width>window.innerWidth&&(a-=16);return a} +function xClientWidth(){xDeprecate("xClientWidth");var a=0;if(xOp6Dn)a=window.innerWidth;else if(document.compatMode=="CSS1Compat"&&!window.opera&&document.documentElement&&document.documentElement.clientWidth)a=document.documentElement.clientWidth;else if(document.body&&document.body.clientWidth)a=document.body.clientWidth;else if(xDef(window.innerWidth,window.innerHeight,document.height))a=window.innerWidth,document.height>window.innerHeight&&(a-=16);return a} +function xCreateElement(a){xDeprecate("xCreateElement");return document.createElement?document.createElement(a):null}function xDef(){xDeprecate("xDef");for(var a=0;ab?a:b}function xGetBodyHeight(){xDeprecate("xGetBodyHeight");var a=xClientHeight(),b=window.document.body.scrollHeight;return a>b?a:b} +function xGetComputedStyle(a,b,d){xDeprecate("xGetComputedStyle");var e="undefined",f=document.defaultView;if(f&&f.getComputedStyle)(a=f.getComputedStyle(a,""))&&(e=a.getPropertyValue(b));else if(a.currentStyle){e=b.split("-");b=e[0];for(f=1;f0&&(a=document.cookie.indexOf(d),a!=-1)){a+=d.length;b=document.cookie.indexOf(";",a);if(b==-1)b=document.cookie.length;b=unescape(document.cookie.substring(a,b))}return b}function xGetElementById(a){xDeprecate("xGetElementById");return typeof a!="string"?a:a=document.getElementById?document.getElementById(a):document.all?document.all[a]:null} +function xGetElementsByAttribute(a,b,d,e){xDeprecate("xGetElementsByAttribute");var f,h=[],d=RegExp(d,"i");f=xGetElementsByTagName(a);for(var g=0;g=0){var e=d=0,f=0,h=0;if(document.compatMode=="CSS1Compat"){var g=xGetComputedStyle,d=g(a,"padding-top",1);if(d!==null)e=g(a,"padding-bottom",1),f=g(a,"border-top-width",1),h=g(a,"border-bottom-width",1);else if(xDef(a.offsetHeight, +a.style.height))a.style.height=b+"px",d=a.offsetHeight-b}b-=d+e+f+h;if(isNaN(b)||b<0)return null;else a.style.height=b+"px"}b=a.offsetHeight}else if(d&&xDef(a.style.pixelHeight)){if(b>=0)a.style.pixelHeight=b;b=a.style.pixelHeight}return b}function xHex(a,b,d){xDeprecate("xHex");var e="",a=Math.ceil(a);d&&(e=d);a=a.toString(16);for(d=0;d=0){var e=d=0,f=0,h=0;if(document.compatMode=="CSS1Compat"){var g=xGetComputedStyle,d=g(a,"padding-left",1);if(d!==null)e=g(a,"padding-right",1),f=g(a,"border-left-width",1),h=g(a,"border-right-width",1);else if(xDef(a.offsetWidth, +a.style.width))a.style.width=b+"px",d=a.offsetWidth-b}b-=d+e+f+h;if(isNaN(b)||b<0)return null;else a.style.width=b+"px"}b=a.offsetWidth}else if(d&&xDef(a.style.pixelWidth)){if(b>=0)a.style.pixelWidth=b;b=a.style.pixelWidth}return b}function xZIndex(a,b){xDeprecate("xZIndex");if(!(a=xGetElementById(a)))return 0;if(a.style&&xDef(a.style.zIndex)){if(xNum(b))a.style.zIndex=b;b=parseInt(a.style.zIndex)}return b} +function xStopPropagation(a){xDeprecate("xStopPropagation");if(a&&a.stopPropagation)a.stopPropagation();else if(window.event)window.event.cancelBubble=true}; diff --git a/common/js/xe.header.js b/common/js/xe.header.js new file mode 100644 index 000000000..9a29487a0 --- /dev/null +++ b/common/js/xe.header.js @@ -0,0 +1,5 @@ +/** + * @file common/js/xe.min.js + * @author NHN (developers@xpressengine.com) + * @brief XE Common JavaScript + **/ diff --git a/common/js/xe.js b/common/js/xe.js new file mode 100644 index 000000000..75fb3e6ec --- /dev/null +++ b/common/js/xe.js @@ -0,0 +1,2088 @@ +/** + * @file common/js/xe.min.js + * @author NHN (developers@xpressengine.com) + * @brief XE Common JavaScript + **/ +/** + * @file js_app.js + * @author NHN (developers@xpressengine.com) + * @brief XE JavaScript Application Framework (JAF) + * @namespace xe + * @update 20100701 + */ +(function($){ + +var _xe_base, _app_base, _plugin_base; +var _apps = []; + +_xe_base = { + /** + * @brief return the name of Core module + */ + getName : function() { + return 'Core'; + }, + + /** + * @brief Create an application class + */ + createApp : function(sName, oDef) { + var _base = getTypeBase(); + + $.extend(_base.prototype, _app_base, oDef); + + _base.prototype.getName = function() { + return sName; + }; + + return _base; + }, + + /** + * @brief Create a plugin class + */ + createPlugin : function(sName, oDef) { + var _base = getTypeBase(); + + $.extend(_base.prototype, _plugin_base, oDef); + + _base.prototype.getName = function() { + return sName; + }; + + return _base; + }, + + /** + * @brief Get the array of applications + */ + getApps : function() { + return $.makeArray(_apps); + }, + + /** + * @brief Get one application + */ + getApp : function(indexOrName) { + indexOrName = (indexOrName||'').toLowerCase(); + if(typeof _apps[indexOrName] != 'undefined') { + return _apps[indexOrName]; + } else { + return null; + } + }, + + /** + * @brief Register an application instance + */ + registerApp : function(oApp) { + var sName = oApp.getName().toLowerCase(); + + _apps.push(oApp); + if (!$.isArray(_apps[sName])) { + _apps[sName] = []; + } + _apps[sName].push(oApp); + + oApp.parent = this; + + // register event + if ($.isFunction(oApp.activate)) oApp.activate(); + }, + + /** + * @brief Unregister an application instance + */ + unregisterApp : function(oApp) { + var sName = oApp.getName().toLowerCase(); + var nIndex = $.inArray(oApp, _apps); + + if (nIndex >= 0) _apps = _apps.splice(nIndex, 1); + + if ($.isArray(_apps[sName])) { + nIndex = $.inArray(oApp, _apps[sName]); + if (nIndex >= 0) _apps[sName] = _apps[sName].splice(nIndex, 1); + } + + // unregister event + if ($.isFunction(oApp.deactivate)) oApp.deactivate(); + }, + + /** + * @brief overrides broadcast method + */ + broadcast : function(msg, params) { + this._broadcast(this, msg, params); + }, + + _broadcast : function(sender, msg, params) { + for(var i=0; i < _apps.length; i++) { + _apps[i]._cast(sender, msg, params); + } + + + // cast to child plugins + this._cast(sender, msg, params); + } +} + +_app_base = { + _plugins : [], + _messages : {}, + + /** + * @brief get plugin + */ + getPlugin : function(sPluginName) { + sPluginName = sPluginName.toLowerCase(); + if ($.isArray(this._plugins[sPluginName])) { + return this._plugins[sPluginName]; + } else { + return []; + } + }, + + /** + * @brief register a plugin instance + */ + registerPlugin : function(oPlugin) { + var self = this; + var sName = oPlugin.getName().toLowerCase(); + + // check if the plugin is already registered + if ($.inArray(oPlugin, this._plugins) >= 0) return false; + + // push the plugin into the _plugins array + this._plugins.push(oPlugin); + + if (!$.isArray(this._plugins[sName])) this._plugins[sName] = []; + this._plugins[sName].push(oPlugin); + + // register method pool + $.each(oPlugin._binded_fn, function(api, fn){ self.registerHandler(api, fn); }); + + // binding + oPlugin.oApp = this; + + // registered event + if ($.isFunction(oPlugin.activate)) oPlugin.activate(); + + return true; + }, + + /** + * @brief register api message handler + */ + registerHandler : function(api, func) { + var msgs = this._messages; api = api.toUpperCase(); + if (!$.isArray(msgs[api])) msgs[api] = []; + msgs[api].push(func); + }, + + cast : function(msg, params) { + return this._cast(this, msg, params || []); + }, + + broadcast : function(sender, msg, params) { + if (this.parent && this.parent._broadcast) { + this.parent._broadcast(sender, msg, params); + } + }, + + _cast : function(sender, msg, params) { + var i, len; + var aMsg = this._messages; + + msg = msg.toUpperCase(); + + // BEFORE hooker + if (aMsg['BEFORE_'+msg] || this['API_BEFORE_'+msg]) { + var bContinue = this._cast(sender, 'BEFORE_'+msg, params); + if (!bContinue) return; + } + + // main api function + var vRet = [], sFn = 'API_'+msg; + if ($.isArray(aMsg[msg])) { + for(i=0; i < aMsg[msg].length; i++) { + vRet.push( aMsg[msg][i](sender, params) ); + } + } + if (vRet.length < 2) vRet = vRet[0]; + + // AFTER hooker + if (aMsg['AFTER_'+msg] || this['API_AFTER_'+msg]) { + this._cast(sender, 'AFTER_'+msg, params); + } + + if (!/^(?:AFTER|BEFORE)_/.test(msg)) { // top level function + return vRet; + } else { + return $.isArray(vRet)?($.inArray(false, vRet)<0):((typeof vRet=='undefined')?true:!!vRet); + } + } +}; + +_plugin_base = { + oApp : null, + + cast : function(msg, params) { + if (this.oApp && this.oApp._cast) { + return this.oApp._cast(this, msg, params || []); + } + }, + + broadcast : function(msg, params) { + if (this.oApp && this.oApp.broadcast) { + this.oApp.broadcast(this, mag, params || []); + } + } + + /** + * Event handler prototype + * + * function (oSender, params) + */ +}; + +function getTypeBase() { + var _base = function() { + var self = this; + var pool = null; + + if ($.isArray(this._plugins)) this._plugins = []; + if (this._messages) this._messages = {}; + else this._binded_fn = {}; + + // bind functions + $.each(this, function(key, val){ + if (!$.isFunction(val)) return true; + if (!/^API_([A-Z0-9_]+)$/.test(key)) return true; + + var api = RegExp.$1; + var fn = function(sender, params){ return self[key](sender, params) }; + + if (self._messages) self._messages[api] = [fn]; + else self._binded_fn[api] = fn; + }); + + if ($.isFunction(this.init)) this.init.apply(this, arguments); + }; + + return _base; +} + +window.xe = $.extend(_app_base, _xe_base); +window.xe.lang = {}; // language repository + +// domready event +$(function(){ xe.broadcast('ONREADY'); }); + +// load event +$(window).load(function(){ xe.broadcast('ONLOAD'); }); + +})(jQuery); +/** + * @file common.js + * @author NHN (developers@xpressengine.com) + * @brief 몇가지 유용한 & 기본적으로 자주 사용되는 자바스크립트 함수들 모음 + **/ + +/* jQuery 참조변수($) 제거 */ +if(jQuery) jQuery.noConflict(); + +(function($) { + /* OS check */ + var UA = navigator.userAgent.toLowerCase(); + $.os = { + Linux: /linux/.test(UA), + Unix: /x11/.test(UA), + Mac: /mac/.test(UA), + Windows: /win/.test(UA) + }; + $.os.name = ($.os.Windows) ? 'Windows' : + ($.os.Linux) ? 'Linux' : + ($.os.Unix) ? 'Unix' : + ($.os.Mac) ? 'Mac' : ''; + + /** + * @brief XE 공용 유틸리티 함수 + * @namespace XE + */ + window.XE = { + loaded_popup_menus : new Array(), + addedDocument : new Array(), + /** + * @brief 특정 name을 가진 체크박스들의 checked 속성 변경 + * @param [itemName='cart',][options={}] + */ + checkboxToggleAll : function(itemName) { + if(!is_def(itemName)) itemName='cart'; + var options = { + wrap : null, + checked : 'toggle', + doClick : false + }; + + switch(arguments.length) { + case 1: + if(typeof(arguments[0]) == "string") { + itemName = arguments[0]; + } else { + $.extend(options, arguments[0] || {}); + itemName = 'cart'; + } + break; + case 2: + itemName = arguments[0]; + $.extend(options, arguments[1] || {}); + } + + if(options.doClick == true) options.checked = null; + if(typeof(options.wrap) == "string") options.wrap ='#'+options.wrap; + + if(options.wrap) { + var obj = $(options.wrap).find('input[name='+itemName+']:checkbox'); + } else { + var obj = $('input[name='+itemName+']:checkbox'); + } + + if(options.checked == 'toggle') { + obj.each(function() { + $(this).attr('checked', ($(this).attr('checked')) ? false : true); + }); + } else { + (options.doClick == true) ? obj.click() : obj.attr('checked', options.checked); + } + }, + + /** + * @brief 문서/회원 등 팝업 메뉴 출력 + */ + displayPopupMenu : function(ret_obj, response_tags, params) { + var target_srl = params["target_srl"]; + var menu_id = params["menu_id"]; + var menus = ret_obj['menus']; + var html = ""; + + if(this.loaded_popup_menus[menu_id]) { + html = this.loaded_popup_menus[menu_id]; + + } else { + if(menus) { + var item = menus['item']; + if(typeof(item.length)=='undefined' || item.length<1) item = new Array(item); + if(item.length) { + for(var i=0;i
'+str+' '; + } + } + } + this.loaded_popup_menus[menu_id] = html; + } + + /* 레이어 출력 */ + if(html) { + var area = $('#popup_menu_area').html('
    '+html+'
'); + var areaOffset = {top:params['page_y'], left:params['page_x']}; + + if(area.outerHeight()+areaOffset.top > $(window).height()+$(window).scrollTop()) + areaOffset.top = $(window).height() - area.outerHeight() + $(window).scrollTop(); + if(area.outerWidth()+areaOffset.left > $(window).width()+$(window).scrollLeft()) + areaOffset.left = $(window).width() - area.outerWidth() + $(window).scrollLeft(); + + area.css({ top:areaOffset.top, left:areaOffset.left }).show(); + } + } + } + +}) (jQuery); + + + +/* jQuery(document).ready() */ +jQuery(function($) { + + /* select - option의 disabled=disabled 속성을 IE에서도 체크하기 위한 함수 */ + if($.browser.msie) { + $('select').each(function(i, sels) { + var disabled_exists = false; + var first_enable = new Array(); + + for(var j=0; j < sels.options.length; j++) { + if(sels.options[j].disabled) { + sels.options[j].style.color = '#CCCCCC'; + disabled_exists = true; + }else{ + first_enable[i] = (first_enable[i] > -1) ? first_enable[i] : j; + } + } + + if(!disabled_exists) return; + + sels.oldonchange = sels.onchange; + sels.onchange = function() { + if(this.options[this.selectedIndex].disabled) { + + this.selectedIndex = first_enable[i]; + /* + if(this.options.length<=1) this.selectedIndex = -1; + else if(this.selectedIndex < this.options.length - 1) this.selectedIndex++; + else this.selectedIndex--; + */ + + } else { + if(this.oldonchange) this.oldonchange(); + } + }; + + if(sels.selectedIndex >= 0 && sels.options[ sels.selectedIndex ].disabled) sels.onchange(); + + }); + } + + /* 단락에디터 fold 컴포넌트 펼치기/접기 */ + var drEditorFold = $('.xe_content .fold_button'); + if(drEditorFold.size()) { + var fold_container = $('div.fold_container', drEditorFold); + $('button.more', drEditorFold).click(function() { + $(this).hide().next('button').show().parent().next(fold_container).show(); + }); + $('button.less', drEditorFold).click(function() { + $(this).hide().prev('button').show().parent().next(fold_container).hide(); + }); + } + +}); + + +/** + * @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.replace(/#$/, ''); + + if(idx != -1) { + var query_string = uri.substr(idx+1, this.length), args = {}, q_list = []; + uri = this.substr(0, idx); + query_string.replace(/([^=]+)=([^&]*)(&|$)/g, function(all,key,val) { args[key] = val; }); + + args[key] = val; + + jQuery.each(args, function(key,val){ + if (!jQuery.trim(val)) return; + q_list.push(key+'='+decodeURI(val)); + }); + + query_string = q_list.join('&'); + uri = uri+(query_string?'?'+query_string:''); + } else { + if(val.toString().trim()) uri = uri+"?"+key+"="+val; + } + + var re = /https:\/\/([^:\/]+)(:\d+|)/i; + var check = re.exec(uri); + if(check) + { + var toReplace = "http://"+check[1]; + if(typeof(http_port)!='undefined' && http_port != 80) + { + toReplace += ":" + http_port; + } + uri = uri.replace(re,toReplace); + } + var bUseSSL = false; + if(typeof(enforce_ssl)!='undefined' && enforce_ssl) + { + bUseSSL = true; + } + else if(typeof(ssl_actions)!='undefined' && typeof(ssl_actions.length)!='undefined' && uri.getQuery('act')) { + var act = uri.getQuery('act'); + for(i=0;i-1 && !url.getQuery('vid')) url = url.setQuery('vid',xeVid); + 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"; + if(typeof(xeVid)!='undefined' && url.indexOf(request_uri)>-1 && !url.getQuery('vid')) url = url.setQuery('vid',xeVid); + winopen(url, target, "width=650,height=500,scrollbars=yes,resizable=yes,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 멀티미디어 출력용 (IE에서 플래쉬/동영상 주변에 점선 생김 방지용) + **/ +function displayMultimedia(src, width, height, options) { + var html = _displayMultimedia(src, width, height, options); + if(html) document.writeln(html); +} +function _displayMultimedia(src, width, height, options) { + if(src.indexOf('files') == 0) src = request_uri + src; + + var defaults = { + wmode : 'transparent', + allowScriptAccess : 'sameDomain', + quality : 'high', + flashvars : '', + autostart : false + }; + + var params = jQuery.extend(defaults, options || {}); + var autostart = (params.autostart && params.autostart != 'false') ? 'true' : 'false'; + delete(params.autostart); + + var clsid = ""; + var codebase = ""; + var html = ""; + + if(/\.(gif|jpg|jpeg|bmp|png)$/i.test(src)){ + html = ''; + } else if(/\.flv$/i.test(src) || /\.mov$/i.test(src) || /\.moov$/i.test(src) || /\.m4v$/i.test(src)) { + html = ''; + } else if(/\.swf/i.test(src)) { + clsid = 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000'; + + if(typeof(enforce_ssl)!='undefined' && enforce_ssl){ codebase = "https://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,28,0"; } + else { codebase = "http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,28,0"; } + html = ''; + html += ''; + for(var name in params) { + if(params[name] != 'undefined' && params[name] != '') { + html += ''; + } + } + html += '' + + '' + + ''; + } else { + if (jQuery.browser.mozilla || jQuery.browser.opera) { + // firefox and opera uses 0 or 1 for autostart parameter. + autostart = (params.autostart && params.autostart != 'false') ? '1' : '0'; + } + + html = '.popup'), w, h, dw, dh, offset; + + offset = $pc.css({overflow:'scroll'}).offset(); + + w = $pc.width(10).height(10000).get(0).scrollWidth + offset.left*2; + h = $pc.height(10).width(10000).get(0).scrollHeight + offset.top*2; + + if(w < 600) w = 600 + offset.left*2; + + dw = $win.width(); + dh = $win.height(); + + if(w != dw) window.resizeBy(w - dw, 0); + if(h != dh) window.resizeBy(0, h - dh); + + $pc.width(w-offset.left*2).css({overflow:'',height:''}); +} + +/** + * @brief 추천/비추천,스크랩,신고기능등 특정 srl에 대한 특정 module/action을 호출하는 함수 + **/ +function doCallModuleAction(module, action, target_srl) { + var params = { + target_srl : target_srl, + cur_mid : current_mid, + mid : current_mid + }; + exec_xml(module, action, params, completeCallModuleAction); +} + +function completeCallModuleAction(ret_obj, response_tags) { + if(ret_obj['message']!='success') alert(ret_obj['message']); + location.reload(); +} + +function completeMessage(ret_obj) { + alert(ret_obj['message']); + location.reload(); +} + + + +/* 언어코드 (lang_type) 쿠키값 변경 */ +function doChangeLangType(obj) { + if(typeof(obj) == "string") { + setLangType(obj); + } else { + var val = obj.options[obj.selectedIndex].value; + setLangType(val); + } + location.href = location.href.setQuery('l', ''); +} +function setLangType(lang_type) { + var expire = new Date(); + expire.setTime(expire.getTime()+ (7000 * 24 * 3600000)); + setCookie('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 editor_sequence = fo_obj.getAttribute('editor_sequence'); + + var content = editorGetContent(editor_sequence); + + var win = window.open("", "previewDocument","toolbars=no,width=700px;height=800px,scrollbars=yes,resizable=yes"); + + var dummy_obj = jQuery("#previewDocument"); + + if(!dummy_obj.length) { + jQuery( + '
'+ + ''+ + ''+ + ''+ + '
' + ).appendTo(document.body); + + dummy_obj = jQuery("#previewDocument")[0]; + } + + if(dummy_obj) { + dummy_obj.content.value = content; + dummy_obj.submit(); + } +} + +/* 게시글 저장 */ +function doDocumentSave(obj) { + var editor_sequence = obj.form.getAttribute('editor_sequence'); + var prev_content = editorRelKeys[editor_sequence]['content'].value; + if(typeof(editor_sequence)!='undefined' && editor_sequence && typeof(editorRelKeys)!='undefined' && typeof(editorGetContent)=='function') { + var content = editorGetContent(editor_sequence); + editorRelKeys[editor_sequence]['content'].value = content; + } + + var params={}, responses=['error','message','document_srl'], elms=obj.form.elements, data=jQuery(obj.form).serializeArray();; + jQuery.each(data, function(i, field){ + var val = jQuery.trim(field.value); + if(!val) return true; + if(/\[\]$/.test(field.name)) field.name = field.name.replace(/\[\]$/, ''); + if(params[field.name]) params[field.name] += '|@|'+val; + else params[field.name] = field.value; + }); + + exec_xml('document','procDocumentTempSave', params, completeDocumentSave, responses, params, obj.form); + + editorRelKeys[editor_sequence]['content'].value = prev_content; + return false; +} + +function completeDocumentSave(ret_obj) { + jQuery('input[name=document_srl]').eq(0).val(ret_obj['document_srl']); + alert(ret_obj['message']); +} + +/* 저장된 게시글 불러오기 */ +var objForSavedDoc = null; +function doDocumentLoad(obj) { + // 저장된 게시글 목록 불러오기 + objForSavedDoc = obj.form; + popopen(request_uri.setQuery('module','document').setQuery('act','dispTempSavedList')); +} + +/* 저장된 게시글의 선택 */ +function doDocumentSelect(document_srl) { + if(!opener || !opener.objForSavedDoc) { + window.close(); + return; + } + + // 게시글을 가져와서 등록하기 + opener.location.href = opener.current_url.setQuery('document_srl', document_srl).setQuery('act', 'dispBoardWrite'); + window.close(); +} + + +/* 스킨 정보 */ +function viewSkinInfo(module, skin) { + popopen("./?module=module&act=dispModuleSkinInfo&selected_module="+module+"&skin="+skin, 'SkinInfo'); +} + + +/* 관리자가 문서를 관리하기 위해서 선택시 세션에 넣음 */ +var addedDocument = new Array(); +function doAddDocumentCart(obj) { + var srl = obj.value; + addedDocument[addedDocument.length] = srl; + setTimeout(function() { callAddDocumentCart(addedDocument.length); }, 100); +} + +function callAddDocumentCart(document_length) { + if(addedDocument.length<1 || document_length != addedDocument.length) return; + var params = new Array(); + params["srls"] = addedDocument.join(","); + exec_xml("document","procDocumentAddCart", params, null); + addedDocument = new Array(); +} + +/* ff의 rgb(a,b,c)를 #... 로 변경 */ +function transRGB2Hex(value) { + if(!value) return value; + if(value.indexOf('#') > -1) return value.replace(/^#/, ''); + + if(value.toLowerCase().indexOf('rgb') < 0) return value; + value = value.replace(/^rgb\(/i, '').replace(/\)$/, ''); + value_list = value.split(','); + + var hex = ''; + for(var i = 0; i < value_list.length; i++) { + var color = parseInt(value_list[i], 10).toString(16); + if(color.length == 1) color = '0'+color; + hex += color; + } + return hex; +} + +/* 보안 로그인 모드로 전환 */ +function toggleSecuritySignIn() { + var href = location.href; + if(/https:\/\//i.test(href)) location.href = href.replace(/^https/i,'http'); + else location.href = href.replace(/^http/i,'https'); +} + +function reloadDocument() { + location.reload(); +} + + +/** +* +* Base64 encode / decode +* http://www.webtoolkit.info/ +* +**/ + +var Base64 = { + + // private property + _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", + + // public method for encoding + encode : function (input) { + var output = ""; + var chr1, chr2, chr3, enc1, enc2, enc3, enc4; + var i = 0; + + input = Base64._utf8_encode(input); + + while (i < input.length) { + + chr1 = input.charCodeAt(i++); + chr2 = input.charCodeAt(i++); + chr3 = input.charCodeAt(i++); + + enc1 = chr1 >> 2; + enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); + enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); + enc4 = chr3 & 63; + + if (isNaN(chr2)) { + enc3 = enc4 = 64; + } else if (isNaN(chr3)) { + enc4 = 64; + } + + output = output + + this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + + this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4); + + } + + return output; + }, + + // public method for decoding + decode : function (input) { + var output = ""; + var chr1, chr2, chr3; + var enc1, enc2, enc3, enc4; + var i = 0; + + input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); + + while (i < input.length) { + + enc1 = this._keyStr.indexOf(input.charAt(i++)); + enc2 = this._keyStr.indexOf(input.charAt(i++)); + enc3 = this._keyStr.indexOf(input.charAt(i++)); + enc4 = this._keyStr.indexOf(input.charAt(i++)); + + chr1 = (enc1 << 2) | (enc2 >> 4); + chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); + chr3 = ((enc3 & 3) << 6) | enc4; + + output = output + String.fromCharCode(chr1); + + if (enc3 != 64) { + output = output + String.fromCharCode(chr2); + } + if (enc4 != 64) { + output = output + String.fromCharCode(chr3); + } + + } + + output = Base64._utf8_decode(output); + + return output; + + }, + + // private method for UTF-8 encoding + _utf8_encode : function (string) { + string = string.replace(/\r\n/g,"\n"); + var utftext = ""; + + for (var n = 0; n < string.length; n++) { + + var c = string.charCodeAt(n); + + if (c < 128) { + utftext += String.fromCharCode(c); + } + else if((c > 127) && (c < 2048)) { + utftext += String.fromCharCode((c >> 6) | 192); + utftext += String.fromCharCode((c & 63) | 128); + } + else { + utftext += String.fromCharCode((c >> 12) | 224); + utftext += String.fromCharCode(((c >> 6) & 63) | 128); + utftext += String.fromCharCode((c & 63) | 128); + } + + } + + return utftext; + }, + + // private method for UTF-8 decoding + _utf8_decode : function (utftext) { + var string = ""; + var i = 0; + var c = c1 = c2 = 0; + + while ( i < utftext.length ) { + + c = utftext.charCodeAt(i); + + if (c < 128) { + string += String.fromCharCode(c); + i++; + } + else if((c > 191) && (c < 224)) { + c2 = utftext.charCodeAt(i+1); + string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); + i += 2; + } + else { + c2 = utftext.charCodeAt(i+1); + c3 = utftext.charCodeAt(i+2); + string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); + i += 3; + } + + } + + return string; + } + +} + + + + + + +/* ---------------------------------------------- + * DEPRECATED + * 하위호환용으로 남겨 놓음 + * ------------------------------------------- */ + +if(typeof(resizeImageContents) == 'undefined') { + function resizeImageContents() {} +} + +if(typeof(activateOptionDisabled) == 'undefined') { + function activateOptionDisabled() {} +} + +objectExtend = jQuery.extend; + +/** + * @brief 특정 Element의 display 옵션 토글 + **/ +function toggleDisplay(objId) { + jQuery('#'+objId).toggle(); +} + +/* 체크박스 선택 */ +function checkboxSelectAll(formObj, name, checked) { + var itemName = name; + var option = {}; + if(typeof(formObj) != "undefined") option.wrap = formObj; + if(typeof(checked) != "undefined") option.checked = checked; + + XE.checkboxToggleAll(itemName, option); +} + +/* 체크박스를 실행 */ +function clickCheckBoxAll(formObj, name) { + var itemName = name; + var option = { doClick:true }; + if(typeof(formObj) != "undefined") option.wrap = formObj; + + XE.checkboxToggleAll(itemName, option); +} + +/** + * @brief 에디터에서 사용하되 내용 여닫는 코드 (zb5beta beta 호환용으로 남겨 놓음) + **/ +function svc_folder_open(id) { + jQuery("#_folder_open_"+id).hide(); + jQuery("#_folder_close_"+id).show(); + jQuery("#_folder_"+id).show(); +} +function svc_folder_close(id) { + jQuery("#_folder_open_"+id).show(); + jQuery("#_folder_close_"+id).hide(); + jQuery("#_folder_"+id).hide(); +} + +/** + * @brief 날짜 선택 (달력 열기) + **/ +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'); +} + +var loaded_popup_menus = XE.loaded_popup_menus; +function createPopupMenu() {} +function chkPopupMenu() {} +function displayPopupMenu(ret_obj, response_tags, params) { + XE.displayPopupMenu(ret_obj, response_tags, params); +} + +function GetObjLeft(obj) { + return jQuery(obj).offset().left; +} +function GetObjTop(obj) { + return jQuery(obj).offset().top; +} + +function replaceOuterHTML(obj, html) { + jQuery(obj).replaceWith(html); +} + +function getOuterHTML(obj) { + return jQuery(obj).html().trim(); +} + +function setCookie(name, value, expire, path) { + var s_cookie = name + "=" + escape(value) + + ((!expire) ? "" : ("; expires=" + expire.toGMTString())) + + "; path=" + ((!path) ? "/" : path); + + document.cookie = s_cookie; +} + +function getCookie(name) { + var match = document.cookie.match(new RegExp(name+'=(.*?)(?:;|$)')); + if(match) return unescape(match[1]); +} + +function is_def(v) { + return (typeof(v)!='undefined'); +} + +function ucfirst(str) { + return str.charAt(0).toUpperCase() + str.slice(1); +} + +function get_by_id(id) { + return document.getElementById(id); +} + +jQuery(function($){ + $('.lang_code').each( + function() + { + var objText = $(this); + var targetName = objText.attr("id"); + if(typeof(targetName) == "undefined") targetName = objText.attr("name"); + if(typeof(targetName) == "undefined") return; + objText.after("find_langcode"); + } + ); + + // display popup menu that contains member actions and document actions + $(document).click(function(evt) { + var $area = $('#popup_menu_area'); + if(!$area.length) $area = $('