<?php
/**
 * Config_file class
 * 
 * Provides methods related to system modules. 
 * Handles read/write ini files 
 * 
 * @package linea21\core\config_files
 * @author $Author$ - linea21 <info@linea21.com>
 * @version $Id$ 
 * @access public
 * @license http://opensource.org/licenses/gpl-3.0.html
 * Configuration Management
 */

include_once('system/afiles/class.afiles.php');
include_once('../lib/lib_common.php');

// should be a singleton
// but we are getting some issues converting it
// when using the installer
class config_file {

  var $file_config = "config.ini"; // fichier define_config
  var $file_release = "release.ini";  // fichier define_release

  var $extra_configfile = false;
  var $section_main = "config";
  var $section_theme = "theme";  // section theme dans le fichier de configuration config
  var $section_database = "database";  // section database dans le fichier de configuration define_release
  var $config_path = '../config/';
  var $ini_path = '../config/';
  var $backup_config_path = '../tmp/backup/config/';
  var $authorized_release_sections = array ('LANGUAGE_SETTINGS','NOTIFICATION_SETTINGS','LOCALES_SETTINGS','MODULES_SETTINGS','DEBUG_SETTINGS', 'COMMENT_SETTINGS', 'SYSTEM_SETTINGS', 'SERVER_SETTINGS','MAIL_SETTINGS','MAIL_INFORMATIONS', 'SUPPORT_SETTINGS','LOGS_SETTINGS'); //sections autorisées dans le fichier release
  var $authorized_db_sections = array ('DATABASE_SETTINGS'); //sections autorisées dans le fichier release
  var $authorized_theme_sections = array ('THEME_SETTINGS'); //sections autorisées dans le fichier release
  var $php_extension = ".php";
  var $config_params;
  var $release_params;
  var $a_file;
  var $theme_settings='THEME_SETTINGS';
  var $theme_public='THEME_PUBLIC';
  var $theme_list_public='THEME_LIST_PUBLIC';
  var $theme_admin='THEME_ADMIN';
  var $theme_list_admin='THEME_LIST_ADMIN';
  protected $dispatcher = null;

  /**
   * config_file::__construct()
   * constructeur de classe
   * @access public
   * @return void
   */
  public function __construct( $extraconfigfile = false)
  {
    $this->a_file=new afiles;
    $this->dispatcher = $GLOBALS['dispatcher'];

    if(is_string($extraconfigfile)) {
    	$this->extra_configfile = $extraconfigfile;
    }

    //charge les paramètres et les place en constante globale par define
    $this->load_config_ini_file();
    $this->load_release_ini_file();
  }

  /**
   * config_file::init()
   * charge les paramètres des fichiers ini et les place en constante globale avec define
   * @access public
   * @param void
   * @return void
   */
  public function init()
  {
  
  	// Notify the beginning of the current method
  	$this->dispatcher->notify(new sfEvent($this, 'config.init'));
  	 
  	foreach ($this->config_params as $section => $existing_params)
  	{
  		foreach ($existing_params as $existing_param => $existing_value)
  		{
  			if(!defined($existing_param))
  			{
  				if(is_numeric($existing_value))
  				{
  					$existing_value=$this->convert_type($existing_value);
  				}
  				define($existing_param,$existing_value);
  			}
  		}
  	}
  	foreach ($this->release_params as $section => $existing_params)
  	{
  		foreach ($existing_params as $existing_param => $existing_value)
  		{
  			if(!defined($existing_param))
  			{
  				if(is_numeric($existing_value))
  				{
  					$existing_value=$this->convert_type($existing_value);
  				}
  				define($existing_param,$existing_value);
  			}
  		}
  	}
  }
  
