pixmicat

Subversion Repositories:
Compare Path: Rev
With Path: Rev
/release/ @ 848  →  /release/ @ 849
New file
/release/PIO-v5/inc_pixmicat-uploader.tpl
@@ -0,0 +1,187 @@
<!-- Theme Description -->
<!--&THEMENAME-->Pixmicat! Uploader-liked Theme<!--/&THEMENAME-->
<!--&THEMEVER-->v20100113<!--/&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="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="Sat, 1 Jan 2000 00:00:00 GMT" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="Content-Language" content="zh-tw" />
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />
<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 IE ]><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}">
<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>
<!--/&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-v5/inc_pixmicat-festival.tpl
@@ -0,0 +1,197 @@
<!-- Theme Description -->
<!--&THEMENAME-->Pixmicat!-Festival Theme<!--/&THEMENAME-->
<!--&THEMEVER-->v20100113<!--/&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-->1<!--/&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-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="Sat, 1 Jan 2000 00:00:00 GMT" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="Content-Language" content="zh-tw" />
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />
<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 IE ]><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-->
 
 
<!--&CLICKENTER_PROP--> onclick="if (!boxclicked) window.location='{$SELF}?res={$NO}';boxclicked=0;"<!--/&CLICKENTER_PROP-->
<!--&THREAD_CLICKENTER--><div class="grid"<!--&IF(&CLICKENTER,'<!--&CLICKENTER_PROP/-->','')-->><!--/&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}">
<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>
<!--/&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-v5/nothumb.gif
New file
/release/PIO-v5/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-v5/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-v5/pixmicat.php
@@ -0,0 +1,1285 @@
<?php
define("PIXMICAT_VER", 'Pixmicat!-PIO 5th.Release (v100521)'); // 版本資訊文字
/*
Pixmicat! : 圖咪貓貼圖版程式
http://pixmicat.openfoundry.org/
版權所有 © 2005-2010 Pixmicat! Development Team
 
版權聲明:
此程式是基於レッツPHP!<http://php.s3.to/>的gazou.php、
双葉ちゃん<http://www.2chan.net>的futaba.php所改寫之衍生著作程式,屬於自由軟體,
以The Clarified Artistic License作為發佈授權條款。
您可以遵照The Clarified Artistic License來自由使用、散播、修改或製成衍生著作。
更詳細的條款及定義請參考隨附"LICENSE"條款副本。
 
發佈這一程式的目的是希望它有用,但沒有任何擔保,甚至沒有適合特定目的而隱含的擔保。
關於此程式相關的問題請不要詢問レッツPHP!及双葉ちゃん。
 
如果您沒有隨著程式收到一份The Clarified Artistic License副本,
請瀏覽http://pixmicat.openfoundry.org/license/以取得一份。
 
最低運行需求:
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)
說明條目可資參考。
*/
 
include_once('./config.php'); // 引入設定檔
include_once('./lib/lib_language.php'); // 引入語系
include_once('./lib/lib_common.php'); // 引入共通函式檔案
include_once('./lib/lib_fileio.php'); // 引入FileIO
include_once('./lib/lib_pio.php'); // 引入PIO
include_once('./lib/lib_pms.php'); // 引入PMS
include_once('./lib/lib_pte.php'); // 引入PTE外部函式庫
 
$PTE = new PTELibrary(TEMPLATE_FILE); // PTE Library
 
/* 更新記錄檔檔案/輸出討論串 */
function updatelog($resno=0,$page_num=-1,$single_page=false){
global $PIO, $FileIO, $PTE, $PMS, $language, $LIMIT_SENSOR;
 
$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 = './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 = total_size(); // 目前附加圖檔使用量
$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){
global $PIO, $FileIO, $PMS, $language;
 
$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
$thumbURL = $FileIO->getImageURL($tim.'s.jpg'); // thumb URL
 
$imgsrc = '<a href="'.$imageURL.'" rel="_blank"><img src="nothumb.gif" class="img" alt="'.$imgsize.'" title="'.$imgsize.'" /></a>'; // 預設顯示圖樣式 (無預覽圖時)
if($tw && $th){
if($FileIO->imageExists($tim.'s.jpg')){ // 有預覽圖
$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 $PIO, $FileIO, $PMS, $language, $BAD_STRING, $BAD_FILEMD5, $BAD_IPADDR, $LIMIT_SENSOR;
$dest = ''; $mes = ''; $up_incomplete = 0; $is_admin = false;
$path = realpath('.').DIRECTORY_SEPARATOR; // 此目錄的絕對位置
 
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 = $path.$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)) $FileIO->deleteImage($files);
}
}
 
// 附加圖檔容量限制功能啟動:刪除過大檔
if(STORAGE_LIMIT && STORAGE_MAX > 0){
$tmp_total_size = total_size(); // 取得目前附加圖檔使用量
if($tmp_total_size > STORAGE_MAX){
$files = $PIO->delOldAttachments($tmp_total_size, STORAGE_MAX, false);
$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);
total_size(true); // 刪除舊容量快取
if($dest && is_file($dest)){
$destFile = $path.IMG_DIR.$tim.$ext; // 圖檔儲存位置
$thumbFile = $path.THUMB_DIR.$tim.'s.jpg'; // 預覽圖儲存位置
rename($dest, $destFile);
if(USE_THUMB !== 0){ // 生成預覽圖
$thumbType = USE_THUMB; if(USE_THUMB==1){ $thumbType = 'gd'; } // 與舊設定相容
require('./lib/thumb/thumb.'.$thumbType.'.php');
$thObj = new ThumbWrapper($destFile, $imgW, $imgH);
$thObj->setThumbnailConfig($W, $H, THUMB_Q);
$thObj->makeThumbnailtoFile($thumbFile);
@chmod($thumbFile, 0666);
unset($thObj);
}
if($FileIO->uploadImage()){ // 支援上傳圖片至其他伺服器
if(file_exists($destFile)) $FileIO->uploadImage($tim.$ext, $destFile, filesize($destFile));
if(file_exists($thumbFile)) $FileIO->uploadImage($tim.'s.jpg', $thumbFile, filesize($thumbFile));
}
}
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(){
global $PIO, $FileIO, $PMS, $language;
// $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');
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,終止刪除動作
}
}
$PMS->useModuleMethods('Authenticate', array($pwd,'userdel',&$haveperm));
 
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->deleteImage($files);
deleteCache($delposts);
total_size(true); // 刪除容量快取
$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(){
global $PMS, $language;
$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(){
global $PIO, $FileIO, $PMS, $language;
 
$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){
//if(!adminAuthenticate('check')) error(_T('admin_wrongpassword'));
 
$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->deleteImage($files);
deleteCache($delno);
total_size(true); // 刪除容量快取
$is_modified = true;
}
// 討論串停止區塊
if($thsflag){
//if(!adminAuthenticate('check')) error(_T('admin_wrongpassword'));
 
$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);
if($FileIO->imageExists($tim.'s.jpg')) $size += $FileIO->getImageFilesize($tim.'s.jpg');
}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',total_size()).'</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) */
function total_size($isupdate=false){
global $PIO, $FileIO;
 
$size = 0; $all = 0;
$cache_file = "./sizecache.dat"; // 附加圖檔使用容量值快取檔案
 
if($isupdate){ // 刪除舊快取
if(is_file($cache_file)) unlink($cache_file);
return;
}
if(!is_file($cache_file)){ // 無快取,新增
$line = $PIO->fetchPostList(); // 取出所有文章編號
$posts = $PIO->fetchPosts($line,'tim,ext');
$linecount = count($posts);
for($i = 0; $i < $linecount; $i++){
extract($posts[$i]);
// 從記錄檔抽出計算附加圖檔使用量
if($ext && $FileIO->imageExists($tim.$ext)) $all += $FileIO->getImageFilesize($tim.$ext); // 附加圖檔合計計算
if($FileIO->imageExists($tim.'s.jpg')) $all += $FileIO->getImageFilesize($tim.'s.jpg'); // 預覽圖合計計算
}
$sp = fopen($cache_file, 'w');
stream_set_write_buffer($sp, 0);
fwrite($sp, $all); // 寫入目前使用容量值
fclose($sp);
@chmod($cache_file, 0666);
}else{ // 使用快取
$sp = file($cache_file);
$all = $sp[0];
unset($sp);
}
return (int)($all / 1024);
}
 
/* 搜尋(全文檢索)功能 */
function search(){
global $PTE, $PIO, $FileIO, $PMS, $language;
 
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(){
global $PTE, $PIO, $PMS, $FileIO, $language;
$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(){
global $PMS, $language;
$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 $PTE, $PIO, $FileIO, $PMS, $language, $LIMIT_SENSOR;
$countline = $PIO->postCount(); // 計算投稿文字記錄檔目前資料筆數
$counttree = $PIO->threadCount(); // 計算樹狀結構記錄檔目前資料筆數
$tmp_total_size = total_size(); // 附加圖檔使用量總大小
$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('./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_Q).'</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(){
global $PIO, $FileIO, $language;
if(!is_writable(realpath('./'))) 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':
$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-v5/inc_pixmicat.tpl
@@ -0,0 +1,165 @@
<!--&THEMENAME-->futaba Theme<!--/&THEMENAME-->
<!--&THEMEVER-->v20100113<!--/&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="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="Sat, 1 Jan 2000 00:00:00 GMT" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="Content-Language" content="zh-tw" />
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />
<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 IE]><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}">
<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>
<!--/&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-v5/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

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v5/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-v5/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-v5/lib/lib_fileio.php
@@ -0,0 +1,33 @@
<?php
/*
FileIO - Pixmicat! File I/O
FileIO Kernel Switcher
*/
 
// 引入必要函式庫
$fileio_file = './lib/fileio/fileio.'.FILEIO_BACKEND.'.php'; // FileIO Backend
if(is_file($fileio_file)) include_once($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.(substr($imgname, -5)=='s.jpg' ? THUMB_DIR : IMG_DIR).$imgname;
}
}
 
$FileIOEnv = array( // FileIO 環境常數
'IFS.PATH' => './lib/fileio/ifs.php',
'IFS.LOG' => FILEIO_INDEXLOG,
'PATH' => realpath('.').DIRECTORY_SEPARATOR,
'IMG' => IMG_DIR,
'THUMB' => THUMB_DIR
);
 
$FileIO = new FileIOWrapper(unserialize(FILEIO_PARAMETER), $FileIOEnv); // FileIO 物件
?>
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v5/lib/lib_pio.php
@@ -0,0 +1,140 @@
<?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);
}
}
 
// 文章自動刪除機制
include('./lib/lib_pio.cond.php');
class PIOSensor{
public static function check($type, $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, $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($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]);
 
// 引入必要函式庫
$pio_file = './lib/pio/pio.'.PIXMICAT_BACKEND.'.php';
$PIOEnv = array( // PIO 環境常數
'BOARD' => '.',
'LUTCACHE' => './lutcache.dat',
'NONAME' => DEFAULT_NONAME,
'NOTITLE' => DEFAULT_NOTITLE,
'NOCOMMENT' => DEFAULT_NOCOMMENT,
'PERIOD.POST' => RENZOKU,
'PERIOD.IMAGEPOST' => RENZOKU2
);
if(is_file($pio_file)) include_once($pio_file);
 
// PIO Kernel Switcher
$pioSwitch = 'PIO'.PIXMICAT_BACKEND;
$PIO = new $pioSwitch(CONNECTION_STRING, $PIOEnv);
?>
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v5/lib/lib_common.php
@@ -0,0 +1,257 @@
<?php
/**
* Pixmicat! Common Library
*
* 存放常用函式供主程式引入
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
*/
 
/* 輸出表頭 */
function head(&$dat,$resno=0){
global $PTE, $PMS, $language;
$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 $PTE, $PMS, $ADDITION_INFO, $language;
$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){
global $PTE, $PMS, $language;
$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=''){
global $PTE;
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(){
// 同時有 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
return ($tmp[0] != 'unknown' ? $tmp[0] : $_SERVER['REMOTE_ADDR']);
}
return $_SERVER['REMOTE_ADDR'];
}
?>
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v5/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-v5/lib/lib_pio.cond.php
@@ -0,0 +1,75 @@
<?php
/**
* PIO Condition Object
*
* 判斷文章是否符合刪除條件並列出刪除編號
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
*/
 
/* 以總文章篇數作為刪除判斷 */
class ByPostCountCondition{
public static function check($type, $limit){
global $PIO;
return $PIO->postCount() >= $limit * ($type=='predict' ? 0.95 : 1);
}
 
public static function listee($type, $limit){
global $PIO;
return $PIO->fetchPostList(0, intval($limit * ($type=='predict' ? 0.95 : 1)) - 1, $limit);
}
 
public static function info($limit){
global $PIO;
return "ByPostCountCondition: ".($pcnt=$PIO->postCount()).'/'.$limit.sprintf(' (%.2f%%)',($pcnt/$limit*100));
}
}
 
/* 以總討論串數作為刪除判斷 */
class ByThreadCountCondition{
public static function check($type, $limit){
global $PIO;
return $PIO->threadCount() >= ($type=='predict' ? $limit * 0.95 : 1);
}
 
public static function listee($type, $limit){
global $PIO;
return $PIO->fetchThreadList(intval($limit * ($type=='predict' ? 0.95 : 1)), $limit);
}
 
public static function info($limit){
global $PIO;
return "ByThreadCountCondition: ".($tcnt=$PIO->threadCount()).'/'.$limit.sprintf(' (%.2f%%)',($tcnt/$limit*100));
}
}
 
/* 以討論串生存時間作為刪除判斷 */
class ByThreadAliveTimeCondition{
public static function check($type, $limit){
global $PIO;
$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){
global $PIO;
$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 "ByThreadAliveTimeCondition: ".$limit.' day(s)';
}
}
?>
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v5/lib/pms.php
@@ -0,0 +1,116 @@
<?php
/**
* Pixmicat! Module System
*
* 增加掛載點供函式掛上並在需要時依序呼叫以動態改變內容或達成各種效果
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
*/
 
class PMS{
var $ENV;
var $moduleInstance, $moduleLists;
var $hookPoints;
var $loaded;
 
/* 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(); // 存放各模組類別名稱
}
 
// 模組載入相關
/* 載入模組 */
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_once($mpath);
$this->moduleLists[] = $f;
$this->moduleInstance[$f] = new $f();
}
}
}
 
/* 取得載入模組列表 */
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])) return false;
if(!isset($this->hookPoints[$hookPoint]) && $hookPoint != 'ModulePage'){
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);
}
}
?>
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v5/lib/fileio/fileio.satellite.php
@@ -0,0 +1,182 @@
<?php
/**
* FileIO Satellite 衛星計畫後端
*
* 搭配 satellite.php/pl 利用遠端空間管理圖檔
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
*/
 
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 ? true : 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 ? true : 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 ? true : 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 ? true : 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 7.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){
if(!$this->parameter[4] && substr($imgname, -5) == 's.jpg') return file_exists($this->thumbLocalPath.$imgname);
return $this->IFS->beRecord($imgname);
}
 
