Dede織夢模板類

/**********************************************
//DedeTagParse Dede織夢模板類
function c____DedeTagParse();
***********************************************/
class DedeTagParse
{
	var $NameSpace = 'dede'; 	//標記的名字空間
	var $TagStartWord = '{'; 	//標記起始
	var $TagEndWord = '}'; 		//標記結束
	var $TagMaxLen = 64; 		//標記名稱的最大值
	var $CharToLow = true; 		// true表示對屬性和標記名稱不區分大小寫
	var $IsCache = FALSE; 		//是否使用緩衝
	var $TempMkTime = 0;
	var $CacheFile = '';
	var $SourceString = '';		//模板字符串
	var $CTags = '';		 	//標記集合
	var $Count = -1;		 	//$Tags標記個數
	var $refObj = '';   		//引用當前模板類的對象
	function __construct()
	{
		if(!isset($GLOBALS['cfg_tplcache']))
		{
			$GLOBALS['cfg_tplcache'] = 'N';
		}
		if($GLOBALS['cfg_tplcache']=='Y')
		{
			$this->IsCache = true;
		}
		else
		{
			$this->IsCache = FALSE;
		}
		$this->NameSpace = 'dede';
		$this->TagStartWord = '{';
		$this->TagEndWord = '}';
		$this->TagMaxLen = 64;
		$this->CharToLow = true;
		$this->SourceString = '';
		$this->CTags = Array();
		$this->Count = -1;
		$this->TempMkTime = 0;
		$this->CacheFile = '';
	}
	function DedeTagParse()
	{
		$this->__construct();
	}
	//設置標記的命名空間,默認爲dede
	function SetNameSpace($str,$s="{",$e="}")
	{
		$this->NameSpace = strtolower($str);
		$this->TagStartWord = $s;
		$this->TagEndWord = $e;
	}
	//重置成員變量或Clear
	function SetDefault()
	{
		$this->SourceString = '';
		$this->CTags = '';
		$this->Count=-1;
	}
	function GetCount()
	{
		return $this->Count+1;
	}
	function Clear()
	{
		$this->SetDefault();
	}
	//檢測模板緩衝
	function LoadCache($filename)
	{
		global $cfg_tplcache,$cfg_tplcache_dir;
		if(!$this->IsCache)
		{
			return false;
		}
		$cdir = dirname($filename);
		$cachedir = DEDEROOT.$cfg_tplcache_dir;
		$ckfile = str_replace($cdir,'',$filename).substr(md5($filename),0,16).'.inc';
		$ckfullfile = $cachedir.'/'.$ckfile;
		$ckfullfile_t = $cachedir.'/'.$ckfile.'.txt';
		$this->CacheFile = $ckfullfile;
		$this->TempMkTime = filemtime($filename);
		if(!file_exists($ckfullfile)||!file_exists($ckfullfile_t))
		{
			return false;
		}
		//檢測模板最後更新時間
		$fp = fopen($ckfullfile_t,'r');
		$time_info = trim(fgets($fp,64));
		fclose($fp);
		if($time_info != $this->TempMkTime)
		{
			return false;
		}
		//引入緩衝數組
		include($this->CacheFile);
		//把緩衝數組內容讀入類
		if(isset($z) && is_array($z))
		{
			foreach($z as $k=>$v)
			{
				$this->Count++;
				$ctag = new DedeTAg();
				$ctag->CAttribute = new DedeAttribute();
				$ctag->IsReplace = FALSE;
				$ctag->TagName = $v[0];
				$ctag->InnerText = $v[1];
				$ctag->StartPos = $v[2];
				$ctag->EndPos = $v[3];
				$ctag->TagValue = '';
				$ctag->TagID = $k;
				if(isset($v[4]) && is_array($v[4]))
				{
					$i = 0;
					foreach($v[4] as $k=>$v)
					{
						$ctag->CAttribute->Count++;
						$ctag->CAttribute->Items[$k]=$v;
					}
				}
				$this->CTags[$this->Count] = $ctag;
			}
		}
		else
		{
			//模板沒有緩衝數組
			$this->CTags = '';
			$this->Count = -1;
		}
		return true;
	}
	//寫入緩衝
	function SaveCache()
	{
		$fp = fopen($this->CacheFile.'.txt',"w");
		fwrite($fp,$this->TempMkTime."/n");
		fclose($fp);
		$fp = fopen($this->CacheFile,"w");
		flock($fp,3);
		fwrite($fp,'<'.'?php'."/r/n");
		if(is_array($this->CTags))
		{
			foreach($this->CTags as $tid=>$ctag)
			{
				$arrayValue = 'Array("'.$ctag->TagName.'",';
				$arrayValue .= '"'.str_replace('$','/$',str_replace("/r","//r",str_replace("/n","//n",str_replace('"','/"',str_replace("//","////",$ctag->InnerText))))).'"';
				$arrayValue .= ",{$ctag->StartPos},{$ctag->EndPos});";
				fwrite($fp,"/$z[$tid]={$arrayValue}/n");
				if(is_array($ctag->CAttribute->Items))
				{
					foreach($ctag->CAttribute->Items as $k=>$v)
					{
						$v = str_replace("//","////",$v);
						$v = str_replace('"',"//".'"',$v);
						$v = str_replace('$','/$',$v);
						$k = trim(str_replace("'","",$k));
						if($k=="")
						{
							continue;
						}
						if($k!='tagname')
						{
							fwrite($fp,"/$z[$tid][4]['$k']=/"$v/";/n");
						}
					}
				}
			}
		}
		fwrite($fp,"/n".'?'.'>');
		fclose($fp);
	}
	//載入模板文件
	function LoadTemplate($filename)
	{
		$this->SetDefault();	//重置成員變量或Clear
		
		//打開模板文件進行讀取
		$fp = @fopen($filename,"r") or die(" Template Not Found! ".$filename."<br />");	
				
		while($line = fgets($fp,1024))
		{
			/*
			 * 賦值運算將原變量的值拷貝到新變量中(傳值賦值),所以改變其中一個並不影響另一個。
			 * 這也適合於在很密集的循環中拷貝一些值例如大數組。也可以使用引用賦值,用 $var = &$othervar; 語法。
			 * 引用賦值意味着兩個變量都指向同一個數據,沒有任何數據的拷貝。
			 * 
			 * .= 相當於 +=
			 */
			$this->SourceString .= $line;
		}
		fclose($fp);	//關閉文件
		
		//如果模板緩衝文件存在返回null,否則進行模板文件解析
		if($this->LoadCache($filename))
		{
			return '';
		}
		else
		{
			$this->ParseTemplet();	//解析模板
		}
	}
	function LoadTemplet($filename)
	{
		$this->LoadTemplate($filename);
	}
	function LoadFile($filename)
	{
		$this->LoadTemplate($filename);
	}
	//載入模板字符串
	function LoadSource($str)
	{
		$this->SetDefault();
		$this->SourceString = $str;
		$this->IsCache = FALSE;
		$this->ParseTemplet();
	}
	function LoadString($str)
	{
		$this->LoadSource($str);
	}
	//獲得指定名稱的Tag的ID(如果有多個同名的Tag,則取沒有被取代爲內容的第一個Tag)
	function GetTagID($str)
	{
		if($this->Count==-1)
		{
			return -1;
		}
		if($this->CharToLow)
		{
			$str=strtolower($str);
		}
		foreach($this->CTags as $id=>$CTag)
		{
			if($CTag->TagName==$str && !$CTag->IsReplace)
			{
				return $id;
				break;
			}
		}
		return -1;
	}
	//獲得指定名稱的CTag數據類(如果有多個同名的Tag,則取沒有被分配內容的第一個Tag)
	function GetTag($str)
	{
		if($this->Count==-1)
		{
			return '';
		}
		if($this->CharToLow)
		{
			$str=strtolower($str);
		}
		foreach($this->CTags as $id=>$CTag)
		{
			if($CTag->TagName==$str && !$CTag->IsReplace)
			{
				return $CTag;
				break;
			}
		}
		return '';
	}
	function GetTagByName($str)
	{
		return $this->GetTag($str);
	}
	//獲得指定ID的CTag數據類
	function GetTagByID($id)
	{
		if(isset($this->CTags[$id]))
		{
			return $this->CTags[$id];
		}
		else
		{
			return '';
		}
	}
	//給_vars數組傳遞一個元素
	function AssignVar($vname,$vvalue)
	{
		if(!isset($_sys_globals['define']))
		{
			$_sys_globals['define'] = 'yes';
		}
		$_sys_globals[$vname] = $vvalue;
	}
	//分配指定ID的標記的值
	function Assign($i,$str,$runfunc = true)
	{
		if(isset($this->CTags[$i]))
		{
			$this->CTags[$i]->IsReplace = true;
			$this->CTags[$i]->TagValue = $str;
			if( $this->CTags[$i]->GetAtt('function')!='' && $runfunc )
			{
				$this->CTags[$i]->TagValue = $this->EvalFunc( $str, $this->CTags[$i]->GetAtt('function'),$this->CTags[$i] );
			}
		}
	}
	//分配指定名稱的標記的值,如果標記包含屬性,請不要用此函數
	function AssignName($tagname,$str)
	{
		foreach($this->CTags as $id=>$CTag)
		{
			if($CTag->TagName==$tagname)
			{
				$this->Assign($id,$str);
			}
		}
	}
	//處理特殊標記
	function AssignSysTag()
	{
		global $_sys_globals;
		for($i=0;$i<=$this->Count;$i++)
		{
			$CTag = $this->CTags[$i];
			$str = '';
			//獲取一個外部變量
			if( $CTag->TagName == 'global' )
			{
				$str = $this->GetGlobals($CTag->GetAtt('name'));
				if( $this->CTags[$i]->GetAtt('function')!='' )
				{
					$str = $this->EvalFunc( $this->CTags[$i]->TagValue, $this->CTags[$i]->GetAtt('function'),$this->CTags[$i] );
				}
				$this->CTags[$i]->IsReplace = true;
				$this->CTags[$i]->TagValue = $str;
			}
			//引入靜態文件
			else if( $CTag->TagName == 'include' )
			{
				$filename = ($CTag->GetAtt('file')=='' ? $CTag->GetAtt('filename') : $CTag->GetAtt('file') );
				$str = $this->IncludeFile($filename,$CTag->GetAtt('ismake'));
				$this->CTags[$i]->IsReplace = true;
				$this->CTags[$i]->TagValue = $str;
			}
			//循環一個普通數組
			else if( $CTag->TagName == 'foreach' )
			{
				$arr = $this->CTags[$i]->GetAtt('array');
				if(isset($GLOBALS[$arr]))
				{
					foreach($GLOBALS[$arr] as $k=>$v)
					{
						$istr = '';
						$istr .= preg_replace("//[field:key([/r/n/t/f ]+)///]/is",$k,$this->CTags[$i]->InnerText);
						$str .= preg_replace("//[field:value([/r/n/t/f ]+)///]/is",$v,$istr);
					}
				}
				$this->CTags[$i]->IsReplace = true;
				$this->CTags[$i]->TagValue = $str;
			}
			//設置/獲取變量值
			else if( $CTag->TagName == 'var' )
			{
				$vname = $this->CTags[$i]->GetAtt('name');
				if($vname=='')
				{
					$str = '';
				}
				else if($this->CTags[$i]->GetAtt('value')!='')
				{
					$_vars[$vname] = $this->CTags[$i]->GetAtt('value');
				}
				else
				{
					$str = (isset($_vars[$vname]) ? $_vars[$vname] : '');
				}
				$this->CTags[$i]->IsReplace = true;
				$this->CTags[$i]->TagValue = $str;
			}
			//運行PHP接口
			if( $CTag->GetAtt('runphp') == 'yes' )
			{
				$this->RunPHP($CTag,$i);
			}
			if(is_array($this->CTags[$i]->TagValue))
			{
				$this->CTags[$i]->TagValue = 'array';
			}
		}
	}
	//運行PHP代碼
	function RunPHP(&$refObj,$i)
	{
		$DedeMeValue = $phpcode = '';
		if($refObj->GetAtt('source')=='value')
		{
			$phpcode = $this->CTags[$i]->TagValue;
		}
		else
		{
			$DedeMeValue = $this->CTags[$i]->TagValue;
			$phpcode = $refObj->GetInnerText();
		}
		$phpcode = eregi_replace("'@me'|/"@me/"|@me",'$DedeMeValue',$phpcode);
		@eval($phpcode); //or die("<xmp>$phpcode</xmp>");
		$this->CTags[$i]->TagValue = $DedeMeValue;
		$this->CTags[$i]->IsReplace = true;
	}
	//把分析模板輸出到一個字符串中
	//不替換沒被處理的值
	function GetResultNP()
	{
		$ResultString = '';
		if($this->Count==-1)
		{
			return $this->SourceString;
		}
		$this->AssignSysTag();
		$nextTagEnd = 0;
		$strok = "";
		for($i=0;$i<=$this->Count;$i++)
		{
			if($this->CTags[$i]->GetValue()!="")
			{
				if($this->CTags[$i]->GetValue()=='#@Delete@#')
				{
					$this->CTags[$i]->TagValue = "";
				}
				$ResultString .= substr($this->SourceString,$nextTagEnd,$this->CTags[$i]->StartPos-$nextTagEnd);
				$ResultString .= $this->CTags[$i]->GetValue();
				$nextTagEnd = $this->CTags[$i]->EndPos;
			}
		}
		$slen = strlen($this->SourceString);
		if($slen>$nextTagEnd)
		{
			$ResultString .= substr($this->SourceString,$nextTagEnd,$slen-$nextTagEnd);
		}
		return $ResultString;
	}
	//把分析模板輸出到一個字符串中,並返回
	function GetResult()
	{
		$ResultString = '';
		if($this->Count==-1)
		{
			return $this->SourceString;
		}
		$this->AssignSysTag();
		$nextTagEnd = 0;
		$strok = "";
		for($i=0;$i<=$this->Count;$i++)
		{
			$ResultString .= substr($this->SourceString,$nextTagEnd,$this->CTags[$i]->StartPos-$nextTagEnd);
			$ResultString .= $this->CTags[$i]->GetValue();
			$nextTagEnd = $this->CTags[$i]->EndPos;
		}
		$slen = strlen($this->SourceString);
		if($slen>$nextTagEnd)
		{
			$ResultString .= substr($this->SourceString,$nextTagEnd,$slen-$nextTagEnd);
		}
		return $ResultString;
	}
	//直接輸出分析模板
	function Display()
	{
		echo $this->GetResult();
	}
	//把分析模板輸出爲文件
	function SaveTo($filename)
	{
		$fp = @fopen($filename,"w") or die("DedeTag Engine Create File False");
		fwrite($fp,$this->GetResult());
		fclose($fp);
	}
	//解析模板
	function ParseTemplet()
	{
		/*
		 * 說明:並置運算符 (.) 用於把兩個字符串值連接起來。
		 * 
		 * 參考:http://www.w3school.com.cn/php/php_string.asp
		 */
		$TagStartWord = $this->TagStartWord;						//標籤起始:{
		$TagEndWord = $this->TagEndWord;							//標籤結束:}
		$sPos = 0; 
		$ePos = 0;
		$FullTagStartWord =  $TagStartWord.$this->NameSpace.":";	//標籤”命名空間“ 起始:	'{' + 'dede' + ':'
		$sTagEndWord =  $TagStartWord."/".$this->NameSpace.":";		//標籤”命名空間“結束:	'{' + '/' + 'dede' + ':'
		$eTagEndWord = "/".$TagEndWord;								//標籤""結束:			'/' + '}'
		$tsLen = strlen($FullTagStartWord);							//標籤“命名空間”長度 :	6
		$sourceLen=strlen($this->SourceString);						//模板字符串長度
		
		/*
		 * 如果“模板字符串”小於或者等於 6 + 3,程序返回。
		 */
		if( $sourceLen <= ($tsLen + 3) )
		{
			return;
		}
		
		$cAtt = new DedeAttributeParse();		//屬性解析器類
		$cAtt->charToLow = $this->CharToLow; 	// true表示對屬性和標記名稱不區分大小寫
		//遍歷模板字符串,請取標記及其屬性信息
		for($i=0; $i < $sourceLen; $i++)
		{
			$tTagName = '';
			//如果不進行此判斷,將無法識別相連的兩個標記
			if($i-1 >= 0)
			{
				$ss = $i-1;
			}
			else
			{
				$ss = 0;
			}
			
			/*
			 * strpos() 函數返回字符串在另一個字符串中第一次出現的位置。
			 * 
			 * strpos(string,find,start)
			 * 
			 * string 必需。規定被搜索的字符串。 
			 * find 必需。規定要查找的字符。 
			 * start 可選。規定開始搜索的位置。 
			 */
			$sPos = strpos($this->SourceString,$FullTagStartWord,$ss);	//從"模板字符串"0位置開始,查找 "{dede:" 第一個出現位置
			$isTag = $sPos;		//標籤0 false 非零true
			
			/*
			 * 第一次循環時,從“模板字符串”中截取字符 {dede: ,並判斷是否與 標籤{dede:相等。
			 */
			if($i==0)
			{
				$headerTag = substr($this->SourceString,0,strlen($FullTagStartWord));
				if($headerTag==$FullTagStartWord)
				{
					$isTag=true;	//設置爲True 爲結束次循環
					$sPos=0;		//模板標籤出現位置 重置0;表示不是標籤
				}
			}
			
			//非{dede: 模板標籤結束退出循環。
			if($isTag===FALSE)
			{
				break;
			}
			//判斷是否已經到倒數第三個字符(可能性機率極小,取消此邏輯)
			/*
			if($sPos > ($sourceLen-$tsLen-3) )
			{
				break;
			}
			*/
			
			/*
			 * 標籤起始位置 + 標籤命名空間長度(6)。
			 * 標籤起始位置 + 標籤命名空間長度(6) + 標記名稱最大值
			 * 
			 * 匹配 {dede: + 【 /,空格,換行符,製表符】 部分。
			 */
			for($j=($sPos+$tsLen);$j<($sPos+$tsLen+$this->TagMaxLen);$j++)
			{
				//判斷{標籤起始位置 + 標籤命名空間長度(6)} 大於  模板全部字符長度,則退出循環
				if($j>($sourceLen-1))
				{
					break;
				}
				//當前字符串不是標籤結束符號 And 正則表達式匹配當前字符滿足【 匹配 /,空格,換行符,製表符】即 [/ /t/r/n]
				else if( ereg("[/ /t/r/n]",$this->SourceString[$j]) || $this->SourceString[$j] == $this->TagEndWord )
				{
					break;	//退出當前層循環嵌套,繼續主循環嵌套
				}
				else
				{
					$tTagName .= $this->SourceString[$j];	//標籤名稱字符累加
				}
			}
			
			//標籤字符不等於 null
			if($tTagName!='')
			{
				$i = $sPos+$tsLen;
				$endPos = -1;
				$fullTagEndWordThis = $sTagEndWord.$tTagName.$TagEndWord;
				
				$e1 = strpos($this->SourceString,$eTagEndWord, $i);
				$e2 = strpos($this->SourceString,$FullTagStartWord, $i);
				$e3 = strpos($this->SourceString,$fullTagEndWordThis,$i);
				
				//$eTagEndWord = /} $FullTagStartWord = {tag: $fullTagEndWordThis = {/tag:xxx]
				
				$e1 = trim($e1); $e2 = trim($e2); $e3 = trim($e3);
				$e1 = ($e1=='' ? '-1' : $e1);
				$e2 = ($e2=='' ? '-1' : $e2);
				$e3 = ($e3=='' ? '-1' : $e3);
				//not found '{/tag:'
				if($e3==-1) 
				{
					$endPos = $e1;
					$elen = $endPos + strlen($eTagEndWord);
				}
				//not found '/}'
				else if($e1==-1) 
				{
					$endPos = $e3;
					$elen = $endPos + strlen($fullTagEndWordThis);
				}
				//found '/}' and found '{/dede:'
				else
				{
					//if '/}' more near '{dede:'、'{/dede:' , end tag is '/}', else is '{/dede:'
					if($e1 < $e2 &&  $e1 < $e3 )
					{
						$endPos = $e1;
						$elen = $endPos + strlen($eTagEndWord);
					}
					else
					{
						$endPos = $e3;
						$elen = $endPos + strlen($fullTagEndWordThis);
					}
				}
				//not found end tag , error
				if($endPos==-1)
				{
					echo "Character postion $sPos, '$tTagName' Error!<br />/r/n";
					break;
				}
				$i = $elen;
				$ePos = $endPos;
				//分析所找到的標記位置等信息
				$attStr = '';
				$innerText = '';
				$startInner = 0;
				for($j=($sPos+$tsLen);$j < $ePos;$j++)
				{
					if($startInner==0 && ($this->SourceString[$j]==$TagEndWord && $this->SourceString[$j-1]!="//") )
					{
						$startInner=1;
						continue;
					}
					if($startInner==0)
					{
						$attStr .= $this->SourceString[$j];
					}
					else
					{
						$innerText .= $this->SourceString[$j];
					}
				}
				//echo "<xmp>$attStr</xmp>/r/n";
				$cAtt->SetSource($attStr);
				if($cAtt->cAttributes->GetTagName()!='')
				{
					$this->Count++;
					$CDTag = new DedeTag();
					$CDTag->TagName = $cAtt->cAttributes->GetTagName();
					$CDTag->StartPos = $sPos;
					$CDTag->EndPos = $i;
					$CDTag->CAttribute = $cAtt->cAttributes;
					$CDTag->IsReplace = FALSE;
					$CDTag->TagID = $this->Count;
					$CDTag->InnerText = $innerText;
					$this->CTags[$this->Count] = $CDTag;
				}
			}
			else	//標籤字符串等於null,退出循環
			{
				$i = $sPos+$tsLen;
				break;
			}
		}//結束遍歷模板字符串
		//是否使用緩存
		if($this->IsCache)
		{
			$this->SaveCache();	//寫入緩存
		}
	}
	//處理某字段的函數
	function EvalFunc($fieldvalue,$functionname,&$refObj)
	{
		$DedeFieldValue = $fieldvalue;
		$functionname = str_replace("{/"","[/"",$functionname);
		$functionname = str_replace("/"}","/"]",$functionname);
		$functionname = eregi_replace("'@me'|/"@me/"|@me",'$DedeFieldValue',$functionname);
		$functionname = "/$DedeFieldValue = ".$functionname;
		@eval($functionname.";"); //or die("<xmp>$functionname</xmp>");
		if(empty($DedeFieldValue))
		{
			return '';
		}
		else
		{
			return $DedeFieldValue;
		}
	}
	//獲得一個外部變量
	function GetGlobals($varname)
	{
		$varname = trim($varname);
		//禁止在模板文件讀取數據庫密碼
		if($varname=="dbuserpwd"||$varname=="cfg_dbpwd")
		{
			return "";
		}
		//正常情況
		if(isset($GLOBALS[$varname]))
		{
			return $GLOBALS[$varname];
		}
		else
		{
			return "";
		}
	}
	//引入文件
	function IncludeFile($filename,$ismake='no')
	{
		global $cfg_df_style;
		$restr = '';
		if($filename=='')
		{
			return '';
		}
		if( file_exists(DEDEROOT."/templets/".$filename) )
		{
			$okfile = DEDEROOT."/templets/".$filename;
		}
		else if(file_exists(DEDEROOT.'/templets/'.$cfg_df_style.'/'.$filename) )
		{
			$okfile = DEDEROOT.'/templets/'.$cfg_df_style.'/'.$filename;
		}
		else
		{
			return "無法在這個位置找到: $filename";
		}
		//編譯
		if($ismake!="no")
		{
			require_once(DEDEINC."/channelunit.func.php");
			$dtp = new DedeTagParse();
			$dtp->LoadTemplet($okfile);
			MakeOneTag($dtp,$this->refObj);
			$restr = $dtp->GetResult();
		}
		else
		{
			$fp = @fopen($okfile,"r");
			while($line=fgets($fp,1024)) $restr.=$line;
			fclose($fp);
		}
		return $restr;
	}
}
/**********************************************
//class DedeAttribute Dede模板標記屬性集合
function c____DedeAttribute();
**********************************************/
//屬性的數據描述
class DedeAttribute
{
	var $Count = -1;
	var $Items = ""; //屬性元素的集合
	//獲得某個屬性
	function GetAtt($str)
	{
		if($str=="")
		{
			return "";
		}
		if(isset($this->Items[$str]))
		{
			return $this->Items[$str];
		}
		else
		{
			return "";
		}
	}
	//同上
	function GetAttribute($str)
	{
		return $this->GetAtt($str);
	}
	//判斷屬性是否存在
	function IsAttribute($str)
	{
		if(isset($this->Items[$str])) return true;
		else return false;
	}
	//獲得標記名稱
	function GetTagName()
	{
		return $this->GetAtt("tagname");
	}
	// 獲得屬性個數
	function GetCount()
	{
		return $this->Count+1;
	}
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章