    /**
   * config_file::rmParams()
   * Supprime un paramètre dans la section souhaitée
   * @access public
   * @param array $array new params - format : array( array('sectionName','parameterName'), array('sectionName','parameterName'), ...);
   * @return bool
   */
  public function rmParams($array)
  {
    // Filter data event + return value
    $r = $this->dispatcher->filter(new sfEvent($this, 'config.rmParams', array('data' => $array)), $array);
    $array = $r->getReturnValue();
     
    //parse du tableau des paramètres
    // ajoute les paramètres de post

    foreach ($array as $key => $param)
    {
		// echo '<h1>['.$param[0].']['.$param[1].']</h1>';
		if(isset($this->config_params[$param[0]][$param[1]])) unset($this->config_params[$param[0]][$param[1]]);
    }

    return true;
  }
  /**
   * config_file::addParams()
   * Ajoute un paramètre dans la section souhaitée
   * @access public
   * @param array $array new params - format : array( array('sectionName','parameterName','parameterValue'), array('sectionName','parameterName','parameterValue'), ...);
   * @return bool
   */
  public function addParams($array)
  {
    // Filter data event + return value
    $r = $this->dispatcher->filter(new sfEvent($this, 'config.addParams', array('data' => $array)), $array);
    $array = $r->getReturnValue();
     
    //parse du tableau des paramètres
    // ajoute les paramètres de post

    foreach ($array as $key => $param)
    {
		$this->config_params[$param[0]][$param[1]] = $this->strip_ini_char($param[2]);
    }

    return true;
  }

  /**
   * config_file::setParams()
   * Ecrase les valeurs contenues dans le tableau stocké dans l'attribut $this->config_params
   * pour mise à jour des valeurs
   * @access public
   * @param array $array new values to override to params
   * @return void
   */
  public function setParams($array)
  {
    // Filter data event + return value
    $r = $this->dispatcher->filter(new sfEvent($this, 'config.set_params', array('data' => $array)), $array);
    $array = $r->getReturnValue();
     
    //parse du tableau des paramètres
    // ajoute les paramètres de post

    foreach ($array as $key => $value)
    {
    	foreach ($this->config_params as $section => $existing_params)
    	{
    		foreach ($existing_params as $existing_param => $existing_value)
    		{
    			// la clé existe dans le tableau de cache release_params, on insère la valeur dans ce tableau
    			if($existing_param == $key) $this->config_params[$section][$key]=$this->strip_ini_char($value);
    		}
    	}
    }

    return true;
  }

  /**
   * config_file::load_release_ini_file()
   * charge les paramètres dans l'attribut $release_params
   * @access private
   * @return void
   */
  private function load_config_ini_file() {

  	// Notify the beginning of the current method
  	$this->dispatcher->notify(new sfEvent($this, 'config.load_release_params'));

    //charge les paramètres release dans un tableau $release_params
    // if an extra_configfile is given, we use it
    if(is_string($this->extra_configfile)) {
    	$this->config_params=$this->parseConfig($this->extra_configfile);
    } else {
    	$this->config_params=$this->parseConfig($this->file_config);
    }
  }
  
  /**
   * config_file::load_db_ini_file()
   * charge les paramètres dans l'attribut $db_params
   * @access public
   * @return void
   */
  private function load_release_ini_file()
  {
  	// Notify the beginning of the current method
  	$this->dispatcher->notify(new sfEvent($this, 'config.load_db_params'));

    //charge les paramètres dans un tableau $db_params
    $this->release_params=$this->parseConfig($this->file_release);
  }

  /**
   * config_file::writeReleaseParams()
   * écrit dans le fichier ini les paramètres du tableau $release_params
   * @access public
   * @return boolean
   */
  public function writeReleaseParams() {

  	// Notify the beginning of the current method
  	$this->dispatcher->notify(new sfEvent($this, 'config.write_release_params'));

    //écrit les paramètres $release_params vers les fichiers release  ini et php
    if($this->backupParams($this->file_config)){
      //on écrit les params après avoir fait le backup
      if($this->a_file->mkini($this->config_params, $this->ini_path.$this->file_config))
      return true;
    }
    return false;
  }

