pixmicat

Subversion Repositories:
Compare Path: Rev
With Path: Rev
/PMC2Prototype/ @ 385  →  /PMC2Prototype/ @ 386
/PMC2Prototype/readme.txt
@@ -1,5 +1,5 @@
Pixmicat!2 Development Frame:
Last Modified: 2007/3/24 20:11
Last Modified: 2007/4/10 20:12
 
* XHTML 1.0 Transitional 化 (1.1 太嚴格不合用)
* 從原本的 PHP 小程式變成 Web Application 的概念,架構一個全新框架
@@ -19,7 +19,7 @@
\_ config [設定檔]
\ \_ global.php [全域性設定檔]
\ \_ config.comic.php [範例:comic 版面設定檔]
\_ lib [各函式庫 PIO, FileIO等]
\_ lib [各函式庫 PIO, FileIO, PMS 等]
\ \_ pio
\ \_ fileio
\ \_ lang
@@ -65,10 +65,41 @@
* 設定檔一定要指定,不可省略。為了多版面相容性,還是這樣好吧。
 
* 瀏覽版面導入 AJAX, JSON,文章使用動態要求並輸出,當然可以快取
* 上傳使用隱藏 iframe 處理
* JSON 結構:
首篇
{
"NO":22,
"NOW":"07/03/31(六)23:48 ID:iBBNAnUQ",
"NAME":"無名氏",
"SUB":"無標題",
"COM":"無內文",
"CATEGORY":"",
"IMG_SRC":"<a href=\"http://127.0.0.1/pixmicat/pmc2/board/comic/src/1175356123131.jpg\" rel=\"_blank\"><img src=\"http://127.0.0.1/pixmicat/pmc2/board/comic/thumb/1175356123131s.jpg\" style=\"width: 250px; height: 250px;\" class=\"img\" alt=\"142 KB\" title=\"142 KB\" /></a>",
"IMG_BAR":"檔名:<a href=\"http://127.0.0.1/pixmicat/pmc2/board/comic/src/1175356123131.jpg\" rel=\"_blank\">1175356123131.jpg</a>-(142 KB, 620x620) <small>[以預覽圖顯示]</small>",
"WARN_OLD":"",
"WARN_BEKILL":"",
"WARN_ENDREPLY":"",
"WARN_HIDEPOST":"",
"replies":[]
}
 
回應
{
"NO":18,
"NOW":"07/03/25(日)20:20 ID:U6zjBR9Y",
"NAME":"&quot;&quot;",
"SUB":"dd",
"COM":"ddd",
"CATEGORY":"",
"IMG_SRC":"",
"IMG_BAR":"",
"WARN_BEKILL":""
}
 
完全依照 PTE 的標籤,這樣在擴充模組動態新增標籤時也比較好辦理。
 
* PTE Client Version 到底是什麼玩意?簡單說就是把回傳的 JSON 給排好這樣,
將以前的 lib_pixmicat.tpl 讀取後把特定標籤取代成資料再丟入某 <div> 內
 
* 頁面快取
如同舊有 Pixmicat!,快取的部分是一頁的份量,討論串的回應模式並沒有去快取,
目前仍僅對一頁量作快取,格式為 JSON。其內容為當頁所有討論串及回應(僅顯示部分)
@@ -76,7 +107,7 @@
* 動態取得回應
利用 XMLHttpRequest GET pixmicat.php/comic/threads/486/2 (仍為 JSON) 再排版印出
* 新增文章 / 回應
使用傳統 Form,不過 target 為事先安排好的 iframe (這也是換用 XHTML 1.0 Transitional 的原因)
使用傳統 Form,不過 target 為事先安排好的隱藏 iframe (這也是換用 XHTML 1.0 Transitional 的原因)
然後正常程序送出表單,不過前端這時看起來是沒什麼動作的。成功之後 iframe 會印出回傳資料,
在這邊可以印 <script>parent.OK();</script> 一類提醒送出成功後的方法
* 後端管理
@@ -90,4 +121,11 @@
#status : 系統資訊
#search : 搜尋
#page;(Page) : 顯示第幾頁
#reply;(Thread No.);(Page) : 回應模式 (特定討論串第幾頁)
#reply;(Thread No.);(Page) : 回應模式 (特定討論串第幾頁)
* 安全性問題:
- http://blog.roodo.com/rocksaying/archives/2955557.html
- http://www.fortifysoftware.com/servlet/downloads/public/JavaScript_Hijacking.pdf
JSON 以 <script> 標記載入並抓取其中資料的問題 (JavaScript Hijacking Vulnerability)
對應方案:
- (避免<script>直接讀取) 前後加上 /* */ ,讀取時再切割出適當部分
- 限定 referrer,僅允許特定網頁讀取
/PMC2Prototype/action/action_ajax.php
@@ -60,39 +60,48 @@
}
 