function deleteImage($imgname){
if(is_array($imgname)){
foreach($imgname as $i){
if(!$this->parameter[4] && substr($i, -5) == 's.jpg'){
@unlink($this->thumbLocalPath.$i);
}else{
if(!$this->_deleteSatellite($i)) return false;
$this->IFS->delRecord($i); // 自索引中刪除
 
}
}
return true;
}
else{
if(!$this->parameter[4] && substr($imgname, -5) == 's.jpg') return @unlink($this->thumbLocalPath.$imgname);
if($result = $this->_deleteSatellite($imgname)) $this->IFS->delRecord($imgname);
return $result;
}
}
 
function uploadImage($imgname='', $imgpath='', $imgsize=0){
if($imgname=='') return true; // 支援上傳方法
if(!$this->parameter[4] && substr($imgname, -5) == 's.jpg') return false; // 不處理預覽圖
$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(!$this->parameter[4] && substr($imgname, -5) == 's.jpg') return @filesize($this->thumbLocalPath.$imgname);
if($rc = $this->IFS->getRecord($imgname)) return $rc['imgSize'];
return false;
}
 
function getImageURL($imgname){
if(!$this->parameter[4] && substr($imgname, -5) == 's.jpg') 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-v5/lib/fileio/fileio.normal.php
@@ -0,0 +1,51 @@
<?php
/**
* FileIO Normal 本機儲存 API
*
* 以本機硬碟空間作為圖檔儲存的方式,並提供一套方法供程式管理圖片
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
*/
 
class FileIO{
var $path, $imgPath, $thumbPath;
 
/* private 藉由檔名分辨圖檔存放位置 */
function _getImagePhysicalPath($imgname){
return (substr($imgname, -5)=='s.jpg' ? $this->thumbPath : $this->imgPath).$imgname;
}
 
function FileIO($parameter='', $ENV){
$this->path = $ENV['PATH'];
$this->imgPath = $this->path.$ENV['IMG'];
$this->thumbPath = $this->path.$ENV['THUMB'];
}
 
function init(){
return true;
}
 
function imageExists($imgname){
return file_exists($this->_getImagePhysicalPath($imgname));
}
 
function deleteImage($imgname){
if(is_array($imgname)){ foreach($imgname as $i){ if(!@unlink($this->_getImagePhysicalPath($i))) return false; } return true; }
else{ return @unlink($this->_getImagePhysicalPath($imgname)); }
}
 
function uploadImage($imgname='', $imgpath='', $imgsize=0){
return false;
}
 
function getImageFilesize($imgname){
return @filesize($this->_getImagePhysicalPath($imgname));
}
 
function getImageURL($imgname){
return $this->getImageLocalURL($imgname);
}
}
?>
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v5/lib/fileio/fileio.ftp.php
@@ -0,0 +1,106 @@
<?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){
if(!$this->parameter[7] && substr($imgname, -5) == 's.jpg') return file_exists($this->thumbLocalPath.$imgname);
return $this->IFS->beRecord($imgname);
}
 
function deleteImage($imgname){
if(!$this->_ftp_login()) return false;
if(is_array($imgname)){
foreach($imgname as $i){
if(!$this->parameter[7] && substr($i, -5) == 's.jpg'){
@unlink($this->thumbLocalPath.$i);
}else{
if(!ftp_delete($this->conn, $i)) return false;
$this->IFS->delRecord($i); // 自索引中刪除
}
}
return true;
}
else{
if(!$this->parameter[7] && substr($imgname, -5) == 's.jpg') return @unlink($this->thumbLocalPath.$imgname);
if($result = ftp_delete($this->conn, $imgname)) $this->IFS->delRecord($imgname);
return $result;
}
}
 
function uploadImage($imgname='', $imgpath='', $imgsize=0){
if($imgname=='') return true; // 支援上傳方法
if(!$this->parameter[7] && substr($imgname, -5) == 's.jpg') return false; // 不處理預覽圖
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(!$this->parameter[7] && substr($imgname, -5) == 's.jpg') return @filesize($this->thumbLocalPath.$imgname);
if($rc = $this->IFS->getRecord($imgname)) return $rc['imgSize'];
return false;
}
 
function getImageURL($imgname){
if(!$this->parameter[7] && substr($imgname, -5) == 's.jpg') 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-v5/lib/fileio/ifs.php
@@ -0,0 +1,117 @@
<?php
/**
* FileIO Index File System
*
* 把遠端圖檔的各種屬性作本機快取及記錄,方便程式取用
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
*/
 
class IndexFS{
var $logfile, $backend, $index, $modified;
 
/* 建構元 */
function IndexFS($logfile){
$this->logfile = $logfile; // 索引記錄檔位置
}
 
/* 初始化 */
function init(){
switch($this->backend){
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('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' => $field[2]);
// 索引格式: 檔名 檔案大小 對應路徑
}
unset($indexlog);
}
}
 
/* 索引是否存在 */
function beRecord($id){
switch($this->backend){
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 getRecord($id){
switch($this->backend){
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 '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 '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);
}
}
}
?>
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v5/lib/fileio/fileio.imageshack.php
@@ -0,0 +1,151 @@
<?php
/**
* FileIO ImageShack 遠端儲存 API
*
* 以 ImageShack 存圖網站作為圖檔儲存的方式,並提供一套方法供程式管理圖片
*
* 使用此功能請遵守 ImageShack 網站的 Terms of Service,並注意以下條約:
* Terms specific to the XML API:
*
* - Website or software must already be developed or have a strategic plan to be developed in the near future.
* - Website or software users must be informed that ImageShack is providing free image hosting.
*
* Free implimentation support is offered to websites that have at least 500 unique visitors per day (users)
* or expect to achieve 500 users in the near future. Otherwise, the XML API is offered as is.
*
* 使用時請自律將 ImageShack 網站連結置於明顯處,並說明正使用其提供之免費圖檔存放功能。
* (http://reg.imageshack.us/content.php?page=linkto 可選擇喜歡方式使用)
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
*/
 
class FileIO{
var $userAgent, $parameter;
var $IFS;
 
/* private 傳檔案到 ImageShack 上面 (發送抓取請求) */
function _transloadImageShack($imgname){
if(!($fp = @fsockopen('www.imageshack.us', 80))) return false;
 
$argument = 'xml=yes&rembar=1&url='.$this->getImageLocalURL($imgname);
$out = "POST /transload.php HTTP/1.1\r\n";
$out .= 'Host: www.imageshack.us'."\r\n";
$out .= 'User-Agent: '.$this->userAgent."\r\n";
$out .= "Content-Type: application/x-www-form-urlencoded\r\n";
if($this->parameter[0]) $out .= 'Cookie: myimages='.$this->parameter[0]."\r\n"; // ImageShack Registration Key Cookie
$out .= 'Content-Length: '.strlen($argument)."\r\n\r\n";
$out .= $argument;
fwrite($fp, $out);
 
$result = '';
while(!feof($fp)){ $result .= fgets($fp, 128); }
fclose($fp);
 
if(strpos($result, '<'.'?xml version="1.0" encoding="iso-8859-1"?>')===false) return false;
else{
$returnValue = array();
$xmlData = explode("\n", $result);
foreach($xmlData as $xmlDatum){
$xmlDatum = trim($xmlDatum);
if($xmlDatum != '' && !eregi('links', $xmlDatum) && !eregi('xml', $xmlDatum)){
$xmlDatum = str_replace('>', '<', $xmlDatum);
$xmlDatum = explode('<', $xmlDatum);
if(count($xmlDatum) >= 3) $returnValue[$xmlDatum[1]] = $xmlDatum[2];
}
}
return $returnValue;
}
}
 
/* private 發出刪除圖片要求 (需填入 Registration Key) */
function _deleteImageShack($imgURL){
if(!$this->parameter[0]) return true; // 沒金鑰無法要求刪除故直接略過
$imgURL = parse_url($imgURL); // 分析 URL 結構準備重組
if(!($fp = @fsockopen($imgURL['host'], 80))) return false;
 
$out = 'GET /delete.php?l='.substr($imgURL['path'], 1).'&c='.$this->parameter[0].'&page=THIS_IS_A_FLAG HTTP/1.1'."\r\n";
$out .= 'Host: '.$imgURL['host']."\r\n";
$out .= 'User-Agent: '.$this->userAgent."\r\n\r\n";
fwrite($fp, $out);
 
$result = '';
while(!feof($fp)){ $result .= fgets($fp, 128); }
fclose($fp);
 
return (strpos($result, 'THIS_IS_A_FLAG')!==false ? true : false); // 偷吃步,偵測page是否為設定的特殊值
}
 
/* private 生成 ImageShack my.php 指向頁面位置 */
function _myphpImageShack($imgurl, $ishotlink){
if($ishotlink) return $imgurl; // 直連:直接傳回不需處理
$imgurl = parse_url($imgurl);
return 'http://'.$imgurl['host'].'/my.php?image='.basename($imgurl['path']);
}
 
/* 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 7.0; Windows NT 5.1)'; // Just for fun ;-)
$this->parameter = $parameter; // 將參數重新解析
/*
[0] : ImageShack 註冊金鑰 (即登入頁面 setlogin.php 網址後面附帶一串編碼) * 可不填,但功能會少
登入後亦可在 http://reg.imageshack.us/content.php?page=register 找到 Your Registration Code
*/
}
 
function init(){
return true;
}
 
function imageExists($imgname){
return $this->IFS->beRecord($imgname);
}
 
function deleteImage($imgname){
if(is_array($imgname)){
foreach($imgname as $i){
if(($rc = $this->IFS->getRecord($i)) && $this->_deleteImageShack($rc['imgURL'])) $this->IFS->delRecord($i); // 自索引中刪除
else return false; // 送出刪除要求失敗
}
return true;
}
else{
if(($rc = $this->IFS->getRecord($imgname)) && $this->_deleteImageShack($rc['imgURL'])){ $this->IFS->delRecord($imgname); return true; }
return false;
}
}
 
function uploadImage($imgname='', $imgpath='', $imgsize=0){
if($imgname=='') return true; // 支援上傳方法
if(substr($imgname, -5)=='s.jpg'){ unlink($imgpath); return true; } // 預覽圖不用上傳,直接刪除
$result = $this->_transloadImageShack($imgname);
if($result){
$this->IFS->addRecord($imgname, $imgsize, $result['image_link']); // 加入索引之中
list($w, $h) = explode('x', $result['resolution']); // 原圖解析度
// 判斷是否自動生成預覽圖 (長寬任一大於200像素) 並儲存預覽圖位置
$this->IFS->addRecord(substr($imgname, 0, 13).'s.jpg', ceil($imgsize / 4), (($w > 200 || $h > 200) ? $result['thumb_link'] : $result['image_link']));
unlink($imgpath); // 確實上傳後刪除本機暫存
}
return $result;
}
 
function getImageFilesize($imgname){
if($rc = $this->IFS->getRecord($imgname)) return $rc['imgSize'];
return false;
}
 
function getImageURL($imgname, $ishotlink = false){ // 是否使用熱連結直連圖檔位置 (極有可能被 Ban 網域!請慎用)
return ($rc = $this->IFS->getRecord($imgname)) ? (substr($imgname, -5)=='s.jpg' ? $rc['imgURL'] : $this->_myphpImageShack($rc['imgURL'], $ishotlink)) : false;
}
}
?>
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v5/lib/pio/pio.logflockp.php
@@ -0,0 +1,568 @@
<?php
/**
* PIO Log API (flock+)
*
* 提供存取以 Log 檔案構成的資料結構後端的物件
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
*/
 