  /**
   * config_file::backupParams()
   * sauvegarde le fichier $file_name dans le répertoire de backup
   * @access public
   * @param string $file_name
   * @return boolean
   */
  public function backupParams($file_name)
  {
  	// Notify the beginning of the current method
  	$this->dispatcher->notify(new sfEvent($this, 'config.backup_params', array('filename' => $file_name)));

  	// do create backup folder is not done yet
    SureCreateDir($this->backup_config_path, 0755);

  	if(is_string($this->extra_configfile)) {
  		$file = $this->extra_configfile;
  	} else {
  		$file = $this->file_config;
  	}
  	// we do backup
  	if(file_exists($this->ini_path.$file)) {
    	if ($this->a_file->cp($this->ini_path.$file,$this->backup_config_path)) return true;
  	}
  	return false;

  }

  /**
   * config_file::getReleaseParams()
   * retourne le contenu HTML en édition ou non des sections éditables
   * @access public
   * @param string $updatable : champ texte modifiable
   * @param string $hidden : présence d'input type hidden
   * @return string
   */
  public function getReleaseParams($updatable = false, $hidden = false) {
  
  	// Notify the beginning of the current method
  	$this->dispatcher->notify(new sfEvent($this, 'config.get_release_params'));
  	 
  	return $this->getParams($this->section_main, $updatable, $hidden);
  }
  
  /**
   * config_file::getDatabaseParams()
   * retourne le contenu HTML en édition ou non des sections éditables
   * @access public
   * @param string $updatable : champ texte modifiable
   * @param string $hidden : présence d'input type hidden
   * @return string
   */
  public function getDatabaseParams($updatable = false, $hidden = false) {
  
  	// Notify the beginning of the current method
  	$this->dispatcher->notify(new sfEvent($this, 'config.get_db_params'));
  	 
  	return $this->getParams($this->section_database, $updatable, $hidden);
  }
  
  /**
   * config_file::getParams()
   * retourne le contenu HTML en édition ou non des sections éditables
   * @access private
   * @param string $updatable : champ texte modifiable
   * @param string $hidden : présence d'input type hidden
   * @param string $file_name
   * @return string
   */
  public function getParams($file_name, $updatable = false, $hidden = false)
  {
  	// Notify the beginning of the current method
  	$this->dispatcher->notify(new sfEvent($this, 'config.get_all_params', array('filename' => $file_name)));
  	 
  	$out = '';
  	$authorized_sections=array();
  
  	include('../languages/' . U_L . '/lang_system.'. CHARSET .'.php');
  
  	// le tableau de cache à utiliser est celui de realease
  	$content=$this->config_params;
  
  	if($file_name == $this->section_main) {
  		//initialisation des sections à afficher/mdofier
  		$authorized_sections=$this->authorized_release_sections;
  	}
  	else if($file_name == $this->section_theme)
  	{
  		$authorized_sections=$this->authorized_theme_sections;
  	}
  	else if($file_name == $this->section_database)
  	{
  		//initialisation des sections à afficher/mdofier
  		$authorized_sections=$this->authorized_db_sections;
  	}
  
  	if (!is_array($content))
  		return false;
  
  	foreach ($content as $key => $ini)
  	{
  
  		if (is_array($ini))
  		{
  			if((in_array($key,$authorized_sections)))
  			{
  
  				if(isset($GLOBALS['lang']['system'][$key])) $label='<abbr title="'.$GLOBALS['lang']['system'][$key].'">'.$key.'</abbr>';
  				else $label=$key;
  
  				$out .= "\n<br style='clear:both' />\n";
  				$out .= "\n<h2>".$label."</h2>\n";
  				if(!$updatable) $out .="<dl>\n";
  				//génération d'un bloc
  				foreach ($ini as $var => $value)
  				{
  					$label='';
  					if(isset($GLOBALS['lang']['system'][$var])) $label='<abbr title="'.$GLOBALS['lang']['system'][$var].'">'.$var.'</abbr>';
  					else $label=$var;
  					if(!$updatable)	{
  						$out .="<dt>".$label;
  						if($hidden) $out .= "<input name=\"".$var."\" id=\"".$var."\" value=\"".$value."\" type=\"hidden\"  />\n";
  						if($var !='LANGUAGE') {
  							if($var=='DB_PASS') {
  								$out .= "</dt><dd>".preg_replace("/[a-zA-Z0-9_]/", "•", $value)."</dd>\n";
  							} else {
  								$out .= "</dt><dd>".$value."</dd>\n";
  							}
  						} else {
  							$out .="</dt><dd>".culture::getInstance()->getCultureInfo($value, 'Language')."</dd>\n";
  						}
  
  					}
  					else {
  						if($var=='DB_PASS') {
  							$out .= "<p><label for=\"".$var."\">".$label."</label><input name=\"".$var."\" id=\"".$var."\" value=\"".$value."\" type=\"password\" class=\"textfield\" /></p>\n";
  						}
  						elseif($var !='LANGUAGE') {
  							$out .= "<p><label for=\"".$var."\">".$label."</label><input name=\"".$var."\" id=\"".$var."\" value=\"".$value."\" type=\"text\" class=\"textfield\" /></p>\n";
  						} else {
  							$out .= "<p><label for=\"".$var."\">".$label."</label>".CultureSelectBox($var, $value)."</p>\n";
  						}
  
  					}
  				}
  				if(!$updatable) $out .="</dl>";
  			}
  		}
  		else
  		{
  			if(!$updatable && $hidden) $out .= "<input name=\"".$key."\" id=\"".$key."\" value=\"".$ini."\" type=\"hidden\"  />\n";
  			if(!$updatable)	$out .="<dt>".$key."</dt><dd>".$ini."</dd>\n";
  			else $out .= "<p><label for=\"".$key."\">".$key."</label><input name=\"".$key."\" id=\"".$key."\" value=\"".$ini."\" type=\"text\" class=\"textfield\" /></p>\n";
  		}
  	}
  	return $out;
  
  }
  
