Browse Source

Support de l'UTF-8.
- changement de la base de données pour la passer en UTF-8. Ceci nécessite la réinstallation complète de Leed après avoir supprimé toutes les tables ou le passage manuel de toutes les tables et tous les champs textes en utf8_general_ci, en sachant que les données déjà existantes pourront avoir des problèmes.
- adaptation du code pour enlever les htmlentities et addslashes non nécessaires. Ajout d'une méthode à MysqlEntity pour sécuriser les données à la place.
- diverses modifications pour ne plus avoir d'addslashes (en particulier, suppression de eval() )

Cette modification devrait corriger la plupart des problèmes d'encodage dans les flux, les évènements des flux et les noms de dossier.



git-svn-id: http://projet.idleman.fr/leed.svn@93 cbb609ad-8cd9-463b-aa97-3ec7c4f0f680

Alef Burzmali 8 years ago
parent
commit
e95b142390
8 changed files with 102 additions and 72 deletions
  1. 3 7
      Event.class.php
  2. 12 9
      Feed.class.php
  3. 3 2
      Folder.class.php
  4. 3 3
      Functions.class.php
  5. 2 1
      MysqlConnector.class.php
  6. 73 44
      MysqlEntity.class.php
  7. 3 3
      action.php
  8. 3 3
      common.php

+ 3 - 7
Event.class.php

