pixmicat

Subversion Repositories:
Compare Path: Rev
With Path: Rev
/ @ 878  →  / @ 879
New file
/release/PIO-v7/inc_pixmicat-uploader.tpl
@@ -0,0 +1,185 @@
<!-- Theme Description -->
<!--&THEMENAME-->Pixmicat! Uploader-liked Theme<!--/&THEMENAME-->
<!--&THEMEVER-->v20110703<!--/&THEMEVER-->
<!--&THEMEAUTHOR-->Pixmicat! Development Team<!--/&THEMEAUTHOR-->
 
<!-- Theme Blocks -->
<!--&HEADER--><?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh-tw">
<head>
<meta http-equiv="Cache-Control" content="max-age=0; must-revalidate" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="Content-Language" content="zh-tw" />
<title>{$TITLE}</title>
<link rel="stylesheet" type="text/css" href="mainstyle.css" />
<style type="text/css"><!--/*--><![CDATA[/*><!--*/
.grid {float: left; width: 99%;height: 1.2em; overflow:auto; margin:2px; padding:0;}
#page_switch table {border: 2px #F0E0D6 solid;}
#page_switch table td {border:0}
.reply { margin: 0.3ex 0.2ex 0.2ex 1em;}
<!--&IF($RESTO,'','#postform {border: 2px #F0E0D6 solid;} #postform hr {display:none;}')-->
/*]]>*/--></style>
<!--/&HEADER-->
 
<!--&JSHEADER-->
<script type="text/javascript"><!--//--><![CDATA[//><!--
var msgs=['{$JS_REGIST_WITHOUTCOMMENT}','{$JS_REGIST_UPLOAD_NOTSUPPORT}','{$JS_CONVERT_SAKURA}'];
var ext="{$ALLOW_UPLOAD_EXT}".toUpperCase().split("|");
var boxclicked=0;
//--><!]]></script>
<script type="text/javascript" src="mainscript.js"></script>
<!--[if lt IE 8]><script type="text/javascript" src="iedivfix.js"></script><![endif]-->
<!--/&JSHEADER-->
 
<!--&TOPLINKS-->
<div id="toplink">
{$HOME} {$SEARCH} {$HOOKLINKS} {$TOP_LINKS} {$STATUS} {$ADMIN} {$REFRESH}
</div>
<!--/&TOPLINKS-->
 
<!--&BODYHEAD-->
<body>
 
<div id="header">
<!--&TOPLINKS/-->
<br />
<h1>{$TITLE}</h1>
<hr class="top" />
</div>
<!--/&BODYHEAD-->
 
<!--&POSTFORM-->
<form action="{$SELF}" method="post" enctype="multipart/form-data" onsubmit="return c();" id="postform_main">
<div id="postform">
<!--&IF($FORMTOP,'{$FORMTOP}','')-->
<input type="hidden" name="mode" value="{$MODE}" />
<input type="hidden" name="MAX_FILE_SIZE" value="{$MAX_FILE_SIZE}" />
<input type="hidden" name="upfile_path" value="" />
<!--&IF($RESTO,'{$RESTO}','')-->
<div style="text-align: center;">
<table cellpadding="1" cellspacing="1" id="postform_tbl" style="margin: 0px auto; text-align: left;">
<tr><td class="Form_bg"><b>{$FORM_NAME_TEXT}</b></td><td>{$FORM_NAME_FIELD}</td></tr>
<tr><td class="Form_bg"><b>{$FORM_EMAIL_TEXT}</b></td><td>{$FORM_EMAIL_FIELD}</td></tr>
<tr><td class="Form_bg"><b>{$FORM_TOPIC_TEXT}</b></td><td>{$FORM_TOPIC_FIELD}{$FORM_SUBMIT}</td></tr>
<tr><td class="Form_bg"><b>{$FORM_COMMENT_TEXT}</b></td><td>{$FORM_COMMENT_FIELD}</td></tr>
<!--&IF($FORM_ATTECHMENT_FIELD,'<tr><td class="Form_bg"><b>{$FORM_ATTECHMENT_TEXT}</b></td><td>{$FORM_ATTECHMENT_FIELD}[{$FORM_NOATTECHMENT_FIELD}<label for="noimg">{$FORM_NOATTECHMENT_TEXT}</label>]','')-->
<!--&IF($FORM_CONTPOST_FIELD,'[{$FORM_CONTPOST_FIELD}<label for="up_series">{$FORM_CONTPOST_TEXT}</label>]','')-->
<!--&IF($FORM_ATTECHMENT_FIELD,'</td></tr>','')-->
<!--&IF($FORM_CATEGORY_FIELD,'<tr><td class="Form_bg"><b>{$FORM_CATEGORY_TEXT}</b></td><td>{$FORM_CATEGORY_FIELD}<small>{$FORM_CATEGORY_NOTICE}</small></td></tr>','')-->
<tr><td class="Form_bg"><b>{$FORM_DELETE_PASSWORD_TEXT}</b></td><td>{$FORM_DELETE_PASSWORD_FIELD}<small>{$FORM_DELETE_PASSWORD_NOTICE}</small></td></tr>
{$FORM_EXTRA_COLUMN}
<tr><td colspan="2">
<div id="postinfo">
<ul>{$FORM_NOTICE}
<!--&IF($FORM_NOTICE_STORAGE_LIMIT,'{$FORM_NOTICE_STORAGE_LIMIT}','')-->
{$HOOKPOSTINFO}
{$ADDITION_INFO}
</ul>
<noscript><div>{$FORM_NOTICE_NOSCRIPT}</div></noscript>
</div>
</td></tr>
</table>
</div>
<script type="text/javascript">l1();</script>
<hr />
</div>
</form>
<!--&IF($FORMBOTTOM,'{$FORMBOTTOM}','')-->
<!--/&POSTFORM-->
 
<!--&FOOTER-->
<div id="footer">
{$FOOTER}
<script type="text/javascript">preset();</script>
</div>
 
</body>
</html>
<!--/&FOOTER-->
 
<!--&ERROR-->
<div id="error">
<div style="text-align: center; font-size: 1.5em; font-weight: bold;">
<span style="color: red;">{$MESG}</span><p />
<a href="{$SELF2}">{$RETURN_TEXT}</a> <a href="javascript:history.back();">{$BACK_TEXT}</a>
</div>
<hr />
</div>
<!--/&ERROR-->
 
 
<!--&RES_THREAD-->
<div class="threadpost" id="r{$NO}"><input type="checkbox" name="{$NO}" value="delete" onclick="boxclicked=1;" /><span class="title">{$SUB}</span>
{$NAME_TEXT}<span class="name">{$NAME}</span> [{$NOW}] {$QUOTEBTN} {$REPLYBTN}</div>
{$IMG_BAR}<!--&IF($IMG_BAR,'<br />','')-->{$IMG_SRC}
{$WARN_OLD}{$WARN_BEKILL}{$WARN_ENDREPLY}{$WARN_HIDEPOST}
<div class="quote">{$COM}</div>
<!--&IF($CATEGORY,'<div class="category">{$CATEGORY_TEXT}{$CATEGORY}</div>','')-->
<!--/&RES_THREAD-->
 
<!--&MAIN_THREAD-->
<tr>
<td><input type="checkbox" name="{$NO}" value="delete" onclick="boxclicked=1;" /></td><td>{$QUOTEBTN}</td><td><span class="name">{$NAME}</span></td>
<td><span class="title">{$SUB}</span></td><td>{$NOW}</td><td>{$IMG_BAR}</td> <td>{$REPLYBTN}</td></tr>
<!--/&MAIN_THREAD-->
 
 
<!--&THREAD-->
<!--&IF($RESTO,'<!--&RES_THREAD/-->','<!--&MAIN_THREAD/-->')-->
<!--/&THREAD-->
 
<!--&REPLY-->
<div class="reply" id="r{$NO}"><div class="replywrap">
<input type="checkbox" name="{$NO}" value="delete" onclick="boxclicked=1;" /><span class="title">{$SUB}</span> {$NAME_TEXT}<span class="name">{$NAME}</span> [{$NOW}] {$QUOTEBTN}&nbsp;<!--&IF($IMG_BAR,'<br />&nbsp;','')-->{$IMG_BAR} {$IMG_SRC}
{$WARN_BEKILL}<div class="quote">{$COM}</div>
<!--&IF($CATEGORY,'<div class="category">{$CATEGORY_TEXT}{$CATEGORY}</div>','')-->
</div></div>
<!--/&REPLY-->
 
<!--&SEARCHRESULT-->
<div class="threadpost">
<span class="title">{$SUB}</span>
{$NAME_TEXT}<span class="name">{$NAME}</span> [{$NOW}] No.{$NO}
<div class="quote">{$COM}</div>
<!--&IF($CATEGORY,'<div class="category">{$CATEGORY_TEXT}{$CATEGORY}</div>','')-->
</div>
<!--&REALSEPARATE/-->
<!--/&SEARCHRESULT-->
 
<!--&THREADSEPARATE-->
<!--&IF($RESTO,'<hr />','</div>')-->
<!--/&THREADSEPARATE-->
 
<!--&REALSEPARATE-->
<hr />
<!--/&REALSEPARATE-->
 
<!--&DELFORM-->
<div id="del">
<table style="float: right;">
<tr><td align="center" style="white-space: nowrap;">
{$DEL_HEAD_TEXT}[{$DEL_IMG_ONLY_FIELD}<label for="onlyimgdel">{$DEL_IMG_ONLY_TEXT}</label>]<br />
{$DEL_PASS_TEXT}{$DEL_PASS_FIELD}{$DEL_SUBMIT_BTN}
</td></tr>
</table>
</div>
<!--/&DELFORM-->
 
<!--&MAIN-->
<div id="contents">
<form action="{$SELF}" method="POST">
<div id="threads">
{$THREADFRONT}
<!--&IF($RESTO,'','<table width="100%" style="clear:both"><tr><th>Del</th><th>No.</th><th>Name</th><th>Title</th><th>Date/ID</th><th>File</th><th>Reply</th></tr>')-->
{$THREADS}
<!--&IF($RESTO,'','</table>')-->
{$THREADREAR}
</div>
<div style="clear:both"></div>
<!--&DELFORM/-->
<script type="text/javascript">l2();</script>
</form>
{$PAGENAV}
</div>
<!--/&MAIN-->
New file
/release/PIO-v7/inc_pixmicat-festival.tpl
@@ -0,0 +1,195 @@
<!-- Theme Description -->
<!--&THEMENAME-->Pixmicat!-Festival Theme<!--/&THEMENAME-->
<!--&THEMEVER-->v20110703<!--/&THEMEVER-->
<!--&THEMEAUTHOR-->Pixmicat! Development Team<!--/&THEMEAUTHOR-->
 
<!-- Theme Settings -->
 
<!-- Festival Theme Settings -->
<!--&CLICKENTER-->1<!--/&CLICKENTER-->
<!--&BLOCKWIDTH-->272px<!--/&BLOCKWIDTH-->
<!--&BLOCKHEIGHT-->400px<!--/&BLOCKHEIGHT-->
<!--&CENTERIMG-->1<!--/&CENTERIMG-->
 
<!-- non-Festival Theme Settings (Replace "!--&" to "!---&" of above and replace "!---&" to "!--&" of below to activate ) -->
<!---&CLICKENTER-->0<!--/&CLICKENTER-->
<!---&BLOCKWIDTH-->99%<!--/&BLOCKWIDTH-->
<!---&BLOCKHEIGHT-->auto<!--/&BLOCKHEIGHT-->
<!---&CENTERIMG-->0<!--/&CENTERIMG-->
 
<!-- Theme Blocks -->
<!--&IMGSTYLE--><!--&IF(&CENTERIMG,'.img {margin:0;}','')--><!--/&IMGSTYLE-->
<!--&HEADER--><?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh-tw">
<head>
<meta http-equit="Cache-Control" content="max-age=0; must-revalidate" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="Content-Language" content="zh-tw" />
<title>{$TITLE}</title>
<link rel="stylesheet" type="text/css" href="mainstyle.css" />
<style type="text/css"><!--/*--><![CDATA[/*><!--*/
.grid {float: left; border: 2px #F0E0D6 solid; width: <!--&BLOCKWIDTH/-->;height: <!--&BLOCKHEIGHT/-->;<!--&IF(&CLICKENTER,'cursor: pointer; cursor: hand;','')--> overflow:auto; margin:2px; padding:0;}
#page_switch table {border: 2px #F0E0D6 solid;}
#page_switch table td {border:0}
.reply { margin: 0.3ex 0.2ex 0.2ex 1em;}
<!--&IF($RESTO,'','#postform {border: 2px #F0E0D6 solid;} #postform hr {display:none;}')-->
<!--&IF($RESTO,'','<!--&IMGSTYLE/-->')-->
/*]]>*/--></style>
<!--/&HEADER-->
 
<!--&JSHEADER-->
<script type="text/javascript"><!--//--><![CDATA[//><!--
var msgs=['{$JS_REGIST_WITHOUTCOMMENT}','{$JS_REGIST_UPLOAD_NOTSUPPORT}','{$JS_CONVERT_SAKURA}'];
var ext="{$ALLOW_UPLOAD_EXT}".toUpperCase().split("|");
var boxclicked=0;
//--><!]]></script>
<script type="text/javascript" src="mainscript.js"></script>
<!--[if lt IE 8]><script type="text/javascript" src="iedivfix.js"></script><![endif]-->
<!--/&JSHEADER-->
 
<!--&TOPLINKS-->
<div id="toplink">
{$HOME} {$SEARCH} {$HOOKLINKS} {$TOP_LINKS} {$STATUS} {$ADMIN} {$REFRESH}
</div>
<!--/&TOPLINKS-->
 
<!--&BODYHEAD-->
<body>
 
<div id="header">
<!--&TOPLINKS/-->
<br />
<h1>{$TITLE}</h1>
<hr class="top" />
</div>
<!--/&BODYHEAD-->
 
<!--&POSTFORM-->
<form action="{$SELF}" method="post" enctype="multipart/form-data" onsubmit="return c();" id="postform_main">
<div id="postform">
<!--&IF($FORMTOP,'{$FORMTOP}','')-->
<input type="hidden" name="mode" value="{$MODE}" />
<input type="hidden" name="MAX_FILE_SIZE" value="{$MAX_FILE_SIZE}" />
<input type="hidden" name="upfile_path" value="" />
<!--&IF($RESTO,'{$RESTO}','')-->
<div style="text-align: center;">
<table cellpadding="1" cellspacing="1" id="postform_tbl" style="margin: 0px auto; text-align: left;">
<tr><td class="Form_bg"><b>{$FORM_NAME_TEXT}</b></td><td>{$FORM_NAME_FIELD}</td></tr>
<tr><td class="Form_bg"><b>{$FORM_EMAIL_TEXT}</b></td><td>{$FORM_EMAIL_FIELD}</td></tr>
<tr><td class="Form_bg"><b>{$FORM_TOPIC_TEXT}</b></td><td>{$FORM_TOPIC_FIELD}{$FORM_SUBMIT}</td></tr>
<tr><td class="Form_bg"><b>{$FORM_COMMENT_TEXT}</b></td><td>{$FORM_COMMENT_FIELD}</td></tr>
<!--&IF($FORM_ATTECHMENT_FIELD,'<tr><td class="Form_bg"><b>{$FORM_ATTECHMENT_TEXT}</b></td><td>{$FORM_ATTECHMENT_FIELD}[{$FORM_NOATTECHMENT_FIELD}<label for="noimg">{$FORM_NOATTECHMENT_TEXT}</label>]','')-->
<!--&IF($FORM_CONTPOST_FIELD,'[{$FORM_CONTPOST_FIELD}<label for="up_series">{$FORM_CONTPOST_TEXT}</label>]','')-->
<!--&IF($FORM_ATTECHMENT_FIELD,'</td></tr>','')-->
<!--&IF($FORM_CATEGORY_FIELD,'<tr><td class="Form_bg"><b>{$FORM_CATEGORY_TEXT}</b></td><td>{$FORM_CATEGORY_FIELD}<small>{$FORM_CATEGORY_NOTICE}</small></td></tr>','')-->
<tr><td class="Form_bg"><b>{$FORM_DELETE_PASSWORD_TEXT}</b></td><td>{$FORM_DELETE_PASSWORD_FIELD}<small>{$FORM_DELETE_PASSWORD_NOTICE}</small></td></tr>
{$FORM_EXTRA_COLUMN}
<tr><td colspan="2">
<div id="postinfo">
<ul>{$FORM_NOTICE}
<!--&IF($FORM_NOTICE_STORAGE_LIMIT,'{$FORM_NOTICE_STORAGE_LIMIT}','')-->
{$HOOKPOSTINFO}
{$ADDITION_INFO}
</ul>
<noscript><div>{$FORM_NOTICE_NOSCRIPT}</div></noscript>
</div>
</td></tr>
</table>
</div>
<script type="text/javascript">l1();</script>
<hr />
</div>
</form>
<!--&IF($FORMBOTTOM,'{$FORMBOTTOM}','')-->
<!--/&POSTFORM-->
 
<!--&FOOTER-->
<div id="footer">
{$FOOTER}
<script type="text/javascript">preset();</script>
</div>
 
</body>
</html>
<!--/&FOOTER-->
 
<!--&ERROR-->
<div id="error">
<div style="text-align: center; font-size: 1.5em; font-weight: bold;">
<span style="color: red;">{$MESG}</span><p />
<a href="{$SELF2}">{$RETURN_TEXT}</a>&emsp;<a href="javascript:history.back();">{$BACK_TEXT}</a>
</div>
<hr />
</div>
<!--/&ERROR-->
 
 
<!--&CLICKENTER_PROP--> onclick="if (!boxclicked) window.location='{$SELF}?res={$NO}';boxclicked=0;"<!--/&CLICKENTER_PROP-->
<!--&THREAD_CLICKENTER--><div class="grid"<!--&IF(&CLICKENTER,'<!--&CLICKENTER_PROP/-->','')--> id="g{$NO}"><!--/&THREAD_CLICKENTER-->
<!--&IMGARRANGE--><!--&IF(&CENTERIMG,'<!--&IMG_CENTER/-->','<!--&IMG_LEFT/-->')--><!--/&IMGARRANGE-->
<!--&IMG_CENTER--><!--&IF($IMG_BAR,'<table align="center"><tr><td>','')-->{$IMG_SRC}<!--&IF($IMG_BAR,'</td></tr></table>','')--><!--/&IMG_CENTER-->
<!--&IMG_LEFT--><!--&IF($IMG_BAR,'<br />','')-->{$IMG_SRC}<!--/&IMG_LEFT-->
 
<!--&THREAD-->
<!--&IF($RESTO,'','<!--&THREAD_CLICKENTER/-->')-->
<div class="threadpost" id="r{$NO}"><input type="checkbox" name="{$NO}" value="delete" onclick="boxclicked=1;" /><span class="title">{$SUB}</span>
{$NAME_TEXT}<span class="name">{$NAME}</span> [{$NOW}] {$QUOTEBTN} {$REPLYBTN}</div>
{$IMG_BAR}<!--&IF($RESTO,'<!--&IMG_LEFT/-->','<!--&IMGARRANGE/-->')-->
{$WARN_OLD}{$WARN_BEKILL}{$WARN_ENDREPLY}{$WARN_HIDEPOST}
<div class="quote">{$COM}</div>
<!--&IF($CATEGORY,'<div class="category">{$CATEGORY_TEXT}{$CATEGORY}</div>','')-->
<!--/&THREAD-->
 
<!--&REPLY-->
<div class="reply" id="r{$NO}"><div class="replywrap">
<input type="checkbox" name="{$NO}" value="delete" onclick="boxclicked=1;" /><span class="title">{$SUB}</span> {$NAME_TEXT}<span class="name">{$NAME}</span> [{$NOW}] {$QUOTEBTN}&nbsp;<!--&IF($IMG_BAR,'<br />&nbsp;','')-->{$IMG_BAR} {$IMG_SRC}
{$WARN_BEKILL}<div class="quote">{$COM}</div>
<!--&IF($CATEGORY,'<div class="category">{$CATEGORY_TEXT}{$CATEGORY}</div>','')-->
</div></div>
<!--/&REPLY-->
 
<!--&SEARCHRESULT-->
<div class="threadpost">
<span class="title">{$SUB}</span>
{$NAME_TEXT}<span class="name">{$NAME}</span> [{$NOW}] No.{$NO}
<div class="quote">{$COM}</div>
<!--&IF($CATEGORY,'<div class="category">{$CATEGORY_TEXT}{$CATEGORY}</div>','')-->
</div>
<!--&REALSEPARATE/-->
<!--/&SEARCHRESULT-->
 
<!--&THREADSEPARATE-->
<!--&IF($RESTO,'<hr />','</div>')-->
<!--/&THREADSEPARATE-->
 
<!--&REALSEPARATE-->
<hr />
<!--/&REALSEPARATE-->
 
<!--&DELFORM-->
<div id="del">
<table style="float: right;">
<tr><td align="center" style="white-space: nowrap;">
{$DEL_HEAD_TEXT}[{$DEL_IMG_ONLY_FIELD}<label for="onlyimgdel">{$DEL_IMG_ONLY_TEXT}</label>]<br />
{$DEL_PASS_TEXT}{$DEL_PASS_FIELD}{$DEL_SUBMIT_BTN}
</td></tr>
</table>
</div>
<!--/&DELFORM-->
 
<!--&MAIN-->
<div id="contents">
{$THREADFRONT}
<form action="{$SELF}" method="post">
<div id="threads" style="clear:both;">
{$THREADS}
</div>
{$THREADREAR}
<div style="clear:both"></div>
<!--&DELFORM/-->
<script type="text/javascript">l2();</script>
</form>
{$PAGENAV}
</div>
<!--/&MAIN-->
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes:

Name: svn:mime-type
+ application/octet-stream

/release/PIO-v7/nothumb.gif
New file
/release/PIO-v7/iedivfix.js
@@ -0,0 +1,10 @@
// IE不支援display: table,故用此方法
document.write('<style type="text/css">.reply { display: inline ; zoom: 1; }</style>');
 
// 解決IE顯示回應區塊時排在同一行的問題
function IEdivfix(){
var divs=document.getElementsByTagName('div'),divs_cnt=divs.length;
for(i=0;i<divs_cnt;i++)
if(divs[i].className.substr(0,5)=='reply') divs[i].insertAdjacentHTML('afterEnd','<br />');
}
hookPresetFunction(IEdivfix); // Hook on
New file
/release/PIO-v7/mainscript.js
@@ -0,0 +1,8 @@
var previous_replyhlno=0,arrPresetFunc=[],arrSakuraTbl=[[63223,12353,82],[63306,12449,85],[63486,12535,4]],arrSakuraTblsp=[[63216,63219,63210,63211,63212,63213],[12293,12540,12541,12542,12445,14446]];function $g(a){return document.getElementById(a)}function getCookie(a){var d,b,e=0,g=0,f;d=" "+document.cookie+";";for(var h=d.length;e<h;){g=d.indexOf(";",e);b=d.substring(e+1,g);f=b.indexOf("=");if(b.substring(0,f)===a)return window.unescape(b.substring(f+1,g-e-1));e=g+1}return""}
function setCookie(a,d){var b=new Date;b.setTime(b.getTime()+6048E5);document.cookie=a+"="+window.escape(d)+"; expires="+b.toGMTString()}
function replace_sakura(a){var d=a.value,b=0,e=0,g=arrSakuraTbl.length,f;for(b=0;b<g;b++){f=arrSakuraTbl[b];for(e=0;e<=f[2];e++)d=d.replace(new RegExp(String.fromCharCode(f[0]+e),"g"),String.fromCharCode(f[1]+e))}g=arrSakuraTblsp[0].legnth;for(b=0;b<g;b++){f=arrSakuraTblsp;d=d.replace(new RegExp(String.fromCharCode(f[0][b]),"g"),String.fromCharCode(f[1][b]))}a.value=d}
function check_sakura(a){a=$g(a);if(window.escape(a.value).toLowerCase().match(/%uf(6[ef]|7[0-9a-f]|80)[0-9a-f]/)!==null){alert(msgs[2]);replace_sakura(a)}}function l1(){var a=getCookie("namec"),d=getCookie("emailc"),b;if(b=$g("fname"))b.value=a;if(b=$g("femail"))b.value=d}function l2(){for(var a=getCookie("pwdc"),d=document,b=d.forms.length,e=0;e<b;e++)if(d.forms[e].pwd)d.forms[e].pwd.value=a}
function c(){var a,d,b,e;try{if(!$g("fupfile"))return true;a=$g("fupfile").value;if(!a&&!$g("fcom").value){alert(msgs[0]);return false}if(a){b=0;e=ext.length;for(d=0;d<e;d++)if(a.substr(a.length-3,3).toUpperCase()===ext[d]){b=1;break}if(!b){alert(msgs[1]);return false}}check_sakura("fcom");check_sakura("fname");check_sakura("fsub");if(window.clipboardData)document.forms[0].upfile_path.value=a;document.forms[0].sendbtn.disabled=true}catch(g){}$g("fname").value&&setCookie("namec",$g("fname").value)}
function fixalllinks(){if(document.getElementsByTagName)for(var a,d=document.getElementsByTagName("a"),b=d.length,e=0;e<b;e++){a=d[e];if(a.getAttribute("href")){if(a.getAttribute("rel")==="_top")a.target="_top";if(a.getAttribute("rel")==="_blank")a.target="_blank"}}}function showform(){$g("postform").className="";$g("postform_tbl").className="";$g("hide").className="show";$g("show").className="hide"}
function hideform(){$g("postform").className="hide_btn";$g("postform_tbl").className="hide";$g("hide").className="hide";$g("show").className="show"}function quote(a){try{$g("fcom").focus();$g("fcom").value+=">>No."+a+"\r\n"}catch(d){}}function replyhl(a,d){var b=$g("r"+a);if(b)if(d)b.className=b.className.replace(" reply_hl","");else{previous_replyhlno&&replyhl(previous_replyhlno,true);previous_replyhlno=a;b.className+=" reply_hl"}}
function hookPresetFunction(a){typeof a==="function"&&arrPresetFunc.push(a)}function preset(){var a,d=arrPresetFunc.length,b;fixalllinks();for(a=0;a<d;a++){b=arrPresetFunc[a];typeof b==="function"&&b()}a=location.href;if(a.indexOf("?res=")){a.match(/#[rq]([0-9]+)$/)&&replyhl(RegExp.$1,false);a.match(/#q([0-9]+)$/)&&quote(RegExp.$1)}};
New file
/release/PIO-v7/pixmicat.php
@@ -0,0 +1,1294 @@
<?php
define("PIXMICAT_VER", 'Pixmicat!-PIO 7th.Release'); // 版本資訊文字
define("PHP_SELF", basename(__FILE__)); // 主程式名
/*
Pixmicat! : 圖咪貓貼圖版程式
http://pixmicat.openfoundry.org/
版權所有 © 2005-2013 Pixmicat! Development Team
 
版權聲明:
此程式是基於レッツPHP!<http://php.s3.to/>的gazou.php、
双葉ちゃん<http://www.2chan.net>的futaba.php所改寫之衍生著作程式,屬於自由軟體,
以Artistic License 2.0作為發佈授權條款。
您可以遵照Artistic License 2.0來自由使用、散播、修改或製成衍生著作。
更詳細的條款及定義請參考隨附"LICENSE"條款副本。
 
發佈這一程式的目的是希望它有用,但沒有任何擔保,甚至沒有適合特定目的而隱含的擔保。
關於此程式相關的問題請不要詢問レッツPHP!及双葉ちゃん。
 
如果您沒有隨著程式收到一份Artistic License 2.0副本,
請瀏覽http://pixmicat.openfoundry.org/license/以取得一份。
 
"Pixmicat!", "Pixmicat", 及"圖咪貓"是Pixmicat! Development Team的商標。
 
最低運行需求:
PHP 5.2.0 / 2 November 2006
GD Version 2.0.28 / 21 July 2004
 
建議運行環境:
PHP 5.2.0 或更高版本並開啟 GD 和 Zlib 支援,如支援 ImageMagick 建議使用
安裝 PHP 編譯快取套件 (如eAccelerator, XCache, APC) 或其他快取套件 (如memcached) 更佳
如伺服器支援 SQLite, MySQL, PostgreSQL 等請盡量使用
 
設置方法:
根目錄的權限請設為777,
首先將pixmicat.php執行過一遍,必要的檔案和資料夾權限皆會自動設定,
自動設定完成後請刪除或註解起來此檔案底部之init(); // ←■■!程式環境初始化(略)一行,
然後再執行一遍pixmicat.php,即完成初始化程序,可以開始使用。
 
細部的設定請打開config.php參考註解修改,另有 Wiki (http://pixmicat.wikidot.com/pmcuse:config)
說明條目可資參考。
*/
 
require './config.php'; // 引入設定檔
require ROOTPATH.'lib/pmclibrary.php'; // 引入函式庫
require ROOTPATH.'lib/lib_errorhandler.php'; // 引入全域錯誤捕捉
require ROOTPATH.'lib/lib_compatible.php'; // 引入相容函式庫
require ROOTPATH.'lib/lib_common.php'; // 引入共通函式檔案
 
/* 更新記錄檔檔案/輸出討論串 */
function updatelog($resno=0,$page_num=-1,$single_page=false){
global $LIMIT_SENSOR;
$PIO = PMCLibrary::getPIOInstance();
$FileIO = PMCLibrary::getFileIOInstance();
$PTE = PMCLibrary::getPTEInstance();
$PMS = PMCLibrary::getPMSInstance();
 
$adminMode = adminAuthenticate('check') && $page_num != -1 && !$single_page; // 前端管理模式
$adminFunc = ''; // 前端管理選擇
if($adminMode){
$adminFunc = '<select name="func"><option value="delete">'._T('admin_delete').'</option>';
$funclist = array();
$dummy = '';
$PMS->useModuleMethods('AdminFunction', array('add', &$funclist, null, &$dummy)); // "AdminFunction" Hook Point
foreach($funclist as $f) $adminFunc .= '<option value="'.$f[0].'">'.$f[1].'</option>'."\n";
$adminFunc .= '</select>';
}
$resno = intval($resno); // 編號數字化
$page_start = $page_end = 0; // 靜態頁面編號
$inner_for_count = 1; // 內部迴圈執行次數
$RES_start = $RES_amount = $hiddenReply = $tree_count = 0;
$kill_sensor = $old_sensor = false; // 預測系統啟動旗標
$arr_kill = $arr_old = array(); // 過舊編號陣列
$pte_vals = array('{$THREADFRONT}'=>'','{$THREADREAR}'=>'','{$SELF}'=>PHP_SELF,
'{$DEL_HEAD_TEXT}' => '<input type="hidden" name="mode" value="usrdel" />'._T('del_head'),
'{$DEL_IMG_ONLY_FIELD}' => '<input type="checkbox" name="onlyimgdel" id="onlyimgdel" value="on" />',
'{$DEL_IMG_ONLY_TEXT}' => _T('del_img_only'),
'{$DEL_PASS_TEXT}' => ($adminMode ? $adminFunc : '')._T('del_pass'),
'{$DEL_PASS_FIELD}' => '<input type="password" name="pwd" size="8" value="" />',
'{$DEL_SUBMIT_BTN}' => '<input type="submit" value="'._T('del_btn').'" />');
if($resno) $pte_vals['{$RESTO}'] = $resno;
 
if(!$resno){
if($page_num==-1){ // remake模式 (PHP動態輸出多頁份)
$threads = $PIO->fetchThreadList(); // 取得全討論串列表
$PMS->useModuleMethods('ThreadOrder', array($resno,$page_num,$single_page,&$threads)); // "ThreadOrder" Hook Point
$threads_count = count($threads);
$inner_for_count = $threads_count > PAGE_DEF ? PAGE_DEF : $threads_count;
$page_end = ceil($threads_count / PAGE_DEF) - 1; // 頁面編號最後值
}else{ // 討論串分頁模式 (PHP動態輸出一頁份)
$threads_count = $PIO->threadCount(); // 討論串個數
if($page_num < 0 || ($page_num * PAGE_DEF) >= $threads_count) error(_T('page_not_found')); // $page_num超過範圍
$page_start = $page_end = $page_num; // 設定靜態頁面編號
$threads = $PIO->fetchThreadList(); // 取得全討論串列表
$PMS->useModuleMethods('ThreadOrder', array($resno,$page_num,$single_page,&$threads)); // "ThreadOrder" Hook Point
$threads = array_splice($threads, $page_num * PAGE_DEF, PAGE_DEF); // 取出分頁後的討論串首篇列表
$inner_for_count = count($threads); // 討論串個數就是迴圈次數
}
}else{
if(!$PIO->isThread($resno)){ error(_T('thread_not_found')); }
$AllRes = isset($_GET['page_num']) && $_GET['page_num']=='all'; // 是否使用 ALL 全部輸出
 
// 計算回應分頁範圍
$tree_count = $PIO->postCount($resno) - 1; // 討論串回應個數
if($tree_count && RE_PAGE_DEF){ // 有回應且RE_PAGE_DEF > 0才做分頁動作
if($page_num==='all'){ // show all
$page_num = 0;
$RES_start = 1; $RES_amount = $tree_count;
}else{
if($page_num==='RE_PAGE_MAX') $page_num = ceil($tree_count / RE_PAGE_DEF) - 1; // 特殊值:最末頁
if($page_num < 0) $page_num = 0; // 負數
if($page_num * RE_PAGE_DEF >= $tree_count) error(_T('page_not_found'));
$RES_start = $page_num * RE_PAGE_DEF + 1; // 開始
$RES_amount = RE_PAGE_DEF; // 取幾個
}
}elseif($page_num > 0) error(_T('page_not_found')); // 沒有回應的情況只允許page_num = 0 或負數
else{ $RES_start = 1; $RES_amount = $tree_count; $page_num = 0; } // 輸出全部回應
 
if(USE_RE_CACHE && !$adminMode){ // 檢查快取是否仍可使用 / 頁面有無更動
$cacheETag = md5(($AllRes ? 'all' : $page_num).'-'.$tree_count); // 最新狀態快取用 ETag
$cacheFile = ROOTPATH.'cache/'.$resno.'-'.($AllRes ? 'all' : $page_num).'.'; // 暫存快取檔位置
$cacheGzipPrefix = extension_loaded('zlib') ? 'compress.zlib://' : ''; // 支援 Zlib Compression Stream 就使用
$cacheControl = isset($_SERVER['HTTP_CACHE_CONTROL']) ? $_SERVER['HTTP_CACHE_CONTROL'] : ''; // 瀏覽器快取控制
if(isset($_SERVER['HTTP_IF_NONE_MATCH']) && $_SERVER['HTTP_IF_NONE_MATCH'] == '"'.$cacheETag.'"'){ // 再度瀏覽而快取無更動
header('HTTP/1.1 304 Not Modified');
header('ETag: "'.$cacheETag.'"');
return;
}elseif(file_exists($cacheFile.$cacheETag) && $cacheControl != 'no-cache'){ // 有(更新的)暫存快取檔存在 (未強制no-cache)
header('X-Cache: HIT from Pixmicat!');
header('ETag: "'.$cacheETag.'"');
header('Connection: close');
readfile($cacheGzipPrefix.$cacheFile.$cacheETag); return;
}else{
header('X-Cache: MISS from Pixmicat!');
}
}
}
 
// 預測過舊文章和將被刪除檔案
if(PIOSensor::check('predict', $LIMIT_SENSOR)){ // 是否需要預測
$old_sensor = true; // 標記打開
$arr_old = array_flip(PIOSensor::listee('predict', $LIMIT_SENSOR)); // 過舊文章陣列
}
$tmp_total_size = $FileIO->getCurrentStorageSize(); // 目前附加圖檔使用量
$tmp_STORAGE_MAX = STORAGE_MAX * (($tmp_total_size >= STORAGE_MAX) ? 1 : 0.95); // 預估上限值
if(STORAGE_LIMIT && STORAGE_MAX > 0 && ($tmp_total_size >= $tmp_STORAGE_MAX)){
$kill_sensor = true; // 標記打開
$arr_kill = $PIO->delOldAttachments($tmp_total_size, $tmp_STORAGE_MAX); // 過舊附檔陣列
}
 
$PMS->useModuleMethods('ThreadFront', array(&$pte_vals['{$THREADFRONT}'], $resno)); // "ThreadFront" Hook Point
$PMS->useModuleMethods('ThreadRear', array(&$pte_vals['{$THREADREAR}'], $resno)); // "ThreadRear" Hook Point
 
// 生成靜態頁面一頁份內容
for($page = $page_start; $page <= $page_end; $page++){
$dat = ''; $pte_vals['{$THREADS}'] = '';
head($dat, $resno);
form($dat, $resno);
// 輸出討論串內容
for($i = 0; $i < $inner_for_count; $i++){
// 取出討論串編號
if($resno) $tID = $resno; // 單討論串輸出 (回應模式)
else{
if($page_num == -1 && ($page * PAGE_DEF + $i) >= $threads_count) break; // remake 超出索引代表已全部完成
$tID = ($page_start==$page_end) ? $threads[$i] : $threads[$page * PAGE_DEF + $i]; // 一頁內容 (一般模式) / 多頁內容 (remake模式)
$tree_count = $PIO->postCount($tID) - 1; // 討論串回應個數
$RES_start = $tree_count - RE_DEF + 1; if($RES_start < 1) $RES_start = 1; // 開始
$RES_amount = RE_DEF; // 取幾個
$hiddenReply = $RES_start - 1; // 被隱藏回應數
}
 
// $RES_start, $RES_amount 拿去算新討論串結構 (分頁後, 部分回應隱藏)
$tree = $PIO->fetchPostList($tID); // 整個討論串樹狀結構
$tree_cut = array_slice($tree, $RES_start, $RES_amount); array_unshift($tree_cut, $tID); // 取出特定範圍回應
$posts = $PIO->fetchPosts($tree_cut); // 取得文章架構內容
$pte_vals['{$THREADS}'] .= arrangeThread($PTE, $tree, $tree_cut, $posts, $hiddenReply, $resno, $arr_kill, $arr_old, $kill_sensor, $old_sensor, true, $adminMode); // 交給這個函式去搞討論串印出
}
$pte_vals['{$PAGENAV}'] = '<div id="page_switch">';
 
// 換頁判斷
$prev = ($resno ? $page_num : $page) - 1;
$next = ($resno ? $page_num : $page) + 1;
if($resno){ // 回應分頁
if(RE_PAGE_DEF > 0){ // 回應分頁開啟
$pte_vals['{$PAGENAV}'] .= '<table border="1"><tr><td style="white-space: nowrap;">';
$pte_vals['{$PAGENAV}'] .= ($prev >= 0) ? '<a href="'.PHP_SELF.'?res='.$resno.'&amp;page_num='.$prev.'">'._T('prev_page').'</a>' : _T('first_page');
$pte_vals['{$PAGENAV}'] .= "</td><td>";
if($tree_count==0) $pte_vals['{$PAGENAV}'] .= '[<b>0</b>] '; // 無回應
else{
for($i = 0, $len = $tree_count / RE_PAGE_DEF; $i < $len; $i++){
if(!$AllRes && $page_num==$i) $pte_vals['{$PAGENAV}'] .= '[<b>'.$i.'</b>] ';
else $pte_vals['{$PAGENAV}'] .= '[<a href="'.PHP_SELF.'?res='.$resno.'&amp;page_num='.$i.'">'.$i.'</a>] ';
}
$pte_vals['{$PAGENAV}'] .= $AllRes ? '[<b>'._T('all_pages').'</b>] ' : ($tree_count > RE_PAGE_DEF ? '[<a href="'.PHP_SELF.'?res='.$resno.'&amp;page_num=all">'._T('all_pages').'</a>] ' : '');
}
$pte_vals['{$PAGENAV}'] .= '</td><td style="white-space: nowrap;">';
$pte_vals['{$PAGENAV}'] .= (!$AllRes && $tree_count > $next * RE_PAGE_DEF) ? '<a href="'.PHP_SELF.'?res='.$resno.'&amp;page_num='.$next.'">'._T('next_page').'</a>' : _T('last_page');
$pte_vals['{$PAGENAV}'] .= '</td></tr></table>'."\n";
}
}else{ // 一般分頁
$pte_vals['{$PAGENAV}'] .= '<table border="1"><tr>';
if($prev >= 0){
if(!$adminMode && $prev==0) $pte_vals['{$PAGENAV}'] .= '<td><form action="'.PHP_SELF2.'" method="get">';
else{
if($adminMode || (STATIC_HTML_UNTIL != -1) && ($prev > STATIC_HTML_UNTIL)) $pte_vals['{$PAGENAV}'] .= '<td><form action="'.PHP_SELF.'?page_num='.$prev.'" method="post">';
else $pte_vals['{$PAGENAV}'] .= '<td><form action="'.$prev.PHP_EXT.'" method="get">';
}
$pte_vals['{$PAGENAV}'] .= '<div><input type="submit" value="'._T('prev_page').'" /></div></form></td>';
}else $pte_vals['{$PAGENAV}'] .= '<td style="white-space: nowrap;">'._T('first_page').'</td>';
$pte_vals['{$PAGENAV}'] .= '<td>';
for($i = 0, $len = $threads_count / PAGE_DEF; $i < $len; $i++){
if($page==$i) $pte_vals['{$PAGENAV}'] .= "[<b>".$i."</b>] ";
else{
$pageNext = ($i==$next) ? ' rel="next"' : '';
if(!$adminMode && $i==0) $pte_vals['{$PAGENAV}'] .= '[<a href="'.PHP_SELF2.'?">0</a>] ';
elseif($adminMode || (STATIC_HTML_UNTIL != -1 && $i > STATIC_HTML_UNTIL)) $pte_vals['{$PAGENAV}'] .= '[<a href="'.PHP_SELF.'?page_num='.$i.'"'.$pageNext.'>'.$i.'</a>] ';
else $pte_vals['{$PAGENAV}'] .= '[<a href="'.$i.PHP_EXT.'?"'.$pageNext.'>'.$i.'</a>] ';
}
}
$pte_vals['{$PAGENAV}'] .= '</td>';
if($threads_count > $next * PAGE_DEF){
if($adminMode || (STATIC_HTML_UNTIL != -1) && ($next > STATIC_HTML_UNTIL)) $pte_vals['{$PAGENAV}'] .= '<td><form action="'.PHP_SELF.'?page_num='.$next.'" method="post">';
else $pte_vals['{$PAGENAV}'] .= '<td><form action="'.$next.PHP_EXT.'" method="get">';
$pte_vals['{$PAGENAV}'] .= '<div><input type="submit" value="'._T('next_page').'" /></div></form></td>';
}else $pte_vals['{$PAGENAV}'] .= '<td style="white-space: nowrap;">'._T('last_page').'</td>';
$pte_vals['{$PAGENAV}'] .= '</tr></table>'."\n";
}
$pte_vals['{$PAGENAV}'] .= '<br style="clear: left;" />
</div>';
$dat .= $PTE->ParseBlock('MAIN', $pte_vals);
foot($dat);
 
// 存檔 / 輸出
if($single_page || ($page_num == -1 && !$resno)){ // 靜態快取頁面生成
if($page==0) $logfilename = PHP_SELF2;
else $logfilename = $page.PHP_EXT;
$fp = fopen($logfilename, 'w');
stream_set_write_buffer($fp, 0);
fwrite($fp, $dat);
fclose($fp);
@chmod($logfilename, 0666);
if(STATIC_HTML_UNTIL != -1 && STATIC_HTML_UNTIL==$page) break; // 頁面數目限制
}else{ // PHP 輸出 (回應模式/一般動態輸出)
if(USE_RE_CACHE && !$adminMode && $resno && !isset($_GET['upseries'])){ // 更新快取
if($oldCaches = glob($cacheFile.'*')){
foreach($oldCaches as $o) unlink($o); // 刪除舊快取
}
$fp = fopen($cacheGzipPrefix.$cacheFile.$cacheETag, 'w');
fwrite($fp, $dat);
fclose($fp);
@chmod($cacheFile.$cacheETag, 0666);
header('ETag: "'.$cacheETag.'"');
header('Connection: close');
}
echo $dat;
break;
}
}
}
 
/* 輸出討論串架構 */
function arrangeThread($PTE, $tree, $tree_cut, $posts, $hiddenReply, $resno=0, $arr_kill, $arr_old, $kill_sensor, $old_sensor, $showquotelink=true, $adminMode=false){
$PIO = PMCLibrary::getPIOInstance();
$FileIO = PMCLibrary::getFileIOInstance();
$PMS = PMCLibrary::getPMSInstance();
 
$thdat = ''; // 討論串輸出碼
$posts_count = count($posts); // 迴圈次數
if(gettype($tree_cut) == 'array') $tree_cut = array_flip($tree_cut); // array_flip + isset 搜尋法
if(gettype($tree) == 'array') $tree_clone = array_flip($tree);
// $i = 0 (首篇), $i = 1~n (回應)
for($i = 0; $i < $posts_count; $i++){
$imgsrc = $img_thumb = $imgwh_bar = '';
$IMG_BAR = $REPLYBTN = $QUOTEBTN = $WARN_OLD = $WARN_BEKILL = $WARN_ENDREPLY = $WARN_HIDEPOST = '';
extract($posts[$i]); // 取出討論串文章內容設定變數
 
// 設定欄位值
$name = str_replace('&'._T('trip_pre'), '&amp;'._T('trip_pre'), $name); // 避免 &#xxxx; 後面被視為 Trip 留下 & 造成解析錯誤
if(CLEAR_SAGE) $email = preg_replace('/^sage( *)/i', '', trim($email)); // 清除E-mail中的「sage」關鍵字
if(ALLOW_NONAME==2){ // 強制砍名
$name = preg_match('/(\\'._T('trip_pre').'.{10})/', $name, $matches) ? '<span class="nor">'.$matches[1].'</span>' : '';
if($email) $now = "<a href=\"mailto:$email\">$now</a>";
}else{
$name = preg_replace('/(\\'._T('trip_pre').'.{10})/', '<span class="nor">$1</span>', $name); // Trip取消粗體
if($email) $name = "<a href=\"mailto:$email\">$name</a>";
}
if(AUTO_LINK) $com = auto_link($com);
$com = quoteLight($com);
if(USE_QUOTESYSTEM && $i){ // 啟用引用瀏覽系統
if(preg_match_all('/((?:&gt;|>)+)(?:No\.)?(\d+)/i', $com, $matches, PREG_SET_ORDER)){ // 找尋>>No.xxx
$matches_unique = array();
foreach($matches as $val){ if(!in_array($val, $matches_unique)) array_push($matches_unique, $val); }
foreach($matches_unique as $val){
if(isset($tree_clone[$val[2]])){
$r_page = $tree_clone[$val[2]]; // 引用回應在整體討論串中的位置
// 在此頁顯示區間內,輸出錨點即可
if(isset($tree_cut[$val[2]])) $com = str_replace($val[0], '<a class="qlink" href="#r'.$val[2].'" onclick="replyhl('.$val[2].');">'.$val[0].'</a>', $com);
// 非此頁顯示區間,輸出完整頁面位置
else $com = str_replace($val[0], '<a class="qlink" href="'.PHP_SELF.'?res='.$tree[0].(RE_PAGE_DEF ? '&amp;page_num='.floor(($r_page - 1) / RE_PAGE_DEF) : '').'#r'.$val[2].'">'.$val[0].'</a>', $com);
}
}
}
}
 
// 設定附加圖檔顯示
if($ext && $FileIO->imageExists($tim.$ext)){
$imageURL = $FileIO->getImageURL($tim.$ext); // image URL
$thumbName = $FileIO->resolveThumbName($tim); // thumb Name
 
$imgsrc = '<a href="'.$imageURL.'" rel="_blank"><img src="nothumb.gif" class="img" alt="'.$imgsize.'" title="'.$imgsize.'" /></a>'; // 預設顯示圖樣式 (無預覽圖時)
if($tw && $th){
if($thumbName != false){ // 有預覽圖
$thumbURL = $FileIO->getImageURL($thumbName); // thumb URL
$img_thumb = '<small>'._T('img_sample').'</small>';
$imgsrc = '<a href="'.$imageURL.'" rel="_blank"><img src="'.$thumbURL.'" style="width: '.$tw.'px; height: '.$th.'px;" class="img" alt="'.$imgsize.'" title="'.$imgsize.'" /></a>';
}elseif($ext=='.swf') $imgsrc = ''; // swf檔案不需預覽圖
}
if(SHOW_IMGWH) $imgwh_bar = ', '.$imgw.'x'.$imgh; // 顯示附加圖檔之原檔長寬尺寸
$IMG_BAR = _T('img_filename').'<a href="'.$imageURL.'" rel="_blank">'.$tim.$ext.'</a>-('.$imgsize.$imgwh_bar.') '.$img_thumb;
}
 
// 設定回應 / 引用連結
if($resno){ // 回應模式
if($showquotelink) $QUOTEBTN = '<a href="javascript:quote('.$no.');" class="qlink">No.'.$no.'</a>';
else $QUOTEBTN = '<a href="'.PHP_SELF.'?res='.$tree.'&amp;page_num=all#r'.$no.'" class="qlink">No.'.$no.'</a>';
}else{
if(!$i) $REPLYBTN = '[<a href="'.PHP_SELF.'?res='.$no.'">'._T('reply_btn').'</a>]'; // 首篇
$QUOTEBTN = '<a href="'.PHP_SELF.'?res='.$tree[0].'#q'.$no.'" class="qlink">No.'.$no.'</a>';
}
if($adminMode){ // 前端管理模式
$modFunc = '';
$PMS->useModuleMethods('AdminList', array(&$modFunc, $posts[$i], $resto)); // "AdminList" Hook Point
$QUOTEBTN .= $modFunc;
}
 
// 設定討論串屬性
if(STORAGE_LIMIT && $kill_sensor) if(isset($arr_kill[$no])) $WARN_BEKILL = '<span class="warn_txt">'._T('warn_sizelimit').'</span><br />'."\n"; // 預測刪除過大檔
if(!$i){ // 首篇 Only
if($old_sensor) if(isset($arr_old[$no])) $WARN_OLD = '<span class="warn_txt">'._T('warn_oldthread').'</span><br />'."\n"; // 快要被刪除的提示
$flgh = $PIO->getPostStatus($status);
if($flgh->exists('TS')) $WARN_ENDREPLY = '<span class="warn_txt">'._T('warn_locked').'</span><br />'."\n"; // 被標記為禁止回應
if($hiddenReply) $WARN_HIDEPOST = '<span class="warn_txt2">'._T('notice_omitted',$hiddenReply).'</span><br />'."\n"; // 有隱藏的回應
}
// 對類別標籤作自動連結
if(USE_CATEGORY){
$ary_category = explode(',', str_replace('&#44;', ',', $category)); $ary_category = array_map('trim', $ary_category);
$ary_category_count = count($ary_category);
$ary_category2 = array();
for($p = 0; $p < $ary_category_count; $p++){
if($c = $ary_category[$p]) $ary_category2[] = '<a href="'.PHP_SELF.'?mode=category&amp;c='.urlencode($c).'">'.$c.'</a>';
}
$category = implode(', ', $ary_category2);
}else $category = '';
 
// 最終輸出處
if($i){ // 回應
$arrLabels = array('{$NO}'=>$no, '{$SUB}'=>$sub, '{$NAME}'=>$name, '{$NOW}'=>$now, '{$CATEGORY}'=>$category, '{$QUOTEBTN}'=>$QUOTEBTN, '{$IMG_BAR}'=>$IMG_BAR, '{$IMG_SRC}'=>$imgsrc, '{$WARN_BEKILL}'=>$WARN_BEKILL, '{$NAME_TEXT}'=>_T('post_name'), '{$CATEGORY_TEXT}'=>_T('post_category'), '{$SELF}'=>PHP_SELF, '{$COM}'=>$com);
if($resno) $arrLabels['{$RESTO}']=$resno;
$PMS->useModuleMethods('ThreadReply', array(&$arrLabels, $posts[$i], $resno)); // "ThreadReply" Hook Point
$thdat .= $PTE->ParseBlock('REPLY',$arrLabels);
}else{ // 首篇
$arrLabels = array('{$NO}'=>$no, '{$SUB}'=>$sub, '{$NAME}'=>$name, '{$NOW}'=>$now, '{$CATEGORY}'=>$category, '{$QUOTEBTN}'=>$QUOTEBTN, '{$REPLYBTN}'=>$REPLYBTN, '{$IMG_BAR}'=>$IMG_BAR, '{$IMG_SRC}'=>$imgsrc, '{$WARN_OLD}'=>$WARN_OLD, '{$WARN_BEKILL}'=>$WARN_BEKILL, '{$WARN_ENDREPLY}'=>$WARN_ENDREPLY, '{$WARN_HIDEPOST}'=>$WARN_HIDEPOST, '{$NAME_TEXT}'=>_T('post_name'), '{$CATEGORY_TEXT}'=>_T('post_category'), '{$SELF}'=>PHP_SELF, '{$COM}'=>$com);
if($resno) $arrLabels['{$RESTO}']=$resno;
$PMS->useModuleMethods('ThreadPost', array(&$arrLabels, $posts[$i], $resno)); // "ThreadPost" Hook Point
$thdat .= $PTE->ParseBlock('THREAD',$arrLabels);
}
}
$thdat .= $PTE->ParseBlock('THREADSEPARATE',($resno)?array('{$RESTO}'=>$resno):array());
return $thdat;
}
 
/* 寫入記錄檔 */
function regist(){
global $BAD_STRING, $BAD_FILEMD5, $BAD_IPADDR, $LIMIT_SENSOR, $THUMB_SETTING;
$PIO = PMCLibrary::getPIOInstance();
$FileIO = PMCLibrary::getFileIOInstance();
$PMS = PMCLibrary::getPMSInstance();
 
$dest = ''; $mes = ''; $up_incomplete = 0; $is_admin = false;
$delta_totalsize = 0; // 總檔案大小的更動值
 
if($_SERVER['REQUEST_METHOD'] != 'POST') error(_T('regist_notpost')); // 非正規POST方式
// 欄位陷阱
$FTname = isset($_POST['name']) ? $_POST['name'] : '';
$FTemail = isset($_POST['email']) ? $_POST['email'] : '';
$FTsub = isset($_POST['sub']) ? $_POST['sub'] : '';
$FTcom = isset($_POST['com']) ? $_POST['com'] : '';
$FTreply = isset($_POST['reply']) ? $_POST['reply'] : '';
if($FTname != 'spammer' || $FTemail != 'foo@foo.bar' || $FTsub != 'DO NOT FIX THIS' || $FTcom != 'EID OG SMAPS' || $FTreply != '') error(_T('regist_nospam'));
 
$name = isset($_POST[FT_NAME]) ? CleanStr($_POST[FT_NAME]) : '';
$email = isset($_POST[FT_EMAIL]) ? CleanStr($_POST[FT_EMAIL]) : '';
$sub = isset($_POST[FT_SUBJECT]) ? CleanStr($_POST[FT_SUBJECT]) : '';
$com = isset($_POST[FT_COMMENT]) ? $_POST[FT_COMMENT] : '';
$pwd = isset($_POST['pwd']) ? $_POST['pwd'] : '';
$category = isset($_POST['category']) ? CleanStr($_POST['category']) : '';
$resto = isset($_POST['resto']) ? intval($_POST['resto']) : 0;
$upfile = isset($_FILES['upfile']['tmp_name']) ? $_FILES['upfile']['tmp_name'] : '';
$upfile_path = isset($_POST['upfile_path']) ? $_POST['upfile_path'] : '';
$upfile_name = isset($_FILES['upfile']['name']) ? $_FILES['upfile']['name'] : false;
$upfile_status = isset($_FILES['upfile']['error']) ? $_FILES['upfile']['error'] : 4;
$pwdc = isset($_COOKIE['pwdc']) ? $_COOKIE['pwdc'] : '';
$ip = getREMOTE_ADDR(); $host = gethostbyaddr($ip);
 
$PMS->useModuleMethods('RegistBegin', array(&$name, &$email, &$sub, &$com, array('file'=>&$upfile, 'path'=>&$upfile_path, 'name'=>&$upfile_name, 'status'=>&$upfile_status), array('ip'=>$ip, 'host'=>$host), $resto)); // "RegistBegin" Hook Point
// 封鎖:IP/Hostname/DNSBL 檢查機能
$baninfo = '';
if(BanIPHostDNSBLCheck($ip, $host, $baninfo)) error(_T('regist_ipfiltered', $baninfo));
// 封鎖:限制出現之文字
foreach($BAD_STRING as $value){
if(strpos($com, $value)!==false || strpos($sub, $value)!==false || strpos($name, $value)!==false || strpos($email, $value)!==false){
error(_T('regist_wordfiltered'));
}
}
 
// 檢查是否輸入櫻花日文假名
foreach(array($name, $email, $sub, $com) as $anti) if(anti_sakura($anti)) error(_T('regist_sakuradetected'));
 
// 時間
$time = time();
$tim = $time.substr(microtime(),2,3);
 
// 判斷上傳狀態
switch($upfile_status){
case 1:
error(_T('regist_upload_exceedphp'));
break;
case 2:
error(_T('regist_upload_exceedcustom'));
break;
case 3:
error(_T('regist_upload_incompelete'));
break;
case 6:
error(_T('regist_upload_direrror'));
break;
case 4: // 無上傳
if(!$resto && !isset($_POST['noimg'])) error(_T('regist_upload_noimg'));
break;
case 0: // 上傳正常
default:
}
 
// 如果有上傳檔案則處理附加圖檔
if($upfile && (@is_uploaded_file($upfile) || @is_file($upfile))){
// 一‧先儲存檔案
$dest = ROOTPATH.$tim.'.tmp';
@move_uploaded_file($upfile, $dest) or @copy($upfile, $dest);
@chmod($dest, 0666);
if(!is_file($dest)) error(_T('regist_upload_filenotfound'), $dest);
 
// 二‧判斷上傳附加圖檔途中是否有中斷
$upsizeTTL = $_SERVER['CONTENT_LENGTH'];
if(isset($_FILES['upfile'])){ // 有傳輸資料才需要計算,避免作白工
$upsizeHDR = 0;
// 檔案路徑:IE附完整路徑,故得從隱藏表單取得
$tmp_upfile_path = $upfile_name;
if($upfile_path) $tmp_upfile_path = get_magic_quotes_gpc() ? stripslashes($upfile_path) : $upfile_path;
list(,$boundary) = explode('=', $_SERVER['CONTENT_TYPE']);
foreach($_POST as $header => $value){ // 表單欄位傳送資料
$upsizeHDR += strlen('--'.$boundary."\r\n");
$upsizeHDR += strlen('Content-Disposition: form-data; name="'.$header.'"'."\r\n\r\n".(get_magic_quotes_gpc()?stripslashes($value):$value)."\r\n");
}
// 附加圖檔欄位傳送資料
$upsizeHDR += strlen('--'.$boundary."\r\n");
$upsizeHDR += strlen('Content-Disposition: form-data; name="upfile"; filename="'.$tmp_upfile_path."\"\r\n".'Content-Type: '.$_FILES['upfile']['type']."\r\n\r\n");
$upsizeHDR += strlen("\r\n--".$boundary."--\r\n");
$upsizeHDR += $_FILES['upfile']['size']; // 傳送附加圖檔資料量
// 上傳位元組差值超過 HTTP_UPLOAD_DIFF:上傳附加圖檔不完全
if(($upsizeTTL - $upsizeHDR) > HTTP_UPLOAD_DIFF){
if(KILL_INCOMPLETE_UPLOAD){
unlink($dest);
die(_T('regist_upload_killincomp')); // 給瀏覽器的提示,假如使用者還看的到的話才不會納悶
}else $up_incomplete = 1;
}
}
 
// 三‧檢查是否為可接受的檔案
$size = @getimagesize($dest);
if(!is_array($size)) error(_T('regist_upload_notimage'), $dest); // $size不為陣列就不是圖檔
$imgsize = @filesize($dest); // 檔案大小
$imgsize = ($imgsize>=1024) ? (int)($imgsize/1024).' KB' : $imgsize.' B'; // KB和B的判別
switch($size[2]){ // 判斷上傳附加圖檔之格式
case 1 : $ext = ".gif"; break;
case 2 : $ext = ".jpg"; break;
case 3 : $ext = ".png"; break;
case 4 : $ext = ".swf"; break;
case 5 : $ext = ".psd"; break;
case 6 : $ext = ".bmp"; break;
case 13 : $ext = ".swf"; break;
default : $ext = ".xxx"; error(_T('regist_upload_notsupport'), $dest);
}
$allow_exts = explode('|', strtolower(ALLOW_UPLOAD_EXT)); // 接受之附加圖檔副檔名
if(array_search(substr($ext, 1), $allow_exts)===false) error(_T('regist_upload_notsupport'), $dest); // 並無在接受副檔名之列
// 封鎖設定:限制上傳附加圖檔之MD5檢查碼
$md5chksum = md5_file($dest); // 檔案MD5
if(array_search($md5chksum, $BAD_FILEMD5)!==FALSE) error(_T('regist_upload_blocked'), $dest); // 在封鎖設定內則阻擋
 
// 四‧計算附加圖檔圖檔縮圖顯示尺寸
$W = $imgW = $size[0];
$H = $imgH = $size[1];
$MAXW = $resto ? MAX_RW : MAX_W;
$MAXH = $resto ? MAX_RH : MAX_H;
if($W > $MAXW || $H > $MAXH){
$W2 = $MAXW / $W;
$H2 = $MAXH / $H;
$key = ($W2 < $H2) ? $W2 : $H2;
$W = ceil($W * $key);
$H = ceil($H * $key);
}
$mes = _T('regist_uploaded', CleanStr($upfile_name));
}
 
// 檢查表單欄位內容並修整
if(strlen($name) > 100) error(_T('regist_nametoolong'), $dest);
if(strlen($email) > 100) error(_T('regist_emailtoolong'), $dest);
if(strlen($sub) > 100) error(_T('regist_topictoolong'), $dest);
if(strlen($resto) > 10) error(_T('regist_longthreadnum'), $dest);
 
// E-mail / 標題修整
$email = str_replace("\r\n", '', $email); $sub = str_replace("\r\n", '', $sub);
// 名稱修整
$name = str_replace(_T('trip_pre'), _T('trip_pre_fake'), $name); // 防止トリップ偽造
$name = str_replace(CAP_SUFFIX, _T('cap_char_fake'), $name); // 防止管理員キャップ偽造
$name = str_replace("\r\n", '', $name);
$nameOri = $name; // 名稱
if(preg_match('/(.*?)[##](.*)/u', $name, $regs)){ // トリップ(Trip)機能
$name = $nameOri = $regs[1]; $cap = strtr($regs[2], array('&amp;'=>'&'));
$salt = preg_replace('/[^\.-z]/', '.', substr($cap.'H.', 1, 2));
$salt = strtr($salt, ':;<=>?@[\\]^_`', 'ABCDEFGabcdef');
$name = $name._T('trip_pre').substr(crypt($cap, $salt), -10);
}
if(CAP_ENABLE && preg_match('/(.*?)[##](.*)/', $email, $aregs)){ // 管理員キャップ(Cap)機能
$acap_name = $nameOri; $acap_pwd = strtr($aregs[2], array('&amp;'=>'&'));
if($acap_name==CAP_NAME && $acap_pwd==CAP_PASS){
$name = '<span class="admin_cap">'.$name.CAP_SUFFIX.'</span>';
$is_admin = true;
$email = $aregs[1]; // 去除 #xx 密碼
}
}
if(!$is_admin){ // 非管理員
$name = str_replace(_T('admin'), '"'._T('admin').'"', $name);
$name = str_replace(_T('deletor'), '"'._T('deletor').'"', $name);
}
$name = str_replace('&'._T('trip_pre'), '&amp;'._T('trip_pre'), $name); // 避免 &#xxxx; 後面被視為 Trip 留下 & 造成解析錯誤
// 內文修整
if((strlen($com) > COMM_MAX) && !$is_admin) error(_T('regist_commenttoolong'), $dest);
$com = CleanStr($com, $is_admin); // 引入$is_admin參數是因為當管理員キャップ啟動時,允許管理員依config設定是否使用HTML
if(!$com && $upfile_status==4) error(_T('regist_withoutcomment'));
$com = str_replace(array("\r\n", "\r"), "\n", $com); $com = preg_replace("/\n(( | )*\n){3,}/", "\n", $com);
if(!BR_CHECK || substr_count($com,"\n") < BR_CHECK) $com = nl2br($com); // 換行字元用<br />代替
$com = str_replace("\n", '', $com); // 若還有\n換行字元則取消換行
// 預設的內容
if(!$name || preg_match("/^[ | |]*$/", $name)){
if(ALLOW_NONAME) $name = DEFAULT_NONAME;
else error(_T('regist_withoutname'), $dest);
}
if(!$sub || preg_match("/^[ | |]*$/", $sub)) $sub = DEFAULT_NOTITLE;
if(!$com || preg_match("/^[ | |\t]*$/", $com)) $com = DEFAULT_NOCOMMENT;
// 修整標籤樣式
if($category && USE_CATEGORY){
$category = explode(',', $category); // 把標籤拆成陣列
$category = ','.implode(',', array_map('trim', $category)).','; // 去空白再合併為單一字串 (左右含,便可以直接以,XX,形式搜尋)
}else{ $category = ''; }
if($up_incomplete) $com .= '<br /><br /><span class="warn_txt">'._T('notice_incompletefile').'</span>'; // 上傳附加圖檔不完全的提示
 
// 密碼和時間的樣式
if($pwd=='') $pwd = ($pwdc=='') ? substr(rand(),0,8) : $pwdc;
$pass = $pwd ? substr(md5($pwd), 2, 8) : '*'; // 生成真正儲存判斷用的密碼
$youbi = array(_T('sun'),_T('mon'),_T('tue'),_T('wed'),_T('thu'),_T('fri'),_T('sat'));
$yd = $youbi[gmdate('w', $time+TIME_ZONE*60*60)];
$now = gmdate('y/m/d', $time+TIME_ZONE*60*60).'('.(string)$yd.')'.gmdate('H:i', $time+TIME_ZONE*60*60);
if(DISP_ID){ // 顯示ID
if($email && DISP_ID==1) $now .= ' ID:???';
else $now .= ' ID:'.substr(crypt(md5(getREMOTE_ADDR().IDSEED.gmdate('Ymd', $time+TIME_ZONE*60*60)),'id'), -8);
}
 
// 連續投稿 / 相同附加圖檔檢查
$checkcount = 50; // 預設檢查50筆資料
$pwdc = substr(md5($pwdc), 2, 8); // Cookies密碼
if($PIO->isSuccessivePost($checkcount, $com, $time, $pass, $pwdc, $host, $upfile_name)) error(_T('regist_successivepost'), $dest); // 連續投稿檢查
if($dest){ if($PIO->isDuplicateAttachment($checkcount, $md5chksum)) error(_T('regist_duplicatefile'), $dest); } // 相同附加圖檔檢查
if($resto) $ThreadExistsBefore = $PIO->isThread($resto);
 
// 舊文章刪除處理
if(PIOSensor::check('delete', $LIMIT_SENSOR)){
$delarr = PIOSensor::listee('delete', $LIMIT_SENSOR);
if(count($delarr)){
deleteCache($delarr);
$PMS->useModuleMethods('PostOnDeletion', array($delarr, 'recycle')); // "PostOnDeletion" Hook Point
$files = $PIO->removePosts($delarr);
if(count($files)) $delta_totalsize -= $FileIO->deleteImage($files); // 更新 delta 值
}
}
 
// 附加圖檔容量限制功能啟動:刪除過大檔
if(STORAGE_LIMIT && STORAGE_MAX > 0){
$tmp_total_size = $FileIO->getCurrentStorageSize(); // 取得目前附加圖檔使用量
if($tmp_total_size > STORAGE_MAX){
$files = $PIO->delOldAttachments($tmp_total_size, STORAGE_MAX, false);
$delta_totalsize -= $FileIO->deleteImage($files);
}
}
 
// 判斷欲回應的文章是不是剛剛被刪掉了
if($resto){
if($ThreadExistsBefore){ // 欲回應的討論串是否存在
if(!$PIO->isThread($resto)){ // 被回應的討論串存在但已被刪
// 提前更新資料來源,此筆新增亦不紀錄
$PIO->dbCommit();
updatelog();
error(_T('regist_threaddeleted'), $dest);
}else{ // 檢查是否討論串被設為禁止回應 (順便取出原討論串的貼文時間)
$post = $PIO->fetchPosts($resto); // [特殊] 取單篇文章內容,但是回傳的$post同樣靠[$i]切換文章!
list($chkstatus, $chktime) = array($post[0]['status'], $post[0]['tim']);
$chktime = substr($chktime, 0, -3); // 拿掉微秒 (後面三個字元)
$flgh = $PIO->getPostStatus($chkstatus);
if($flgh->exists('TS')) error(_T('regist_threadlocked'), $dest);
}
}else error(_T('thread_not_found'), $dest); // 不存在
}
 
// 計算某些欄位值
$no = $PIO->getLastPostNo('beforeCommit') + 1;
isset($ext) ? 0 : $ext = '';
isset($imgW) ? 0 : $imgW = 0;
isset($imgH) ? 0 : $imgH = 0;
isset($imgsize) ? 0 : $imgsize = '';
isset($W) ? 0 : $W = 0;
isset($H) ? 0 : $H = 0;
isset($md5chksum) ? 0 : $md5chksum = '';
$age = false;
$status = '';
if($resto){
if(!stristr($email, 'sage') && ($PIO->postCount($resto) <= MAX_RES || MAX_RES==0)){
if(!MAX_AGE_TIME || (($time - $chktime) < (MAX_AGE_TIME * 60 * 60))) $age = true; // 討論串並無過期,推文
}
}
$PMS->useModuleMethods('RegistBeforeCommit', array(&$name, &$email, &$sub, &$com, &$category, &$age, $dest, $resto, array($W, $H, $imgW, $imgH), &$status)); // "RegistBeforeCommit" Hook Point
 
// 正式寫入儲存
$PIO->addPost($no,$resto,$md5chksum,$category,$tim,$ext,$imgW,$imgH,$imgsize,$W,$H,$pass,$now,$name,$email,$sub,$com,$host,$age,$status);
$PIO->dbCommit();
$lastno = $PIO->getLastPostNo('afterCommit'); // 取得此新文章編號
$PMS->useModuleMethods('RegistAfterCommit', array($lastno, $resto, $name, $email, $sub, $com)); // "RegistAfterCommit" Hook Point
 
// Cookies儲存:密碼與E-mail部分,期限是一週
setcookie('pwdc', $pwd, time()+7*24*3600);
setcookie('emailc', $email, time()+7*24*3600);
if($dest && is_file($dest)){
$destFile = ROOTPATH.IMG_DIR.$tim.$ext; // 圖檔儲存位置
$thumbFile = ROOTPATH.THUMB_DIR.$tim.'s.'.$THUMB_SETTING['Format']; // 預覽圖儲存位置
if(USE_THUMB !== 0){ // 生成預覽圖
$thumbType = USE_THUMB; if(USE_THUMB==1){ $thumbType = 'gd'; } // 與舊設定相容
require(ROOTPATH.'lib/thumb/thumb.'.$thumbType.'.php');
$thObj = new ThumbWrapper($dest, $imgW, $imgH);
$thObj->setThumbnailConfig($W, $H, $THUMB_SETTING);
$thObj->makeThumbnailtoFile($thumbFile);
@chmod($thumbFile, 0666);
unset($thObj);
}
rename($dest, $destFile);
if(file_exists($destFile)){
$FileIO->uploadImage($tim.$ext, $destFile, filesize($destFile));
$delta_totalsize += filesize($destFile);
}
if(file_exists($thumbFile)){
$FileIO->uploadImage($tim.'s.'.$THUMB_SETTING['Format'], $thumbFile, filesize($thumbFile));
$delta_totalsize += filesize($thumbFile);
}
}
// delta != 0 表示總檔案大小有更動,須更新快取
if($delta_totalsize != 0){
$FileIO->updateStorageSize($delta_totalsize);
}
updatelog();
 
// 引導使用者至新頁面
$RedirURL = PHP_SELF2.'?'.$tim; // 定義儲存資料後轉址目標
if(isset($_POST['up_series'])){ // 勾選連貼機能
if($resto) $RedirURL = PHP_SELF.'?res='.$resto.'&amp;upseries=1'; // 回應後繼續轉回此主題下
else{
$RedirURL = PHP_SELF.'?res='.$lastno.'&amp;upseries=1'; // 新增主題後繼續轉到此主題下
}
}
$RedirforJS = strtr($RedirURL, array("&amp;"=>"&")); // JavaScript用轉址目標
 
echo '<?xml version="1.0" encoding="UTF-8"?>'."\n";
echo <<< _REDIR_
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh-tw">
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="Refresh" content="1;URL=$RedirURL" />
<script type="text/javascript">
// Redirection (use JS)
// <![CDATA[
function redir(){
location.href = "$RedirforJS";
}
setTimeout("redir()", 1000);
// ]]>
</script>
</head>
<body>
<div>
_REDIR_;
echo _T('regist_redirect',$mes,$RedirURL).'</div>
</body>
</html>';
}
 
/* 使用者刪除 */
function usrdel(){
$PIO = PMCLibrary::getPIOInstance();
$FileIO = PMCLibrary::getFileIOInstance();
$PMS = PMCLibrary::getPMSInstance();
 
// $pwd: 使用者輸入值, $pwdc: Cookie記錄密碼
$pwd = isset($_POST['pwd']) ? $_POST['pwd'] : '';
$pwdc = isset($_COOKIE['pwdc']) ? $_COOKIE['pwdc'] : '';
$onlyimgdel = isset($_POST['onlyimgdel']) ? $_POST['onlyimgdel'] : '';
$delno = array();
reset($_POST);
while($item = each($_POST)){ if($item[1]=='delete' && $item[0] != 'func') array_push($delno, $item[0]); }
$haveperm = ($pwd==ADMIN_PASS) || adminAuthenticate('check');
$PMS->useModuleMethods('Authenticate', array($pwd,'userdel',&$haveperm));
if($haveperm && isset($_POST['func'])){ // 前端管理功能
$message = '';
$PMS->useModuleMethods('AdminFunction', array('run', &$delno, $_POST['func'], &$message)); // "AdminFunction" Hook Point
if($_POST['func'] != 'delete'){
if(isset($_SERVER['HTTP_REFERER'])){
header('HTTP/1.1 302 Moved Temporarily');
header('Location: '.$_SERVER['HTTP_REFERER']);
}
exit(); // 僅執行AdminFunction,終止刪除動作
}
}
 
if($pwd=='' && $pwdc!='') $pwd = $pwdc;
$pwd_md5 = substr(md5($pwd),2,8);
$host = gethostbyaddr(getREMOTE_ADDR());
$search_flag = $delflag = false;
 
if(!count($delno)) error(_T('del_notchecked'));
 
$delposts = array(); // 真正符合刪除條件文章
$posts = $PIO->fetchPosts($delno);
foreach($posts as $post){
if($pwd_md5==$post['pwd'] || $host==$post['host'] || $haveperm){
$search_flag = true; // 有搜尋到
array_push($delposts, $post['no']);
}
}
if($search_flag){
if(!$onlyimgdel) $PMS->useModuleMethods('PostOnDeletion', array($delposts, 'frontend')); // "PostOnDeletion" Hook Point
$files = $onlyimgdel ? $PIO->removeAttachments($delposts) : $PIO->removePosts($delposts);
$FileIO->updateStorageSize(-$FileIO->deleteImage($files)); // 更新容量快取
deleteCache($delposts);
$PIO->dbCommit();
}else error(_T('del_wrongpwornotfound'));
if(isset($_POST['func']) && $_POST['func'] == 'delete'){ // 前端管理刪除文章返回管理頁面
if(isset($_SERVER['HTTP_REFERER'])){
header('HTTP/1.1 302 Moved Temporarily');
header('Location: '.$_SERVER['HTTP_REFERER']);
}
exit();
}
}
 
/* 管理員密碼認證 */
function valid(){
$PMS = PMCLibrary::getPMSInstance();
 
$pass = isset($_POST['pass']) ? $_POST['pass'] : ''; // 管理者密碼
$haveperm = false;
$isCheck = adminAuthenticate('check'); // 登入是否正確
if(!$isCheck && $pass){
$haveperm = ($pass == ADMIN_PASS);
$PMS->useModuleMethods('Authenticate', array($pass,'admin',&$haveperm));
if($haveperm){ adminAuthenticate('login'); $isCheck = true; }
else error(_T('admin_wrongpassword'));
}
$dat = '';
head($dat);
$links = '[<a href="'.PHP_SELF2.'?'.time().'">'._T('return').'</a>] [<a href="'.PHP_SELF.'?mode=remake">'._T('admin_remake').'</a>] [<a href="'.PHP_SELF.'?page_num=0">'._T('admin_frontendmanage').'</a>]';
$PMS->useModuleMethods('LinksAboveBar', array(&$links,'admin',$isCheck)); // LinksAboveBar hook point
$dat .= '<div id="banner">'.$links.'<div class="bar_admin">'._T('admin_top').'</div>
</div>
<form action="'.PHP_SELF.'" method="post" name="adminform">
<div id="admin-check" style="text-align: center;">
';
echo $dat;
if(!$isCheck){
echo '<br />
<input type="radio" name="admin" value="del" checked="checked" />'._T('admin_manageposts').'
<input type="radio" name="admin" value="optimize" />'._T('admin_optimize').'
<input type="radio" name="admin" value="check" />'._T('admin_check').'
<input type="radio" name="admin" value="repair" />'._T('admin_repair').'
<input type="radio" name="admin" value="export" />'._T('admin_export').'<p />
<input type="hidden" name="mode" value="admin" />
<input type="password" name="pass" size="8" />
<input type="submit" value="'._T('admin_verify_btn').'" />
</div>
</form>';
die("\n</body>\n</html>");
}elseif(!isset($_REQUEST['admin'])){
echo '<br />
<input type="radio" name="admin" value="del" checked="checked" />'._T('admin_manageposts').'
<input type="radio" name="admin" value="optimize" />'._T('admin_optimize').'
<input type="radio" name="admin" value="check" />'._T('admin_check').'
<input type="radio" name="admin" value="repair" />'._T('admin_repair').'
<input type="radio" name="admin" value="export" />'._T('admin_export').'
<input type="radio" name="admin" value="logout" />'._T('admin_logout').'<p />
<input type="hidden" name="mode" value="admin" />
<input type="submit" value="'._T('admin_submit_btn').'" />
</div>
</form>';
die("\n</body>\n</html>");
}
}
 
/* 管理文章模式 */
function admindel(){
$PIO = PMCLibrary::getPIOInstance();
$FileIO = PMCLibrary::getFileIOInstance();
$PMS = PMCLibrary::getPMSInstance();
 
$pass = isset($_POST['pass']) ? $_POST['pass'] : ''; // 管理者密碼
$page = isset($_REQUEST['page']) ? $_REQUEST['page'] : 0; // 切換頁數
$onlyimgdel = isset($_POST['onlyimgdel']) ? $_POST['onlyimgdel'] : ''; // 只刪圖
$modFunc = '';
$delno = $thsno = array();
$delflag = isset($_POST['func']) && ($_POST['func'] == 'delete') && isset($_POST['clist']); // 是否有「刪除」勾選
$thsflag = isset($_POST['stop']); // 是否有「停止」勾選
$is_modified = false; // 是否改寫檔案
$message = ''; // 操作後顯示訊息
 
if(isset($_POST['func']) && isset($_POST['clist']))
$PMS->useModuleMethods('AdminFunction', array('run', &$_POST['clist'], $_POST['func'], &$message)); // "AdminFunction" Hook Point
 
// 刪除文章區塊
if($delflag){
$delno = array_merge($delno, $_POST['clist']);
if($onlyimgdel != 'on') $PMS->useModuleMethods('PostOnDeletion', array($delno, 'backend')); // "PostOnDeletion" Hook Point
$files = ($onlyimgdel != 'on') ? $PIO->removePosts($delno) : $PIO->removeAttachments($delno);
$FileIO->updateStorageSize(-$FileIO->deleteImage($files));
deleteCache($delno);
$is_modified = true;
}
// 討論串停止區塊
if($thsflag){
$thsno = array_merge($thsno, $_POST['stop']);
$threads = $PIO->fetchPosts($thsno); // 取得文章
foreach($threads as $th){
$flgh = $PIO->getPostStatus($th['status']);
$flgh->toggle('TS');
$PIO->setPostStatus($th['no'], $flgh->toString());
}
$is_modified = true;
}
if(($delflag || $thsflag) && $is_modified) $PIO->dbCommit(); // 無論如何都有檔案操作,回寫檔案
 
$line = $PIO->fetchPostList(0, $page * ADMIN_PAGE_DEF, ADMIN_PAGE_DEF); // 分頁過的文章列表
$posts_count = count($line); // 迴圈次數
$posts = $PIO->fetchPosts($line); // 文章內容陣列
 
echo '<input type="hidden" name="mode" value="admin" />
<input type="hidden" name="admin" value="del" />
<div style="text-align: left;">'._T('admin_notices').'</div>
<div>'.$message.'</div>
<table border="1" cellspacing="0" style="margin: 0px auto;">
<tr style="background-color: #6080f6;">'._T('admin_list_header').'</tr>
';
 
for($j = 0; $j < $posts_count; $j++){
$bg = ($j % 2) ? 'ListRow1_bg' : 'ListRow2_bg'; // 背景顏色
extract($posts[$j]);
 
// 修改欄位樣式
$now = preg_replace('/.{2}\/(.{5})\(.+?\)(.{5}).*/', '$1 $2', $now);
$name = htmlspecialchars(str_cut(html_entity_decode(strip_tags($name)), 8));
$sub = htmlspecialchars(str_cut(html_entity_decode($sub), 8));
if($email) $name = "<a href=\"mailto:$email\">$name</a>";
$com = str_replace('<br />',' ',$com);
$com = htmlspecialchars(str_cut(html_entity_decode($com), 20));
 
// 討論串首篇停止勾選框 及 模組功能
$modFunc = $THstop = ' ';
$PMS->useModuleMethods('AdminList', array(&$modFunc, $posts[$j], $resto)); // "AdminList" Hook Point
if($resto==0){ // $resto = 0 (即討論串首篇)
$flgh = $PIO->getPostStatus($status);
$THstop = '<input type="checkbox" name="stop[]" value="'.$no.'" />'.($flgh->exists('TS') ? _T('admin_stop_btn') : '');
}
 
// 從記錄抽出附加圖檔使用量並生成連結
if($ext && $FileIO->imageExists($tim.$ext)){
$clip = '<a href="'.$FileIO->getImageURL($tim.$ext).'" rel="_blank">'.$tim.$ext.'</a>';
$size = $FileIO->getImageFilesize($tim.$ext);
$thumbName = $FileIO->resolveThumbName($tim);
if($thumbName != false) $size += $FileIO->getImageFilesize($thumbName);
}else{
$clip = $md5chksum = '--';
$size = 0;
}
 
// 印出介面
echo <<< _ADMINEOF_
<tr class="$bg" align="left">
<th align="center">$modFunc</th><th align="center">$THstop</th><th><input type="checkbox" name="clist[]" value="$no" />$no</th><td><small>$now</small></td><td>$sub</td><td><b>$name</b></td><td><small>$com</small></td><td>$host</td><td align="center">$clip ($size)<br />$md5chksum</td>
</tr>
 
_ADMINEOF_;
}
echo '</table>
<p>
<select name="func"><option value="delete">'._T('admin_delete').'</option>';
$funclist = array();
$dummy = '';
$PMS->useModuleMethods('AdminFunction', array('add', &$funclist, null, &$dummy)); // "AdminFunction" Hook Point
foreach($funclist as $f) echo '<option value="'.$f[0].'">'.$f[1].'</option>';
echo '</select>
<input type="submit" value="'._T('admin_submit_btn').'" /> <input type="reset" value="'._T('admin_reset_btn').'" /> [<input type="checkbox" name="onlyimgdel" id="onlyimgdel" value="on" /><label for="onlyimgdel">'._T('del_img_only').'</label>]</p>
<p>'._T('admin_totalsize', $FileIO->getCurrentStorageSize()).'</p>
</div>
</form>
<hr />
';
 
$countline = $PIO->postCount(); // 總文章數
$page_max = ceil($countline / ADMIN_PAGE_DEF) - 1; // 總頁數
echo '<table border="1" style="float: left;"><tr>';
if($page) echo '<td><a href="'.PHP_SELF.'?mode=admin&amp;admin=del&amp;page='.($page - 1).'">'._T('prev_page').'</a></td>';
else echo '<td style="white-space: nowrap;">'._T('first_page').'</td>';
echo '<td>';
for($i = 0; $i <= $page_max; $i++){
if($i==$page) echo '[<b>'.$i.'</b>] ';
else echo '[<a href="'.PHP_SELF.'?mode=admin&amp;admin=del&amp;page='.$i.'">'.$i.'</a>] ';
}
echo '</td>';
if($page < $page_max) echo '<td><a href="'.PHP_SELF.'?mode=admin&amp;admin=del&amp;page='.($page + 1).'">'._T('next_page').'</a></td>';
else echo '<td style="white-space: nowrap;">'._T('last_page').'</td>';
die('</tr></table><br/><br/>
</body>
</html>');
}
 
/**
* 計算目前附加圖檔使用容量 (單位:KB)
* @deprecated Use FileIO->getCurrentStorageSize() / FileIO->updateStorageSize($delta) instead
*/
function total_size($delta=0){
$FileIO = PMCLibrary::getFileIOInstance();
return $FileIO->getCurrentStorageSize($delta);
}
 
/* 搜尋(全文檢索)功能 */
function search(){
$PIO = PMCLibrary::getPIOInstance();
$FileIO = PMCLibrary::getFileIOInstance();
$PTE = PMCLibrary::getPTEInstance();
$PMS = PMCLibrary::getPMSInstance();
 
if(!USE_SEARCH) error(_T('search_disabled'));
$searchKeyword = isset($_POST['keyword']) ? trim($_POST['keyword']) : ''; // 欲搜尋的文字
$dat = '';
head($dat);
$links = '[<a href="'.PHP_SELF2.'?'.time().'">'._T('return').'</a>]';
$PMS->useModuleMethods('LinksAboveBar', array(&$links,'search'));
$dat .= '<div id="banner">'.$links.'<div class="bar_admin">'._T('search_top').'</div>
</div>
';
echo $dat;
if($searchKeyword==''){
echo '<form action="'.PHP_SELF.'" method="post">
<div id="search">
<input type="hidden" name="mode" value="search" />
';
echo '<ul>'._T('search_notice').'<input type="text" name="keyword" size="30" />
'._T('search_target').'<select name="field"><option value="com" selected="selected">'._T('search_target_comment').'</option><option value="name">'._T('search_target_name').'</option><option value="sub">'._T('search_target_topic').'</option><option value="no">'._T('search_target_number').'</option></select>
'._T('search_method').'<select name="method"><option value="AND" selected="selected">'._T('search_method_and').'</option><option value="OR">'._T('search_method_or').'</option></select>
<input type="submit" value="'._T('search_submit_btn').'" />
</li>
</ul>
</div>
</form>';
}else{
$searchField = $_POST['field']; // 搜尋目標 (no:編號, name:名稱, sub:標題, com:內文)
$searchMethod = $_POST['method']; // 搜尋方法
$searchKeyword = preg_split('/( | )+/', trim($searchKeyword)); // 搜尋文字用空格切割
$hitPosts = $PIO->searchPost($searchKeyword, $searchField, $searchMethod); // 直接傳回符合的文章內容陣列
 
echo '<div id="search_result">
';
$resultlist = '';
foreach($hitPosts as $post){
extract($post);
if(USE_CATEGORY){
$ary_category = explode(',', str_replace('&#44;', ',', $category)); $ary_category = array_map('trim', $ary_category);
$ary_category_count = count($ary_category);
$ary_category2 = array();
for($p = 0; $p < $ary_category_count; $p++){
if($c = $ary_category[$p]) $ary_category2[] = '<a href="'.PHP_SELF.'?mode=category&amp;c='.urlencode($c).'">'.$c.'</a>';
}
$category = implode(', ', $ary_category2);
}else $category = '';
$arrLabels = array('{$NO}'=>'<a href="'.PHP_SELF.'?res='.($resto?$resto.'#r'.$no:$no).'">'.$no.'</a>', '{$SUB}'=>$sub, '{$NAME}'=>$name, '{$NOW}'=>$now, '{$COM}'=>$com, '{$CATEGORY}'=>$category, '{$NAME_TEXT}'=>_T('post_name'), '{$CATEGORY_TEXT}'=>_T('post_category'));
$resultlist .= $PTE->ParseBlock('SEARCHRESULT',$arrLabels);
}
echo $resultlist ? $resultlist : '<div style="text-align: center">'._T('search_notfound').'<br/><a href="?mode=search">'._T('search_back').'</a></div>';
echo "</div>";
}
echo "</body>\n</html>";
}
 
/* 利用類別標籤搜尋符合的文章 */
function searchCategory(){
$PIO = PMCLibrary::getPIOInstance();
$FileIO = PMCLibrary::getFileIOInstance();
$PTE = PMCLibrary::getPTEInstance();
$PMS = PMCLibrary::getPMSInstance();
 
$category = isset($_GET['c']) ? strtolower(strip_tags(trim($_GET['c']))) : ''; // 搜尋之類別標籤
if(!$category) error(_T('category_nokeyword'));
$category_enc = urlencode($category); $category_md5 = md5($category);
$page = isset($_GET['p']) ? @intval($_GET['p']) : 1; if($page < 1) $page = 1; // 目前瀏覽頁數
$isrecache = isset($_GET['recache']); // 是否強制重新生成快取
 
// 利用Session快取類別標籤出現篇別以減少負擔
session_start(); // 啟動Session
if(!isset($_SESSION['loglist_'.$category_md5]) || $isrecache){
$loglist = $PIO->searchCategory($category);
$_SESSION['loglist_'.$category_md5] = serialize($loglist);
}else $loglist = unserialize($_SESSION['loglist_'.$category_md5]);
 
$loglist_count = count($loglist);
if(!$loglist_count) error(_T('category_notfound'));
$page_max = ceil($loglist_count / PAGE_DEF); if($page > $page_max) $page = $page_max; // 總頁數
 
// 分割陣列取出適當範圍作分頁之用
$loglist_cut = array_slice($loglist, PAGE_DEF * ($page - 1), PAGE_DEF); // 取出特定範圍文章
$loglist_cut_count = count($loglist_cut);
 
$dat = '';
head($dat);
$links = '[<a href="'.PHP_SELF2.'?'.time().'">'._T('return').'</a>][<a href="'.PHP_SELF.'?mode=category&amp;c='.$category_enc.'&amp;recache=1">'._T('category_recache').'</a>]';
$PMS->useModuleMethods('LinksAboveBar', array(&$links,'category'));
$dat .= "<div>$links</div>\n";
for($i = 0; $i < $loglist_cut_count; $i++){
$posts = $PIO->fetchPosts($loglist_cut[$i]); // 取得文章內容
$dat .= arrangeThread($PTE, ($posts[0]['resto'] ? $posts[0]['resto'] : $posts[0]['no']), null, $posts, 0, $loglist_cut[$i], array(), array(), false, false, false); // 逐個輸出 (引用連結不顯示)
}
 
$dat .= '<table border="1"><tr>';
if($page > 1) $dat .= '<td><form action="'.PHP_SELF.'?mode=category&amp;c='.$category_enc.'&amp;p='.($page - 1).'" method="post"><div><input type="submit" value="'._T('prev_page').'" /></div></form></td>';
else $dat .= '<td style="white-space: nowrap;">'._T('first_page').'</td>';
$dat .= '<td>';
for($i = 1; $i <= $page_max ; $i++){
if($i==$page) $dat .= "[<b>".$i."</b>] ";
else $dat .= '[<a href="'.PHP_SELF.'?mode=category&amp;c='.$category_enc.'&amp;p='.$i.'">'.$i.'</a>] ';
}
$dat .= '</td>';
if($page < $page_max) $dat .= '<td><form action="'.PHP_SELF.'?mode=category&amp;c='.$category_enc.'&amp;p='.($page + 1).'" method="post"><div><input type="submit" value="'._T('next_page').'" /></div></form></td>';
else $dat .= '<td style="white-space: nowrap;">'._T('last_page').'</td>';
$dat .= '</tr></table>'."\n";
 
foot($dat);
echo $dat;
}
 
/* 顯示已載入模組資訊 */
function listModules(){
$PMS = PMCLibrary::getPMSInstance();
 
$dat = '';
head($dat);
$links = '[<a href="'.PHP_SELF2.'?'.time().'">'._T('return').'</a>]';
$PMS->useModuleMethods('LinksAboveBar', array(&$links,'modules'));
$dat .= '<div id="banner">'.$links.'<div class="bar_admin">'._T('module_info_top').'</div>
</div>
 
<div id="modules">
';
/* Module Loaded */
$dat .= _T('module_loaded').'<ul>'."\n";
foreach($PMS->getLoadedModules() as $m) $dat .= '<li>'.$m."</li>\n";
$dat .= "</ul><hr />\n";
 
/* Module Infomation */
$dat .= _T('module_info').'<ul>'."\n";
foreach($PMS->moduleInstance as $m) $dat .= '<li>'.$m->getModuleName().'<div style="padding-left:2em;">'.$m->getModuleVersionInfo()."</div></li>\n";
$dat .= '</ul><hr />
</div>
 
';
foot($dat);
echo $dat;
}
 
/* 刪除舊頁面快取檔 */
function deleteCache($no){
foreach($no as $n){
if($oldCaches = glob('./cache/'.$n.'-*')){
foreach($oldCaches as $o) @unlink($o);
}
}
}
 
/* 顯示系統各項資訊 */
function showstatus(){
global $LIMIT_SENSOR, $THUMB_SETTING;
$PIO = PMCLibrary::getPIOInstance();
$FileIO = PMCLibrary::getFileIOInstance();
$PTE = PMCLibrary::getPTEInstance();
$PMS = PMCLibrary::getPMSInstance();
 
$countline = $PIO->postCount(); // 計算投稿文字記錄檔目前資料筆數
$counttree = $PIO->threadCount(); // 計算樹狀結構記錄檔目前資料筆數
$tmp_total_size = $FileIO->getCurrentStorageSize(); // 附加圖檔使用量總大小
$tmp_ts_ratio = STORAGE_MAX > 0 ? $tmp_total_size / STORAGE_MAX : 0; // 附加圖檔使用量
 
// 決定「附加圖檔使用量」提示文字顏色
if($tmp_ts_ratio < 0.3 ) $clrflag_sl = '235CFF';
elseif($tmp_ts_ratio < 0.5 ) $clrflag_sl = '0CCE0C';
elseif($tmp_ts_ratio < 0.7 ) $clrflag_sl = 'F28612';
elseif($tmp_ts_ratio < 0.9 ) $clrflag_sl = 'F200D3';
else $clrflag_sl = 'F2004A';
 
// 生成預覽圖物件資訊及功能是否正常
$func_thumbWork = '<span style="color: red;">'._T('info_nonfunctional').'</span>';
$func_thumbInfo = '(No thumbnail)';
if(USE_THUMB !== 0){
$thumbType = USE_THUMB; if(USE_THUMB==1){ $thumbType = 'gd'; }
require(ROOTPATH.'lib/thumb/thumb.'.$thumbType.'.php');
$thObj = new ThumbWrapper();
if($thObj->isWorking()) $func_thumbWork = '<span style="color: blue;">'._T('info_functional').'</span>';
$func_thumbInfo = $thObj->getClass();
unset($thObj);
}
 
// PIOSensor
if(count($LIMIT_SENSOR))
$piosensorInfo=nl2br(PIOSensor::info($LIMIT_SENSOR));
 
$dat = '';
head($dat);
$links = '[<a href="'.PHP_SELF2.'?'.time().'">'._T('return').'</a>] [<a href="'.PHP_SELF.'?mode=moduleloaded">'._T('module_info_top').'</a>]';
$PMS->useModuleMethods('LinksAboveBar', array(&$links,'status'));
$dat .= '<div id="banner">'.$links.'<div class="bar_admin">'._T('info_top').'</div>
</div>
';
 
$dat .= '
<div id="status-table" style="text-align: center;">
<table border="1" style="margin: 0px auto; text-align: left;">
<tr><td align="center" colspan="4">'._T('info_basic').'</td></tr>
<tr><td style="width: 240px;">'._T('info_basic_ver').'</td><td colspan="3"> '.PIXMICAT_VER.' </td></tr>
<tr><td>'._T('info_basic_pio').'</td><td colspan="3"> '.PIXMICAT_BACKEND.' : '.$PIO->pioVersion().'</td></tr>
<tr><td>'._T('info_basic_threadsperpage').'</td><td colspan="3"> '.PAGE_DEF.' '._T('info_basic_threads').'</td></tr>
<tr><td>'._T('info_basic_postsperpage').'</td><td colspan="3"> '.RE_DEF.' '._T('info_basic_posts').'</td></tr>
<tr><td>'._T('info_basic_postsinthread').'</td><td colspan="3"> '.RE_PAGE_DEF.' '._T('info_basic_posts').' '._T('info_basic_posts_showall').'</td></tr>
<tr><td>'._T('info_basic_bumpposts').'</td><td colspan="3"> '.MAX_RES.' '._T('info_basic_posts').' '._T('info_basic_0disable').'</td></tr>
<tr><td>'._T('info_basic_bumphours').'</td><td colspan="3"> '.MAX_AGE_TIME.' '._T('info_basic_hours').' '._T('info_basic_0disable').'</td></tr>
<tr><td>'._T('info_basic_urllinking').'</td><td colspan="3"> '.AUTO_LINK.' '._T('info_0no1yes').'</td></tr>
<tr><td>'._T('info_basic_com_limit').'</td><td colspan="3"> '.COMM_MAX._T('info_basic_com_after').'</td></tr>
<tr><td>'._T('info_basic_anonpost').'</td><td colspan="3"> '.ALLOW_NONAME.' '._T('info_basic_anonpost_opt').'</td></tr>
<tr><td>'._T('info_basic_del_incomplete').'</td><td colspan="3"> '.KILL_INCOMPLETE_UPLOAD.' '._T('info_0no1yes').'</td></tr>
<tr><td>'._T('info_basic_use_sample', $THUMB_SETTING['Quality']).'</td><td colspan="3"> '.USE_THUMB.' '._T('info_0notuse1use').'</td></tr>
<tr><td>'._T('info_basic_useblock').'</td><td colspan="3"> '.BAN_CHECK.' '._T('info_0disable1enable').'</td></tr>
<tr><td>'._T('info_basic_showid').'</td><td colspan="3"> '.DISP_ID.' '._T('info_basic_showid_after').'</td></tr>
<tr><td>'._T('info_basic_cr_limit').'</td><td colspan="3"> '.BR_CHECK._T('info_basic_cr_after').'</td></tr>
<tr><td>'._T('info_basic_timezone').'</td><td colspan="3"> GMT '.TIME_ZONE.'</td></tr>
<tr><td>'._T('info_basic_theme').'</td><td colspan="3"> '.$PTE->BlockValue('THEMENAME').' '.$PTE->BlockValue('THEMEVER').'<br/>by '.$PTE->BlockValue('THEMEAUTHOR').'</td></tr>
<tr><td align="center" colspan="4">'._T('info_dsusage_top').'</td></tr>
<tr align="center"><td>'._T('info_basic_threadcount').'</td><td colspan="'.(isset($piosensorInfo)?'2':'3').'"> '.$counttree.' '._T('info_basic_threads').'</td>'.(isset($piosensorInfo)?'<td rowspan="2">'.$piosensorInfo.'</td>':'').'</tr>
<tr align="center"><td>'._T('info_dsusage_count').'</td><td colspan="'.(isset($piosensorInfo)?'2':'3').'">'.$countline.'</td></tr>
<tr><td align="center" colspan="4">'._T('info_fileusage_top').STORAGE_LIMIT.' '._T('info_0disable1enable').'</td></tr>';
 
if(STORAGE_LIMIT){
$dat .= '
<tr align="center"><td>'._T('info_fileusage_limit').'</td><td colspan="2">'.STORAGE_MAX.' KB</td><td rowspan="2">'._T('info_dsusage_usage').'<br /><span style="color: #'.$clrflag_sl.'">'.substr(($tmp_ts_ratio * 100), 0, 6).'</span> %</td></tr>
<tr align="center"><td>'._T('info_fileusage_count').'</td><td colspan="2"><span style="color: #'.$clrflag_sl.'">'.$tmp_total_size.' KB</span></td></tr>';
}else{
$dat .= '
<tr align="center"><td>'._T('info_fileusage_count').'</td><td>'.$tmp_total_size.' KB</td><td colspan="2">'._T('info_dsusage_usage').'<br /><span style="color: green;">'._T('info_fileusage_unlimited').'</span></td></tr>';
}
 
$dat .= '
<tr><td align="center" colspan="4">'._T('info_server_top').'</td></tr>
<tr align="center"><td colspan="3">'.$func_thumbInfo.'</td><td>'.$func_thumbWork.'</td></tr>
</table>
<hr />
</div>'."\n";
 
foot($dat);
echo $dat;
}
 
/* 程式首次執行之初始化 */
function init(){
$PIO = PMCLibrary::getPIOInstance();
$FileIO = PMCLibrary::getFileIOInstance();
 
if(!is_writable(ROOTPATH)) error(_T('init_permerror'));
 
$chkfolder = array(IMG_DIR, THUMB_DIR, 'cache/');
// 逐一自動建置資料夾
foreach($chkfolder as $value) if(!is_dir($value)){ mkdir($value); @chmod($value, 0777); } // 沒有就建立
 
$PIO->dbInit(); // PIO Init
$FileIO->init(); // FileIO Init
 
error(_T('init_inited'));
}
 
/*-----------程式各項功能主要判斷-------------*/
if(GZIP_COMPRESS_LEVEL && ($Encoding = CheckSupportGZip())){ ob_start(); ob_implicit_flush(0); } // 支援且開啟Gzip壓縮就設緩衝區
$mode = isset($_GET['mode']) ? $_GET['mode'] : (isset($_POST['mode']) ? $_POST['mode'] : ''); // 目前執行模式 (GET, POST)
 
//init(); // ←■■!程式環境初始化,跑過一次後請刪除此行!■■
switch($mode){
case 'regist':
regist();
break;
case 'admin':
$admin = isset($_REQUEST['admin']) ? $_REQUEST['admin'] : ''; // 管理者執行模式
valid();
switch($admin){
case 'del': admindel(); break;
case 'logout':
adminAuthenticate('logout');
header('HTTP/1.1 302 Moved Temporarily');
header('Location: '.fullURL().PHP_SELF2.'?'.time());
break;
case 'optimize':
case 'check':
case 'repair':
case 'export':
if(!$PIO->dbMaintanence($admin)) echo _T('action_main_notsupport');
else echo _T('action_main_'.$admin).(($mret = $PIO->dbMaintanence($admin,true))?_T('action_main_success'):_T('action_main_failed')).(is_bool($mret)?'':'<br/>'.$mret);
die("</div></form></body>\n</html>");
break;
default:
}
break;
case 'search':
search();
break;
case 'status':
showstatus();
break;
case 'category':
searchCategory();
break;
case 'module':
$PMS = PMCLibrary::getPMSInstance();
$loadModule = isset($_GET['load']) ? $_GET['load'] : '';
if($PMS->onlyLoad($loadModule)) $PMS->moduleInstance[$loadModule]->ModulePage();
else echo '404 Not Found';
break;
case 'moduleloaded':
listModules();
break;
case 'usrdel':
usrdel();
case 'remake':
updatelog();
header('HTTP/1.1 302 Moved Temporarily');
header('Location: '.fullURL().PHP_SELF2.'?'.time());
break;
default:
// 如果瀏覽器支援XHTML標準MIME就輸出
header('Content-Type: '.((USE_XHTML && strpos($_SERVER['HTTP_ACCEPT'],'application/xhtml+xml')!==FALSE) ? 'application/xhtml+xml' : 'text/html').'; charset=utf-8');
$res = isset($_GET['res']) ? $_GET['res'] : 0; // 欲回應編號
if($res){ // 回應模式輸出
$page = isset($_GET['page_num']) ? $_GET['page_num'] : 'RE_PAGE_MAX';
if(!($page=='all' || $page=='RE_PAGE_MAX')) $page = intval($_GET['page_num']);
updatelog($res, $page); // 實行分頁
}elseif(isset($_GET['page_num']) && intval($_GET['page_num']) > -1){ // PHP動態輸出一頁
updatelog(0, intval($_GET['page_num']));
}else{ // 導至靜態庫存頁
if(!is_file(PHP_SELF2)) updatelog();
header('HTTP/1.1 302 Moved Temporarily');
header('Location: '.fullURL().PHP_SELF2.'?'.time());
}
}
if(GZIP_COMPRESS_LEVEL && $Encoding){ // 有啟動Gzip
if(!ob_get_length()) exit; // 沒內容不必壓縮
header('Content-Encoding: '.$Encoding);
header('X-Content-Encoding-Level: '.GZIP_COMPRESS_LEVEL);
header('Vary: Accept-Encoding');
print gzencode(ob_get_clean(), GZIP_COMPRESS_LEVEL); // 壓縮內容
}
?>
New file
/release/PIO-v7/inc_pixmicat.tpl
@@ -0,0 +1,164 @@
<!--&THEMENAME-->futaba Theme<!--/&THEMENAME-->
<!--&THEMEVER-->v20110703<!--/&THEMEVER-->
<!--&THEMEAUTHOR-->Pixmicat! Development Team<!--/&THEMEAUTHOR-->
 
<!--&HEADER--><?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh-tw">
<head>
<meta http-equiv="Cache-Control" content="max-age=0; must-revalidate" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="Content-Language" content="zh-tw" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=2.0, user-scalable=yes" />
<title>{$TITLE}</title>
<link rel="stylesheet" type="text/css" href="mainstyle.css" />
<!--/&HEADER-->
 
<!--&JSHEADER-->
<script type="text/javascript">
// <![CDATA[
var msgs=['{$JS_REGIST_WITHOUTCOMMENT}','{$JS_REGIST_UPLOAD_NOTSUPPORT}','{$JS_CONVERT_SAKURA}'];
var ext="{$ALLOW_UPLOAD_EXT}".toUpperCase().split("|");
// ]]>
</script>
<script type="text/javascript" src="mainscript.js"></script>
<!--[if lt IE 8]><script type="text/javascript" src="iedivfix.js"></script><![endif]-->
<!--/&JSHEADER-->
 
<!--&TOPLINKS-->
<div id="toplink">
{$HOME} {$SEARCH} {$HOOKLINKS} {$TOP_LINKS} {$STATUS} {$ADMIN} {$REFRESH}
</div>
<!--/&TOPLINKS-->
 
<!--&BODYHEAD-->
<body>
 
<div id="header">
<!--&TOPLINKS/-->
<br />
<h1>{$TITLE}</h1>
<hr class="top" />
</div>
<!--/&BODYHEAD-->
 
<!--&POSTFORM-->
<form action="{$SELF}" method="post" enctype="multipart/form-data" onsubmit="return c();" id="postform_main">
<div id="postform">
<!--&IF($FORMTOP,'{$FORMTOP}','')-->
<input type="hidden" name="mode" value="{$MODE}" />
<input type="hidden" name="MAX_FILE_SIZE" value="{$MAX_FILE_SIZE}" />
<input type="hidden" name="upfile_path" value="" />
<!--&IF($RESTO,'{$RESTO}','')-->
<div style="text-align: center;">
<table cellpadding="1" cellspacing="1" id="postform_tbl" style="margin: 0px auto; text-align: left;">
<tr><td class="Form_bg"><b>{$FORM_NAME_TEXT}</b></td><td>{$FORM_NAME_FIELD}</td></tr>
<tr><td class="Form_bg"><b>{$FORM_EMAIL_TEXT}</b></td><td>{$FORM_EMAIL_FIELD}</td></tr>
<tr><td class="Form_bg"><b>{$FORM_TOPIC_TEXT}</b></td><td>{$FORM_TOPIC_FIELD}{$FORM_SUBMIT}</td></tr>
<tr><td class="Form_bg"><b>{$FORM_COMMENT_TEXT}</b></td><td>{$FORM_COMMENT_FIELD}</td></tr>
<!--&IF($FORM_ATTECHMENT_FIELD,'<tr><td class="Form_bg"><b>{$FORM_ATTECHMENT_TEXT}</b></td><td>{$FORM_ATTECHMENT_FIELD}[{$FORM_NOATTECHMENT_FIELD}<label for="noimg">{$FORM_NOATTECHMENT_TEXT}</label>]','')-->
<!--&IF($FORM_CONTPOST_FIELD,'[{$FORM_CONTPOST_FIELD}<label for="up_series">{$FORM_CONTPOST_TEXT}</label>]','')-->
<!--&IF($FORM_ATTECHMENT_FIELD,'</td></tr>','')-->
<!--&IF($FORM_CATEGORY_FIELD,'<tr><td class="Form_bg"><b>{$FORM_CATEGORY_TEXT}</b></td><td>{$FORM_CATEGORY_FIELD}<small>{$FORM_CATEGORY_NOTICE}</small></td></tr>','')-->
<tr><td class="Form_bg"><b>{$FORM_DELETE_PASSWORD_TEXT}</b></td><td>{$FORM_DELETE_PASSWORD_FIELD}<small>{$FORM_DELETE_PASSWORD_NOTICE}</small></td></tr>
{$FORM_EXTRA_COLUMN}
<tr><td colspan="2">
<div id="postinfo">
<ul>{$FORM_NOTICE}
<!--&IF($FORM_NOTICE_STORAGE_LIMIT,'{$FORM_NOTICE_STORAGE_LIMIT}','')-->
{$HOOKPOSTINFO}
{$ADDITION_INFO}
</ul>
<noscript><div>{$FORM_NOTICE_NOSCRIPT}</div></noscript>
</div>
</td></tr>
</table>
</div>
<script type="text/javascript">l1();</script>
<hr />
</div>
</form>
<!--&IF($FORMBOTTOM,'{$FORMBOTTOM}','')-->
<!--/&POSTFORM-->
 
<!--&FOOTER-->
<div id="footer">
{$FOOTER}
<script type="text/javascript">preset();</script>
</div>
 
</body>
</html>
<!--/&FOOTER-->
 
<!--&ERROR-->
<div id="error">
<div style="text-align: center; font-size: 1.5em; font-weight: bold;">
<span style="color: red;">{$MESG}</span><p />
<a href="{$SELF2}">{$RETURN_TEXT}</a> <a href="javascript:history.back();">{$BACK_TEXT}</a>
</div>
<hr />
</div>
<!--/&ERROR-->
 
 
<!--&THREAD-->
<div class="threadpost" id="r{$NO}">
{$IMG_BAR}<!--&IF($IMG_BAR,'<br />','')-->{$IMG_SRC}<input type="checkbox" name="{$NO}" value="delete" /><span class="title">{$SUB}</span>
{$NAME_TEXT}<span class="name">{$NAME}</span> [{$NOW}] {$QUOTEBTN}&nbsp;{$REPLYBTN}
<div class="quote">{$COM}</div>
<!--&IF($CATEGORY,'<div class="category">{$CATEGORY_TEXT}{$CATEGORY}</div>','')-->
{$WARN_OLD}{$WARN_BEKILL}{$WARN_ENDREPLY}{$WARN_HIDEPOST}</div>
<!--/&THREAD-->
 
<!--&REPLY-->
<div class="reply" id="r{$NO}"><div class="replywrap">
<input type="checkbox" name="{$NO}" value="delete" /><span class="title">{$SUB}</span> {$NAME_TEXT}<span class="name">{$NAME}</span> [{$NOW}] {$QUOTEBTN} &nbsp;<!--&IF($IMG_BAR,'<br />&nbsp;','')-->{$IMG_BAR} {$IMG_SRC}
<div class="quote">{$COM}</div>
<!--&IF($CATEGORY,'<div class="category">{$CATEGORY_TEXT}{$CATEGORY}</div>','')-->
{$WARN_BEKILL}</div></div>
<!--/&REPLY-->
 
<!--&SEARCHRESULT-->
<div class="threadpost">
<span class="title">{$SUB}</span>
{$NAME_TEXT}<span class="name">{$NAME}</span> [{$NOW}] No.{$NO}
<div class="quote">{$COM}</div>
<!--&IF($CATEGORY,'<div class="category">{$CATEGORY_TEXT}{$CATEGORY}</div>','')-->
</div>
<!--&REALSEPARATE/-->
<!--/&SEARCHRESULT-->
 
<!--&THREADSEPARATE-->
<hr />
<!--/&THREADSEPARATE-->
 
<!--&REALSEPARATE-->
<hr />
<!--/&REALSEPARATE-->
 
<!--&DELFORM-->
<div id="del">
<table style="float: right;">
<tr><td align="center" style="white-space: nowrap;">
{$DEL_HEAD_TEXT}[{$DEL_IMG_ONLY_FIELD}<label for="onlyimgdel">{$DEL_IMG_ONLY_TEXT}</label>]<br />
{$DEL_PASS_TEXT}{$DEL_PASS_FIELD}{$DEL_SUBMIT_BTN}
</td></tr>
</table>
</div>
<!--/&DELFORM-->
 
<!--&MAIN-->
<div id="contents">
{$THREADFRONT}
<form action="{$SELF}" method="post">
<div id="threads" class="autopagerize_page_element">
{$THREADS}
</div>
{$THREADREAR}
<!--&DELFORM/-->
<script type="text/javascript">l2();</script>
</form>
{$PAGENAV}
</div>
<!--/&MAIN-->
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v7/lib/lib_fileio.php
@@ -0,0 +1,58 @@
<?php
/*
FileIO - Pixmicat! File I/O
FileIO Kernel Switcher
*/
 
// 引入必要函式庫
$fileio_file = ROOTPATH.'lib/fileio/fileio.'.FILEIO_BACKEND.'.php'; // FileIO Backend
if(is_file($fileio_file)) require($fileio_file);
 
// 擴充物件
class FileIOWrapper extends FileIO{
var $absoluteURL; // 伺服器絕對位置
function _getAbsoluteURL(){
return 'http://'.$_SERVER['HTTP_HOST'].substr($_SERVER['PHP_SELF'], 0, strpos($_SERVER['PHP_SELF'], PHP_SELF));
}
 
function getImageLocalURL($imgname){
if(!isset($this->absoluteURL)) $this->absoluteURL = $this->_getAbsoluteURL();
 
return $this->absoluteURL.(strpos($imgname, 's.') !== false ? THUMB_DIR : IMG_DIR).$imgname;
}
 
/* 檢查遠端檔案是否存在 */
function remoteImageExists($img){
return (@file_get_contents($img, false, null, 0, 1) !== false);
}
 
/* 回傳目前總檔案大小 */
function getCurrentStorageSize($delta=0){
$size = 0;
$cache_file = ROOTPATH.'sizecache.dat'; // 使用快取檔案記錄
 
if(!is_file($cache_file)){ // 無快取,新增
$size = $this->IFS->getCurrentStorageSize();
file_put_contents($cache_file, $size, LOCK_EX);
@chmod($cache_file, 0666);
}else{ // 使用快取
$size = file_get_contents($cache_file);
if($delta != 0){ // 快取值更動
$size += $delta;
file_put_contents($cache_file, $size, LOCK_EX);
}
}
return intval($size / 1024);
}
 
/* 更新總檔案大小數值 */
function updateStorageSize($delta){
$this->getCurrentStorageSize($delta);
}
 
/* 搜尋預覽圖檔之完整檔名 */
function resolveThumbName($thumbPattern){
return $this->IFS->findThumbName($thumbPattern);
}
}
?>
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v7/lib/lib_pio.cond.php
@@ -0,0 +1,81 @@
<?php
/**
* PIO Condition Object
*
* 判斷文章是否符合刪除條件並列出刪除編號
*
* @package PMCLibrary
* @version $Id$
*/
 
/* 以總文章篇數作為刪除判斷 */
class ByPostCountCondition implements IPIOCondition {
public static function check($type, $limit){
$PIO = PMCLibrary::getPIOInstance();
return $PIO->postCount() >= $limit * ($type=='predict' ? 0.95 : 1);
}
 
public static function listee($type, $limit){
$PIO = PMCLibrary::getPIOInstance();
return $PIO->fetchPostList(0,
intval($limit * ($type=='predict' ? 0.95 : 1)) - 1, $limit);
}
 
public static function info($limit){
$PIO = PMCLibrary::getPIOInstance();
return __CLASS__.': '.($pcnt=$PIO->postCount()).'/'.$limit.
sprintf(' (%.2f%%)',($pcnt/$limit*100));
}
}
 
/* 以總討論串數作為刪除判斷 */
class ByThreadCountCondition implements IPIOCondition {
public static function check($type, $limit){
$PIO = PMCLibrary::getPIOInstance();
return $PIO->threadCount() >= ($type=='predict' ? $limit * 0.95 : 1);
}
 
public static function listee($type, $limit){
$PIO = PMCLibrary::getPIOInstance();
return $PIO->fetchThreadList(
intval($limit * ($type=='predict' ? 0.95 : 1)), $limit);
}
 
public static function info($limit){
$PIO = PMCLibrary::getPIOInstance();
return __CLASS__.': '.($tcnt=$PIO->threadCount()).'/'.$limit.
sprintf(' (%.2f%%)',($tcnt/$limit*100));
}
}
 
/* 以討論串生存時間作為刪除判斷 */
class ByThreadAliveTimeCondition implements IPIOCondition {
public static function check($type, $limit){
$PIO = PMCLibrary::getPIOInstance();
// 最舊討論串編號
$oldestThreadNo = $PIO->fetchThreadList($PIO->threadCount() - 1, 1, true);
$oldestThread = $PIO->fetchPosts($oldestThreadNo);
return (time() - substr($oldestThread[0]['tim'], 0, 10) >= 86400 *
$limit * ($type=='predict' ? 0.95 : 1));
}
 
public static function listee($type, $limit){
$PIO = PMCLibrary::getPIOInstance();
// 討論串編號陣列 (由舊到新)
$ThreadNo = $PIO->fetchThreadList(0, 0, true); sort($ThreadNo);
$NowTime = time();
$i = 0;
foreach($ThreadNo as $t){
$post = $PIO->fetchPosts($t);
if($NowTime - substr($post[0]['tim'], 0, 10) < 86400 * $limit *
($type=='predict' ? 0.95 : 1)) break; // 時間不符合
$i++;
}
if(count($ThreadNo)===$i){ $i--; } // 保留最新的一篇避免全部刪除
return array_slice($ThreadNo, 0, $i);
}
 
public static function info($limit){
return __CLASS__.": $limit day(s)";
}
}
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v7/lib/pio/pio.mysqli.php
@@ -0,0 +1,550 @@
<?php
/**
* PIO MySQL improved API
*
* 提供存取以 MySQL 資料庫構成的資料結構後端的物件 (使用 MySQL improved extension)
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
*/
 
class PIOmysqli implements IPIO {
private $ENV, $username, $password, $server, $port, $dbname, $tablename; // Local Constant
private $con, $prepared, $useTransaction; // Local Global
 
public function __construct($connstr='', $ENV){
$this->ENV = $ENV;
$this->prepared = 0;
if($connstr) $this->dbConnect($connstr);
}
 
public function __destruct(){
if (!is_null($this->con)) @mysqli_close($this->con);
}
 
/* private 攔截SQL錯誤 */
private function _error_handler(array $errarray, $query=''){
$err = sprintf('%s on line %d.', $errarray[0], $errarray[1]);
if (defined('DEBUG') && DEBUG) {
$err .= sprintf(PHP_EOL."Description: #%d: %s".PHP_EOL.
"SQL: %s", $this->con->errno, $this->con->error, $query);
}
throw new RuntimeException($err, $this->con->errno);
}
 
/* private 使用SQL字串和MySQL伺服器要求 */
private function _mysql_call($query, $errarray=false){
$resource = $this->con->query($query);
if(is_array($errarray) && $resource===false) $this->_error_handler($errarray, $query);
else return $resource;
}
 
/**
* mysqli_result::fetch_all < PHP 5.3 的相容方法。
* 如果 < PHP 5.3,使用傳統 for 迴圈取得全部陣列。
*
* @param mysqli_result $result mysqli_result 物件
* @param int $resulttype 回傳陣列類型
* @return array 結果陣列
*/
private function _mysqli_fetch_all(mysqli_result $result, $resulttype = MYSQLI_NUM) {
if (method_exists($result, 'fetch_all')) {
$res = $result->fetch_all($resulttype);
} else {
$res = array();
while ($row = $result->fetch_array($resulttype)) {
$res[] = $row;
}
}
return $res;
}
 
/* PIO模組版本 */
public function pioVersion(){
return '0.6 (v20130202)';
}
 
/* 處理連線字串/連接 */
public function dbConnect($connStr){
// 格式: mysqli://帳號:密碼@伺服器位置:埠號(可省略)/資料庫/資料表/
// 示例: mysqli://pixmicat:1234@127.0.0.1/pixmicat_use/imglog/
if(preg_match('/^mysqli:\/\/(.*)\:(.*)\@(.*)(?:\:([0-9]+))?\/(.*)\/(.*)\/$/i', $connStr, $linkinfos)){
$this->username = $linkinfos[1]; // 登入帳號
$this->password = $linkinfos[2]; // 登入密碼
$this->server = $linkinfos[3]; // 登入伺服器
$this->port = $linkinfos[4] ? intval($linkinfos[4]) : 3306; // 埠號
$this->dbname = $linkinfos[5]; // 資料庫名稱
$this->tablename = $linkinfos[6]; // 資料表名稱
}
}
 
/* 初始化 */
public function dbInit($isAddInitData=true){
$this->dbPrepare();
if($this->con->query("SHOW TABLES LIKE '".$this->tablename."'")->num_rows == 0){ // 資料表不存在
$result = "CREATE TABLE ".$this->tablename." (primary key(no),
index (resto),index (root),index (time),
no int(1) not null auto_increment,
resto int(1) not null,
root timestamp null DEFAULT 0,
time int(1) not null,
md5chksum varchar(32) not null,
category varchar(255) not null,
tim bigint(1) not null,
ext varchar(4) not null,
imgw smallint(1) not null,
imgh smallint(1) not null,
imgsize varchar(10) not null,
tw smallint(1) not null,
th smallint(1) not null,
pwd varchar(8) not null,
now varchar(255) not null,
name varchar(255) not null,
email varchar(255) not null,
sub varchar(255) not null,
com text not null,
host varchar(255) not null,
status varchar(255) not null)
ENGINE = MYISAM
COMMENT = 'PIO Structure V3'";
if(version_compare($this->con->server_info, '5.5', '<')){ // 5.5 以前版本
$result = "CREATE TABLE ".$this->tablename." (primary key(no),
index (resto),index (root),index (time),
no int(1) not null auto_increment,
resto int(1) not null,
root timestamp(14) null DEFAULT 0,
time int(1) not null,
md5chksum varchar(32) not null,
category varchar(255) not null,
tim bigint(1) not null,
ext varchar(4) not null,
imgw smallint(1) not null,
imgh smallint(1) not null,
imgsize varchar(10) not null,
tw smallint(1) not null,
th smallint(1) not null,
pwd varchar(8) not null,
now varchar(255) not null,
name varchar(255) not null,
email varchar(255) not null,
sub varchar(255) not null,
com text not null,
host varchar(255) not null,
status varchar(255) not null)
TYPE = MYISAM
COMMENT = 'PIO Structure V3'";
}
 
$result2 = $this->con->query("SHOW CHARACTER SET like 'utf8'"); // 是否支援UTF-8 (MySQL 4.1.1開始支援)
if($result2 && $result2->num_rows > 0){
$result .= ' CHARACTER SET utf8 COLLATE utf8_general_ci'; // 資料表追加UTF-8編碼
$result2->free();
}
$this->con->query($result); // 正式新增資料表
// 追加一筆新資料
if($isAddInitData) $this->addPost(1, 0, '', '', 0, '', 0, 0, '', 0, 0, '', '05/01/01(六)00:00', $this->ENV['NONAME'], '', $this->ENV['NOTITLE'], $this->ENV['NOCOMMENT'], '');
$this->dbCommit();
}
}
 
/* 準備/讀入 */
public function dbPrepare($reload=false, $transaction=false){
if($this->prepared) return true;
 
$this->con = new mysqli($this->server, $this->username, $this->password, $this->dbname, $this->port);
if($this->con->connect_error)
$this->_error_handler(array('Open database failed', __LINE__));
 
$this->con->query("SET NAMES 'utf8'");
 
$this->useTransaction = $transaction;
if($transaction) $this->con->autocommit(FALSE);
 
$this->prepared = 1;
}
 
/* 提交/儲存 */
public function dbCommit(){
if(!$this->prepared) return false;
if($this->useTransaction) $this->con->commit();
}
 
/* 資料表維護 */
public function dbMaintanence($action, $doit=false){
switch($action) {
case 'optimize':
if($doit){
$this->dbPrepare(false);
return $this->con->query('OPTIMIZE TABLES '.$this->tablename);
}else return true; // 支援最佳化資料表
break;
case 'check':
if($doit){
$this->dbPrepare(false);
if($rs = $this->con->query('CHECK TABLE '.$this->tablename)){
$rs->data_seek($rs->num_rows - 1);
$row = $rs->fetch_assoc();
return 'Table '.$row['Table'].': '.$row['Msg_type'].' = '.$row['Msg_text'];
}
else return false;
}else return true; // 支援檢查資料表
break;
case 'repair':
if($doit){
$this->dbPrepare(false);
if($rs = $this->con->query('REPAIR TABLE '.$this->tablename)){
$rs->data_seek($rs->num_rows - 1);
$row = $rs->fetch_assoc();
return 'Table '.$row['Table'].': '.$row['Msg_type'].' = '.$row['Msg_text'];
}
else return false;
}else return true; // 支援修復資料表
break;
case 'export':
if($doit){
$this->dbPrepare(false);
$gp = gzopen('piodata.log.gz', 'w9');
gzwrite($gp, $this->dbExport());
gzclose($gp);
return '<a href="piodata.log.gz">下載 piodata.log.gz 中介檔案</a>';
}else return true; // 支援匯出資料
break;
default: return false; // 不支援
}
}
 
/* 匯入資料來源 */
public function dbImport($data){
$this->dbInit(false); // 僅新增結構不新增資料
$data = explode("\r\n", $data);
$data_count = count($data) - 1;
$replaceComma = create_function('$txt', 'return str_replace("&#44;", ",", $txt);');
$SQL = 'INSERT INTO '.$this->tablename.' (no,resto,root,time,md5chksum,category,tim,ext,imgw,imgh,imgsize,tw,th,pwd,now,name,email,sub,com,host,status) VALUES '
.'(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
$stmt = $this->con->prepare($SQL);
for($i = 0; $i < $data_count; $i++){
$line = array_map($replaceComma, explode(',', $data[$i])); // 取代 &#44; 為 ,
$tim = substr($line[5], 0, 10);
if ($line[2] == '0') $line[2] = '0000-00-00 00:00:00';
$stmt->bind_param('iisissisiisiissssssss',
$line[0],
$line[1],
$line[2],
$tim,
$line[3],
$line[4],
$line[5],
$line[6],
$line[7],
$line[8],
$line[9],
$line[10],
$line[11],
$line[12],
$line[13],
$line[14],
$line[15],
$line[16],
$line[17],
$line[18],
$line[19]);
$stmt->execute() or $this->_error_handler(array('Insert a new post failed', __LINE__));
}
$this->dbCommit(); // 送交
return true;
}
 
/* 匯出資料來源 */
public function dbExport(){
if(!$this->prepared) $this->dbPrepare();
$line = $this->con->query('SELECT no,resto,root,md5chksum,category,tim,ext,imgw,imgh,imgsize,tw,th,pwd,now,name,email,sub,com,host,status FROM '.$this->tablename.' ORDER BY no DESC');
$data = '';
$replaceComma = create_function('$txt', 'return str_replace(",", "&#44;", $txt);');
while($row = $line->fetch_assoc()){
$row = array_map($replaceComma, $row); // 取代 , 為 &#44;
if($row['root']=='0000-00-00 00:00:00') $row['root'] = '0'; // 初始值設為 0
$data .= implode(',', $row).",\r\n";
}
$line->free();
return $data;
}
 
/* 文章數目 */
public function postCount($resno=0){
if(!$this->prepared) $this->dbPrepare();
 
if($resno){ // 回傳討論串總文章數目
$line = $this->_mysql_call('SELECT COUNT(no) FROM '.$this->tablename.' WHERE resto = '.intval($resno),
array('Fetch count in thread failed', __LINE__));
$rs = $line->fetch_row();
$countline = $rs[0] + 1;
}else{ // 回傳總文章數目
$line = $this->_mysql_call('SELECT COUNT(no) FROM '.$this->tablename, array('Fetch count of posts failed', __LINE__));
$rs = $line->fetch_row();
$countline = $rs[0];
}
$line->free();
return $countline;
}
 
/* 討論串數目 */
public function threadCount(){
if(!$this->prepared) $this->dbPrepare();
 
$tree = $this->_mysql_call('SELECT COUNT(no) FROM '.$this->tablename.' WHERE resto = 0',
array('Fetch count of threads failed', __LINE__));
$counttree = $tree->fetch_row(); // 計算討論串目前資料筆數
$tree->free();
return $counttree[0];
}
 
/* 取得最後文章編號 */
public function getLastPostNo($state){
if(!$this->prepared) $this->dbPrepare();
 
if($state=='afterCommit'){ // 送出後的最後文章編號
$tree = $this->_mysql_call('SELECT MAX(no) FROM '.$this->tablename, array('Get the last No. failed', __LINE__));
$lastno = $tree->fetch_row();
$tree->free();
return $lastno[0];
}else return 0; // 其他狀態沒用
}
 
/* 輸出文章清單 */
public function fetchPostList($resno=0, $start=0, $amount=0){
if(!$this->prepared) $this->dbPrepare();
 
$line = array();
$resno = intval($resno);
if($resno){ // 輸出討論串的結構 (含自己, EX : 1,2,3,4,5,6)
$tmpSQL = 'SELECT no FROM '.$this->tablename.' WHERE no = '.$resno.' OR resto = '.$resno.' ORDER BY no';
}else{ // 輸出所有文章編號,新的在前
$tmpSQL = 'SELECT no FROM '.$this->tablename.' ORDER BY no DESC';
$start = intval($start); $amount = intval($amount);
if($amount) $tmpSQL .= " LIMIT {$start}, {$amount}"; // 有指定數量才用 LIMIT
}
$tree = $this->_mysql_call($tmpSQL, array('Fetch post list failed', __LINE__));
while($rows = $tree->fetch_row()) $line[] = $rows[0];
$tree->free();
return $line;
}
 
/* 輸出討論串清單 */
public function fetchThreadList($start=0, $amount=0, $isDESC=false){
if(!$this->prepared) $this->dbPrepare();
 
$start = intval($start); $amount = intval($amount);
$treeline = array();
$tmpSQL = 'SELECT no FROM '.$this->tablename.' WHERE resto = 0 ORDER BY '.($isDESC ? 'no' : 'root').' DESC';
if($amount) $tmpSQL .= " LIMIT {$start}, {$amount}"; // 有指定數量才用 LIMIT
$tree = $this->_mysql_call($tmpSQL, array('Fetch thread list failed', __LINE__));
while($rows = $tree->fetch_row()) $treeline[] = $rows[0];
$tree->free();
return $treeline;
}
 
/* 輸出文章 */
public function fetchPosts($postlist,$fields='*'){
if(!$this->prepared) $this->dbPrepare();
 
if(is_array($postlist)){ // 取多串
$pno = implode(',', $postlist); // ID字串
$tmpSQL = 'SELECT '.$fields.' FROM '.$this->tablename.' WHERE no IN ('.$pno.') ORDER BY no';
if(count($postlist) > 1){ if($postlist[0] > $postlist[1]) $tmpSQL .= ' DESC'; } // 由大排到小
}else $tmpSQL = 'SELECT '.$fields.' FROM '.$this->tablename.' WHERE no = '.intval($postlist); // 取單串
$line = $this->_mysql_call($tmpSQL, array('Fetch the post content failed', __LINE__));
return $this->_mysqli_fetch_all($line, MYSQLI_ASSOC); // 輸出陣列結構
}
 
/* 刪除舊附件 (輸出附件清單) */
public function delOldAttachments($total_size, $storage_max, $warnOnly=true){
$FileIO = PMCLibrary::getFileIOInstance();
if(!$this->prepared) $this->dbPrepare();
 
$arr_warn = $arr_kill = array(); // 警告 / 即將被刪除標記陣列
$result = $this->_mysql_call('SELECT no,ext,tim FROM '.$this->tablename.' WHERE ext <> \'\' ORDER BY no',
array('Get old posts failed', __LINE__));
while(list($dno, $dext, $dtim) = $result->fetch_row()){ // 個別跑舊文迴圈
$dfile = $dtim.$dext; // 附加檔案名稱
$dthumb = $FileIO->resolveThumbName($dtim); // 預覽檔案名稱
if($FileIO->imageExists($dfile)){ $total_size -= $FileIO->getImageFilesize($dfile) / 1024; $arr_kill[] = $dno; $arr_warn[$dno] = 1; } // 標記刪除
if($dthumb && $FileIO->imageExists($dthumb)) $total_size -= $FileIO->getImageFilesize($dthumb) / 1024;
if($total_size < $storage_max) break;
}
$result->free();
return $warnOnly ? $arr_warn : $this->removeAttachments($arr_kill);
}
 
/* 刪除文章 */
public function removePosts($posts){
if(!$this->prepared) $this->dbPrepare();
if(count($posts)==0) return array();
 
$files = $this->removeAttachments($posts, true); // 先遞迴取得刪除文章及其回應附件清單
$pno = implode(', ', $posts); // ID字串
$this->_mysql_call('DELETE FROM '.$this->tablename.' WHERE no IN ('.$pno.') OR resto IN('.$pno.')',
array('Delete old posts and replies failed', __LINE__)); // 刪掉文章
return $files;
}
 
/* 刪除附件 (輸出附件清單) */
public function removeAttachments($posts, $recursion=false){
$FileIO = PMCLibrary::getFileIOInstance();
if(!$this->prepared) $this->dbPrepare();
if(count($posts)==0) return array();
 
$files = array();
$pno = implode(', ', $posts); // ID字串
if($recursion) $tmpSQL = 'SELECT ext,tim FROM '.$this->tablename.' WHERE (no IN ('.$pno.') OR resto IN('.$pno.")) AND ext <> ''"; // 遞迴取出 (含回應附件)
else $tmpSQL = 'SELECT ext,tim FROM '.$this->tablename.' WHERE no IN ('.$pno.") AND ext <> ''"; // 只有指定的編號
 
$result = $this->_mysql_call($tmpSQL, array('Get attachments of the post failed', __LINE__));
while(list($dext, $dtim) = $result->fetch_row()){ // 個別跑迴圈
$dfile = $dtim.$dext; // 附加檔案名稱
$dthumb = $FileIO->resolveThumbName($dtim); // 預覽檔案名稱
if($FileIO->imageExists($dfile)) $files[] = $dfile;
if($dthumb && $FileIO->imageExists($dthumb)) $files[] = $dthumb;
}
$result->free();
return $files;
}
 
/* 新增文章/討論串 */
public function addPost($no, $resto, $md5chksum, $category, $tim, $ext, $imgw, $imgh, $imgsize, $tw, $th, $pwd, $now, $name, $email, $sub, $com, $host, $age=false, $status=''){
if(!$this->prepared) $this->dbPrepare();
 
$time = (int)substr($tim, 0, -3); // 13位數的數字串是檔名,10位數的才是時間數值
$updatetime = gmdate('Y-m-d H:i:s'); // 更動時間 (UTC)
if($resto){ // 新增回應
$root = '0000-00-00 00:00:00';
if($age){ // 推文
$result = $this->con->prepare('UPDATE '.$this->tablename.' SET root = ? WHERE no = ?');
$result->bind_param('si', $updatetime, $resto);
$result->execute() or $this->_error_handler(array('Push the post failed', __LINE__));
}
}else $root = $updatetime; // 新增討論串, 討論串最後被更新時間
 
$SQL = 'INSERT INTO '.$this->tablename
.' (resto,root,time,md5chksum,category,tim,ext,imgw,imgh,imgsize,tw,th,pwd,now,name,email,sub,com,host,status) VALUES '
.'(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
$stmt = $this->con->prepare($SQL);
$stmt->bind_param('isissisiisiissssssss',
$resto,
$root,
$time,
$md5chksum,
$category,
$tim,
$ext,
$imgw,
$imgh,
$imgsize,
$tw,
$th,
$pwd,
$now,
$name,
$email,
$sub,
$com,
$host,
$status);
$stmt->execute() or $this->_error_handler(array('Insert a new post failed', __LINE__));
}
 
/* 檢查是否連續投稿 */
public function isSuccessivePost($lcount, $com, $timestamp, $pass, $passcookie, $host, $isupload){
$FileIO = PMCLibrary::getFileIOInstance();
if(!$this->prepared) $this->dbPrepare();
 
if(!$this->ENV['PERIOD.POST']) return false; // 關閉連續投稿檢查
$timestamp = intval($timestamp);
$tmpSQL = 'SELECT pwd,host FROM '.$this->tablename.' WHERE time > '.($timestamp - (int)$this->ENV['PERIOD.POST']); // 一般投稿時間檢查
if($isupload) $tmpSQL .= ' OR time > '.($timestamp - (int)$this->ENV['PERIOD.IMAGEPOST']); // 附加圖檔的投稿時間檢查 (與下者兩者擇一)
else $tmpSQL .= ' OR md5(com) = "'.md5($com).'"'; // 內文一樣的檢查 (與上者兩者擇一)
 
$result = $this->_mysql_call($tmpSQL, array('Get the post to check the succession failed', __LINE__));
while(list($lpwd, $lhost) = $result->fetch_row()){
// 判斷為同一人發文且符合連續投稿條件
if($host==$lhost || $pass==$lpwd || $passcookie==$lpwd) return true;
}
return false;
}
 
/* 檢查是否重複貼圖 */
public function isDuplicateAttachment($lcount, $md5hash){
$FileIO = PMCLibrary::getFileIOInstance();
if(!$this->prepared) $this->dbPrepare();
 
$result = $this->_mysql_call('SELECT tim,ext FROM '.$this->tablename." WHERE ext <> '' AND md5chksum = '$md5hash' ORDER BY no DESC",
array('Get the post to check the duplicate attachment failed', __LINE__));
while(list($ltim, $lext) = $result->fetch_row()){
if($FileIO->imageExists($ltim.$lext)) return true; // 有相同檔案
}
return false;
}
 
/* 有此討論串? */
public function isThread($no){
if(!$this->prepared) $this->dbPrepare();
 
$result = $this->_mysql_call('SELECT no FROM '.$this->tablename.' WHERE no = '.intval($no).' AND resto = 0');
return $result->fetch_row() ? true : false;
}
 
/* 搜尋文章 */
public function searchPost($keyword, $field, $method){
if(!$this->prepared) $this->dbPrepare();
 
$keyword_cnt = count($keyword);
$SearchQuery = 'SELECT * FROM '.$this->tablename." WHERE {$field} LIKE '".$this->con->real_escape_string('%'.$keyword[0].'%')."'";
if($keyword_cnt > 1){
for($i = 1; $i < $keyword_cnt; $i++){
$SearchQuery .= " {$method} {$field} LIKE '".$this->con->real_escape_string('%'.$keyword[$i].'%')."'"; // 多重字串交集 / 聯集搜尋
}
}
$SearchQuery .= ' ORDER BY no DESC'; // 按照號碼大小排序
$line = $this->_mysql_call($SearchQuery, array('Search the post failed', __LINE__));
return $this->_mysqli_fetch_all($line, MYSQLI_ASSOC); // 輸出陣列結構
}
 
/* 搜尋類別標籤 */
public function searchCategory($category){
if(!$this->prepared) $this->dbPrepare();
 
$foundPosts = array();
$result = $this->con->prepare('SELECT no FROM '.$this->tablename.' WHERE lower(category) LIKE ? ORDER BY no DESC');
$param = '%,'.strtolower($category).',%';
$result->bind_param('s', $param);
$result->bind_result($no);
$result->execute();
while($rows = $result->fetch()) $foundPosts[] = $no;
return $foundPosts;
}
 
/* 取得文章屬性 */
public function getPostStatus($status){
return new FlagHelper($status); // 回傳 FlagHelper 物件
}
 
/* 更新文章 */
public function updatePost($no, $newValues){
if(!$this->prepared) $this->dbPrepare();
 
$no = intval($no);
$chk = array('resto', 'md5chksum', 'category', 'tim', 'ext', 'imgw', 'imgh', 'imgsize', 'tw', 'th', 'pwd', 'now', 'name', 'email', 'sub', 'com', 'host', 'status');
foreach($chk as $c){
if(isset($newValues[$c])){
$this->_mysql_call('UPDATE '.$this->tablename." SET $c = '".$this->con->real_escape_string($newValues[$c])."', root = root WHERE no = ".$no,
array('Update the field of the post failed', __LINE__)); // 更新討論串屬性
}
}
}
 
/* 設定文章屬性 */
public function setPostStatus($no, $newStatus){
$this->updatePost($no, array('status' => $newStatus));
}
}
?>
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v7/lib/pio/pio.logflockp.php
@@ -0,0 +1,533 @@
<?php
/**
* PIO Log API (flock+)
*
* 提供存取以 Log 檔案構成的資料結構後端的物件
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
* @deprecated
*/
 
class PIOlogflockp implements IPIO {
var $ENV, $logfile, $treefile, $porderfile; // Local Constant
var $logs, $trees, $LUT, $porder, $torder, $prepared; // Local Global
 
function PIOlogflockp($connstr='', $ENV){
$this->ENV = $ENV;
$this->logs = $this->trees = $this->LUT = $this->porder = $this->torder = array();
$this->prepared = 0;
 
if($connstr) $this->dbConnect($connstr);
}
 
/* private 把每一行 Log 解析轉換成陣列資料 */
function _AnalysisLogs($line){
$tline = array();
list($tline['no'], $tline['resto'], $tline['md5chksum'], $tline['category'], $tline['tim'], $tline['ext'], $tline['imgw'], $tline['imgh'], $tline['imgsize'], $tline['tw'], $tline['th'], $tline['pwd'], $tline['now'], $tline['name'], $tline['email'], $tline['sub'], $tline['com'], $tline['host'], $tline['status']) = explode(',', $line);
return array_reverse($tline); // list()是由右至左代入的
}
 
/* private 將回文放進陣列 */
function _includeReplies($posts){
$torder_flip = array_flip($this->torder);
foreach($posts as $post){
if(array_key_exists($post, $torder_flip)){ // 討論串首篇
$posts = array_merge($posts, $this->trees[$post]);
}
}
return array_merge(array(), array_unique($posts)); // 去除重複值
}
 
/* private 取代 , 成為 &#44; 避免衝突 */
function _replaceComma($txt){
return str_replace(',', '&#44;', $txt);
}
 
/* private 由編號取出資料分析成陣列 */
function _ArrangeArrayStructure($line){
$line = (array)$line; // 全部視為Arrays
$posts = array();
foreach($line as $i){
if(!isset($this->LUT[$i])) continue;
if(!is_array($this->logs[$this->LUT[$i]])){ // 進行分析轉換
$line = $this->logs[$this->LUT[$i]]; if($line=='') continue;
$this->logs[$this->LUT[$i]] = $this->_AnalysisLogs($line);
}
$posts[] = $this->logs[$this->LUT[$i]];
}
return $posts;
}
 
/* PIO模組版本 */
function pioVersion(){
return '0.5 (v20080920)';
}
 
/* 處理連線字串/連接 */
function dbConnect($connStr){
if(preg_match('/^logflockp:\/\/(.*)\:(.*)\/$/i', $connStr, $linkinfos)){
$this->logfile = $this->ENV['BOARD'].$linkinfos[1]; // 投稿文字記錄檔檔名
$this->treefile = $this->ENV['BOARD'].$linkinfos[2]; // 樹狀結構記錄檔檔名
$this->porderfile = $this->ENV['BOARD'].$this->ENV['LUTCACHE']; // LUT索引查找表暫存檔案
}
}
 
/* 初始化 */
function dbInit(){
$chkfile = array($this->logfile, $this->treefile, $this->porderfile);
// 自動建置
foreach($chkfile as $value){
if(!is_file($value)){ // 檔案不存在
$fp = fopen($value, 'w');
stream_set_write_buffer($fp, 0);
if($value==$this->logfile) fwrite($fp, '1,0,,,0,,0,0,,0,0,,05/01/01(六)00:00,'.$this->ENV['NONAME'].',,'.$this->ENV['NOTITLE'].','.$this->ENV['NOCOMMENT'].',,,'); // PIO Structure V3
if($value==$this->treefile) fwrite($fp, '1');
if($value==$this->porderfile) fwrite($fp, '1');
fclose($fp);
unset($fp);
@chmod($value, 0666);
}
}
return true;
}
 
/* 準備/讀入 */
function dbPrepare($reload=false, $transaction=true){
if($this->prepared && !$reload) return true;
if($reload && $this->prepared) $this->porder = $this->torder = $this->LUT = $this->logs = $this->trees = array();
 
$this->logs = file($this->logfile); // Log每行原始資料
if(!file_exists($this->porderfile)){ // LUT不在,重生成
$lut = '';
foreach($this->logs as $line){
if(!isset($line)) continue;
$tmp = explode(',', $line); $lut .= $tmp[0]."\r\n";
}
$this->_lock($this->porderfile);
$fp = fopen($this->porderfile, 'w'); // LUT
stream_set_write_buffer($fp, 0);
// flock($fp, LOCK_EX); // 鎖定檔案
fwrite($fp, $lut);
// flock($fp, LOCK_UN); // 解鎖
fclose($fp);
$this->_unlock($this->porderfile);
}
$this->porder = array_map('rtrim', file($this->porderfile)); // 文章編號陣列
$this->LUT = array_flip($this->porder); // LUT索引查找表
 
$tree = array_map('rtrim', file($this->treefile));
foreach($tree as $treeline){ // 解析樹狀結構製成索引
if($treeline=='') continue;
$tline = explode(',', $treeline);
$this->torder[] = $tline[0]; // 討論串首篇編號陣列
$this->trees[$tline[0]] = $tline; // 特定編號討論串完整結構陣列
}
 
$this->prepared = 1;
}
 
/* 提交/儲存 */
function dbCommit(){
if(!$this->prepared) return false;
 
$log = $tree = $lut = '';
$this->logs = array_merge(array(), $this->logs); // 更新logs鍵值
$this->torder = array_merge(array(), $this->torder); // 更新torder鍵值
$this->porder = $this->LUT = array(); // 重新生成索引
 
foreach($this->logs as $line){
if(!isset($line)) continue;
if(is_array($line)){ // 已被分析過
$log .= implode(',', $line).",\r\n";
$lut .= ($this->porder[] = $line['no'])."\r\n";
}else{ // 尚未分析過
$log .= $line;
$tmp = explode(',', $line); $lut .= ($this->porder[] = $tmp[0])."\r\n";
}
}
$this->LUT = array_flip($this->porder);
$tcount = count($this->trees);
for($tline = 0; $tline < $tcount; $tline++){
$tree .= $this->isThread($this->torder[$tline]) ? implode(',', $this->trees[$this->torder[$tline]])."\r\n" : '';
}
//$this->_memcacheSet(false); // 更新快取 (不需要再分析)
 
$this->_lock($this->logfile);
$fp = fopen($this->logfile, 'w'); // Log
stream_set_write_buffer($fp, 0);
// flock($fp, LOCK_EX); // 鎖定檔案
fwrite($fp, $log);
// flock($fp, LOCK_UN); // 解鎖
fclose($fp);
$this->_unlock($this->logfile);
 
$this->_lock($this->treefile);
$fp = fopen($this->treefile, 'w'); // tree
stream_set_write_buffer($fp, 0);
// flock($fp, LOCK_EX); // 鎖定檔案
fwrite($fp, $tree);
// flock($fp, LOCK_UN); // 解鎖
fclose($fp);
$this->_unlock($this->treefile);
 
$this->_lock($this->porderfile);
$fp = fopen($this->porderfile, 'w'); // LUT
stream_set_write_buffer($fp, 0);
// flock($fp, LOCK_EX); // 鎖定檔案
fwrite($fp, $lut);
// flock($fp, LOCK_UN); // 解鎖
fclose($fp);
$this->_unlock($this->porderfile);
}
 
/* 資料表維護 */
function dbMaintanence($action,$doit=false){
switch($action) {
case 'export':
if($doit){
$this->dbPrepare(false);
$gp = gzopen('piodata.log.gz', 'w9');
gzwrite($gp, $this->dbExport());
gzclose($gp);
return '<a href="piodata.log.gz">下載 piodata.log.gz 中介檔案</a>';
}else return true; // 支援匯出資料
break;
case 'optimize':
case 'check':
case 'repair':
default: return false; // 不支援
}
}
 
/* 匯入資料來源 */
function dbImport($data){
$arrData = explode("\r\n", $data);
$arrData_cnt = count($arrData) - 1; // 最後一個是空的
$arrTree = array();
$tree = $logs = $lut = '';
for($i = 0; $i < $arrData_cnt; $i++){
$line = explode(',', $arrData[$i], 4); // 切成四段
$logs .= $line[0].','.$line[1].','.$line[3]."\r\n"; // 重建討論結構
$lut .= $line[0]."\r\n"; // 重建 LUT 查找表結構
if($line[1]==0){ // 首篇
if(!isset($arrTree[$line[0]])) $arrTree[$line[0]] = array($line[0]); // 僅自身一篇
else array_unshift($arrTree[$line[0]], $line[0]);
continue;
}
if(!isset($arrTree[$line[1]])) $arrTree[$line[1]] = array();
array_unshift($arrTree[$line[1]], $line[0]);
}
foreach($arrTree as $t) $tree .= implode(',', $t)."\r\n"; // 重建樹狀結構
$chkfile = array($this->logfile, $this->treefile, $this->porderfile);
foreach($chkfile as $value){
$fp = fopen($value, 'w');
stream_set_write_buffer($fp, 0);
if($value==$this->logfile) fwrite($fp, $logs);
if($value==$this->treefile) fwrite($fp, $tree);
if($value==$this->porderfile) fwrite($fp, $lut);
fclose($fp);
unset($fp);
@chmod($value, 0666);
}
return true;
}
 
/* 匯出資料來源 */
function dbExport(){
if(!$this->prepared) $this->dbPrepare();
$f = file($this->logfile);
$data = '';
foreach($f as $line){
$line = explode(',', $line, 3); // 分成三段 (最後一段特別長)
if($line[1]==0 && isset($this->trees[$line[0]])){
$lastno = array_pop($this->trees[$line[0]]);
$line2 = $this->fetchPosts($lastno);
$root = gmdate('Y-m-d H:i:s', substr($line2[0]['tim'], 0, 10)); // UTC 時間
unset($this->trees[$line[0]]); // 刪除表示已取過
}else{
$root = '0';
}
$data .= $line[0].','.$line[1].','.$root.','.$line[2];
}
return $data;
}
 
/* 文章數目 */
function postCount($resno=0){
if(!$this->prepared) $this->dbPrepare();
 
return $resno ? ($this->isThread($resno) ? count(@$this->trees[$resno]) : 0) : count($this->porder);
}
 
/* 討論串數目 */
function threadCount(){
if(!$this->prepared) $this->dbPrepare();
 
return count($this->torder);
}
 
/* 取得最後的文章編號 */
function getLastPostNo($state){
if(!$this->prepared) $this->dbPrepare();
 
switch($state){
case 'beforeCommit':
case 'afterCommit':
return reset($this->porder);
}
}
 
/* 輸出文章清單 */
function fetchPostList($resno=0, $start=0, $amount=0){
if(!$this->prepared) $this->dbPrepare();
 
$plist = array();
if($resno){
if($this->isThread($resno)){
if($start && $amount){
$plist = array_slice($this->trees[$resno], $start, $amount);
array_unshift($plist, $resno);
}
if(!$start && $amount) $plist = array_slice($this->trees[$resno], 0, $amount);
if(!$start && !$amount) $plist = $this->trees[$resno];
}
}else{
$plist = $amount ? array_slice($this->porder, $start, $amount) : $this->porder;
}
return $plist;
}
 
/* 輸出討論串清單 */
function fetchThreadList($start=0, $amount=0, $isDESC=false){
if(!$this->prepared) $this->dbPrepare();
$tmp_array = $this->torder;
if($isDESC) rsort($tmp_array); // 按編號遞減排序 (預設為按最後更新時間排序)
return $amount ? array_slice($tmp_array, $start, $amount) : $tmp_array;
}
 
/* 輸出文章 */
function fetchPosts($postlist,$fields='*'){
if(!$this->prepared) $this->dbPrepare();
 
return $this->_ArrangeArrayStructure($postlist); // 輸出陣列結構
}
 
/* 刪除舊附件 (輸出附件清單) */
function delOldAttachments($total_size, $storage_max, $warnOnly=true){
$FileIO = PMCLibrary::getFileIOInstance();
if(!$this->prepared) $this->dbPrepare();
 
$rpord = $this->porder; sort($rpord); // 由舊排到新 (小->大)
$arr_warn = $arr_kill = array();
foreach($rpord as $post){
$logsarray = $this->_ArrangeArrayStructure($post); // 分析資料為陣列
$dfile = $logsarray[0]['tim'].$logsarray[0]['ext'];
$dthumb = $FileIO->resolveThumbName($logsarray[0]['tim']);
if($FileIO->imageExists($dfile)){ $total_size -= $FileIO->getImageFilesize($dfile) / 1024; $arr_kill[] = $post; $arr_warn[$post] = 1; } // 標記刪除
if($dthumb && $FileIO->imageExists($dthumb)) $total_size -= $FileIO->getImageFilesize($dthumb) / 1024;
if($total_size < $storage_max) break;
}
return $warnOnly ? $arr_warn : $this->removeAttachments($arr_kill);
}
 
/* 刪除文章 */
function removePosts($posts){
if(!$this->prepared) $this->dbPrepare();
if(count($posts)==0) return array();
 
$posts = $this->_includeReplies($posts); // 包含所有回文
$filelist = $this->removeAttachments($posts); // 欲刪除附件
$torder_flip = array_flip($this->torder);
$pcount = count($posts);
$logsarray = $this->_ArrangeArrayStructure($posts); // 分析資料為陣列
for($p = 0; $p < $pcount; $p++){
if(!isset($logsarray[$p])) continue;
if($logsarray[$p]['resto']==0){ // 討論串頭
unset($this->trees[$logsarray[$p]['no']]); // 刪除樹狀記錄
if(array_key_exists($logsarray[$p]['no'], $torder_flip)) unset($this->torder[$torder_flip[$logsarray[$p]['no']]]); // 從討論串首篇陣列中移除
}else{
// 從樹狀檔刪除
if(array_key_exists($logsarray[$p]['resto'], $this->trees)){
$tr_flip = array_flip($this->trees[$logsarray[$p]['resto']]);
unset($this->trees[$logsarray[$p]['resto']][$tr_flip[$posts[$p]]]);
}
}
unset($this->logs[$this->LUT[$logsarray[$p]['no']]]);
if(array_key_exists($logsarray[$p]['no'], $this->LUT)) unset($this->porder[$this->LUT[$logsarray[$p]['no']]]); // 從討論串編號陣列中移除
}
$this->LUT = array_flip($this->porder);
return $filelist;
}
 
/* 刪除附件 (輸出附件清單) */
function removeAttachments($posts){
$FileIO = PMCLibrary::getFileIOInstance();
if(!$this->prepared) $this->dbPrepare();
if(count($posts)==0) return array();
 
$files = array();
$logsarray = $this->_ArrangeArrayStructure($posts); // 分析資料為陣列
$lcount = count($logsarray);
for($i = 0; $i < $lcount; $i++){
if($logsarray[$i]['ext']){
$dfile = $logsarray[$i]['tim'].$logsarray[$i]['ext'];
$dthumb = $FileIO->resolveThumbName($logsarray[$i]['tim']);
if($FileIO->imageExists($dfile)) $files[] = $dfile;
if($dthumb && $FileIO->imageExists($dthumb)) $files[] = $dthumb;
}
}
return $files;
}
 
/* 新增文章/討論串 */
function addPost($no, $resto, $md5chksum, $category, $tim, $ext, $imgw, $imgh, $imgsize, $tw, $th, $pwd, $now, $name, $email, $sub, $com, $host, $age=false, $status='') {
if(!$this->prepared) $this->dbPrepare();
 
$tline = array($no, $resto, $md5chksum, $category, $tim, $ext, $imgw, $imgh, $imgsize, $tw, $th, $pwd, $now, $name, $email, $sub, $com, $host, $status);
$tline = array_map(array($this, '_replaceComma'), $tline); // 將資料內的 , 轉換 (Only Log needed)
array_unshift($this->logs, implode(',', $tline).",\r\n"); // 更新logs
array_unshift($this->porder, $no); // 更新porder
$this->LUT = array_flip($this->porder); // 更新LUT
 
// 更新torder及trees
if($resto){
$this->trees[$resto][] = $no;
if($age){
$torder_flip = array_flip($this->torder);
unset($this->torder[$torder_flip[$resto]]); // 先刪除舊有位置
array_unshift($this->torder, $resto); // 再移到頂端
}
}else{
$this->trees[$no][0] = $no;
array_unshift($this->torder, $no);
}
}
 
/* 檢查是否連續投稿 */
function isSuccessivePost($lcount, $com, $timestamp, $pass, $passcookie, $host, $isupload){
if(!$this->prepared) $this->dbPrepare();
 
$pcount = $this->postCount();
$lcount = ($pcount > $lcount) ? $lcount : $pcount;
for($i = 0; $i < $lcount; $i++){
$logsarray = $this->_ArrangeArrayStructure($this->porder[$i]); // 分析資料為陣列
list($lcom, $lhost, $lpwd, $ltime) = array($logsarray[0]['com'], $logsarray[0]['host'], $logsarray[0]['pwd'], substr($logsarray[0]['tim'],0,-3));
if($host==$lhost || $pass==$lpwd || $passcookie==$lpwd) $pchk = 1;
else $pchk = 0;
if($this->ENV['PERIOD.POST'] && $pchk){ // 密碼比對符合且開啟連續投稿時間限制
if($timestamp - $ltime < $this->ENV['PERIOD.POST']) return true; // 投稿時間相距太短
if($timestamp - $ltime < $this->ENV['PERIOD.IMAGEPOST'] && $isupload) return true; // 附加圖檔的投稿時間相距太短
if($com == $lcom && !$isupload) return true; // 內文一樣
}
}
return false;
}
 
/* 檢查是否重複貼圖 */
function isDuplicateAttachment($lcount, $md5hash){
$FileIO = PMCLibrary::getFileIOInstance();
 
$pcount = $this->postCount();
$lcount = ($pcount > $lcount) ? $lcount : $pcount;
for($i = 0; $i < $lcount; $i++){
$logsarray = $this->_ArrangeArrayStructure($this->porder[$i]); // 分析資料為陣列
if(!$logsarray[0]['md5chksum']) continue; // 無附加圖檔
if($logsarray[0]['md5chksum']==$md5hash){
if($FileIO->imageExists($logsarray[0]['tim'].$logsarray[0]['ext'])) return true; // 存在MD5雜湊相同的檔案
}
}
return false;
}
 
/* 有此討論串? */
function isThread($no){
if(!$this->prepared) $this->dbPrepare();
 
return isset($this->trees[$no]);
}
 
/* 搜尋文章 */
function searchPost($keyword,$field,$method){
if(!$this->prepared) $this->dbPrepare();
 
$foundPosts = array();
$keyword_cnt = count($keyword);
$pcount = $this->postCount();
for($i = 0; $i < $pcount; $i++){
$logsarray = $this->_ArrangeArrayStructure($this->porder[$i]); // 分析資料為陣列
$found = 0;
foreach($keyword as $k){
if(strpos($logsarray[0][$field], $k)!==FALSE) $found++;
if($method=="OR" && $found) break;
}
if($method=="AND" && $found==$keyword_cnt) array_push($foundPosts, $logsarray[0]); // 全部都有找到 (AND交集搜尋)
elseif($method=="OR" && $found) array_push($foundPosts, $logsarray[0]); // 有找到 (OR聯集搜尋)
}
return $foundPosts;
}
 
/* 搜尋類別標籤 */
function searchCategory($category){
if(!$this->prepared) $this->dbPrepare();
 
$category = strtolower($category);
$foundPosts = array();
$pcount = $this->postCount();
for($i = 0; $i < $pcount; $i++){
$logsarray = $this->_ArrangeArrayStructure($this->porder[$i]); // 分析資料為陣列
if(!($ary_category = $logsarray[0]['category'])) continue;
if(strpos(strtolower($ary_category), '&#44;'.$category.'&#44;')!==false) array_push($foundPosts, $logsarray[0]['no']); // 找到標籤,加入名單
}
return $foundPosts;
}
 
/* 取得文章屬性 */
function getPostStatus($status){
return new FlagHelper($status); // 回傳 FlagHelper 物件
}
 
/* 更新文章 */
function updatePost($no, $newValues){
if(!$this->prepared) $this->dbPrepare();
 
$chk = array('resto', 'md5chksum', 'category', 'tim', 'ext', 'imgw', 'imgh', 'imgsize', 'tw', 'th', 'pwd', 'now', 'name', 'email', 'sub', 'com', 'host', 'status');
 
$this->_ArrangeArrayStructure($no); // 將資料變成陣列
foreach($chk as $c)
if(isset($newValues[$c]))
$this->logs[$this->LUT[$no]][$c] = $newValues[$c]; // 修改數值
}
 
/* 設定文章屬性 */
function setPostStatus($no, $newStatus){
$this->updatePost($no, array('status' => $newStatus));
}
 
/* 檔案鎖定/解鎖處理 */
function _lock($lock, $tries=10) {
ignore_user_abort(true);
$lock0 = ".{$lock}0";
$lock1 = ".{$lock}1";
for ($i=0; $i<$tries; $i++) {
if (!is_file($lock0)) {
touch($lock0);
if (!is_file($lock1)) {
touch($lock1);
return TRUE;
}
}
usleep(100);
}
return FALSE;
}
 
// Unlock a file.
function _unlock($lock) {
unlink(".{$lock}1");
unlink(".{$lock}0");
ignore_user_abort(false);
}
}
?>
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v7/lib/pio/pio.log.php
@@ -0,0 +1,498 @@
<?php
/**
* PIO Log API
*
* 提供存取以 Log 檔案構成的資料結構後端的物件
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
* @deprecated
*/
 
class PIOlog implements IPIO {
var $ENV, $logfile, $treefile, $porderfile; // Local Constant
var $logs, $trees, $LUT, $porder, $torder, $prepared; // Local Global
 
function PIOlog($connstr='', $ENV){
$this->ENV = $ENV;
$this->logs = $this->trees = $this->LUT = $this->porder = $this->torder = array();
$this->prepared = 0;
 
if($connstr) $this->dbConnect($connstr);
}
 
/* private 把每一行 Log 解析轉換成陣列資料 */
function _AnalysisLogs($line){
$tline = array();
list($tline['no'], $tline['resto'], $tline['md5chksum'], $tline['category'], $tline['tim'], $tline['ext'], $tline['imgw'], $tline['imgh'], $tline['imgsize'], $tline['tw'], $tline['th'], $tline['pwd'], $tline['now'], $tline['name'], $tline['email'], $tline['sub'], $tline['com'], $tline['host'], $tline['status']) = explode(',', $line);
return array_reverse($tline); // list()是由右至左代入的
}
 
/* private 將回文放進陣列 */
function _includeReplies($posts){
$torder_flip = array_flip($this->torder);
foreach($posts as $post){
if(array_key_exists($post, $torder_flip)){ // 討論串首篇
$posts = array_merge($posts, $this->trees[$post]);
}
}
return array_merge(array(), array_unique($posts)); // 去除重複值
}
 
/* private 取代 , 成為 &#44; 避免衝突 */
function _replaceComma($txt){
return str_replace(',', '&#44;', $txt);
}
 
/* private 由編號取出資料分析成陣列 */
function _ArrangeArrayStructure($line){
$line = (array)$line; // 全部視為Arrays
$posts = array();
foreach($line as $i){
if(!isset($this->LUT[$i])) continue;
if(!is_array($this->logs[$this->LUT[$i]])){ // 進行分析轉換
$line = $this->logs[$this->LUT[$i]]; if($line=='') continue;
$this->logs[$this->LUT[$i]] = $this->_AnalysisLogs($line);
}
$posts[] = $this->logs[$this->LUT[$i]];
}
return $posts;
}
 
/* PIO模組版本 */
function pioVersion(){
return '0.5 (v20080920)';
}
 
/* 處理連線字串/連接 */
function dbConnect($connStr){
if(preg_match('/^log:\/\/(.*)\:(.*)\/$/i', $connStr, $linkinfos)){
$this->logfile = $this->ENV['BOARD'].$linkinfos[1]; // 投稿文字記錄檔檔名
$this->treefile = $this->ENV['BOARD'].$linkinfos[2]; // 樹狀結構記錄檔檔名
$this->porderfile = $this->ENV['BOARD'].$this->ENV['LUTCACHE']; // LUT索引查找表暫存檔案
}
}
 
/* 初始化 */
function dbInit(){
$chkfile = array($this->logfile, $this->treefile, $this->porderfile);
// 自動建置
foreach($chkfile as $value){
if(!is_file($value)){ // 檔案不存在
$fp = fopen($value, 'w');
stream_set_write_buffer($fp, 0);
if($value==$this->logfile) fwrite($fp, '1,0,,,0,,0,0,,0,0,,05/01/01(六)00:00,'.$this->ENV['NONAME'].',,'.$this->ENV['NOTITLE'].','.$this->ENV['NOCOMMENT'].',,,'); // PIO Structure V3
if($value==$this->treefile) fwrite($fp, '1');
if($value==$this->porderfile) fwrite($fp, '1');
fclose($fp);
unset($fp);
@chmod($value, 0666);
}
}
return true;
}
 
/* 準備/讀入 */
function dbPrepare($reload=false, $transaction=true){
if($this->prepared && !$reload) return true;
if($reload && $this->prepared) $this->porder = $this->torder = $this->LUT = $this->logs = $this->trees = array();
 
$this->logs = file($this->logfile); // Log每行原始資料
if(!file_exists($this->porderfile)){ // LUT不在,重生成
$lut = '';
foreach($this->logs as $line){
if(!isset($line)) continue;
$tmp = explode(',', $line); $lut .= $tmp[0]."\r\n";
}
$fp = fopen($this->porderfile, 'w'); // LUT
stream_set_write_buffer($fp, 0);
flock($fp, LOCK_EX); // 鎖定檔案
fwrite($fp, $lut);
flock($fp, LOCK_UN); // 解鎖
fclose($fp);
}
$this->porder = array_map('rtrim', file($this->porderfile)); // 文章編號陣列
$this->LUT = array_flip($this->porder); // LUT索引查找表
 
$tree = array_map('rtrim', file($this->treefile));
foreach($tree as $treeline){ // 解析樹狀結構製成索引
if($treeline=='') continue;
$tline = explode(',', $treeline);
$this->torder[] = $tline[0]; // 討論串首篇編號陣列
$this->trees[$tline[0]] = $tline; // 特定編號討論串完整結構陣列
}
$this->prepared = 1;
}
 
/* 提交/儲存 */
function dbCommit(){
if(!$this->prepared) return false;
 
$log = $tree = $lut = '';
$this->logs = array_merge(array(), $this->logs); // 更新logs鍵值
$this->torder = array_merge(array(), $this->torder); // 更新torder鍵值
$this->porder = $this->LUT = array(); // 重新生成索引
 
foreach($this->logs as $line){
if(!isset($line)) continue;
if(is_array($line)){ // 已被分析過
$log .= implode(',', $line).",\r\n";
$lut .= ($this->porder[] = $line['no'])."\r\n";
}else{ // 尚未分析過
$log .= $line;
$tmp = explode(',', $line); $lut .= ($this->porder[] = $tmp[0])."\r\n";
}
}
$this->LUT = array_flip($this->porder);
$tcount = count($this->trees);
for($tline = 0; $tline < $tcount; $tline++){
$tree .= $this->isThread($this->torder[$tline]) ? implode(',', $this->trees[$this->torder[$tline]])."\r\n" : '';
}
 
$fp = fopen($this->logfile, 'w'); // Log
stream_set_write_buffer($fp, 0);
flock($fp, LOCK_EX); // 鎖定檔案
fwrite($fp, $log);
flock($fp, LOCK_UN); // 解鎖
fclose($fp);
 
$fp = fopen($this->treefile, 'w'); // tree
stream_set_write_buffer($fp, 0);
flock($fp, LOCK_EX); // 鎖定檔案
fwrite($fp, $tree);
flock($fp, LOCK_UN); // 解鎖
fclose($fp);
 
$fp = fopen($this->porderfile, 'w'); // LUT
stream_set_write_buffer($fp, 0);
flock($fp, LOCK_EX); // 鎖定檔案
fwrite($fp, $lut);
flock($fp, LOCK_UN); // 解鎖
fclose($fp);
}
 
/* 資料表維護 */
function dbMaintanence($action,$doit=false){
switch($action) {
case 'export':
if($doit){
$this->dbPrepare(false);
$gp = gzopen('piodata.log.gz', 'w9');
gzwrite($gp, $this->dbExport());
gzclose($gp);
return '<a href="piodata.log.gz">下載 piodata.log.gz 中介檔案</a>';
}else return true; // 支援匯出資料
break;
case 'optimize':
case 'check':
case 'repair':
default: return false; // 不支援
}
}
 
/* 匯入資料來源 */
function dbImport($data){
$arrData = explode("\r\n", $data);
$arrData_cnt = count($arrData) - 1; // 最後一個是空的
$arrTree = array();
$tree = $logs = $lut = '';
for($i = 0; $i < $arrData_cnt; $i++){
$line = explode(',', $arrData[$i], 4); // 切成四段
$logs .= $line[0].','.$line[1].','.$line[3]."\r\n"; // 重建討論結構
$lut .= $line[0]."\r\n"; // 重建 LUT 查找表結構
if($line[1]==0){ // 首篇
if(!isset($arrTree[$line[0]])) $arrTree[$line[0]] = array($line[0]); // 僅自身一篇
else array_unshift($arrTree[$line[0]], $line[0]);
continue;
}
if(!isset($arrTree[$line[1]])) $arrTree[$line[1]] = array();
array_unshift($arrTree[$line[1]], $line[0]);
}
foreach($arrTree as $t) $tree .= implode(',', $t)."\r\n"; // 重建樹狀結構
$chkfile = array($this->logfile, $this->treefile, $this->porderfile);
foreach($chkfile as $value){
$fp = fopen($value, 'w');
stream_set_write_buffer($fp, 0);
if($value==$this->logfile) fwrite($fp, $logs);
if($value==$this->treefile) fwrite($fp, $tree);
if($value==$this->porderfile) fwrite($fp, $lut);
fclose($fp);
unset($fp);
@chmod($value, 0666);
}
return true;
}
 
/* 匯出資料來源 */
function dbExport(){
if(!$this->prepared) $this->dbPrepare();
$f = file($this->logfile);
$data = '';
foreach($f as $line){
$line = explode(',', $line, 3); // 分成三段 (最後一段特別長)
if($line[1]==0 && isset($this->trees[$line[0]])){
$lastno = array_pop($this->trees[$line[0]]);
$line2 = $this->fetchPosts($lastno);
$root = gmdate('Y-m-d H:i:s', substr($line2[0]['tim'], 0, 10)); // UTC 時間
unset($this->trees[$line[0]]); // 刪除表示已取過
}else{
$root = '0';
}
$data .= $line[0].','.$line[1].','.$root.','.$line[2];
}
return $data;
}
 
/* 文章數目 */
function postCount($resno=0){
if(!$this->prepared) $this->dbPrepare();
 
return $resno ? ($this->isThread($resno) ? count(@$this->trees[$resno]) : 0) : count($this->porder);
}
 
/* 討論串數目 */
function threadCount(){
if(!$this->prepared) $this->dbPrepare();
 
return count($this->torder);
}
 
/* 取得最後的文章編號 */
function getLastPostNo($state){
if(!$this->prepared) $this->dbPrepare();
 
switch($state){
case 'beforeCommit':
case 'afterCommit':
return reset($this->porder);
}
}
 
/* 輸出文章清單 */
function fetchPostList($resno=0, $start=0, $amount=0){
if(!$this->prepared) $this->dbPrepare();
 
$plist = array();
if($resno){
if($this->isThread($resno)){
if($start && $amount){
$plist = array_slice($this->trees[$resno], $start, $amount);
array_unshift($plist, $resno);
}
if(!$start && $amount) $plist = array_slice($this->trees[$resno], 0, $amount);
if(!$start && !$amount) $plist = $this->trees[$resno];
}
}else{
$plist = $amount ? array_slice($this->porder, $start, $amount) : $this->porder;
}
return $plist;
}
 
/* 輸出討論串清單 */
function fetchThreadList($start=0, $amount=0, $isDESC=false){
if(!$this->prepared) $this->dbPrepare();
$tmp_array = $this->torder;
if($isDESC) rsort($tmp_array); // 按編號遞減排序 (預設為按最後更新時間排序)
return $amount ? array_slice($tmp_array, $start, $amount) : $tmp_array;
}
 
/* 輸出文章 */
function fetchPosts($postlist,$fields='*'){
if(!$this->prepared) $this->dbPrepare();
 
return $this->_ArrangeArrayStructure($postlist); // 輸出陣列結構
}
 
/* 刪除舊附件 (輸出附件清單) */
function delOldAttachments($total_size, $storage_max, $warnOnly=true){
$FileIO = PMCLibrary::getFileIOInstance();
if(!$this->prepared) $this->dbPrepare();
 
$rpord = $this->porder; sort($rpord); // 由舊排到新 (小->大)
$arr_warn = $arr_kill = array();
foreach($rpord as $post){
$logsarray = $this->_ArrangeArrayStructure($post); // 分析資料為陣列
$dfile = $logsarray[0]['tim'].$logsarray[0]['ext'];
$dthumb = $FileIO->resolveThumbName($logsarray[0]['tim']);
if($FileIO->imageExists($dfile)){ $total_size -= $FileIO->getImageFilesize($dfile) / 1024; $arr_kill[] = $post; $arr_warn[$post] = 1; } // 標記刪除
if($dthumb && $FileIO->imageExists($dthumb)) $total_size -= $FileIO->getImageFilesize($dthumb) / 1024;
if($total_size < $storage_max) break;
}
return $warnOnly ? $arr_warn : $this->removeAttachments($arr_kill);
}
 
/* 刪除文章 */
function removePosts($posts){
if(!$this->prepared) $this->dbPrepare();
if(count($posts)==0) return array();
 
$posts = $this->_includeReplies($posts); // 包含所有回文
$filelist = $this->removeAttachments($posts); // 欲刪除附件
$torder_flip = array_flip($this->torder);
$pcount = count($posts);
$logsarray = $this->_ArrangeArrayStructure($posts); // 分析資料為陣列
for($p = 0; $p < $pcount; $p++){
if(!isset($logsarray[$p])) continue;
if($logsarray[$p]['resto']==0){ // 討論串頭
unset($this->trees[$logsarray[$p]['no']]); // 刪除樹狀記錄
if(array_key_exists($logsarray[$p]['no'], $torder_flip)) unset($this->torder[$torder_flip[$logsarray[$p]['no']]]); // 從討論串首篇陣列中移除
}else{
// 從樹狀檔刪除
if(array_key_exists($logsarray[$p]['resto'], $this->trees)){
$tr_flip = array_flip($this->trees[$logsarray[$p]['resto']]);
unset($this->trees[$logsarray[$p]['resto']][$tr_flip[$posts[$p]]]);
}
}
unset($this->logs[$this->LUT[$logsarray[$p]['no']]]);
if(array_key_exists($logsarray[$p]['no'], $this->LUT)) unset($this->porder[$this->LUT[$logsarray[$p]['no']]]); // 從討論串編號陣列中移除
}
$this->LUT = array_flip($this->porder);
return $filelist;
}
 
/* 刪除附件 (輸出附件清單) */
function removeAttachments($posts){
$FileIO = PMCLibrary::getFileIOInstance();
if(!$this->prepared) $this->dbPrepare();
if(count($posts)==0) return array();
 
$files = array();
$logsarray = $this->_ArrangeArrayStructure($posts); // 分析資料為陣列
$lcount = count($logsarray);
for($i = 0; $i < $lcount; $i++){
if($logsarray[$i]['ext']){
$dfile = $logsarray[$i]['tim'].$logsarray[$i]['ext'];
$dthumb = $FileIO->resolveThumbName($logsarray[$i]['tim']);
if($FileIO->imageExists($dfile)) $files[] = $dfile;
if($dthumb && $FileIO->imageExists($dthumb)) $files[] = $dthumb;
}
}
return $files;
}
 
/* 新增文章/討論串 */
function addPost($no, $resto, $md5chksum, $category, $tim, $ext, $imgw, $imgh, $imgsize, $tw, $th, $pwd, $now, $name, $email, $sub, $com, $host, $age=false, $status='') {
if(!$this->prepared) $this->dbPrepare();
 
$tline = array($no, $resto, $md5chksum, $category, $tim, $ext, $imgw, $imgh, $imgsize, $tw, $th, $pwd, $now, $name, $email, $sub, $com, $host, $status);
$tline = array_map(array($this, '_replaceComma'), $tline); // 將資料內的 , 轉換 (Only Log needed)
array_unshift($this->logs, implode(',', $tline).",\r\n"); // 更新logs
array_unshift($this->porder, $no); // 更新porder
$this->LUT = array_flip($this->porder); // 更新LUT
 
// 更新torder及trees
if($resto){
$this->trees[$resto][] = $no;
if($age){
$torder_flip = array_flip($this->torder);
unset($this->torder[$torder_flip[$resto]]); // 先刪除舊有位置
array_unshift($this->torder, $resto); // 再移到頂端
}
}else{
$this->trees[$no][0] = $no;
array_unshift($this->torder, $no);
}
}
 
/* 檢查是否連續投稿 */
function isSuccessivePost($lcount, $com, $timestamp, $pass, $passcookie, $host, $isupload){
if(!$this->prepared) $this->dbPrepare();
 
$pcount = $this->postCount();
$lcount = ($pcount > $lcount) ? $lcount : $pcount;
for($i = 0; $i < $lcount; $i++){
$logsarray = $this->_ArrangeArrayStructure($this->porder[$i]); // 分析資料為陣列
list($lcom, $lhost, $lpwd, $ltime) = array($logsarray[0]['com'], $logsarray[0]['host'], $logsarray[0]['pwd'], substr($logsarray[0]['tim'],0,-3));
if($host==$lhost || $pass==$lpwd || $passcookie==$lpwd) $pchk = 1;
else $pchk = 0;
if($this->ENV['PERIOD.POST'] && $pchk){ // 密碼比對符合且開啟連續投稿時間限制
if($timestamp - $ltime < $this->ENV['PERIOD.POST']) return true; // 投稿時間相距太短
if($timestamp - $ltime < $this->ENV['PERIOD.IMAGEPOST'] && $isupload) return true; // 附加圖檔的投稿時間相距太短
if($com == $lcom && !$isupload) return true; // 內文一樣
}
}
return false;
}
 
/* 檢查是否重複貼圖 */
function isDuplicateAttachment($lcount, $md5hash){
$FileIO = PMCLibrary::getFileIOInstance();
 
$pcount = $this->postCount();
$lcount = ($pcount > $lcount) ? $lcount : $pcount;
for($i = 0; $i < $lcount; $i++){
$logsarray = $this->_ArrangeArrayStructure($this->porder[$i]); // 分析資料為陣列
if(!$logsarray[0]['md5chksum']) continue; // 無附加圖檔
if($logsarray[0]['md5chksum']==$md5hash){
if($FileIO->imageExists($logsarray[0]['tim'].$logsarray[0]['ext'])) return true; // 存在MD5雜湊相同的檔案
}
}
return false;
}
 
/* 有此討論串? */
function isThread($no){
if(!$this->prepared) $this->dbPrepare();
 
return isset($this->trees[$no]);
}
 
/* 搜尋文章 */
function searchPost($keyword,$field,$method){
if(!$this->prepared) $this->dbPrepare();
 
$foundPosts = array();
$keyword_cnt = count($keyword);
$pcount = $this->postCount();
for($i = 0; $i < $pcount; $i++){
$logsarray = $this->_ArrangeArrayStructure($this->porder[$i]); // 分析資料為陣列
$found = 0;
foreach($keyword as $k){
if(strpos($logsarray[0][$field], $k)!==FALSE) $found++;
if($method=="OR" && $found) break;
}
if($method=="AND" && $found==$keyword_cnt) array_push($foundPosts, $logsarray[0]); // 全部都有找到 (AND交集搜尋)
elseif($method=="OR" && $found) array_push($foundPosts, $logsarray[0]); // 有找到 (OR聯集搜尋)
}
return $foundPosts;
}
 
/* 搜尋類別標籤 */
function searchCategory($category){
if(!$this->prepared) $this->dbPrepare();
 
$category = strtolower($category);
$foundPosts = array();
$pcount = $this->postCount();
for($i = 0; $i < $pcount; $i++){
$logsarray = $this->_ArrangeArrayStructure($this->porder[$i]); // 分析資料為陣列
if(!($ary_category = $logsarray[0]['category'])) continue;
if(strpos(strtolower($ary_category), '&#44;'.$category.'&#44;')!==false) array_push($foundPosts, $logsarray[0]['no']); // 找到標籤,加入名單
}
return $foundPosts;
}
 
/* 取得文章屬性 */
function getPostStatus($status){
return new FlagHelper($status); // 回傳 FlagHelper 物件
}
 
/* 更新文章 */
function updatePost($no, $newValues){
if(!$this->prepared) $this->dbPrepare();
 
$chk = array('resto', 'md5chksum', 'category', 'tim', 'ext', 'imgw', 'imgh', 'imgsize', 'tw', 'th', 'pwd', 'now', 'name', 'email', 'sub', 'com', 'host', 'status');
 
$this->_ArrangeArrayStructure($no); // 將資料變成陣列
foreach($chk as $c)
if(isset($newValues[$c]))
$this->logs[$this->LUT[$no]][$c] = $this->_replaceComma($newValues[$c]); // 修改數值
}
 
/* 設定文章屬性 */
function setPostStatus($no, $newStatus){
$this->updatePost($no, array('status' => $newStatus));
}
}
?>
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v7/lib/pio/pio.sqlite.php
@@ -0,0 +1,442 @@
<?php
/**
* PIO SQLite API
*
* 提供存取以 SQLite 資料庫構成的資料結構後端的物件
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
*/
 
class PIOsqlite implements IPIO {
var $ENV, $dbname, $tablename; // Local Constant
var $con, $prepared, $useTransaction; // Local Global
 
function PIOsqlite($connstr='', $ENV){
$this->ENV = $ENV;
$this->prepared = 0;
if($connstr) $this->dbConnect($connstr);
}
 
/* private 攔截SQL錯誤 */
function _error_handler(array $errarray, $query=''){
$err = sprintf('%s on line %d.', $errarray[0], $errarray[1]);
$errno = sqlite_last_error($this->con);
if (defined('DEBUG') && DEBUG) {
$err .= sprintf(PHP_EOL."Description: #%d: %s".PHP_EOL.
"SQL: %s", $errno, sqlite_error_string($errno), $query);
}
throw new RuntimeException($err, $errno);
}
 
/* private 使用SQL字串和SQLite要求 */
function _sqlite_call($query, $errarray=false){
$resource = sqlite_query($this->con, $query);
if(is_array($errarray) && $resource===false) $this->_error_handler($errarray, $query);
else return $resource;
}
 
/* private SQLite的sqlite_result頂替函數 */
function _sqlite_result($rh, $row, $field){
$currrow = sqlite_fetch_all($rh, SQLITE_NUM);
return $currrow[$row][$field];
}
 
/* PIO模組版本 */
function pioVersion(){
return '0.6 (v20121213)';
}
 
/* 處理連線字串/連接 */
function dbConnect($connStr){
// 格式: sqlite://SQLite檔案之位置/資料表/
// 示例: sqlite://pixmicat.db/imglog/
if(preg_match('/^sqlite:\/\/(.*)\/(.*)\/$/i', $connStr, $linkinfos)){
$this->dbname = $linkinfos[1]; // SQLite檔案之位置
$this->tablename = $linkinfos[2]; // 資料表名稱
}
}
 
/* 初始化 */
function dbInit($isAddInitData=true){
$this->dbPrepare();
if(sqlite_num_rows(sqlite_query($this->con, "SELECT name FROM sqlite_master WHERE name LIKE '".$this->tablename."'"))===0){ // 資料表不存在
$result = 'CREATE TABLE '.$this->tablename.' (
"no" INTEGER NOT NULL PRIMARY KEY,
"resto" INTEGER NOT NULL,
"root" TIMESTAMP DEFAULT \'0\' NOT NULL,
"time" INTEGER NOT NULL,
"md5chksum" VARCHAR(32) NOT NULL,
"category" VARCHAR(255) NOT NULL,
"tim" INTEGER NOT NULL,
"ext" VARCHAR(4) NOT NULL,
"imgw" INTEGER NOT NULL,
"imgh" INTEGER NOT NULL,
"imgsize" VARCHAR(10) NOT NULL,
"tw" INTEGER NOT NULL,
"th" INTEGER NOT NULL,
"pwd" VARCHAR(8) NOT NULL,
"now" VARCHAR(255) NOT NULL,
"name" VARCHAR(255) NOT NULL,
"email" VARCHAR(255) NOT NULL,
"sub" VARCHAR(255) NOT NULL,
"com" TEXT NOT NULL,
"host" VARCHAR(255) NOT NULL,
"status" VARCHAR(255) NOT NULL
);'; // PIO Structure V3
$idx = array('resto', 'root', 'time');
foreach($idx as $x){
$result .= 'CREATE INDEX IDX_'.$this->tablename.'_'.$x.' ON '.$this->tablename.'('.$x.');';
}
$result .= 'CREATE INDEX IDX_'.$this->tablename.'_resto_no ON '.$this->tablename.'(resto,no);';
if($isAddInitData)
$result .= 'INSERT INTO '.$this->tablename.' (resto,root,time,md5chksum,category,tim,ext,imgw,imgh,imgsize,tw,th,pwd,now,name,email,sub,com,host,status) VALUES (0, datetime("now"), 1111111111, "", "", 1111111111111, "", 0, 0, "", 0, 0, "", "05/01/01(六)00:00", "'.$this->ENV['NONAME'].'", "", "'.$this->ENV['NOTITLE'].'", "'.$this->ENV['NOCOMMENT'].'", "", "");';
sqlite_exec($this->con, $result); // 正式新增資料表
$this->dbCommit();
}
}
 
/* 準備/讀入 */
function dbPrepare($reload=false, $transaction=true){
if($this->prepared) return true;
 
if(@!$this->con=sqlite_popen($this->dbname, 0666)) $this->_error_handler(array('Open database failed', __LINE__));
$this->useTransaction = $transaction;
if($transaction) @sqlite_exec($this->con, 'BEGIN;'); // 啟動交易性能模式
 
$this->prepared = 1;
}
 
/* 提交/儲存 */
function dbCommit(){
if(!$this->prepared) return false;
if($this->useTransaction) @sqlite_exec($this->con, 'COMMIT;'); // 交易性能模式提交
}
 
/* 資料表維護 */
function dbMaintanence($action, $doit=false){
switch($action) {
case 'optimize':
if($doit){
$this->dbPrepare(false);
if($this->_sqlite_call('VACUUM '.$this->tablename)) return true;
else return false;
}else return true; // 支援最佳化資料表
break;
case 'export':
if($doit){
$this->dbPrepare(false);
$gp = gzopen('piodata.log.gz', 'w9');
gzwrite($gp, $this->dbExport());
gzclose($gp);
return '<a href="piodata.log.gz">下載 piodata.log.gz 中介檔案</a>';
}else return true; // 支援匯出資料
break;
case 'check':
case 'repair':
default: return false; // 不支援
}
}
 
/* 匯入資料來源 */
function dbImport($data){
$this->dbInit(false); // 僅新增結構不新增資料
$data = explode("\r\n", $data);
$data_count = count($data) - 1;
$replaceComma = create_function('$txt', 'return str_replace("&#44;", ",", $txt);');
for($i = 0; $i < $data_count; $i++){
$line = array_map($replaceComma, explode(',', $data[$i])); // 取代 &#44; 為 ,
$SQL = 'INSERT INTO '.$this->tablename.' (no,resto,root,time,md5chksum,category,tim,ext,imgw,imgh,imgsize,tw,th,pwd,now,name,email,sub,com,host,status) VALUES ('.
$line[0].','.
$line[1].',\''.
$line[2].'\','.
substr($line[5], 0, 10).',\''.
sqlite_escape_string($line[3]).'\',\''.
sqlite_escape_string($line[4]).'\','.
$line[5].',\''.sqlite_escape_string($line[6]).'\','.
$line[7].','.$line[8].',\''.sqlite_escape_string($line[9]).'\','.$line[10].','.$line[11].',\''.
sqlite_escape_string($line[12]).'\',\''.
sqlite_escape_string($line[13]).'\',\''.
sqlite_escape_string($line[14]).'\',\''.
sqlite_escape_string($line[15]).'\',\''.
sqlite_escape_string($line[16]).'\',\''.
sqlite_escape_string($line[17]).'\',\''.
sqlite_escape_string($line[18]).'\',\''.
sqlite_escape_string($line[19]).'\')';
$this->_sqlite_call($SQL, array('Insert a new post failed', __LINE__));
}
$this->dbCommit(); // 送交
return true;
}
 
/* 匯出資料來源 */
function dbExport(){
if(!$this->prepared) $this->dbPrepare();
$line = $this->_sqlite_call('SELECT no,resto,root,md5chksum,category,tim,ext,imgw,imgh,imgsize,tw,th,pwd,now,name,email,sub,com,host,status FROM '.$this->tablename.' ORDER BY no DESC',
array('Export posts failed', __LINE__));
$data = '';
$replaceComma = create_function('$txt', 'return str_replace(",", "&#44;", $txt);');
while($row = sqlite_fetch_array($line, SQLITE_ASSOC)){
$row = array_map($replaceComma, $row); // 取代 , 為 &#44;
$data .= implode(',', $row).",\r\n";
}
return $data;
}
 
/* 文章數目 */
function postCount($resno=0){
if(!$this->prepared) $this->dbPrepare();
 
if($resno){ // 回傳討論串總文章數目
$line = $this->_sqlite_call('SELECT COUNT(no) FROM '.$this->tablename.' WHERE resto = '.intval($resno),
array('Fetch count in thread failed', __LINE__));
$countline = $this->_sqlite_result($line, 0, 0) + 1;
}else{ // 回傳總文章數目
$line = $this->_sqlite_call('SELECT COUNT(no) FROM '.$this->tablename, array('Fetch count of posts failed', __LINE__));
$countline = $this->_sqlite_result($line, 0, 0);
}
return $countline;
}
 
/* 討論串數目 */
function threadCount(){
if(!$this->prepared) $this->dbPrepare();
 
$tree = $this->_sqlite_call('SELECT COUNT(no) FROM '.$this->tablename.' WHERE resto = 0',
array('Fetch count of threads failed', __LINE__));
$counttree = $this->_sqlite_result($tree, 0, 0); // 計算討論串目前資料筆數
return $counttree;
}
 
/* 取得最後文章編號 */
function getLastPostNo($state){
if(!$this->prepared) $this->dbPrepare();
 
if($state=='afterCommit'){ // 送出後的最後文章編號
$tree = $this->_sqlite_call('SELECT MAX(no) FROM '.$this->tablename, array('Get the last No. failed', __LINE__));
$lastno = $this->_sqlite_result($tree, 0, 0);
return $lastno;
}else return 0; // 其他狀態沒用
}
 
/* 輸出文章清單 */
function fetchPostList($resno=0, $start=0, $amount=0){
if(!$this->prepared) $this->dbPrepare();
 
$line = array();
$resno = intval($resno);
if($resno){ // 輸出討論串的結構 (含自己, EX : 1,2,3,4,5,6)
$tmpSQL = 'SELECT no FROM '.$this->tablename.' WHERE no = '.$resno.' OR resto = '.$resno.' ORDER BY no';
}else{ // 輸出所有文章編號,新的在前
$tmpSQL = 'SELECT no FROM '.$this->tablename.' ORDER BY no DESC';
$start = intval($start); $amount = intval($amount);
if($amount) $tmpSQL .= " LIMIT {$start}, {$amount}"; // 有指定數量才用 LIMIT
}
$tree = $this->_sqlite_call($tmpSQL, array('Fetch post list failed', __LINE__));
while($rows = sqlite_fetch_array($tree)) $line[] = $rows[0]; // 迴圈
return $line;
}
 
/* 輸出討論串清單 */
function fetchThreadList($start=0, $amount=0, $isDESC=false) {
if(!$this->prepared) $this->dbPrepare();
 
$start = intval($start); $amount = intval($amount);
$treeline = array();
$tmpSQL = 'SELECT no FROM '.$this->tablename.' WHERE resto = 0 ORDER BY '.($isDESC ? 'no' : 'root').' DESC';
if($amount) $tmpSQL .= " LIMIT {$start}, {$amount}"; // 有指定數量才用 LIMIT
$tree = $this->_sqlite_call($tmpSQL, array('Fetch thread list failed', __LINE__));
while($rows = sqlite_fetch_array($tree)) $treeline[] = $rows[0]; // 迴圈
return $treeline;
}
 
/* 輸出文章 */
function fetchPosts($postlist,$fields='*'){
if(!$this->prepared) $this->dbPrepare();
 
if(is_array($postlist)){ // 取多串
$pno = implode(',', $postlist); // ID字串
$tmpSQL = 'SELECT '.$fields.' FROM '.$this->tablename.' WHERE no IN ('.$pno.') ORDER BY no';
if(count($postlist) > 1){ if($postlist[0] > $postlist[1]) $tmpSQL .= ' DESC'; } // 由大排到小
}else $tmpSQL = 'SELECT '.$fields.' FROM '.$this->tablename.' WHERE no = '.intval($postlist); // 取單串
$line = $this->_sqlite_call($tmpSQL, array('Fetch the post content failed', __LINE__));
return sqlite_fetch_all($line, SQLITE_ASSOC);
}
 
/* 刪除舊附件 (輸出附件清單) */
function delOldAttachments($total_size, $storage_max, $warnOnly=true){
$FileIO = PMCLibrary::getFileIOInstance();
if(!$this->prepared) $this->dbPrepare();
 
$arr_warn = $arr_kill = array(); // 警告 / 即將被刪除標記陣列
$result = $this->_sqlite_call('SELECT no,ext,tim FROM '.$this->tablename.' WHERE ext <> \'\' ORDER BY no',
array('Get the old post failed', __LINE__));
while(list($dno, $dext, $dtim) = sqlite_fetch_array($result)){ // 個別跑舊文迴圈
$dfile = $dtim.$dext; // 附加檔案名稱
$dthumb = $FileIO->resolveThumbName($dtim); // 預覽檔案名稱
if($FileIO->imageExists($dfile)){ $total_size -= $FileIO->getImageFilesize($dfile) / 1024; $arr_kill[] = $dno; $arr_warn[$dno] = 1; } // 標記刪除
if($dthumb && $FileIO->imageExists($dthumb)) $total_size -= $FileIO->getImageFilesize($dthumb) / 1024;
if($total_size < $storage_max) break;
}
return $warnOnly ? $arr_warn : $this->removeAttachments($arr_kill);
}
 
/* 刪除文章 */
function removePosts($posts){
if(!$this->prepared) $this->dbPrepare();
if(count($posts)==0) return array();
 
$files = $this->removeAttachments($posts, true); // 先遞迴取得刪除文章及其回應附件清單
$pno = implode(', ', $posts); // ID字串
$this->_sqlite_call('DELETE FROM '.$this->tablename.' WHERE no IN ('.$pno.') OR resto IN('.$pno.')',
array('Delete old posts and replies failed', __LINE__)); // 刪掉文章
return $files;
}
 
/* 刪除附件 (輸出附件清單) */
function removeAttachments($posts, $recursion=false){
$FileIO = PMCLibrary::getFileIOInstance();
if(!$this->prepared) $this->dbPrepare();
if(count($posts)==0) return array();
 
$files = array();
$pno = implode(', ', $posts); // ID字串
if($recursion) $tmpSQL = 'SELECT ext,tim FROM '.$this->tablename.' WHERE (no IN ('.$pno.') OR resto IN('.$pno.")) AND ext <> ''"; // 遞迴取出 (含回應附件)
else $tmpSQL = 'SELECT ext,tim FROM '.$this->tablename.' WHERE no IN ('.$pno.") AND ext <> ''"; // 只有指定的編號
 
$result = $this->_sqlite_call($tmpSQL, array('Get attachments of the post failed', __LINE__));
while(list($dext, $dtim) = sqlite_fetch_array($result)){ // 個別跑迴圈
$dfile = $dtim.$dext; // 附加檔案名稱
$dthumb = $FileIO->resolveThumbName($dtim); // 預覽檔案名稱
if($FileIO->imageExists($dfile)) $files[] = $dfile;
if($dthumb && $FileIO->imageExists($dthumb)) $files[] = $dthumb;
}
return $files;
}
 
/* 新增文章/討論串 */
function addPost($no, $resto, $md5chksum, $category, $tim, $ext, $imgw, $imgh, $imgsize, $tw, $th, $pwd, $now, $name, $email, $sub, $com, $host, $age=false, $status=''){
if(!$this->prepared) $this->dbPrepare();
 
$time = (int)substr($tim, 0, -3); // 13位數的數字串是檔名,10位數的才是時間數值
$updatetime = gmdate('Y-m-d H:i:s'); // 更動時間 (UTC)
$resto = intval($resto);
if($resto){ // 新增回應
$root = '0';
if($age){ // 推文
$this->_sqlite_call('UPDATE '.$this->tablename.' SET root = "'.$updatetime.'" WHERE no = '.$resto,
array('Push the post failed', __LINE__)); // 將被回應的文章往上移動
}
}else $root = $updatetime; // 新增討論串, 討論串最後被更新時間
 
$query = 'INSERT INTO '.$this->tablename.' (resto,root,time,md5chksum,category,tim,ext,imgw,imgh,imgsize,tw,th,pwd,now,name,email,sub,com,host,status) VALUES ('.
$resto.','. // 回應編號
"'$root',". // 最後更新時間
$time.','. // 發文時間數值
"'$md5chksum',". // 附加檔案md5
"'".sqlite_escape_string($category)."',". // 分類標籤
"$tim, '$ext',". // 附加檔名
(int)$imgw.','.(int)$imgh.",'".$imgsize."',".(int)$tw.','.(int)$th.','. // 圖檔長寬及檔案大小;預覽圖長寬
"'".sqlite_escape_string($pwd)."',".
"'$now',". // 時間(含ID)字串
"'".sqlite_escape_string($name)."',".
"'".sqlite_escape_string($email)."',".
"'".sqlite_escape_string($sub)."',".
"'".sqlite_escape_string($com)."',".
"'".sqlite_escape_string($host)."', '".sqlite_escape_string($status)."')";
$this->_sqlite_call($query, array('Insert a new post failed', __LINE__));
}
 
/* 檢查是否連續投稿 */
function isSuccessivePost($lcount, $com, $timestamp, $pass, $passcookie, $host, $isupload){
$FileIO = PMCLibrary::getFileIOInstance();
if(!$this->prepared) $this->dbPrepare();
 
if(!$this->ENV['PERIOD.POST']) return false; // 關閉連續投稿檢查
$timestamp = intval($timestamp);
$tmpSQL = 'SELECT pwd,host FROM '.$this->tablename.' WHERE time > '.($timestamp - (int)$this->ENV['PERIOD.POST']); // 一般投稿時間檢查
if($isupload) $tmpSQL .= ' OR time > '.($timestamp - (int)$this->ENV['PERIOD.IMAGEPOST']); // 附加圖檔的投稿時間檢查 (與下者兩者擇一)
else $tmpSQL .= " OR php('md5', com) = '".md5($com)."'"; // 內文一樣的檢查 (與上者兩者擇一) * 此取巧採用了PHP登錄的函式php來叫用md5
 
$result = $this->_sqlite_call($tmpSQL, array('Get the post to check the succession failed', __LINE__));
while(list($lpwd, $lhost) = sqlite_fetch_array($result)){
// 判斷為同一人發文且符合連續投稿條件
if($host==$lhost || $pass==$lpwd || $passcookie==$lpwd) return true;
}
return false;
}
 
/* 檢查是否重複貼圖 */
function isDuplicateAttachment($lcount, $md5hash){
$FileIO = PMCLibrary::getFileIOInstance();
if(!$this->prepared) $this->dbPrepare();
 
$result = $this->_sqlite_call('SELECT tim,ext FROM '.$this->tablename." WHERE ext <> '' AND md5chksum = '$md5hash' ORDER BY no DESC",
array('Get the post to check the duplicate attachment failed', __LINE__));
while(list($ltim, $lext) = sqlite_fetch_array($result)){
if($FileIO->imageExists($ltim.$lext)) return true; // 有相同檔案
}
return false;
}
 
/* 有此討論串? */
function isThread($no){
if(!$this->prepared) $this->dbPrepare();
 
$result = $this->_sqlite_call('SELECT no FROM '.$this->tablename.' WHERE no = '.intval($no).' AND resto = 0');
return sqlite_fetch_array($result) ? true : false;
}
 
/* 搜尋文章 */
function searchPost($keyword, $field, $method){
if(!$this->prepared) $this->dbPrepare();
 
$keyword_cnt = count($keyword);
$SearchQuery = 'SELECT * FROM '.$this->tablename." WHERE {$field} LIKE '%".sqlite_escape_string($keyword[0])."%'";
if($keyword_cnt > 1){
for($i = 1; $i < $keyword_cnt; $i++){
$SearchQuery .= " {$method} {$field} LIKE '%".sqlite_escape_string($keyword[$i])."%'"; // 多重字串交集 / 聯集搜尋
}
}
$SearchQuery .= ' ORDER BY no DESC'; // 按照號碼大小排序
$line = $this->_sqlite_call($SearchQuery, array('Search the post failed', __LINE__));
return sqlite_fetch_all($line, SQLITE_ASSOC);
}
 
/* 搜尋類別標籤 */
function searchCategory($category){
if(!$this->prepared) $this->dbPrepare();
 
$foundPosts = array();
$SearchQuery = 'SELECT no FROM '.$this->tablename." WHERE lower(category) LIKE '%,".strtolower(sqlite_escape_string($category)).",%' ORDER BY no DESC";
$line = $this->_sqlite_call($SearchQuery, array('Search the category failed', __LINE__));
while($rows = sqlite_fetch_array($line)) $foundPosts[] = $rows[0];
return $foundPosts;
}
 
/* 取得文章屬性 */
function getPostStatus($status){
return new FlagHelper($status); // 回傳 FlagHelper 物件
}
 
/* 更新文章 */
function updatePost($no, $newValues){
if(!$this->prepared) $this->dbPrepare();
 
$no = intval($no);
$chk = array('resto', 'md5chksum', 'category', 'tim', 'ext', 'imgw', 'imgh', 'imgsize', 'tw', 'th', 'pwd', 'now', 'name', 'email', 'sub', 'com', 'host', 'status');
foreach($chk as $c){
if(isset($newValues[$c])){
$this->_sqlite_call('UPDATE '.$this->tablename." SET $c = '".sqlite_escape_string($newValues[$c])."' WHERE no = $no",
array('Update the field of the post failed', __LINE__)); // 更新討論串屬性
}
}
}
 
/* 設定文章屬性 */
function setPostStatus($no, $newStatus){
$this->updatePost($no, array('status' => $newStatus));
}
}
?>
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v7/lib/pio/pio.sqlite3.php
@@ -0,0 +1,419 @@
<?php
/**
* PIO SQLite3 (PDO) API
*
* 提供存取以 SQLite3 資料庫構成的資料結構後端的物件 (需要 PHP 5.1.0 以上並開啟 PDO 功能)
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
*/
 
class PIOsqlite3 implements IPIO {
private $ENV, $DSN, $tablename; // Local Constant
private $con, $prepared, $useTransaction; // Local Global
 
public function __construct($connstr='', $ENV){
$this->ENV = $ENV;
$this->prepared = false;
if($connstr) $this->dbConnect($connstr);
}
 
/* private 攔截SQL錯誤 */
private function _error_handler($errtext, $errline){
$err = sprintf('%s on line %d.', $errtext, $errline);
if (defined('DEBUG') && DEBUG) {
$err .= sprintf(PHP_EOL."Description: %s",
print_r($this->con->errorInfo(), true));
}
throw new RuntimeException($err);
}
 
/* PIO模組版本 */
function pioVersion(){
return '0.6 (v20130102)';
}
 
/* 處理連線字串/連接 */
public function dbConnect($connStr){
// 格式: sqlite3://資料庫檔案之位置/資料表/
// 示例: sqlite3://pixmicat.db/imglog/
//     sqlite3://:memory:/imglog
if(preg_match('/^sqlite3:\/\/(.*)\/(.*)\/$/i', $connStr, $linkinfos)){
$this->DSN = 'sqlite:'.$linkinfos[1];
$this->tablename = $linkinfos[2];
}
}
 
/* 初始化 */
public function dbInit($isAddInitData=true){
$this->dbPrepare();
$nline = $this->con->query('SELECT COUNT(name) FROM sqlite_master WHERE name LIKE "'.$this->tablename.'"')->fetch();
if($nline[0]==='0'){ // 資料表不存在
$result = 'CREATE TABLE '.$this->tablename.' (
"no" INTEGER NOT NULL PRIMARY KEY,
"resto" INTEGER NOT NULL,
"root" TIMESTAMP DEFAULT \'0\' NOT NULL,
"time" INTEGER NOT NULL,
"md5chksum" VARCHAR(32) NOT NULL,
"category" VARCHAR(255) NOT NULL,
"tim" INTEGER NOT NULL,
"ext" VARCHAR(4) NOT NULL,
"imgw" INTEGER NOT NULL,
"imgh" INTEGER NOT NULL,
"imgsize" VARCHAR(10) NOT NULL,
"tw" INTEGER NOT NULL,
"th" INTEGER NOT NULL,
"pwd" VARCHAR(8) NOT NULL,
"now" VARCHAR(255) NOT NULL,
"name" VARCHAR(255) NOT NULL,
"email" VARCHAR(255) NOT NULL,
"sub" VARCHAR(255) NOT NULL,
"com" TEXT NOT NULL,
"host" VARCHAR(255) NOT NULL,
"status" VARCHAR(255) NOT NULL
);'; // PIO Structure V3
$idx = array('resto', 'root', 'time');
foreach($idx as $x) $result .= 'CREATE INDEX IDX_'.$this->tablename.'_'.$x.' ON '.$this->tablename.'('.$x.');';
$result .= 'CREATE INDEX IDX_'.$this->tablename.'_resto_no ON '.$this->tablename.'(resto,no);';
if($isAddInitData) $result .= 'INSERT INTO '.$this->tablename.' (resto,root,time,md5chksum,category,tim,ext,imgw,imgh,imgsize,tw,th,pwd,now,name,email,sub,com,host,status) VALUES (0, datetime("now"), 1111111111, "", "", 1111111111111, "", 0, 0, "", 0, 0, "", "05/01/01(六)00:00", "'.$this->ENV['NONAME'].'", "", "'.$this->ENV['NOTITLE'].'", "'.$this->ENV['NOCOMMENT'].'", "", "");';
$this->con->exec($result);
$this->dbCommit();
}
}
 
/* 準備/讀入 */
public function dbPrepare($reload=false, $transaction=false){
if($this->prepared) return true;
 
($this->con = new PDO($this->DSN, '', '', array(PDO::ATTR_PERSISTENT => true))) or $this->_error_handler('Open database failed', __LINE__);
$this->useTransaction = $transaction;
if($transaction) @$this->con->beginTransaction(); // 啟動交易性能模式
 
$this->prepared = true;
}
 
/* 提交/儲存 */
public function dbCommit(){
if(!$this->prepared) return false;
if($this->useTransaction) @$this->con->commit(); // 交易性能模式提交
}
 
/* 資料表維護 */
public function dbMaintanence($action, $doit=false){
switch($action) {
case 'optimize':
if($doit){
$this->dbPrepare(false);
if($this->con->exec('VACUUM '.$this->tablename)!==false) return true;
else return false;
}else return true; // 支援最佳化資料表
break;
case 'export':
if($doit){
$this->dbPrepare(false);
$gp = gzopen('piodata.log.gz', 'w9');
gzwrite($gp, $this->dbExport());
gzclose($gp);
return '<a href="piodata.log.gz">下載 piodata.log.gz 中介檔案</a>';
}else return true; // 支援匯出資料
break;
case 'check':
case 'repair':
default: return false; // 不支援
}
}
 
/* 匯入資料來源 */
public function dbImport($data){
$this->dbInit(false); // 僅新增結構不新增資料
$data = explode("\r\n", $data);
$data_count = count($data) - 1;
$replaceComma = create_function('$txt', 'return str_replace("&#44;", ",", $txt);');
$SQL = 'INSERT INTO '.$this->tablename.' (no,resto,root,time,md5chksum,category,tim,ext,imgw,imgh,imgsize,tw,th,pwd,now,name,email,sub,com,host,status) VALUES '
.'(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
$PDOStmt = $this->con->prepare($SQL);
for($i = 0; $i < $data_count; $i++){
$line = array_map($replaceComma, explode(',', $data[$i])); // 取代 &#44; 為 ,
$tim = substr($line[5], 0, 10);
$PDOStmt->bindValue(1, $line[0], PDO::PARAM_INT);
$PDOStmt->bindValue(2, $line[1], PDO::PARAM_INT);
$PDOStmt->bindValue(3, $line[2], PDO::PARAM_STR);
$PDOStmt->bindValue(4, $tim, PDO::PARAM_INT);
$PDOStmt->bindValue(5, $line[3], PDO::PARAM_STR);
$PDOStmt->bindValue(6, $line[4], PDO::PARAM_STR);
$PDOStmt->bindValue(7, floatval($line[5])); // 13-digit BIGINT workground
$PDOStmt->bindValue(8, $line[6], PDO::PARAM_STR);
$PDOStmt->bindValue(9, $line[7], PDO::PARAM_INT);
$PDOStmt->bindValue(10, $line[8], PDO::PARAM_INT);
$PDOStmt->bindValue(11, $line[9], PDO::PARAM_STR);
$PDOStmt->bindValue(12, $line[10], PDO::PARAM_INT);
$PDOStmt->bindValue(13, $line[11], PDO::PARAM_INT);
$PDOStmt->bindValue(14, $line[12], PDO::PARAM_STR);
$PDOStmt->bindValue(15, $line[13], PDO::PARAM_STR);
$PDOStmt->bindValue(16, $line[14], PDO::PARAM_STR);
$PDOStmt->bindValue(17, $line[15], PDO::PARAM_STR);
$PDOStmt->bindValue(18, $line[16], PDO::PARAM_STR);
$PDOStmt->bindValue(19, $line[17], PDO::PARAM_STR);
$PDOStmt->bindValue(20, $line[18], PDO::PARAM_STR);
$PDOStmt->bindValue(21, $line[19], PDO::PARAM_STR);
$PDOStmt->execute() or $this->_error_handler('Insert a new post failed', __LINE__);
}
$this->dbCommit(); // 送交
return true;
}
 
/* 匯出資料來源 */
public function dbExport(){
if(!$this->prepared) $this->dbPrepare();
$line = $this->con->query('SELECT no,resto,root,md5chksum,category,tim,ext,imgw,imgh,imgsize,tw,th,pwd,now,name,email,sub,com,host,status FROM '.$this->tablename.' ORDER BY no DESC');
$data = '';
$replaceComma = create_function('$txt', 'return str_replace(",", "&#44;", $txt);');
while($row = $line->fetch(PDO::FETCH_ASSOC)){
$row = array_map($replaceComma, $row); // 取代 , 為 &#44;
$data .= implode(',', $row).",\r\n";
}
return $data;
}
 
/* 文章數目 */
public function postCount($resno=0){
if(!$this->prepared) $this->dbPrepare();
 
if($resno){ // 一討論串文章總數目
$line = $this->con->query('SELECT COUNT(no) FROM '.$this->tablename.' WHERE resto = '.intval($resno))->fetch();
$countline = $line[0] + 1;
}else{ // 文章總數目
$line = $this->con->query('SELECT COUNT(no) FROM '.$this->tablename)->fetch();
$countline = $line[0];
}
return $countline;
}
 
/* 討論串數目 */
public function threadCount(){
if(!$this->prepared) $this->dbPrepare();
 
$tree = $this->con->query('SELECT COUNT(no) FROM '.$this->tablename.' WHERE resto = 0')->fetch();
return $tree[0]; // 討論串目前數目
}
 
/* 取得最後文章編號 */
public function getLastPostNo($state){
if(!$this->prepared) $this->dbPrepare();
 
if($state=='afterCommit'){ // 送出後的最後文章編號
$lastno = $this->con->query('SELECT MAX(no) FROM '.$this->tablename)->fetch();
return $lastno[0];
}else return 0; // 其他狀態沒用
}
 
/* 輸出文章清單 */
public function fetchPostList($resno=0, $start=0, $amount=0){
if(!$this->prepared) $this->dbPrepare();
 
$resno = intval($resno);
if($resno){ // 輸出討論串的結構 (含自己, EX : 1,2,3,4,5,6)
$tmpSQL = 'SELECT no FROM '.$this->tablename.' WHERE no = '.$resno.' OR resto = '.$resno.' ORDER BY no';
}else{ // 輸出所有文章編號,新的在前
$tmpSQL = 'SELECT no FROM '.$this->tablename.' ORDER BY no DESC';
$start = intval($start); $amount = intval($amount);
if($amount) $tmpSQL .= " LIMIT {$start}, {$amount}"; // 指定數量
}
return $this->con->query($tmpSQL)->fetchAll(PDO::FETCH_COLUMN, 0);
}
 
/* 輸出討論串清單 */
public function fetchThreadList($start=0, $amount=0, $isDESC=false) {
if(!$this->prepared) $this->dbPrepare();
 
$tmpSQL = 'SELECT no FROM '.$this->tablename.' WHERE resto = 0 ORDER BY '.($isDESC ? 'no' : 'root').' DESC';
$start = intval($start); $amount = intval($amount);
if($amount) $tmpSQL .= " LIMIT {$start}, {$amount}"; // 指定數量
return $this->con->query($tmpSQL)->fetchAll(PDO::FETCH_COLUMN, 0);
}
 
/* 輸出文章 */
public function fetchPosts($postlist,$fields='*'){
if(!$this->prepared) $this->dbPrepare();
 
if(is_array($postlist)){ // 取多串
$pno = implode(',', $postlist); // ID字串
$tmpSQL = 'SELECT '.$fields.' FROM '.$this->tablename.' WHERE no IN ('.$pno.') ORDER BY no';
if(count($postlist) > 1){ if($postlist[0] > $postlist[1]) $tmpSQL .= ' DESC'; } // 由大排到小
}else $tmpSQL = 'SELECT '.$fields.' FROM '.$this->tablename.' WHERE no = '.intval($postlist); // 取單串
$line = $this->con->query($tmpSQL)->fetchAll(PDO::FETCH_ASSOC);
return $line;
}
 
/* 刪除舊附件 (輸出附件清單) */
public function delOldAttachments($total_size, $storage_max, $warnOnly=true){
$FileIO = PMCLibrary::getFileIOInstance();
if(!$this->prepared) $this->dbPrepare();
 
$arr_warn = $arr_kill = array(); // 警告 / 即將被刪除標記
($result = $this->con->query('SELECT no,ext,tim FROM '.$this->tablename.' WHERE ext <> "" ORDER BY no')) or $this->_error_handler('Get the old post failed', __LINE__);
while(list($dno, $dext, $dtim) = $result->fetch(PDO::FETCH_NUM)){
$dfile = $dtim.$dext; $dthumb = $FileIO->resolveThumbName($dtim);
if($FileIO->imageExists($dfile)){ $total_size -= $FileIO->getImageFilesize($dfile) / 1024; $arr_kill[] = $dno; $arr_warn[$dno] = 1; } // 標記刪除
if($dthumb && $FileIO->imageExists($dthumb)) $total_size -= $FileIO->getImageFilesize($dthumb) / 1024;
if($total_size < $storage_max) break;
}
return $warnOnly ? $arr_warn : $this->removeAttachments($arr_kill);
}
 
/* 刪除文章 */
public function removePosts($posts){
if(!$this->prepared) $this->dbPrepare();
 
$files = $this->removeAttachments($posts, true); // 先遞迴取得刪除文章及其回應附件清單
$pno = implode(', ', $posts); // ID字串
if(!$this->con->exec('DELETE FROM '.$this->tablename.' WHERE no IN ('.$pno.') OR resto IN('.$pno.')')) $this->_error_handler('Delete old posts and replies failed', __LINE__);
return $files;
}
 
/* 刪除附件 (輸出附件清單) */
public function removeAttachments($posts, $recursion=false){
$FileIO = PMCLibrary::getFileIOInstance();
if(!$this->prepared) $this->dbPrepare();
 
$files = array();
$pno = implode(', ', $posts); // ID字串
if($recursion) $tmpSQL = 'SELECT ext,tim FROM '.$this->tablename.' WHERE (no IN ('.$pno.') OR resto IN('.$pno.")) AND ext <> ''"; // 遞迴取出 (含回應附件)
else $tmpSQL = 'SELECT ext,tim FROM '.$this->tablename.' WHERE no IN ('.$pno.") AND ext <> ''"; // 只有指定的編號
 
($result = $this->con->query($tmpSQL)) or $this->_error_handler('Get attachments of the post failed', __LINE__);
while(list($dext, $dtim) = $result->fetch(PDO::FETCH_NUM)){
$dfile = $dtim.$dext; $dthumb = $FileIO->resolveThumbName($dtim);
if($FileIO->imageExists($dfile)) $files[] = $dfile;
if($dthumb && $FileIO->imageExists($dthumb)) $files[] = $dthumb;
}
return $files;
}
 
/* 新增文章/討論串 */
public function addPost($no, $resto, $md5chksum, $category, $tim, $ext, $imgw, $imgh, $imgsize, $tw, $th, $pwd, $now, $name, $email, $sub, $com, $host, $age=false, $status=''){
if(!$this->prepared) $this->dbPrepare();
 
$time = (int)substr($tim, 0, -3); // 13位數的數字串是檔名,10位數的才是時間數值
$updatetime = gmdate('Y-m-d H:i:s'); // 更動時間 (UTC)
if($resto){ // 新增回應
$root = '0';
if($age){ // 推文
$result = $this->con->prepare('UPDATE '.$this->tablename.' SET root = :now WHERE no = :resto');
$result->execute(array(':now' => $updatetime, ':resto' => $resto)) or $this->_error_handler('Push the post failed', __LINE__);
}
}else $root = $updatetime; // 新增討論串, 討論串最後被更新時間
 
$SQL = 'INSERT INTO '.$this->tablename.' (resto,root,time,md5chksum,category,tim,ext,imgw,imgh,imgsize,tw,th,pwd,now,name,email,sub,com,host,status) VALUES '
.'(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
$PDOStmt = $this->con->prepare($SQL);
$PDOStmt->bindValue(1, $resto, PDO::PARAM_INT);
$PDOStmt->bindValue(2, $root, PDO::PARAM_STR);
$PDOStmt->bindValue(3, $time, PDO::PARAM_INT);
$PDOStmt->bindValue(4, $md5chksum, PDO::PARAM_STR);
$PDOStmt->bindValue(5, $category, PDO::PARAM_STR);
$PDOStmt->bindValue(6, floatval($tim)); // 13-digit BIGINT workground
$PDOStmt->bindValue(7, $ext, PDO::PARAM_STR);
$PDOStmt->bindValue(8, $imgw, PDO::PARAM_INT);
$PDOStmt->bindValue(9, $imgh, PDO::PARAM_INT);
$PDOStmt->bindValue(10, $imgsize, PDO::PARAM_STR);
$PDOStmt->bindValue(11, $tw, PDO::PARAM_INT);
$PDOStmt->bindValue(12, $th, PDO::PARAM_INT);
$PDOStmt->bindValue(13, $pwd, PDO::PARAM_STR);
$PDOStmt->bindValue(14, $now, PDO::PARAM_STR);
$PDOStmt->bindValue(15, $name, PDO::PARAM_STR);
$PDOStmt->bindValue(16, $email, PDO::PARAM_STR);
$PDOStmt->bindValue(17, $sub, PDO::PARAM_STR);
$PDOStmt->bindValue(18, $com, PDO::PARAM_STR);
$PDOStmt->bindValue(19, $host, PDO::PARAM_STR);
$PDOStmt->bindValue(20, $status, PDO::PARAM_STR);
$PDOStmt->execute() or $this->_error_handler('Insert a new post failed', __LINE__);
}
 
/* 檢查是否連續投稿 */
public function isSuccessivePost($lcount, $com, $timestamp, $pass, $passcookie, $host, $isupload){
$FileIO = PMCLibrary::getFileIOInstance();
if(!$this->prepared) $this->dbPrepare();
 
if(!$this->ENV['PERIOD.POST']) return false; // 關閉連續投稿檢查
$timestamp = intval($timestamp);
$tmpSQL = 'SELECT pwd,host FROM '.$this->tablename.' WHERE time > '.($timestamp - (int)$this->ENV['PERIOD.POST']); // 一般投稿時間檢查
if($isupload) $tmpSQL .= ' OR time > '.($timestamp - (int)$this->ENV['PERIOD.IMAGEPOST']); // 附加圖檔的投稿時間檢查 (與下者兩者擇一)
else $tmpSQL .= " OR md5(com) = '".md5($com)."'"; // 內文一樣的檢查 (與上者兩者擇一)
$this->con->sqliteCreateFunction('md5', 'md5', 1); // Register MD5 function
($result = $this->con->query($tmpSQL)) or $this->_error_handler('Get the post to check the succession failed', __LINE__);
while(list($lpwd, $lhost) = $result->fetch(PDO::FETCH_NUM)){
// 判斷為同一人發文且符合連續投稿條件
if($host==$lhost || $pass==$lpwd || $passcookie==$lpwd) return true;
}
return false;
}
 
/* 檢查是否重複貼圖 */
public function isDuplicateAttachment($lcount, $md5hash){
$FileIO = PMCLibrary::getFileIOInstance();
if(!$this->prepared) $this->dbPrepare();
 
($result = $this->con->query('SELECT tim,ext FROM '.$this->tablename.' WHERE ext <> "" AND md5chksum = "'.$md5hash.'" ORDER BY no DESC'))
or $this->_error_handler('Get the post to check the duplicate attachment failed', __LINE__);
while(list($ltim, $lext) = $result->fetch(PDO::FETCH_NUM)){
if($FileIO->imageExists($ltim.$lext)) return true; // 有相同檔案
}
return false;
}
 
/* 有此討論串? */
public function isThread($no){
if(!$this->prepared) $this->dbPrepare();
 
$result = $this->con->query('SELECT no FROM '.$this->tablename.' WHERE no = '.intval($no).' AND resto = 0');
return $result->fetch() ? true : false;
}
 
/* 搜尋文章 */
public function searchPost($keyword, $field, $method){
if(!$this->prepared) $this->dbPrepare();
 
$keyword_cnt = count($keyword);
$SearchQuery = 'SELECT * FROM '.$this->tablename." WHERE {$field} LIKE ".$this->con->quote('%'.$keyword[0].'%')."";
if($keyword_cnt > 1) for($i = 1; $i < $keyword_cnt; $i++) $SearchQuery .= " {$method} {$field} LIKE ".$this->con->quote('%'.$keyword[$i].'%'); // 多重字串交集 / 聯集搜尋
$SearchQuery .= ' ORDER BY no DESC'; // 按照號碼大小排序
($line = $this->con->query($SearchQuery)) or $this->_error_handler('Search the post failed', __LINE__);
return $line->fetchAll(PDO::FETCH_ASSOC);
}
 
/* 搜尋類別標籤 */
public function searchCategory($category){
if(!$this->prepared) $this->dbPrepare();
 
$result = $this->con->prepare('SELECT no FROM '.$this->tablename.' WHERE lower(category) LIKE :category ORDER BY no DESC');
$result->execute(array(':category' => '%,'.strtolower($category).',%'));
return $result->fetchAll(PDO::FETCH_COLUMN, 0);
}
 
/* 取得文章屬性 */
public function getPostStatus($status){
return new FlagHelper($status); // 回傳 FlagHelper 物件
}
 
/* 更新文章 */
public function updatePost($no, $newValues){
if(!$this->prepared) $this->dbPrepare();
 
$no = intval($no);
$chk = array('resto', 'md5chksum', 'category', 'tim', 'ext', 'imgw', 'imgh', 'imgsize', 'tw', 'th', 'pwd', 'now', 'name', 'email', 'sub', 'com', 'host', 'status');
foreach($chk as $c){
if(isset($newValues[$c])){
if(!$this->con->exec('UPDATE '.$this->tablename." SET $c = ".$this->con->quote($newValues[$c]).' WHERE no = '.$no))
$this->_error_handler('Update the field of the post failed', __LINE__); // 更新討論串屬性
}
}
}
 
/* 設定文章屬性 */
public function setPostStatus($no, $newStatus){
$this->updatePost($no, array('status' => $newStatus));
}
}
?>
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v7/lib/pio/pio.mysql.php
@@ -0,0 +1,509 @@
<?php
/**
* PIO MySQL API
*
* 提供存取以 MySQL 資料庫構成的資料結構後端的物件
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
* @deprecated
*/
 
class PIOmysql implements IPIO {
var $ENV, $username, $password, $server, $dbname, $tablename; // Local Constant
var $con, $prepared, $useTransaction; // Local Global
 
function PIOmysql($connstr='', $ENV){
$this->ENV = $ENV;
$this->prepared = 0;
if($connstr) $this->dbConnect($connstr);
}
 
/* private 攔截SQL錯誤 */
function _error_handler(array $errarray, $query=''){
$err = sprintf('%s on line %d.', $errarray[0], $errarray[1]);
if (defined('DEBUG') && DEBUG) {
$err .= sprintf(PHP_EOL."Description: #%d: %s".PHP_EOL.
"SQL: %s", mysql_errno(), mysql_error(), $query);
}
throw new RuntimeException($err, mysql_errno());
}
 
/* private 使用SQL字串和MySQL伺服器要求 */
function _mysql_call($query, $errarray=false){
$resource = mysql_query($query);
if(is_array($errarray) && $resource===false) $this->_error_handler($errarray, $query);
else return $resource;
}
 
/* private 由資源輸出陣列 */
function _ArrangeArrayStructure($line){
$posts = array();
while($row = mysql_fetch_array($line, MYSQL_ASSOC)) $posts[] = $row;
mysql_free_result($line);
return $posts;
}
 
/* PIO模組版本 */
function pioVersion(){
return '0.6 (v20121213)';
}
 
/* 處理連線字串/連接 */
function dbConnect($connStr){
// 格式: mysql://帳號:密碼@伺服器位置:埠號(可省略)/資料庫/資料表/
// 示例: mysql://pixmicat:1234@127.0.0.1/pixmicat_use/imglog/
if(preg_match('/^mysql:\/\/(.*)\:(.*)\@(.*(?:\:[0-9]+)?)\/(.*)\/(.*)\/$/i', $connStr, $linkinfos)){
$this->username = $linkinfos[1]; // 登入帳號
$this->password = $linkinfos[2]; // 登入密碼
$this->server = $linkinfos[3]; // 登入伺服器 (含埠號)
$this->dbname = $linkinfos[4]; // 資料庫名稱
$this->tablename = $linkinfos[5]; // 資料表名稱
}
}
 
/* 初始化 */
function dbInit($isAddInitData=true){
$this->dbPrepare();
if(mysql_num_rows(mysql_query("SHOW TABLES LIKE '".$this->tablename."'"))!=1){ // 資料表不存在
if(version_compare(mysql_get_server_info(), '5.5', '>=')){ // 5.5+
$result = "CREATE TABLE ".$this->tablename." (primary key(no),
index (resto),index (root),index (time),
no int(1) not null auto_increment,
resto int(1) not null,
root timestamp null DEFAULT 0,
time int(1) not null,
md5chksum varchar(32) not null,
category varchar(255) not null,
tim bigint(1) not null,
ext varchar(4) not null,
imgw smallint(1) not null,
imgh smallint(1) not null,
imgsize varchar(10) not null,
tw smallint(1) not null,
th smallint(1) not null,
pwd varchar(8) not null,
now varchar(255) not null,
name varchar(255) not null,
email varchar(255) not null,
sub varchar(255) not null,
com text not null,
host varchar(255) not null,
status varchar(255) not null)
ENGINE = MYISAM
COMMENT = 'PIO Structure V3'";
}else{ // 5.5 以前版本
$result = "CREATE TABLE ".$this->tablename." (primary key(no),
index (resto),index (root),index (time),
no int(1) not null auto_increment,
resto int(1) not null,
root timestamp(14) null DEFAULT 0,
time int(1) not null,
md5chksum varchar(32) not null,
category varchar(255) not null,
tim bigint(1) not null,
ext varchar(4) not null,
imgw smallint(1) not null,
imgh smallint(1) not null,
imgsize varchar(10) not null,
tw smallint(1) not null,
th smallint(1) not null,
pwd varchar(8) not null,
now varchar(255) not null,
name varchar(255) not null,
email varchar(255) not null,
sub varchar(255) not null,
com text not null,
host varchar(255) not null,
status varchar(255) not null)
TYPE = MYISAM
COMMENT = 'PIO Structure V3'";
}
 
$result2 = @mysql_query("SHOW CHARACTER SET like 'utf8'"); // 是否支援UTF-8 (MySQL 4.1.1開始支援)
if($result2 && mysql_num_rows($result2)){
$result .= ' CHARACTER SET utf8 COLLATE utf8_general_ci'; // 資料表追加UTF-8編碼
mysql_free_result($result2);
}
mysql_query($result); // 正式新增資料表
// 追加一筆新資料
if($isAddInitData) $this->addPost(1, 0, '', '', 0, '', 0, 0, '', 0, 0, '', '05/01/01(六)00:00', $this->ENV['NONAME'], '', $this->ENV['NOTITLE'], $this->ENV['NOCOMMENT'], '');
$this->dbCommit();
}
}
 
/* 準備/讀入 */
function dbPrepare($reload=false,$transaction=false){
if($this->prepared) return true;
 
if(@!$this->con = mysql_connect($this->server, $this->username, $this->password)) $this->_error_handler(array('Open database failed', __LINE__));
@mysql_select_db($this->dbname, $this->con);
@mysql_query("SET NAMES 'utf8'"); // MySQL資料以UTF-8模式傳送
$this->useTransaction = $transaction;
if($transaction) @mysql_query('START TRANSACTION'); // 啟動交易性能模式
 
$this->prepared = 1;
}
 
/* 提交/儲存 */
function dbCommit(){
if(!$this->prepared) return false;
if($this->useTransaction) @mysql_query('COMMIT'); // 交易性能模式提交
}
 
/* 資料表維護 */
function dbMaintanence($action, $doit=false){
switch($action) {
case 'optimize':
if($doit){
$this->dbPrepare(false);
if($this->_mysql_call('OPTIMIZE TABLES '.$this->tablename)) return true;
else return false;
}else return true; // 支援最佳化資料表
break;
case 'check':
if($doit){
$this->dbPrepare(false);
if($rs=$this->_mysql_call('CHECK TABLE '.$this->tablename)){
mysql_data_seek($rs, mysql_num_rows($rs)-1);
$row = mysql_fetch_assoc($rs);
return 'Table '.$row['Table'].': '.$row['Msg_type'].' = '.$row['Msg_text'];
}
else return false;
}else return true; // 支援檢查資料表
break;
case 'repair':
if($doit){
$this->dbPrepare(false);
if($rs=$this->_mysql_call('REPAIR TABLE '.$this->tablename)){
mysql_data_seek($rs, mysql_num_rows($rs)-1);
$row = mysql_fetch_assoc($rs);
return 'Table '.$row['Table'].': '.$row['Msg_type'].' = '.$row['Msg_text'];
}
else return false;
}else return true; // 支援修復資料表
break;
case 'export':
if($doit){
$this->dbPrepare(false);
$gp = gzopen('piodata.log.gz', 'w9');
gzwrite($gp, $this->dbExport());
gzclose($gp);
return '<a href="piodata.log.gz">下載 piodata.log.gz 中介檔案</a>';
}else return true; // 支援匯出資料
break;
default: return false; // 不支援
}
}
 
/* 匯入資料來源 */
function dbImport($data){
$this->dbInit(false); // 僅新增結構不新增資料
$data = explode("\r\n", $data);
$data_count = count($data) - 1;
$replaceComma = create_function('$txt', 'return str_replace("&#44;", ",", $txt);');
for($i = 0; $i < $data_count; $i++){
$line = array_map($replaceComma, explode(',', $data[$i])); // 取代 &#44; 為 ,
if ($line[2] == '0') $line[2] = '0000-00-00 00:00:00';
$SQL = 'INSERT INTO '.$this->tablename.' (no,resto,root,time,md5chksum,category,tim,ext,imgw,imgh,imgsize,tw,th,pwd,now,name,email,sub,com,host,status) VALUES ('.
$line[0].','.
$line[1].',\''.
$line[2].'\','.
substr($line[5], 0, 10).',\''.
mysql_real_escape_string($line[3], $this->con).'\',\''.
mysql_real_escape_string($line[4], $this->con).'\','.
$line[5].',\''.mysql_real_escape_string($line[6], $this->con).'\','.
$line[7].','.$line[8].',\''.mysql_real_escape_string($line[9], $this->con).'\','.$line[10].','.$line[11].',\''.
mysql_real_escape_string($line[12], $this->con).'\',\''.
mysql_real_escape_string($line[13], $this->con).'\',\''.
mysql_real_escape_string($line[14], $this->con).'\',\''.
mysql_real_escape_string($line[15], $this->con).'\',\''.
mysql_real_escape_string($line[16], $this->con).'\',\''.
mysql_real_escape_string($line[17], $this->con).'\',\''.
mysql_real_escape_string($line[18], $this->con).'\',\''.
mysql_real_escape_string($line[19], $this->con).'\')';
$this->_mysql_call($SQL, array('Import a new post failed', __LINE__));
}
$this->dbCommit(); // 送交
return true;
}
 
/* 匯出資料來源 */
function dbExport(){
if(!$this->prepared) $this->dbPrepare();
$line = $this->_mysql_call('SELECT no,resto,root,md5chksum,category,tim,ext,imgw,imgh,imgsize,tw,th,pwd,now,name,email,sub,com,host,status FROM '.$this->tablename.' ORDER BY no DESC',
array('Export posts failed', __LINE__));
$data = '';
$replaceComma = create_function('$txt', 'return str_replace(",", "&#44;", $txt);');
while($row = mysql_fetch_array($line, MYSQL_ASSOC)){
$row = array_map($replaceComma, $row); // 取代 , 為 &#44;
if($row['root']=='0000-00-00 00:00:00') $row['root'] = '0'; // 初始值設為 0
$data .= rtrim(implode(',', $row)).",\r\n";
}
mysql_free_result($line);
return $data;
}
 
/* 文章數目 */
function postCount($resno=0){
if(!$this->prepared) $this->dbPrepare();
 
if($resno){ // 回傳討論串總文章數目
$line = $this->_mysql_call('SELECT COUNT(no) FROM '.$this->tablename.' WHERE resto = '.intval($resno),
array('Fetch count in thread failed', __LINE__));
$countline = mysql_result($line, 0) + 1;
}else{ // 回傳總文章數目
$line = $this->_mysql_call('SELECT COUNT(no) FROM '.$this->tablename, array('Fetch count of posts failed', __LINE__));
$countline = mysql_result($line, 0);
}
mysql_free_result($line);
return $countline;
}
 
/* 討論串數目 */
function threadCount(){
if(!$this->prepared) $this->dbPrepare();
 
$tree = $this->_mysql_call('SELECT COUNT(no) FROM '.$this->tablename.' WHERE resto = 0',
array('Fetch count of threads failed', __LINE__));
$counttree = mysql_result($tree, 0); mysql_free_result($tree); // 計算討論串目前資料筆數
return $counttree;
}
 
/* 取得最後文章編號 */
function getLastPostNo($state){
if(!$this->prepared) $this->dbPrepare();
 
if($state=='afterCommit'){ // 送出後的最後文章編號
$tree = $this->_mysql_call('SELECT MAX(no) FROM '.$this->tablename, array('Get the last No. failed', __LINE__));
$lastno = mysql_result($tree, 0); mysql_free_result($tree);
return $lastno;
}else return 0; // 其他狀態沒用
}
 
/* 輸出文章清單 */
function fetchPostList($resno=0, $start=0, $amount=0){
if(!$this->prepared) $this->dbPrepare();
 
$line = array();
$resno = intval($resno);
if($resno){ // 輸出討論串的結構 (含自己, EX : 1,2,3,4,5,6)
$tmpSQL = 'SELECT no FROM '.$this->tablename.' WHERE no = '.$resno.' OR resto = '.$resno.' ORDER BY no';
}else{ // 輸出所有文章編號,新的在前
$tmpSQL = 'SELECT no FROM '.$this->tablename.' ORDER BY no DESC';
$start = intval($start); $amount = intval($amount);
if($amount) $tmpSQL .= " LIMIT {$start}, {$amount}"; // 有指定數量才用 LIMIT
}
$tree = $this->_mysql_call($tmpSQL, array('Fetch post list failed', __LINE__));
while($rows = mysql_fetch_row($tree)) $line[] = $rows[0]; // 迴圈
mysql_free_result($tree);
return $line;
}
 
/* 輸出討論串清單 */
function fetchThreadList($start=0, $amount=0, $isDESC=false){
if(!$this->prepared) $this->dbPrepare();
 
$start = intval($start); $amount = intval($amount);
$treeline = array();
$tmpSQL = 'SELECT no FROM '.$this->tablename.' WHERE resto = 0 ORDER BY '.($isDESC ? 'no' : 'root').' DESC';
if($amount) $tmpSQL .= " LIMIT {$start}, {$amount}"; // 有指定數量才用 LIMIT
$tree = $this->_mysql_call($tmpSQL, array('Fetch thread list failed', __LINE__));
while($rows = mysql_fetch_row($tree)) $treeline[] = $rows[0]; // 迴圈
mysql_free_result($tree);
return $treeline;
}
 
/* 輸出文章 */
function fetchPosts($postlist,$fields='*'){
if(!$this->prepared) $this->dbPrepare();
 
if(is_array($postlist)){ // 取多串
$pno = implode(',', $postlist); // ID字串
$tmpSQL = 'SELECT '.$fields.' FROM '.$this->tablename.' WHERE no IN ('.$pno.') ORDER BY no';
if(count($postlist) > 1){ if($postlist[0] > $postlist[1]) $tmpSQL .= ' DESC'; } // 由大排到小
}else $tmpSQL = 'SELECT '.$fields.' FROM '.$this->tablename.' WHERE no = '.intval($postlist); // 取單串
$line = $this->_mysql_call($tmpSQL, array('Fetch the post content failed', __LINE__));
return $this->_ArrangeArrayStructure($line); // 輸出陣列結構
}
 
/* 刪除舊附件 (輸出附件清單) */
function delOldAttachments($total_size, $storage_max, $warnOnly=true){
$FileIO = PMCLibrary::getFileIOInstance();
if(!$this->prepared) $this->dbPrepare();
 
$arr_warn = $arr_kill = array(); // 警告 / 即將被刪除標記陣列
$result = $this->_mysql_call('SELECT no,ext,tim FROM '.$this->tablename.' WHERE ext <> \'\' ORDER BY no',
array('Get old posts failed', __LINE__));
while(list($dno, $dext, $dtim) = mysql_fetch_row($result)){ // 個別跑舊文迴圈
$dfile = $dtim.$dext; // 附加檔案名稱
$dthumb = $FileIO->resolveThumbName($dtim); // 預覽檔案名稱
if($FileIO->imageExists($dfile)){ $total_size -= $FileIO->getImageFilesize($dfile) / 1024; $arr_kill[] = $dno; $arr_warn[$dno] = 1; } // 標記刪除
if($dthumb && $FileIO->imageExists($dthumb)) $total_size -= $FileIO->getImageFilesize($dthumb) / 1024;
if($total_size < $storage_max) break;
}
mysql_free_result($result);
return $warnOnly ? $arr_warn : $this->removeAttachments($arr_kill);
}
 
/* 刪除文章 */
function removePosts($posts){
if(!$this->prepared) $this->dbPrepare();
if(count($posts)==0) return array();
 
$files = $this->removeAttachments($posts, true); // 先遞迴取得刪除文章及其回應附件清單
$pno = implode(', ', $posts); // ID字串
$this->_mysql_call('DELETE FROM '.$this->tablename.' WHERE no IN ('.$pno.') OR resto IN('.$pno.')',
array('Delete old posts and replies failed', __LINE__)); // 刪掉文章
return $files;
}
 
/* 刪除附件 (輸出附件清單) */
function removeAttachments($posts, $recursion=false){
$FileIO = PMCLibrary::getFileIOInstance();
if(!$this->prepared) $this->dbPrepare();
if(count($posts)==0) return array();
 
$files = array();
$pno = implode(', ', $posts); // ID字串
if($recursion) $tmpSQL = 'SELECT ext,tim FROM '.$this->tablename.' WHERE (no IN ('.$pno.') OR resto IN('.$pno.")) AND ext <> ''"; // 遞迴取出 (含回應附件)
else $tmpSQL = 'SELECT ext,tim FROM '.$this->tablename.' WHERE no IN ('.$pno.") AND ext <> ''"; // 只有指定的編號
 
$result = $this->_mysql_call($tmpSQL, array('Get attachments of the post failed', __LINE__));
while(list($dext, $dtim) = mysql_fetch_row($result)){ // 個別跑迴圈
$dfile = $dtim.$dext; // 附加檔案名稱
$dthumb = $FileIO->resolveThumbName($dtim); // 預覽檔案名稱
if($FileIO->imageExists($dfile)) $files[] = $dfile;
if($dthumb && $FileIO->imageExists($dthumb)) $files[] = $dthumb;
}
mysql_free_result($result);
return $files;
}
 
/* 新增文章/討論串 */
function addPost($no, $resto, $md5chksum, $category, $tim, $ext, $imgw, $imgh, $imgsize, $tw, $th, $pwd, $now, $name, $email, $sub, $com, $host, $age=false, $status=''){
if(!$this->prepared) $this->dbPrepare();
 
$time = (int)substr($tim, 0, -3); // 13位數的數字串是檔名,10位數的才是時間數值
$updatetime = gmdate('Y-m-d H:i:s'); // 更動時間 (UTC)
$resto = intval($resto);
if($resto){ // 新增回應
$root = '0000-00-00 00:00:00';
if($age){ // 推文
$this->_mysql_call('UPDATE '.$this->tablename.' SET root = "'.$updatetime.'" WHERE no = '.$resto,
array('Push the post failed', __LINE__)); // 將被回應的文章往上移動
}
}else $root = $updatetime; // 新增討論串, 討論串最後被更新時間
 
$query = 'INSERT INTO '.$this->tablename.' (resto,root,time,md5chksum,category,tim,ext,imgw,imgh,imgsize,tw,th,pwd,now,name,email,sub,com,host,status) VALUES ('.
$resto.','. // 回應編號
"'$root',". // 最後更新時間
$time.','. // 發文時間數值
"'$md5chksum',". // 附加檔案md5
"'".mysql_real_escape_string($category, $this->con)."',". // 分類標籤
"'$tim', '$ext',". // 附加檔名
(int)$imgw.','.(int)$imgh.",'".$imgsize."',".(int)$tw.','.(int)$th.','. // 圖檔長寬及檔案大小;預覽圖長寬
"'".mysql_real_escape_string($pwd, $this->con)."',".
"'$now',". // 時間(含ID)字串
"'".mysql_real_escape_string($name, $this->con)."',".
"'".mysql_real_escape_string($email, $this->con)."',".
"'".mysql_real_escape_string($sub, $this->con)."',".
"'".mysql_real_escape_string($com, $this->con)."',".
"'".mysql_real_escape_string($host, $this->con)."', '".mysql_real_escape_string($status, $this->con)."')";
$this->_mysql_call($query, array('Insert a new post failed', __LINE__));
}
 
/* 檢查是否連續投稿 */
function isSuccessivePost($lcount, $com, $timestamp, $pass, $passcookie, $host, $isupload){
$FileIO = PMCLibrary::getFileIOInstance();
if(!$this->prepared) $this->dbPrepare();
 
if(!$this->ENV['PERIOD.POST']) return false; // 關閉連續投稿檢查
$timestamp = intval($timestamp);
$tmpSQL = 'SELECT pwd,host FROM '.$this->tablename.' WHERE time > '.($timestamp - (int)$this->ENV['PERIOD.POST']); // 一般投稿時間檢查
if($isupload) $tmpSQL .= ' OR time > '.($timestamp - (int)$this->ENV['PERIOD.IMAGEPOST']); // 附加圖檔的投稿時間檢查 (與下者兩者擇一)
else $tmpSQL .= ' OR md5(com) = "'.md5($com).'"'; // 內文一樣的檢查 (與上者兩者擇一)
 
$result = $this->_mysql_call($tmpSQL, array('Get the post to check the succession failed', __LINE__));
while(list($lpwd, $lhost) = mysql_fetch_row($result)){
// 判斷為同一人發文且符合連續投稿條件
if($host==$lhost || $pass==$lpwd || $passcookie==$lpwd) return true;
}
return false;
}
 
/* 檢查是否重複貼圖 */
function isDuplicateAttachment($lcount, $md5hash){
$FileIO = PMCLibrary::getFileIOInstance();
if(!$this->prepared) $this->dbPrepare();
 
$result = $this->_mysql_call('SELECT tim,ext FROM '.$this->tablename." WHERE ext <> '' AND md5chksum = '$md5hash' ORDER BY no DESC",
array('Get the post to check the duplicate attachment failed', __LINE__));
while(list($ltim, $lext) = mysql_fetch_row($result)){
if($FileIO->imageExists($ltim.$lext)) return true; // 有相同檔案
}
return false;
}
 
/* 有此討論串? */
function isThread($no){
if(!$this->prepared) $this->dbPrepare();
 
$result = $this->_mysql_call('SELECT no FROM '.$this->tablename.' WHERE no = '.intval($no).' AND resto = 0');
return mysql_fetch_array($result) ? true : false;
}
 
/* 搜尋文章 */
function searchPost($keyword, $field, $method){
if(!$this->prepared) $this->dbPrepare();
 
$keyword_cnt = count($keyword);
$SearchQuery = 'SELECT * FROM '.$this->tablename." WHERE {$field} LIKE '%".mysql_real_escape_string($keyword[0], $this->con)."%'";
if($keyword_cnt > 1){
for($i = 1; $i < $keyword_cnt; $i++){
$SearchQuery .= " {$method} {$field} LIKE '%".mysql_real_escape_string($keyword[$i], $this->con)."%'"; // 多重字串交集 / 聯集搜尋
}
}
$SearchQuery .= ' ORDER BY no DESC'; // 按照號碼大小排序
$line = $this->_mysql_call($SearchQuery, array('Search the post failed', __LINE__));
return $this->_ArrangeArrayStructure($line); // 輸出陣列結構
}
 
/* 搜尋類別標籤 */
function searchCategory($category){
if(!$this->prepared) $this->dbPrepare();
 
$foundPosts = array();
$SearchQuery = 'SELECT no FROM '.$this->tablename." WHERE lower(category) LIKE '%,".strtolower(mysql_real_escape_string($category, $this->con)).",%' ORDER BY no DESC";
$line = $this->_mysql_call($SearchQuery, array('Search the category failed', __LINE__));
while($rows = mysql_fetch_row($line)) $foundPosts[] = $rows[0];
mysql_free_result($line);
return $foundPosts;
}
 
/* 取得文章屬性 */
function getPostStatus($status){
return new FlagHelper($status); // 回傳 FlagHelper 物件
}
 
/* 更新文章 */
function updatePost($no, $newValues){
if(!$this->prepared) $this->dbPrepare();
 
$no = intval($no);
$chk = array('resto', 'md5chksum', 'category', 'tim', 'ext', 'imgw', 'imgh', 'imgsize', 'tw', 'th', 'pwd', 'now', 'name', 'email', 'sub', 'com', 'host', 'status');
foreach($chk as $c){
if(isset($newValues[$c])){
$this->_mysql_call('UPDATE '.$this->tablename." SET $c = '".mysql_real_escape_string($newValues[$c], $this->con)."', root = root WHERE no = ".$no,
array('Update the field of the post failed', __LINE__)); // 更新討論串屬性
}
}
}
 
/* 設定文章屬性 */
function setPostStatus($no, $newStatus){
$this->updatePost($no, array('status' => $newStatus));
}
}
?>
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v7/lib/pio/pio.pgsql.php
@@ -0,0 +1,459 @@
<?php
/**
* PIO PostgreSQL API
*
* 提供存取以 PostgreSQL 資料庫構成的資料結構後端的物件
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
*/
 
class PIOpgsql implements IPIO {
var $ENV, $username, $password, $server, $port, $dbname, $tablename; // Local Constant
var $con, $prepared, $useTransaction; // Local Global
 
function PIOpgsql($connstr='', $ENV){
$this->ENV = $ENV;
$this->prepared = 0;
if($connstr) $this->dbConnect($connstr);
}
 
/* private 攔截SQL錯誤 */
function _error_handler(array $errarray, $query=''){
$err = sprintf('%s on line %d.', $errarray[0], $errarray[1]);
if (defined('DEBUG') && DEBUG) {
$err .= sprintf(PHP_EOL."Description: %s".PHP_EOL.
"SQL: %s", pg_last_error($this->con), $query);
}
throw new RuntimeException($err);
}
 
/* private 使用SQL字串和PostgreSQL伺服器要求 */
function _pgsql_call($query, $errarray=false){
$resource = pg_query($this->con, $query);
if(is_array($errarray) && $resource===false) $this->_error_handler($errarray, $query);
else return $resource;
}
 
/* private 由資源輸出陣列 */
function _ArrangeArrayStructure($line){
$posts = array();
while($row = pg_fetch_array($line, null, PGSQL_ASSOC)) $posts[] = $row;
pg_free_result($line);
return $posts;
}
 
/* PIO模組版本 */
function pioVersion(){
return '0.6 (v20121213)';
}
 
/* 處理連線字串/連接 */
function dbConnect($connStr){
// 格式: pgsql://帳號:密碼@伺服器位置:埠號(可省略)/資料庫/資料表/
// 示例: pgsql://pixmicat:1234@127.0.0.1/pixmicat_use/imglog/
if(preg_match('/^pgsql:\/\/(.*)\:(.*)\@([^\:]*)((\:)([0-9]+)){0,1}\/(.*)\/(.*)\/$/i', $connStr, $linkinfos)){
$this->username = $linkinfos[1]; // 登入帳號
$this->password = $linkinfos[2]; // 登入密碼
$this->server = $linkinfos[3]; // 登入伺服器
$this->port = ($linkinfos[6] ? $linkinfos[6] : '5432'); // 登入埠號
$this->dbname = $linkinfos[7]; // 資料庫名稱
$this->tablename = $linkinfos[8]; // 資料表名稱
}
}
 
/* 初始化 */
function dbInit($isAddInitData=true){
$this->dbPrepare();
if(pg_num_rows(pg_query($this->con, "SELECT relname FROM pg_class WHERE relname = '".$this->tablename."'"))!=1){ // 資料表不存在
$result = "CREATE SEQUENCE ".$this->tablename."_no_seq;
CREATE TABLE ".$this->tablename." (
\"no\" int NOT NULL DEFAULT nextval('".$this->tablename."_no_seq'),
\"resto\" int NOT NULL,
\"root\" timestamp NULL DEFAULT '1980-01-01 00:00:00',
\"time\" int NOT NULL,
\"md5chksum\" varchar(32) NOT NULL,
\"category\" varchar(255) NOT NULL,
\"tim\" bigint NOT NULL,
\"ext\" varchar(4) NOT NULL,
\"imgw\" smallint NOT NULL,
\"imgh\" smallint NOT NULL,
\"imgsize\" varchar(10) NOT NULL,
\"tw\" smallint NOT NULL,
\"th\" smallint NOT NULL,
\"pwd\" varchar(8) NOT NULL,
\"now\" varchar(255) NOT NULL,
\"name\" varchar(255) NOT NULL,
\"email\" varchar(255) NOT NULL,
\"sub\" varchar(255) NOT NULL,
\"com\" text NOT NULL,
\"host\" varchar(255) NOT NULL,
\"status\" varchar(255) NOT NULL,
PRIMARY KEY (\"no\"));"; // PIO Structure V3
$idxs = array('resto', 'root', 'time');
foreach($idxs as $idx) $result .= 'CREATE INDEX '.$this->tablename.'_'.$idx.'_index ON '.$this->tablename.' ('.$idx.');';
pg_query($this->con, $result); // 正式新增資料表
if($isAddInitData) $this->addPost(1, 0, '', '', 0, '', 0, 0, '', 0, 0, '', '05/01/01(六)00:00', $this->ENV['NONAME'], '', $this->ENV['NOTITLE'], $this->ENV['NOCOMMENT'], ''); // 追加一筆新資料
$this->dbCommit();
}
}
 
/* 準備/讀入 */
function dbPrepare($reload=false, $transaction=true){
if($this->prepared) return true;
 
$this->con = pg_pconnect("host='".$this->server."' port=".$this->port." dbname='".$this->dbname."' user='".$this->username."' password='".$this->password."'");
if(!$this->con) $this->_error_handler(array('Open database failed', __LINE__));
$this->useTransaction = $transaction;
if($transaction) @pg_query($this->con, 'START TRANSACTION;'); // 啟動交易性能模式
 
$this->prepared = 1;
}
 
/* 提交/儲存 */
function dbCommit(){
if(!$this->prepared) return false;
 
if($this->useTransaction) @pg_query($this->con, 'COMMIT;'); // 交易性能模式提交
}
 
/* 資料表維護 */
function dbMaintanence($action, $doit=false){
switch($action) {
case 'optimize':
if($doit){
$this->dbPrepare(false);
if($this->_pgsql_call('VACUUM '.$this->tablename)) return true;
else return false;
}else return true; // 支援最佳化資料表
break;
case 'export':
if($doit){
$this->dbPrepare(false);
$gp = gzopen('piodata.log.gz', 'w9');
gzwrite($gp, $this->dbExport());
gzclose($gp);
return '<a href="piodata.log.gz">下載 piodata.log.gz 中介檔案</a>';
}else return true; // 支援匯出資料
break;
case 'check':
case 'repair':
default: return false; // 不支援
}
}
 
/* 匯入資料來源 */
function dbImport($data){
$this->dbInit(false); // 僅新增結構不新增資料
$data = explode("\r\n", $data);
$data_count = count($data) - 1;
$replaceComma = create_function('$txt', 'return str_replace("&#44;", ",", $txt);');
for($i = 0; $i < $data_count; $i++){
$line = array_map($replaceComma, explode(',', $data[$i])); // 取代 &#44; 為 ,
if($line[2]=='0') $line[2] = '1980-01-01 00:00:00'; // 零值
$SQL = 'INSERT INTO '.$this->tablename.' (no,resto,root,time,md5chksum,category,tim,ext,imgw,imgh,imgsize,tw,th,pwd,now,name,email,sub,com,host,status) VALUES ('.
$line[0].','.
$line[1].',\''.
$line[2].'\','.
substr($line[5], 0, 10).',\''.
pg_escape_string($line[3]).'\',\''.
pg_escape_string($line[4]).'\','.
$line[5].',\''.pg_escape_string($line[6]).'\','.
$line[7].','.$line[8].',\''.pg_escape_string($line[9]).'\','.$line[10].','.$line[11].',\''.
pg_escape_string($line[12]).'\',\''.
pg_escape_string($line[13]).'\',\''.
pg_escape_string($line[14]).'\',\''.
pg_escape_string($line[15]).'\',\''.
pg_escape_string($line[16]).'\',\''.
pg_escape_string($line[17]).'\',\''.
pg_escape_string($line[18]).'\',\''.
pg_escape_string($line[19]).'\');';
$this->_pgsql_call($SQL, array('Insert a new post failed', __LINE__));
}
$this->dbCommit(); // 送交
return true;
}
 
/* 匯出資料來源 */
function dbExport(){
if(!$this->prepared) $this->dbPrepare();
$line = $this->_pgsql_call('SELECT no,resto,root,md5chksum,category,tim,ext,imgw,imgh,imgsize,tw,th,pwd,now,name,email,sub,com,host,status FROM '.$this->tablename.' ORDER BY no DESC',
array('Export posts failed', __LINE__));
$data = '';
$replaceComma = create_function('$txt', 'return str_replace(",", "&#44;", $txt);');
while($row = pg_fetch_array($line, null, PGSQL_ASSOC)){
$row = array_map($replaceComma, $row); // 取代 , 為 &#44;
if($row['root']=='1980-01-01 00:00:00') $row['root'] = '0'; // 初始值設為 0
$data .= implode(',', $row).",\r\n";
}
pg_free_result($line);
return $data;
}
 
/* 文章數目 */
function postCount($resno=0){
if(!$this->prepared) $this->dbPrepare();
 
if($resno){ // 回傳討論串總文章數目
$line = $this->_pgsql_call('SELECT COUNT(no) FROM '.$this->tablename.' WHERE resto = '.intval($resno),
array('Fetch count in thread failed', __LINE__));
$countline = pg_fetch_result($line, 0, 0) + 1;
}else{ // 回傳總文章數目
$line = $this->_pgsql_call('SELECT COUNT(no) FROM '.$this->tablename, array('Fetch count of posts failed', __LINE__));
$countline = pg_fetch_result($line, 0, 0);
}
pg_free_result($line);
return $countline;
}
 
/* 討論串數目 */
function threadCount(){
if(!$this->prepared) $this->dbPrepare();
 
$tree = $this->_pgsql_call('SELECT COUNT(no) FROM '.$this->tablename.' WHERE resto = 0',
array('Fetch count of threads failed', __LINE__));
$counttree = pg_fetch_result($tree, 0, 0); pg_free_result($tree); // 計算討論串目前資料筆數
return $counttree;
}
 
/* 取得最後文章編號 */
function getLastPostNo($state){
if(!$this->prepared) $this->dbPrepare();
 
if($state=='afterCommit'){ // 送出後的最後文章編號
$tree = $this->_pgsql_call('SELECT MAX(no) FROM '.$this->tablename, array('Get the last No. failed', __LINE__));
$lastno = pg_fetch_result($tree, 0, 0); pg_free_result($tree);
return $lastno;
}else return 0; // 其他狀態沒用
}
 
/* 輸出文章清單 */
function fetchPostList($resno=0, $start=0, $amount=0){
if(!$this->prepared) $this->dbPrepare();
 
$line = array();
$resno = intval($resno);
if($resno){ // 輸出討論串的結構 (含自己, EX : 1,2,3,4,5,6)
$tmpSQL = 'SELECT no FROM '.$this->tablename.' WHERE no = '.$resno.' OR resto = '.$resno.' ORDER BY no';
}else{ // 輸出所有文章編號,新的在前
$tmpSQL = 'SELECT no FROM '.$this->tablename.' ORDER BY no DESC';
$start = intval($start); $amount = intval($amount);
if($amount) $tmpSQL .= " LIMIT {$amount} OFFSET {$start}"; // 有指定數量才用 LIMIT
}
$tree = $this->_pgsql_call($tmpSQL, array('Fetch post list failed', __LINE__));
while($rows = pg_fetch_array($tree)) $line[] = $rows[0]; // 迴圈
 
pg_free_result($tree);
return $line;
}
 
/* 輸出討論串清單 */
function fetchThreadList($start=0, $amount=0, $isDESC=false){
if(!$this->prepared) $this->dbPrepare();
 
$start = intval($start); $amount = intval($amount);
$treeline = array();
$tmpSQL = 'SELECT no FROM '.$this->tablename.' WHERE resto = 0 ORDER BY '.($isDESC ? 'no' : 'root').' DESC';
if($amount) $tmpSQL .= " LIMIT {$amount} OFFSET {$start}"; // 有指定數量才用 LIMIT
$tree = $this->_pgsql_call($tmpSQL, array('Fetch thread list failed', __LINE__));
while($rows = pg_fetch_array($tree)) $treeline[] = $rows[0]; // 迴圈
 
pg_free_result($tree);
return $treeline;
}
 
/* 輸出文章 */
function fetchPosts($postlist,$fields='*'){
if(!$this->prepared) $this->dbPrepare();
 
if(is_array($postlist)){ // 取多串
if(!count($postlist)) return array();
$pno = implode(',', $postlist); // ID字串
$tmpSQL = 'SELECT '.$fields.' FROM '.$this->tablename.' WHERE no IN ('.$pno.') ORDER BY no';
if(count($postlist) > 1){ if($postlist[0] > $postlist[1]) $tmpSQL .= ' DESC'; } // 由大排到小
}else $tmpSQL = 'SELECT '.$fields.' FROM '.$this->tablename.' WHERE no = '.intval($postlist); // 取單串
$line = $this->_pgsql_call($tmpSQL, array('Fetch the post content failed', __LINE__));
 
return $this->_ArrangeArrayStructure($line); // 輸出陣列結構
}
 
/* 刪除舊附件 (輸出附件清單) */
function delOldAttachments($total_size, $storage_max, $warnOnly=true){
$FileIO = PMCLibrary::getFileIOInstance();
if(!$this->prepared) $this->dbPrepare();
 
$arr_warn = $arr_kill = array(); // 警告 / 即將被刪除標記陣列
$result = $this->_pgsql_call('SELECT no,ext,tim FROM '.$this->tablename." WHERE ext <> '' ORDER BY no",
array('Get the old post failed', __LINE__));
while(list($dno, $dext, $dtim) = pg_fetch_array($result)){ // 個別跑舊文迴圈
$dfile = $dtim.$dext; // 附加檔案名稱
$dthumb = $FileIO->resolveThumbName($dtim); // 預覽檔案名稱
if($FileIO->imageExists($dfile)){ $total_size -= $FileIO->getImageFilesize($dfile) / 1024; $arr_kill[] = $dno; $arr_warn[$dno] = 1; } // 標記刪除
if($dthumb && $FileIO->imageExists($dthumb)) $total_size -= $FileIO->getImageFilesize($dthumb) / 1024;
if($total_size < $storage_max) break;
}
pg_free_result($result);
return $warnOnly ? $arr_warn : $this->removeAttachments($arr_kill);
}
 
/* 刪除文章 */
function removePosts($posts){
if(!$this->prepared) $this->dbPrepare();
if(count($posts)==0) return array();
 
$files = $this->removeAttachments($posts, true); // 先遞迴取得刪除文章及其回應附件清單
$pno = implode(', ', $posts); // ID字串
$this->_pgsql_call('DELETE FROM '.$this->tablename.' WHERE no IN ('.$pno.') OR resto IN('.$pno.')',
array('Delete old posts and replies failed', __LINE__)); // 刪掉文章
return $files;
}
 
/* 刪除附件 (輸出附件清單) */
function removeAttachments($posts, $recursion=false){
$FileIO = PMCLibrary::getFileIOInstance();
if(!$this->prepared) $this->dbPrepare();
if(count($posts)==0) return array();
 
$files = array();
$pno = implode(', ', $posts); // ID字串
if($recursion) $tmpSQL = 'SELECT ext,tim FROM '.$this->tablename.' WHERE (no IN ('.$pno.') OR resto IN('.$pno.")) AND ext <> ''"; // 遞迴取出 (含回應附件)
else $tmpSQL = 'SELECT ext,tim FROM '.$this->tablename.' WHERE no IN ('.$pno.") AND ext <> ''"; // 只有指定的編號
 
$result = $this->_pgsql_call($tmpSQL, array('Get attachments of the post failed', __LINE__));
while(list($dext, $dtim) = pg_fetch_array($result)){ // 個別跑迴圈
$dfile = $dtim.$dext; // 附加檔案名稱
$dthumb = $FileIO->resolveThumbName($dtim); // 預覽檔案名稱
if($FileIO->imageExists($dfile)) $files[] = $dfile;
if($dthumb && $FileIO->imageExists($dthumb)) $files[] = $dthumb;
}
pg_free_result($result);
return $files;
}
 
/* 新增文章/討論串 */
function addPost($no, $resto, $md5chksum, $category, $tim, $ext, $imgw, $imgh, $imgsize, $tw, $th, $pwd, $now, $name, $email, $sub, $com, $host, $age=false, $status=''){
if(!$this->prepared) $this->dbPrepare();
 
$time = (int)substr($tim, 0, -3); // 13位數的數字串是檔名,10位數的才是時間數值
$updatetime = gmdate('Y-m-d H:i:s'); // 更動時間 (UTC)
$resto = intval($resto);
if($resto){ // 新增回應
$root = '1980-01-01 00:00:00';
if($age){ // 推文
$this->_pgsql_call('UPDATE '.$this->tablename.' SET root = \''.$updatetime.'\' WHERE no = '.$resto,
array('Push the post failed', __LINE__)); // 將被回應的文章往上移動
}
}else $root = $updatetime; // 新增討論串, 討論串最後被更新時間
 
$query = 'INSERT INTO '.$this->tablename.' (resto,root,time,md5chksum,category,tim,ext,imgw,imgh,imgsize,tw,th,pwd,now,name,email,sub,com,host,status) VALUES ('.
$resto.",'". // 回應編號
$root."',". // 最後更新時間
$time.','. // 發文時間數值
"'$md5chksum',". // 附加檔案md5
"'".pg_escape_string($category)."',". // 分類標籤
"'$tim', '$ext',". // 附加檔名
(int)$imgw.','.(int)$imgh.",'".$imgsize."',".(int)$tw.','.(int)$th.','. // 圖檔長寬及檔案大小;預覽圖長寬
"'".pg_escape_string($pwd)."',".
"'$now',". // 時間(含ID)字串
"'".pg_escape_string($name)."',".
"'".pg_escape_string($email)."',".
"'".pg_escape_string($sub)."',".
"'".pg_escape_string($com)."',".
"'".pg_escape_string($host)."', '".pg_escape_string($status)."');";
$this->_pgsql_call($query, array('Insert a new post failed', __LINE__));
}
 
/* 檢查是否連續投稿 */
function isSuccessivePost($lcount, $com, $timestamp, $pass, $passcookie, $host, $isupload){
$FileIO = PMCLibrary::getFileIOInstance();
if(!$this->prepared) $this->dbPrepare();
 
if(!$this->ENV['PERIOD.POST']) return false; // 關閉連續投稿檢查
$timestamp = intval($timestamp);
$tmpSQL = 'SELECT pwd,host FROM '.$this->tablename.' WHERE time > '.($timestamp - (int)$this->ENV['PERIOD.POST']); // 一般投稿時間檢查
if($isupload) $tmpSQL .= ' OR time > '.($timestamp - (int)$this->ENV['PERIOD.IMAGEPOST']); // 附加圖檔的投稿時間檢查 (與下者兩者擇一)
else $tmpSQL .= " OR md5(com) = '".md5($com)."'"; // 內文一樣的檢查 (與上者兩者擇一)
 
$result = $this->_pgsql_call($tmpSQL, array('Get the post to check the succession failed', __LINE__));
while(list($lpwd, $lhost) = pg_fetch_array($result)){
// 判斷為同一人發文且符合連續投稿條件
if($host==$lhost || $pass==$lpwd || $passcookie==$lpwd) return true;
}
return false;
}
 
/* 檢查是否重複貼圖 */
function isDuplicateAttachment($lcount, $md5hash){
$FileIO = PMCLibrary::getFileIOInstance();
if(!$this->prepared) $this->dbPrepare();
 
$result = $this->_pgsql_call('SELECT tim,ext FROM '.$this->tablename." WHERE ext <> '' AND md5chksum = '$md5hash' ORDER BY no DESC",
array('Get the post to check the duplicate attachment failed', __LINE__));
while(list($ltim, $lext) = pg_fetch_array($result)){
if($FileIO->imageExists($ltim.$lext)) return true; // 有相同檔案
}
return false;
}
 
/* 有此討論串? */
function isThread($no){
if(!$this->prepared) $this->dbPrepare();
 
$result = $this->_pgsql_call('SELECT no FROM '.$this->tablename.' WHERE no = '.intval($no).' AND resto = 0');
return pg_fetch_array($result) ? true : false;
}
 
/* 搜尋文章 */
function searchPost($keyword, $field, $method){
if(!$this->prepared) $this->dbPrepare();
 
$keyword_cnt = count($keyword);
$SearchQuery = 'SELECT * FROM '.$this->tablename." WHERE {$field} ILIKE '%".pg_escape_string($keyword[0])."%'";
if($keyword_cnt > 1){
for($i = 1; $i < $keyword_cnt; $i++){
$SearchQuery .= " {$method} {$field} ILIKE '%".pg_escape_string($keyword[$i])."%'"; // 多重字串交集 / 聯集搜尋
}
}
$SearchQuery .= ' ORDER BY no DESC'; // 按照號碼大小排序
$line = $this->_pgsql_call($SearchQuery, array('Search the post failed', __LINE__));
return $this->_ArrangeArrayStructure($line); // 輸出陣列結構
}
 
/* 搜尋類別標籤 */
function searchCategory($category){
if(!$this->prepared) $this->dbPrepare();
 
$foundPosts = array();
$SearchQuery = 'SELECT no FROM '.$this->tablename." WHERE category ~* ',".pg_escape_string($category).",' ORDER BY no DESC";
$line = $this->_pgsql_call($SearchQuery, array('Search the category failed', __LINE__));
while($rows = pg_fetch_array($line)) $foundPosts[] = $rows[0];
pg_free_result($line);
return $foundPosts;
}
 
/* 取得文章屬性 */
function getPostStatus($status){
return new FlagHelper($status); // 回傳 FlagHelper 物件
}
 
/* 更新文章 */
function updatePost($no, $newValues){
if(!$this->prepared) $this->dbPrepare();
 
$no = intval($no);
$chk = array('resto', 'md5chksum', 'category', 'tim', 'ext', 'imgw', 'imgh', 'imgsize', 'tw', 'th', 'pwd', 'now', 'name', 'email', 'sub', 'com', 'host', 'status');
foreach($chk as $c){
if(isset($newValues[$c])){
$this->_pgsql_call('UPDATE '.$this->tablename." SET $c = '".pg_escape_string($newValues[$c])."' WHERE no = ".$no,
array('Update the field of the post failed', __LINE__)); // 更新討論串屬性
}
}
}
 
/* 設定文章屬性 */
function setPostStatus($no, $newStatus){
$this->updatePost($no, array('status' => $newStatus));
}
}
?>
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v7/lib/pmclibrary.php
@@ -0,0 +1,118 @@
<?php
/**
* Pixmicat! Library Singleton Factory
*
* 集中函式庫以方便呼叫,並可回傳單例物件。
*
* @package PMCLibrary
* @version $Id$
* @since 7th.Release
*/
 
require ROOTPATH.'lib/interfaces.php';
require ROOTPATH.'lib/lib_simplelogger.php';
require ROOTPATH.'lib/lib_loggerinterceptor.php';
 
class PMCLibrary {
/**
* 取得 PIO 函式庫物件
*
* @return IPIO PIO 函式庫物件
*/
public static function getPIOInstance() {
global $PIOEnv;
static $instPIO = null;
if ($instPIO == null) {
require ROOTPATH.'lib/lib_pio.php';
$pioExactClass = 'PIO'.PIXMICAT_BACKEND;
$instPIO = new LoggerInjector(
new $pioExactClass(CONNECTION_STRING, $PIOEnv),
new LoggerInterceptor(PMCLibrary::getLoggerInstance($pioExactClass))
);
}
return $instPIO;
}
 
/**
* 取得 PTE 函式庫物件
*
* @return PTELibrary PTE 函式庫物件
*/
public static function getPTEInstance() {
static $instPTE = null;
if ($instPTE == null) {
require ROOTPATH.'lib/lib_pte.php';
$instPTE = new PTELibrary(ROOTPATH.TEMPLATE_FILE);
}
return $instPTE;
}
 
/**
* 取得 PMS 函式庫物件
*
* @return PMS PMS 函式庫物件
*/
public static function getPMSInstance() {
global $ModuleList;
static $instPMS = null;
if ($instPMS == null) {
require ROOTPATH.'lib/lib_pms.php';
$instPMS = new PMS(array( // PMS 環境常數
'MODULE.PATH' => ROOTPATH.'module/',
'MODULE.PAGE' => PHP_SELF.'?mode=module&amp;load=',
'MODULE.LOADLIST' => $ModuleList
));
}
return $instPMS;
}
 
/**
* 取得 FileIO 函式庫物件
*
* @return FileIO FileIO 函式庫物件
*/
public static function getFileIOInstance() {
static $instFileIO = null;
if ($instFileIO == null) {
require ROOTPATH.'lib/lib_fileio.php';
$instFileIO = new FileIOWrapper(unserialize(FILEIO_PARAMETER),
array( // FileIO 環境常數
'IFS.PATH' => ROOTPATH.'lib/fileio/ifs.php',
'IFS.LOG' => ROOTPATH.FILEIO_INDEXLOG,
'PATH' => ROOTPATH,
'IMG' => IMG_DIR,
'THUMB' => THUMB_DIR
)
);
}
return $instFileIO;
}
 
/**
* 取得 Logger 函式庫物件
*
* @param string $name 識別名稱
* @return ILogger Logger 函式庫物件
*/
public static function getLoggerInstance($name = 'Global') {
static $instLogger = array();
if (!array_key_exists($name, $instLogger)) {
$instLogger[$name] = new SimpleLogger($name, ROOTPATH.'error.log');
}
return $instLogger[$name];
}
 
/**
* 取得語言函式庫物件
*
* @return LanguageLoader Language 函式庫物件
*/
public static function getLanguageInstance() {
static $instLanguage = null;
if ($instLanguage == null) {
require ROOTPATH.'lib/lib_language.php';
$instLanguage = LanguageLoader::getInstance();
}
return $instLanguage;
}
}
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v7/lib/lib_compatible.php
@@ -0,0 +1,39 @@
<?php
/**
* Pixmicat! compatible components
*
* @package PMCLibrary
* @version $Id$
*/
 
/**
* 取出翻譯資源檔對應字串。
*
* @param args 翻譯資源檔索引、其餘變數
* @see LanguageLoader->getTranslation
*/
function _T(/*$args[]*/) {
// 因為 5.3 以前 func_get_args 無法直接指派,故需要由變數 $args 承接再帶入
$args = func_get_args();
return call_user_func_array(
array(PMCLibrary::getLanguageInstance(), 'getTranslation'),
$args);
}
 
/**
* 動態附加翻譯資源。此函式已經由 {@link #LanguageLoader->attachLanguage} 取代。
*
* @deprecated 7th.Release. Use LanguageLoader->attachLanguage instead.
* @param callable $fcall 附加翻譯資源字串的函式
*/
function AttachLanguage($fcall){
$GLOBALS['language'] = array();
call_user_func($fcall);
PMCLibrary::getLanguageInstance()->attachLanguage($GLOBALS['language']);
}
 
// 為了相容舊寫法而保留
$PIO = PMCLibrary::getPIOInstance();
$FileIO = PMCLibrary::getFileIOInstance();
$PTE = PMCLibrary::getPTEInstance();
$PMS = PMCLibrary::getPMSInstance();
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v7/lib/lib_language.php
@@ -0,0 +1,119 @@
<?php
/**
* Pixmicat! Language module loader
*
* @package PMCLibrary
* @version $Id$
*/
 
class LanguageLoader {
private $locale;
private $language;
private $languageFallback;
private $hasFallback;
 
private function __construct($locale, array $language) {
$this->locale = $locale;
$this->language = $language;
}
 
/**
* 取得語言物件之單例。
*
* @return LanguageLoader 語言物件
* @throws InvalidArgumentException 如果找不到設定語言
*/
public static function getInstance() {
static $inst = null;
if ($inst == null) {
$locale = PIXMICAT_LANGUAGE;
$langFile = ROOTPATH."lib/lang/{$locale}.php";
if (file_exists($langFile)) {
require $langFile;
} else {
throw new InvalidArgumentException(
sprintf('Assigned locale: %s not found.', $locale)
);
}
$inst = new LanguageLoader($locale, $language);
$inst->setFallback('en_US');
}
return $inst;
}
 
/**
* 設定備用語系。
*
* @param string $localeFallback 備用語系
*/
public function setFallback($localeFallback = 'en_US') {
if ($localeFallback != $this->getLocale()) {
require ROOTPATH."lib/lang/{$localeFallback}.php";
$this->hasFallback = true;
$this->languageFallback = $language;
} else {
// 備用無效
$this->hasFallback = false;
}
}
 
/**
* 取得語系設定。
*
* @see PIXMICAT_LANGUAGE
* @return string 語系代表字串
*/
public function getLocale() {
return $this->locale;
}
 
/**
* 取得翻譯資源字串陣列。
*
* @return array 翻譯字串陣列
*/
public function getLanguage() {
return $this->language;
}
 
/**
* 自翻譯資源字串陣列取出對應文字。
*
* @param string $index 翻譯資源索引
* @return string 對應文字
*/
private function getTranslationBody($index) {
$str = $index;
if (array_key_exists($index, $this->language)) {
$str = $this->language[$index];
} else if ($this->hasFallback && array_key_exists($index, $this->languageFallback)) {
$str = $this->languageFallback[$index];
}
return $str;
}
 
/**
* 取得指定項目之翻譯,並進行變數字串的替代。
*
* @param string arg1 翻譯資源索引字
* @param mixed arg2 變數
* @return string 翻譯後之字串
*/
public function getTranslation(/*args[]*/) {
if (!func_num_args()) {
return '';
}
$argList = func_get_args();
$argList[0] = $this->getTranslationBody($argList[0]);
return call_user_func_array('sprintf', $argList);
}
 
/**
* 附加翻譯資源字串。
*
* @param array $language 翻譯資源字串陣列
*/
public function attachLanguage(array $language) {
$this->language = $this->language + $language;
}
}
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v7/lib/lib_pio.php
@@ -0,0 +1,141 @@
<?php
/*
PIO - Pixmicat! data source I/O
*/
 
// 協助設定 status 旗標的類別
class FlagHelper{
var $_status;
 
function FlagHelper($status=''){
$this->_write($status);
}
 
function _write($status=''){
$this->_status = $status;
}
 
function toString(){
return $this->_status;
}
 
function get($flag){
$result = preg_match('/_('.$flag.'(\:(.*))*)_/U', $this->toString(), $match);
return $result ? $match[1] : false;
}
 
function exists($flag){
return $this->get($flag) !== false;
}
 
function value($flag){
$wholeflag = $this->get($flag);
if($scount = substr_count($wholeflag, ':')){
$wholeflag = preg_replace('/^'.$flag.'\:/', '', $wholeflag);
return ($scount > 1 ? explode(':', $wholeflag) : $wholeflag);
}else return $wholeflag !== false;
}
 
function add($flag, $value=null){
return $this->update($flag, $value);
}
 
function update($flag, $value=null){
if($value===null){
$ifexist = $this->get($flag);
if($ifexist !== $flag) $this->_write($this->toString()."_${flag}_");
}else{
if(is_array($value)) $value = $this->join($value); // Array Flatten
$ifexist = $this->get($flag);
if($ifexist !== $flag.':'.$value){
if($ifexist) $this->_write($this->replace($ifexist, "$flag:$value")); // 已立flag,不同值
else $this->_write($this->toString()."_$flag:${value}_"); // 無flag
}
}
return $this;
}
 
function replace($from, $to){
return str_replace("_${from}_", "_${to}_", $this->toString());
}
 
function remove($flag){
$wholeflag = $this->get($flag);
$this->_write(str_replace("_${wholeflag}_", '', $this->toString()));
return $this;
}
 
function toggle($flag){
return ($this->get($flag) ? $this->remove($flag) : $this->add($flag));
}
 
function offsetValue($flag, $d=0){
$v = intval($this->value($flag));
return $this->update($flag, $v + $d);
}
 
function plus($flag){ return $this->offsetValue($flag, 1); }
function minus($flag){ return $this->offsetValue($flag, -1); }
 
function join(){
$arg = func_get_args();
$newval = array();
foreach($arg as $a){
if(is_array($a)) array_push($newval, implode(':', $a));
else array_push($newval, $a);
}
return implode(':', $newval);
}
 
public function __toString() {
return sprintf('%s {status = %s}', __CLASS__, $this->toString());
}
}
 
// 文章自動刪除機制
include(ROOTPATH.'lib/lib_pio.cond.php');
class PIOSensor{
public static function check($type, array $condobj){
foreach($condobj as $i => $j){
// 有其中一個需要處理
if(call_user_func_array(array($i, 'check'), array($type, $j))===true) return true;
}
return false;
}
 
public static function listee($type, array $condobj){
$tmparray = array(); // 項目陣列
foreach($condobj as $i => $j){
// 結果併進 $tmparray
$tmparray = array_merge($tmparray, call_user_func_array(array($i, 'listee'), array($type, $j)));
}
sort($tmparray); // 由舊排到新 (小到大)
return array_unique($tmparray);
}
 
public static function info(array $condobj){
$sensorinfo='';
foreach($condobj as $i => $j){
$sensorinfo .= call_user_func_array(array($i, 'info'), array($j))."\n";
}
return $sensorinfo;
}
}
 
// 分析連線字串
if(preg_match('/^(.*):\/\//i', CONNECTION_STRING, $backend)) define('PIXMICAT_BACKEND', $backend[1]);
 
// 引入必要函式庫
$PIOEnv = array( // PIO 環境常數
'BOARD' => ROOTPATH,
'LUTCACHE' => 'lutcache.dat',
'NONAME' => DEFAULT_NONAME,
'NOTITLE' => DEFAULT_NOTITLE,
'NOCOMMENT' => DEFAULT_NOCOMMENT,
'PERIOD.POST' => RENZOKU,
'PERIOD.IMAGEPOST' => RENZOKU2
);
 
$pio_file = ROOTPATH.'lib/pio/pio.'.PIXMICAT_BACKEND.'.php';
if(is_file($pio_file)) include($pio_file);
?>
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v7/lib/lib_errorhandler.php
@@ -0,0 +1,43 @@
<?php
/**
* Global error handler
*
* @package PMCLibrary
* @version $Id$
*/
 
/**
* Handles normal PHP errors
*/
function errorHandler($errno, $errstr, $errfile, $errline) {
// Ignore @ prefix suppressed error
if (!(error_reporting() & $errno)) {
return;
}
 
PMCLibrary::getLoggerInstance('Global')->
error('Error caught: #%d: %s in %s on line %d',
$errno, $errstr, $errfile, $errline);
}
set_error_handler('errorHandler');
 
/**
* Handles fatal PHP errors. Only PHP 5.2+ supports this method.
*/
function fatalErrorHandler() {
$e = error_get_last();
if($e !== NULL) {
PMCLibrary::getLoggerInstance('Global')->
error('Fatal error caught: #%d: %s in %s on line %d',
$e['type'], $e['message'], $e['file'], $e['line']);
}
}
register_shutdown_function('fatalErrorHandler');
 
/**
* Handles thrown exceptions by program itself or PHP.
*/
function exceptionHandler($e) {
PMCLibrary::getLoggerInstance('Global')->error('Exception caught: %s', $e);
}
set_exception_handler('exceptionHandler');
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v7/lib/lib_pte.php
@@ -0,0 +1,87 @@
<?php
/*
Pixmicat! Template-Embedded Library v070618
by: scribe & RT
Copyright(C) 2005-2007 Pixmicat! Development Team
 
Pixmicat! Template-Embedded Library (PTE) is released under The Clarified
Artistic License.
A more detailed definition of the terms please refer to the attached "LICENSE"
file. If you do not receive the program with The Artistic License copy, please
visit http://pixmicat.openfoundry.org/license/ to obtain a copy.
 
$Id$
*/
 
class PTELibrary{
var $tpl_block, $tpl;
 
/* 開啟樣板檔案並取出區塊 */
function PTELibrary($tplname){
$this->tpl_block = array();
$this->tpl = file_get_contents($tplname);
}
 
/* 回傳區塊樣板碼並快取 */
function _readBlock($blockName){
if(!isset($this->tpl_block[$blockName])){ // 是否找過
if(preg_match('/<!--&'.$blockName.'-->(.*)<!--\/&'.$blockName.'-->/smU', $this->tpl, $matches))
$this->tpl_block[$blockName] = $matches[1]; // 找到了存入陣列快取
else
$this->tpl_block[$blockName] = false; // 找過但沒找到
}
return $this->tpl_block[$blockName];
}
 
/* 回傳去除前後空格的區塊樣板碼 */
function BlockValue($blockName){
return trim($this->_readBlock($blockName));
}
 
/* 將樣版的標籤取代為正確的字串並傳回 */
function ParseBlock($blockName, $ary_val){
if(($tmp_block = $this->_readBlock($blockName))===false) return ""; // 找無
foreach($ary_val as $akey=>$aval) $ary_val[$akey] = str_replace('{$', '{'.chr(1).'$', $ary_val[$akey]);
$tmp_block = $this->EvalFOREACH($tmp_block, $ary_val); // 解析FOREACH敘述
$tmp_block = $this->EvalIF($tmp_block, $ary_val); // 解析IF敘述
$tmp_block = $this->EvalInclude($tmp_block, $ary_val); // 解析引用
return @str_replace('{'.chr(1).'$','{$',@str_replace(@array_keys($ary_val), @array_values($ary_val), $tmp_block));
}
 
/* 解析IF敘述 */
function EvalIF($tpl, $ary){
$tmp_tpl = $tpl;
if(preg_match_all('/<!--&IF\(([\$&].*),\'(.*)\',\'(.*)\'\)-->/smU', $tmp_tpl, $matches, PREG_SET_ORDER)){
foreach($matches as $submatches){
$isblock = substr($submatches[1],0,1) == "&"; $vari = substr($submatches[1],1); $iftrue = $submatches[2]; $iffalse = $submatches[3];
$tmp_tpl = @str_replace($submatches[0], (($isblock ? $this->BlockValue($vari) : ($ary['{$'.$vari.'}'] !== '' && $ary['{$'.$vari.'}'] !== false && $ary['{$'.$vari.'}'] !== null)) ? $this->EvalInclude($iftrue, $ary) : $this->EvalInclude($iffalse, $ary)), $tmp_tpl);
}
}
return $tmp_tpl;
}
/* 解析FOREACH敘述 */
function EvalFOREACH($tpl, $ary){
$tmp_tpl = $tpl;
if(preg_match_all('/<!--&FOREACH\((\$.*),\'(.*)\'\)-->/smU', $tmp_tpl, $matches, PREG_SET_ORDER)){
foreach($matches as $submatches){
$vari = $submatches[1]; $block = $submatches[2];
$foreach_tmp = '';
if(isset($ary['{'.$vari.'}']) && is_array($ary['{'.$vari.'}']))
foreach($ary['{'.$vari.'}'] as $eachvar)
$foreach_tmp .= $this->ParseBlock($block, $eachvar);
$tmp_tpl = @str_replace($submatches[0], $foreach_tmp, $tmp_tpl);
}
}
return $tmp_tpl;
}
/* 解析區塊引用 */
function EvalInclude($tpl, $ary){
$tmp_tpl = $tpl;
if(preg_match_all('/<!--&(.*)\/-->/smU', $tmp_tpl, $matches, PREG_SET_ORDER))
foreach($matches as $submatches)
$tmp_tpl = str_replace($submatches[0], $this->ParseBlock($submatches[1], $ary), $tmp_tpl);
return $tmp_tpl;
}
}
?>
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v7/lib/lib_common.php
@@ -0,0 +1,271 @@
<?php
/**
* Pixmicat! Common Library
*
* 存放常用函式供主程式引入
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
*/
 
/* 輸出表頭 */
function head(&$dat,$resno=0){
$PTE = PMCLibrary::getPTEInstance();
$PMS = PMCLibrary::getPMSInstance();
 
$pte_vals = array('{$TITLE}'=>TITLE,'{$RESTO}'=>$resno?$resno:'');
$dat .= $PTE->ParseBlock('HEADER',$pte_vals);
$PMS->useModuleMethods('Head', array(&$dat,$resno)); // "Head" Hook Point
$pte_vals+=array('{$ALLOW_UPLOAD_EXT}' => ALLOW_UPLOAD_EXT,
'{$JS_REGIST_WITHOUTCOMMENT}' => str_replace('\'', '\\\'', _T('regist_withoutcomment')),
'{$JS_REGIST_UPLOAD_NOTSUPPORT}' => str_replace('\'', '\\\'', _T('regist_upload_notsupport')),
'{$JS_CONVERT_SAKURA}' => str_replace('\'', '\\\'', _T('js_convert_sakura')));
$dat .= $PTE->ParseBlock('JSHEADER',$pte_vals);
$dat .= '</head>';
$pte_vals += array('{$TOP_LINKS}' => TOP_LINKS,
'{$HOME}' => '[<a href="'.HOME.'" rel="_top">'._T('head_home').'</a>]',
'{$STATUS}' => '[<a href="'.PHP_SELF.'?mode=status">'._T('head_info').'</a>]',
'{$ADMIN}' => '[<a href="'.PHP_SELF.'?mode=admin">'._T('head_admin').'</a>]',
'{$REFRESH}' => '[<a href="'.PHP_SELF2.'?">'._T('head_refresh').'</a>]',
'{$SEARCH}' => (USE_SEARCH) ? '[<a href="'.PHP_SELF.'?mode=search">'._T('head_search').'</a>]' : '',
'{$HOOKLINKS}' => '');
$PMS->useModuleMethods('Toplink', array(&$pte_vals['{$HOOKLINKS}'],$resno)); // "Toplink" Hook Point
$dat .= $PTE->ParseBlock('BODYHEAD',$pte_vals);
}
 
/* 發表用表單輸出 */
function form(&$dat, $resno, $iscollapse=true, $retURL=PHP_SELF, $name='', $mail='', $sub='', $com='', $cat='', $mode='regist'){
global $ADDITION_INFO;
$PTE = PMCLibrary::getPTEInstance();
$PMS = PMCLibrary::getPMSInstance();
 
$pte_vals = array('{$SELF}'=>$retURL, '{$FORMTOP}'=>'', '{$MODE}'=>$mode);
$isedit = ($mode == 'edit'); // 是否為編輯模式
if($resno && !$isedit){
$links = '[<a href="'.PHP_SELF2.'?'.time().'">'._T('return').'</a>]';
$PMS->useModuleMethods('LinksAboveBar', array(&$links,'reply',$resno)); // "LinksAboveBar" Hook Point
$pte_vals['{$FORMTOP}'] = $links.'<div class="bar_reply">'._T('form_top').'</div>';
}
if(USE_FLOATFORM && !$resno && $iscollapse) $pte_vals['{$FORMTOP}'] .= "\n".'[<span id="show" class="hide" onmouseover="showform();" onclick="showform();">'._T('form_showpostform').'</span><span id="hide" class="show" onmouseover="hideform();" onclick="hideform();">'._T('form_hidepostform').'</span>]';
$pte_vals += array('{$MAX_FILE_SIZE}' => MAX_KB * 1024,
'{$RESTO}' => $resno ? '<input type="hidden" name="resto" value="'.$resno.'" />' : '',
'{$FORM_NAME_TEXT}' => _T('form_name'),
'{$FORM_NAME_FIELD}' => '<input class="hide" type="text" name="name" value="spammer" /><input type="text" name="'.FT_NAME.'" id="fname" size="28" value="'.$name.'" />',
'{$FORM_EMAIL_TEXT}' => _T('form_email'),
'{$FORM_EMAIL_FIELD}' => '<input type="text" name="'.FT_EMAIL.'" id="femail" size="28" value="'.$mail.'" /><input type="text" class="hide" name="email" value="foo@foo.bar" />',
'{$FORM_TOPIC_TEXT}' => _T('form_topic'),
'{$FORM_TOPIC_FIELD}' => '<input class="hide" value="DO NOT FIX THIS" type="text" name="sub" /><input type="text" name="'.FT_SUBJECT.'" id="fsub" size="28" value="'.$sub.'" />',
'{$FORM_SUBMIT}' => '<input type="submit" name="sendbtn" value="'._T('form_submit_btn').'" />',
'{$FORM_COMMENT_TEXT}' => _T('form_comment'),
'{$FORM_COMMENT_FIELD}' => '<textarea name="'.FT_COMMENT.'" id="fcom" cols="48" rows="4" style="width: 400px; height: 80px;">'.$com.'</textarea><textarea name="com" class="hide" cols="48" rows="4">EID OG SMAPS</textarea>',
'{$FORM_DELETE_PASSWORD_FIELD}' => '<input type="password" name="pwd" size="8" maxlength="8" value="" />',
'{$FORM_DELETE_PASSWORD_TEXT}' => _T('form_delete_password'),
'{$FORM_DELETE_PASSWORD_NOTICE}' => _T('form_delete_password_notice'),
'{$FORM_EXTRA_COLUMN}' => '',
'{$FORM_NOTICE}' => _T('form_notice',str_replace('|',', ',ALLOW_UPLOAD_EXT),MAX_KB,($resno ? MAX_RW : MAX_W),($resno ? MAX_RH : MAX_H)),
'{$HOOKPOSTINFO}' => '',
'{$ADDITION_INFO}' => $ADDITION_INFO,
'{$FORM_NOTICE_NOSCRIPT}' => _T('form_notice_noscript'));
$PMS->useModuleMethods('PostForm', array(&$pte_vals['{$FORM_EXTRA_COLUMN}'])); // "PostForm" Hook Point
if(!$isedit && (RESIMG || !$resno)){
$pte_vals += array('{$FORM_ATTECHMENT_TEXT}' => _T('form_attechment'),
'{$FORM_ATTECHMENT_FIELD}' => '<input type="file" name="upfile" id="fupfile" size="25" /><input class="hide" type="checkbox" name="reply" value="yes" />',
'{$FORM_NOATTECHMENT_TEXT}' => _T('form_noattechment'),
'{$FORM_NOATTECHMENT_FIELD}' => '<input type="checkbox" name="noimg" id="noimg" value="on" />');
if(USE_UPSERIES) { // 啟動連貼機能
$pte_vals['{$FORM_CONTPOST_FIELD}'] = '<input type="checkbox" name="up_series" id="up_series" value="on"'.((isset($_GET["upseries"]) && $resno)?' checked="checked"':'').' />';
$pte_vals['{$FORM_CONTPOST_TEXT}'] = _T('form_contpost');
}
}
if(USE_CATEGORY) {
$pte_vals += array('{$FORM_CATEGORY_FIELD}' => '<input type="text" name="category" size="28" value="'.$cat.'" />',
'{$FORM_CATEGORY_TEXT}' => _T('form_category'),
'{$FORM_CATEGORY_NOTICE}' => _T('form_category_notice'));
}
if(STORAGE_LIMIT) $pte_vals['{$FORM_NOTICE_STORAGE_LIMIT}'] = _T('form_notice_storage_limit',total_size(),STORAGE_MAX);
$PMS->useModuleMethods('PostInfo', array(&$pte_vals['{$HOOKPOSTINFO}'])); // "PostInfo" Hook Point
 
if(USE_FLOATFORM && !$resno && $iscollapse) $pte_vals['{$FORMBOTTOM}'] = '<script type="text/javascript">hideform();</script>';
$dat .= $PTE->ParseBlock('POSTFORM',$pte_vals);
}
 
/* 輸出頁尾文字 */
function foot(&$dat){
$PTE = PMCLibrary::getPTEInstance();
$PMS = PMCLibrary::getPMSInstance();
 
$pte_vals = array('{$FOOTER}'=>'<!-- GazouBBS v3.0 --><!-- ふたば改0.8 --><!-- Pixmicat! -->');
$PMS->useModuleMethods('Foot', array(&$pte_vals['{$FOOTER}'])); // "Foot" Hook Point
$pte_vals['{$FOOTER}'] .= '<small>- <a href="http://php.s3.to" rel="_top">GazouBBS</a> + <a href="http://www.2chan.net/" rel="_top">futaba</a> + <a href="http://pixmicat.openfoundry.org/" rel="_blank">Pixmicat!</a> -</small>';
$dat .= $PTE->ParseBlock('FOOTER',$pte_vals);
}
 
/* 網址自動連結 */
function auto_link_callback($matches){
return (strtolower($matches[3]) == "</a>") ? $matches[0] : preg_replace('/(https?|ftp|news)(:\/\/[\w\+\$\;\?\.\{\}%,!#~*\/:@&=_-]+)/u', '<a href="$1$2" rel="_blank">$1$2</a>', $matches[0]);
}
function auto_link($proto){
$proto = preg_replace('|<br\s*/?>|',"\n",$proto);
$proto = preg_replace_callback('/(>|^)([^<]+?)(<.*?>|$)/m','auto_link_callback',$proto);
return str_replace("\n",'<br />',$proto);
}
 
/* 引用標註 */
function quoteLight($comment){
return preg_replace('/(^|<br \/>)((?:&gt;|>).*?)(?=<br \/>|$)/u', '$1<span class="resquote">$2</span>', $comment);
}
 
/* 取得完整的網址 */
function fullURL(){
return 'http://'.$_SERVER['HTTP_HOST'].substr($_SERVER['PHP_SELF'], 0, strpos($_SERVER['PHP_SELF'], PHP_SELF));
}
 
/* 反櫻花字 */
function anti_sakura($str){
return preg_match('/[\x{E000}-\x{F848}]/u', $str);
}
 
/* 輸出錯誤畫面 */
function error($mes, $dest=''){
$PTE = PMCLibrary::getPTEInstance();
 
if(is_file($dest)) unlink($dest);
$pte_vals = array('{$SELF2}'=>PHP_SELF2.'?'.time(), '{$MESG}'=>$mes, '{$RETURN_TEXT}'=>_T('return'), '{$BACK_TEXT}'=>_T('error_back'));
$dat = '';
head($dat);
$dat .= $PTE->ParseBlock('ERROR',$pte_vals);
foot($dat);
exit($dat);
}
 
/* 文字修整 */
function CleanStr($str, $IsAdmin=false){
$str = trim($str); // 去除前後多餘空白
if(get_magic_quotes_gpc()) $str = stripslashes($str); // "\"斜線符號去除
// XML 1.1 Second Edition: 部分避免用字 (http://www.w3.org/TR/2006/REC-xml11-20060816/#charsets)
$str = preg_replace('/([\x1-\x8\xB-\xC\xE-\x1F\x7F-\x84\x86-\x9F\x{FDD0}-\x{FDDF}])/u', '', htmlspecialchars($str));
 
if($IsAdmin && CAP_ISHTML){ // 管理員開啟HTML
$str = preg_replace('/&lt;(.*?)&gt;/', '<$1>', $str); // 如果有&lt;...&gt;則轉回<...>成為正常標籤
}
return $str;
}
 
/* 適用UTF-8環境的擬substr,取出特定數目字元
原出處:Sea Otter @ 2005.05.10
http://www.meyu.net/star/viewthread.php?tid=267&fpage=10 */
function str_cut($str, $maxlen=20){
$i = $l = 0; $len = strlen($str); $f = true; $return_str = $str;
while($i < $len){
$chars = ord($str{$i});
if($chars < 0x80){ $l++; $i++; }
elseif($chars < 0xe0){ $l++; $i += 2; }
elseif($chars < 0xf0){ $l += 2; $i += 3; }
elseif($chars < 0xf8){ $l++; $i += 4; }
elseif($chars < 0xfc){ $l++; $i += 5; }
elseif($chars < 0xfe){ $l++; $i += 6; }
if(($l >= $maxlen) && $f){
$return_str = substr($str, 0, $i);
$f = false;
}
if(($l > $maxlen) && ($i <= $len)){
$return_str = $return_str.'…';
break;
}
}
return $return_str;
}
 
/* 檢查瀏覽器和伺服器是否支援gzip壓縮方式 */
function CheckSupportGZip(){
$HTTP_ACCEPT_ENCODING = isset($_SERVER['HTTP_ACCEPT_ENCODING']) ? $_SERVER['HTTP_ACCEPT_ENCODING'] : '';
if(headers_sent() || connection_aborted()) return 0; // 已送出資料,取消
if(!(function_exists('gzencode') && function_exists('ob_start') && function_exists('ob_get_clean'))) return 0; // 伺服器相關的套件或函式無法使用,取消
if(strpos($HTTP_ACCEPT_ENCODING, 'gzip')!==false) return 'gzip';
return 0;
}
 
/* 封鎖 IP / Hostname / DNSBL 綜合性檢查 */
function BanIPHostDNSBLCheck($IP, $HOST, &$baninfo){
if(!BAN_CHECK) return false; // Disabled
global $BANPATTERN, $DNSBLservers, $DNSBLWHlist;
 
// IP/Hostname Check
$HOST = strtolower($HOST);
$checkTwice = ($IP != $HOST); // 是否需檢查第二次
$IsBanned = false;
foreach($BANPATTERN as $pattern){
$slash = substr_count($pattern, '/');
if($slash==2){ // RegExp
$pattern .= 'i';
}elseif($slash==1){ // CIDR Notation
if(matchCIDR($IP, $pattern)){ $IsBanned = true; break; }
continue;
}elseif(strpos($pattern, '*')!==false || strpos($pattern, '?')!==false){ // Wildcard
$pattern = '/^'.str_replace(array('.', '*', '?'), array('\.', '.*', '.?'), $pattern).'$/i';
}else{ // Full-text
if($IP==$pattern || ($checkTwice && $HOST==strtolower($pattern))){ $IsBanned = true; break; }
continue;
}
if(preg_match($pattern, $HOST) || ($checkTwice && preg_match($pattern, $IP))){ $IsBanned = true; break; }
}
if($IsBanned){ $baninfo = _T('ip_banned'); return true; }
 
// DNS-based Blackhole List(DNSBL) 黑名單
if(!$DNSBLservers[0]) return false; // Skip check
if(array_search($IP, $DNSBLWHlist)!==false) return false; // IP位置在白名單內
$rev = implode('.', array_reverse(explode('.', $IP)));
$lastPoint = count($DNSBLservers) - 1; if($DNSBLservers[0] < $lastPoint) $lastPoint = $DNSBLservers[0];
$isListed = false;
for($i = 1; $i <= $lastPoint; $i++){
$query = $rev.'.'.$DNSBLservers[$i].'.'; // FQDN
$result = gethostbyname($query);
if($result && ($result != $query)){ $isListed = $DNSBLservers[$i]; break; }
}
if($isListed){ $baninfo = _T('ip_dnsbl_banned',$isListed); return true; }
return false;
}
function matchCIDR($addr, $cidr) {
list($ip, $mask) = explode('/', $cidr);
return (ip2long($addr) >> (32 - $mask) == ip2long($ip.str_repeat('.0', 3 - substr_count($ip, '.'))) >> (32 - $mask));
}
 
/* 後端登入權限管理 */
function adminAuthenticate($mode){
@session_start();
$loginkey = md5($_SERVER['HTTP_USER_AGENT'].ADMIN_PASS.$_SERVER['REMOTE_ADDR']);
switch($mode){
case 'logout':
if(isset($_SESSION['pmcLogin'])) unset($_SESSION['pmcLogin']);
return true; break;
case 'login':
$_SESSION['pmcLogin'] = $loginkey;
break;
case 'check':
if(isset($_SESSION['pmcLogin']) && $_SESSION['pmcLogin']==$loginkey){
session_regenerate_id(true); // 更換 Session id key 避免 Hijacking
return true;
}
return false;
break;
}
}
 
/* 取得 (Transparent) Proxy 提供之 IP 參數 */
function getREMOTE_ADDR(){
// 確定有需要才使用 HTTP_X_FORWARDED_FOR
if(defined('TRUST_HTTP_X_FORWARDED_FOR') && TRUST_HTTP_X_FORWARDED_FOR) {
// 同時有 VIA 和 FORWARDED_FOR 較可能為 Proxy
if(isset($_SERVER['HTTP_VIA']) && isset($_SERVER['HTTP_X_FORWARDED_FOR'])){
$tmp = preg_split('/[ ,]+/', $_SERVER['HTTP_X_FORWARDED_FOR']);
// 防止 Squid "unknown" 問題,此種情況直接使用 REMOTE_ADDR
// 如果結果為 Private IP 或 Reserved IP,捨棄改用 REMOTE_ADDR
if(filter_var($tmp[0], FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)){
return $tmp[0];
}
}
}
return $_SERVER['REMOTE_ADDR'];
}
?>
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v7/lib/lib_simplelogger.php
@@ -0,0 +1,73 @@
<?php
/**
* A simple ILogger implementation.
* Log everything it got to the log file. (Default log level: ERROR only)
*
* @package PMCLibrary
* @version $Id$
*/
 
class SimpleLogger implements ILogger {
private $logName;
private $logFile;
 
public function __construct($logName, $logFile) {
$this->logName = $logName;
$this->logFile = $logFile;
}
 
public function isDebugEnabled() {
return (defined('DEBUG') && DEBUG);
}
 
public function isInfoEnabled() {
return (defined('DEBUG') && DEBUG);
}
 
public function isErrorEnabled() {
return true;
}
 
public function debug($format, $varargs = '') {
if (!$this->isDebugEnabled()) return;
 
if (is_array($varargs)) {
// Array into structure string
$varargs = array(var_export($varargs, true));
} else {
$varargs = func_get_args();
array_shift($varargs);
}
$this->logFormat('DEBUG', $format, $varargs);
}
 
public function info($format, $varargs = '') {
if (!$this->isInfoEnabled()) return;
 
$varargs = func_get_args();
array_shift($varargs);
$this->logFormat(' INFO', $format, $varargs);
}
 
public function error($format, $varargs = '') {
if (!$this->isErrorEnabled()) return;
 
$varargs = func_get_args();
array_shift($varargs);
$this->logFormat('ERROR', $format, $varargs);
}
 
/**
* Log with format message.
*
* @param string $logLevel Log level
* @param string $message Format message
* @param array $vars Prarameters
*/
private function logFormat($logLevel, $message, array $vars) {
$dateTime = date('c');
$message = vsprintf($message, $vars);
error_log("$dateTime $logLevel {$this->logName} - $message".PHP_EOL,
3, $this->logFile);
}
}
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v7/lib/fileio/fileio.satellite.php
@@ -0,0 +1,188 @@
<?php
/**
* FileIO Satellite 衛星計畫後端
*
* 搭配 satellite.php/pl 利用遠端空間管理圖檔
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
* @deprecated
*/
 
class FileIO{
var $userAgent, $parameter, $thumbLocalPath;
var $IFS;
 
/* private 測試連線並且初始化遠端衛星主機 */
function _initSatellite(){
if(!($fp = @fsockopen($this->parameter[0]['host'], 80))) return false;
 
$argument = 'mode=init&key='.$this->parameter[2];
$out = 'POST '.$this->parameter[0]['path']." HTTP/1.1\r\n";
$out .= 'Host: '.$this->parameter[0]['host']."\r\n";
$out .= 'User-Agent: '.$this->userAgent."\r\n";
$out .= "Content-Type: application/x-www-form-urlencoded\r\n";
$out .= 'Content-Length: '.strlen($argument)."\r\n\r\n";
$out .= $argument;
fwrite($fp, $out);
$result = fgets($fp, 128); // 取一次足以取到檔頭
fclose($fp);
 
return (strpos($result, '202 Accepted') !== false); // 檢查狀態值偵測是否傳輸成功
}
 
/* private 傳送抓取要求到遠端衛星主機上面 */
function _transloadSatellite($imgname){
if(!($fp = @fsockopen($this->parameter[0]['host'], 80))) return false;
 
$argument = 'mode=transload&key='.$this->parameter[2].'&imgurl='.$this->getImageLocalURL($imgname).'&imgname='.$imgname;
$out = 'POST '.$this->parameter[0]['path']." HTTP/1.1\r\n";
$out .= 'Host: '.$this->parameter[0]['host']."\r\n";
$out .= 'User-Agent: '.$this->userAgent."\r\n";
$out .= "Content-Type: application/x-www-form-urlencoded\r\n";
$out .= 'Content-Length: '.strlen($argument)."\r\n\r\n";
$out .= $argument;
fwrite($fp, $out);
$result = fgets($fp, 128); // 取一次足以取到檔頭
fclose($fp);
 
return (strpos($result, '202 Accepted') !== false); // 檢查狀態值偵測是否傳輸成功
}
 
/* private 直接傳送檔案到遠端衛星主機上面 */
function _uploadSatellite($imgname, $imgpath){
srand((double) microtime()*1000000);
$boundary = '---------------------'.substr(md5(rand(0,32000)), 0, 10); // 生成分隔線
 
$argument = ''; // 資料暫存
// 一般欄位資料轉換
$formField = array('mode' => 'upload', 'key' => $this->parameter[2], 'imgname' => $imgname);
foreach($formField as $ikey => $ival){
$argument .= "--$boundary\r\n";
$argument .= "Content-Disposition: form-data; name=\"".$ikey."\"\r\n\r\n";
$argument .= $ival."\r\n";
$argument .= "--$boundary\r\n";
}
// 上傳檔案欄位資料轉換
$imginfo = getimagesize($imgpath); // 取得圖檔資訊
$argument .= "--$boundary\r\n";
$argument .= 'Content-Disposition: form-data; name="imgfile"; filename="'.$imgname.'"'."\r\n";
$argument .= 'Content-Type: '.$imginfo['mime']."\r\n\r\n";
$argument .= join('', file($imgpath))."\r\n";
$argument .= "--$boundary--\r\n";
 
$out = 'POST '.$this->parameter[0]['path']." HTTP/1.1\r\n";
$out .= 'Host: '.$this->parameter[0]['host']."\r\n";
$out .= 'User-Agent: '.$this->userAgent."\r\n";
$out .= "Content-Type: multipart/form-data, boundary=$boundary\r\n";
$out .= 'Content-Length: '.strlen($argument)."\r\n\r\n";
$out .= $argument;
 
if(!($fp = @fsockopen($this->parameter[0]['host'], 80))) return false;
fwrite($fp, $out);
$result = fgets($fp, 128);
fclose($fp);
 
return (strpos($result, '202 Accepted') !== false);
}
 
/* private 發出刪除圖片要求 */
function _deleteSatellite($imgname){
if(!($fp = @fsockopen($this->parameter[0]['host'], 80))) return false;
 
$argument = 'mode=delete&key='.$this->parameter[2].'&imgname='.$imgname;
$out = 'POST '.$this->parameter[0]['path']." HTTP/1.1\r\n";
$out .= 'Host: '.$this->parameter[0]['host']."\r\n";
$out .= 'User-Agent: '.$this->userAgent."\r\n";
$out .= "Content-Type: application/x-www-form-urlencoded\r\n";
$out .= 'Content-Length: '.strlen($argument)."\r\n\r\n";
$out .= $argument;
fwrite($fp, $out);
$result = fgets($fp, 128);
fclose($fp);
 
return (strpos($result, '202 Accepted') !== false);
}
 
/* private 儲存索引檔 */
function _setIndex(){
$this->IFS->saveIndex(); // 索引表更新
}
 
function FileIO($parameter, $ENV){
require($ENV['IFS.PATH']);
$this->IFS = new IndexFS($ENV['IFS.LOG']); // IndexFS 物件
$this->IFS->openIndex();
register_shutdown_function(array($this, '_setIndex')); // 設定解構元 (PHP 結束前執行)
set_time_limit(120); // 執行時間 120 秒 (傳輸過程可能很長)
$this->userAgent = 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1)'; // Just for fun ;-)
$this->thumbLocalPath = $ENV['PATH'].$ENV['THUMB']; // 預覽圖本機位置
$this->parameter = $parameter; // 將參數重新解析
$this->parameter[0] = parse_url($this->parameter[0]); // URL 位置拆解
/*
[0] : 衛星程式遠端 URL 位置
[1] : 是否使用 Transload 方式要求衛星程式抓取圖檔 (true:是 false:否,使用傳統 HTTP 上傳)
[2] : 傳輸認證金鑰
[3] : 遠端目錄對應 URL
[4] : 預覽圖是否上傳至遠端 (true: 是, false: 否,使用本機檔案)
*/
}
 
function init(){
return $this->_initSatellite();
}
 
function imageExists($imgname){
return $this->IFS->beRecord($imgname);
}
 
function deleteImage($imgname){
if(!is_array($imgname))
$imgname = array($imgname); // 單一名稱參數
 
$size = 0; $size_perimg = 0;
foreach($imgname as $i){
$size_perimg = $this->getImageFilesize($i);
if(!$this->parameter[4] && strpos($i, 's.') !== false){
@unlink($this->thumbLocalPath.$i);
}else{
// 刪除出現錯誤
if(!$this->_deleteSatellite($i)){
if($this->remoteImageExists($this->parameter[3].$i)) continue; // 無法刪除,檔案存在 (保留索引)
// 無法刪除,檔案消失 (更新索引)
}
}
$this->IFS->delRecord($i);
$size += $size_perimg;
}
return $size;
}
 
function uploadImage($imgname='', $imgpath='', $imgsize=0){
if($imgname=='') return true; // 支援上傳方法
if(!$this->parameter[4] && strpos($imgname, 's.') !== false){
$this->IFS->addRecord($imgname, $imgsize, ''); // 加入索引之中
return true; // 不處理預覽圖
}
$result = $this->parameter[1]
? $this->_transloadSatellite($imgname)
: $this->_uploadSatellite($imgname, $imgpath); // 選擇傳輸方法
if($result){
$this->IFS->addRecord($imgname, $imgsize, ''); // 加入索引之中
unlink($imgpath); // 確實上傳後刪除本機暫存
}
return $result;
}
 
function getImageFilesize($imgname){
if($rc = $this->IFS->getRecord($imgname)) return $rc['imgSize'];
return false;
}
 
function getImageURL($imgname){
if(!$this->parameter[4] && strpos($imgname, 's.') !== false) return $this->getImageLocalURL($imgname);
return $this->IFS->beRecord($imgname) ? $this->parameter[3].$imgname : false;
}
}
?>
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v7/lib/fileio/fileio.normal.php
@@ -0,0 +1,77 @@
<?php
/**
* FileIO Normal 本機儲存 API
*
* 以本機硬碟空間作為圖檔儲存的方式,並提供一套方法供程式管理圖片
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
*/
 
class FileIO{
var $path, $imgPath, $thumbPath;
var $IFS;
 
/* private 藉由檔名分辨圖檔存放位置 */
function _getImagePhysicalPath($imgname){
return (strpos($imgname, 's.') !== false ? $this->thumbPath : $this->imgPath).$imgname;
}
 
/* private 儲存索引檔 */
function _close(){
$this->IFS->saveIndex(); // 索引表更新
}
 
function FileIO($parameter='', $ENV){
require($ENV['IFS.PATH']);
$this->path = $ENV['PATH'];
$this->imgPath = $this->path.$ENV['IMG'];
$this->thumbPath = $this->path.$ENV['THUMB'];
$this->IFS = new IndexFS($ENV['IFS.LOG']); // IndexFS 物件
$this->IFS->openIndex();
register_shutdown_function(array($this, '_close')); // 設定解構元 (PHP 結束前執行)
}
 
function init(){
return true;
}
 
function imageExists($imgname){
return $this->IFS->beRecord($imgname);
}
 
function deleteImage($imgname){
if(!is_array($imgname))
$imgname = array($imgname); // 單一名稱參數
 
$size = 0; $size_perimg = 0;
foreach($imgname as $i){
$size_perimg = $this->getImageFilesize($i);
// 刪除出現錯誤
if(!@unlink($this->_getImagePhysicalPath($i))){
if($this->imageExists($i)) continue; // 無法刪除,檔案存在 (保留索引)
// 無法刪除,檔案消失 (更新索引)
}
$this->IFS->delRecord($i);
$size += $size_perimg;
}
return $size;
}
 
function uploadImage($imgname='', $imgpath='', $imgsize=0){
if($imgname=='') return true; // 為檔案作索引
$this->IFS->addRecord($imgname, $imgsize, ''); // 加入索引之中
return true;
}
 
function getImageFilesize($imgname){
if($rc = $this->IFS->getRecord($imgname)) return $rc['imgSize'];
return false;
}
 
function getImageURL($imgname){
return $this->getImageLocalURL($imgname);
}
}
?>
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v7/lib/fileio/fileio.ftp.php
@@ -0,0 +1,109 @@
<?php
/**
* FileIO FTP 遠端儲存 API
*
* 以遠端硬碟空間作為圖檔儲存的方式 (以 FTP 存取),並提供一套方法供程式管理圖片
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
*/
 
class FileIO{
var $conn, $parameter, $thumbLocalPath;
var $IFS;
 
/* private 登入 FTP */
function _ftp_login(){
if($this->conn) return true;
$this->conn = ftp_connect($this->parameter[0], $this->parameter[1]);
if($result = @ftp_login($this->conn, $this->parameter[2], $this->parameter[3])){
if($this->parameter[4]=='PASV') ftp_pasv($this->conn, true); // 被動模式
ftp_set_option($this->conn, FTP_TIMEOUT_SEC, 120); // 延長 Timeout 至 120 秒
@ftp_chdir($this->conn, $this->parameter[5]);
}
return $result;
}
 
/* private 關閉 FTP 及儲存索引檔 */
function _ftp_close(){
if($this->conn) ftp_close($this->conn); // 有開啟 FTP 連線則關閉
$this->IFS->saveIndex(); // 索引表更新
}
 
function FileIO($parameter, $ENV){
require($ENV['IFS.PATH']);
$this->IFS = new IndexFS($ENV['IFS.LOG']); // IndexFS 物件
$this->IFS->openIndex();
register_shutdown_function(array($this, '_ftp_close')); // 設定解構元 (PHP 結束前執行)
set_time_limit(120); // 執行時間 120 秒 (FTP 傳輸過程可能很長)
$this->thumbLocalPath = $ENV['PATH'].$ENV['THUMB']; // 預覽圖本機位置
$this->parameter = $parameter;
/*
[0] : FTP 伺服器位置
[1] : FTP 伺服器埠號
[2] : FTP 使用者帳號
[3] : FTP 使用者密碼
[4] : 是否使用被動模式? (PASV: 使用, NOPASV: 不使用)
[5] : FTP 預設工作目錄
[6] : 工作目錄對應 URL
[7] : 預覽圖是否上傳至遠端 (true: 是, false: 否,使用本機檔案)
*/
}
 
function init(){
return true;
}
 
function imageExists($imgname){
return $this->IFS->beRecord($imgname);
}
 
function deleteImage($imgname){
if(!$this->_ftp_login()) return 0;
if(!is_array($imgname))
$imgname = array($imgname); // 單一名稱參數
 
$size = 0; $size_perimg = 0;
foreach($imgname as $i){
$size_perimg = $this->getImageFilesize($i);
if(!$this->parameter[7] && strpos($i, 's.') !== false){
@unlink($this->thumbLocalPath.$i);
}else{
if(!ftp_delete($this->conn, $i)){
if($this->remoteImageExists($this->parameter[6].$i)) continue; // 無法刪除,檔案存在 (保留索引)
// 無法刪除,檔案消失 (更新索引)
}
}
$this->IFS->delRecord($i); // 自索引中刪除
$size += $size_perimg;
}
return $size;
}
 
function uploadImage($imgname='', $imgpath='', $imgsize=0){
if($imgname=='') return true; // 支援上傳方法
if(!$this->parameter[7] && strpos($imgname, 's.') !== false){
$this->IFS->addRecord($imgname, $imgsize, ''); // 加入索引之中
return true; // 不處理預覽圖
}
if(!$this->_ftp_login()) return false;
$result = ftp_put($this->conn, $imgname, $imgpath, FTP_BINARY);
if($result){
$this->IFS->addRecord($imgname, $imgsize, ''); // 加入索引之中
unlink($imgpath); // 確實上傳後刪除本機暫存
}
return $result;
}
 
function getImageFilesize($imgname){
if($rc = $this->IFS->getRecord($imgname)) return $rc['imgSize'];
return false;
}
 
function getImageURL($imgname){
if(!$this->parameter[7] && strpos($imgname, 's.') !== false) return $this->getImageLocalURL($imgname);
return $this->IFS->beRecord($imgname) ? $this->parameter[6].$imgname : false;
}
}
?>
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v7/lib/fileio/ifs.php
@@ -0,0 +1,194 @@
<?php
/**
* FileIO Index File System
*
* 把遠端圖檔的各種屬性作本機快取及記錄,方便程式取用
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
*/
 
class IndexFS{
var $logfile, $backend, $index, $modified, $keylist;
 
/* 建構元 */
function IndexFS($logfile){
// 索引記錄檔位置
$this->logfile = $logfile;
}
 
/* 初始化 */
function init(){
switch($this->backend){
case 'pdo_sqlite':
$execText = 'CREATE TABLE IndexFS (
"imgName" VARCHAR(20) NOT NULL PRIMARY KEY,
"imgSize" INTEGER NOT NULL,
"imgURL" VARCHAR(255) NOT NULL
); CREATE INDEX IDX_IndexFS_imgName ON IndexFS(imgName);';
$this->index->exec($execText);
break;
case 'log':
touch($this->logfile); chmod($this->logfile, 0666); // 建立索引檔
break;
case 'sqlite2':
$execText = 'CREATE TABLE IndexFS (
"imgName" VARCHAR(20) NOT NULL PRIMARY KEY,
"imgSize" INTEGER NOT NULL,
"imgURL" VARCHAR(255) NOT NULL
); CREATE INDEX IDX_IndexFS_imgName ON IndexFS(imgName);';
sqlite_exec($this->index, $execText);
break;
}
}
 
/* 開啟索引檔並讀入 */
function openIndex(){
if(extension_loaded('pdo_sqlite')){
$this->backend = 'pdo_sqlite';
$this->index = new PDO('sqlite:'.$this->logfile);
if($this->index->query("SELECT COUNT(name) FROM sqlite_master WHERE name LIKE 'IndexFS'")->fetchColumn() === '0') $this->init();
}else if(extension_loaded('SQLite')){
$this->backend = 'sqlite2';
 
$this->index = sqlite_open($this->logfile, 0666);
if(sqlite_num_rows(sqlite_query($this->index, "SELECT name FROM sqlite_master WHERE name LIKE 'IndexFS'"))===0) $this->init();
}else{
$this->backend = 'log';
$this->modified = false;
if(!file_exists($this->logfile)){ $this->init(); return; }
if(filesize($this->logfile)==0) return;
$indexlog = file($this->logfile); $indexlog_count = count($indexlog); // 讀入索引檔並計算目前筆數
$this->index = array();
for($i = 0; $i < $indexlog_count; $i++){
if(!($trimline = rtrim($indexlog[$i]))) continue; // 本行無意義
$field = explode("\t\t", $trimline);
$this->index[$field[0]] = array('imgSize' => $field[1], 'imgURL' => isset($field[2]) ? $field[2] : '');
// 索引格式: 檔名 檔案大小 對應路徑
}
$this->keylist = array_keys($this->index);
unset($indexlog);
}
PMCLibrary::getLoggerInstance(__CLASS__)->
info('Backend: %s, Path: %s', $this->backend, $this->logfile);
}
 
/* 索引是否存在 */
function beRecord($id){
switch($this->backend){
case 'pdo_sqlite':
$sth = $this->index->prepare('SELECT COUNT(imgName) FROM IndexFS WHERE imgName = ?');
$sth->execute(array($id));
return $sth->fetchColumn() != false;
case 'log':
return isset($this->index[$id]);
case 'sqlite2':
return (sqlite_fetch_array(sqlite_query($this->index, 'SELECT imgName FROM IndexFS WHERE imgName = "'.sqlite_escape_string($id).'"'), SQLITE_ASSOC) ? true : false);
}
}
 
/* 搜尋預覽圖檔檔名 */
function findThumbName($pattern){
switch($this->backend){
case 'pdo_sqlite':
$sth = $this->index->prepare('SELECT imgName FROM IndexFS WHERE imgName >= ? AND imgName < ?');
$sth->execute(array($pattern.'s', $pattern.'t'));
return $sth->fetchColumn();
case 'log':
if(count($this->keylist) != count($this->index)){ // Index Sync
$this->keylist = array_keys($this->index);
}
// O(n) not optimized
foreach($this->keylist as $k){
if(strpos($k, $pattern.'s.') !== false) return $k;
}
return false;
case 'sqlite2':
$ptrn = sqlite_escape_string($pattern);
// LIKE Optimization by >= AND < using index
// Original: LIKE "1234567890123s.%"
$result = sqlite_fetch_array(sqlite_query($this->index, 'SELECT imgName FROM IndexFS WHERE imgName >= "'.$ptrn.'s" AND imgName < "'.$ptrn.'t"'), SQLITE_ASSOC);
return (isset($result) ? $result['imgName'] : false);
}
}
 
/* 取得一筆索引 */
function getRecord($id){
switch($this->backend){
case 'pdo_sqlite':
$sth = $this->index->prepare('SELECT * FROM IndexFS WHERE imgName = ?');
$sth->execute(array($id));
return $sth->fetch();
case 'log':
return isset($this->index[$id]) ? $this->index[$id] : false;
case 'sqlite2':
return sqlite_fetch_array(sqlite_query($this->index, 'SELECT * FROM IndexFS WHERE imgName = "'.sqlite_escape_string($id).'"'), SQLITE_ASSOC);
}
}
 
/* 新增一筆索引 */
function addRecord($id, $imgSize, $imgURL){
switch($this->backend){
case 'pdo_sqlite':
$sth = $this->index->prepare('INSERT INTO IndexFS (imgName, imgSize, imgURL) VALUES (?, ?, ?)');
$sth->execute(array($id, $imgSize, $imgURL));
break;
case 'log':
$this->modified = true;
$this->index[$id] = array('imgSize' => $imgSize, 'imgURL' => $imgURL); // 加入索引之中
break;
case 'sqlite2':
sqlite_exec($this->index, 'INSERT INTO IndexFS (imgName, imgSize, imgURL) VALUES ("'.sqlite_escape_string($id).'", '.sqlite_escape_string($imgSize).', "'.sqlite_escape_string($imgURL).'");');
break;
}
}
 
/* 刪除一筆索引 */
function delRecord($id){
switch($this->backend){
case 'pdo_sqlite':
$sth = $this->index->prepare('DELETE FROM IndexFS WHERE imgName = ?');
return $sth->execute(array($id));
case 'log':
if(isset($this->index[$id])){ unset($this->index[$id]); $this->modified = true; return true; }
return false;
case 'sqlite2':
return sqlite_exec($this->index, 'DELETE FROM IndexFS WHERE imgName = "'.sqlite_escape_string($id).'";');
}
}
 
/* 儲存索引變更 */
function saveIndex(){
if($this->backend=='log' && $this->modified){ // 如果有修改索引就回存
$indexlog = '';
if(count($this->index)) foreach($this->index as $ikey => $ival){ $indexlog .= $ikey."\t\t".$ival['imgSize']."\t\t".$ival['imgURL']."\n"; } // 有資料才跑迴圈
$fp = fopen($this->logfile, 'w');
fwrite($fp, $indexlog);
fclose($fp);
}elseif($this->backend=='sqlite2'){
sqlite_close($this->index);
}
}
 
/* 取得目前索引之所有檔案大小 */
function getCurrentStorageSize(){
switch($this->backend){
case 'pdo_sqlite':
$size = $this->index->query('SELECT SUM(imgSize) FROM IndexFS');
return intval($size->fetchColumn());
case 'log':
$size = 0;
if(count($this->index)){
foreach($this->index as $ival){
$size += $ival['imgSize'];
}
}
return intval($size);
case 'sqlite2':
$size = sqlite_fetch_array(sqlite_query($this->index, 'SELECT SUM(imgSize) FROM IndexFS'));
return intval($size[0]);
}
}
}
?>
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v7/lib/lib_noplogger.php
@@ -0,0 +1,31 @@
<?php
/**
* NopLogger means it doesn't log anything.
* Only for production use. Do not use on testing enviroments.
* Because there is no traceable information left to provide for debugging.
*
* @package PMCLibrary
* @version $Id$
*/
 
class NopLogger implements ILogger {
public function __construct($logName, $logFile) {}
 
public function isDebugEnabled() {
return false;
}
 
public function isInfoEnabled() {
return false;
}
 
public function isErrorEnabled() {
return false;
}
 
public function debug($format, $varargs = '') {}
 
public function info($format, $varargs = '') {}
 
public function error($format, $varargs = '') {}
}
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v7/lib/interfaces.php
@@ -0,0 +1,371 @@
<?php
/**
* Pixmicat! interface declarations
*
* @package PMCLibrary
* @version $Id$
*/
 
/**
* IPIO
*/
interface IPIO {
/**
* 取得 PIO 模組版本。
*
* @return string PIO 版本資訊字串
*/
public function pioVersion();
 
/**
* 處理連線字串/連接。
*
* @param string $connStr 連線字串
*/
public function dbConnect($connStr);
 
/**
* 資料來源初始化。
*
* @param boolean $isAddInitData 是否建立一筆預設資料
*/
public function dbInit($isAddInitData = true);
 
/**
* 連接資料來源並準備使用。
*
* @param boolean $reload 是否強制重新連接
* @param boolean $transaction 是否使用交易模式(如果支援的話)
*/
public function dbPrepare($reload = false, $transaction = false);
 
/**
* 提交/儲存。
*/
public function dbCommit();
 
/**
* 維護資料來源的操作。
*
* @param string $action 執行操作
* @param boolean $doit 是否執行
* @return boolean 是否支援此操作 ($doit為false時做為查詢之用)
*/
public function dbMaintanence($action, $doit = false);
 
/**
* 自中介格式匯入資料來源。
*
* @param string $data 中介檔的檔案全文
* @return boolean 操作是否成功
*/
public function dbImport($data);
 
/**
* 匯出資料來源至中介格式。
*
* @return string 中介檔的檔案全文
*/
public function dbExport();
 
/**
* 取得文章數目。
*
* @param integer $resno 討論串文章編號。有指定的話則回傳指定討論串之文章數
* @return integer 文章數目
*/
public function postCount($resno = 0);
 
/**
* 取得討論串數目。
*
* @return integer 討論串數目
*/
public function threadCount();
 
/**
* 取得最後文章編號。
*
* @param string $state 取得狀態 'beforeCommit', 'afterCommit'
* @return integer 最後文章編號
*/
public function getLastPostNo($state);
 
/**
* 輸出文章清單
*
* @param integer $resno 指定編號討論串
* @param integer $start 起始位置
* @param integer $amount 數目
* @return array 文章編號陣列
*/
public function fetchPostList($resno = 0, $start = 0, $amount = 0);
 
/**
* 輸出討論串清單
*
* @param integer $start 起始位置
* @param integer $amount 數目
* @param boolean $isDESC 是否依編號遞減排序
* @return array 文章編號陣列
*/
public function fetchThreadList($start = 0, $amount = 0, $isDESC = false);
 
/**
* 輸出文章
*
* @param mixed $postlist 指定文章編號或文章編號陣列
* @param string $fields 選擇輸出的欄位
* @return array 文章內容陣列
*/
public function fetchPosts($postlist, $fields = '*');
 
/**
* 刪除舊附件 (輸出附件清單)
*
* @param int $total_size 目前使用容量
* @param int $storage_max 總容量限制
* @param boolean $warnOnly 是否僅提醒不刪除
* @return array 附加圖檔及預覽圖陣列
*/
public function delOldAttachments($total_size, $storage_max, $warnOnly = true);
 
/**
* 刪除文章
*
* @param array $posts 刪除之文章編號陣列
* @return array 附加圖檔及預覽圖陣列
*/
public function removePosts($posts);
 
/**
* 刪除附件 (輸出附件清單)
*
* @param array $posts 刪除之文章編號陣列
* @param boolean $recursion 是否遞迴尋找相關文章與回應
* @return array 附加圖檔及預覽圖陣列
*/
public function removeAttachments($posts, $recursion = false);
 
/**
* 新增文章/討論串
*
* @param int $no 文章編號
* @param int $resto 回應編號
* @param string $md5chksum 附加圖MD5
* @param string $category 類別
* @param string $tim 時間戳
* @param string $ext 附加圖副檔名
* @param int $imgw 附加圖寬
* @param int $imgh 附加圖高
* @param string $imgsize 附加圖大小
* @param int $tw 預覽圖寬
* @param int $th 預覽圖高
* @param string $pwd 密碼
* @param string $now 發文時間字串
* @param string $name 名稱
* @param string $email 電子郵件
* @param string $sub 標題
* @param string $com 內文
* @param string $host 主機名稱
* @param boolean $age 是否推文
* @param string $status 狀態旗標
*/
public function addPost($no, $resto, $md5chksum, $category, $tim, $ext,
$imgw, $imgh, $imgsize, $tw, $th, $pwd, $now, $name, $email, $sub,
$com, $host, $age = false, $status = '');
 
/**
* 檢查是否連續投稿
*
* @param int $lcount 檢查數目
* @param string $com 內文
* @param int $timestamp 發文時間戳
* @param string $pass 密碼
* @param string $passcookie Cookie 密碼
* @param string $host 主機名稱
* @param boolean $isupload 是否上傳附加圖檔
* @return boolean 是否為連續投稿
*/
public function isSuccessivePost($lcount, $com, $timestamp, $pass,
$passcookie, $host, $isupload);
 
/**
* 檢查是否重複貼圖
*
* @param int $lcount 檢查數目
* @param string $md5hash MD5
* @return boolean 是否為連續貼圖
*/
public function isDuplicateAttachment($lcount, $md5hash);
 
/**
* 有此討論串?
*
* @param int $no 文章編號
* @return boolean 討論串是否存在
*/
public function isThread($no);
 
/**
* 搜尋文章
*
* @param array $keyword 關鍵字陣列
* @param string $field 欄位
* @param string $method 搜尋方法
* @return array 文章內容陣列
*/
public function searchPost($keyword, $field, $method);
 
/**
* 搜尋類別標籤
*
* @param string $category 類別
* @return array 此類別之文章編號陣列
*/
public function searchCategory($category);
 
/**
* 取得文章狀態
*
* @param string $status 旗標狀態
* @return FlagHelper 旗標狀態修改物件
*/
public function getPostStatus($status);
 
/**
* 更新文章
*
* @param int $no 文章編號
* @param array $newValues 新欄位值陣列
*/
public function updatePost($no, $newValues);
 
/**
* 設定文章屬性
*
* @param int $no 文章編號
*/
public function setPostStatus($no, $newStatus);
}
 
/**
* IPIOCondition
*/
interface IPIOCondition {
/**
* 檢查是否需要進行檢查步驟。
*
* @param string $type 目前模式 ("predict" 預知提醒、"delete" 真正刪除)
* @param mixed $limit 判斷機制上限參數
* @return boolean 是否需要進行進一步檢查
*/
public static function check($type, $limit);
 
/**
* 列出需要刪除的文章編號列表。
*
* @param string $type 目前模式 ("predict" 預知提醒、"delete" 真正刪除)
* @param mixed $limit 判斷機制上限參數
* @return array 文章編號列表陣列
*/
public static function listee($type, $limit);
 
/**
* 輸出 Condition 物件資訊。
*
* @param mixed $limit 判斷機制上限參數
* @return string 物件資訊文字
*/
public static function info($limit);
}
 
/**
* ILogger
*/
interface ILogger {
/**
* 建構元。
*
* @param string $logName Logger 名稱
* @param string $logFile 記錄檔案位置
*/
public function __construct($logName, $logFile);
/**
* 檢查是否 logger 要記錄 DEBUG 等級。
*
* @return boolean 要記錄 DEBUG 等級與否
*/
public function isDebugEnabled();
 
/**
* 檢查是否 logger 要記錄 INFO 等級。
*
* @return boolean 要記錄 INFO 等級與否
*/
public function isInfoEnabled();
 
/**
* 檢查是否 logger 要記錄 ERROR 等級。
*
* @return boolean 要記錄 ERROR 等級與否
*/
public function isErrorEnabled();
 
/**
* 以 DEBUG 等級記錄訊息。
*
* @param string $format 格式化訊息內容
* @param mixed $varargs 參數
*/
public function debug($format, $varargs = '');
 
/**
* 以 INFO 等級記錄訊息。
*
* @param string $format 格式化訊息內容
* @param mixed $varargs 參數
*/
public function info($format, $varargs = '');
 
/**
* 以 ERROR 等級記錄訊息。
*
* @param string $format 格式化訊息內容
* @param mixed $varargs 參數
*/
public function error($format, $varargs = '');
}
 
/**
* MethodInterceptor (AOP Around Advice)
*/
interface MethodInterceptor {
/**
* 代理呼叫方法。
*
* @param array $callable 要被呼叫的方法
* @param array $args 方法傳遞的參數
* @return mixed 方法執行的結果
*/
public function invoke(array $callable, array $args);
}
 
/**
* IModule
*/
interface IModule {
/**
* 回傳模組名稱方法
*
* @return string 模組名稱。建議回傳格式: mod_xxx : 簡短註解
*/
public function getModuleName();
 
/**
* 回傳模組版本號方法
*
* @return string 模組版本號
*/
public function getModuleVersionInfo();
}
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v7/lib/lib_loggerinterceptor.php
@@ -0,0 +1,78 @@
<?php
/**
* AOP Logger
*
* @package PMCLibrary
* @version $Id$
* @since 7th.Release
*/
 
/**
* Logger Around Advice Interceptor
*/
class LoggerInterceptor implements MethodInterceptor {
private $LOG;
 
public function __construct(ILogger $logger) {
$this->setLogger($logger);
}
 
private function setLogger(ILogger $logger) {
$this->LOG = $logger;
}
 
public function invoke(array $callable, array $args) {
$result = null;
$methodName = $callable[1];
$this->LOG->info('Executing %s method', $methodName);
$this->LOG->debug('Args: %s', $args);
 
try {
$result = call_user_func_array($callable, $args);
} catch (Exception $e) {
$this->LOG->error('[%s] %s', $methodName, $e);
}
 
$this->LOG->debug('Return: %s', $result);
return $result;
}
}
 
/**
* 事件記錄器注入
* 使用 MethodInterceptor 代理包裹物件方法,藉此注入 Logger。
*/
class LoggerInjector {
private $principalClass;
private $mi;
 
public function __construct($principalClass, MethodInterceptor $mi) {
$this->setPrincipalClass($principalClass);
$this->setMethodInterceptor($mi);
}
 
private function setPrincipalClass($principalClass) {
if (!is_object($principalClass)) {
throw new InvalidArgumentException('PrincipalClass is not a valid object.');
}
$this->principalClass = $principalClass;
}
 
private function setMethodInterceptor(MethodInterceptor $mi) {
$this->mi = $mi;
}
 
/**
* 以 MethodInterceptor 注入記錄器
*
* @param string $name 呼叫方法名稱
* @param array $args 呼叫方法參數
* @return mixed 呼叫方法回傳值
*/
public function __call($name, $args) {
if (!method_exists($this->principalClass, $name)) {
return;
}
return $this->mi->invoke(array($this->principalClass, $name), $args);
}
}
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v7/lib/lib_pms.php
@@ -0,0 +1,251 @@
<?php
/**
* Pixmicat! Module System
*
* 增加掛載點供函式掛上並在需要時依序呼叫以動態改變內容或達成各種效果
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
*/
 
class PMS{
var $ENV;
var $moduleInstance, $moduleLists;
var $hookPoints;
var $loaded;
var $CHPList;
 
/* Constructor */
function PMS($ENV){
$this->loaded = false; // 是否載入完成 (模組及函式)
$this->ENV = $ENV; // 環境變數
$this->hooks = array_flip(array('Head', 'Toplink', 'LinksAboveBar', 'PostInfo', 'PostForm',
'ThreadFront', 'ThreadRear', 'ThreadPost', 'ThreadReply',
'Foot', 'ModulePage', 'RegistBegin', 'RegistBeforeCommit', 'RegistAfterCommit', 'PostOnDeletion',
'AdminList', 'AdminFunction', 'Authenticate', 'ThreadOrder'
));
$this->hookPoints = array(); // 掛載點
$this->moduleInstance = array(); // 存放各模組實體
$this->moduleLists = array(); // 存放各模組類別名稱
$this->CHPList = array(); // CHP List
}
 
// 模組載入相關
/* 載入模組 */
function init(){
$this->loaded = true;
$this->loadModules();
return true;
}
 
/* 單載入模式 */
function onlyLoad($specificModule){
// 搜尋載入模組列表有沒有,沒有就直接取消程式
if(array_search($specificModule, $this->ENV['MODULE.LOADLIST'])===false) return false;
$this->loadModules($specificModule);
return isset($this->hookPoints['ModulePage']);
}
 
/* 載入擴充模組 */
function loadModules($specificModule=false){
$loadlist = $specificModule ? array($specificModule) : $this->ENV['MODULE.LOADLIST'];
foreach($loadlist as $f){
$mpath = $this->ENV['MODULE.PATH'].$f.'.php';
if(is_file($mpath) && array_search($f, $this->moduleLists)===false){
include($mpath);
$this->moduleLists[] = $f;
$this->moduleInstance[$f] = new $f($this); // Sent $PMS into constructor
}
}
}
 
/* 取得載入模組列表 */
function getLoadedModules(){
if(!$this->loaded) $this->init();
return $this->moduleLists;
}
 
/* 取得模組實體 */
function getModuleInstance($module){
return isset($this->moduleInstance[$module])?$this->moduleInstance[$module]:null;
}
 
/* 取得特定模組方法列表 */
function getModuleMethods($module){
if(!$this->loaded) $this->init();
return array_search($module, $this->moduleLists)!==false ? get_class_methods($module) : array();
}
 
// 提供給模組的取用資訊
/* 取得模組註冊獨立頁面之網址 */
function getModulePageURL($name){
return $this->ENV['MODULE.PAGE'].$name;
}
 
// 模組掛載與使用相關
/* 自動掛載相關模組方法於掛載點並回傳掛載點 (Return by Reference) */
function &__autoHookMethods($hookPoint){
if(isset($this->hooks[$hookPoint]) && !isset($this->hookPoints[$hookPoint])){ // 尚未掛載
$this->hookPoints[$hookPoint] = array();
foreach($this->moduleLists as $m){
if(method_exists($this->moduleInstance[$m], 'autoHook'.$hookPoint)){
$this->hookModuleMethod($hookPoint, array(&$this->moduleInstance[$m], 'autoHook'.$hookPoint));
}
}
}
return $this->hookPoints[$hookPoint];
}
 
/* 將模組方法掛載於特定掛載點 */
function hookModuleMethod($hookPoint, $methodObject){
if(!isset($this->hooks[$hookPoint])){ // Treat as CHP
if(!isset($this->CHPList[$hookPoint])) $this->CHPList[$hookPoint] = 1;
}else if(!isset($this->hookPoints[$hookPoint]) && $hookPoint != 'ModulePage'){ // Treat as normal hook point
if(!$this->loaded) $this->init();
$this->__autoHookMethods($hookPoint);
}
$this->hookPoints[$hookPoint][] = $methodObject;
}
 
/* 使用模組方法 */
function useModuleMethods($hookPoint, $parameter){
if(!$this->loaded) $this->init();
$arrMethod =& $this->__autoHookMethods($hookPoint); // 取得掛載點模組方法
$imax = count($arrMethod);
for($i = 0; $i < $imax; $i++) call_user_func_array($arrMethod[$i], $parameter);
}
 
// CHP (Custom Hook Point)
/* 新增 CHP */
function addCHP($CHPName, $methodObject){
$this->hookModuleMethod($CHPName, $methodObject);
}
 
/* 呼叫 CHP */
function callCHP($CHPName, $parameter){
if(!$this->loaded) $this->init(); // 若尚未完全載入則載入全部模組
if(isset($this->CHPList[$CHPName])) $this->useModuleMethods($CHPName, $parameter);
}
}
 
/**
* ModuleHelper
* 預先取得 PMS 常用功能方便呼叫
*/
abstract class ModuleHelper implements IModule {
protected static $PMS;
private $clazz;
 
public function __construct($PMS) {
// 儲存 $PMS 參考
if (self::$PMS == null) {
self::$PMS = $PMS;
}
$this->clazz = get_class($this);
 
// 自動註冊模組頁面
if (method_exists($this, 'ModulePage')) {
$PMS->hookModuleMethod('ModulePage', $this->clazz);
}
}
 
/**
* moduleName 建構器,協助組合出一致的模組名稱
*
* @param string $description 模組簡易用途說明
* @return string 格式化模組名稱
*/
protected function moduleNameBuilder($description) {
return "{$this->clazz} : $description";
}
 
/**
* 回傳模組獨立頁面 URL,並協助建立查詢參數
*
* @param array $params URL 參數鍵值表
* @return string 模組獨立頁面 URL
* @see http_build_query()
*/
protected function getModulePageURL(array $params = array()) {
$query = count($params) != 0 ?
'&amp;'.http_build_query($params, '', '&amp;') : '';
return self::$PMS->getModulePageURL($this->clazz).$query;
}
 
/**
* 將模組方法掛載於特定掛載點
*
* @param string $hookPoint 掛載點名稱
* @param callable $methodObject 可執行函式
*/
protected function hookModuleMethod($hookPoint, $methodObject) {
self::$PMS->hookModuleMethod($hookPoint, $methodObject);
}
 
/**
* 新增自訂掛載點
*
* @param string $chpName 自訂掛載點名稱
* @param callable $callable 可執行函式
*/
protected function addCHP($chpName, $callable) {
self::$PMS->addCHP($chpName, $callable);
}
 
/**
* 呼叫自訂掛載點
*
* @param string $chpName 自訂掛載點名稱
* @param array $params 函式參數
*/
protected function callCHP($chpName, array $params) {
self::$PMS->callCHP($chpName, $params);
}
 
/**
* 附加翻譯資源字串。
*
* @param array $lang 翻譯資源字串陣列
* @param string $fallbackLang 備用語系
* @throws InvalidArgumentException 如果找不到設定備用語系
*/
protected function attachLanguage(array $lang, $fallbackLang = 'en_US') {
// 取出使用語言,如果不存在則用備用
if (isset($lang[PIXMICAT_LANGUAGE])) {
$lang = $lang[PIXMICAT_LANGUAGE];
} else if (isset($lang[$fallbackLang])) {
$lang = $lang[$fallbackLang];
} else {
throw new InvalidArgumentException(
sprintf('Assigned locale: %s not found.', $fallbackLang)
);
}
 
$langKeys = array_keys($lang);
// 為字串資源鍵值加上模組名前綴
foreach ($langKeys as $k) {
$lang[$this->clazz.'_'.$k] = $lang[$k];
unset($lang[$k]);
}
 
PMCLibrary::getLanguageInstance()->attachLanguage($lang);
}
 
/**
* 取出翻譯資源檔對應字串。
*
* @param args 翻譯資源檔索引、其餘變數
* @see LanguageLoader->getTranslation
*/
protected function _T() {
$args = func_get_args();
// 為字串資源鍵值加上模組名前綴
if (isset($args[0]) && !empty($args[0])) {
$args[0] = $this->clazz.'_'.$args[0];
}
return call_user_func_array(
array(PMCLibrary::getLanguageInstance(), 'getTranslation'),
$args);
}
}
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v7/lib/thumb/thumb.imagemagick.php
@@ -0,0 +1,54 @@
<?php
/**
* Thumbnail Generate API: ImageMagick Wrapper
*
* 提供程式便於以 ImageMagick 命令列生成預覽圖的物件
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
*/
 
class ThumbWrapper{
var $sourceFile, $sourceWidth, $sourceHeight, $thumbWidth, $thumbHeight, $thumbSetting, $thumbQuality;
var $_exec;
 
function ThumbWrapper($sourceFile='', $sourceWidth=0, $sourceHeight=0){
$this->sourceFile = $sourceFile;
$this->sourceWidth = $sourceWidth;
$this->sourceHeight = $sourceHeight;
$this->_exec = 'convert'; // ImageMagick "convert" Binary Location
}
 
function getClass(){
$str = 'ImageMagick Wrapper';
if($this->isWorking()){
$a = null;
preg_match('/^Version: ImageMagick (.*?) [hf]/', `$this->_exec -version`, $a);
$str .= ' : '.$a[1];
unset($a);
}
return $str;
}
 
function isWorking(){
if(!function_exists('exec')) return false;
@exec("$this->_exec -version", $status, $retval);
return ($retval===0);
}
 
function setThumbnailConfig($thumbWidth, $thumbHeight, $thumbSetting){
$this->thumbWidth = $thumbWidth;
$this->thumbHeight = $thumbHeight;
$this->thumbSetting = $thumbSetting;
$this->thumbQuality = $thumbSetting['Quality'];
}
 
function makeThumbnailtoFile($destFile){
if(!$this->isWorking()) return false;
$CLI = "$this->_exec -thumbnail {$this->thumbWidth}x{$this->thumbHeight} -quality $this->thumbQuality \"$this->sourceFile\" \"$destFile\"";
@exec($CLI);
return true;
}
}
?>
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v7/lib/thumb/thumb.repng2jpeg.php
@@ -0,0 +1,90 @@
<?php
/**
* Thumbnail Generate API: Imagick Wrapper
*
* 提供程式便於以 repng2jpeg 生成預覽圖的物件
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
*/
 
class ThumbWrapper{
var $sourceFile, $sourceWidth, $sourceHeight, $thumbWidth, $thumbHeight, $thumbSetting, $thumbQuality;
var $_exec, $_sys_exec, $_support_bmp, $_shell_escape;
 
function ThumbWrapper($sourceFile='', $sourceWidth=0, $sourceHeight=0){
$this->sourceFile = $sourceFile;
$this->sourceWidth = $sourceWidth;
$this->sourceHeight = $sourceHeight;
$this->_exec = realpath('./repng2jpeg'.(strtoupper(substr(PHP_OS, 0, 3))==='WIN' ? '.exe' : ''));
if(strtoupper(substr(PHP_OS, 0, 3))==='WIN' && strpos($this->_exec,' ')!==false) {
$this->_shell_escape = 1;
$this->_exec = '"'.$this->_exec.'"';
}
elseif(strtoupper(substr(PHP_OS, 0, 3))!='WIN' && strpos($this->_exec,' ')!==false) {
$this->_shell_escape = 2;
$this->_exec = str_replace(' ','\ ',$this->_exec);
}
if(function_exists('exec')) {
@exec('repng2jpeg --version', $status, $retval);
if($retval===0) {
$this->_sys_exec = true;
$this->_exec = 'repng2jpeg';
}
$this->_support_bmp = (strpos(`$this->_exec --help`,'BMP')!==false);
}
}
 
function _shell_unescape($exec) {
if($this->_shell_escape == 1)
return substr($exec,1,-1);
elseif($this->_shell_escape == 2)
return str_replace('\ ',' ',$exec);
else
return $exec;
}
 
function getClass(){
$str = 'repng2jpeg Wrapper';
if($this->isWorking()){
$str .= ' : '.`$this->_exec --version`;
if($this->_support_bmp) $str .= '(BMP supported)';
if($this->_sys_exec) $str .= '[S]';
}
return $str;
}
 
function isWorking(){
return ($this->_sys_exec || file_exists($this->_shell_unescape($this->_exec))) && function_exists('exec') && ($this->_sys_exec || strtoupper(substr(PHP_OS, 0, 3))==='WIN' || is_executable($this->_shell_unescape($this->_exec)));
}
 
function setThumbnailConfig($thumbWidth, $thumbHeight, $thumbSetting){
$this->thumbWidth = $thumbWidth;
$this->thumbHeight = $thumbHeight;
$this->thumbSetting = $thumbSetting;
$this->thumbQuality = $thumbSetting['Quality'];
if($thumbSetting['Format']=='png') $this->thumbQuality = 'P';
elseif($thumbSetting['Format']=='gif') $this->thumbQuality = 'G';
}
 
function makeThumbnailtoFile($destFile){
if(!$this->isWorking()) return false;
$size = getimagesize($this->sourceFile);
switch($size[2]){
case IMAGETYPE_JPEG:
case IMAGETYPE_GIF:
case IMAGETYPE_PNG:
break; // 僅支援此三種格式
case IMAGETYPE_BMP:
if($this->_support_bmp) break;
else return false;
default:
return false;
}
$CLI = "$this->_exec \"$this->sourceFile\" \"$destFile\" $this->thumbWidth $this->thumbHeight $this->thumbQuality";
@exec($CLI);
return true;
}
}
?>
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v7/lib/thumb/thumb.imagick.php
@@ -0,0 +1,53 @@
<?php
/**
* Thumbnail Generate API: Imagick Wrapper
*
* 提供程式便於以 Imagick (Imagick Image Library) 生成預覽圖的物件
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
*/
 
class ThumbWrapper{
var $sourceFile, $sourceWidth, $sourceHeight, $thumbWidth, $thumbHeight, $thumbSetting, $thumbQuality;
 
function ThumbWrapper($sourceFile='', $sourceWidth=0, $sourceHeight=0){
$this->sourceFile = $sourceFile;
$this->sourceWidth = $sourceWidth;
$this->sourceHeight = $sourceHeight;
}
 
function getClass(){
$str = 'Imagick Wrapper';
if($this->isWorking()){
$a = new Imagick(); $b = $a->getVersion(); $b = $b['versionString'];
$str .= ' : '.str_replace(strrchr($b, ' '), '', $b);
unset($a); unset($b);
}
return $str;
}
 
function isWorking(){
return extension_loaded('imagick') && class_exists('Imagick');
}
 
function setThumbnailConfig($thumbWidth, $thumbHeight, $thumbSetting){
$this->thumbWidth = $thumbWidth;
$this->thumbHeight = $thumbHeight;
$this->thumbSetting = $thumbSetting;
$this->thumbQuality = $thumbSetting['Quality'];
}
 
function makeThumbnailtoFile($destFile){
$returnVal = false;
if(!$this->isWorking()) return false;
$image = new Imagick($this->sourceFile);
$image->setCompressionQuality($this->thumbQuality);
$image->thumbnailImage($this->thumbWidth, $this->thumbHeight);
$returnVal = $image->writeImage($destFile);
unset($image);
return $returnVal;
}
}
?>
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v7/lib/thumb/thumb.magickwand.php
@@ -0,0 +1,54 @@
<?php
/**
* Thumbnail Generate API: MagickWand Wrapper
*
* 提供程式便於以 MagickWand for PHP 生成預覽圖的物件
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
*/
 
class ThumbWrapper{
var $sourceFile, $sourceWidth, $sourceHeight, $thumbWidth, $thumbHeight, $thumbSetting, $thumbQuality;
 
function ThumbWrapper($sourceFile='', $sourceWidth=0, $sourceHeight=0){
$this->sourceFile = $sourceFile;
$this->sourceWidth = $sourceWidth;
$this->sourceHeight = $sourceHeight;
}
 
function getClass(){
$str = 'MagickWand Wrapper';
if($this->isWorking()){
$a = MagickGetVersion(); $b = $a[0];
$str .= ' : '.str_replace(strrchr($b, ' '), '', $b);
unset($a); unset($b);
}
return $str;
}
 
function isWorking(){
return extension_loaded('magickwand') && function_exists('MagickThumbnailImage');
}
 
function setThumbnailConfig($thumbWidth, $thumbHeight, $thumbSetting){
$this->thumbWidth = $thumbWidth;
$this->thumbHeight = $thumbHeight;
$this->thumbSetting = $thumbSetting;
$this->thumbQuality = $thumbSetting['Quality'];
}
 
function makeThumbnailtoFile($destFile){
$returnVal = false;
if(!$this->isWorking()) return false;
$image = NewMagickWand();
MagickReadImage($image, $this->sourceFile);
MagickSetImageCompressionQuality($image, $this->thumbQuality);
MagickThumbnailImage($image, $this->thumbWidth, $this->thumbHeight);
$returnVal = MagickWriteImage($image, $destFile);
unset($image);
return $returnVal;
}
}
?>
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v7/lib/thumb/thumb.gd.php
@@ -0,0 +1,256 @@
<?php
/**
* Thumbnail Generate API: GD Wrapper
*
* 提供程式便於以 GD Library 生成預覽圖的物件
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
*/
 
class ThumbWrapper{
var $sourceFile, $sourceWidth, $sourceHeight, $thumbWidth, $thumbHeight, $thumbSetting, $thumbQuality;
 
function ThumbWrapper($sourceFile='', $sourceWidth=0, $sourceHeight=0){
$this->sourceFile = $sourceFile;
$this->sourceWidth = $sourceWidth;
$this->sourceHeight = $sourceHeight;
}
 
function _GetLeftShiftCount($dwVal,$len=4) {
$nCount = 0;
for ($i=0; $i<$len * 8; $i++) {
if ($dwVal & 1) $nCount++;
$dwVal >>= 1;
}
return (8 - $nCount);
}
function _GetRightShiftCount($dwVal,$len=4) {
for ($i=0; $i<$len * 8; $i++) {
if ($dwVal & 1) return $i;
$dwVal >>= 1;
}
return -1;
}
 
/* ImageCreateFromBMP : 讓GD可處理BMP圖檔
此為修改後最適化版本。原出處:http://www.php.net/imagecreate#53879
原作宣告:
*****************************
Function: ImageCreateFromBMP
Author: DHKold
Contact: admin@dhkold.com
Date: The 15th of June 2005
Version: 2.0B
*****************************/
function _ImageCreateFromBMP($filename){
// 序章:以二進位模式開啟檔案流
if(!$f1 = fopen($filename, 'rb')) return FALSE;
 
// 第一步:讀取BMP檔頭
$FILE = unpack('vfile_type/Vfile_size/Vreserved/Vbitmap_offset', fread($f1, 14));
if($FILE['file_type']!=19778) return FALSE; // BM
 
// 第二步:讀取BMP資訊
// 僅支援BITMAPINFOHEADER,不支援BITMAPV4HEADER及BITMAPV5HEADER
$BMP = unpack('Vheader_size/Vwidth/Vheight/vplanes/vbits_per_pixel/Vcompression/Vsize_bitmap/Vhoriz_resolution/Vvert_resolution/Vcolors_used/Vcolors_important', fread($f1, 40));
$BMP['colors'] = pow(2, $BMP['bits_per_pixel']);
if($BMP['size_bitmap']==0) $BMP['size_bitmap'] = $FILE['file_size'] - $FILE['bitmap_offset'];
$BMP['bytes_per_pixel'] = $BMP['bits_per_pixel'] / 8;
$BMP['decal'] = ($BMP['width'] * $BMP['bytes_per_pixel'] / 4);
$BMP['decal'] -= floor($BMP['width'] * $BMP['bytes_per_pixel'] / 4);
$BMP['decal'] = 4 - (4 * $BMP['decal']);
if($BMP['decal']==4) $BMP['decal'] = 0;
 
// 第三步:讀取色盤資訊
$PALETTE = array();
if ($BMP['colors'] <=256 || $BMP['compression'] == 3) {
if($BMP['compression'] == 3) // BI_BITFIELDS
$PALETTE = unpack('V3', fread($f1,12));
else
$PALETTE = unpack('V'.$BMP['colors'], fread($f1,$BMP['colors']*4));
}
if($BMP['compression'] == 3) // BI_BITFIELDS
$mask = array(array($PALETTE[1],$PALETTE[2],$PALETTE[3]),
array($this->_GetRightShiftCount($PALETTE[1]),$this->_GetRightShiftCount($PALETTE[2]),$this->_GetRightShiftCount($PALETTE[3])),
array($this->_GetLeftShiftCount($PALETTE[1]),$this->_GetLeftShiftCount($PALETTE[2]),$this->_GetLeftShiftCount($PALETTE[3])));
else {
if($BMP['colors'] == 65536) $mask = array(array(0x7C00,0x3E0,0x1F),array(10,5,0),array(3,3,3));
}
 
// 第四步:關閉檔案,變換每一個畫素
$IMG = fread($f1, $BMP['size_bitmap']);
fclose($f1);
$VIDE = chr(0);
 
$res = ImageCreateTrueColor($BMP['width'], $BMP['height']);
$P = 0;
$Y = $BMP['height'] - 1;
 
if($BMP['compression'] == 1 && $BMP['colors'] == 256) { // BI_RLE8
$imgDataLen=strlen($IMG);
$RLEData='';
while(true) {
$prefix=ord(substr($IMG, floor($P++), 1));
$suffix=ord(substr($IMG, floor($P++), 1));
 
if(($prefix==0)&&($suffix==1)) break; // end of RLE stream
if($P>=$imgDataLen) break;
 
while(!(($prefix==0)&&($suffix==0))){ // ! end of RLE line
if($prefix==0){ // Command
$RLEData.=substr($IMG, floor($P), $suffix);
$P+=$suffix;
if($P%2==1) $P++;
} elseif($prefix>0){ // Repeat
for($r=0;$r<$prefix;$r++)
$RLEData.=chr($suffix);
}
$prefix=ord(substr($IMG, floor($P++), 1));
$suffix=ord(substr($IMG, floor($P++), 1));
}
for($X=0;$X<strlen($RLEData);$X++) // Write
ImageSetPixel($res, $X, $Y, $PALETTE[ord($RLEData[$X])+1]);
$RLEData='';
$Y--;
}
} elseif($BMP['compression'] == 2 && $BMP['colors'] == 16) { // BI_RLE4
$imgDataLen=strlen($IMG);
$RLEData='';
while(true) {
$prefix=ord(substr($IMG, floor($P++), 1));
$suffix=ord(substr($IMG, floor($P++), 1));
 
if(($prefix==0)&&($suffix==1)) break; // end of RLE stream
if($P>=$imgDataLen) break;
 
while(!(($prefix==0)&&($suffix==0))){ // ! end of RLE line
if($prefix==0){ // Command
for($h=0;$h<$suffix;$h++) {
$COLOR = ord(substr($IMG, floor($P), 1));
$RLEData.=($h%2==0)?chr($COLOR >> 4):chr($COLOR & 0x0F);
$P += $BMP['bytes_per_pixel'];
}
$P=ceil($P);
if($P%2==1) $P++;
} elseif($prefix>0){ // Repeat
for($r=0;$r<$prefix;$r++)
$RLEData.=($r%2==0)?chr($suffix >> 4):chr($suffix & 0x0F);
}
$prefix=ord(substr($IMG, floor($P++), 1));
$suffix=ord(substr($IMG, floor($P++), 1));
}
 
for($X=0;$X<strlen($RLEData);$X++) // Write
ImageSetPixel($res, $X, $Y, $PALETTE[ord($RLEData[$X])+1]);
$RLEData='';
$Y--;
 
}
} else {
while($Y >= 0){
$X = 0;
while($X < $BMP['width']){
switch($BMP['bits_per_pixel']){
case 32:
$COLOR = unpack('V', substr($IMG, $P, 4));
$COLOR[1] &= 0xFFFFFF; // 不處理Alpha
break;
case 24: $COLOR = unpack('V', substr($IMG, $P, 3).$VIDE); break;
case 16:
$COLOR = unpack("v",substr($IMG,$P,2));
$COLOR[1] = (((($COLOR[1] & $mask[0][0])>>$mask[1][0])<<$mask[2][0])<<16) |
(((($COLOR[1] & $mask[0][1])>>$mask[1][1])<<$mask[2][1])<<8) |
((($COLOR[1] & $mask[0][2])>>$mask[1][2])<<$mask[2][2]);
break;
case 8: $COLOR = unpack('n', $VIDE.substr($IMG, $P, 1)); break;
case 4:
$COLOR = unpack('n', $VIDE.substr($IMG, floor($P), 1));
if(($P*2)%2==0) $COLOR[1] = ($COLOR[1] >> 4);
else $COLOR[1] = ($COLOR[1] & 0x0F);
break;
case 1:
$COLOR = unpack('n', $VIDE.substr($IMG, floor($P), 1));
switch(($P * 8) % 8){
case 0: $COLOR[1] = $COLOR[1] >> 7; break;
case 1: $COLOR[1] = ($COLOR[1] & 0x40) >> 6; break;
case 2: $COLOR[1] = ($COLOR[1] & 0x20) >> 5; break;
case 3: $COLOR[1] = ($COLOR[1] & 0x10) >> 4; break;
case 4: $COLOR[1] = ($COLOR[1] & 0x8) >> 3; break;
case 5: $COLOR[1] = ($COLOR[1] & 0x4) >> 2; break;
case 6: $COLOR[1] = ($COLOR[1] & 0x2) >> 1; break;
case 7: $COLOR[1] = ($COLOR[1] & 0x1);
}
break;
default:
return FALSE;
}
if($BMP['bits_per_pixel']<16) $COLOR[1] = $PALETTE[$COLOR[1]+1];
ImageSetPixel($res, $X, $Y, $COLOR[1]);
$X++;
$P += $BMP['bytes_per_pixel'];
}
$Y--;
$P += $BMP['decal'];
}
}
 
// 終章:回傳新圖像
return $res;
}
 
function getClass(){
$str = 'GD Wrapper';
if($this->isWorking()){
$a = gd_info(); $str .= ' : '.$a['GD Version'];
}
return $str;
}
 
function isWorking(){
return extension_loaded('gd') && function_exists('ImageCreateTrueColor') && function_exists('ImageCopyResampled');
}
 
function setThumbnailConfig($thumbWidth, $thumbHeight, $thumbSetting){
$this->thumbWidth = $thumbWidth;
$this->thumbHeight = $thumbHeight;
$this->thumbSetting = $thumbSetting;
$this->thumbQuality = $thumbSetting['Quality'];
}
 
function makeThumbnailtoFile($destFile){
if(!$this->isWorking()) return false;
$size = getimagesize($this->sourceFile);
switch($size[2]){
case IMAGETYPE_JPEG:
$im_in = @ImageCreateFromJPEG($this->sourceFile); break;
case IMAGETYPE_GIF:
$im_in = @ImageCreateFromGIF($this->sourceFile); break;
case IMAGETYPE_PNG:
$im_in = @ImageCreateFromPNG($this->sourceFile); break;
case IMAGETYPE_BMP:
$im_in = $this->_ImageCreateFromBMP($this->sourceFile); break;
default:
return false;
}
if(!$im_in) return false;
$im_out = ImageCreateTrueColor($this->thumbWidth, $this->thumbHeight);
ImageCopyResampled($im_out, $im_in, 0, 0, 0, 0, $this->thumbWidth, $this->thumbHeight, $this->sourceWidth, $this->sourceHeight);
switch(strtolower($this->thumbSetting['Format'])){
case 'png':
ImagePNG($im_out, $destFile, $this->thumbQuality);
break;
case 'gif':
ImageGIF($im_out, $destFile);
break;
case 'jpg':
default:
ImageJPEG($im_out, $destFile, $this->thumbQuality);
break;
}
ImageDestroy($im_in); ImageDestroy($im_out);
return true;
}
}
?>
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v7/lib/lang/en_US.php
@@ -0,0 +1,201 @@
<?php
/*
Pixmicat! language file - English (U.S.) [en_US]
*/
if (!isset($language)) $language = Array();
 
// pixmicat.php
$language['page_not_found'] = 'Sorry, the page you requested is not found.';
$language['thread_not_found'] = 'The thread you want to reply does not exist!';
$language['del_head'] = '[Delete Post]';
$language['del_img_only'] = 'Delete image only';
$language['del_pass'] = 'Password: ';
$language['del_btn'] = ' Submit ';
$language['prev_page'] = 'Previous';
$language['first_page'] = 'First';
$language['all_pages'] = 'ALL';
$language['next_page'] = 'Next';
$language['last_page'] = 'Last';
$language['img_sample'] = '[Thumbnail]';
$language['img_filename'] = 'Filename: ';
$language['reply_btn'] = 'Reply';
$language['warn_sizelimit'] = 'Image will be deleted soon because of the storage limit of image.';
$language['warn_oldthread'] = 'Thread will be deleted soon because it is old.';
$language['warn_locked'] = 'Thread is locked by administrator.';
$language['notice_omitted'] = '%1$s posts omitted. Click Reply to view.';
$language['post_name'] = 'Name: ';
$language['post_category'] = 'Category: ';
$language['regist_notpost'] = 'Please use the form on the board to submit.';
$language['regist_nospam'] = 'Anti Spambot system activated.';
$language['regist_ipfiltered'] = 'Your connection is refused. Reason: %1$s';
$language['regist_wordfiltered'] = 'Banned words is found, cannot submit.';
$language['regist_upload_exceedphp'] = 'Upload failed.<br />File size exceeds server limitation.';
$language['regist_upload_exceedcustom'] = 'Upload failed.<br />File size exceeds limitation.';
$language['regist_upload_incompelete'] = 'Upload failed.<br />Incomplete upload. Please retry.';
$language['regist_upload_direrror'] = 'Upload failed.<br />Wrong setting of upload temporary directory. Please contact the system administrator.';
$language['regist_upload_noimg'] = 'Please check No Image check box if you post without uploading an image!';
$language['regist_upload_filenotfound'] = 'Upload failed.<br />Server disabled file uploading, permission denied, or unsupported format.';
$language['regist_upload_killincomp'] = '[Notice] Your sending was canceled because of the incorrect file size.';
$language['regist_upload_notimage'] = 'Upload failed.<br />Files other than image is not accepted.';
$language['regist_upload_notsupport'] = 'Unsupported image.';
$language['regist_upload_blocked'] = 'Upload failed.<br />Uploading this image is blocked.';
$language['regist_uploaded'] = 'Image %1$s uploaded.<br />';
$language['regist_sakuradetected'] = 'Big5 sakura Japanese characters detected.';
$language['regist_withoutname'] = 'Please fill your name.';
$language['regist_withoutcomment'] = 'Please fill comment field if you don\'t upload an image.';
$language['regist_nametoolong'] = 'Name is too long.';
$language['regist_emailtoolong'] = 'E-mail is too long.';
$language['regist_topictoolong'] = 'Topic is too long.';
$language['regist_longthreadnum'] = 'The thread you reply may be wrong.';
$language['admin'] = 'Admin';
$language['deletor'] = 'Deletor';
$language['trip_pre'] = '!';
$language['trip_pre_fake'] = '|';
$language['cap_char'] = '¤';
$language['cap_char_fake'] = 'ø';
$language['regist_commenttoolong'] = 'Comment is too long.';
$language['notice_incompletefile'] = 'Notice: Incomplete image.';
$language['sun'] = 'Sun';
$language['mon'] = 'Mon';
$language['tue'] = 'Tue';
$language['wed'] = 'Wed';
$language['thu'] = 'Thu';
$language['fri'] = 'Fri';
$language['sat'] = 'Sat';
$language['regist_successivepost'] = 'Please wait for a while for continuous posting.';
$language['regist_duplicatefile'] = 'Upload failed.<br />Same file uploaded lately.';
$language['regist_threaddeleted'] = 'This thread is too old that it is deleted!';
$language['regist_threadlocked'] = 'Thread is locked by administrator!';
$language['regist_redirect'] = '%1$s Redirecting... <p>If your browser don\'t redirect for you, please click: <a href="%2$s">Return</a></p>';
$language['del_notchecked'] = 'Nothing selected for deletion. Please go back and select again.';
$language['del_wrongpwornotfound'] = 'No such post or wrong password.';
$language['admin_wrongpassword'] = 'Wrong password';
$language['return'] = 'Return';
$language['admin_remake'] = 'Update';
$language['admin_frontendmanage'] = 'Front-End Manage (Sign in needed)';
$language['admin_delete'] = 'Delete';
$language['admin_top'] = 'Administrator mode';
$language['admin_manageposts'] = 'Manage posts';
$language['admin_optimize'] = 'Optimize';
$language['admin_check'] = 'Check data source';
$language['admin_repair'] = 'Repair data source';
$language['admin_export'] = 'Export data';
$language['admin_logout'] = 'Sign out';
$language['admin_verify_btn'] = ' Sign in ';
$language['admin_archive'] = '<th>Archive</th>';
$language['admin_notices'] = '<ul><li>If you want to delete a post, check the "delete" checkbox before that post and click Submit button.</li><li>If you want to delete image only, please check "Delete image only" checkbox and follow normal deletion procedures.</li><li>If you want to lock/unlock a thread, please check "Stop" checkbox of that thread and click "Submit" button.</li><li>Please press "Update" after managing posts.</li></ul>';
$language['admin_submit_btn'] = ' Submit ';
$language['admin_reset_btn'] = ' Reset ';
$language['admin_list_header'] = '<th>Func</th><th>Stop</th><th>Del</th><th>Date</th><th>Topic</th><th>Name</th><th>Comment</th><th>Host</th><th>Image (Bytes)<br />MD5 checksum</th>';
$language['admin_archive_btn'] = 'A';
$language['admin_stop_btn'] = 'Stop';
$language['admin_totalsize'] = '[Total size of images: <b>%1$s</b> KB ]';
$language['search_disabled'] = 'Search is disabled!';
$language['search_top'] = 'Search';
$language['search_notice'] = '<li>Please enter keyword and set search target, then press "Search" button.</li><li>Searching with multiple keywords can be done by seperating keywords with space.</li><li>You can set Search method("and searching" and "or searching") for multiple keywords searching.<p />Keyword:';
$language['search_target'] = 'Target:';
$language['search_target_comment'] = 'Comment';
$language['search_target_name'] = 'Name';
$language['search_target_topic'] = 'Topic';
$language['search_target_number'] = 'No.';
$language['search_method'] = 'Method:';
$language['search_method_and'] = 'AND';
$language['search_method_or'] = 'OR';
$language['search_submit_btn'] = ' Search ';
$language['search_notfound'] = 'no search result with specified keyword.';
$language['search_back'] = '[Back]';
$language['category_nokeyword'] = 'Please enter category for searching similar posts.';
$language['category_notfound'] = 'No matching posts for this category.';
$language['category_recache'] = 'Recache';
$language['module_info_top'] = 'Module Information';
$language['module_loaded'] = 'Module Loaded:';
$language['module_info'] = 'Module Infomation:';
$language['info_top'] = 'System Information';
$language['info_disabled'] = 'Disabled';
$language['info_enabled'] = 'Enabled';
$language['info_functional'] = 'Functional';
$language['info_nonfunctional'] = 'Non functional';
$language['info_basic'] = 'Basic Settings';
$language['info_basic_ver'] = 'Program version';
$language['info_basic_pio'] = 'PIO library backend and version';
$language['info_basic_threadsperpage'] = 'Threads per page';
$language['info_basic_threads'] = '';
$language['info_basic_postsperpage'] = 'Replies to show in index';
$language['info_basic_posts'] = '';
$language['info_basic_postsinthread'] = 'Posts per page in Reply mode';
$language['info_basic_posts_showall'] = '(Show all:0)';
$language['info_basic_bumpposts'] = 'Do not bump post if reply is more than';
$language['info_basic_bumphours'] = 'Thread bumping hours';
$language['info_basic_hours'] = 'hour(s)';
$language['info_basic_0disable'] = '(Disable:0)';
$language['info_basic_urllinking'] = 'URL Auto Linking';
$language['info_0no1yes'] = '(Yes:1 No:0)';
$language['info_basic_com_limit'] = 'Maximum size of comments';
$language['info_basic_com_after'] = ' Bytes';
$language['info_basic_anonpost'] = 'Anonymous posting';
$language['info_basic_anonpost_opt'] = '(Force anonymous:2 Yes:1 No:0)';
$language['info_basic_del_incomplete'] = 'Delete incomplete images';
$language['info_basic_use_sample'] = 'Use thumbnails (Quality: %1$s)';
$language['info_0notuse1use'] = '(Use:1 Not used:0)';
$language['info_basic_use_sample_func'] = '+ Thumbnails generation';
$language['info_basic_useblock'] = 'IP blocking';
$language['info_0disable1enable'] = '(Enable:1 Disable:0)';
$language['info_basic_showid'] = 'Show ID';
$language['info_basic_showid_after'] = '(force show:2 selective show:1 do not show:0)';
$language['info_basic_cr_limit'] = 'Comment row limit';
$language['info_basic_cr_after'] = ' Row(s) (unlimited:0)';
$language['info_basic_timezone'] = 'Time zone';
$language['info_basic_threadcount'] = 'Total threads count';
$language['info_basic_theme'] = 'Theme';
$language['info_dsusage_top'] = 'Data source usage';
$language['info_dsusage_max'] = 'Maximum size';
$language['info_dsusage_usage'] = 'Usage';
$language['info_dsusage_count'] = 'Current usage';
$language['info_fileusage_top'] = 'Storage limit of images:';
$language['info_fileusage_limit'] = 'Maximum size';
$language['info_fileusage_count'] = 'Current usage';
$language['info_fileusage_unlimited'] = 'Unlimited';
$language['info_server_top'] = 'Server infomation';
$language['info_server_gd'] = 'GD library ';
$language['init_permerror'] = 'No write permission in root directory. Please modify permission settings.<br />';
$language['init_inited'] = 'Environment initialized.<br />Please edit this file and remove init() statement.<br />';
$language['action_main_notsupport'] = 'Backend does not support this operation.';
$language['action_main_optimize'] = 'Optimization ';
$language['action_main_check'] = 'Check ';
$language['action_main_repair'] = 'Repair ';
$language['action_main_export'] = 'Export ';
$language['action_main_success'] = 'success!';
$language['action_main_failed'] = 'failed!';
 
// lib_common.php
$language['head_home'] = 'Home';
$language['head_search'] = 'Search';
$language['head_info'] = 'Info';
$language['head_admin'] = 'Admin';
$language['head_refresh'] = 'Refresh';
$language['form_top'] = 'Reply Mode';
$language['form_showpostform'] = 'Post';
$language['form_hidepostform'] = 'Hide form';
$language['form_name'] = 'Name';
$language['form_email'] = 'E-mail';
$language['form_topic'] = 'Topic';
$language['form_submit_btn'] = 'Submit';
$language['form_comment'] = 'Comment';
$language['form_attechment'] = 'Image';
$language['form_noattechment'] = 'No Image';
$language['form_contpost'] = 'Continuous';
$language['form_category'] = 'Category';
$language['form_category_notice'] = '(Use , to seperate)';
$language['form_delete_password'] = 'Password';
$language['form_delete_password_notice']= '(for deletion, 8 chars max)';
$language['form_notice'] = '<li>Supported file types are: %1$s</li><li>Maximum file size allowed is %2$s KB. Fill E-mail with sage for not bumping post.</li><li>mages greater than %3$s * %4$s pixels will be thumbnailed.</li>';
$language['form_notice_storage_limit'] = '<li>Current storage usage: %1$s KB / %2$s KB</li>';
$language['form_notice_noscript'] = '*** You disabled JavaScript, but this won\'t affect you when browsing and posting.';
$language['error_back'] = 'Back';
$language['ip_banned'] = 'Listed in IP/Hostname Blacklist';
$language['ip_dnsbl_banned'] = 'Listed in DNSBL(%1$s) Blacklist';
 
// mainscript.js // regist_withoutcomment,regist_upload_notsupport,js_convert_sakura
$language['js_convert_sakura'] = 'Big5 sakura Japanese characters detected, please try to convert to standard one.';
 
?>
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v7/lib/lang/zh_TW.php
@@ -0,0 +1,201 @@
<?php
/*
Pixmicat! language file - Chinese Traditional [zh_TW]
*/
if (!isset($language)) $language = Array();
 
// pixmicat.php
$language['page_not_found'] = '對不起,您所要求的頁數並不存在';
$language['thread_not_found'] = '欲回應之文章並不存在!';
$language['del_head'] = '【刪除文章】';
$language['del_img_only'] = '僅刪除附加圖檔';
$language['del_pass'] = '刪除用密碼: ';
$language['del_btn'] = ' 執行 ';
$language['prev_page'] = '上一頁';
$language['first_page'] = '第一頁';
$language['all_pages'] = 'ALL';
$language['next_page'] = '下一頁';
$language['last_page'] = '最後一頁';
$language['img_sample'] = '[以預覽圖顯示]';
$language['img_filename'] = '檔名:';
$language['reply_btn'] = '回應';
$language['warn_sizelimit'] = '這篇因附加圖檔容量限制,附加圖檔不久後就會刪除。';
$language['warn_oldthread'] = '這篇已經很舊了,不久後就會刪除。';
$language['warn_locked'] = '這篇討論串已被管理員標記為禁止回應。';
$language['notice_omitted'] = '有回應 %1$s 篇被省略。要閱讀所有回應請按下回應連結。';
$language['post_name'] = '名稱: ';
$language['post_category'] = '類別: ';
$language['regist_notpost'] = '請使用此版提供的表單來上傳';
$language['regist_nospam'] = '防止 Spambot 機制啟動!';
$language['regist_ipfiltered'] = '您所使用的連線已被拒絕。原因:%1$s';
$language['regist_wordfiltered'] = '發出的文章中有被管理員列為限制的字句,送出失敗';
$language['regist_upload_exceedphp'] = '上傳失敗<br />上傳的附加圖檔容量超過PHP內定值';
$language['regist_upload_exceedcustom'] = '上傳失敗<br />上傳的附加圖檔容量超過上傳容量限制';
$language['regist_upload_incompelete'] = '上傳失敗<br />上傳的附加圖檔不完整,請回版面再重試';
$language['regist_upload_direrror'] = '上傳失敗<br />上傳的暫存資料夾設定錯誤,請通報系統管理員';
$language['regist_upload_noimg'] = '因應防止Spam對策,發文無附加圖檔請勾選[無貼圖]核選框!';
$language['regist_upload_filenotfound'] = '上傳失敗<br />伺服器有可能禁止上傳、沒有權限,或不支援此格式';
$language['regist_upload_killincomp'] = '[Notice] Your sending was canceled because of the incorrect file size.';
$language['regist_upload_notimage'] = '上傳失敗<br />不接受圖片以外的檔案';
$language['regist_upload_notsupport'] = '附加圖檔為系統不支援的格式';
$language['regist_upload_blocked'] = '上傳失敗<br />此附加圖檔被管理員列為禁止上傳';
$language['regist_uploaded'] = '附加圖檔 %1$s 上傳完畢<br />';
$language['regist_sakuradetected'] = '偵測到您有輸入櫻花日文假名';
$language['regist_withoutname'] = '您沒有填寫名稱';
$language['regist_withoutcomment'] = '在沒有附加圖檔的情況下,請寫入內文';
$language['regist_nametoolong'] = '名稱過長';
$language['regist_emailtoolong'] = 'E-mail過長';
$language['regist_topictoolong'] = '標題過長';
$language['regist_longthreadnum'] = '欲回應的文章編號可能有誤';
$language['admin'] = '管理';
$language['deletor'] = '刪除';
$language['trip_pre'] = '◆';
$language['trip_pre_fake'] = '◇';
$language['cap_char'] = '★';
$language['cap_char_fake'] = '☆';
$language['regist_commenttoolong'] = '內文過長';
$language['notice_incompletefile'] = '注意:附加圖檔上傳不完全';
$language['sun'] = '日';
$language['mon'] = '一';
$language['tue'] = '二';
$language['wed'] = '三';
$language['thu'] = '四';
$language['fri'] = '五';
$language['sat'] = '六';
$language['regist_successivepost'] = '連續投稿請稍候一段時間';
$language['regist_duplicatefile'] = '上傳失敗<br />近期已經有相同的附加圖檔';
$language['regist_threaddeleted'] = '此討論串因為過舊已被刪除!';
$language['regist_threadlocked'] = '這篇討論串已被管理員標記為禁止回應!';
$language['regist_redirect'] = '%1$s 畫面正在切換 <p>如果瀏覽器沒有自動切換,請手動按連結前往:<a href="%2$s">回到版面</a></p>';
$language['del_notchecked'] = '你真的有要刪除嗎?請回頁面重勾選';
$language['del_wrongpwornotfound'] = '無此文章或是密碼錯誤';
$language['admin_wrongpassword'] = '密碼錯誤';
$language['return'] = '回到版面';
$language['admin_remake'] = '更新文章';
$language['admin_frontendmanage'] = '前台管理 (需先登入)';
$language['admin_delete'] = '刪除文章';
$language['admin_top'] = '管理模式';
$language['admin_manageposts'] = '管理文章';
$language['admin_optimize'] = '資料表最佳化';
$language['admin_check'] = '檢查資料表';
$language['admin_repair'] = '修復資料表';
$language['admin_export'] = '匯出資料';
$language['admin_logout'] = '登出';
$language['admin_verify_btn'] = ' 認證 ';
$language['admin_archive'] = '<th>庫存</th>';
$language['admin_notices'] = '<ul><li>想刪除文章,請勾選該文章前之「刪除」核取框之後按下執行按鈕</li><li>只想刪除文章的附加圖檔,請先勾選「僅刪除附加圖檔」再按照一般刪文方式</li><li>想停止/繼續討論串,請勾選該文章前之「停止」核取框之後按下執行按鈕</li><li>管理文章完畢,記得順手按下「更新文章」以更新靜態快取</li></ul>';
$language['admin_submit_btn'] = ' 執行 ';
$language['admin_reset_btn'] = ' 重置 ';
$language['admin_list_header'] = '<th>功能</th><th>停止</th><th>刪除</th><th>投稿日</th><th>標題</th><th>名稱</th><th>內文</th><th>主機位置名稱</th><th>附加圖檔 (Bytes)<br />MD5 檢查碼</th>';
$language['admin_archive_btn'] = '存';
$language['admin_stop_btn'] = '停';
$language['admin_totalsize'] = '【 附加圖檔使用容量總計 : <b>%1$s</b> KB 】';
$language['search_disabled'] = '管理員選擇不開放搜尋功能!';
$language['search_top'] = '搜尋';
$language['search_notice'] = '<li>請輸入要搜尋的關鍵字,設定好搜尋目標之後,按下「搜尋」按鈕。</li><li>關鍵字使用半形空白可以區隔多個搜尋關鍵字作搜尋。</li><li>如果有多個關鍵字,可以選擇搜尋方法,系統提供 AND(交集) 和 OR(聯集) 方式搜尋。<p />關鍵字:';
$language['search_target'] = '搜尋目標:';
$language['search_target_comment'] = '內文';
$language['search_target_name'] = '名稱';
$language['search_target_topic'] = '標題';
$language['search_target_number'] = '編號';
$language['search_method'] = '搜尋方法:';
$language['search_method_and'] = 'AND';
$language['search_method_or'] = 'OR';
$language['search_submit_btn'] = ' 搜尋 ';
$language['search_notfound'] = '找不到符合的關鍵字。';
$language['search_back'] = '[回上一頁]';
$language['category_nokeyword'] = '請輸入類別標籤以搜尋類似文章。';
$language['category_notfound'] = '沒有符合此類別標籤的文章';
$language['category_recache'] = '重新快取';
$language['module_info_top'] = '模組資訊';
$language['module_loaded'] = 'Module Loaded:';
$language['module_info'] = 'Module Infomation:';
$language['info_top'] = '系統資訊';
$language['info_disabled'] = '未開啟';
$language['info_enabled'] = '已開啟';
$language['info_functional'] = '功能正常';
$language['info_nonfunctional'] = '功能失常';
$language['info_basic'] = '基本設定';
$language['info_basic_ver'] = '程式版本';
$language['info_basic_pio'] = 'PIO 函式庫後端及版本';
$language['info_basic_threadsperpage'] = '一頁顯示幾篇討論串';
$language['info_basic_threads'] = '篇';
$language['info_basic_postsperpage'] = '一篇討論串最多顯示之回應筆數';
$language['info_basic_posts'] = '筆';
$language['info_basic_postsinthread'] = '回應模式一頁顯示幾筆回應內容';
$language['info_basic_posts_showall'] = '(全部顯示:0)';
$language['info_basic_bumpposts'] = '回應筆數超過多少則不自動推文';
$language['info_basic_bumphours'] = '討論串可接受推文的時間範圍';
$language['info_basic_hours'] = '小時';
$language['info_basic_0disable'] = '(關閉:0)';
$language['info_basic_urllinking'] = 'URL文字自動作成超連結';
$language['info_0no1yes'] = '(是:1 否:0)';
$language['info_basic_com_limit'] = '內文接受Bytes數';
$language['info_basic_com_after'] = ' Bytes (中文字為2Bytes)';
$language['info_basic_anonpost'] = '接受匿名發送';
$language['info_basic_anonpost_opt'] = '(強制砍名:2 是:1 否:0)';
$language['info_basic_del_incomplete'] = '自動刪除上傳不完整附加圖檔';
$language['info_basic_use_sample'] = '使用預覽圖機能 (品質:%1$s)';
$language['info_0notuse1use'] = '(使用:1 不使用:0)';
$language['info_basic_use_sample_func'] = '└ 預覽圖生成功能';
$language['info_basic_useblock'] = '封鎖檢查功能';
$language['info_0disable1enable'] = '(啟動:1 關閉:0)';
$language['info_basic_showid'] = '顯示ID';
$language['info_basic_showid_after'] = '(強制顯示:2 選擇性顯示:1 永遠不顯示:0)';
$language['info_basic_cr_limit'] = '文字換行行數上限';
$language['info_basic_cr_after'] = ' 行 (不限:0)';
$language['info_basic_timezone'] = '時區設定';
$language['info_basic_threadcount'] = '目前總討論串篇數';
$language['info_basic_theme'] = '主題風格';
$language['info_dsusage_top'] = '記錄檔使用量';
$language['info_dsusage_max'] = '最大筆數';
$language['info_dsusage_usage'] = '使用率';
$language['info_dsusage_count'] = '目前筆數';
$language['info_fileusage_top'] = '附加圖檔總容量限制功能:';
$language['info_fileusage_limit'] = '上限大小';
$language['info_fileusage_count'] = '目前容量';
$language['info_fileusage_unlimited'] = '無上限';
$language['info_server_top'] = '伺服器支援情報';
$language['info_server_gd'] = 'GD函式庫';
$language['init_permerror'] = '根目錄沒有寫入權限,請修改權限<br />';
$language['init_inited'] = '環境初始化成功!<br />請現在打開此程式刪除init()程式環境初始化區段<br />';
$language['action_main_notsupport'] = '後端並不支援此動作';
$language['action_main_optimize'] = '資料表最佳化';
$language['action_main_check'] = '資料表檢查';
$language['action_main_repair'] = '資料表修復';
$language['action_main_export'] = '資料匯出';
$language['action_main_success'] = '成功!';
$language['action_main_failed'] = '失敗!';
 
// lib_common.php
$language['head_home'] = '回首頁';
$language['head_search'] = '搜尋';
$language['head_info'] = '系統資訊';
$language['head_admin'] = '管理區';
$language['head_refresh'] = '重新整理';
$language['form_top'] = '回應模式';
$language['form_showpostform'] = '投稿';
$language['form_hidepostform'] = '隱藏表單';
$language['form_name'] = '名 稱';
$language['form_email'] = 'E-mail';
$language['form_topic'] = '標 題';
$language['form_submit_btn'] = '送 出';
$language['form_comment'] = '內 文';
$language['form_attechment'] = '附加圖檔';
$language['form_noattechment'] = '無貼圖';
$language['form_contpost'] = '連貼機能';
$language['form_category'] = '類別標籤';
$language['form_category_notice'] = '(請以 , 逗號分隔多個標籤)';
$language['form_delete_password'] = '刪除用密碼';
$language['form_delete_password_notice']= '(刪除文章用。英數字8字元以內)';
$language['form_notice'] = '<li>可附加圖檔類型:%1$s,瀏覽器才能正常附加圖檔</li><li>附加圖檔最大上傳資料量為 %2$s KB。當回文時E-mail填入sage為不推文功能</li><li>當檔案超過寬 %3$s 像素、高 %4$s 像素時會自動縮小尺寸顯示</li>';
$language['form_notice_storage_limit'] = '<li>目前附加圖檔使用量大小: %1$s KB / %2$s KB</li>';
$language['form_notice_noscript'] = '*您選擇關閉了JavaScript,但這對您的瀏覽及發文應無巨大影響';
$language['error_back'] = '回上頁';
$language['ip_banned'] = '被列在 IP/Hostname 封鎖名單之內';
$language['ip_dnsbl_banned'] = '被列在 DNSBL(%1$s) 封鎖名單之內';
 
// mainscript.js // regist_withoutcomment,regist_upload_notsupport,js_convert_sakura
$language['js_convert_sakura'] = '偵測到您有輸入櫻花日文假名的可能性,將自動為您轉換';
 
?>
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v7/lib/lang/ja_JP.php
@@ -0,0 +1,201 @@
<?php
/*
Pixmicat! language file - Japanese [ja_JP]
*/
if (!isset($language)) $language = Array();
 
// pixmicat.php
$language['page_not_found'] = 'ページがありません';
$language['thread_not_found'] = '該当記事がみつかりません';
$language['del_head'] = '【記事削除】';
$language['del_img_only'] = '画像だけ消す';
$language['del_pass'] = '削除キー: ';
$language['del_btn'] = ' 実行 ';
$language['prev_page'] = '前のページ';
$language['first_page'] = '最初のページ';
$language['all_pages'] = 'すべて';
$language['next_page'] = '次のページ';
$language['last_page'] = '最後のページ';
$language['img_sample'] = '[サムネ表示]';
$language['img_filename'] = '画像ファイル名:';
$language['reply_btn'] = '返信';
$language['warn_sizelimit'] = 'この画像は容量リミットの原因なので、もうすぐ消えます。';
$language['warn_oldthread'] = 'このスレは古いので、もうすぐ消えます。';
$language['warn_locked'] = '管理者によるスレッドストップです。';
$language['notice_omitted'] = 'レス %1$s 件省略。全て読むには返信ボタンを押してください。';
$language['post_name'] = 'Name ';
$language['post_category'] = 'カテゴリ: ';
$language['regist_notpost'] = '不正な投稿をしないで下さい(post)';
$language['regist_nospam'] = '反 Spambot 機能スタート!';
$language['regist_ipfiltered'] = '拒絶されました(host) 原因:%1$s';
$language['regist_wordfiltered'] = '拒絶されました(str)';
$language['regist_upload_exceedphp'] = 'アップロードに失敗しました<br />PHPファイルサイズオーバー';
$language['regist_upload_exceedcustom'] = 'アップロードに失敗しました<br />ファイルサイズオーバー';
$language['regist_upload_incompelete'] = 'アップロードに失敗しました<br />ファイルが破損しています';
$language['regist_upload_direrror'] = 'アップロードに失敗しました<br />パーミッションエラー';
$language['regist_upload_noimg'] = '画像がありません';
$language['regist_upload_filenotfound'] = 'アップロードに失敗しました<br />サーバがサポートしていない可能性があります';
$language['regist_upload_killincomp'] = '[Notice] Your sending was canceled because of the incorrect file size.';
$language['regist_upload_notimage'] = 'アップロードに失敗しました<br />画像ファイル以外は受け付けません';
$language['regist_upload_notsupport'] = 'アップロードに失敗しました<br />GIF,JPG,PNG以外の画像ファイルは受け付けません';
$language['regist_upload_blocked'] = 'アップロードに失敗しました<br />禁止画像です';
$language['regist_uploaded'] = '画像 %1$s のアップロードが成功しました<br />';
$language['regist_sakuradetected'] = '櫻花仮名を検出した';
$language['regist_withoutname'] = '名前がありません';
$language['regist_withoutcomment'] = '何か書いて下さい';
$language['regist_nametoolong'] = '名前が長すぎます';
$language['regist_emailtoolong'] = 'メル欄が長すぎます';
$language['regist_topictoolong'] = '題名が長すぎます';
$language['regist_longthreadnum'] = '変なレス番号指定するんじゃないの';
$language['admin'] = '管理';
$language['deletor'] = '削除';
$language['trip_pre'] = '◆';
$language['trip_pre_fake'] = '◇';
$language['cap_char'] = '★';
$language['cap_char_fake'] = '☆';
$language['regist_commenttoolong'] = 'コメントが長すぎます';
$language['notice_incompletefile'] = '注意:ファイルが破損しています';
$language['sun'] = '日';
$language['mon'] = '月';
$language['tue'] = '火';
$language['wed'] = '水';
$language['thu'] = '木';
$language['fri'] = '金';
$language['sat'] = '土';
$language['regist_successivepost'] = '連続投稿はもうしばらく時間を置いてからお願い致します';
$language['regist_duplicatefile'] = 'アップロードに失敗しました<br />同じ画像があります';
$language['regist_threaddeleted'] = 'スレッドがありません';
$language['regist_threadlocked'] = 'もう書けませんです';
$language['regist_redirect'] = '%1$s 画面を切り替えます。<p>しばらく待っても変わらない場合は、<a href="%2$s">こちら</a>をクリックしてください。</p>';
$language['del_notchecked'] = '何でもない削除のために選択されなかった。';
$language['del_wrongpwornotfound'] = '該当記事が見つからないかパスワードが間違っています';
$language['admin_wrongpassword'] = '"パスワードが違います';
$language['return'] = '掲示板に戻る';
$language['admin_remake'] = 'ログを更新する';
$language['admin_frontendmanage'] = 'フロントエンド管理 (認証が必要です)';
$language['admin_delete'] = '削除する';
$language['admin_top'] = '管理モード';
$language['admin_manageposts'] = '記事管理';
$language['admin_optimize'] = 'テーブルを最適化する';
$language['admin_check'] = 'テーブルをチェックする';
$language['admin_repair'] = 'テーブルを修復する';
$language['admin_export'] = 'データをエクスポートする';
$language['admin_logout'] = 'ログアウト';
$language['admin_verify_btn'] = ' 認証 ';
$language['admin_archive'] = '<th>保管</th>';
$language['admin_notices'] = '<ul><li>削除したい記事の削除チェックボックスにチェックを入れ、実行ボタンを押して下さい。</li><li>終了/再開したいスレのストップチェックボックスにチェックを入れ、実行ボタンを押して下さい。</li><li>記事を管理した後に、[ログを更新する]を押してください。</li></ul>';
$language['admin_submit_btn'] = ' 実行する ';
$language['admin_reset_btn'] = ' リセット ';
$language['admin_list_header'] = '<th>機能</th><th>ストップ</th><th>削除</th><th>投稿日</th><th>題名</th><th>投稿者</th><th>コメント</th><th>ホスト名</th><th>添付(Bytes)<br />MD5</th>';
$language['admin_archive_btn'] = '保管';
$language['admin_stop_btn'] = 'ストップ';
$language['admin_totalsize'] = '【 画像データ合計 : <b>%1$s</b> KB 】';
$language['search_disabled'] = '検索は禁じられます。';
$language['search_top'] = '検索';
$language['search_notice'] = '<li>お探しの記事に関連する言葉をキーワード入力欄に入力して検索ボタンを押してください。</li><li>複数のキーワードの間にスペースを入れて入力します。</li><li>複数キーワード間の検索条件を細かく指定する場合に利用します。<p />キーワード:';
$language['search_target'] = '検索対象:';
$language['search_target_comment'] = 'コメント';
$language['search_target_name'] = 'お名前';
$language['search_target_topic'] = '題名';
$language['search_target_number'] = '記事No.';
$language['search_method'] = '検索条件:';
$language['search_method_and'] = 'AND';
$language['search_method_or'] = 'OR';
$language['search_submit_btn'] = ' 検索 ';
$language['search_notfound'] = 'キーワードにマッチする記事がありませんでした。';
$language['search_back'] = '[戻る]';
$language['category_nokeyword'] = '探すことの同様の記事のためにカテゴリに入ってください。'; /***/
$language['category_notfound'] = 'カテゴリにマッチする記事がありませんでした。';
$language['category_recache'] = 'キャッシュを更新する';
$language['module_info_top'] = 'モジュール情報';
$language['module_loaded'] = 'Module Loaded:';
$language['module_info'] = 'Module Infomation:';
$language['info_top'] = 'システム情報';
$language['info_disabled'] = '無効';
$language['info_enabled'] = '有効';
$language['info_functional'] = '働ける';
$language['info_nonfunctional'] = '働けない';
$language['info_basic'] = '基本設定';
$language['info_basic_ver'] = 'バージョン';
$language['info_basic_pio'] = 'PIO バックエンドとバージョン';
$language['info_basic_threadsperpage'] = '1ページに表示する記事';
$language['info_basic_threads'] = '件';
$language['info_basic_postsperpage'] = 'スレに表示するレス';
$language['info_basic_posts'] = '件';
$language['info_basic_postsinthread'] = '1ページに表示するレス';
$language['info_basic_posts_showall'] = '(すべて:0)';
$language['info_basic_bumpposts'] = '強制sageレス数';
$language['info_basic_bumphours'] = '強制sageまでの時間';
$language['info_basic_hours'] = '時間';
$language['info_basic_0disable'] = '(無効:0)';
$language['info_basic_urllinking'] = 'URLをリンク';
$language['info_0no1yes'] = '(する:1 しない:0)';
$language['info_basic_com_limit'] = 'コメント欄最大文字数';
$language['info_basic_com_after'] = '';
$language['info_basic_anonpost'] = '名前を必須に';
$language['info_basic_anonpost_opt'] = '(強制名無し化:2 いいえ:1 はい:0)';
$language['info_basic_del_incomplete'] = '破損したファイルを消す';
$language['info_basic_use_sample'] = 'サムネイルを作る (画質:%1$s)';
$language['info_0notuse1use'] = '(する:1 しない:0)';
$language['info_basic_use_sample_func'] = '└ サムネイルを作り関数';
$language['info_basic_useblock'] = '拒絶するホスト';
$language['info_0disable1enable'] = '(有効:1 無効:0)';
$language['info_basic_showid'] = 'IDを表示する?';
$language['info_basic_showid_after'] = '(強制:2 する:1 しない:0)';
$language['info_basic_cr_limit'] = '改行を抑制する行数';
$language['info_basic_cr_after'] = ' 行 (しない:0)';
$language['info_basic_timezone'] = '時間帯設定';
$language['info_basic_threadcount'] = '現在のスレ数';
$language['info_basic_theme'] = 'テーマ';
$language['info_dsusage_top'] = 'データソース使用量';
$language['info_dsusage_max'] = '最大値';
$language['info_dsusage_usage'] = '使用率';
$language['info_dsusage_count'] = '現在の値';
$language['info_fileusage_top'] = '添付ファイル容量リミット:';
$language['info_fileusage_limit'] = '大きさの上限';
$language['info_fileusage_count'] = '現在の容量';
$language['info_fileusage_unlimited'] = '限界なし';
$language['info_server_top'] = 'サーバー支援情報';
$language['info_server_gd'] = 'GD モジュール';
$language['init_permerror'] = 'カレントディレクトリに書けません<br />';
$language['init_inited'] = '環境初始化成功した!<br />このファイルを編集して、init()を削除してください。<br />';
$language['action_main_notsupport'] = 'バックエンドには、この動作がありません。';
$language['action_main_optimize'] = 'テーブル最適化';
$language['action_main_check'] = 'テーブルチェック';
$language['action_main_repair'] = 'テーブル修復';
$language['action_main_export'] = 'データエクスポート';
$language['action_main_success'] = '成功した';
$language['action_main_failed'] = '失敗した';
 
// lib_common.php
$language['head_home'] = 'ホーム';
$language['head_search'] = '検索';
$language['head_info'] = 'システム情報';
$language['head_admin'] = '管理用';
$language['head_refresh'] = 'リロード';
$language['form_top'] = 'レス送信モード';
$language['form_showpostform'] = '投稿';
$language['form_hidepostform'] = 'フォームを隠す';
$language['form_name'] = 'おなまえ';
$language['form_email'] = 'E-mail';
$language['form_topic'] = '題  名';
$language['form_submit_btn'] = '送信する';
$language['form_comment'] = 'コメント';
$language['form_attechment'] = '添付File';
$language['form_noattechment'] = '画像なし';
$language['form_contpost'] = '連貼り';
$language['form_category'] = 'カテゴリ';
$language['form_category_notice'] = '(複数のカテゴリの間に「,」を入れて入力します)';
$language['form_delete_password'] = '投稿キー';
$language['form_delete_password_notice']= '(記事の削除用。英数字で8文字以内)';
$language['form_notice'] = '<li>添付可能ファイル:%1$s ブラウザによっては正常に添付できないことがあります。</li><li>最大投稿データ量は %2$s KBまで。sage機能付き。</li><li>画像は横 %3$s ピクセル、縦 %4$s ピクセルを超えると縮小表示されます。</li>';
$language['form_notice_storage_limit'] = '<li>現在の添付ファイル使用量: %1$s KB / %2$s KB</li>';
$language['form_notice_noscript'] = '*JavaScriptは無効にでもブラウジングと返信に影響しない。';
$language['error_back'] = '戻る';
$language['ip_banned'] = 'IP/ホスト名ブラックリストに記載されています。';
$language['ip_dnsbl_banned'] = 'DNSBL(%1$s) ブラックリストに記載されています。';
 
// mainscript.js // regist_withoutcomment,regist_upload_notsupport,js_convert_sakura
$language['js_convert_sakura'] = '櫻花仮名を検出した。変換します。';
 
?>
New file
/release/PIO-v7/ChangeLog
@@ -0,0 +1,533 @@
Pixmicat!-PIO 修改紀錄
----------------------
2013/02/08 : 7th.Release
- [config] 增加 DEBUG 除錯開關
- [config] 增加 ROOTPATH 根目錄絕對位置常數
- [config] 增加 TRUST_HTTP_X_FORWARDED_FOR 開關
- [PMCLibrary] 抽出介面,面向介面設計,減低直接對實作的依賴
- [PMCLibrary] PIO 實作介面 IPIO,並修改錯誤拋出機制
- [PMCLibrary] PIO Condition 實作介面 IPIOCondition
- [PMCLibrary] [#39171] 修正 PIO 匯入時間戳問題
- [PMCLibrary] 新增 ILogger 介面及 SimpleLogger 實作,可作為除錯紀錄
- [PMCLibrary] 新增 LoggerInjector,攔截錯誤訊息並可提供額外的函式除錯資訊
- [PMCLibrary] Language 改寫為單例
- [PMCLibrary] PMS 去除不必要的架構,僅留單檔 lib_pms.php
- [PMCLibrary] 新增單例類別 PMCLibrary,取代原有各函式庫 global 變數
- [pixmicat] 新增全域例外捕捉機制,並可產生除錯紀錄
- [pixmicat] 修改函式庫載入方式
 
2012/11/22
- 修正 PIOmysqli 部分錯字
- [#39169] 修正 PIO mysql_pconnect 關閉造成連線失敗
- [#39171] 修正 PIO root 欄位參數 '0' 在 MySQL 嚴格設定下被拒絕的問題
- [#39172] 修正 IFS 因 register_shutdown_function 造成回寫 log 內容時工作目錄跑掉的問題
 
2012/08/02
-[ifs] 修正 pdo_sqlite getRecord() 回傳陣列層級的問題使得處理圖檔部分錯誤
 
2012/04/08
-[pio.mysql] 修正 MySQL 5.5+ 無法建立資料表的問題
-[ifs] 修正 log 索引格式找不到預覽圖檔名的問題
-[ifs] 新增 PDO SQLite 索引避免 PHP 5.4+ SQLite 無內建的問題
-[ifs] 修正因 register_shutdown_function 可能造成的相對路徑檔案更新問題
 
2012/01/02
-[pixmicat] 修正usrdel()方法Authenticate掛載點呼叫太晚的問題
 
2011/07/03
-[tpl] 移除會造成瀏覽器快取頁面的 <meta http-equiv="Expires" /> 標籤
-[config] 調整 STATIC_HTML_UNTIL 預設值
 
2011/05/28
-[pixmicat] 修正程式當預覽圖因為PHP最大記憶體限制而強迫終止時,sizecache可能產生誤差的問題
-[config] 將PHP最大記憶體限制調整至現今預設值 128M
-[thumb.gd][thumb.repng2jpeg] 修改來源圖檔類型判斷方式
 
2011/05/16
-[PIO][FileIO] 修正依賴 s.jpg 查詢預覽圖的敘述
-[thumb.gd] 新增 PNG, GIF 格式預覽圖生成
 
2011/04/13, 14
-[thumb] 預覽圖生成物件修改為準備可支援其他圖像格式
-[config] 整合預覽圖生成設定 $THUMB_SETTING
-[FileIO] 新增 resolveThumbName() 以此查詢預覽圖副檔名可變動之完整檔名
-[FileIO] 移除 fileio.imageshack.php
-[FileIO] 將 File I/O 查詢函數 (file_exists, filesize, glob) 改用 IFS 索引查詢以加快速度
-[FileIO] 最佳化 resolveThumbName() (使用索引查找取代 LIKE 搜尋)
 
2011/04/11
-[FileIO] 將附加圖檔容量相關函式搬到 FileIO (getCurrentStorageSize, updateStorageSize)
-[FileIO] 精簡 deleteImage()
-[pixmicat] 移除 total_size() (僅保留作相容性用途)
-[pixmicat] 更新授權 Artistic License 2.0
 
2011/03/23
-[mainstyle] 使用 CSS Media query 和 Viewport 改善行動版檢視 (Android / iOS)
 
2011/03/04
-[pixmicat] 解決 total_size() 的效率問題
-[FileIO] 更新 FileIO 0.3
 
2010/09/14
-[PMS] 修正 AdminFunction 掛載點第二個參數 $param 沒有傳址發生錯誤的問題
 
2010/08/17
-[PMS] 修正 Bug
-[PMS] onlyLoad 恢復單載入,直到呼叫 callCHP 才會載入全部模組
 
2010/08/16
-[PMS] 新增 addCHP, callCHP 以實現模組間互相呼叫的方法
-[PMS] onlyLoad 會載入全部模組方便 ModulePage 其他模組的相互作用
 
2010/05/21 : 5th.Release (v100521)
-正式釋出版
 
2010/04/03
-[mainscript] 使用 Closure Complier 取代先前使用之 Packer
 
2010/02/05
-[pixmicat] 修正討論串分頁 ThreadOrder 不正常的問題
 
2010/01/28
-[PTE] 標籤內容中的標籤無效化 (01/26 的再修正)
 
2010/01/26
-[pixmicat] 刪除回應排版 $arrLabels 多餘的 {$QUOTEBTN}
-[PTE] 修正內文輸入 PTE 取代標籤會誤變換的情況 (ex: {$NAME_TEXT} => 名稱:)
 
2010/01/13
-[pixmicat] 修正autoHookAdminFunction參數不是ref的問題 *PHP 5.3相容修正
-[Templates] 修正 IE8 標準成像模式下無法複製回應文字的問題
-更新版權日期
 
2009/12/15
-[PIO] fetchPosts 新增 $fields 參數以降低total_size()的記憶體使用量 (log/logflockp只修改prototype)
 
2009/07/01
-[PIO] 靜態呼叫方法定義為靜態 *PHP 5.3相容修正
-[pixmicat.php] ereg 系列函式以 preg 系列替代 *PHP 5.3相容修正
-[PIO.sqlite3] 修正 tim 圖片檔名欄位超過 int 整數最大上限造成檔名溢位儲存無法對應的問題
 
2009/05/21 : 4th.Anniversary (v090521)
-正式釋出版
 
2009/05/16
-[pixmicat] 新增依特定類別標籤篩選觀看文章時,能根據No.連結找到文章的完整討論串位置
-[PMS] PostOnDeletion 掛載點新增 $cond 參數判斷被呼叫時機 (frontend, backend, recycle)
 
2009/04/25
-[pixmicat] 修正發文時重複引用同一文章多次後造成的HTML重複問題
 
2009/04/02
-[lib_common] 修改 getREMOTE_ADDR() 對 HTTP_X_FORWARDED_FOR 的使用問題
 
2009/03/21
-[inc_pixmicat] 因應 IE8 推出,修改針對舊版 IE 的 Bugfix 判斷條件
 
2009/01/12
-[pixmicat] 修改分頁運算方法
-[inc_pixmicat] 支援 AutoPagerize 套件換頁定義 (http://userscripts.org/scripts/show/8551)
 
2008/11/30
-[pixmicat] 修改上傳圖檔判斷是否存在的方法 (增加 is_uploaded_file() 偵測法)
 
2008/09/20
-[pixmicat] 修正發文後判斷附加圖檔容量過大開始刪除的敘述 (>= 改成 >)
-[pixmicat] 修改管理區上方的連結列 (LinksAboveBar) 是否登入的判斷參數
-[PIO] 新增 remove 系方法的 $posts 參數為空之錯誤處理
-[pixmicat] 修正後端登入之 Authenticate 掛載點
-[lib_common] 修正發文框下方說明列,回應模式時附加圖檔尺寸限制沒有正確反映的問題
 
2008/09/10 : 4th.Release.3 (v080910)
-[PMS] 修正 PHP 4 的相容性問題
 
2008/09/03 : 4th.Release.3 (v080903)
-正式釋出版
 
2008/08/07
-[PIO] 修改 FlagHelper 物件,使其更為方便讓模組修改屬性
 
2008/07/27
-[pixmicat] 修正當附加圖檔總容量限制功能關閉時,系統資訊顯示的排版錯誤問題
 
2008/07/03
-[pixmicat] 修正回應文章使用連貼機能時之不當快取,讓後續的連貼機能勾選框保持為勾選狀態的問題
-[PIO] 0.6 版定案 (SQL Only)
 
2008/05/31
-[pixmicat] 修正刪除文章時 $_POST['func'] 未定義的問題
 
2008/05/19
-[FileIO] FTP, Satellite 方式參數增加是否要將預覽圖放至遠端的功能
 
2008/04/28
-[pixmicat] 修正類別標籤受到 magic_quotes_gpc() 開啟的影響
-[pixmicat] 修正 glob() 在找不到回應快取時,因 PHP 的版本別而產生結果差異的錯誤
-[PIO.sqlite3] 修正類別標籤搜尋時列出的文章沒有按照編號遞減排序
 
2008/04/17:
-[PTE][pixmicat-festival] 排版跟預設主題一致化。
 
2008/04/07
-[PIO] PIO 0.6 alpha: 交易模式支援、SQL Injection 防護等
 
2008/03/31
-[pixmicat] 修正版面更新文章 (remake) 過程中頁面超過索引值造成的錯誤
 
2008/03/26
-[PMS] autoHookRegistBegin 增加 $isReply 參數
 
2008/03/19
-[pixmicat] 修正 res=XXX 接受非數字的格式造成的 XSS & SQL Injection 問題
-[PIO.sqlite3] 預設取消交易模式
 
2008/03/18
-[pixmicat] 修正直接瀏覽 pixmicat.php 出現動態輸出頁面並提示 page_num 未定義錯誤
-[pixmicat] 修正前台管理刪除文章時處理文章陣列混入 'func' 造成刪除錯誤
-[pixmicat] 修正前台管理刪除文章後沒有跳回前台管理頁面的問題
-[pixmicat] 修正 usrdel() 之 AdminFunction 掛載點時機,delete 也可正常抓取
-[language] 修改 del_btn 文字
 
2008/03/14
-[pixmicat] 新增前端管理功能 (以 PHP 動態輸出),支援 AdminList 跟 AdminFunction 掛載點之管理功能
-[language] 新增 admin_delete, admin_frontendmanage
 
2008/03/09
-[inc_pixmicat-uploader] 更改 uploader-liked theme <table>的擺放位置
 
2008/03/06
-[PIO.mysql] 修正 dbMaintanence()
 
2008/03/04
-[PMS][pixmicat] 試驗性新增 AdminFunction 掛載點
-[PIO] 修正 dbMaintanence('export',true) 出錯問題
-[pixmicat] valid() 加入其他 dbMaintanence 功能
-[language] action_opt_* 廢止,改為 action_main_* 並加入其他 dbMaintanence 功能
 
2008/02/28
-[PMS] 新增取得模組實體函式 getModuleInstance($module)
 
2008/02/22
-[pixmicat][PMS] 新增掛載點 ThreadOrder
-[thumb][GD] GD 支援 RLE4/8 判斷式稍作修改,如果沒壓縮就不必再判斷
 
2008/02/20
-[thumb][GD] _ImageCreateFromBMP 修正 RLE4/RLE8 可能誤判的問題
-[pixmicat] 讓 updatelog() 支援單頁生成 .htm 快取
-[PIO] dbOptimize($doit=false) 廢止,改為 dbMaintanence($action,$doit=false)
 
2008/02/19
-[PMS] 修改模組單載入後手動掛載 ModulePage 掛載點造成多載入啟動、重複載入模組並作用的問題
 
2008/02/11
-[pixmicat][PTE] 讓 PTE MAIN block 能存取 {$RESTO}
-[inc_pixmicat-uploader] 新增 uploader-liked theme
 
2008/02/06, 08
-[pixmicat][PMS] 新增掛載點 PostOnDeletion, RegistAfterCommit,移除掛載點 UsageExceed
 
2008/01/31
-[thumb][GD] _ImageCreateFromBMP 支援 RLE4/RLE8, 副檔名支援 '.rle'
 
2008/01/27
-[pixmicat][Language] 修正{$FORM_NOTICE}未與ALLOW_UPLOAD_EXT同步問題
 
2008/01/18, 20
-[pixmicat] 修正當欄位只填 XHTML 非法字元時會被系統清除而沒有任何內容卻也沒填預設值的錯誤
-[pixmicat] 修正類別標籤字元 htmlentities() 後變成亂碼的問題
-[config] 部分伺服器不相容$_SERVER['SCRIPT_FILENAME'],修改為常數方式
-[pixmicat] 部分程式碼位置更動、精簡
 
2008/01/13
-[pixmicat] 搜尋結果增加連結至原討論串連結
-[pixmicat] 系統資訊可查看目前使用之文章自動刪除機制名稱
-[pixmicat] 修正當 STORAGE_MAX 設為 0 之程式非預期錯誤情況 (一般情況此值請設定 > 0)
-[pixmicat] 修正類別標籤可能發生 XSS 的情況
 
2007/12/16
-[PMS] 修正在未初始化前 hookModuleMethod 的話會造成 autoHookXXXX 沒動作
-[pixmicat] 修正在不支援緩衝區的情況下修改 XHTML MIME 動作會發生錯誤
-[PTE][inc_pixmicat-festival] festival theme的post form下需要clear:both
 
2007/11/09,18: 4th.Release.2 (v071120)
-[lib_common] 修改 CIDR Notation 分析函式
-[lib_common] 修改 CleanStr() 刪除 XML 1.1 SE 規範之避免用字
-[pixmicat] 更改回傳 XHTML 檔頭時機
-[PIO] 修正 Log 後端 updatePost() 時未處理逗號造成資料移位兼毀損問題
-[PMS] 修正當載入模組未有掛載 ModulePage 方法仍試圖執行模組頁面造成錯誤
 
2007/10/27: 4th.Release.2 RC4 (b071027)
-[pixmicat] 修正類別標籤顯示頁面呼叫 arrangeThread() 傳入參數不合問題
-[lib_common][config][pixmicat] 新增 USE_XHTML 選項能夠指定是否回傳 XHTML 檔頭
 
2007/10/19: 4th.Release.2 RC3 (b071019)
-[pixmicat] 修正引用瀏覽系統不斷 array_flip 造成搜尋對應文章失敗的問題
 
2007/10/13,14: 4th.Release.2 RC2 (b071014)
-[PIO] PIO 0.5 正式版 (修正 updatePost() 掉引號造成失敗問題)
-[lib_common] 封鎖設定黑名單支援 CIDR Notation 模式 (ex: 192.168.0.1/16)
-[PMS][pixmicat] 模組單載入功能復活,使用動態偵測決定是否要載入其他模組
 
2007/10/06: 4th.Release.2 RC1 (b071006)
-[PMS] 模組單載入功能取消 (當其他套件未載入可能造成功能上的錯誤)
-[pixmicat] 引用瀏覽系統捨棄array_search()、開放引用首篇,對應修正樣板
-[pixmicat] 修改樣板 POSTFORM 部分,去除 Ugly hack fix 新增 {$MODE} 修改模式
-[pixmicat] $mode 取值順序改為 GET, POST
 
2007/09/29
-[config] 去除使用 $_SERVER['PHP_SELF'] (有 XSS 潛在危機)
-[lib_common][FileIO] 修正 fullURL() / _getAbsoluteURL() 使用 $_SERVER['PHP_SELF'] 之 XSS 潛在危機
 
2007/09/21
-[PIO] PIO 0.5beta: Log 增加要求編號文章是否存在判定
-[lib_common][pixmicat] 暫實裝 getREMOTE_ADDR() 函式取得使用者 IP (待完成)
-[pixmicat] 提前容量暫存刪除時機,避免因預覽圖生成失敗未運作造成暫時的容量計算錯誤
 
2007/09/16
-[pixmicat][lib_common] 實裝 Session 後端認證功能
 
2007/08/29,30
-[pixmicat] 新增支援瀏覽器強制 no-cache (Ctrl + F5) 更新快取功能
-[pixmicat] 移除筆數限制及使用量顯示
-[pixmicat] 新增快取隨文章刪除功能
-[pixmicat] 修正 Gzip 壓縮功能關閉時 $Encoding 變數未定義錯誤
-[PIO] 新增文章自動刪除機制,可用自行定義好的模式來進行自動刪除判斷
-[PIO] 移除 delOldPostes() 方法 (被文章自動刪除機制替代)
 
2007/08/17,18,20
-[PIO] 修正 MySQL setPostStatus() 自動更新到 root 欄位的問題
-[PIO] 新增 FlagHelper 類別,加回 getPostStatus() [會回傳 FlagHelper 物件]
-[pixmicat] 新增回應頁面顯示快取功能降低系統負擔 (因應原按鈕式 POST 換頁無法快取改為連結式)
-[PMS] 修正 autoHookThreadFront/Rear 掛載點生成靜態頁面會重複輸出的錯誤
 
2007/08/07,08
-[PIO] PIO Structure V3: 加大 status 儲存量
-[pixmicat][PIO] PIO 0.5alpha: 取消 getPostStatus(),修改 setPostStatus()
-[pixmicat][PIO] PIO 0.5alpha: addPost() 新增 $status 引入
-[PMS] RegistBeforeCommit 掛載點新增 &$status 引入
-[lib_common] 修正 GD ImageCreateFormBMP 對 16bit BMP 的支援
 
2007/07/26
-[pixmicat] $path 取消 global
-[pixmicat] 新增預覽圖生成函式庫物件,可以支援其他圖像處理函式庫
 
2007/07/13
-[pixmicat] 偵測上傳中斷前先判斷是否有資料長度才作,避免作白工
-[PMS] autoHookRegistBegin 修改 $upfileInfo 為傳址, 去除 $POST ($_POST 為 superglobal)
 
2007/07/09:
-[PMS] 自事前掛載改為動態掛載,並可於模組頁面時只讀取特定模組
 
2007/06/17: 4th.Release (v070617)
-正式釋出版
 
2007/06/15: 4th.Release-dev RC2 (b070615)
-[PMS] 新增 PostForm, UsageExceed 掛載點
 
2007/06/07: 4th.Release-dev RC (b070607)
-[PIO] PIO 0.4 穩定版
 
2007/06/05
-[config] 刪除 CAPTCHA 預留設定 (不考慮內建)
 
2007/05/27
-[pixmicat][PMS] 新增 RegistBegin, RegistBeforeCommit 掛載點
-[pixmicat] 修正模組資訊頁面的排版問題
-[PTE] 強化並取代原內建輸出
 
2007/05/13
-[mainscript] 改寫增加效率,以 /packer/ 壓縮得到更小檔案
- 新增 Preset() Hook 機能,能在頁面載入完執行特定掛載函式
- replace_sakura() 改寫,增加效率
-[iedivfix] 改寫,使用 Hook 機能提前執行時機
-[lib_common] 把 mainscript.js 和 iedivfix.js 順序對調,防止執行錯誤
 
2007/05/04, 05, 06
-[pixmicat] 修正名稱輸入特殊字元轉成 Unicode 參照碼後被視為 Trip 轉換留下 & 造成 XML 解析錯誤的問題 (儲存/顯示雙重檢查)
-[pixmicat] 修正搜尋文章結果印出錯誤的問題 (Since rev.363)
-[pixmicat] 修正週五 (Friday) 字串顯示錯誤的問題
 
2007/04/30
-[PMS] 新增 AdminList 掛載點功能
 
2007/04/25
-將函式庫搬移至 /lib 目錄
 
2007/04/15
-[PMS] PMS 更新 (與 Pixmicat!2 共用相同的函式庫),並廢止PMS::getModulePageURL()方法
 
2007/03/31
-[pixmicat] 修改 Trip, Cap 生成函式 (ereg 在 PHP6 即將廢止,以 PCRE 函式取代)
-[pixmicat] 修正 "管理"、"刪除" 在使用 Admin Cap 後仍然不能作為名稱的問題
-[PIO] PIO 0.4gamma (與 Pixmicat!2 共用相同的函式庫)
-[FileIO] FileIO 更新 (與 Pixmicat!2 共用相同的函式庫)
 
2007/03/12
01.[pixmicat] 修正翻譯索引錯誤 useage => usage
 
2007/02/24
01.[pixmicat][lib_common] 修正部份變數未定義錯誤
 
2007/02/21
01.[PIO] 修正 SQLite3 isThread() 總是回傳 true 的錯誤
 
2007/02/14
01.[PIO] PIO 0.4 beta,修改 fetchThreadList 方法
 
2007/02/10, 11
01.[PIO] 有關時區部份改以 UTC 時間手動位移避免混亂
 
2007/02/07
01.[PTE] 預設樣板更新 (inc_pixmicat.tpl)
 
2007/02/01,02
01.[lib_common] str_cut() 補上來源
02.[upgradePIO] mysql2pio.php 修正,新增欄位次序修正 (舊版無影響)
03.[PIO] PIO 0.4 alpha,新增 dbImport / dbExport 方法
 
2007/01/31
01.[lib_common][config] 刪除 RE_COL,改以 CSS 樣式表設定
 
2007/01/30
01.[FileIO] ImageShack 後端因應 mod_archiver 修改 getImageURL 方法
02.[PMS] 新增 PMS::getModulePageURL(),可讓模組取得獨立頁面網址
03.[PIO] Log postCount() 修正為回傳本篇加回應的數目,非先前僅有回應數
04.[pixmicat] 修正 MAX_RES 不推文判斷,回應超過這個數字才不推 (先前是等於就不推了)
 
2007/01/28,29
01.[PMS] ThreadPost, ThreadReply 掛載點新增是否位於回應模式的判斷參數
02.[PMS][lib_common] 新增 PostInfo 掛載點
 
2007/01/27
01.[PIO] SQLite3 類別標籤搜尋 SQL 語法更正
02.[pixmicat][PMS] 新增 Foot / ThreadPost, ThreadReply 掛載點,可搭配 PTE 動態新增插入標籤
 
2007/01/26
01.[PMS] 增設 Toplink Hook Point, 並完成 mod_catalog
02.[config] 更改模組載入形式,較可以自由切換載入模組 (註解起來即可)
 
2007/01/25
01.[PMS][config] 試驗性模組系統雛型架上,並完成 mod_rss 改寫
02.[pixmicat][lib_common] fullURL(), anti_sakura() 移至 lib_common.php
 
2007/01/24
01.[lib_common] 廢除 PROXY_CHECK 檢查功能
02.[config][pixmicat] 強化 IP 封鎖為綜合性 IP/Hostname/DNSBL 封鎖機能,支援 RegExp/Wildcard 模式
 
2007/01/05:3rd.Release (v070106)
01.[lib_common] 減少 str_cut() 的 ord() 呼叫次數
02.[lib_common] CheckSupportGZip() 判斷式修改
03.[FileIO] 修改 ImageShack 後端當上傳圖檔小於 200x200 時無法顯示縮圖的問題
 
2006/12/28
01.[pixmicat] 新增回應模式分頁 [ALL] 全部顯示功能 (顯示條件:分頁數超過一頁)
02.[lib_common] 修正 2006/10/27 01. 項失效的問題 (Cookie 無法正確填入欄位)
03.[PIO] SQL 讀寫新增 _error_handler() 錯誤攔截機制
 
2006/12/23
01.[FileIO] 修改normal模式使用getImageURL()回傳相對位置造成mod_rss生成RSS不合語法的問題
02.[FileIO] 減少getImageLocalURL()每次都要取目前伺服器絕對位置造成的CPU浪費
 
2006/12/22
01.[lib_common][pixmicat] Publicβ 開始
02.[config] 修改部分預設設定
 
2006/12/19
01.[PIO] 修正 PIO PgSQL, SQLite 儲存密碼欄位時變數名稱打錯的問題
 
2006/12/16
01.[config] 新增 PIO 及 FileIO 參考設定,移除 *_URL_PREFIX 設定 (目前取消)
02.[PIO] 新增 searchCategory() 輸出順序依編號由大排到小
 
2006/12/12
01.[pixmicat] 修改 Category 印出時使用 urlencode 以確保各瀏覽器相容性
02.[FileIO] 修改 FileIO IndexFS 使其在需要時才讀取索引以節省 CPU
 
2006/12/11
01.[PIOlog] 修正當 LUT 快取不見時無法重新生成造成版面無法復原問題
02.[PTE] 預設樣版新增類別標籤欄位 (供修改製作參考)
 
2006/12/10
01.[PTE][lib_common] 改回原本樣式,原獨立樣式表方式廢棄 (見2006/09/22,23)
02.[pixmicat] 新增搜尋類別標籤的重新快取功能,以免 Session 快取過久無法顯示最新文章
 
2006/12/09
01.[FileIO] Satellite PHP & Perl CGI 完成
02.[FileIO] FileIO Index File System 獨立,FileIO 完成
 
2006/12/07
01.[FileIO] FileIO Satellite 後端完成,衛星 PHP 亦完成
02.[FileIO] FileIO ImageShack 大圖連結 URL 更改,預設連至顯示頁面
 
2006/12/06
01.[FileIO] FileIO ImageShack 實裝註冊金鑰認證、要求刪圖功能及取消預覽圖資訊列
 
2006/12/05
01.[FileIO] FileIO 改用建立物件式,為此所有操作到 FileIO 敘述全部更改
02.[FileIO] FileIO FTP 後端重寫完成
03.[config] FileIO 參數規格修改,改以傳入陣列方式供後端自行分析使用
04.字詞修正 CATALOG -> CATEGORY
05.[FileIO] FileIO ImageShack 後端完成
 
2006/12/04
01.[FileIO] FileIO 0.2 版,並完成 Normal 後端
02.[PIO][pixmicat] PIO, pixmicat.php 與 FileIO 相關函式修改
 
2006/12/03
01.[pixmicat] 修正顯示附加圖檔檔案大小時多一個B變成KBB的問題
02.[pixmicat] 類別標籤分頁功能
03.[PIO] PIO 0.3 更新
 
2006/11/13
01.[pixmicat] 修正E-mail填入 ◆XXX 的Trip後會造成版面亂掉的問題
02.[pixmicat][config] 新增強制砍名功能 (仿二次元壁紙@ふたば、攤開Customized)
 
2006/10/27
01.[mainscript][lib_common] 修正Firefox瀏覽器自動填入Cookies儲存密碼時底端欄位漏填的問題
02.[pixmicat] 用語更改:防止Spam對策機制啟動! -> 防止 Spambot 機制啟動!
03.[PIO] searchCatalog 完成
 
2006/10/23
01.[mainscript] 更新方法,並重新以JSLint, JSMin檢測壓縮
02.[lib_common][config][pixmicat] 實裝Spambot欄位陷阱
 
2006/09/22,23
01.[pixmicat] searchCatalog()初步完成,分頁功能暫時未加入 (目前採Session儲存,有可能改變)
02.[pixmicat][lib_common] 無貼圖勾選框加回來以防止Spam攻擊
03.[PTE][mainstyle] 將StyleSheet部分獨立出新樣板區塊,將mainstyle的討論串樣式搬過來
04.[PIOlog][PIOmysql] 完成searchCatalog(),SQL錯誤訊息預設不顯示避免被利用
 
2006/09/20
01.[pixmicat] 搜尋部分(searchCatalog)實作中
 
2006/09/15
01.[pixmicat] 新增類別標籤功能,目前可以儲存標籤、印出標籤,搜尋部分尚未完成
 
2006/08/26
01.[PIOlog] 修正索引鍵錯亂造成Log丟失問題及刪舊文不刪其圖問題
02.[PIOlog] 取消array_search改用array_flip + array_key_exists
03.[PIOlog] 修正porder[0]刪掉後造成編號歸零計算
04.[PIOlog] 修正torder混亂的問題
 
2006/08/25
01.[PIO] 廢除PIO Class Wrapper,以PIO Kernel Switcher代替
02.[PIOlog] 修改處理結構,增加LUT Cache (目前尚有Bug, 徵求除錯)
03.[pixmicat] 不具合小修正
04.[PIOlog] 修正資料筆數必須要到LOG_MAX+1才會開始刪除舊文的問題
 
2006/08/24
01.[PIO] 各資料來源的資料結構更新完成
02.[PIOlog] 修正在管理模式時若改變文章狀態會在dbCommit()刪除resto鍵值造成後續的輸出發生錯誤
03.[pixmicat] 修正發文後檢查是否討論串被設為禁止回應的tim鍵值使用錯誤的問題
 
2006/08/23
01.[PIO] 修改資料結構,增加catalog和imgw, imgh, imgsize等欄位
02.[pixmicat] 去除extract POST, GET以增強安全性和效率,需要時再解析取得
03.[pixmicat] 去除對URL「?mode=search」POST的不符標準
 
2006/08/11
01.[pixmicat] PIO版本決議將繼續開發直到成熟為止
02.[PIO] PIOlog, mysql, sqlite, pgsql物件化
03.[pixmicat] 加回Refresh標籤,發文完成即使關閉JavaScript也能跳轉頁面
04.[pixmicat] 配合PIO物件化修改,連續投稿 / 相同附加圖檔檢查給PIO處理
05.[PIO] 新增checkSuccessivePost及checkDuplicateAttechment函式
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v7/mainscript_original.js
@@ -0,0 +1,172 @@
// ==ClosureCompiler==
// @compilation_level SIMPLE_OPTIMIZATIONS
// @output_file_name mainscript.js
// ==/ClosureCompiler==
/*jslint browser: true, devel: true, undef: true, eqeqeq: true, regexp: true, newcap: true, immed: true */
/*global window, msgs, ext */
 
var previous_replyhlno = 0;
var arrPresetFunc = [];
var arrSakuraTbl = [[63223, 12353, 82], [63306, 12449, 85], [63486, 12535, 4]]; // Big5-Sakura to Unicode Table
var arrSakuraTblsp = [[63216, 63219, 63210, 63211, 63212, 63213], [12293, 12540, 12541, 12542, 12445, 14446]]; // Special Characters
 
/* getElementById shortcut */
function $g(i){ return document.getElementById(i); }
 
/* 讀取Cookies值 */
function getCookie(key){
var tmp1, tmp2, xx1 = 0, xx2 = 0, xx3;
tmp1 = ' '+document.cookie+';';
var len = tmp1.length;
while(xx1 < len){
xx2 = tmp1.indexOf(';', xx1);
tmp2 = tmp1.substring(xx1 + 1, xx2);
xx3 = tmp2.indexOf('=');
if(tmp2.substring(0, xx3)===key){ return window.unescape(tmp2.substring(xx3 + 1, xx2 - xx1 - 1)); }
xx1 = xx2 + 1;
}
return '';
}
 
/* 寫入Cookies值 */
function setCookie(name, value){
var exp = new Date();
exp.setTime(exp.getTime() + 86400000 * 7);
document.cookie = name+'='+window.escape(value)+'; expires='+exp.toGMTString();
}
 
/* 將Unicode使用者造字區字集之日文對應至Unicode日文假名區 */
function replace_sakura(tar_obj){
var temp = tar_obj.value, i = 0, p = 0;
var Tblcount = arrSakuraTbl.length, Tbl;
for(i = 0; i < Tblcount; i++){ // 處理假名部分
Tbl = arrSakuraTbl[i];
for(p = 0; p <= Tbl[2]; p++){ temp = temp.replace(new RegExp(String.fromCharCode(Tbl[0] + p), 'g'), String.fromCharCode(Tbl[1] + p)); }
}
Tblcount = arrSakuraTblsp[0].legnth;
for(i = 0; i < Tblcount; i++){ // 處理符號部分
Tbl = arrSakuraTblsp;
temp = temp.replace(new RegExp(String.fromCharCode(Tbl[0][i]), 'g'), String.fromCharCode(Tbl[1][i]));
}
tar_obj.value = temp;
}
 
/* 檢查使用者是否輸入了Unicode使用者造字區字集 (多為櫻花日文假名) */
function check_sakura(field){
var tar_obj = $g(field);
var checktext = window.escape(tar_obj.value).toLowerCase(); // %uxxxx形式 (全部小寫)
var regular_exp = /%uf(6[ef]|7[0-9a-f]|80)[0-9a-f]/; // U+F6Ex~F80x為櫻花日文的概略位置 (比對時用小寫)
if(checktext.match(regular_exp)!==null){
alert(msgs[2]);
replace_sakura(tar_obj); // 代轉
}
}
 
/* 取出Cookies的值並填入表單 */
function l1(){
var N = getCookie('namec'), E = getCookie('emailc'), obj;
if((obj = $g('fname'))){ obj.value = N; }
if((obj = $g('femail'))){ obj.value = E; }
}
 
/* 填入表單密碼 */
function l2(){
var P = getCookie('pwdc'), d = document, forms_length = d.forms.length;
for(var i = 0; i < forms_length; i++){
if(d.forms[i].pwd){ d.forms[i].pwd.value = P; }
}
}
 
/* 前端檢查表單機制 */
function c(){
var upfilevalue, j, ext_allowed, ext_length;
try{
if(!$g('fupfile')){ return true; }
upfilevalue = $g('fupfile').value;
if(!upfilevalue && !$g('fcom').value){ alert(msgs[0]); return false; }
if(upfilevalue){
ext_allowed = 0; ext_length = ext.length;
for(j = 0; j < ext_length; j++){
if(upfilevalue.substr(upfilevalue.length - 3, 3).toUpperCase()===ext[j]){
ext_allowed = 1;
break;
}
}
if(!ext_allowed){ alert(msgs[1]); return false; }
}
check_sakura('fcom'); check_sakura('fname'); check_sakura('fsub'); // 檢查櫻花日文
if(window.clipboardData){ document.forms[0].upfile_path.value = upfilevalue; } // IE的Senddata為完整路徑名稱
document.forms[0].sendbtn.disabled = true;
}catch(e){ }
if($g('fname').value){ setCookie('namec', $g('fname').value); } // Cookies寫入名稱
}
 
/* 動態改變超連結的視窗目標 */
function fixalllinks(){
if(!document.getElementsByTagName){ return; }
var anchor, anchors = document.getElementsByTagName('a');
var anchors_length = anchors.length;
for(var i = 0; i < anchors_length; i++){
anchor = anchors[i];
if(anchor.getAttribute('href')){
if(anchor.getAttribute('rel') === '_top'){ anchor.target = '_top'; }
if(anchor.getAttribute('rel') === '_blank'){ anchor.target = '_blank'; }
}
}
}
 
/* 顯示發文表單 */
function showform(){
$g("postform").className = '';
$g("postform_tbl").className = '';
$g("hide").className = 'show';
$g("show").className = 'hide';
}
 
/* 隱藏發文表單 */
function hideform(){
$g("postform").className = 'hide_btn';
$g("postform_tbl").className = 'hide';
$g("hide").className = 'hide';
$g("show").className = 'show';
}
 
/* 內文引用編號 */
function quote(text){
try{
$g('fcom').focus();
$g('fcom').value += '>>No.' + text + "\r\n";
}catch(e){ }
}
 
/* 回應背景標亮 / 取消 */
function replyhl(id, isRecover){
var rpydiv = $g('r'+id);
if(rpydiv){
if(isRecover){
rpydiv.className = rpydiv.className.replace(' reply_hl', '');
}else{
if(previous_replyhlno){ replyhl(previous_replyhlno, true); }
previous_replyhlno = id;
rpydiv.className += ' reply_hl';
}
}
}
 
/* 掛載當執行 preset() 後跟著執行的函式 */
function hookPresetFunction(func){
if(typeof func === 'function'){ arrPresetFunc.push(func); }
}
 
/* 載入後執行的函式 */
function preset(){
var i, l = arrPresetFunc.length, f;
 
fixalllinks(); // 修正連結目標
for(i = 0; i < l; i++){ f = arrPresetFunc[i]; if(typeof f==='function'){ f(); } }
var url = location.href;
if(url.indexOf('?res=')){
if(url.match(/#[rq]([0-9]+)$/)){ replyhl(RegExp.$1, false); } // 回應標亮
if(url.match(/#q([0-9]+)$/)){ quote(RegExp.$1); } // 回應引用
}
}
New file
/release/PIO-v7/TestCase/runAllTestCoverage.bat
@@ -0,0 +1,3 @@
@ECHO OFF
runAllTest --coverage-html ./report
pause
New file
/release/PIO-v7/TestCase/FlagHelperTest.php
@@ -0,0 +1,111 @@
<?php
require_once dirname(__FILE__).'/prerequire.php';
 
class FlagHelperTest extends PHPUnit_Framework_TestCase {
public static function setUpBeforeClass() {
// Let the system load lib_pio (FlagHelper)
PMCLibrary::getPIOInstance();
}
 
public function testInstance() {
$fg = new FlagHelper('');
$this->assertNotNull($fg);
}
 
public function testToString() {
$fg = new FlagHelper('TEST');
$this->assertEquals('TEST', $fg->toString());
}
 
public function testGet() {
$fg = new FlagHelper('_flag1:1024__flag2:0_');
$this->assertEquals('flag1:1024', $fg->get('flag1'));
}
 
public function testExists() {
$fg = new FlagHelper('_flag1:1024__flag2:0_');
$this->assertTrue($fg->exists('flag1'));
$this->assertFalse($fg->exists('flag8'));
}
 
public function testValue() {
$fg = new FlagHelper('_flag1:1024__flag2:0_');
$this->assertEquals('1024', $fg->value('flag1'));
$this->assertFalse($fg->value('flag8'));
}
 
public function testAdd() {
$fg = new FlagHelper('_flag1:1024__flag2:0_');
$fg->add('flag3', 'Test');
$this->assertTrue($fg->exists('flag3'));
$this->assertEquals('Test', $fg->value('flag3'));
$this->assertEquals('_flag1:1024__flag2:0__flag3:Test_', $fg->toString());
}
 
public function testUpdate() {
$fg = new FlagHelper('_flag1:1024__flag2:0_');
$fg->update('flag2', '86400');
$this->assertEquals('86400', $fg->value('flag2'));
$fg->update('flag3', array('a', 'b', 'c'));
$this->assertEquals(array('a', 'b', 'c'), $fg->value('flag3'));
$fg->update('flag4');
$this->assertTrue($fg->value('flag4'));
}
 
public function testReplace() {
$fg = new FlagHelper('_flag1:1024__flag2:0_');
$fg->update('flag2', '86400');
$this->assertEquals('86400', $fg->value('flag2'));
}
 
public function testRemove() {
$fg = new FlagHelper('_flag1:1024__flag2:0_');
$fg->remove('flag1');
$this->assertFalse($fg->exists('flag1'));
$this->assertFalse($fg->get('flag1'));
$this->assertFalse($fg->value('flag1'));
}
 
public function testToggle() {
$fg = new FlagHelper('_flag1:1024__flag2:0_');
$fg->toggle('flag3');
$this->assertTrue($fg->value('flag3'));
$fg->toggle('flag3');
$this->assertFalse($fg->value('flag3'));
}
 
public function testOffsetValue() {
$fg = new FlagHelper('_flag1:1024__flag2:0_');
$fg->offsetValue('flag1', -1024);
$this->assertEquals('0', $fg->value('flag1'));
 
$fg->offsetValue('flag1', 65535);
$this->assertEquals('65535', $fg->value('flag1'));
 
$fg->offsetValue('flag1', -131070);
$this->assertEquals('-65535', $fg->value('flag1'));
}
 
public function testPlus() {
$fg = new FlagHelper('_flag1:1024__flag2:0_');
$fg->plus('flag2');
$this->assertEquals('1', $fg->value('flag2'));
}
 
public function testMinus() {
$fg = new FlagHelper('_flag1:1024__flag2:0_');
$fg->minus('flag2');
$this->assertEquals('-1', $fg->value('flag2'));
}
 
public function testJoin() {
$fg = new FlagHelper('_flag1:1024__flag2:0_');
$this->assertEquals('1:2:3', $fg->join(1, 2, 3));
$this->assertEquals('1:a:b:3', $fg->join(1, array('a', 'b'), 3));
}
 
public function testToStringClass() {
$fg = new FlagHelper('_flag1:1024__flag2:0_');
$this->assertEquals('FlagHelper {status = _flag1:1024__flag2:0_}', (string) $fg);
}
}
New file
/release/PIO-v7/TestCase/runAllTest.bat
@@ -0,0 +1,4 @@
@ECHO OFF
set PHPDIR=..\..\..\php
%PHPDIR%\php phpunit.phar --no-globals-backup --verbose %1 %2 %~dp0
pause
New file
/release/PIO-v7/TestCase/LoggerInjectorTest.php
@@ -0,0 +1,61 @@
<?php
require_once dirname(__FILE__).'/prerequire.php';
 
class TempClass {
public function printMessage($msg) {
return "Hello, $msg!";
}
 
public function throwException() {
throw new RuntimeException('Exception thrown.');
}
}
 
class LoggerInjectorTest extends PHPUnit_Framework_TestCase {
private $agent;
 
public function setUp() {
$this->agent = new LoggerInjector(new TempClass(),
new LoggerInterceptor(PMCLibrary::getLoggerInstance('TempClass')));
}
 
public function testInstance() {
$this->assertNotNull($this->agent);
}
 
/**
* @expectedException InvalidArgumentException
*/
public function testInstanceInvaildPrincipal() {
new LoggerInjector(
array(1, 2, 3),
new LoggerInterceptor(PMCLibrary::getLoggerInstance('TempClass'))
);
}
 
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testInstanceInvaildInterceptor() {
new LoggerInjector(new TempClass(), new TempClass());
}
 
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testInstanceInvaildInterceptor2() {
new LoggerInjector(new TempClass(), NULL);
}
 
public function testCall() {
$this->assertEquals('Hello, Mary!', $this->agent->printMessage('Mary'));
}
 
public function testCallNotExists() {
$this->assertNull($this->agent->NonExists());
}
 
public function testCallException() {
$this->assertNull($this->agent->throwException());
}
}
New file
/release/PIO-v7/TestCase/LoggerInterceptorTest.php
@@ -0,0 +1,24 @@
<?php
require_once dirname(__FILE__).'/prerequire.php';
 
class LoggerInterceptorTest extends PHPUnit_Framework_TestCase {
public function testInstance() {
$obj = new LoggerInterceptor(PMCLibrary::getLoggerInstance('Test'));
$this->assertNotNull($obj);
$this->assertInstanceOf('MethodInterceptor', $obj);
}
 
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testInstanceException() {
$obj = new LoggerInterceptor();
}
 
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testInstanceException2() {
$obj = new LoggerInterceptor(NULL);
}
}
New file
/release/PIO-v7/TestCase/LoggerTest.php
@@ -0,0 +1,58 @@
<?php
require_once dirname(__FILE__).'/prerequire.php';
 
class LoggerTest extends PHPUnit_Framework_TestCase {
public $Logger;
 
protected function setUp() {
$this->Logger = PMCLibrary::getLoggerInstance('TestCase');
@rename(ROOTPATH.'error.log', ROOTPATH.'orig-error.log');
}
protected function tearDown() {
@unlink(ROOTPATH.'error.log');
@rename(ROOTPATH.'orig-error.log', ROOTPATH.'error.log');
}
 
public function testLoggerInstance() {
$obj2 = PMCLibrary::getLoggerInstance('TestCase');
$this->assertSame($this->Logger, $obj2);
$this->assertNotNull($this->Logger);
}
 
public function testLoggerInstance2() {
$obj3 = PMCLibrary::getLoggerInstance();
$this->assertNotSame($this->Logger, $obj3);
$obj4 = PMCLibrary::getLoggerInstance('Global');
$this->assertSame($obj3, $obj4);
}
 
public function testError() {
$this->Logger->error('This is a error.');
$content = file_get_contents(ROOTPATH.'error.log');
//var_dump($content);
$this->assertRegExp('/This is a error/', $content);
}
 
public function testInfo() {
$this->Logger->info('This is a info: %d', time());
$content = file_get_contents(ROOTPATH.'error.log');
//var_dump($content);
$this->assertRegExp('/This is a info/', $content);
}
 
public function testDebug() {
$this->Logger->debug('This is a debug message: %s', 'No name given');
$content = file_get_contents(ROOTPATH.'error.log');
//var_dump($content);
$this->assertRegExp('/This is a debug message/', $content);
}
public function testDebugWithArray() {
$this->Logger->debug('This is a debug message2: %s', array(1, 2, 3));
$content = file_get_contents(ROOTPATH.'error.log');
//var_dump($content);
$this->assertRegExp('/This is a debug message2/', $content);
}
}
New file
/release/PIO-v7/TestCase/PMSTest.php
@@ -0,0 +1,41 @@
<?php
require_once dirname(__FILE__).'/prerequire.php';
 
class PMSTest extends PHPUnit_Framework_TestCase {
public $PMS;
 
public function setUp() {
$this->PMS = PMCLibrary::getPMSInstance();
}
 
public function testPMSInstance() {
$obj2 = PMCLibrary::getPMSInstance();
$this->assertSame($this->PMS, $obj2);
 
$this->assertNotNull($this->PMS);
}
 
public function testGetLoadedModules() {
$moduleLists = $this->PMS->getLoadedModules();
//var_dump($moduleLists);
$this->assertNotNull($moduleLists);
}
 
public function testGetModuleInstanceNonExists() {
$this->assertNull($this->PMS->getModuleInstance('mod_nonexists'));
}
 
public function testGetModuleMethodsNonExists() {
$this->assertEmpty($this->PMS->getModuleMethods('mod_nonexists'));
}
 
public function testGetModulePageURL() {
$URL = $this->PMS->getModulePageURL('mod_test');
$this->assertEquals('pixmicat.php?mode=module&amp;load=mod_test', $URL);
}
 
public function testCHP() {
$this->PMS->addCHP('TestCHP', function($name){ echo "Hello World, $name"; });
$this->PMS->callCHP('TestCHP', array('Duke'));
}
}
New file
/release/PIO-v7/TestCase/LanguageTest.php
@@ -0,0 +1,98 @@
<?php
require_once dirname(__FILE__).'/prerequire.php';
require_once ROOTPATH.'lib/lib_compatible.php';
 
class LanguageTest extends PHPUnit_Framework_TestCase {
public $Lang;
 
public function setUp() {
$this->Lang = PMCLibrary::getLanguageInstance();
}
 
public function testGetInstance() {
$expect = $this->Lang;
$result = PMCLibrary::getLanguageInstance();
$this->assertSame($expect, $result);
}
 
public function testGetLocale() {
$expect = 'zh_TW';
$result = $this->Lang->getLocale();
$this->assertSame($expect, $result);
}
 
public function testGetLanguage() {
$expect = 188;
$result = count($this->Lang->getLanguage());
$this->assertSame($expect, $result);
}
 
public function testGetTranslation() {
$expect = '[Notice] Your sending was canceled because of the incorrect file size.';
$result = $this->Lang->getTranslation('regist_upload_killincomp');
$this->assertEquals($expect, $result);
}
 
public function testGetTranslationWithArgs() {
$expect = '被列在 DNSBL(127.0.0.1) 封鎖名單之內';
$result = $this->Lang->getTranslation('ip_dnsbl_banned', '127.0.0.1');
$this->assertEquals($expect, $result);
}
 
public function testGetTranslationNoArg() {
$expect = '';
$result = $this->Lang->getTranslation();
$this->assertEquals($expect, $result);
}
 
public function testGetTranslationIndexNotExists() {
$expect = 'WTF_IS_THIS';
$result = $this->Lang->getTranslation('WTF_IS_THIS');
$this->assertEquals($expect, $result);
}
 
public function test_T() {
$expect = '資料表最佳化';
$result = _T('admin_optimize');
$this->assertEquals($expect, $result);
}
 
public function test_TWithArgs() {
$expect = '【 附加圖檔使用容量總計 : <b>51200</b> KB 】';
$result = _T('admin_totalsize', '51200');
$this->assertEquals($expect, $result);
}
 
public function test_TNoArg() {
$expect = '';
$result = _T();
$this->assertEquals($expect, $result);
}
 
public function test_TIndexNotExists() {
$expect = 'WTF_IS_THIS_ANYWAY';
$result = _T('WTF_IS_THIS_ANYWAY');
$this->assertEquals($expect, $result);
}
 
public function testAttachLanguageOldway() {
AttachLanguage(function(){
global $language;
$language['testIndex'] = 'testValue';
});
 
$expect = 'testValue';
$result = _T('testIndex');
$this->assertEquals($expect, $result);
}
 
public function testAttachLanguageNewway() {
$langArray = array();
$langArray['testIndex2'] = 'testValue2';
PMCLibrary::getLanguageInstance()->attachLanguage($langArray);
 
$expect = 'testValue2';
$result = _T('testIndex2');
$this->assertEquals($expect, $result);
}
}
New file
/release/PIO-v7/TestCase/prerequire.php
@@ -0,0 +1,4 @@
<?php
define('PHP_SELF', 'pixmicat.php');
require dirname(__FILE__).'/../config.php';
require ROOTPATH.'lib/pmclibrary.php';
New file
/release/PIO-v7/TestCase/PMCLibraryTest.php
@@ -0,0 +1,52 @@
<?php
require_once dirname(__FILE__).'/prerequire.php';
 
class PMCLibraryTest extends PHPUnit_Framework_TestCase {
public function testGetPIOInstance() {
$PIO1 = PMCLibrary::getPIOInstance();
$this->assertNotNull($PIO1);
 
$PIO2 = PMCLibrary::getPIOInstance();
$this->assertSame($PIO1, $PIO2);
}
 
public function testGetPTEInstance() {
$PTE1 = PMCLibrary::getPTEInstance();
$this->assertNotNull($PTE1);
 
$PTE2 = PMCLibrary::getPTEInstance();
$this->assertSame($PTE1, $PTE2);
}
 
public function testGetPMSInstance() {
$PMS1 = PMCLibrary::getPMSInstance();
$this->assertNotNull($PMS1);
 
$PMS2 = PMCLibrary::getPMSInstance();
$this->assertSame($PMS1, $PMS2);
}
 
public function testGetFileIOInstance() {
$FileIO1 = PMCLibrary::getFileIOInstance();
$this->assertNotNull($FileIO1);
 
$FileIO2 = PMCLibrary::getFileIOInstance();
$this->assertSame($FileIO1, $FileIO2);
}
 
public function testGetLoggerInstance() {
$Logger1 = PMCLibrary::getLoggerInstance(__CLASS__);
$this->assertNotNull($Logger1);
 
$Logger2 = PMCLibrary::getLoggerInstance(__CLASS__);
$this->assertSame($Logger1, $Logger2);
}
 
public function testGetLanguageInstance() {
$Language1 = PMCLibrary::getLanguageInstance();
$this->assertNotNull($Language1);
 
$Language2 = PMCLibrary::getLanguageInstance();
$this->assertSame($Language1, $Language2);
}
}
New file
/release/PIO-v7/TestCase/ModuleHelperTest.php
@@ -0,0 +1,127 @@
<?php
require_once dirname(__FILE__).'/prerequire.php';
 
class mod_test extends ModuleHelper {
public function __construct($PMS) {
parent::__construct($PMS);
}
 
public function getModuleName() {
return $this->moduleNameBuilder('Test Test');
}
 
public function getModuleVersionInfo() {
return '1.0';
}
 
public function testModulePageURL(array $p = array()) {
return $this->getModulePageURL($p);
}
 
public function testHookFunc(&$txt) {
$txt .= ' hello';
}
 
public function testHook() {
$this->hookModuleMethod('TopLink', array($this, 'testHookFunc'));
}
 
public function testAddCHP() {
$this->addCHP(__CLASS__.'_test', array($this, 'testHookFunc'));
}
 
public function testCallCHP($txt) {
$this->callCHP(__CLASS__.'_test', array(&$txt));
return $txt;