  /**
   * config_file::getThemeParams()
   * retourne le contenu HTML en édition ou non des sections éditables
   * @access public
   * @param string $updatable : champ texte modifiable
   * @param string $hidden : présence d'input type hidden
   * @return string
   */
  public function getThemeParams($updatable = false, $hidden = false) {
  
  	// Notify the beginning of the current method
  	$this->dispatcher->notify(new sfEvent($this, 'config.get_theme_params'));
  	 
  	$out = '';
  	$authorized_sections=array();
  
  	include('../languages/' . U_L . '/lang_system.'. CHARSET .'.php');
  
  	$content=$this->config_params;
  	$authorized_sections=$this->authorized_theme_sections;
  
  	$theme_public=$content[$this->theme_settings][$this->theme_public];
  	$theme_admin=$content[$this->theme_settings][$this->theme_admin];
  
  	if(isset($GLOBALS['lang']['system'][$this->theme_public])) $label='<abbr title="'.$GLOBALS['lang']['system'][$this->theme_public].'">'.$this->theme_public.'</abbr>';
  	else $label=$this->theme_public;
  
  	$out .= "\n<h2 style=\"clear:both;\">".$label."</h2>\n";
  	$out .=$this->getOneThemeParams($updatable, $hidden, true,$theme_public,$this->theme_public,$this->theme_public, "public");
  
  	$public_themes = getThemes('public');
  
  	foreach ($public_themes as $key => $value)
  	{
  		if($value != $theme_public)
  			$out .=$this->getOneThemeParams($updatable, $hidden, false,$value,$this->theme_list_public,$this->theme_public, "public");
  	}
  
  	if(isset($GLOBALS['lang']['system'][$this->theme_admin])) $label='<abbr title="'.$GLOBALS['lang']['system'][$this->theme_admin].'">'.$this->theme_admin.'</abbr>';
  	else $label=$this->theme_admin;
  
  	$out .= "\n<h2 style=\"clear:both;\">".$label."</h2>\n";
  
  	$out .=$this->getOneThemeParams($updatable, $hidden, true,$theme_admin,$this->theme_admin,$this->theme_admin, "admin");
  
  	$admin_themes = getThemes('admin');
  
  	foreach ($admin_themes as $key => $value)
  	{
  		if($value != $theme_admin)
  			$out .=$this->getOneThemeParams($updatable, $hidden, false,$value,$this->theme_list_admin,$this->theme_admin, "admin");
  	}
  
  	$out .= "\n<br style=\"clear:both;\" />\n";
  	return $out;
  }
  
  
  /**
   * config_file::getOneThemeParams()
   * retourne les informations relatives à un thème
   * @access private
   * @param string $updatable
   * @param string $hidden
   * @param string $selected
   * @param string $value
   * @param string $field_name
   * @param string $radio_name
   * @return string
   */
  public function getOneThemeParams($updatable = false, $hidden = false, $selected=false,$value,$field_name,$radio_name, $type) {
  
  	// Notify the beginning of the current method
  	$this->dispatcher->notify(new sfEvent($this, 'config.get_one_theme_params'));
  	 
  	include('../languages/' . U_L . '/lang_system.'. CHARSET .'.php');
  
  	$selected_pattern='';
  	$checked_pattern='';
  	$hidden_pattern='';
  	$updatable_pattern='';
  	$info = loadThemeInfo($type, $value);
  
  	if ($selected)
  	{
  		$selected_pattern='_selected';
  		$checked_pattern='checked="checked"';
  	}
  	if(!$updatable)$hidden_pattern='style="display:none;"';
  	else $updatable_pattern="onclick=\"javascript:document.getElementById('".$type.$value."').checked='checked';\"";
  	$out = '';
  	$out .="<div class=\"config_theme" . $selected_pattern. "\">\n";
  	$out .="<img src=\"../templates/" .$type. "/" .$value. "/preview.png\" style=\"width:250px;height:160px;\" alt=\"" .$value. "\" id=\"img_" .$type."-".$value. "\" ".$updatable_pattern."/>\n";
  
  	$out .= '<input type="radio" name="' .$radio_name. '" id="' .$type.'-'.$value. '" value="' .$value. '" ' .$checked_pattern. ' ' .$hidden_pattern. ' />';
  	$out .=	'<label for="' .$type.'-'.$value. '" class="autowidth">' . $value . '</label>';
  	$out .= '<div class="themeinfo"><em>'._t('theme','name').'</em> : '.$info['name'].'<br />'.PHP_EOL;
  	$out .= '<em>'._t('theme','author').'</em> : <a href="'.$info['homepage'].'">'.$info['author'].'</a><br />'.PHP_EOL;
  	$out .= '<em>'._t('theme','version').'</em> : '.$info['version'].' | ';
  	$out .= '<em>'._t('theme','compatibility').'</em> : '.$info['compatibility'].'<br />'.PHP_EOL;
  	$out .= '<em>'._t('theme','description').'</em> : '.$info['description'].'<br />'.PHP_EOL;
  	$out .= '</div>';
  	$out .="</div>";
  	return $out;
  }

