pixmicat - Blame information for rev 311

Subversion Repositories:
Rev:
Rev Author Line No. Line
311 roytam1 1 <?php
2 /*
3 PIO - Pixmicat! data source I/O
4 Log API
5 */
6  
7 class PIOlog {
8         var $logfile,$treefile; // Local Constant
9         var $porder,$torder,$logs,$trees,$restono,$prepared; // Local Global
10  
11         function PIOlog($connstr='') {
12                 $this->porder=array();
13                 $this->torder=array();
14                 $this->logs=array();
15                 $this->trees=array();
16                 $this->restono=array();
17                 $this->prepared=0;
18  
19                 if($connstr) $this->dbConnect($connstr);
20         }
21  
22         /* PIO模組版本 */
23         function pioVersion() {
24                 return 'v20060812β';
25         }
26  
27         /* 將回文放進陣列 */
28         /* private */ function includeReplies($posts) {
29                 foreach($posts as $post) {
30                         if($this->restono[$post]==$post) { // 討論串頭
31                                 $posts=array_merge($posts,$this->trees[$post]);
32                         }
33                 }
34                 return array_merge(array(),array_unique($posts));
35         }
36  
37         /* 取代 , 成為 &#44; 避免衝突 */
38         /* private */ function _replaceComma($txt) {
39                 return str_replace(',', '&#44;', $txt);
40         }
41  
42         /* 處理連線字串/連接 */
43         function dbConnect($connStr) {
44                 if(preg_match('/^log:\/\/(.*)\:(.*)\/$/i', $connStr, $linkinfos)){
45                         $this->logfile=$linkinfos[1]; // 投稿文字記錄檔檔名
46                         $this->treefile=$linkinfos[2]; // 樹狀結構記錄檔檔名
47                 }
48         }
49  
50         /* 初始化 */
51         function dbInit() {
52                 $chkfile = array($this->logfile, $this->treefile);
53                 // 逐一自動建置tree及log檔案
54                 foreach($chkfile as $value){
55                         if(!is_file($value)){ // 檔案不存在
56                                 $fp = fopen($value, 'w');
57                                 stream_set_write_buffer($fp, 0);
58                                 if($value==$this->logfile) fwrite($fp, '1,05/01/01(六)00:00,無名氏,,無標題,無內文,,,,,,,,');
59                                 if($value==$this->treefile) fwrite($fp, '1');
60                                 fclose($fp);
61                                 unset($fp);
62                                 @chmod($value, 0666);
63                         }
64                 }
65                 return true;
66         }
67  
68         /* 準備/讀入 */
69         function dbPrepare($reload=false,$transaction=true) {
70                 if($this->prepared && !$reload) return true;
71                 if($reload && $this->prepared) $this->porder=$this->torder=$this->logs=$this->restono=$this->trees=array();
72                 $lines = file($this->logfile);
73                 $tree = file($this->treefile);
74  
75                 foreach($tree as $treeline) {
76                         if($treeline=='') continue;
77                         $tline=explode(',', rtrim($treeline));
78                         $this->trees[$tline[0]]=$tline;
79                         $this->torder[]=$tline[0];
80                         foreach($tline as $post) $this->restono[$post]=$tline[0];
81                 }
82                 foreach($lines as $line) {
83                         if($line=='') continue;
84                         $tline=array();
85                         list($tline['no'],$tline['now'],$tline['name'],$tline['email'],$tline['sub'],$tline['com'],$tline['url'],$tline['host'],$tline['pw'],$tline['ext'],$tline['w'],$tline['h'],$tline['time'],$tline['chk'])=explode(',', $line);
86                         $tline['resto']=$this->restono[$tline['no']]; // 欲回應編號
87                         $this->porder[]=$tline['no'];
88                         $this->logs[$tline['no']]=array_reverse($tline); // list()是由右至左代入的
89                 }
90  
91                 $this->prepared = 1;
92         }
93  
94         /* 提交/儲存 */
95         function dbCommit() {
96                 if(!$this->prepared) return false;
97                 $pcount=$this->postCount();
98                 $tcount=$this->threadCount();
99  
100                 $log=$tree='';
101                 for($post=0;$post<$pcount;$post++){
102                         if(isset($this->logs[$this->porder[$post]])){
103                                 if(array_key_exists('resto', $this->logs[$this->porder[$post]])) array_shift($this->logs[$this->porder[$post]]); // resto不屬於原log架構故除去
104                                 $log .= implode(',',$this->logs[$this->porder[$post]]).",\n";
105                         }
106                 }
107                 for($tline=0;$tline<$tcount;$tline++)
108                         $tree.=$this->is_Thread($this->torder[$tline])?implode(',',$this->trees[$this->torder[$tline]])."\n":'';
109  
110                 $fp = fopen($this->logfile, 'w');
111                 stream_set_write_buffer($fp, 0);
112                 flock($fp, LOCK_EX); // 鎖定檔案
113                 fwrite($fp, $log);
114                 flock($fp, LOCK_UN); // 解鎖
115                 fclose($fp);
116  
117                 $fp = fopen($this->treefile, 'w');
118                 stream_set_write_buffer($fp, 0);
119                 flock($fp, LOCK_EX); // 鎖定檔案
120                 fwrite($fp, $tree);
121                 flock($fp, LOCK_UN); // 解鎖
122                 fclose($fp);
123         }
124  
125         /* 優化資料表 */
126         function dbOptimize($doit=false) {
127                 return false; // 不支援
128         }
129  
130         /* 刪除舊文 */
131         function delOldPostes() {
132                 if(!$this->prepared) $this->dbPrepare();
133  
134                 $delPosts=@array_splice($this->porder,LOG_MAX);
135                 if(count($delPosts)) return $this->removePosts(includeReplies($delPosts));
136                 else return false;
137         }
138  
139         /* 刪除文章 */
140         function removePosts($posts) {
141                 if(!$this->prepared) $this->dbPrepare();
142  
143                 $posts=$this->includeReplies($posts);
144                 $files=$this->removeAttachments($posts);
145                 $porder_flip=array_flip($this->porder);
146                 $torder_flip=array_flip($this->torder);
147                 $pcount=count($posts);
148                 for($p=0;$p<$pcount;$p++) {
149                         if(!isset($this->logs[$posts[$p]])) continue;
150                         if($this->restono[$posts[$p]]==$posts[$p]) { // 討論串頭
151                                 unset($this->trees[$posts[$p]]); // 刪除樹狀記錄
152                                 if(array_key_exists($posts[$p],$torder_flip)) unset($this->torder[$torder_flip[$posts[$p]]]);
153                         }
154                         unset($this->logs[$posts[$p]]);
155                         if(array_key_exists($this->restono[$posts[$p]],$this->trees)) {
156                                 $tr_flip=array_flip($this->trees[$this->restono[$posts[$p]]]);
157                                 unset($this->trees[$this->restono[$posts[$p]]][$tr_flip[$posts[$p]]]);
158                         }
159                         unset($this->restono[$posts[$p]]);
160                         if(array_key_exists($posts[$p],$porder_flip)) unset($this->porder[$porder_flip[$posts[$p]]]);
161                 }
162                 $this->porder=array_merge(array(),$this->porder);
163                 $this->torder=array_merge(array(),$this->torder);
164                 return $files;
165         }
166  
167         /* 刪除舊附件 (輸出附件清單) */
168         function delOldAttachments($total_size,$storage_max,$warnOnly=true) {
169                 global $path;
170                 if(!$this->prepared) $this->dbPrepare();
171  
172                 $rpord = $this->porder; sort($rpord); // 由舊排到新 (小->大)
173                 $arr_warn = $arr_kill = array();
174                 foreach($rpord as $post) {
175                         if(file_func('exist',$path.IMG_DIR.$this->logs[$post]['time'].$this->logs[$post]['ext'])) { $total_size -= file_func('size',$path.IMG_DIR.$this->logs[$post]['time'].$this->logs[$post]['ext']) / 1024; $arr_kill[] = $post;$arr_warn[$post] = 1; } // 標記刪除
176                         if(file_func('exist',$path.THUMB_DIR.$this->logs[$post]['time'].'s.jpg')) { $total_size -= file_func('size',$path.THUMB_DIR.$this->logs[$post]['time'].'s.jpg') / 1024; }
177                         if($total_size<$storage_max) break;
178                 }
179                 return $warnOnly?$arr_warn:$this->removeAttachments($arr_kill);
180         }
181  
182         /* 刪除附件 (輸出附件清單) */
183         function removeAttachments($posts) {
184                 global $path;
185                 if(!$this->prepared) $this->dbPrepare();
186  
187                 $files=array();
188                 foreach($posts as $post) {
189                         if($this->logs[$post]['ext']) {
190                                 if(file_func('exist',$path.IMG_DIR.$this->logs[$post]['time'].$this->logs[$post]['ext'])) $files[]=IMG_DIR.$this->logs[$post]['time'].$this->logs[$post]['ext'];
191                                 if(file_func('exist',$path.THUMB_DIR.$this->logs[$post]['time'].'s.jpg')) $files[]=THUMB_DIR.$this->logs[$post]['time'].'s.jpg';
192                                 $this->logs[$post]['ext']='';
193                         }
194                 }
195                 return $files;
196         }
197  
198         /* 檢查是否連續投稿 */
199         function checkSuccessivePost($lcount, $com, $timestamp, $pass, $passcookie, $host, $upload_filename){
200                 if(!$this->prepared) $this->dbPrepare();
201  
202                 $pcount = $this->postCount();
203                 $lcount = ($pcount > $lcount) ? $lcount : $pcount;
204                 for($i=0;$i<$lcount;$i++) {
205                         $post=$this->logs[$this->porder[$i]];
206                         list($lcom,$lhost,$lpwd,$ltime) = array($post['com'],$post['host'],$post['pw'],$post['time']);
207                         $ltime2 = substr($ltime, 0, -3);
208                         if($host==$lhost || $pass==$lpwd || $passcookie==$lpwd) $pchk = 1;
209                         else $pchk = 0;
210                         if(RENZOKU && $pchk){ // 密碼比對符合且開啟連續投稿時間限制
211                                 if($timestamp - $ltime2 < RENZOKU) return true; // 投稿時間相距太短
212                                 if($timestamp - $ltime2 < RENZOKU2 && $upload_filename) return true; // 附加圖檔的投稿時間相距太短
213                                 if($com == $lcom && !$upload_filename) return true; // 內文一樣
214                         }
215                 }
216                 return false;
217         }
218  
219         /* 檢查是否重複貼圖 */
220         function checkDuplicateAttechment($lcount, $md5hash){
221                 global $path;
222  
223                 $pcount = $this->postCount();
224                 $lcount = ($pcount > $lcount) ? $lcount : $pcount;
225                 if(!$md5hash) return false; // 無附加圖檔
226                 for($i=0;$i<$lcount;$i++) {
227                         if(!$this->logs[$this->porder[$i]]['chk']) continue; // 無附加圖檔
228                         if($this->logs[$this->porder[$i]]['chk']==$md5hash) {
229                                 $dfile = $path.IMG_DIR.$this->logs[$this->porder[$i]]['time'].$this->logs[$this->porder[$i]]['ext'];
230                                 if(file_func('exist', $dfile)) return true; // 存在MD5雜湊相同的檔案
231                         }
232                 }
233                 return false;
234         }
235  
236         /* 文章數目 */
237         function postCount($resno=0) {
238                 if(!$this->prepared) $this->dbPrepare();
239  
240                 return ($resno)?$this->is_Thread($resno)?count(@$this->trees[$resno])-1:0:count($this->porder);
241         }
242  
243         /* 討論串數目 */
244         function threadCount() {
245                 if(!$this->prepared) $this->dbPrepare();
246  
247                 return count($this->torder);
248         }
249  
250         /* 輸出文章清單 */
251         function fetchPostList($resno=0,$start=0,$amount=0) {
252                 if(!$this->prepared) $this->dbPrepare();
253  
254                 $plist=array();
255                 if($resno) {
256                         if($this->is_Thread($resno)) {
257                                 if($start && $amount) {
258                                         $plist=array_slice($this->trees[$resno],$start,$amount);array_unshift($plist,$resno);
259                                 }
260                                 if(!$start && $amount) $plist=array_slice($this->trees[$resno],0,$amount);
261                                 if(!$start && !$amount) $plist=$this->trees[$resno];
262                         }
263                 } else {
264                         $plist=$amount?array_slice($this->porder,$start,$amount):$this->porder;
265                 }
266                 return $plist;
267         }
268  
269         /* 輸出討論串清單 */
270         function fetchThreadList($start=0,$amount=0) {
271                 if(!$this->prepared) $this->dbPrepare();
272  
273                 return $amount?array_slice($this->torder,$start,$amount):$this->torder;
274         }
275  
276         /* 輸出文章 */
277         function fetchPosts($postlist) {
278                 if(!$this->prepared) $this->dbPrepare();
279  
280                 $posts=array();
281                 if(!is_array($postlist)) { // Single Post
282                         array_push($posts,$this->logs[$postlist]);
283                 } else {
284                         foreach($postlist as $p) array_push($posts,$this->logs[$p]);
285                 }
286                 return $posts;
287         }
288  
289         /* 有此討論串? */
290         function is_Thread($no) {
291                 if(!$this->prepared) $this->dbPrepare();
292  
293                 return isset($this->trees[$no]);
294         }
295  
296         /* 搜尋文章 */
297         function searchPost($keyword,$field,$method) {
298                 if(!$this->prepared) $this->dbPrepare();
299  
300                 $foundPosts=array();
301                 $keyword_cnt=count($keyword);
302                 foreach($this->logs as $log) {
303                         $found=0;
304                         foreach($keyword as $k)
305                                 if(strpos($log[$field], $k)!==FALSE) $found++;
306                         if($method=="AND" && $found==$keyword_cnt) array_push($foundPosts,$log); // 全部都有找到 (AND交集搜尋)
307                         elseif($method=="OR" && $found) array_push($foundPosts,$log); // 有找到 (OR聯集搜尋)
308                 }
309                 return $foundPosts;
310         }
311  
312         /* 新增文章/討論串 */
313         function addPost($no,$resno,$now,$name,$email,$sub,$com,$url,$host,$pass,$ext,$W,$H,$tim,$chk,$age=false) {
314                 if(!$this->prepared) $this->dbPrepare();
315  
316                 $tline=array();
317                 list($tline['no'],$tline['now'],$tline['name'],$tline['email'],$tline['sub'],$tline['com'],$tline['url'],$tline['host'],$tline['pw'],$tline['ext'],$tline['w'],$tline['h'],$tline['time'],$tline['chk'])=array($no,$now,$name,$email,$sub,$com,$url,$host,$pass,$ext,$W,$H,$tim,$chk);
318                 $tline = array_map(array($this,'_replaceComma'), $tline); // 只有Log版需要將資料內的 , 轉換
319                 $this->logs[$no]=array_reverse($tline);
320                 array_unshift($this->porder,$no);
321  
322                 if($resno) {
323                         $this->trees[$resno][]=$no;
324                         $this->restono[$no]=$resno;
325                         if($age) {
326                                 $torder_flip=array_flip($this->torder);
327                                 array_splice($this->torder,$torder_flip[$resno],1);
328                                 array_unshift($this->torder,$resno);
329                         }
330                 } else {
331                         $this->trees[$no][0]=$no;
332                         $rthis->estono[$no]=$no;
333                         array_unshift($this->torder,$no);
334                 }
335         }
336  
337         /* 取得文章屬性 */
338         function getPostStatus($status,$statusType) {
339                 if(!$this->prepared) $this->dbPrepare();
340  
341                 $returnValue = 0; // 回傳值
342  
343                 switch($statusType){
344                         case 'TS': // 討論串是否鎖定
345                                 $returnValue = (strpos($status,'_THREADSTOP_')!==false) ? 1 : 0; // 討論串是否鎖定
346                                 break;
347                         default:
348                 }
349                 return $returnValue;
350         }
351  
352         /* 設定文章屬性 */
353         function setPostStatus($no, $status, $statusType, $newValue) {
354                 if(!$this->prepared) $this->dbPrepare();
355  
356                 $scount=count($no);
357                 for($i=0;$i<$scount;$i++) {
358                         $statusType[$i]=explode(',',$statusType[$i]);
359                         $newValue[$i]=explode(',',$newValue[$i]);
360                         $st_count=count($statusType[$i]);
361                         for($j=0;$j<$st_count;$j++) {
362                                 switch($statusType[$i][$j]){
363                                         case 'TS': // 討論串鎖定
364                                                 if(strpos($status[$i],'_THREADSTOP_')!==false && $newValue[$i][$j]==0)
365                                                         $status[$i] = str_replace('_THREADSTOP_','',$status[$i]); // 討論串解除鎖定
366                                                 elseif(strpos($status[$i],'_THREADSTOP_')===false && $newValue[$i][$j]==1)
367                                                         $status[$i] .= '_THREADSTOP_'; // 討論串鎖定
368                                                 break;
369                                         default:
370                                 }
371                         }
372                         $this->logs[$no[$i]]['url']=$status[$i];
373                 }
374         }
375  
376         /* 取得最後的文章編號 */
377         function getLastPostNo($state) {
378                 if(!$this->prepared) $this->dbPrepare();
379  
380                 switch($state) {
381                         case 'beforeCommit':
382                         case 'afterCommit':
383                                 return $this->porder[0];
384                 }
385         }
386 }
387 ?>