// 輸出討論串 JSON 結構
function arrangeThread($post, $hiddenReply, $arr_kill, $arr_old){
global $PIO, $FileIO;
$dat = '';
function arrangeThread($post, $WARN_HIDEPOST, $arr_kill, $arr_old){
global $PIO, $FileIO, $PMS;
$dat = $tmp = '';
$arrLabels = array();
$postCount = count($post);
$arrReplies = array();
for($i = 0; $i < $postCount; $i++){
$IMG_SRC = $IMG_BAR = '';
$post[$i] = array_map('jsonified', $post[$i]);
extract($post[$i]);
// 修整資料以便輸出為 JSON
// 名稱
if(PMCCore_getConfig('CLEAR_SAGE')) $email = preg_replace('/^sage( *)/i', '', trim($email)); // 清除E-mail中的「sage」關鍵字
if(PMCCore_getConfig('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($ext && $FileIO->imageExists($tim.$ext)){
$imageURL = $FileIO->getImageURL($tim.$ext); // image URL
$thumbURL = $FileIO->getImageURL($tim.'s.jpg'); // thumb URL
 
$IMG_SRC = '<a href=\\"'.$imageURL.'" rel=\\"_blank\\"><img src=\\"nothumb.gif\\" class=\\"img\\" alt=\\"'.$imgsize.'\\" title=\\"'.$imgsize.'\\" /></a>'; // 預設顯示圖樣式 (無預覽圖時)
$IMG_SRC = '<a href="'.$imageURL.'" target="_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>';
$IMG_SRC = '<a href=\\"'.$imageURL.'\\" rel=\\"_blank\\"><img src=\\"'.$thumbURL.'\\" style=\\"width: '.$tw.'px; height: '.$th.'px;\\" class=\\"img\\" alt=\\"'.$imgsize.'\\" title=\\"'.$imgsize.'\\" /></a>';
$IMG_SRC = '<a href="'.$imageURL.'" target="_blank"><img src="'.$thumbURL.'" style="width: '.$tw.'px; height: '.$th.'px;" class="img" alt="'.$imgsize.'" title="'.$imgsize.'" /></a>';
}elseif($ext=='.swf') $IMG_SRC = ''; // swf檔案不需預覽圖
}
if(PMCCore_getConfig('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;
$IMG_BAR = _T('img_filename').'<a href="'.$imageURL.'" target="_blank">'.$tim.$ext.'</a>-('.$imgsize.$imgwh_bar.') '.$img_thumb;
}
 
// 提示標籤
$WARN_BEKILL = isset($arr_kill[$no]) ? '<span class=\\"warn_txt\\">'._T('warn_sizelimit').'</span><br />' : ''; // 預測刪除過大檔
$WARN_BEKILL = isset($arr_kill[$no]) ? '<span class="warn_txt">'._T('warn_sizelimit').'</span><br />' : ''; // 預測刪除過大檔
if(!$i){ // 首篇 Only
$WARN_OLD = (isset($arr_old[$no]) && $arr_old[$no] + 1 >= PMCCore_getConfig('LOG_MAX') * 0.95) ? '<span class=\\"warn_txt\\">'._T('warn_oldthread').'</span><br />' : '';
$WARN_ENDREPLY = $PIO->getPostStatus($status, 'TS') ? '<span class=\\"warn_txt\\">'._T('warn_locked').'</span><br />' : '';
$hiddenReply = $hiddenReply ? '<span class=\\"warn_txt2\\">'._T('notice_omitted', $hiddenReply).'</span><br />' : '';
$WARN_OLD = (isset($arr_old[$no]) && $arr_old[$no] + 1 >= PMCCore_getConfig('LOG_MAX') * 0.95) ? '<span class="warn_txt">'._T('warn_oldthread').'</span><br />' : '';
$WARN_ENDREPLY = $PIO->getPostStatus($status, 'TS') ? '<span class="warn_txt">'._T('warn_locked').'</span><br />' : '';
$WARN_HIDEPOST = $WARN_HIDEPOST ? '<span class="warn_txt2">'._T('notice_omitted', $WARN_HIDEPOST).'</span><br />' : '';
}
 
// 類別
@@ -100,12 +109,28 @@
$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=\\"'.PMCCore_getConfig('ENV.PHP_SELF').'/search_ajax/category/'.urlencode($c).'\\">'.$c.'</a>';
if($c = $ary_category[$p]) $ary_category2[] = '<a href="'.PMCCore_getConfig('ENV.PHP_SELF').'/search_ajax/category/'.urlencode($c).'">'.$c.'</a>';
}
$category = implode(', ', $ary_category2);
 
if($i===0){ $dat = '{"hiddenReply":"'.$hiddenReply.'","no":'.$no.',"now":"'.$now.'","name":"'.$name.'","email":"'.$email.'","sub":"'.$sub.'","com":"'.$com.'","category":"'.$category.'","imgsrc":"'.$IMG_SRC.'","imgbar":"'.$IMG_BAR.'","WARN_BEKILL":"'.$WARN_BEKILL.'","WARN_OLD":"'.$WARN_OLD.'","WARN_ENDREPLY":"'.$WARN_ENDREPLY.'","replies":['; continue; }
$arrReplies[] = '{"no":'.$no.',"now":"'.$now.'","name":"'.$name.'","email":"'.$email.'","sub":"'.$sub.'","com":"'.$com.'","category":"'.$category.'","imgsrc":"'.$IMG_SRC.'","imgbar":"'.$IMG_BAR.'","WARN_BEKILL":"'.$WARN_BEKILL.'"}';
// 連結
$QUOTEBTN = 'No.'.$no;
$REPLYBTN = '[回應]';
 
if($i===0){ // 首篇
$arrLabels = array('{$NO}'=>$no, '{$SUB}'=>$sub, '{$NAME}'=>$name, '{$NOW}'=>$now, '{$COM}'=>$com, '{$CATEGORY}'=>$category, '{$QUOTEBTN}'=>$QUOTEBTN, '{$REPLYBTN}'=>$REPLYBTN, '{$IMG_BAR}'=>$IMG_BAR, '{$IMG_SRC}'=>$IMG_SRC, '{$WARN_OLD}'=>$WARN_OLD, '{$WARN_BEKILL}'=>$WARN_BEKILL, '{$WARN_ENDREPLY}'=>$WARN_ENDREPLY, '{$WARN_HIDEPOST}'=>$WARN_HIDEPOST);
//$PMS->useModuleMethods('ThreadPost', array(&$arrLabels, $post[$i], false)); // "ThreadPost" Hook Point
}else{ // 回應
$arrLabels = array('{$NO}'=>$no, '{$SUB}'=>$sub, '{$NAME}'=>$name, '{$NOW}'=>$now, '{$COM}'=>$com, '{$CATEGORY}'=>$category, '{$QUOTEBTN}'=>$QUOTEBTN, '{$IMG_BAR}'=>$IMG_BAR, '{$IMG_SRC}'=>$IMG_SRC, '{$WARN_BEKILL}'=>$WARN_BEKILL, '{$QUOTEBTN}'=>$QUOTEBTN);
//$PMS->useModuleMethods('ThreadReply', array(&$arrLabels, $post[$i], $resno)); // "ThreadReply" Hook Point
}
$tmp = array();
foreach(array_map('jsonified', $arrLabels) as $akey => $aval){
preg_match('/\{\$(.*)\}/', $akey, $matches);
$tmp[] = '"'.$matches[1].'":"'.$aval.'"';
}
if($i===0){ $dat = '{'.implode(',', $tmp).',"replies":['; }
else{ $arrReplies[] = '{'.implode(',', $tmp).'}'; }
}
if(count($arrReplies)) $dat .= implode(',', $arrReplies);
$dat .= ']}';
/PMC2Prototype/resource/xmlhttp.js
@@ -67,15 +67,21 @@
if(template.match(/<!--&SEPARATE-->([\s\S]*)<!--\/&SEPARATE-->/)){ Trender.PTE.cache.SEPARATE = RegExp.$1.replace(/&nbsp;/g, '&#160;'); }
},
_replaceMain : function(obj){
var arrLabels = {'{$NO}' : obj.no, '{$SUB}' : obj.sub, '{$NAME}' : obj.name, '{$NOW}' : obj.now, '{$COM}' : obj.com, '{$CATEGORY}' : obj.category, '{$QUOTEBTN}' : '', '{$REPLYBTN}' : '', '{$IMG_BAR}' : obj.imgbar, '{$IMG_SRC}' : obj.imgsrc, '{$WARN_OLD}' : obj.WARN_OLD, '{$WARN_BEKILL}' : obj.WARN_BEKILL, '{$QUOTEBTN}' : '', '{$WARN_ENDREPLY}' : obj.WARN_ENDREPLY, '{$WARN_HIDEPOST}' : obj.hiddenReply};
var dat = Trender.PTE._evalIf(Trender.PTE.cache.MAIN, arrLabels);
for(l in arrLabels){ dat = dat.replace(new RegExp(l.replace('$', '\\$'), 'g'), arrLabels[l]); }
//var arrLabels = {'{$NO}' : obj.no, '{$SUB}' : obj.sub, '{$NAME}' : obj.name, '{$NOW}' : obj.now, '{$COM}' : obj.com, '{$CATEGORY}' : obj.category, '{$QUOTEBTN}' : '', '{$REPLYBTN}' : '', '{$IMG_BAR}' : obj.imgbar, '{$IMG_SRC}' : obj.imgsrc, '{$WARN_OLD}' : obj.WARN_OLD, '{$WARN_BEKILL}' : obj.WARN_BEKILL, '{$QUOTEBTN}' : '', '{$WARN_ENDREPLY}' : obj.WARN_ENDREPLY, '{$WARN_HIDEPOST}' : obj.hiddenReply};
var dat = Trender.PTE._evalIf(Trender.PTE.cache.MAIN, obj);
for(l in obj){
if(l=='replies'){ continue; }
dat = dat.replace(new RegExp('{\\$'+l+'}', 'g'), obj[l]);
}
return dat;
},
_replaceReply : function(obj){
var arrLabels = {'{$NO}' : obj.no, '{$SUB}' : obj.sub, '{$NAME}' : obj.name, '{$NOW}' : obj.now, '{$COM}' : obj.com, '{$CATEGORY}' : obj.category, '{$QUOTEBTN}' : '', '{$IMG_BAR}' : obj.imgbar, '{$IMG_SRC}' : obj.imgsrc, '{$WARN_BEKILL}' : obj.WARN_BEKILL, '{$QUOTEBTN}' : ''};
var dat = Trender.PTE._evalIf(Trender.PTE.cache.REPLY, arrLabels);
for(var l in arrLabels){ dat = dat.replace(new RegExp(l.replace('$', '\\$'), 'g'), arrLabels[l]); }
//var arrLabels = {'{$NO}' : obj.no, '{$SUB}' : obj.sub, '{$NAME}' : obj.name, '{$NOW}' : obj.now, '{$COM}' : obj.com, '{$CATEGORY}' : obj.category, '{$QUOTEBTN}' : '', '{$IMG_BAR}' : obj.imgbar, '{$IMG_SRC}' : obj.imgsrc, '{$WARN_BEKILL}' : obj.WARN_BEKILL, '{$QUOTEBTN}' : ''};
var dat = Trender.PTE._evalIf(Trender.PTE.cache.REPLY, obj);
for(var l in obj){
//dat = dat.replace(new RegExp(l.replace('$', '\\$'), 'g'), arrLabels[l]);
dat = dat.replace(new RegExp('{\\$'+l+'}', 'g'), obj[l]);
}
return dat;
},
_replaceSeparate : function(){
@@ -83,9 +89,9 @@
},
_evalIf : function(dat, arrLabels){
var vari, ifTrue, ifFalse;
while(dat.match(/(<!--&IF\((\$.*),\'(.*)\',\'(.*)\'\)-->)/)){
while(dat.match(/(<!--&IF\(\$(.*),\'(.*)\',\'(.*)\'\)-->)/)){
vari = RegExp.$2; ifTrue = RegExp.$3; ifFalse = RegExp.$4;
dat = dat.replace(RegExp.$1, (arrLabels['{'+vari+'}'] ? ifTrue : ifFalse));
dat = dat.replace(RegExp.$1, (arrLabels[vari] ? ifTrue : ifFalse));
}
return dat;
},
@@ -117,10 +123,9 @@
PageNavi : function(pMax, pCurrent){
var TableTxt = '<ul>';
TableTxt += '<li>' + ((pCurrent==1) ? '第一頁' : '<a href="#page;'+(pCurrent-1)+'" onclick="Thistory.register(this)">上一頁</a>') + "</li>\n";
for(var i = 1; i <= pMax; i++){
TableTxt += '<li>[' + ((i==pCurrent) ? '<b>'+i+'</b>' : '<a href="#page;'+i+'" onclick="Thistory.register(this)">'+i+'</a>') + "]</li>\n";
}
TableTxt += '<li>' + ((pCurrent==pMax) ? '最後一頁' : '<a href="#page;'+(pCurrent+1)+'" onclick="Thistory.register(this)">下一頁</a>') + '</li></ul>';
TableTxt += '<li>' + ((pCurrent==pMax) ? '最後一頁' : '<a href="#page;'+(pCurrent+1)+'" onclick="Thistory.register(this)">下一頁</a>') + "</li>\n";
for(var i = 1; i <= pMax; i++){ TableTxt += '<li>[' + ((i==pCurrent) ? '<b>'+i+'</b>' : '<a href="#page;'+i+'" onclick="Thistory.register(this)">'+i+'</a>') + "]</li>\n"; }
TableTxt += '</ul>';
$('TrenderPageNavi').innerHTML = TableTxt;
},
// 成像瀏覽頁面
/PMC2Prototype/resource/mainstyle.css
@@ -13,7 +13,7 @@
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; } /* 表單收縮按鈕樣式 */
.hide_btn { float: right; width: 4em; height: 1.25em; margin-right: 1.25em; overflow: hidden; text-align: center; background: #F0E0D6; } /* 表單收縮按鈕樣式 */
.show { color: #00E; }
.hide { display: none; }
#postinfo { font-size: 0.8em; } /* 上傳說明樣式 */
@@ -40,7 +40,7 @@
.ListRow2_bg { background: #F6F6F6; } /* 管理模式欄位背景顏色2(輪替出現) */
 
#TrenderPageNavi ul { clear: both; }
#TrenderPageNavi ul li { display: inline; }
#TrenderPageNavi ul li { display: inline; padding-left: 3px; }
}
 
@media screen{ /* 標準顯示(一般顯示器)模式附加規則 */
/PMC2Prototype/resource/pixmicat_ajax.tmpl
@@ -118,8 +118,8 @@
<div id="TrenderPageNavi">
<ul>
<li>第一頁</li>
<li>最後一頁</li>
<li>[<b>1</b>]</li>
<li>最後一頁</li>
</ul>
</div>