@@ -73,13 +73,11 @@ class Event extends MysqlEntity{
 	}
 
 	function getDescription(){
-		// return utf8_encode($this->description);
 		return $this->description;
 	}
 
 	function setDescription($description,$encoding = true){
-		$this->description =  str_replace('’','\'',$description);
-		if($encoding)$this->description = utf8_decode($this->description);
+		$this->description = $description;
 	}
 
 	function getPubdate($format=false){
@@ -127,13 +125,11 @@ class Event extends MysqlEntity{
 	}
 
 	function getContent(){
-		//return utf8_encode($this->content);
 		return $this->content;
 	}
 
 	function setContent($content,$encoding=true){
-		$this->content = str_replace('’','\'',$content);
-		if($encoding)$this->content = utf8_decode($this->content);
+		$this->content = $content;
 	}
 
 
@@ -167,4 +163,4 @@ class Event extends MysqlEntity{
 
 }
 
-?>
+?>

+ 12 - 9
Feed.class.php

@@ -41,12 +41,15 @@ class Feed extends MysqlEntity{
 
 		
 		$feed = new SimplePie();
+		
+
 		$feed->set_feed_url($this->url);
 
 		$feed->set_useragent('Mozilla/4.0 Leed (LightFeed Agrgegator) '.VERSION_NAME.' by idleman http://projet.idleman.fr/leed');
 
 		$feed->init();
 		$feed->handle_content_type();
+		
 
 		if($this->name=='') $this->name = $feed->get_title();
 		if($this->name=='') $this->name = $this->url;
@@ -62,18 +65,18 @@ class Feed extends MysqlEntity{
 			
 				//Definition du GUID : 
 			
-				$alreadyParsed = $eventManager->rowCount(array('guid'=>htmlentities($item->get_id())));
+				$alreadyParsed = $eventManager->rowCount(array('guid'=>$this->secure($item->get_id(), 'guid')));
 				
 				if($alreadyParsed==0 && $iEvents<100){
 					$event = new Event();
 					$event->setGuid($item->get_id());
-					$event->setTitle($item->get_title());
+					$event->setTitle(html_entity_decode($item->get_title(), ENT_COMPAT, 'UTF-8'));
 					$event->setPubdate($item->get_date());
 					$event->setCreator( (is_object($item->get_author())?$item->get_author()->name:'Anonyme') );
 				
 					$event->setLink($item->get_permalink());
-					$event->setContent($item->get_content());
-					$event->setDescription($item->get_description());
+					$event->setContent(html_entity_decode($item->get_content(), ENT_COMPAT, 'UTF-8'));
+					$event->setDescription(html_entity_decode($item->get_description(), ENT_COMPAT, 'UTF-8'));
 					
 					if(trim($event->getDescription())=='')
 						$event->setDescription(substr($event->getContent(),0,300).'...<br><a href="'.$event->getLink().'">Lire la suite de l\'article</a>');
@@ -117,11 +120,11 @@ class Feed extends MysqlEntity{
 	}
 
 	function getDescription(){
-		return stripslashes($this->description);
+		return $this->description;
 	}
 
 	function setDescription($description){
-		$this->description = html_entity_decode($description);
+		$this->description = $description;
 	}
 	function getWebSite(){
 		return $this->website;
@@ -148,7 +151,7 @@ class Feed extends MysqlEntity{
 	}
 
 	function setName($name){
-		$this->name = html_entity_decode($name);
+		$this->name = $name;
 	}
 
 
@@ -176,7 +179,7 @@ class Feed extends MysqlEntity{
 		$results = Feed::customQuery("SELECT ".MYSQL_PREFIX."feed.name AS name, ".MYSQL_PREFIX."feed.id   AS id, ".MYSQL_PREFIX."feed.url  AS url, ".MYSQL_PREFIX."folder.id AS folder FROM ".MYSQL_PREFIX."feed INNER JOIN ".MYSQL_PREFIX."folder ON ( ".MYSQL_PREFIX."feed.folder = ".MYSQL_PREFIX."folder.id ) ORDER BY ".MYSQL_PREFIX."feed.name ;");
 		if($results!=false){
 			while($item = mysql_fetch_array($results)){
-				$name = html_entity_decode($item['name']);
+				$name = $item['name'];
 				$feedsIdMap[$item['id']]['name'] = $name;
 				
 
@@ -212,4 +215,4 @@ class Feed extends MysqlEntity{
 
 }
 
-?>
+?>

+ 3 - 2
Folder.class.php

@@ -34,7 +34,8 @@ class Folder extends MysqlEntity{
 			while($item = mysql_fetch_array($results)){
 				$object = new Event();
 					foreach($object->getObject_fields() as $field=>$type){
-						if(isset($item[$field])) eval('$object->set'.ucFirst($field) .'(html_entity_decode(\''. addslashes($item[$field]).'\'),false);');
+						$setter = 'set'.ucFirst($field);
+						if(isset($item[$field])) $object->$setter($item[$field]);
 					}
 					$objects[] = $object;
 					unset($object);
@@ -95,4 +96,4 @@ class Folder extends MysqlEntity{
 
 }
 
-?>
+?>

+ 3 - 3
Functions.class.php

@@ -20,8 +20,8 @@ class Functions
 	 */
 
 	public static function secure($var,$level = 1){
-		$var = htmlentities($var, ENT_QUOTES, "UTF-8");
-		if($level<1)$var = mysql_escape_string($var);
+		$var = htmlspecialchars($var, ENT_QUOTES, "UTF-8");
+		if($level<1)$var = mysql_real_escape_string($var);
 		if($level<2)$var = addslashes($var);
 		return $var;
 	}
@@ -381,4 +381,4 @@ class Functions
 
 
 }
-?>
+?>

+ 2 - 1
MysqlConnector.class.php

@@ -45,6 +45,7 @@ class MysqlConnector
 	
 	public function connect(){
 		$this->connection = mysql_connect(MYSQL_HOST,MYSQL_LOGIN,MYSQL_MDP);
+		mysql_query('SET NAMES utf8');
 		mysql_select_db(MYSQL_BDD,$this->connection);
 	}
 	
@@ -195,4 +196,4 @@ class MysqlConnector
 		$this->port = $port;
 	}
 }
-?>
+?>

+ 73 - 44
MysqlEntity.class.php

@@ -22,10 +22,10 @@ class MysqlEntity
 		switch($type){
 			case 'string':
 			case 'timestamp':
-				$return = 'VARCHAR(225)';
+				$return = 'VARCHAR(225) CHARACTER SET utf8 COLLATE utf8_general_ci';
 			break;
 			case 'longstring':
-				$return = 'TEXT';
+				$return = 'TEXT CHARACTER SET utf8 COLLATE utf8_general_ci';
 			break;
 			case 'key':
 				$return = 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY';
@@ -38,7 +38,35 @@ class MysqlEntity
 				$return = 'INT(1)';
 			break;
 			default;
-				$return = 'TEXT';
+				$return = 'TEXT CHARACTER SET utf8 COLLATE utf8_general_ci';
+			break;
+		}
+		return $return ;
+	}
+	
+	/**
+	 * Protège une variable pour MySQL
+	 */
+	protected function secure($value, $field){
+		$type = false;
+		
+		// ce champ n'existe pas : on le considère comme une chaîne de caractères
+		if (isset($this->object_fields[$field]))
+			$type = $this->object_fields[$field];
+		
+		$return = false;
+		switch($type){
+			case 'key':
+			case 'object':
+			case 'integer':
+			case 'boolean':
+				$return = intval($value);
+			break;
+			case 'string':
+			case 'timestamp':
+			case 'longstring':
+			default;
+				$return = mysql_real_escape_string((string)$value);
 			break;
 		}
 		return $return ;
@@ -58,10 +86,10 @@ class MysqlEntity
 	* Methode de suppression de l'entité
 	* @author Valentin CARRUESCO
 	* @category manipulation SQL
-	* @param <String> $debug='false' active le debug mode (0 ou 1)
+	* @param <String> $debug=false active le debug mode (0 ou 1)
 	* @return Aucun retour
 	*/
-	public function destroy($debug='false')
+	public function destroy($debug=false)
 	{
 		$query = 'DROP TABLE IF EXISTS '.MYSQL_PREFIX.$this->TABLE_NAME.';';
 		if($this->debug)echo '<hr>'.$this->CLASS_NAME.' ('.__METHOD__ .') : Requete --> '.$query.'<br>'.mysql_error();
@@ -72,10 +100,10 @@ class MysqlEntity
 	* Methode de nettoyage de l'entité
 	* @author Valentin CARRUESCO
 	* @category manipulation SQL
-	* @param <String> $debug='false' active le debug mode (0 ou 1)
+	* @param <String> $debug=false active le debug mode (0 ou 1)
 	* @return Aucun retour
 	*/
-	public function truncate($debug='false')
+	public function truncate($debug=false)
 	{
 			$query = 'TRUNCATE TABLE '.MYSQL_PREFIX.$this->TABLE_NAME.';';
 			if($this->debug)echo '<hr>'.$this->CLASS_NAME.' ('.__METHOD__ .') : Requete --> '.$query.'<br>'.mysql_error();
@@ -86,10 +114,10 @@ class MysqlEntity
 	* Methode de creation de l'entité
 	* @author Valentin CARRUESCO
 	* @category manipulation SQL
-	* @param <String> $debug='false' active le debug mode (0 ou 1)
+	* @param <String> $debug=false active le debug mode (0 ou 1)
 	* @return Aucun retour
 	*/
-	public function create($debug='false'){
+	public function create($debug=false){
 		$query = 'CREATE TABLE IF NOT EXISTS `'.MYSQL_PREFIX.$this->TABLE_NAME.'` (';
 
 		$i=false;
@@ -99,7 +127,10 @@ class MysqlEntity
 			
 		}
 
-		$query .= ');';
+		$query .= ')
+		ENGINE InnoDB,
+		DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci
+		;';
 		if($this->debug)echo '<hr>'.$this->CLASS_NAME.' ('.__METHOD__ .') : Requete --> '.$query.'<br>'.mysql_error();
 		$myQuery = mysql_query($query) or die(mysql_error());
 	}
@@ -120,7 +151,6 @@ class MysqlEntity
 			$query .=') select';
 			$u = false;
 
-			
 			foreach($events as $event){
 				
 					if($u){$query .=' union select ';}else{$u=true;}
@@ -129,7 +159,7 @@ class MysqlEntity
 					foreach($event->object_fields as $field=>$type){
 						if($type!='key'){
 							if($i){$query .=',';}else{$i=true;}
-							$query .='"'.eval('return htmlentities($event->'.$field.');').'"';
+							$query .='"'.$this->secure($event->$field, $field).'"';
 						}
 					}
 				
@@ -137,7 +167,7 @@ class MysqlEntity
 			}
 
 			$query .=';';
-
+			if($this->debug)echo '<hr>'.$this->CLASS_NAME.' ('.__METHOD__ .') : Requete --> '.$query.'<br>'.mysql_error();
 		
 		mysql_query($query) or die(mysql_error());
 
@@ -158,8 +188,8 @@ class MysqlEntity
 			$i=false;
 			foreach($this->object_fields as $field=>$type){
 				if($i){$query .=',';}else{$i=true;}
-				$id = eval('return htmlentities($this->'.$field.');');
-				$query .= '`'.$field.'`="'.$id.'"';
+				$id = $this->$field;
+				$query .= '`'.$field.'`="'.$this->secure($id, $field).'"';
 			}
 
 			$query .= ' WHERE `id`="'.$this->id.'";';
@@ -174,12 +204,12 @@ class MysqlEntity
 			$i=false;
 			foreach($this->object_fields as $field=>$type){
 				if($i){$query .=',';}else{$i=true;}
-				$query .='"'.eval('return htmlentities($this->'.$field.');').'"';
+				$query .='"'.$this->secure($this->$field, $field).'"';
 			}
 
 			$query .=');';
 		}
-		//echo '<hr>'.$this->CLASS_NAME.' ('.__METHOD__ .') : Requete --> '.$query.'<br>'.mysql_error();
+		if($this->debug)echo '<hr>'.$this->CLASS_NAME.' ('.__METHOD__ .') : Requete --> '.$query.'<br>'.mysql_error();
 		mysql_query($query)or die(mysql_error());
 		$this->id =  (!isset($this->id)?mysql_insert_id():$this->id);
 	}
@@ -191,25 +221,25 @@ class MysqlEntity
 	* @param <Array> $colonnes=>$valeurs
 	* @param <Array> $colonnes (WHERE) =>$valeurs (WHERE)
 	* @param <String> $operation="=" definis le type d'operateur pour la requete select
-	* @param <String> $debug='false' active le debug mode (0 ou 1)
+	* @param <String> $debug=false active le debug mode (0 ou 1)
 	* @return Aucun retour
 	*/
-	public function change($columns,$columns2,$operation='=',$debug='false'){
+	public function change($columns,$columns2,$operation='=',$debug=false){
 		$query = 'UPDATE `'.MYSQL_PREFIX.$this->TABLE_NAME.'` SET ';
 		$i=false;
 		foreach ($columns as $column=>$value){
 			if($i){$query .=',';}else{$i=true;}
-			$query .= '`'.$column.'`="'.$value.'" ';
+			$query .= '`'.$column.'`="'.$this->secure($value, $column).'" ';
 		}
 		$query .=' WHERE '; 
 
 		$i = false;
 		foreach ($columns2 as $column=>$value){
 			if($i){$query .='AND ';}else{$i=true;}
-			$query .= '`'.$column.'`'.$operation.'"'.$value.'" ';
+			$query .= '`'.$column.'`'.$operation.'"'.$this->secure($value, $column).'" ';
 			
 		}
-		//echo '<hr>'.$this->CLASS_NAME.' ('.__METHOD__ .') : Requete --> '.$query.'<br>'.mysql_error();
+		if($this->debug)echo '<hr>'.$this->CLASS_NAME.' ('.__METHOD__ .') : Requete --> '.$query.'<br>'.mysql_error();
 		mysql_query($query)or die(mysql_error());
 	}
 
@@ -219,11 +249,11 @@ class MysqlEntity
 	* @category manipulation SQL
 	* @param <String> $ordre=null
 	* @param <String> $limite=null
-	* @param <String> $debug='false' active le debug mode (0 ou 1)
+	* @param <String> $debug=false active le debug mode (0 ou 1)
 	* @return <Array<Entity>> $Entity
 	*/
-	public function populate($order='null',$limit='null',$debug='false'){
-		eval('$results = '.$this->CLASS_NAME.'::loadAll(array(),"'.$order.'",'.$limit.',\'=\','.$debug.');');
+	public function populate($order=null,$limit=null,$debug=false){
+		$results = $this->loadAll(array(),$order,$limit,'=',$debug);
 		return $results;
 	}
 
@@ -236,10 +266,10 @@ class MysqlEntity
 	* @param <String> $ordre=null
 	* @param <String> $limite=null
 	* @param <String> $operation="=" definis le type d'operateur pour la requete select
-	* @param <String> $debug='false' active le debug mode (0 ou 1)
+	* @param <String> $debug=false active le debug mode (0 ou 1)
 	* @return <Array<Entity>> $Entity
 	*/
-	public function loadAll($columns,$order=null,$limit=null,$operation="=",$debug='false',$selColumn='*'){
+	public function loadAll($columns,$order=null,$limit=null,$operation="=",$debug=false,$selColumn='*'){
 		$objects = array();
 		$whereClause = '';
 	
@@ -249,7 +279,7 @@ class MysqlEntity
 				foreach($columns as $column=>$value){
 
 					if($i){$whereClause .=' AND ';}else{$i=true;}
-					$whereClause .= '`'.$column.'`'.$operation.'"'.$value.'"';
+					$whereClause .= '`'.$column.'`'.$operation.'"'.$this->secure($value, $column).'"';
 				}
 			}
 			$query = 'SELECT '.$selColumn.' FROM `'.MYSQL_PREFIX.$this->TABLE_NAME.'` '.$whereClause.' ';
@@ -257,13 +287,13 @@ class MysqlEntity
 			if($limit!=null) $query .='LIMIT '.$limit.' ';
 			$query .=';';
 
-			// echo '<br>'.__METHOD__.' : Requete --> '.$query.'<br>';
+			if($this->debug)echo '<hr>'.$this->CLASS_NAME.' ('.__METHOD__ .') : Requete --> '.$query.'<br>'.mysql_error();
 			$execQuery = mysql_query($query) or die(mysql_error());
 			while($queryReturn = mysql_fetch_assoc($execQuery)){
 
-				$object = eval(' return new '.$this->CLASS_NAME.'();');
+				$object = new $this->CLASS_NAME();
 				foreach($this->object_fields as $field=>$type){
-					if(isset($queryReturn[$field])) eval('$object->'.$field .'= html_entity_decode(\''. addslashes($queryReturn[$field]).'\');');
+					if(isset($queryReturn[$field])) $object->$field = $queryReturn[$field];
 				}
 				$objects[] = $object;
 				unset($object);
@@ -271,8 +301,8 @@ class MysqlEntity
 			return $objects;
 	}
 
-		public function loadAllOnlyColumn($selColumn,$columns,$order=null,$limit=null,$operation="=",$debug='false'){
-		eval('$objects = $this->loadAll($columns,\''.$order.'\',\''.$limit.'\',\''.$operation.'\',\''.$debug.'\',\''.$selColumn.'\');');
+		public function loadAllOnlyColumn($selColumn,$columns,$order=null,$limit=null,$operation="=",$debug=false){
+		$objects = $this->loadAll($columns,$order,$limit,$operation,$debug,$selColumn);
 		if(count($objects)==0)$objects = array();
 		return $objects;
 	}
@@ -285,11 +315,11 @@ class MysqlEntity
 	* @param <Array> $colonnes (WHERE)
 	* @param <Array> $valeurs (WHERE)
 	* @param <String> $operation="=" definis le type d'operateur pour la requete select
-	* @param <String> $debug='false' active le debug mode (0 ou 1)
+	* @param <String> $debug=false active le debug mode (0 ou 1)
 	* @return <Entity> $Entity ou false si aucun objet n'est trouvé en base
 	*/
-	public function load($columns,$operation='=',$debug='false'){
-		eval('$objects = $this->loadAll($columns,null,\'1\',\''.$operation.'\',\''.$debug.'\');');
+	public function load($columns,$operation='=',$debug=false){
+		$objects = $this->loadAll($columns,null,1,$operation,$debug);
 		if(!isset($objects[0]))$objects[0] = false;
 		return $objects[0];
 	}
@@ -301,10 +331,10 @@ class MysqlEntity
 	* @param <Array> $colonnes (WHERE)
 	* @param <Array> $valeurs (WHERE)
 	* @param <String> $operation="=" definis le type d'operateur pour la requete select
-	* @param <String> $debug='false' active le debug mode (0 ou 1)
+	* @param <String> $debug=false active le debug mode (0 ou 1)
 	* @return <Entity> $Entity ou false si aucun objet n'est trouvé en base
 	*/
-	public function getById($id,$operation='=',$debug='false'){
+	public function getById($id,$operation='=',$debug=false){
 		return $this->load(array('id'=>$id),$operation,$debug);
 	}
 
@@ -312,7 +342,7 @@ class MysqlEntity
 	* Methode de comptage des éléments de l'entité
 	* @author Valentin CARRUESCO
 	* @category manipulation SQL
-	* @param <String> $debug='false' active le debug mode (0 ou 1)
+	* @param <String> $debug=false active le debug mode (0 ou 1)
 	* @return<Integer> nombre de ligne dans l'entité'
 	*/
 	public function rowCount($columns=null)
@@ -323,7 +353,7 @@ class MysqlEntity
 			$i=false;
 			foreach($columns as $column=>$value){
 					if($i){$whereClause .=' AND ';}else{$i=true;}
-					$whereClause .= '`'.$column.'`="'.$value.'"';
+					$whereClause .= '`'.$column.'`="'.$this->secure($value, $column).'"';
 			}
 		}
 		$query = 'SELECT COUNT(id) FROM '.MYSQL_PREFIX.$this->TABLE_NAME.$whereClause;
@@ -340,16 +370,16 @@ class MysqlEntity
 	* @param <Array> $colonnes (WHERE)
 	* @param <Array> $valeurs (WHERE)
 	* @param <String> $operation="=" definis le type d'operateur pour la requete select
-	* @param <String> $debug='false' active le debug mode (0 ou 1)
+	* @param <String> $debug=false active le debug mode (0 ou 1)
 	* @return Aucun retour
 	*/
-	public function delete($columns,$operation='=',$debug='false'){
+	public function delete($columns,$operation='=',$debug=false){
 		$whereClause = '';
 
 			$i=false;
 			foreach($columns as $column=>$value){
 				if($i){$whereClause .=' AND ';}else{$i=true;}
-				$whereClause .= '`'.$column.'`'.$operation.'"'.$value.'"';
+				$whereClause .= '`'.$column.'`'.$operation.'"'.$this->secure($value, $column).'"';
 			}
 			$query = 'DELETE FROM `'.MYSQL_PREFIX.$this->TABLE_NAME.'` WHERE '.$whereClause.' ;';
 			if($this->debug)echo '<hr>'.$this->CLASS_NAME.' ('.__METHOD__ .') : Requete --> '.$query.'<br>'.mysql_error();
@@ -395,6 +425,5 @@ class MysqlEntity
 	public function getObject_fields(){
 		return $this->object_fields;
 	}
-
 }
 ?>

+ 3 - 3
action.php

@@ -302,14 +302,14 @@ switch ($_['action']){
 	case 'renameFeed':
 		if($myUser==false) exit('Vous devez vous connecter pour cette action.');
 		if(isset($_['id'])){
-			$feedManager->change(array('name'=>utf8_encode(html_entity_decode($_['name']))),array('id'=>$_['id']));
+			$feedManager->change(array('name'=>$_['name']),array('id'=>$_['id']));
 		}
 	break;
 
 	case 'removeFolder':
 		if($myUser==false) exit('Vous devez vous connecter pour cette action.');
 		if(isset($_['id'])){
-			$eventManager->customExecute('DELETE FROM '.MYSQL_PREFIX.'event WHERE '.MYSQL_PREFIX.'event.feed in (SELECT '.MYSQL_PREFIX.'feed.id FROM '.MYSQL_PREFIX.'feed WHERE '.MYSQL_PREFIX.'feed.folder ='.$_['id'].') ;');
+			$eventManager->customExecute('DELETE FROM '.MYSQL_PREFIX.'event WHERE '.MYSQL_PREFIX.'event.feed in (SELECT '.MYSQL_PREFIX.'feed.id FROM '.MYSQL_PREFIX.'feed WHERE '.MYSQL_PREFIX.'feed.folder ='.intval($_['id']).') ;');
 			$feedManager->delete(array('folder'=>$_['id']));
 			$folderManager->delete(array('id'=>$_['id']));
 		}
@@ -372,4 +372,4 @@ switch ($_['action']){
 }
 
 
-?>
+?>

+ 3 - 3
common.php

@@ -55,12 +55,12 @@ $tpl->assign('synchronisationCode',$configurationManager->get('synchronisationCo
 //Récuperation et sécurisation de toutes les variables POST et GET
 $_ = array();
 foreach($_POST as $key=>$val){
-$_[$key]=Functions::secure($val);
+$_[$key]=Functions::secure($val, 2); // on ne veut pas d'addslashes
 }
 foreach($_GET as $key=>$val){
-$_[$key]=Functions::secure($val);
+$_[$key]=Functions::secure($val, 2); // on ne veut pas d'addslashes
 }
 
 $tpl->assign('_',$_);
 $tpl->assign('action','');
-?>
+?>