class PIOlogflockp{
var $ENV, $logfile, $treefile, $porderfile; // Local Constant
var $logs, $trees, $LUT, $porder, $torder, $prepared; // Local Global
//var $memcached, $mid;
 
/* private 設定 memcached 資料
function _memcacheSet($isAnalysis=true){
if(!$this->_memcachedEstablish()) return false;
$this->memcached->set('pmc'.$this->mid.'_isset', true);
// 是否需要將每行資料分析為陣列
$this->memcached->set('pmc'.$this->mid.'_logs', ($isAnalysis ? array_map(array($this, '_AnalysisLogs'), $this->logs) : $this->logs));
$this->memcached->set('pmc'.$this->mid.'_trees', $this->trees);
$this->memcached->set('pmc'.$this->mid.'_LUT', $this->LUT);
$this->memcached->set('pmc'.$this->mid.'_porder', $this->porder);
$this->memcached->set('pmc'.$this->mid.'_torder', $this->torder);
}
 
/* private 取得 memcached 資料
function _memcacheGet(){
if(!$this->_memcachedEstablish()) return false;
if($this->memcached->get('pmc'.$this->mid.'_isset')){ // 有資料
$this->logs = $this->memcached->get('pmc'.$this->mid.'_logs');
$this->trees = $this->memcached->get('pmc'.$this->mid.'_trees');
$this->LUT = $this->memcached->get('pmc'.$this->mid.'_LUT');
$this->porder = $this->memcached->get('pmc'.$this->mid.'_porder');
$this->torder = $this->memcached->get('pmc'.$this->mid.'_torder');
return true;
}else return false;
}
 
/* private 建立 memcached 實體
function _memcachedEstablish(){
if(!extension_loaded('memcache')) return ($this->memcached = false);
if(is_null($this->memcached)){
$this->memcached = new Memcache;
if(!$this->memcached->pconnect('localhost')) return ($this->memcached = false);
return true;
}
return ($this->memcached===false) ? false : true;
}*/
 
function PIOlogflockp($connstr='', $ENV){
$this->ENV = $ENV;
$this->logs = $this->trees = $this->LUT = $this->porder = $this->torder = array();
$this->prepared = 0;
//$this->mid = md5($_SERVER['SCRIPT_FILENAME']); // Unique ID
//$this->memcached = false; // memcached object (null: use, false: don't use)
 
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['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();
//if($this->_memcacheGet()){ $this->prepared = 1; return true; } // 如果 memcache 有快取則直接使用
 
$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->_memcacheSet(); // 把目前資料設定到 memcached 內
$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){
global $FileIO;
if(!$this->prepared) $this->dbPrepare();
 
$rpord = $this->porder; sort($rpord); // 由舊排到新 (小->大)
$arr_warn = $arr_kill = array();
foreach($rpord as $post){
$logsarray = $this->_ArrangeArrayStructure($post); // 分析資料為陣列
if($FileIO->imageExists($logsarray[0]['tim'].$logsarray[0]['ext'])){ $total_size -= $FileIO->getImageFilesize($logsarray[0]['tim'].$logsarray[0]['ext']) / 1024; $arr_kill[] = $post; $arr_warn[$post] = 1; } // 標記刪除
if($FileIO->imageExists($logsarray[0]['tim'].'s.jpg')) $total_size -= $FileIO->getImageFilesize($logsarray[0]['tim'].'s.jpg') / 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){
global $FileIO;
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']){
if($FileIO->imageExists($logsarray[$i]['tim'].$logsarray[$i]['ext'])) $files[] = $logsarray[$i]['tim'].$logsarray[$i]['ext'];
if($FileIO->imageExists($logsarray[$i]['tim'].'s.jpg')) $files[] = $logsarray[$i]['tim'].'s.jpg';
}
}
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){
global $FileIO;
 
$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-v5/lib/pio/pio.log.php
@@ -0,0 +1,535 @@
<?php
/**
* PIO Log API
*
* 提供存取以 Log 檔案構成的資料結構後端的物件
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
*/
 
class PIOlog{
var $ENV, $logfile, $treefile, $porderfile; // Local Constant
var $logs, $trees, $LUT, $porder, $torder, $prepared; // Local Global
//var $memcached, $mid;
 
/* private 設定 memcached 資料
function _memcacheSet($isAnalysis=true){
if(!$this->_memcachedEstablish()) return false;
$this->memcached->set('pmc'.$this->mid.'_isset', true);
// 是否需要將每行資料分析為陣列
$this->memcached->set('pmc'.$this->mid.'_logs', ($isAnalysis ? array_map(array($this, '_AnalysisLogs'), $this->logs) : $this->logs));
$this->memcached->set('pmc'.$this->mid.'_trees', $this->trees);
$this->memcached->set('pmc'.$this->mid.'_LUT', $this->LUT);
$this->memcached->set('pmc'.$this->mid.'_porder', $this->porder);
$this->memcached->set('pmc'.$this->mid.'_torder', $this->torder);
}
 
/* private 取得 memcached 資料
function _memcacheGet(){
if(!$this->_memcachedEstablish()) return false;
if($this->memcached->get('pmc'.$this->mid.'_isset')){ // 有資料
$this->logs = $this->memcached->get('pmc'.$this->mid.'_logs');
$this->trees = $this->memcached->get('pmc'.$this->mid.'_trees');
$this->LUT = $this->memcached->get('pmc'.$this->mid.'_LUT');
$this->porder = $this->memcached->get('pmc'.$this->mid.'_porder');
$this->torder = $this->memcached->get('pmc'.$this->mid.'_torder');
return true;
}else return false;
}
 
/* private 建立 memcached 實體
function _memcachedEstablish(){
if(!extension_loaded('memcache')) return ($this->memcached = false);
if(is_null($this->memcached)){
$this->memcached = new Memcache;
if(!$this->memcached->pconnect('localhost')) return ($this->memcached = false);
return true;
}
return ($this->memcached===false) ? false : true;
}*/
 
function PIOlog($connstr='', $ENV){
$this->ENV = $ENV;
$this->logs = $this->trees = $this->LUT = $this->porder = $this->torder = array();
$this->prepared = 0;
//$this->mid = md5($_SERVER['SCRIPT_FILENAME']); // Unique ID
//$this->memcached = false; // memcached object (null: use, false: don't use)
 
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['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();
//if($this->_memcacheGet()){ $this->prepared = 1; return true; } // 如果 memcache 有快取則直接使用
 
$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->_memcacheSet(); // 把目前資料設定到 memcached 內
$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); // 更新快取 (不需要再分析)
 
$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){
global $FileIO;
if(!$this->prepared) $this->dbPrepare();
 
$rpord = $this->porder; sort($rpord); // 由舊排到新 (小->大)
$arr_warn = $arr_kill = array();
foreach($rpord as $post){
$logsarray = $this->_ArrangeArrayStructure($post); // 分析資料為陣列
if($FileIO->imageExists($logsarray[0]['tim'].$logsarray[0]['ext'])){ $total_size -= $FileIO->getImageFilesize($logsarray[0]['tim'].$logsarray[0]['ext']) / 1024; $arr_kill[] = $post; $arr_warn[$post] = 1; } // 標記刪除
if($FileIO->imageExists($logsarray[0]['tim'].'s.jpg')) $total_size -= $FileIO->getImageFilesize($logsarray[0]['tim'].'s.jpg') / 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){
global $FileIO;
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']){
if($FileIO->imageExists($logsarray[$i]['tim'].$logsarray[$i]['ext'])) $files[] = $logsarray[$i]['tim'].$logsarray[$i]['ext'];
if($FileIO->imageExists($logsarray[$i]['tim'].'s.jpg')) $files[] = $logsarray[$i]['tim'].'s.jpg';
}
}
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){
global $FileIO;
 
$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-v5/lib/pio/pio.sqlite.php
@@ -0,0 +1,439 @@
<?php
/**
* PIO SQLite API
*
* 提供存取以 SQLite 資料庫構成的資料結構後端的物件
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
*/
 
class PIOsqlite{
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($errarray, $query=''){
$err = 'Pixmicat! SQL Error: '.$errarray[0].' on line '.$errarray[1];
//error_log($err."\n".sqlite_last_error($this->con).': '.sqlite_error_string(sqlite_last_error($this->con))."\n".$query."\n\n", 3, 'error.log');
trigger_error($err, E_USER_ERROR);
exit();
}
 
/* 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 (v20080920)';
}
 
/* 處理連線字串/連接 */
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($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){
global $FileIO;
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 = $dtim.'s.jpg'; // 預覽檔案名稱
if($FileIO->imageExists($dfile)){ $total_size -= $FileIO->getImageFilesize($dfile) / 1024; $arr_kill[] = $dno; $arr_warn[$dno] = 1; } // 標記刪除
if($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){
global $FileIO;
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 = $dtim.'s.jpg'; // 預覽檔案名稱
if($FileIO->imageExists($dfile)) $files[] = $dfile;
if($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){
global $FileIO;
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){
global $FileIO;
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-v5/lib/pio/pio.sqlite3.php
@@ -0,0 +1,417 @@
<?php
/**
* PIO SQLite3 (PDO) API
*
* 提供存取以 SQLite3 資料庫構成的資料結構後端的物件 (需要 PHP 5.1.0 以上並開啟 PDO 功能)
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
*/
 
class PIOsqlite3{
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 = "Pixmicat! SQL Error: $errtext on line $errline";
//error_log($err."\n".print_r($this->con->errorInfo(),true)."\n", 3, 'error.log');
trigger_error($err, E_USER_ERROR);
exit();
}
 
/* PIO模組版本 */
function pioVersion(){
return '0.6 (v20090701)';
}
 
/* 處理連線字串/連接 */
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($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, $line[5], PDO::PARAM_INT);
$PDOStmt->bindValue(8, $line[6], PDO::PARAM_STR);
$PDOStmt->bindValue(9, floatval($line[7])); // 13-digit BIGINT workground
$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();
return $line;
}
 
/* 刪除舊附件 (輸出附件清單) */
public function delOldAttachments($total_size, $storage_max, $warnOnly=true){
global $FileIO;
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 = $dtim.'s.jpg';
if($FileIO->imageExists($dfile)){ $total_size -= $FileIO->getImageFilesize($dfile) / 1024; $arr_kill[] = $dno; $arr_warn[$dno] = 1; } // 標記刪除
if($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){
global $FileIO;
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 = $dtim.'s.jpg';
if($FileIO->imageExists($dfile)) $files[] = $dfile;
if($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){
global $FileIO;
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){
global $FileIO;
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();
}
 
/* 搜尋類別標籤 */
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-v5/lib/pio/pio.mysql.php
@@ -0,0 +1,476 @@
<?php
/**
* PIO MySQL API
*
* 提供存取以 MySQL 資料庫構成的資料結構後端的物件
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
*/
 
class PIOmysql{
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($errarray, $query=''){
$err = 'Pixmicat! SQL Error: '.$errarray[0].' on line '.$errarray[1];
//error_log($err."\n".mysql_errno().': '.mysql_error()."\n".$query."\n\n", 3, 'error.log');
trigger_error($err, E_USER_ERROR);
exit();
}
 
/* 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 (v20080920)';
}
 
/* 處理連線字串/連接 */
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){ // 資料表不存在
$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($transaction=false){
if($this->prepared) return true;
 
if(@!$this->con = mysql_pconnect($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; 為 ,
$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){
global $FileIO;
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 = $dtim.'s.jpg'; // 預覽檔案名稱
if($FileIO->imageExists($dfile)){ $total_size -= $FileIO->getImageFilesize($dfile) / 1024; $arr_kill[] = $dno; $arr_warn[$dno] = 1; } // 標記刪除
if($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){
global $FileIO;
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 = $dtim.'s.jpg'; // 預覽檔案名稱
if($FileIO->imageExists($dfile)) $files[] = $dfile;
if($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 = '0';
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){
global $FileIO;
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){
global $FileIO;
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-v5/lib/pio/pio.pgsql.php
@@ -0,0 +1,457 @@
<?php
/**
* PIO PostgreSQL API
*
* 提供存取以 PostgreSQL 資料庫構成的資料結構後端的物件
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
*/
 
class PIOpgsql{
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($errarray, $query=''){
$err = 'Pixmicat! SQL Error: '.$errarray[0].' on line '.$errarray[1];
//error_log($err."\n".pg_last_error($this->con)."\n".$query."\n\n", 3, 'error.log');
trigger_error($err, E_USER_ERROR);
exit();
}
 
/* 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 (v20080920)';
}
 
/* 處理連線字串/連接 */
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($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){
global $FileIO;
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 = $dtim.'s.jpg'; // 預覽檔案名稱
if($FileIO->imageExists($dfile)){ $total_size -= $FileIO->getImageFilesize($dfile) / 1024; $arr_kill[] = $dno; $arr_warn[$dno] = 1; } // 標記刪除
if($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){
global $FileIO;
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 = $dtim.'s.jpg'; // 預覽檔案名稱
if($FileIO->imageExists($dfile)) $files[] = $dfile;
if($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){
global $FileIO;
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){
global $FileIO;
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-v5/lib/lib_pms.php
@@ -0,0 +1,19 @@
<?php
/**
* Pixmicat! Module System Dispatcher
*
* 設定環境變數並初始化物件以供使用
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
*/
 
$PMSEnv = array( // PMS 環境常數
'MODULE.PATH' => './module/',
'MODULE.PAGE' => PHP_SELF.'?mode=module&amp;load=',
'MODULE.LOADLIST' => $ModuleList
);
require('./lib/pms.php');
$PMS = new PMS($PMSEnv);
?>
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v5/lib/lib_language.php
@@ -0,0 +1,35 @@
<?php
/*
Pixmicat! Language module loader
*/
$langloaded = false; // Is language file loaded?
 
function _T(/*$arg1, $arg2...$argN*/) {
global $language,$langloaded;
if (!$langloaded){ // language file is not loaded
LoadLanguage(PIXMICAT_LANGUAGE); $langloaded = true;
}
if (!func_num_args()) // called with no arg
return '';
$arg_list = func_get_args();
$arg_list[0] = isset($language[$arg_list[0]]) ? $language[$arg_list[0]] : $arg_list[0];
return call_user_func_array('sprintf',$arg_list);
}
 
function LoadLanguage($locale = 'en_US') {
global $language;
if(!defined('PIXMICAT_LANGUAGE') || defined('PIXMICAT_LANGUAGE_OVERLOADING')) // language overloading
include_once("./lib/lang/en_US.php");
if (file_exists("./lib/lang/$locale.php"))
include_once("./lib/lang/$locale.php");
else
include_once("./lib/lang/en_US.php");
}
function AttachLanguage($fcall){
global $language,$langloaded;
if (!$langloaded){ // language file is not loaded
LoadLanguage(PIXMICAT_LANGUAGE); $langloaded = true;
}
call_user_func($fcall);
}
?>
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v5/lib/thumb/thumb.imagemagick.php
@@ -0,0 +1,53 @@
<?php
/**
* Thumbnail Generate API: ImageMagick Wrapper
*
* 提供程式便於以 ImageMagick 命令列生成預覽圖的物件
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
*/
 
class ThumbWrapper{
var $sourceFile, $sourceWidth, $sourceHeight, $thumbWidth, $thumbHeight, $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, $thumbQuality=50){
$this->thumbWidth = $thumbWidth;
$this->thumbHeight = $thumbHeight;
$this->thumbQuality = $thumbQuality;
}
 
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-v5/lib/thumb/thumb.repng2jpeg.php
@@ -0,0 +1,56 @@
<?php
/**
* Thumbnail Generate API: Imagick Wrapper
*
* 提供程式便於以 repng2jpeg 生成預覽圖的物件
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
*/
 
class ThumbWrapper{
var $sourceFile, $sourceWidth, $sourceHeight, $thumbWidth, $thumbHeight, $thumbQuality;
var $_exec;
 
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' : ''));
}
 
function getClass(){
$str = 'repng2jpeg Wrapper';
if($this->isWorking()){
$str .= ' : '.`$this->_exec --version`;
}
return $str;
}
 
function isWorking(){
return file_exists($this->_exec) && function_exists('exec') && (strtoupper(substr(PHP_OS, 0, 3))==='WIN' || is_executable($this->_exec));
}
 
function setThumbnailConfig($thumbWidth, $thumbHeight, $thumbQuality=50){
$this->thumbWidth = $thumbWidth;
$this->thumbHeight = $thumbHeight;
$this->thumbQuality = $thumbQuality;
}
 
function makeThumbnailtoFile($destFile){
if(!$this->isWorking()) return false;
switch(strtolower(strrchr($this->sourceFile, '.'))){ // 取出副檔名
case '.jpg':
case '.gif':
case '.png':
break; // 僅支援此三種格式
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-v5/lib/thumb/thumb.imagick.php
@@ -0,0 +1,52 @@
<?php
/**
* Thumbnail Generate API: Imagick Wrapper
*
* 提供程式便於以 Imagick (Imagick Image Library) 生成預覽圖的物件
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
*/
 
class ThumbWrapper{
var $sourceFile, $sourceWidth, $sourceHeight, $thumbWidth, $thumbHeight, $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, $thumbQuality=50){
$this->thumbWidth = $thumbWidth;
$this->thumbHeight = $thumbHeight;
$this->thumbQuality = $thumbQuality;
}
 
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-v5/lib/thumb/thumb.magickwand.php
@@ -0,0 +1,53 @@
<?php
/**
* Thumbnail Generate API: MagickWand Wrapper
*
* 提供程式便於以 MagickWand for PHP 生成預覽圖的物件
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
*/
 
class ThumbWrapper{
var $sourceFile, $sourceWidth, $sourceHeight, $thumbWidth, $thumbHeight, $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, $thumbQuality=50){
$this->thumbWidth = $thumbWidth;
$this->thumbHeight = $thumbHeight;
$this->thumbQuality = $thumbQuality;
}
 
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-v5/lib/thumb/thumb.gd.php
@@ -0,0 +1,244 @@
<?php
/**
* Thumbnail Generate API: GD Wrapper
*
* 提供程式便於以 GD Library 生成預覽圖的物件
*
* @package PMCLibrary
* @version $Id$
* @date $Date$
*/
 
class ThumbWrapper{
var $sourceFile, $sourceWidth, $sourceHeight, $thumbWidth, $thumbHeight, $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, $thumbQuality=50){
$this->thumbWidth = $thumbWidth;
$this->thumbHeight = $thumbHeight;
$this->thumbQuality = $thumbQuality;
}
 
function makeThumbnailtoFile($destFile){
if(!$this->isWorking()) return false;
switch(strtolower(strrchr($this->sourceFile, '.'))){ // 取出副檔名
case '.jpg':
$im_in = @ImageCreateFromJPEG($this->sourceFile); break;
case '.gif':
$im_in = @ImageCreateFromGIF($this->sourceFile); break;
case '.png':
$im_in = @ImageCreateFromPNG($this->sourceFile); break;
case '.rle':
case '.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);
ImageJPEG($im_out, $destFile, $this->thumbQuality);
ImageDestroy($im_in); ImageDestroy($im_out);
return true;
}
}
?>
New file
/release/PIO-v5/ChangeLog
@@ -0,0 +1,454 @@
Pixmicat!-PIO 修改紀錄
----------------------
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-v5/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

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v5/Utilities/log2pio.php
@@ -0,0 +1,85 @@
<?php
/**
* Pixmicat! PIO 公用程式 - Pixmicat!-Log -> Pixmciat-PIO (Log) 資料格式轉換器
*
* 本程式可以自 Log 版轉換格式自 PIO 版 Log 資料來源。
*
* 注意:本程式是給 Log 版舊程式使用以轉換,非直接用在 PIO 新版上面
*
* @package PMCUtility
* @version $Id$
* @date $Date$
*/
include_once('./config.php');
define('DEL_ZOMBIE', true); // 如果有文章沒有出現在樹狀結構,是否不要轉換直接刪除?
define('SAVE_LOG', true); // 是否儲存新結構 (舊結構將保留並更名)
 
// 各資料儲存檔位置
$logimg = file(LOGFILE); $logimg_cnt = count($logimg);
$trees = array(); // 文章回應對應編號陣列 (回應No. => 首篇No.)
$logtree = array_map('rtrim', file(TREEFILE));
foreach($logtree as $treeline){ // 解析樹狀結構製成對應索引
if($treeline=='') continue;
$tline = explode(',', $treeline); $tline_cnt = count($tline);
$trees[$tline[0]] = 0;
for($t = 1; $t < $tline_cnt; $t++){ $trees[$tline[$t]] = $tline[0]; }
}
unset($logtree);
 
// 圖檔存放位置
$dirimg = realpath('.').DIRECTORY_SEPARATOR.IMG_DIR;
 
/* 取得圖檔的寬長以存入資料 */
function getImageWH($imgname){
global $dirimg;
 
$imgpath = $dirimg.$imgname;
if(!file_exists($imgpath)) return array(0, 0);
list($width, $height,) = getimagesize($imgpath);
return array($width, $height); // 回傳寬高陣列
}
 
/* 取得圖檔的檔案大小字串 (單位 KB) */
function getImageSizeText($imgname){
global $dirimg;
 
$imgpath = $dirimg.$imgname;
if(!file_exists($imgpath)) return false;
$imgsize = filesize($imgpath);
return ($imgsize >= 1024 ? (int)($imgsize / 1024).' KB' : $imgsize.' B'); // 回傳檔案大小字串
}
 
/* 取得回應的對象編號 */
function getReplyTargetNo($no){
global $trees;
 
if(isset($trees[$no])) return $trees[$no];
return false;
}
 
/* 更改 log 檔結構 */
// OLD: 編號,時間 (ID),名稱,E-Mail,標題,內文,狀態旗標,主機位置,編碼後文章密碼,附加圖檔類型,預覽圖寬,預覽圖長,Unix時間撮記,附加圖檔MD5,
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13
// NEW: 編號,回應目標編號,附加圖檔MD5,類別標籤,Unix時間撮記,附加圖檔類型,圖檔寬,圖檔長,圖檔大小,預覽圖寬,預覽圖長,編碼後文章密碼,時間 (ID),名稱,E-mail,標題,內文,主機位置,狀態旗標,
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
header('Content-Type: text/plain; charset=utf-8');
$newLine = array(); // 新資料格式
if(count(explode(',', $logimg[0])) != 15) die('File structure error. maybe it\'s already a PIO structure.');
for($i = 0; $i < $logimg_cnt; $i++){
$l = explode(',', $logimg[$i]); // 舊資料格式 (用逗號拆開)
$s = getImageWH($l[12].$l[9]); // 圖檔寬長 (寬, 長)
$l[6] = str_replace('_THREADSTOP_', '_TS_', $l[6]); // 討論串停止旗標自 _THREADSTOP_ 改為 _TS_
if(!DEL_ZOMBIE || getReplyTargetNo($l[0]) !== false) $newLine[] = implode(',', array($l[0], getReplyTargetNo($l[0]), $l[13], '', $l[12], $l[9], $s[0], $s[1], getImageSizeText($l[12].$l[9]), (int) $l[10], (int) $l[11], $l[8], $l[1], $l[2], $l[3], $l[4], $l[5], $l[7], $l[6], ''))."\r\n";
}
$writeContent = implode('', $newLine);
 
if(SAVE_LOG){
rename(LOGFILE, LOGFILE.'.old');
$fs = fopen(LOGFILE, 'w');
fwrite($fs, $writeContent);
fclose($fs);
die('File save OK. The old file already renamed.');
}else{
echo $writeContent;
}
?>
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v5/Utilities/piov2tov3.php
@@ -0,0 +1,50 @@
<?php
/**
* Pixmicat! PIO 公用程式 - PIO V2 -> PIO V3 資料格式轉換器
*
* 本程式可以將舊版的 PIO V2 結構轉成新的 PIO V3 結構,
* 主要不同點在於 status 欄位擴充至 VARCHAR(255) 及討論串停止代表旗標參數改變 (T -> _TS_)。
*
* @package PMCUtility
* @version $Id$
* @date $Date$
*/
 
include_once('./config.php');
include_once('./lib/lib_pio.php');
 
$PIO->dbConnect(CONNECTION_STRING);
$PIO->dbPrepare();
 
switch(PIXMICAT_BACKEND){
case 'mysql': // MySQL
/* 修正 status VARCHAR(255), T -> _TS_ */
$PIO->_mysql_call('ALTER TABLE '.$PIO->tablename.' CHANGE status status VARCHAR(255) NOT NULL');
$PIO->_mysql_call('UPDATE '.$PIO->tablename.' SET status = "_TS_" WHERE status = "T"');
break;
case 'pgsql': // PostgresSQL
/* 修正 status VARCHAR(255), T -> _TS_ */
$PIO->_pgsql_call('ALTER TABLE '.$PIO->tablename.' ALTER COLUMN status TYPE VARCHAR(255); UPDATE '.$PIO->tablename.' SET status = "_TS_" WHERE status = "T"');
break;
case 'sqlite':
/* 修正 T -> _TS_ (SQLite VARCHAR 無硬性限制) */
$PIO->_sqlite_call('UPDATE '.$PIO->tablename.' SET status = "_TS_" WHERE status = "T"');
break;
case 'sqlite3':
case 'log':
case 'logflockp':
/* 修正 _THREADSTOP_ -> _TS_ */
$plist = $PIO->fetchThreadList(0, $PIO->threadCount());
$post = $PIO->fetchPosts($plist); // 取出資料
$post_count = count($post);
 
for($i = 0; $i < $post_count; $i++){
$PIO->setPostStatus($post[$i]['no'], str_replace('_THREADSTOP_', '_TS_', $post[$i]['status']));
}
break;
default:
echo('What backend did you use? Sorry we can\'t fix it now.<br />');
}
$PIO->dbCommit();
echo('PIO V3 Update OK.');
?>
New file
/release/PIO-v5/Utilities/import2ifs.php
@@ -0,0 +1,22 @@
<?php
 
include('./config.php');
 
include('./lib/fileio/ifs.php');
 
 
$fio = new IndexFS(FILEIO_INDEXLOG);
 
$fio->openIndex();
clearstatcache();
$dirs = array(IMG_DIR, THUMB_DIR);
foreach ($dirs as $dir) {
if ($handle = opendir($dir)) {
while (false !== ($file = readdir($handle))) {
if ($file == "." || $file == "..") continue;
$fio->addRecord($file,filesize($dir.'/'.$file),'');
}
}
}
$fio->saveIndex();
echo "done";
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v5/Utilities/mysql2pio.php
@@ -0,0 +1,70 @@
<?php
/**
* Pixmicat! PIO 公用程式 - Pixmicat!-MySQL -> Pixmciat-PIO (MySQL) 資料格式轉換器
*
* 本程式可以自 MySQL 版轉換格式自 PIO 版 MySQL 資料來源。
*
* 注意:本程式是給 MySQL 版舊程式使用以轉換,非直接用在 PIO 新版上面
*
* @package PMCUtility
* @version $Id$
* @date $Date$
*/
include_once('./config.php');
 
$dirimg = realpath('.').DIRECTORY_SEPARATOR.IMG_DIR; // 圖檔存放位置
 
/* 取得圖檔的寬長以存入資料 */
function getImageWH($imgname){
global $dirimg;
 
$imgpath = $dirimg.$imgname;
if(!file_exists($imgpath)) return array(0, 0);
list($width, $height,) = getimagesize($imgpath);
return array($width, $height); // 回傳寬高陣列
}
 
/* 取得圖檔的檔案大小字串 (單位 KB) */
function getImageSizeText($imgname){
global $dirimg;
 
$imgpath = $dirimg.$imgname;
if(!file_exists($imgpath)) return false;
$imgsize = filesize($imgpath);
return ($imgsize >= 1024 ? (int)($imgsize / 1024).' KB' : $imgsize.' B'); // 回傳檔案大小字串
}
 
 
if(@!$con=mysql_connect(MYSQL_SERVER, MYSQL_USER, MYSQL_PASSWORD)){
echo 'It occurred a fatal error when connecting to the MySQL server.<p>';
echo 'Check your MySQL login setting in config file or the MySQL server status.';
exit;
}
mysql_select_db(MYSQL_DBNAME, $con);
@mysql_query("SET NAMES 'utf8'"); // MySQL資料以UTF-8模式傳送
 
if(($result = mysql_query("SHOW COLUMNS FROM ".SQLLOG." LIKE 'category'")) && mysql_num_rows($result) == 0){ // 更新資料表結構
mysql_query('ALTER TABLE '.SQLLOG.' ADD category VARCHAR(255) NOT NULL AFTER md5, COMMENT = "For Pixmicat!-PIO [Structure V3]"'); // category
mysql_query('ALTER TABLE '.SQLLOG.' ADD imgw SMALLINT(1) NOT NULL AFTER ext'); // imgw
mysql_query('ALTER TABLE '.SQLLOG.' ADD imgh SMALLINT(1) NOT NULL AFTER imgw'); // imgh
mysql_query('ALTER TABLE '.SQLLOG.' ADD imgsize VARCHAR(10) NOT NULL AFTER imgh'); // imgsize
mysql_query('ALTER TABLE '.SQLLOG.' CHANGE md5 md5chksum VARCHAR(32) NOT NULL'); // md5chksum
mysql_query('ALTER TABLE '.SQLLOG.' CHANGE w tw SMALLINT(1) NOT NULL'); // tw
mysql_query('ALTER TABLE '.SQLLOG.' CHANGE h th SMALLINT(1) NOT NULL'); // th
mysql_query('ALTER TABLE '.SQLLOG.' MODIFY status VARCHAR(255) NOT NULL'); // status
mysql_query('UPDATE '.SQLLOG.' SET status = "_TS_" WHERE status = "T"'); // status 旗標改變
mysql_free_result($result);
 
$tmpSQL = 'SELECT no,tim,ext FROM '.SQLLOG.' WHERE ext <> "" ORDER BY no';
if(!$result2=mysql_query($tmpSQL)) echo "sql失敗814<br>";
while(list($dno, $dtim, $dext)=mysql_fetch_row($result2)){ // 個別跑迴圈
$s = getImageWH($dtim.$dext); // 圖檔寬長
mysql_query('UPDATE '.SQLLOG.' SET imgsize = "'.getImageSizeText($dtim.$dext).'", imgw = '.$s[0].', imgh = '.$s[1].' WHERE no = '.$dno);
}
mysql_free_result($result2);
echo 'done.';
}else{
echo 'It seems already done.';
}
mysql_close($con);
?>
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v5/Utilities/satellite/satellite.php
@@ -0,0 +1,117 @@
<?php
/**
* Pixmicat! PIO 公用程式 - FileIO Satellite PHP
*
* 利用此一放置於外部空間的衛星程式,可以讓 FileIO 利用外部空間存放圖檔。
*
* @package PMCUtility
* @version $Id$
* @date $Date$
*/
 
define('TRANSPORT_KEY', '12345678'); // 傳輸認證金鑰
define('USER_AGENT', 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1) Gecko/20061010 Firefox/2.0'); // Just for fun ;-)
define('STORAGE_DIRECTORY', 'src/'); // 圖檔儲存目錄
$mode = isset($_POST['mode']) ? $_POST['mode'] : ''; // 要求模式
$Tkey = isset($_POST['key']) ? $_POST['key'] : ''; // 對方送來傳輸金鑰
$imgname = isset($_POST['imgname']) ? $_POST['imgname'] : ''; // 圖檔名稱
 
switch($mode){
case 'init': // 初始化
DoConstruct() ? DoOK() : DoError();
break;
case 'transload': // 遠端抓取
DoTransload($imgname) ? DoOK() : DoError();
break;
case 'upload': // 上傳檔案
DoUpload($imgname) ? DoOK() : DoError();
break;
case 'delete': // 刪除檔案
DoDelete($imgname) ? DoOK() : DoError();
break;
default:
DoNotFound();
}
 
/* 初始化 */
function DoConstruct(){
global $Tkey;
if($Tkey != TRANSPORT_KEY) return false; // 金鑰不符
 
if(!is_dir(STORAGE_DIRECTORY)){ mkdir(STORAGE_DIRECTORY); @chmod(STORAGE_DIRECTORY, 0777); }
return true;
}
 
/* 進行遠端抓取檔案並儲存 */
function DoTransload($imgname){
$imgurl = isset($_POST['imgurl']) ? parse_url($_POST['imgurl']) : false; // 圖檔遠端URL位置
if(!is_dir(STORAGE_DIRECTORY)) DoConstruct();
 
if(!($fp = @fsockopen($imgurl['host'], 80))) return false;
 
$out = 'GET '.$imgurl['path']." HTTP/1.1\r\n";
$out .= 'Host: '.$imgurl['host']."\r\n";
$out .= 'User-Agent: '.USER_AGENT."\r\n\r\n";
fwrite($fp, $out);
$result = '';
while(!feof($fp)){ $result .= fgets($fp, 128); }
fclose($fp);
 
$result = explode("\r\n\r\n", $result); // 將檔頭和內容分隔開
if(strpos($result[0], '200 OK')===false) return false; // 檔案不存在或伺服器出現問題
 
$fs = fopen(STORAGE_DIRECTORY.$imgname, "wb"); // 二進位儲存
if(fwrite($fs, $result[1])===false) return false; // 寫入錯誤
chmod(STORAGE_DIRECTORY.$imgname, 0666);
fclose($fs);
 
return true;
}
 
/* 接受上傳檔案並儲存 */
function DoUpload($imgname){
$imgfile = isset($_FILES['imgfile']['tmp_name']) ? $_FILES['imgfile']['tmp_name'] : false;
if(!$imgfile) return false;
if(!is_dir(STORAGE_DIRECTORY)) DoConstruct();
 
$result = move_uploaded_file($imgfile, realpath('.').DIRECTORY_SEPARATOR.STORAGE_DIRECTORY.$imgname); // 搬移上傳檔案
if($result) chmod(STORAGE_DIRECTORY.$imgname, 0666);
 
return $result;
}
 
/* 刪除檔案 */
function DoDelete($imgname){
global $Tkey;
if($Tkey != TRANSPORT_KEY) return false;
 
return @unlink(STORAGE_DIRECTORY.$imgname);
}
 
/* 阻止閒雜人士進入 */
function DoNotFound(){
header('HTTP/1.1 404 Not Found');
echo '<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>404 - Not Found</title>
</head>
<body>
<h1>404 - Not Found</h1>
</body>
 
</html>';
}
 
/* 操作成功,回傳成功訊息 */
function DoOK(){
header('HTTP/1.1 202 Accepted');
}
 
/* 操作失敗,回傳錯誤訊息 */
function DoError(){
header('HTTP/1.1 403 Forbidden');
}
?>
New file
/release/PIO-v5/Utilities/satellite/cgi-lib.pl
@@ -0,0 +1,471 @@
# Perl Routines to Manipulate CGI input
# cgi-lib@pobox.com
# $Id: cgi-lib.pl,v 2.18 1999/02/23 08:16:43 brenner Exp $
#
# Copyright (c) 1993-1999 Steven E. Brenner
# Unpublished work.
# Permission granted to use and modify this library so long as the
# copyright above is maintained, modifications are documented, and
# credit is given for any use of the library.
#
# Thanks are due to many people for reporting bugs and suggestions
 
# For more information, see:
# http://cgi-lib.stanford.edu/cgi-lib/
 
$cgi_lib'version = sprintf("%d.%02d", q$Revision: 2.18 $ =~ /(\d+)\.(\d+)/);
 
 
# Parameters affecting cgi-lib behavior
# User-configurable parameters affecting file upload.
$cgi_lib'maxdata = 131072; # maximum bytes to accept via POST - 2^17
$cgi_lib'writefiles = 0; # directory to which to write files, or
# 0 if files should not be written
$cgi_lib'filepre = "cgi-lib"; # Prefix of file names, in directory above
 
# Do not change the following parameters unless you have special reasons
$cgi_lib'bufsize = 8192; # default buffer size when reading multipart
$cgi_lib'maxbound = 100; # maximum boundary length to be encounterd
$cgi_lib'headerout = 0; # indicates whether the header has been printed
 
 
# ReadParse
# Reads in GET or POST data, converts it to unescaped text, and puts
# key/value pairs in %in, using "\0" to separate multiple selections
 
# Returns >0 if there was input, 0 if there was no input
# undef indicates some failure.
 
# Now that cgi scripts can be put in the normal file space, it is useful
# to combine both the form and the script in one place. If no parameters
# are given (i.e., ReadParse returns FALSE), then a form could be output.
 
# If a reference to a hash is given, then the data will be stored in that
# hash, but the data from $in and @in will become inaccessable.
# If a variable-glob (e.g., *cgi_input) is the first parameter to ReadParse,
# information is stored there, rather than in $in, @in, and %in.
# Second, third, and fourth parameters fill associative arrays analagous to
# %in with data relevant to file uploads.
 
# If no method is given, the script will process both command-line arguments
# of the form: name=value and any text that is in $ENV{'QUERY_STRING'}
# This is intended to aid debugging and may be changed in future releases
 
sub ReadParse {
# Disable warnings as this code deliberately uses local and environment
# variables which are preset to undef (i.e., not explicitly initialized)
local ($perlwarn);
$perlwarn = $^W;
$^W = 0;
 
local (*in) = shift if @_; # CGI input
local (*incfn, # Client's filename (may not be provided)
*inct, # Client's content-type (may not be provided)
*insfn) = @_; # Server's filename (for spooled files)
local ($len, $type, $meth, $errflag, $cmdflag, $got, $name);
binmode(STDIN); # we need these for DOS-based systems
binmode(STDOUT); # and they shouldn't hurt anything else
binmode(STDERR);
# Get several useful env variables
$type = $ENV{'CONTENT_TYPE'};
$len = $ENV{'CONTENT_LENGTH'};
$meth = $ENV{'REQUEST_METHOD'};
if ($len > $cgi_lib'maxdata) { #'
&CgiDie("cgi-lib.pl: Request to receive too much data: $len bytes\n");
}
if (!defined $meth || $meth eq '' || $meth eq 'GET' ||
$meth eq 'HEAD' ||
$type eq 'application/x-www-form-urlencoded') {
local ($key, $val, $i);
# Read in text
if (!defined $meth || $meth eq '') {
$in = $ENV{'QUERY_STRING'};
$cmdflag = 1; # also use command-line options
} elsif($meth eq 'GET' || $meth eq 'HEAD') {
$in = $ENV{'QUERY_STRING'};
} elsif ($meth eq 'POST') {
if (($got = read(STDIN, $in, $len) != $len))
{$errflag="Short Read: wanted $len, got $got\n";};
} else {
&CgiDie("cgi-lib.pl: Unknown request method: $meth\n");
}
 
@in = split(/[&;]/,$in);
push(@in, @ARGV) if $cmdflag; # add command-line parameters
 
foreach $i (0 .. $#in) {
# Convert plus to space
$in[$i] =~ s/\+/ /g;
 
# Split into key and value.
($key, $val) = split(/=/,$in[$i],2); # splits on the first =.
 
# Convert %XX from hex numbers to alphanumeric
$key =~ s/%([A-Fa-f0-9]{2})/pack("c",hex($1))/ge;
$val =~ s/%([A-Fa-f0-9]{2})/pack("c",hex($1))/ge;
 
# Associate key and value
$in{$key} .= "\0" if (defined($in{$key})); # \0 is the multiple separator
$in{$key} .= $val;
}
 
} elsif ($ENV{'CONTENT_TYPE'} =~ m#^multipart/form-data#) {
# for efficiency, compile multipart code only if needed
$errflag = !(eval <<'END_MULTIPART');
 
local ($buf, $boundary, $head, @heads, $cd, $ct, $fname, $ctype, $blen);
local ($bpos, $lpos, $left, $amt, $fn, $ser);
local ($bufsize, $maxbound, $writefiles) =
($cgi_lib'bufsize, $cgi_lib'maxbound, $cgi_lib'writefiles);
 
 
# The following lines exist solely to eliminate spurious warning messages
$buf = '';
 
($boundary) = $type =~ /boundary="([^"]+)"/; #"; # find boundary
($boundary) = $type =~ /boundary=(\S+)/ unless $boundary;
&CgiDie ("Boundary not provided: probably a bug in your server")
unless $boundary;
$boundary = "--" . $boundary;
$blen = length ($boundary);
 
if ($ENV{'REQUEST_METHOD'} ne 'POST') {
&CgiDie("Invalid request method for multipart/form-data: $meth\n");
}
 
if ($writefiles) {
local($me);
stat ($writefiles);
$writefiles = "/tmp" unless -d _ && -w _;
# ($me) = $0 =~ m#([^/]*)$#;
$writefiles .= "/$cgi_lib'filepre";
}
 
# read in the data and split into parts:
# put headers in @in and data in %in
# General algorithm:
# There are two dividers: the border and the '\r\n\r\n' between
# header and body. Iterate between searching for these
# Retain a buffer of size(bufsize+maxbound); the latter part is
# to ensure that dividers don't get lost by wrapping between two bufs
# Look for a divider in the current batch. If not found, then
# save all of bufsize, move the maxbound extra buffer to the front of
# the buffer, and read in a new bufsize bytes. If a divider is found,
# save everything up to the divider. Then empty the buffer of everything
# up to the end of the divider. Refill buffer to bufsize+maxbound
# Note slightly odd organization. Code before BODY: really goes with
# code following HEAD:, but is put first to 'pre-fill' buffers. BODY:
# is placed before HEAD: because we first need to discard any 'preface,'
# which would be analagous to a body without a preceeding head.
 
$left = $len;
PART: # find each part of the multi-part while reading data
while (1) {
die $@ if $errflag;
 
$amt = ($left > $bufsize+$maxbound-length($buf)
? $bufsize+$maxbound-length($buf): $left);
$errflag = (($got = read(STDIN, $buf, $amt, length($buf))) != $amt);
die "Short Read: wanted $amt, got $got\n" if $errflag;
$left -= $amt;
 
$in{$name} .= "\0" if defined $in{$name};
$in{$name} .= $fn if $fn;
 
$name=~/([-\w]+)/; # This allows $insfn{$name} to be untainted
if (defined $1) {
$insfn{$1} .= "\0" if defined $insfn{$1};
$insfn{$1} .= $fn if $fn;
}
BODY:
while (($bpos = index($buf, $boundary)) == -1) {
if ($left == 0 && $buf eq '') {
foreach $value (values %insfn) {
unlink(split("\0",$value));
}
&CgiDie("cgi-lib.pl: reached end of input while seeking boundary " .
"of multipart. Format of CGI input is wrong.\n");
}
die $@ if $errflag;
if ($name) { # if no $name, then it's the prologue -- discard
if ($fn) { print FILE substr($buf, 0, $bufsize); }
else { $in{$name} .= substr($buf, 0, $bufsize); }
}
$buf = substr($buf, $bufsize);
$amt = ($left > $bufsize ? $bufsize : $left); #$maxbound==length($buf);
$errflag = (($got = read(STDIN, $buf, $amt, length($buf))) != $amt);
die "Short Read: wanted $amt, got $got\n" if $errflag;
$left -= $amt;
}
if (defined $name) { # if no $name, then it's the prologue -- discard
if ($fn) { print FILE substr($buf, 0, $bpos-2); }
else { $in {$name} .= substr($buf, 0, $bpos-2); } # kill last \r\n
}
close (FILE);
last PART if substr($buf, $bpos + $blen, 2) eq "--";
substr($buf, 0, $bpos+$blen+2) = '';
$amt = ($left > $bufsize+$maxbound-length($buf)
? $bufsize+$maxbound-length($buf) : $left);
$errflag = (($got = read(STDIN, $buf, $amt, length($buf))) != $amt);
die "Short Read: wanted $amt, got $got\n" if $errflag;
$left -= $amt;
 
 
undef $head; undef $fn;
HEAD:
while (($lpos = index($buf, "\r\n\r\n")) == -1) {
if ($left == 0 && $buf eq '') {
foreach $value (values %insfn) {
unlink(split("\0",$value));
}
&CgiDie("cgi-lib: reached end of input while seeking end of " .
"headers. Format of CGI input is wrong.\n$buf");
}
die $@ if $errflag;
$head .= substr($buf, 0, $bufsize);
$buf = substr($buf, $bufsize);
$amt = ($left > $bufsize ? $bufsize : $left); #$maxbound==length($buf);
$errflag = (($got = read(STDIN, $buf, $amt, length($buf))) != $amt);
die "Short Read: wanted $amt, got $got\n" if $errflag;
$left -= $amt;
}
$head .= substr($buf, 0, $lpos+2);
push (@in, $head);
@heads = split("\r\n", $head);
($cd) = grep (/^\s*Content-Disposition:/i, @heads);
($ct) = grep (/^\s*Content-Type:/i, @heads);
 
($name) = $cd =~ /\bname="([^"]+)"/i; #";
($name) = $cd =~ /\bname=([^\s:;]+)/i unless defined $name;
 
($fname) = $cd =~ /\bfilename="([^"]*)"/i; #"; # filename can be null-str
($fname) = $cd =~ /\bfilename=([^\s:;]+)/i unless defined $fname;
$incfn{$name} .= (defined $in{$name} ? "\0" : "") .
(defined $fname ? $fname : "");
 
($ctype) = $ct =~ /^\s*Content-type:\s*"([^"]+)"/i; #";
($ctype) = $ct =~ /^\s*Content-Type:\s*([^\s:;]+)/i unless defined $ctype;
$inct{$name} .= (defined $in{$name} ? "\0" : "") . $ctype;
 
if ($writefiles && defined $fname) {
$ser++;
$fn = $writefiles . ".$$.$ser";
open (FILE, ">$fn") || &CgiDie("Couldn't open $fn\n");
binmode (FILE); # write files accurately
}
substr($buf, 0, $lpos+4) = '';
undef $fname;
undef $ctype;
}
 
1;
END_MULTIPART
if ($errflag) {
local ($errmsg, $value);
$errmsg = $@ || $errflag;
foreach $value (values %insfn) {
unlink(split("\0",$value));
}
&CgiDie($errmsg);
} else {
# everything's ok.
}
} else {
&CgiDie("cgi-lib.pl: Unknown Content-type: $ENV{'CONTENT_TYPE'}\n");
}
 
# no-ops to avoid warnings
$insfn = $insfn;
$incfn = $incfn;
$inct = $inct;
 
$^W = $perlwarn;
 
return ($errflag ? undef : scalar(@in));
}
 
 
# PrintHeader
# Returns the magic line which tells WWW that we're an HTML document
 
sub PrintHeader {
return "Content-type: text/html\n\n";
}
 
 
# HtmlTop
# Returns the <head> of a document and the beginning of the body
# with the title and a body <h1> header as specified by the parameter
 
sub HtmlTop
{
local ($title) = @_;
 
return <<END_OF_TEXT;
<html>
<head>
<title>$title</title>
</head>
<body>
<h1>$title</h1>
END_OF_TEXT
}
 
 
# HtmlBot
# Returns the </body>, </html> codes for the bottom of every HTML page
 
sub HtmlBot
{
return "</body>\n</html>\n";
}
 
 
# SplitParam
# Splits a multi-valued parameter into a list of the constituent parameters
 
sub SplitParam
{
local ($param) = @_;
local (@params) = split ("\0", $param);
return (wantarray ? @params : $params[0]);
}
 
 
# MethGet
# Return true if this cgi call was using the GET request, false otherwise
 
sub MethGet {
return (defined $ENV{'REQUEST_METHOD'} && $ENV{'REQUEST_METHOD'} eq "GET");
}
 
 
# MethPost
# Return true if this cgi call was using the POST request, false otherwise
 
sub MethPost {
return (defined $ENV{'REQUEST_METHOD'} && $ENV{'REQUEST_METHOD'} eq "POST");
}
 
 
# MyBaseUrl
# Returns the base URL to the script (i.e., no extra path or query string)
sub MyBaseUrl {
local ($ret, $perlwarn);
$perlwarn = $^W; $^W = 0;
$ret = 'http://' . $ENV{'SERVER_NAME'} .
($ENV{'SERVER_PORT'} != 80 ? ":$ENV{'SERVER_PORT'}" : '') .
$ENV{'SCRIPT_NAME'};
$^W = $perlwarn;
return $ret;
}
 
 
# MyFullUrl
# Returns the full URL to the script (i.e., with extra path or query string)
sub MyFullUrl {
local ($ret, $perlwarn);
$perlwarn = $^W; $^W = 0;
$ret = 'http://' . $ENV{'SERVER_NAME'} .
($ENV{'SERVER_PORT'} != 80 ? ":$ENV{'SERVER_PORT'}" : '') .
$ENV{'SCRIPT_NAME'} . $ENV{'PATH_INFO'} .
(length ($ENV{'QUERY_STRING'}) ? "?$ENV{'QUERY_STRING'}" : '');
$^W = $perlwarn;
return $ret;
}
 
 
# MyURL
# Returns the base URL to the script (i.e., no extra path or query string)
# This is obsolete and will be removed in later versions
sub MyURL {
return &MyBaseUrl;
}
 
 
# CgiError
# Prints out an error message which which containes appropriate headers,
# markup, etcetera.
# Parameters:
# If no parameters, gives a generic error message
# Otherwise, the first parameter will be the title and the rest will
# be given as different paragraphs of the body
 
sub CgiError {
local (@msg) = @_;
local ($i,$name);
 
if (!@msg) {
$name = &MyFullUrl;
@msg = ("Error: script $name encountered fatal error\n");
};
 
if (!$cgi_lib'headerout) { #')
print &PrintHeader;
print "<html>\n<head>\n<title>$msg[0]</title>\n</head>\n<body>\n";
}
print "<h1>$msg[0]</h1>\n";
foreach $i (1 .. $#msg) {
print "<p>$msg[$i]</p>\n";
}
 
$cgi_lib'headerout++;
}
 
 
# CgiDie
# Identical to CgiError, but also quits with the passed error message.
 
sub CgiDie {
local (@msg) = @_;
&CgiError (@msg);
die @msg;
}
 
 
# PrintVariables
# Nicely formats variables. Three calling options:
# A non-null associative array - prints the items in that array
# A type-glob - prints the items in the associated assoc array
# nothing - defaults to use %in
# Typical use: &PrintVariables()
 
sub PrintVariables {
local (*in) = @_ if @_ == 1;
local (%in) = @_ if @_ > 1;
local ($out, $key, $output);
 
$output = "\n<dl compact>\n";
foreach $key (sort keys(%in)) {
foreach (split("\0", $in{$key})) {
($out = $_) =~ s/\n/<br>\n/g;
$output .= "<dt><b>$key</b>\n <dd>:<i>$out</i>:<br>\n";
}
}
$output .= "</dl>\n";
 
return $output;
}
 
# PrintEnv
# Nicely formats all environment variables and returns HTML string
sub PrintEnv {
&PrintVariables(*ENV);
}
 
 
# The following lines exist only to avoid warning messages
$cgi_lib'writefiles = $cgi_lib'writefiles;
$cgi_lib'bufsize = $cgi_lib'bufsize ;
$cgi_lib'maxbound = $cgi_lib'maxbound;
$cgi_lib'version = $cgi_lib'version;
$cgi_lib'filepre = $cgi_lib'filepre;
 
1; #return true
 
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v5/Utilities/satellite/satellite.cgi
@@ -0,0 +1,193 @@
#!/usr/bin/perl -w
 
##
# Pixmicat! PIO 公用程式 - FileIO Satellite Perl
#
# 利用此一放置於外部空間的衛星程式,可以讓 FileIO 利用外部空間存放圖檔。
#
# @package PMCUtility
# @version $Id$
# @date $Date$
#
 
require './cgi-lib.pl';
 
$cgi_lib'maxdata = 2097152; # 上傳檔案大小上限
$TRANSPORT_KEY = '12345678'; # 傳輸認證金鑰
$USER_AGENT = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1) Gecko/20061010 Firefox/2.0'; # Just for fun ;-)
$STORAGE_DIRECTORY = 'src/'; # 圖檔儲存目錄
 
&ReadParse;
$EOL = "\015\012";
$BLANK = $EOL x 2;
 
sub parse_url {
local($url) = @_;
 
$ftp_port = 21;
$http_port = 80;
$gopher_port = 70;
$telnet_port = 23;
$wais_port = 210;
$news_port = 119;
 
if ($url =~ m#^(\w+):#) {
$protocol = $1;
$protocol =~ tr/A-Z/a-z/;
} else {
return undef;
}
 
# URL of type: http://host[:port]/path[?search-string]
 
if ($protocol eq "http") {
if ($url =~ m#^\s*\w+://([\w-\.]+):?(\d*)([^ \t]*)$#) {
$server = $1;
$server =~ tr/A-Z/a-z/;
$port = ($2 ne "" ? $2 : $http_port);
$path = ( $3 ? $3 : '/');
return { protocol => $protocol, host => $server, port => $port, path => $path }; # Return by reference
}
return undef;
}
 
}
 
if ($ENV{'REQUEST_METHOD'} eq 'POST') {
$mode = defined($in{'mode'}) ? $in{'mode'} : ''; # 要求模式
$Tkey = defined($in{'key'}) ? $in{'key'} : ''; # 對方送來傳輸金鑰
$imgname = defined($in{'imgname'}) ? $in{'imgname'} : ''; # 圖檔名稱
}
 
if ($mode eq 'init') { # 初始化
DoConstruct() ? DoOK() : DoError();
} elsif ($mode eq 'transload') { # 遠端抓取
DoTransload($imgname) ? DoOK() : DoError();
} elsif ($mode eq 'upload') { # 上傳檔案
DoUpload($imgname) ? DoOK() : DoError();
} elsif ($mode eq 'delete') { # 刪除檔案
DoDelete($imgname) ? DoOK() : DoError();
} else {
DoNotFound();
}
 
### 初始化
sub DoConstruct{
return undef if $Tkey != $TRANSPORT_KEY; # 金鑰不符
 
if(! -d $STORAGE_DIRECTORY){ mkdir($STORAGE_DIRECTORY); chmod($STORAGE_DIRECTORY, 0777); }
return 1;
}
 
### 進行遠端抓取檔案並儲存
sub DoTransload{
use Socket;
my $imgname=$_[0];
$imgurl = defined($in{'imgurl'}) ? parse_url($in{'imgurl'}) : undef; # 圖檔遠端URL位置
if(! -d $STORAGE_DIRECTORY){ DoConstruct(); }
 
my ($remote,$port,$doc) = ($$imgurl{'host'},$$imgurl{'port'},$$imgurl{'path'}); # $$var = dereference
$sockaddr = 'S n a4 x8';
 
if ($port =~ /\D/) { $port = getservbyname($port, 'tcp'); }
return undef unless $port;
$thataddr = gethostbyname($remote);
$that = pack($sockaddr, AF_INET, $port, $thataddr);
$proto = (getprotobyname('tcp'))[2] || 6;
socket(SOCK, PF_INET, SOCK_STREAM, $proto) || return undef;
connect(SOCK, $that) || return undef;
binmode(SOCK);
$ofh = select(SOCK); $| = 1; select($ofh); # flush buffer on every write
 
print SOCK "GET $doc HTTP/1.1".$EOL.
"Host: $remote".$EOL.
"User-Agent: $USER_AGENT".$BLANK;
 
vec($rin='', fileno(SOCK), 1) = 1;
select($rin, undef, undef, 20) || return undef; # no response from server
 
while( <SOCK> ) {
s/\r\n/\n/g;
s/\r/\n/g;
if ( /HTTP([\/\.\d]+)\s+(\d+)\s+(.*)\n/i ) { $status = $2; }
if ( /Content-Length: (\s*)(\d+)\n/i ) { $content_length = $2; }
last if $_ =~ /^$/;
}
 
$content='';
if ($content_length) {
read(SOCK, $content, $content_length);
} else {
while ( <SOCK> ) { $content .= $_; }
}
close(SOCK);
select($ofh);
 
return undef if $status ne "200"; # 檔案不存在或伺服器出現問題
 
open(FS,">$STORAGE_DIRECTORY$imgname") || return undef;
binmode(FS);
print FS $content;
chmod($STORAGE_DIRECTORY.$imgname, 0666);
close(FS);
 
return 1;
}
 
### 接受上傳檔案並儲存
sub DoUpload{
my $imgname=$_[0];
$imgfile = defined($in{'imgfile'}) ? $in{'imgfile'} : undef;
if(!$imgfile){ return undef; }
if(! -d $STORAGE_DIRECTORY){ DoConstruct(); }
 
open(FS,">$STORAGE_DIRECTORY$imgname") || return undef;
binmode(FS);
print FS $imgfile;
chmod($STORAGE_DIRECTORY.$imgname, 0666);
close(FS);
 
return 1;
}
 
### 刪除檔案
sub DoDelete{
my $imgname=$_[0];
return undef if $Tkey != $TRANSPORT_KEY; # 金鑰不符
 
return unlink($STORAGE_DIRECTORY.$imgname);
}
 
### 阻止閒雜人士進入
sub DoNotFound{
print "Status: 404 Not Found".$EOL.
"Content-type: text/html".$BLANK.
"<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n".
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n".
" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n".
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n".
" <head>\n".
" <title>404 - Not Found</title>\n".
" </head>\n".
" <body>\n".
" <h1>404 - Not Found</h1>\n".
" </body>\n".
"</html>";
}
 
### 操作成功,回傳成功訊息
sub DoOK{
print "Status: 202 Accepted".$EOL.
"Content-type: text/plain".$BLANK.
"Succeed.";
 
}
 
### 操作失敗,回傳錯誤訊息
sub DoError{
print "Status: 403 Forbidden".$EOL.
"Content-type: text/plain".$BLANK.
"Failed.";
}
 
__END__
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v5/Utilities/pio2pio.php
@@ -0,0 +1,94 @@
<?php
/**
* Pixmicat! PIO 公用程式 - PIO 匯入匯出轉換器
*
* 這是 PIO 額外提供的功能,可以執行匯出備份、匯入備份、來源轉換等動作。
* 請修改下方 PIO_ANOTHER_CONNSTR PIO 連線字串 (匯入及轉換動作才需要,匯入則直接使用設定檔設定)
*
* @package PMCUtility
* @version $Id$
* @date $Date$
*/
include('./config.php');
define('PIO_ANOTHER_CONNSTR', 'sqlite://pixmicat.db/imglog/'); // Another-PIO 連線字串 (此來源必須無任何資料,全新)
$PIOEnv = array( // PIO 環境常數
'BOARD' => '.',
'LUTCACHE' => './lutcache.dat',
'NONAME' => DEFAULT_NONAME,
'NOTITLE' => DEFAULT_NOTITLE,
'NOCOMMENT' => DEFAULT_NOCOMMENT,
'LOG_MAX' => defined('LOG_MAX') ? LOG_MAX : 0,
'PERIOD.POST' => RENZOKU,
'PERIOD.IMAGEPOST' => RENZOKU2
);
?>
<!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="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="Content-Language" content="zh-tw" />
<title>PIO -> PIO Im/Exporter</title>
</head>
<body>
 
<div id="main">
請選擇下列一項進行操作:
<ul>
<li><a href="?mode=export">匯出目前 PIO 資料成中介檔案</a></li>
<li><a href="?mode=import">匯入目前中介檔案到 Another-PIO (見此頁原始碼設定)</a></li>
<li><a href="?mode=convert">將目前 PIO 資料轉換到 Another-PIO</a></li>
</ul>
<hr />
<div id="result">
<?php
$mode = isset($_GET['mode']) ? $_GET['mode'] : '';
switch($mode){
case 'export': doExport(); break;
case 'import': doImport(); break;
case 'convert': doConvert(); break;
}
 
function doExport(){
global $PIOEnv;
if(preg_match('/^(.*):\/\//i', CONNECTION_STRING, $backend)) define('PIO_FROM', $backend[1]);
include('./lib/pio/pio.'.PIO_FROM.'.php');
$pio1 = 'PIO'.PIO_FROM; $PIO = new $pio1(CONNECTION_STRING, $PIOEnv);
 
$gp = gzopen('piodata.log.gz', 'w9');
gzwrite($gp, $PIO->dbExport());
gzclose($gp);
echo '<a href="piodata.log.gz">下載 piodata.log.gz 中介檔案</a>';
}
 
function doImport(){
global $PIOEnv;
if(preg_match('/^(.*):\/\//i', PIO_ANOTHER_CONNSTR, $backend)) define('PIO_FROM', $backend[1]);
include('./lib/pio/pio.'.PIO_FROM.'.php');
$pio1 = 'PIO'.PIO_FROM; $PIO = new $pio1(PIO_ANOTHER_CONNSTR, $PIOEnv);
 
if(!file_exists('piodata.log.gz')){ echo '檔案不存在,請先放置在相同目錄。'; return; }
$data = '';
$gp = gzopen('piodata.log.gz', 'r');
while(!gzeof($gp)) $data .= gzread($gp, 4096);
gzclose($gp);
echo $PIO->dbImport($data) ? '匯入成功' : '匯入失敗';
}
 
function doConvert(){
global $PIOEnv;
if(preg_match('/^(.*):\/\//i', CONNECTION_STRING, $backend)) define('PIO_FROM', $backend[1]);
if(preg_match('/^(.*):\/\//i', PIO_ANOTHER_CONNSTR, $backend)) define('PIO_TO', $backend[1]);
 
include('./lib/pio/pio.'.PIO_FROM.'.php');
include('./lib/pio/pio.'.PIO_TO.'.php');
 
$pio1 = 'PIO'.PIO_FROM; $pio2 = 'PIO'.PIO_TO;
$PIOa = new $pio1(CONNECTION_STRING, $PIOEnv); $PIOb = new $pio2(PIO_ANOTHER_CONNSTR, $PIOEnv);
echo $PIOb->dbImport($PIOa->dbExport()) ? '轉換成功' : '轉換失敗'; // PIOa -> PIOb
}
?>
</div>
</div>
 
</body>
</html>
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v5/Utilities/checkenv.php
@@ -0,0 +1,232 @@
<?php
/**
* Pixmicat! PIO 公用程式 - 檢查伺服器執行環境支援 (SQLite, PDO, PostgreSQL, MySQL, GD, Imagick, MagickWand, ImageMagick, repng2jpeg)
*
* 本公用程式可為您檢查伺服器支援的項目,讓您選擇最適合的 PIO 資料來源後端和預覽圖生成物件。
* - 伺服器資訊: 得知伺服器的基本資訊,如伺服器版本、PHP 版本等
* - PIO 檢查: 在設定檔的 CONNECTION_STRING 可以使用的後端檢查,除了 Log 外您還可以使用更穩定的 SQL
* - 預覽圖生成檢查: 檢查您的伺服器是否支援各類預覽圖生成,並提供多種支援供您選擇
* - ImageMagick 和 repng2jpeg 支援檢查: 協助您選擇最適用的 repng2jpeg 執行檔和找出 ImageMagick convert 程式路徑
*
* Original source: SUGA <http://sugachan.dip.jp/> @ 2004/10/16
* Custom: SakaQ <http://www.punyu.net/> @ 2004/11/22
* FIXED: scribe <http://scribe.chkkk.idv.tw> @ 2005/07/09
* Rewrite: Pixmicat! Development Team <http://pixmicat.openfoundry.org/>
*
* @package PMCUtility
* @version $Id$
* @date $Date$
*/
 
class CheckEnvironment{
/* 尋找檔案路徑 */
function _findfile($filename, $isWin=false){
if(@is_file("./$filename")){ return "./$filename"; }
$ary = explode(PATH_SEPARATOR, getenv('PATH'));
if(!$isWin){
$ary = array_merge($ary, explode(':',
':/bin'.
':/usr/bin'.
':/usr/ucb'.
':/etc'.
':/lib'.
':/usr/etc'.
':/usr/lib'.
':/usr/local/bin'.
':/usr/local/X11R6/bin'.
':/usr/local/bin/mh'.
':/usr/local/lib'.
':/usr/local/lib/mh'.
':/usr/local/sbin'.
':/usr/local/libexec'.
':/usr/local/canna/bin'.
':'.ini_get('safe_mode_include_dir')));
}
foreach($ary as $value){
if(@is_file($value.DIRECTORY_SEPARATOR.$filename)){ return ($value.DIRECTORY_SEPARATOR.$filename); }
if(@realpath($value.DIRECTORY_SEPARATOR.$filename)){ return ($value.DIRECTORY_SEPARATOR.$filename); } // smbfs workaround, from http://php.net/file-exists#82269
}
return false;
}
 
/* 檢查適用環境之 repng2jpeg 種類 */
function checkRepng2jpegRecommended(){
$os = PHP_OS;
$msg = ''; // HTML 文字流
if(!function_exists('exec')){ return 'This server has disabled the exec() function. So repng2jpeg can\'t be used.'; } // 封鎖 exec 功能
if(stristr($os, 'Linux')){ // 系統為 Linux
$libjpeg = $this->_findfile('libjpeg.so.62'); $libpng = $this->_findfile('libpng.so.2'); $libz = $this->_findfile('libz.so.1');
$libm = $this->_findfile('libm.so.6'); $libc = $this->_findfile('libc.so.6'); $ldlinux = $this->_findfile("ld-linux.so.2");
$msg .= '- libjpeg -> '.($libjpeg ? $libjpeg : 'Not Found')."\n";
$msg .= '- libpng -> '.($libpng ? $libpng : 'Not Found')."\n";
$msg .= '- libz -> '.($libz ? $libz : 'Not Found')."\n";
$msg .= '- libm -> '.($libm ? $libm : 'Not Found')."\n";
$msg .= '- libc -> '.($libc ? $libc : 'Not Found')."\n";
$msg .= '- ldlinux -> '.($ldlinux ? $ldlinux : 'Not Found')."\n";
 
if($libjpeg && $libpng && $libz && $libm && $libc && $ldlinux){
$msg .= 'You can use repng2jpeg (i386_linux_dynamic).';
return $msg;
}
if($libz && $libm && $libc && $ldlinux){
$msg .= 'You can use repng2jpeg (i386_linux_standard).';
return $msg;
}
$msg .= 'You can use repng2jpeg (i386_linux_static).';
return $msg;
}elseif(stristr($os, 'FreeBSD')){ // 系統為 FreeBSD
$libjpeg = $this->_findfile('libjpeg.so.9'); $libpng = $this->_findfile('libpng.so.5'); $libz = $this->_findfile('libz.so.2');
$libm = $this->_findfile('libm.so.2'); $libc = $this->_findfile('libc.so.4');
$msg .= '- libjpeg -> '.($libjpeg ? $libjpeg : 'Not Found')."\n";
$msg .= '- libpng -> '.($libpng ? $libpng : 'Not Found')."\n";
$msg .= '- libz -> '.($libz ? $libz : 'Not Found')."\n";
$msg .= '- libm -> '.($libm ? $libm : 'Not Found')."\n";
$msg .= '- libc -> '.($libc ? $libc : 'Not Found')."\n";
 
if($libjpeg && $libpng && $libz && $libm && $libc){
$msg .= 'You can use repng2jpeg (i386_freebsd4_dynamic).';
return $msg;
}
if($libz && $libm && $libc){
$msg .= 'You can use repng2jpeg (386_freebsd4_standard).';
return $msg;
}
$msg .= 'You can use repng2jpeg (i386_freebsd4_static).';
return $msg;
}elseif(stristr($os, 'Solaris')){ // 系統為 Solaris
$libc = $this->_findfile('libc.so.1'); $libdl = $this->_findfile('libdl.so.1');
$msg .= '- libc -> '.($libc ? $libc : 'Not Found')."\n";
$msg .= '- libdl -> '.($libdl ? $libdl : 'Not Found')."\n";
 
if($libc && $libdl){
$msg .= 'You can use repng2jpeg (i386_solaris_standard).';
return $msg;
}
$msg .= 'You can use repng2jpeg (i386_solaris_static).';
return $msg;
}elseif(stristr($os, 'Win')){ // 系統為 Windows
$msvcrt = $this->_findfile('msvcrt.dll', 1);
$msg .= '- msvcrt.dll -> '.($msvcrt ? $msvcrt : 'Not Found')."\n";
 
if($msvcrt){
$msg .= 'You can use repng2jpeg (i386_win32).';
}else{
$msg .= 'You can\'t use repng2jpeg (i386_win32). Try others.';
}
return $msg;
}else{ return 'You can\'t use repng2jpeg on this platform because no suitable binary can be used.'; } // 無法支援的系統
}
 
/* 檢查 repng2jpeg 可用性 */
function checkRepng2jpeg(){
$_exec = realpath('./repng2jpeg'.(strtoupper(substr(PHP_OS, 0, 3))==='WIN' ? '.exe' : ''));
if(function_exists('exec') && file_exists($_exec) && (strtoupper(substr(PHP_OS, 0, 3))==='WIN' || is_executable($_exec))){
return `$_exec --version`;
}else{ return false; }
}
 
/* 檢查 GD 可用性 */
function checkGD(){
if(extension_loaded('gd') && function_exists('ImageCreateTrueColor') && function_exists('ImageCopyResampled')){
$a = gd_info(); return $a['GD Version'];
}else{ return false; }
}
 
/* 檢查 Imagick 可用性 */
function checkImagick(){
if(extension_loaded('imagick') && class_exists('Imagick')){
$a = new Imagick(); $b = $a->getVersion(); $b = $b['versionString'];
unset($a);
return $b;
}else{ return false; }
}
 
/* 檢查 MagickWand 可用性 */
function checkMagickWand(){
if(extension_loaded('magickwand') && function_exists('MagickThumbnailImage')){
$a = MagickGetVersion(); $b = $a[0];
unset($a);
return $b;
}else{ return false; }
}
 
/* 檢查 ImageMagick 可用性 */
function checkImageMagick(){
if(!function_exists('exec')) return false;
$_exec = 'convert'.(strtoupper(substr(PHP_OS, 0, 3))==='WIN' ? '.exe' : '');
if($newexec = $this->_findfile($_exec)){ $_exec = $newexec; } // ImageMagick "convert" Binary Location
 
@exec("\"$_exec\" -version", $status, $retval);
$a = null;
if(preg_match('/^Version: (.*)/', $status[0], $a)){
return $a[1]."\n\t\t".'- Location guessed: '.$_exec;
}else{ return false; }
}
 
/* 檢查 MySQL 可用性 */
function checkPIOMySQL(){
return (extension_loaded('mysql') && function_exists('mysql_pconnect'));
}
 
/* 檢查 SQLite 可用性 */
function checkPIOSQLite(){
return (extension_loaded('sqlite') && function_exists('sqlite_popen'));
}
 
/* 檢查 PostgreSQL 可用性 */
function checkPIOPostgreSQL(){
return (extension_loaded('pgsql') && function_exists('pg_pconnect'));
}
 
/* 檢查 PDO SQLite3 可用性 */
function checkPIOPDOSQLite3(){
return (class_exists('PDO') && extension_loaded('pdo_sqlite'));
}
 
/* 回傳伺服器資訊 */
function getServerInfo(){
$msg = "\t".'PHP: '.phpversion()."\n";
$msg .= "\t\t".'- upload_max_filesize: '.ini_get('upload_max_filesize')."\n";
$msg .= "\t\t".'- post_max_size: '.ini_get('post_max_size')."\n";
$msg .= "\t\t".'- disable_functions: '.ini_get('disable_functions')."\n";
$msg .= "\t".'HTTPd: '.($_SERVER['SERVER_SOFTWARE'] ? $_SERVER['SERVER_SOFTWARE'] : getenv('SERVER_SOFTWARE'))."\n";
$msg .= "\t".'OS: '.php_uname('s').' '.php_uname('r').' '.php_uname('v');
return $msg;
}
}
 
$objChk = new CheckEnvironment();
?>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>CheckEnvironment</title>
</head>
<body>
<pre>
Server Infomation:
 
<?php echo $objChk->getServerInfo(); ?>
 
PIO Check:
 
SQLite Support: <?php echo $objChk->checkPIOSQLite(); ?>
MySQL Support: <?php echo $objChk->checkPIOMySQL(); ?>
PDO SQLite3 Support: <?php echo $objChk->checkPIOPDOSQLite3(); ?>
PostgreSQL Support: <?php echo $objChk->checkPIOPostgreSQL(); ?>
 
Thumbnail Generator Check:
 
GD Support: <?php echo $objChk->checkGD(); ?>
Imagick Support: <?php echo $objChk->checkImagick(); ?>
MagickWand Support: <?php echo $objChk->checkMagickWand(); ?>
ImageMagick Support: <?php echo $objChk->checkImageMagick(); ?>
repng2jpeg Support: <?php echo $objChk->checkRepng2jpeg(); ?>
 
Check suitable repng2jpeg:
 
<?php echo $objChk->checkRepng2jpegRecommended(); ?>
 
</pre>
</body>
</html>
New file

Property changes:

Name: svn:keywords
+ Id Date Author

/release/PIO-v5/Utilities/rebuildtree.php
@@ -0,0 +1,46 @@
<?php
/**
* Pixmicat! PIO 公用程式 - 重建 tree 樹狀結構檔
*
* 本公用程式可嘗試修復毀損的樹狀結構檔。原理是 PIO Log 來源已有內建一些相關樹狀結構供恢復用,
* 此程式可以將這些資訊統整後再輸出,即成為樹狀結構檔。
*
* 注意:若原文章儲存檔已有部分毀損,恕無法正確重建資訊。另原文章 sage 效果亦無法重現,一切按時間排序。
*
* @package PMCUtility
* @version $Id$
* @date $Date$
*/
include('./config.php');
if(preg_match('/^log:\/\/(.*)\:(.*)\/$/i', CONNECTION_STRING, $linkinfos)){
$logfile = './'.$linkinfos[1]; // 投稿文字記錄檔檔名
$treefile = './'.$linkinfos[2]; // 樹狀結構記錄檔檔名
}else{
exit('PIO Connection String Error! ("log://" Expected).');
}
 
$tree = array(); // 樹狀結構陣列
$treeline = ''; // 樹狀結構資料
$f = file($logfile);
$f_cnt = count($f);
for($i = 0; $i < $f_cnt; $i++){
$line = explode(',', $f[$i]);
if($line[1]==0){ // 首篇
if(!isset($tree[$line[0]])) $tree[$line[0]] = array($line[0]); // 僅自身一篇
else array_unshift($tree[$line[0]], $line[0]);
continue;
}
if(!isset($tree[$line[1]])) $tree[$line[1]] = array();
array_unshift($tree[$line[1]], $line[0]);
}
 
foreach($tree as $t){ $treeline .= implode(',', $t)."\r\n"; } // 自陣列整理成文字檔形式
$fp = fopen($treefile.'.new', 'w');
stream_set_write_buffer($fp, 0);
fwrite($fp, $treeline);
fclose($fp);
unset($fp);
@chmod($treefile.'.new', 0666);
 
echo '重建完成,檔案名稱為 "'.$treefile.'.new",請自行更名為 "'.$treefile.'"。以下是預覽:<hr/><pre>'.$treeline.'</pre>';
?>
New file
/release/PIO-v5/config.php
@@ -0,0 +1,136 @@
<?php
/*
Pixmicat! : 圖咪貓貼圖版程式
http://pixmicat.openfoundry.org/
版權所有 © 2005-2010 Pixmicat! Development Team
 
版權聲明:
此程式是基於レッツPHP!<http://php.s3.to/>的gazou.php、
双葉ちゃん<http://www.2chan.net>的futaba.php所改寫之衍生著作程式,屬於自由軟體,
以The Clarified Artistic License作為發佈授權條款。
您可以遵照The Clarified Artistic License來自由使用、散播、修改或製成衍生著作。
更詳細的條款及定義請參考隨附"LICENSE"條款副本。
 
發佈這一程式的目的是希望它有用,但沒有任何擔保,甚至沒有適合特定目的而隱含的擔保。
關於此程式相關的問題請不要詢問レッツPHP!及双葉ちゃん。
 
如果您沒有隨著程式收到一份The Clarified Artistic License副本,
請瀏覽http://pixmicat.openfoundry.org/license/以取得一份。
*/
/*---- Part 1:程式基本設定 ----*/
// 伺服器常態設定
define("PHP_SELF", 'pixmicat.php'); // 主程式名 (若是修改了主程式名,請配合修改)
define("TIME_ZONE", '+8'); // 時區設定 (GMT時區,參照 http://wwp.greenwichmeantime.com/ )
define("PIXMICAT_LANGUAGE", 'zh_TW'); // 語系語定
define("HTTP_UPLOAD_DIFF", 50); // HTTP上傳所有位元組與實際位元組之允許誤差值
ini_set("memory_limit", '32M'); // PHP運行的最大記憶體使用量 (php內定8M / 建議32M)
 
// FileIO設定
define("FILEIO_BACKEND", 'normal'); // FileIO後端指定
define("FILEIO_INDEXLOG", 'fileioindex.dat'); // FileIO索引記錄檔 (儲存在本機端)
define("FILEIO_PARAMETER", ''); // FileIO參數 (本機端儲存)
//define("FILEIO_PARAMETER", serialize(array('ftp.example.com', 21, 'demo', 'demo', 'PASV', '/pwd/', 'http://www.example.com/~demo/pwd/', true))); // FileIO參數 (FTP)
//define("FILEIO_PARAMETER", serialize(array('00000000000000000000000000000000'))); // FileIO參數 (ImageShack)
//define("FILEIO_PARAMETER", serialize(array('http://www.example.com/~demo/satellite.cgi', true, '12345678', 'http://www.example.com/~demo/src/', true))); // FileIO參數 (Satellite)
 
// PIO資料來源設定
define("CONNECTION_STRING", 'log://img.log:tree.log/'); // PIO 連線字串 (Log)
//define("CONNECTION_STRING", 'mysql://pixmicat:pass@localhost/test/imglog/'); // PIO 連線字串 (MySQL)
//define("CONNECTION_STRING", 'sqlite://pixmicat.db/imglog/'); // PIO 連線字串 (SQLite)
//define("CONNECTION_STRING", 'pgsql://pixmicat:1234@localhost/pixmicat_use/imglog/'); // PIO 連線字串 (PostgreSQL)
 
/*---- Part 2:板面各項細部功能設定 ----*/
define("IMG_DIR", 'src/'); // 圖片存放目錄
define("THUMB_DIR", 'thumb/'); // 預覽圖存放目錄
define("PHP_SELF2", 'index.htm'); // 入口檔名
define("PHP_EXT", '.htm'); // 第一頁以後生成檔案之副檔名
define("TITLE", 'Pixmicat!-PIO'); // 網頁標題
define("HOME", '../'); // 回首頁的連結
define("TOP_LINKS", ''); // 頁面右上方的額外連結,請直接以[<a href="網址" rel="_blank">名稱</a>]格式鍵入,如果不需要開新視窗可刪除rel一段
define("ADMIN_PASS", 'futaba'); // 管理員密碼
define("IDSEED", 'id種'); // 生成ID之隨機種子
 
// 管理員キャップ(Cap)設定 (啟用條件:開啟使用;名稱輸入識別名稱,E-mail輸入#啟動密碼)
define("CAP_ENABLE", 1); // 是否使用管理員キャップ (使用:1 不使用:0)
define("CAP_NAME", 'futaba'); // 管理員キャップ識別名稱
define("CAP_PASS", 'futaba'); // 管理員キャップ啟動密碼 (在E-mail一欄輸入#啟動密碼)
define("CAP_SUFFIX", ' ★'); // 管理員キャップ後綴字元 (請務必有★以便程式防止偽造,或可自行修改程式的防偽造部份)
define("CAP_ISHTML", 1); // 管理員キャップ啟動後內文是否接受HTML標籤 (是:1 否:0)
 
// 功能切換
define("USE_THUMB", 1); // 使用預覽圖機能 (使用:1 不使用:0)
define("USE_FLOATFORM", 1); // 新增文章表單使用自動隱藏 (是:1 否:0)
define("USE_SEARCH", 1); // 開放搜尋功能 (是:1 否:0)
define("USE_UPSERIES", 1); // 是否啟用連貼機能 [開主題後自動指向到主題下以方便連貼] (是:1 否:0)
define("RESIMG", 1); // 回應附加圖檔機能 (開啟:1 關閉:0)
define("AUTO_LINK", 1); // 討論串文字內的URL是否自動作成超連結 (是:1 否:0)
define("KILL_INCOMPLETE_UPLOAD", 1); // 自動刪除上傳不完整附加圖檔 (是:1 否:0)
define("ALLOW_NONAME", 1); // 是否接受匿名發送 (強制砍名:2 是:1 否:0)
define("DISP_ID", 2); // 顯示ID (強制顯示:2 選擇性顯示:1 永遠不顯示:0)
define("CLEAR_SAGE", 0); // 使用不推文模式時清除E-mail中的「sage」關鍵字 (是:1 否:0)
define("USE_QUOTESYSTEM", 1); // 是否打開引用瀏覽系統 [自動轉換>>No.xxx文字成連結並導引] (是:1 否:0)
define("SHOW_IMGWH", 1); // 是否顯示附加圖檔之原檔長寬尺寸 (是:1 否:0)
define("USE_CATEGORY", 1); // 是否開啟使用類別標籤分類功能 (是:1 否:0)
define("USE_RE_CACHE", 1); // 是否使用回應頁面顯示快取功能 (是:1 否:0)
define("USE_XHTML", 1); // 是否回傳 XHTML 檔頭讓瀏覽器以更嚴格的方式解析 [僅限瀏覽器支援者] (是:1 否:0)
 
// 模組載入
$ModuleList = array();
//$ModuleList[] = 'mod_rss';
//$ModuleList[] = 'mod_catalog';
 
// 封鎖設定
define("BAN_CHECK", 0); // 綜合性封鎖檢查功能 (關閉:0, 開啟:1)
$BANPATTERN = array(); // IP/Hostname封鎖黑名單
$DNSBLservers = array(0, 'sbl-xbl.spamhaus.org', 'list.dsbl.org', 'bl.blbl.org', 'bl.spamcop.net'); // DNSBL伺服器列表 (首項:使用伺服器個數)
$DNSBLWHlist = array(); // DNSBL白名單 (請輸入IP位置)
$BAD_STRING = array("dummy_string","dummy_string2"); // 限制出現之文字
$BAD_FILEMD5 = array("dummy","dummy2"); // 限制上傳附加圖檔之MD5檢查碼
 
// 附加圖檔限制
define("MAX_KB", 2000); // 附加圖檔上傳容量限制KB (php內定為最高2MB)
define("STORAGE_LIMIT", 1); // 附加圖檔總容量限制功能 (啟動:1 關閉:0)
define("STORAGE_MAX", 30000); // 附加圖檔總容量限制上限大小 (單位:KB)
define("ALLOW_UPLOAD_EXT", 'GIF|JPG|PNG|BMP|SWF'); // 接受之附加圖檔副檔名 (送出前表單檢查用,用 | 分隔)
 
// 連續投稿時間限制
define("RENZOKU", 10); // 連續投稿間隔秒數
define("RENZOKU2", 10); // 連續貼圖間隔秒數
 
// 預覽圖片相關限制
define("MAX_W", 250); // 討論串本文預覽圖片寬度 (超過則自動縮小)
define("MAX_H", 250); // 討論串本文預覽圖片高度
define("MAX_RW", 125); // 討論串回應預覽圖片寬度 (超過則自動縮小)
define("MAX_RH", 125); // 討論串回應預覽圖片高度
define("THUMB_Q", 75); // 預覽圖片之品質 (1-100, 建議預設75,越高品質越好但檔案也越大)
 
// 外觀設定
$ADDITION_INFO = ""; // 可在表單下顯示額外文字
$LIMIT_SENSOR = array('ByPostCountCondition'=>500); // 文章自動刪除機制設定
define("TEMPLATE_FILE", 'inc_pixmicat.tpl'); // 樣板位置
define("PAGE_DEF", 15); // 一頁顯示幾篇討論串
define("ADMIN_PAGE_DEF", 20); // 管理模式下,一頁顯示幾筆資料
define("RE_DEF", 10); // 一篇討論串最多顯示之回應筆數 (超過則自動隱藏,全部隱藏:0)
define("RE_PAGE_DEF", 30); // 回應模式一頁顯示幾筆回應內容 (分頁用,全部顯示:0)
define("MAX_RES", 30); // 回應筆數超過多少則不自動推文 (關閉:0)
define("MAX_AGE_TIME", 0); // 討論串可接受推文的時間範圍 (單位:小時,討論串存在超過此時間則回應皆不再自動推文 關閉:0)
define("COMM_MAX", 2000); // 內文接受Bytes數 (注意:中文字為2Bytes)
define("BR_CHECK", 0); // 文字換行行數上限 (不限:0)
define("STATIC_HTML_UNTIL", -1); // 更新文章時自動生成的靜態網頁至第幾頁止 (全部生成:-1 僅入口頁:0)
define("GZIP_COMPRESS_LEVEL", 3); // PHP動態輸出頁面使用Gzip壓縮層級 (關閉:0 啟動:1~9,推薦值:3)
define("DEFAULT_NOTITLE", '無標題'); // 預設文章標題
define("DEFAULT_NONAME", '無名氏'); // 預設文章名稱
define("DEFAULT_NOCOMMENT", '無內文'); // 預設文章內文
 
/*---- Part 3:Anti-SPAM 防止垃圾訊息機器人發文 ----*/
/* 欄位陷阱 (Field Trap)
介紹:
機器人會針對常見的欄位名稱送出垃圾資料,將這些常見的欄位製成陷阱,
另設名稱怪異的欄位為正確欄位,以避免直接的攻擊。
防止機器人學習的可能,請隔一段時間修改底下欄位值,建議英數大小寫隨機6~10個 (避免特殊符號、第一位不能為數字)。
*/
define("FT_NAME", 'bvUFbdrIC'); // 名稱欄位
define("FT_EMAIL", 'ObHGyhdTR'); // E-mail欄位
define("FT_SUBJECT", 'SJBgiFbhj'); // 標題欄位
define("FT_COMMENT", 'pOBvrtyJK'); // 內文欄位
?>
New file
/release/PIO-v5/mainstyle.css
@@ -0,0 +1,61 @@
@media all{ /* 通用設定 */
html { background: #FFE; color: #800000; } /* 基本網頁背景樣式 */
a:link { color: #00E; } /* 正常連結樣式 */
a:hover { color: #D00; } /* hover時連結樣式 */
a:visited { color: #00E; } /* 已拜訪連結樣式 */
a.qlink { text-decoration: none; } /* 引用用連結樣式 */
small { font-size: 0.8em; } /* 小字樣式(eg.[以預覽圖顯示]) */
hr { clear: left; } /* 分隔線樣式 */
img { border: 0; } /* 圖片顯示樣式 */
 
#toplink { text-align: right; } /* 頁首連結條樣式 */
h1 { color: #800000; text-align: center; margin: 0 auto; } /* 網頁主標題樣式 */
hr.top { width: 90%; height: 1px; } /* 主標題下分隔線樣式 */
 
.Form_bg { background: #EA8; } /* 送出表單左方欄位之底色 */
.hide_btn { float: right; width: 4em; height: 1.25em; overflow: hidden; text-align: center; background: #F0E0D6; } /* 表單收縮按鈕樣式 */
.show { color: #00E; }
.hide { display: none; }
#postinfo { font-size: 0.8em; } /* 上傳說明樣式 */
form { padding: 0; margin: 0; } /* 修正表單標籤造成的排版問題 */
 
.threadpost { } /* 討論串首篇樣式 */
.reply { display: table; margin: 0.5ex 1em 0 1em; background: #F0E0D6; } /* 討論串回應樣式 */
.reply_hl { background: #F0D5B7; } /* 討論串回應背景標亮樣式 */
.name { color: #117743; font-weight: bold; } /* 文章張貼者名稱樣式 */
.admin_cap { color: #0000FF; } /* 管理員キャップ樣式設定 */
.img { float: left; margin: 1ex 2ex; } /* 討論串圖片顯示樣式 */
.title { color: #CC1105; font-size: 1.125em; font-weight: bold; } /* 討論串標題樣式 */
.nor { font-weight: normal; } /* Trip取消粗體用 */
.quote { margin: 1em 2em; } /* 討論串內文縮排樣式 */
.resquote { color: #789922; } /* 標註引用回文顏色 */
.category { font-size: 0.8em; color: gray; } /* 類別標籤顯示樣式 */
 
.warn_txt { color: #F00000; font-weight: bold; } /* 討論串狀態警告文字(eg.文章即將被刪除) */
.warn_txt2 { color: #707070; } /* 討論串狀態提示文字(eg.回應幾篇被隱藏) */
#footer { text-align: center; clear: both; } /* 頁尾樣式 */
.bar_reply { background: #E04000; color: #FFF; font-weight: bold; text-align: center; } /* 回應模式樣式標題列 */
.bar_admin { background: #E08000; color: #FFF; font-weight: bold; text-align: center; } /* 管理模式樣式標題列 */
.ListRow1_bg { background: #D6D6F6; } /* 管理模式欄位背景顏色1(輪替出現) */
.ListRow2_bg { background: #F6F6F6; } /* 管理模式欄位背景顏色2(輪替出現) */
}
 
@media screen{ /* 標準顯示(一般顯示器)模式附加規則 */
 
}
 
@media handheld{ /* 手持設備(手機,PDA等)附加規則 */
html { max-width: 320px; margin: auto; font-size: 9px; } /* 320px為多數PDA的顯示寬度 */
input { max-width: 200px; }
textarea { max-width: 200px; height: 40px; }
 
.img { margin: 0.5ex 0.5ex; max-width: 125px; } /* 討論串圖片顯示樣式 */
.reply { clear: left; margin: 0.5ex 0 0 0; } /* 討論串回應樣式 */
.quote { margin: 1ex 1ex; } /* 討論串內文縮排樣式 */
#page_switch { clear: right; } /* 避免頁尾表格交疊 */
}
 
@media print{ /* 列印,預覽列印附加規則 */
html { background: #FFF; color: #000; } /* 網頁改成白底黑字,節省彩色列印成本 */
#postform_main { display: none; } /* 貼文表單在列印模式沒有用處,故不顯示以節省空間 */
}

Property changes:

Name: svn:ignore
+ .project

/PIO-v5