  /**
   * config_file::writePhpParams()
   * écrit le fichier $file_name dans le répertoire de config, au format PHP
   * @access public
   * méthode dépréciée
   * @param string $file_name
   * @return boolean
   */
  public function writePhpParams($file_name)
  {
  	// Notify the beginning of the current method
  	$this->dispatcher->notify(new sfEvent($this, 'config.write_php_params', array('filename' => $file_name)));

    $content=$this->generateHeader($file_name);
    $content.=$this->generateBody($file_name);
    $content.=$this->generateFooter($file_name);
    //on écrit le contenu dans le fichier php
    if($this->a_file->mkfile($content,$this->config_path.$file_name.$this->php_extension)) return true;
    else return false;
  }
  /**
   * config_file::parseConfig()
   * retourne un tableau contenant les paramètres du fichier ini $file_name
   * @access public
   * @param string $file_name
   * @return array
   */
  public function parseConfig($file_name, $whithsection = true)
  {
  	// Notify the beginning of the current method
  	$this->dispatcher->notify(new sfEvent($this, 'config.parse_config', array('filename' => $file_name)));

    //parse_ini_file
    return $this->a_file->parse_ini($this->ini_path.$file_name, $whithsection);

  }

  /**
   * config_file::generateBody()
   * génère le contenu d'un fichier php de configuration à parti du $file_name
   * @access private
   * @param string $file_name
   * @return string
   */
  private function generateBody($file_name)
  {

    $out = '';

    if($file_name == $this->section_main) $content=$this->config_params;
    else $content=$this->release_params;

    if (!is_array($content))
    return false;

    foreach ($content as $key => $ini)
    {
      if (is_array($ini))
      {
        $out .= "\n\n//	".$key." /////// \n\n";

        foreach ($ini as $var => $value)
        {
          $out .= "define('".$var."',\t\t".$this->a_file->quote_ini($value).");\n";
        }
      }
      else
      {
        $out .= "define('".$key."',\t\t".$this->a_file->quote_ini($ini).");\n";
      }
    }

    // Filter data event + return value
    $r = $this->dispatcher->filter(new sfEvent($this, 'config.generate_body', array('filename' => $file_name, 'content' => $out)), $out);
    $out = $r->getReturnValue();

    return $out;
  }
  /**
   * config_file::generateHeader()
   * génère l'en tête d'un fichier php de configuration à partir du $file_name
   * @access private
   * @param string $file_name
   * @return string
   */
  private function generateHeader($file_name)
  {
    $ret.="<?php \n";
    $ret.="// Linea 21 - PHP ".$file_name."\n";
    $ret.="// last_modify	: ".date("d-m-Y")."\n";
    $ret.="// GPL 3\n";
    $ret.="// http://opensource.org/licenses/gpl-3.0.html\n\n";
    $ret.="// BE CAREFULL THIS FILE IS SYSTEM FILE : use interface or ".$file_name." FOR CONFIGURATION";

    // Filter data event + return value
    $r = $this->dispatcher->filter(new sfEvent($this, 'config.generate_header', array('filename' => $file_name, 'content' => $ret)), $ret);
    $ret = $r->getReturnValue();

    return $ret;
  }
  /**
   * config_file::generateFooter()
   * génère le pied d'un fichier php de configuration à parti du $file_name
   * @access private
   * @param string $file_name
   * @return string
   */
  private function generateFooter($file_name)
  {
    $s = "?> \n";

    // Filter data event + return value
    $r = $this->dispatcher->filter(new sfEvent($this, 'config.generate_footer', array('filename' => $file_name, 'content' => $s)), $s);
    $s = $r->getReturnValue();

    return $s;
  }



  /**
   * config_file::strip_ini_char
   * suppression des caractères interdits dans un fichier ini
   * @access private
   * @param string $content
   * @return string
   */
  function strip_ini_char($content)
  {
    $ret='';
    // on remplace les caractères "[]; par '(),
    $ret=strtr($content, "\"[];", "'(),");
    if (get_magic_quotes_gpc()) $ret=stripslashes($ret);

    return $ret;
  }

  /**
   * config_file::convert_type()
   * conversion d'une variable en entier ou en float selon son contenu
   * @param unknown_type $var
   * @return int or float
   */
  public function convert_type( $var )
  {
    if( is_numeric( $var ) )
    {
      if( (float)$var != (int)$var )
      {
        return (float)$var;
      }
      else
      {
        return (int)$var;
      }
    }
    if( $var == "true" )    return true;
    if( $var == "false" )    return false;
    return $var;
  }
  /**
   * config_file::getPhpInfo()
   * Retourne un string contenant les informations php en html
   * @access public
   * @return string
   */
  public function getPhpInfo() {
    ob_start();
    phpinfo();
    $info = ob_get_contents();
    ob_end_clean();
    return preg_replace('%^.*<body>(.*)</body>.*$%ms', '$1', $info);
  }
}

?>