Skip to content
Snippets Groups Projects
Select Git revision
  • 5acc58092025d08753f59c0a19f24e3a785b1ecb
  • master default
2 results

generate-list.php

Blame
  • functions.lib.php 90.88 KiB
    <?php
    /* Copyright (C) 2000-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
     * Copyright (C) 2003      Jean-Louis Bergamo   <jlb@j1b.org>
     * Copyright (C) 2004-2008 Laurent Destailleur  <eldy@users.sourceforge.net>
     * Copyright (C) 2004      Sebastien Di Cintio  <sdicintio@ressource-toi.org>
     * Copyright (C) 2004      Benoit Mortier       <benoit.mortier@opensides.be>
     * Copyright (C) 2004      Christophe Combelles <ccomb@free.fr>
     * Copyright (C) 2005-2007 Regis Houssin        <regis@dolibarr.fr>
     * Copyright (C) 2008      Raphael Bertrand (Resultic)       <raphael.bertrand@resultic.fr>
     *
     * This program is free software; you can redistribute it and/or modify
     * it under the terms of the GNU General Public License as published by
     * the Free Software Foundation; either version 2 of the License, or
     * (at your option) any later version.
     *
     * This program is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     * GNU General Public License for more details.
     *
     * You should have received a copy of the GNU General Public License
     * along with this program; if not, write to the Free Software
     * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
     * or see http://www.gnu.org/
     */
    
    /**
     *	\file			htdocs/lib/functions.lib.php
     *	\brief			A set of functions for Dolibarr
     *					This file contains all frequently used functions.
     *	\version		$Id$
     */
    
    // For compatibility during upgrade
    if (! defined('DOL_DOCUMENT_ROOT'))	 define('DOL_DOCUMENT_ROOT', '..');
    if (! defined('ADODB_DATE_VERSION')) include_once(DOL_DOCUMENT_ROOT."/includes/adodbtime/adodb-time.inc.php");
    
    
    
    /**
     *	\brief      Return true if email syntax is ok
     *	\param	    address     email (Ex: "toto@titi.com", "John Do <johndo@titi.com>")
     *	\return     boolean     true if email ok, false if ko
     */
    function ValidEmail($address)
    {
    	if (eregi(".*<(.+)>", $address, $regs)) {
    		$address = $regs[1];
    	}
    	if (eregi("^[^@  ]+@([a-zA-Z0-9\-]+\.)+([a-zA-Z0-9\-]{2}|coop|aero|biz|com|edu|gov|info|int|mil|name|net|org)\$",$address))
    	{
    		return true;
    	}
    	else
    	{
    		return false;
    	}
    }
    
    /**
     *	\brief      Renvoi vrai si l'email a un nom de domaine qui r�soud via dns
     *	\param	    mail        adresse email (Ex: "toto@titi.com", "John Do <johndo@titi.com>")
     *	\return     boolean     true si email valide, false sinon
     */
    function check_mail($mail)
    {
    	list($user, $domain) = split("@", $mail, 2);
    	if (checkdnsrr($domain, "MX"))
    	{
    		return true;
    	}
    	else
    	{
    		return false;
    	}
    }
    
    /**
     *	\brief          Clean a string to use it as a file name.
     *	\param          str             String to clean
     * 	\param			newstr			String to replace bad chars by
     *	\return         string          String cleaned (a-zA-Z_)
     */
    function sanitizeFileName($str,$newstr='_')
    {
    	return dol_string_nospecial(dol_string_unaccent($str),$newstr);
    }
    
    /**
     *	\brief          Clean a string from all accent characters
     *	\param          str             String to clean
     *	\return         string          Cleaned string
     */
    function dol_string_unaccent($str)
    {
    	if (utf8_check($str))
    	{
    		$string = rawurlencode($str);
    		$replacements = array(
    		'%C3%80' => 'A','%C3%81' => 'A',
    		'%C3%88' => 'E','%C3%89' => 'E',
    		'%C3%8C' => 'I','%C3%8D' => 'I',
    		'%C3%92' => 'O','%C3%93' => 'O',
    		'%C3%99' => 'U','%C3%9A' => 'U',
    		'%C3%A0' => 'a','%C3%A1' => 'a','%C3%A2' => 'a',
    		'%C3%A8' => 'e','%C3%A9' => 'e','%C3%AA' => 'e','%C3%AB' => 'e',
    		'%C3%AC' => 'i','%C3%AD' => 'i',
    		'%C3%B2' => 'o','%C3%B3' => 'o',
    		'%C3%B9' => 'u','%C3%BA' => 'u'
    		);
    		$string=strtr($string, $replacements);
    		return rawurldecode($string);
    	}
    	else
    	{
    		$string = strtr($str,
    		"xC0\xC1\xC2\xC3\xC5\xC7
    	        \xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1
    	        \xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD
    	        \xE0\xE1\xE2\xE3\xE5\xE7\xE8\xE9\xEA\xEB
    	        \xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8
    	        \xF9\xFA\xFB\xFD\xFF",
    		"AAAAAC
    	        EEEEIIIIDN
    	        OOOOOUUUY
    	        aaaaaceeee
    	        iiiidnooooo
    	        uuuyy");  
    		$string = strtr($string, array("\xC4"=>"Ae", "\xC6"=>"AE", "\xD6"=>"Oe", "\xDC"=>"Ue", "\xDE"=>"TH", "\xDF"=>"ss", "\xE4"=>"ae", "\xE6"=>"ae", "\xF6"=>"oe", "\xFC"=>"ue", "\xFE"=>"th"));
    		return $string;
    	}
    }
    
    /**
     *	\brief          Clean a string from all punctuation characters
     *	\param          str             String to clean
     * 	\param			newstr			String to replace bad chars by
     * 	\return         string          Cleaned string
     */
    function dol_string_nospecial($str,$newstr='_')
    {
    	$forbidden_chars_to_underscore=array(" ","'","/","\\",":","*","?","\"","<",">","|","[","]",",",";","=");
    	//$forbidden_chars_to_remove=array("(",")");
    	$forbidden_chars_to_remove=array();
    
    	return str_replace($forbidden_chars_to_underscore,$newstr,str_replace($forbidden_chars_to_remove,"",$str));
    }
    
    /**
     *  \brief       Returns text escaped for inclusion in javascript code
     *  \param       $stringtoescape	String to escape
     *  \return      string      		Escaped string
     */
    function dol_escape_js($stringtoescape)
    {
    	// escape quotes and backslashes, newlines, etc.
    	return strtr($stringtoescape, array('\\'=>'\\\\',"'"=>"\\'",'"'=>'\\"',"\r"=>'\\r',"\n"=>'\\n','</'=>'<\/'));
    }
    
    
    /* For backward compatiblity */
    function dolibarr_syslog($message, $level=LOG_INFO)
    {
    	return dol_syslog($message, $level);
    }
    
    /**
     *	\brief      Write log message in a file or to syslog process
     *				Pour fichier:   	fichier defined by SYSLOG_FILE
     *				Pour syslog:    	facility defined by SYSLOG_FACILITY
     * 				Warning, les fonctions syslog sont buggues sous Windows et generent des
     *				fautes de protection memoire. Pour resoudre, utiliser le loggage fichier,
     *				au lieu du loggage syslog (configuration du module).
     *				Si SYSLOG_FILE_NO_ERROR defini, on ne gere pas erreur ecriture log
     * 	\param      message		    	Line to log. Ne doit pas etre traduit si level = LOG_ERR
     *	\param      level           	Log level
     *	\remarks	Cette fonction n'a un effet que si le module syslog est active.
     *	\remarks	On Windows LOG_ERR=4, LOG_WARNING=5, LOG_NOTICE=LOG_INFO=LOG_DEBUG=6
     *				On Linux   LOG_ERR=3, LOG_WARNING=4, LOG_INFO=6, LOG_DEBUG=7
     */
    function dol_syslog($message, $level=LOG_INFO)
    {
    	global $conf,$user,$langs,$REQUEST;
    
    	// If adding log inside HTML page is required
    	if (! empty($REQUEST['logtohtml']))
    	{
    		$conf->logbuffer[]=strftime("%Y-%m-%d %H:%M:%S",time())." ".$message;			
    
    	}
    
    	// If syslog module enabled
    	if (! empty($conf->syslog->enabled))
    	{
    		//print $level.' - '.$conf->global->SYSLOG_LEVEL.' - '.$conf->syslog->enabled." \n";
    		if ($level > $conf->global->SYSLOG_LEVEL) return;
    
    		// Load error message files if this is an error message (rare)
    		if ($level == LOG_ERR)
    		{
    			$langs->load("errors");
    			if ($message != $langs->trans($message)) $message = $langs->trans($message);
    		}
    
    		// Add user to log message
    		$login='???';
    		if (is_object($user) && $user->id) $login=$user->login;
    		$message=sprintf("%-8s",$login)." ".$message;
    
    		// Check if log is to a file (SYSLOG_FILE defined) or to syslog
    		if (defined("SYSLOG_FILE") && SYSLOG_FILE)
    		{
    			$filelog=SYSLOG_FILE;
    			$filelog=eregi_replace('DOL_DATA_ROOT',DOL_DATA_ROOT,$filelog);
    			if (defined("SYSLOG_FILE_NO_ERROR")) $file=@fopen($filelog,"a+");
    			else $file=fopen($filelog,"a+");
    			if ($file)
    			{
    				$ip='unknown_ip';
    				if (! empty($_SERVER["REMOTE_ADDR"])) $ip=$_SERVER["REMOTE_ADDR"];
    
    				$liblevelarray=array(LOG_ERR=>'ERROR',LOG_WARNING=>'WARN',LOG_INFO=>'INFO',LOG_DEBUG=>'DEBUG');
    				$liblevel=$liblevelarray[$level];
    				if (! $liblevel) $liblevel='UNDEF';
    
    				$message=strftime("%Y-%m-%d %H:%M:%S",time())." ".sprintf("%-5s",$liblevel)." ".$ip." ".$message;
    
    				fwrite($file,$message."\n");
    				fclose($file);
    				// This is for log file, we do not change permissions
    
    				// If enable html log tag enabled and url parameter log defined, we show output log on HTML comments
    				if (! empty($conf->global->MAIN_ENABLE_LOG_HTML) && ! empty($_GET["log"]))
    				{
    					print "\n\n<!-- Log start\n";
    					print $message."\n";
    					print "Log end -->\n";
    				}
    			}
    			elseif (! defined("SYSLOG_FILE_NO_ERROR"))
    			{
    				$langs->load("main");
    				print $langs->trans("ErrorFailedToOpenFile",$filelog);
    			}
    		}
    		else
    		{
    			if (function_exists('openlog'))	// This function does not exists on some ISP (Ex: Free in France)
    			{
    				//define_syslog_variables(); already defined in master.inc.php
    				if (defined("MAIN_SYSLOG_FACILITY") && MAIN_SYSLOG_FACILITY)
    				{
    					$facility = constant("MAIN_SYSLOG_FACILITY");
    				}
    				elseif (defined("SYSLOG_FACILITY") && SYSLOG_FACILITY)
    				{
    					// Exemple: SYSLOG_FACILITY vaut LOG_USER qui vaut 8. On a besoin de 8 dans $facility.
    					$facility = constant("SYSLOG_FACILITY");
    				}
    				else
    				{
    					$facility = LOG_USER;
    				}
    
    				openlog("dolibarr", LOG_PID | LOG_PERROR, $facility);
    
    				if (! $level)
    				{
    					syslog(LOG_ERR, $message);
    				}
    				else
    				{
    					syslog($level, $message);
    				}
    
    				closelog();
    			}
    		}
    	}
    }
    
    
    /* For backward compatibility */
    function dolibarr_fiche_head($links, $active='0', $title='', $notab=0)
    {
    	return dol_fiche_head($links, $active, $title, $notab);
    }
    
    /**
     *	\brief      Affiche le header d'une fiche
     *	\param	    links		Tableau de titre d'onglets
     *	\param	    active      0=onglet non actif, 1=onglet actif
     *	\param      title       Titre tabelau ("" par defaut)
     *	\param      notab		0=Add tab header, 1=no tab header
     */
    function dol_fiche_head($links, $active='0', $title='', $notab=0)
    {
    	print "\n".'<div class="tabs">'."\n";
    
    	// Affichage titre
    	if ($title)
    	{
    		$limittitle=30;
    		print '<a class="tabTitle">';
    		print
    		((!defined('MAIN_USE_SHORT_TITLE')) || (defined('MAIN_USE_SHORT_TITLE') &&  MAIN_USE_SHORT_TITLE))
    		? dolibarr_trunc($title,$limittitle)
    		: $title;
    		print '</a>';
    	}
    
    	// Affichage onglets
    	for ($i = 0 ; $i < sizeof($links) ; $i++)
    	{
    		if ($links[$i][2] == 'image')
    		{
    			print '<a class="tabimage" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
    		}
    		else
    		{
    			//print "x $i $active ".$links[$i][2]." z";
    			if ((is_numeric($active) && $i == $active)
    			|| (! is_numeric($active) && $active == $links[$i][2]))
    			{
    				print '<a id="active" class="tab" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
    			}
    			else
    			{
    				print '<a class="tab" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
    			}
    		}
    	}
    
    	print "</div>\n";
    
    	if (! $notab) print '<div class="tabBar">'."\n\n";
    }
    
    
    /**
     *	\brief      Add a delay to a date
     *	\param	    time                Date timestamp ou au format YYYY-MM-DD
     *	\param	    duration_value      Value of delay to add
     *	\param	    duration_unit       Unit of added delay (d, m, y)
     *	\return     int                 New timestamp
     */
    function dol_time_plus_duree($time,$duration_value,$duration_unit)
    {
    	if ($duration_value == 0) return $time;
    	if ($duration_value > 0) $deltastring="+".abs($duration_value);
    	if ($duration_value < 0) $deltastring="-".abs($duration_value);
    	if ($duration_unit == 'd') { $deltastring.=" day"; }
    	if ($duration_unit == 'm') { $deltastring.=" month"; }
    	if ($duration_unit == 'y') { $deltastring.=" year"; }
    	return strtotime($deltastring,$time);
    }
    
    
    /* For backward compatibility */
    function dolibarr_print_date($time,$format='',$to_gmt=false,$outputlangs='')
    {
    	return dol_print_date($time,$format,$to_gmt,$outputlangs);
    }
    
    /**
     *	\brief      Output date in a string format according to language $conf->language
     *	\param	    time        	GM Timestamps date (or 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS' in server TZ)
     *	\param	    format      	Output date format
     *								"%d %b %Y",
     *								"%d/%m/%Y %H:%M",
     *								"%d/%m/%Y %H:%M:%S",
     *								"day", "daytext", "dayhour", "dayhourldap", "dayhourtext"
     * 	\param		to_gmt			false=output string if for local server TZ users, true=output string is for GMT users
     *	\param		outputlangs		Object lang that contains charset_output property to define output
     * 								This means output is endoded in UTF-8 in default case.
     * 	\return     string      	Formated date or '' if time is null
     */
    function dol_print_date($time,$format='',$to_gmt=false,$outputlangs='')
    {
    	global $conf,$langs;
    
    	// Si format non defini, on prend $conf->format_date_text_short sinon %Y-%m-%d %H:%M:%S
    	if (! $format) $format=(isset($conf->format_date_text_short) ? $conf->format_date_text_short : '%Y-%m-%d %H:%M:%S');
    
    	if ($format == 'day')          $format=$conf->format_date_short;
    	if ($format == 'hour')         $format=$conf->format_hour_short;
    	if ($format == 'daytext')      $format=$conf->format_date_text;
    	if ($format == 'daytextshort') $format=$conf->format_date_text_short;
    	if ($format == 'dayhour')      $format=$conf->format_date_hour_short;
    	if ($format == 'dayhourtext')  $format=$conf->format_date_hour_text;
    	if ($format == 'dayhourtextshort')  $format=$conf->format_date_hour_text_short;
    
    	if ($format == 'dayhourlog')   $format='%Y%m%d%H%M%S';
    	if ($format == 'dayhourldap')  $format='%Y%m%d%H%M%SZ';
    	if ($format == 'dayhourxcard') $format='%Y%m%dT%H%M%SZ';
    
    	// If date undefined or "", we return ""
    	if (strlen($time) == 0) return '';		// $time=0 allowed (it means 01/01/1970 00:00:00)
    	
    	// Analyse de la date (deprecated)
    	if (eregi('^([0-9]+)\-([0-9]+)\-([0-9]+) ?([0-9]+)?:?([0-9]+)?:?([0-9]+)?',$time,$reg))
    	{
    		// This part of code should not be used.
    		dolibarr_syslog("Functions.lib::dolibarr_print_date function call with deprecated parameter in page ".$_SERVER["PHP_SELF"], LOG_WARNING);
    		// Date est au format 'YYYY-MM-DD' ou 'YYYY-MM-DD HH:MM:SS'
    		$syear = $reg[1];
    		$smonth = $reg[2];
    		$sday = $reg[3];
    		$shour = $reg[4];
    		$smin = $reg[5];
    		$ssec = $reg[6];
    
    		$ret=adodb_strftime($format,dolibarr_mktime($shour,$smin,$ssec,$smonth,$sday,$syear),$to_gmt);
    	}
    	else
    	{
    		// Date is a timestamps
    		$ret=adodb_strftime($format,$time,$to_gmt);
    	}
    
    	// What is page code of texts from strftime functions ?
    	$pagecodefrom='ISO-8859-1';
    	$localtime=setlocale(LC_TIME,0);
    	//print $localtime;
    	if (eregi('UTF',$localtime)) $pagecodefrom='UTF-8';
    
    	if (! is_object($outputlangs)) $outputlangs=$langs;
    
    	return $outputlangs->convToOutputCharset($ret,$pagecodefrom);
    }
    
    
    /**
     *	\brief  	Convert a GM string date into a GM Timestamps date
     *	\param		string			Date in a string
     *				YYYYMMDD
     *				YYYYMMDDHHMMSS
     *				DD/MM/YY ou DD/MM/YYYY
     *				DD/MM/YY HH:MM:SS ou DD/MM/YYYY HH:MM:SS
     *	\return		date			Date
     * 	\example	19700101020000 -> 7200
     */
    function dol_stringtotime($string)
    {
    	if (eregi('^([0-9]+)\/([0-9]+)\/([0-9]+) ?([0-9]+)?:?([0-9]+)?:?([0-9]+)?',$string,$reg))
    	{
    		// This part of code should not be used.
    		dolibarr_syslog("Functions.lib::dol_stringtotime call to function with deprecated parameter", LOG_WARN);
    		// Date est au format 'DD/MM/YY' ou 'DD/MM/YY HH:MM:SS'
    		// Date est au format 'DD/MM/YYYY' ou 'DD/MM/YYYY HH:MM:SS'
    		$sday = $reg[1];
    		$smonth = $reg[2];
    		$syear = $reg[3];
    		$shour = $reg[4];
    		$smin = $reg[5];
    		$ssec = $reg[6];
    		if ($syear < 50) $syear+=1900;
    		if ($syear >= 50 && $syear < 100) $syear+=2000;
    		$string=sprintf("%04d%02d%02d%02d%02d%02d",$syear,$smonth,$sday,$shour,$smin,$ssec);
    	}
    
    	$string=eregi_replace('[^0-9]','',$string);
    	$tmp=$string.'000000';
    	$date=dolibarr_mktime(substr($tmp,8,2),substr($tmp,10,2),substr($tmp,12,2),substr($tmp,4,2),substr($tmp,6,2),substr($tmp,0,4),1);
    	return $date;
    }
    
    
    /**
     *	\brief  	Return an array with date info
     *	\param		timestamp		Timestamp
     *	\param		fast			Fast mode
     *	\return		array			Array of informations
     *				If no fast mode:
     *				'seconds' => $secs,
     *				'minutes' => $min,
     *				'hours' => $hour,
     *				'mday' => $day,
     *				'wday' => $dow,
     *				'mon' => $month,
     *				'year' => $year,
     *				'yday' => floor($secsInYear/$_day_power),
     *				'weekday' => gmdate('l',$_day_power*(3+$dow)),
     *				'month' => gmdate('F',mktime(0,0,0,$month,2,1971)),
     *				0 => $origd
     *				If fast mode:
     *				'seconds' => $secs,
     *				'minutes' => $min,
     *				'hours' => $hour,
     *				'mday' => $day,
     *				'mon' => $month,
     *				'year' => $year,
     *				'yday' => floor($secsInYear/$_day_power),
     *				'leap' => $leaf,
     *				'ndays' => $ndays
     *	\remarks	PHP getdate is restricted to the years 1901-2038 on Unix and 1970-2038 on Windows
     */
    function dol_getdate($timestamp,$fast=false)
    {
    	$usealternatemethod=false;
    	if ($timestamp <= 0) $usealternatemethod=true;				// <= 1970
    	if ($timestamp >= 2145913200) $usealternatemethod=true;		// >= 2038
    
    	if ($usealternatemethod)
    	{
    		$arrayinfo=adodb_getdate($timestamp,$fast);
    	}
    	else
    	{
    		$arrayinfo=getdate($timestamp);
    	}
    
    	return $arrayinfo;
    }
    
    /* For backward compatibility */
    function dolibarr_mktime($hour,$minute,$second,$month,$day,$year,$gm=0,$check=1)
    {
    	return dol_mktime($hour,$minute,$second,$month,$day,$year,$gm,$check);
    }
    
    /**
     *	Return a GMT date built from detailed informations
     * 	Replace function mktime not available under Windows if year < 1970
     *	PHP mktime is restricted to the years 1901-2038 on Unix and 1970-2038 on Windows
     * 	@param		hour			Hour	(can be -1 for undefined)
     *	@param		minute			Minute	(can be -1 for undefined)
     *	@param		second			Second	(can be -1 for undefined)
     *	@param		month			Month
     *	@param		day				Day
     *	@param		year			Year
     *	@param		gm				1=Input informations are GMT values, otherwise local to user
     *	@param		check			0=No check on parameters (Can use day 32, etc...)
     *	@return		timestamp		Date en timestamp, '' if error
     */
    function dol_mktime($hour,$minute,$second,$month,$day,$year,$gm=0,$check=1)
    {
    	//print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
    
    	// Clean parameters
    	if ($hour   == -1) $hour=0;
    	if ($minute == -1) $minute=0;
    	if ($second == -1) $second=0;
    
    	// Check parameters
    	if ($check)
    	{
    		if (! $month || ! $day)  return '';
    		if ($day   > 31) return '';
    		if ($month > 12) return '';
    		if ($hour  < 0 || $hour   > 24) return '';
    		if ($minute< 0 || $minute > 60) return '';
    		if ($second< 0 || $second > 60) return '';
    	}
    
    	$usealternatemethod=false;
    	if ($year <= 1970) $usealternatemethod=true;		// <= 1970
    	if ($year >= 2038) $usealternatemethod=true;		// >= 2038
    
    	if ($usealternatemethod || $gm)	// Si time gm, seule adodb peut convertir
    	{
    		/*
    		 // On peut utiliser strtotime pour obtenir la traduction.
    		 // strtotime is ok for range: Vendredi 13 D�cembre 1901 20:45:54 GMT au Mardi 19 Janvier 2038 03:14:07 GMT.
    		 $montharray=array(1=>'january',2=>'february',3=>'march',4=>'april',5=>'may',6=>'june',
    		 7=>'july',8=>'august',9=>'september',10=>'october',11=>'november',12=>'december');
    		 $string=$day." ".$montharray[0+$month]." ".$year." ".$hour.":".$minute.":".$second." GMT";
    		 $date=strtotime($string);
    		 print "- ".$string." ".$date." -";
    		 */
    		$date=adodb_mktime($hour,$minute,$second,$month,$day,$year,0,$gm);
    	}
    	else
    	{
    		$date=mktime($hour,$minute,$second,$month,$day,$year);
    	}
    	return $date;
    }
    
    
    /* For backward compatibility */
    function dolibarr_date($fmt, $timestamp, $gm=0)
    {
    	return dol_date($fmt, $timestamp, $gm);
    }
    
    /**
     *	\brief  	Returns formated date
     *	\param		fmt				Format (Exemple: 'Y-m-d H:i:s')
     *	\param		timestamp		Date. Exemple: Si timestamp=0 et gm=1, renvoi 01/01/1970 00:00:00
     *	\param		gm				1 if timestamp was built with gmmktime, 0 if timestamp was build with mktime
     *	\return		string			Formated date
     */
    function dol_date($fmt, $timestamp, $gm=0)
    {
    	$usealternatemethod=false;
    	if ($timestamp <= 0) $usealternatemethod=true;
    	if ($timestamp >= 2145913200) $usealternatemethod=true;
    
    	if ($usealternatemethod || $gm)	// Si time gm, seule adodb peut convertir
    	{
    		$string=adodb_date($fmt,$timestamp,$gm);
    	}
    	else
    	{
    		$string=date($fmt,$timestamp);
    	}
    
    	return $string;
    }
    
    
    /**
     *	\brief  Affiche les informations d'un objet
     *	\param	object			objet a afficher
     */
    function dol_print_object_info($object)
    {
    	global $langs;
    	$langs->load("other");
    
    	if (isset($object->user_creation) && $object->user_creation->fullname)
    	print $langs->trans("CreatedBy")." : " . $object->user_creation->fullname . '<br>';
    
    	if (isset($object->date_creation))
    	print $langs->trans("DateCreation")." : " . dolibarr_print_date($object->date_creation,"dayhourtext") . '<br>';
    
    	if (isset($object->user_modification) && $object->user_modification->fullname)
    	print $langs->trans("ModifiedBy")." : " . $object->user_modification->fullname . '<br>';
    
    	if (isset($object->date_modification))
    	print $langs->trans("DateLastModification")." : " . dolibarr_print_date($object->date_modification,"dayhourtext") . '<br>';
    
    	if (isset($object->user_validation) && $object->user_validation->fullname)
    	print $langs->trans("ValidatedBy")." : " . $object->user_validation->fullname . '<br>';
    
    	if (isset($object->date_validation))
    	print $langs->trans("DateValidation")." : " . dolibarr_print_date($object->date_validation,"dayhourtext") . '<br>';
    
    	if (isset($object->user_cloture) && $object->user_cloture->fullname )
    	print $langs->trans("ClosedBy")." : " . $object->user_cloture->fullname . '<br>';
    
    	if (isset($object->date_cloture))
    	print $langs->trans("DateClosing")." : " . dolibarr_print_date($object->date_cloture,"dayhourtext") . '<br>';
    
    	if (isset($object->user_rappro) && $object->user_rappro->fullname )
    	print $langs->trans("ConciliatedBy")." : " . $object->user_rappro->fullname . '<br>';
    
    	if (isset($object->date_rappro))
    	print $langs->trans("DateConciliating")." : " . dolibarr_print_date($object->date_rappro,"dayhourtext") . '<br>';
    }
    
    
    /**
     * \brief		Return string with formated size
     * \param		size		Size to print
     * \return		string		Link
     */
    function dol_print_size($size)
    {
    	global $langs;
    
    	return $size.' '.$langs->trans("Bytes");
    }
    
    /**
     * \brief		Show Url link
     * \param		url			Url to show
     * \param		target		Target for link
     * \param		max			Max number of characters to show
     * \return		string		HTML Link
     */
    function dol_print_url($url,$target='_blank',$max=32)
    {
    	if (empty($url)) return '';
    	
    	$link='<a href="';
    	if (! eregi('^http',$url)) $link.='http://';
    	$link.=$url;
    	if ($target) $link.='" target="'.$target.'">';
    	if (! eregi('^http',$url)) $link.='http://';
    	$link.=dolibarr_trunc($url,$max);
    	$link.='</a>';
    	return $link;
    }
    
    /**
     * \brief		Show EMail link
     * \param		email		EMail to show
     * \param 		cid 		Id of contact if known
     * \param 		socid 		Id of third party if known
     * \param 		addlink		0=no link to create action
     * \param		max			Max number of characters to show
     * \return		string		HTML Link
     */
    function dol_print_email($email,$cid=0,$socid=0,$addlink=0,$max=64)
    {
    	global $conf,$user,$langs;
    	
    	$newemail=$email;
    	
        if (empty($email)) return '&nbsp;';
    
        if (! ValidEmail($email)) return "Bad email";
    	
    	if (! empty($addlink))
    	{
    		$newemail='<a href="';
    		if (! eregi('^mailto:',$email)) $newemail.='mailto:';
    		$newemail.=$email;
    		$newemail.='">';
    		$newemail.=dol_trunc($email,$max);
    		$newemail.='</a>';
    				
    		if (($cid || $socid) && $conf->agenda->enabled && $user->rights->agenda->myactions->create)
    		{
    			$type='AC_EMAIL';
    			$link='<a href="'.DOL_URL_ROOT.'/comm/action/fiche.php?action=create&amp;backtopage=1&amp;actioncode='.$type.'&amp;contactid='.$cid.'&amp;socid='.$socid.'">'.img_object($langs->trans("AddAction"),"calendar").'</a>';
    			$newemail='<table class="nobordernopadding"><tr><td>'.$newemail.' </td><td>&nbsp;'.$link.'</td></tr></table>';
    		}
    	}
    		
    	return $newemail;
    }
    
    /* For backward compatibility */
    function dolibarr_print_phone($phone,$country="FR",$cid=0,$socid=0,$addlink=0,$separ="&nbsp;")
    {
    	return dol_print_phone($phone,$country,$cid,$socid,$addlink,$separ);
    }
    
    /**
     * 	\brief 		Format phone numbers according to country
     * 	\param 		phone 		Phone number to format
     * 	\param 		country 	Country to use for formatting
     * 	\param 		cid 		Id of contact if known
     * 	\param 		socid 		Id of third party if known
     * 	\param 		addlink		0=no link to create action
     * 	\param 		separ 		separation between numbers for a better visibility example : xx.xx.xx.xx.xx
     * 	\return 	string 		Formated phone number
     */
    function dol_print_phone($phone,$country="FR",$cid=0,$socid=0,$addlink=0,$separ="&nbsp;")
    {
    	global $conf,$user,$langs;
    	
    	// Clean phone parameter
    	$phone = ereg_replace("[ .-]","",trim($phone));
    	if (empty($phone)) { return ''; }
    
    	$newphone=$phone;
    	if (strtoupper($country) == "FR")
    	{
    		// France
    		if (strlen($phone) == 10) {
    			$newphone=substr($newphone,0,2).$separ.substr($newphone,2,2).$separ.substr($newphone,4,2).$separ.substr($newphone,6,2).$separ.substr($newphone,8,2);
    		}
    		elseif (strlen($newphone) == 7)
    		{
    			$newphone=substr($newphone,0,3).$separ.substr($newphone,3,2).$separ.substr($newphone,5,2);
    		}
    		elseif (strlen($newphone) == 9)
    		{
    			$newphone=substr($newphone,0,2).$separ.substr($newphone,2,3).$separ.substr($newphone,5,2).$separ.substr($newphone,7,2);
    		}
    		elseif (strlen($newphone) == 11)
    		{
    			$newphone=substr($newphone,0,3).$separ.substr($newphone,3,2).$separ.substr($newphone,5,2).$separ.substr($newphone,7,2).$separ.substr($newphone,9,2);
    		}
    		elseif (strlen($newphone) == 12)
    		{
    			$newphone=substr($newphone,0,4).$separ.substr($newphone,4,2).$separ.substr($newphone,6,2).$separ.substr($newphone,8,2).$separ.substr($newphone,10,2);
    		}
    	}
    	
    	if (! empty($addlink))
    	{
    		if ($conf->clicktodial->enabled)
    		{
    			if (empty($user->clicktodial_loaded)) $user->fetch_clicktodial();
    			
    			if (empty($conf->global->CLICKTODIAL_URL)) $urlmask='ErrorClickToDialModuleNotConfigured'; 
    			else $urlmask=$conf->global->CLICKTODIAL_URL;
    			$url = sprintf($urlmask, urlencode($phone), urlencode($user->clicktodial_poste), urlencode($user->clicktodial_login), urlencode($user->clicktodial_password));
    			$newphone='<a href="'.$url.'">'.$newphone.'</a>';
    		}
    
    		if (($cid || $socid) && $conf->agenda->enabled && $user->rights->agenda->myactions->create)
    		{
    			$type='AC_TEL';
    			if ($addlink == 'AC_FAX') $type='AC_FAX';
    			$link='<a href="'.DOL_URL_ROOT.'/comm/action/fiche.php?action=create&amp;backtopage=1&amp;actioncode='.$type.'&amp;contactid='.$cid.'&amp;socid='.$socid.'">'.img_object($langs->trans("AddAction"),"calendar").'</a>';
    			$newphone='<table class="nobordernopadding"><tr><td>'.$newphone.' </td><td>&nbsp;'.$link.'</td></tr></table>';
    		}
    	}
    
    	return $newphone;
    }
    
    
    /**
     * Make a strlen call. Works even in mbstring module not enabled
     *
     * @param unknown_type $string
     * @param unknown_type $stringencoding
     * @return unknown
     */
    function dol_strlen($string,$stringencoding='')
    {
    	global $langs;
    	
    	if (empty($stringencoding)) $stringencoding=$langs->charset_output;
    
    	$ret='';
    	if (function_exists('mb_strlen'))
    	{
    		$ret=mb_strlen($string,$stringencoding);
    	}
    	else
    	{
    		$ret=strlen($string);
    	}
    	return $ret;
    }
    
    /**
     * Make a substring. Works even in mbstring module not enabled
     *
     * @param unknown_type $string
     * @param unknown_type $start
     * @param unknown_type $length
     * @param unknown_type $stringencoding
     * @return unknown
     */
    function dol_substr($string,$start,$length,$stringencoding='')
    {
    	global $langs;
    	
    	if (empty($stringencoding)) $stringencoding=$langs->charset_output;
    
    	$ret='';
    	if (function_exists('mb_substr'))
    	{
    		$ret=mb_substr($string,$start,$length,$stringencoding);
    	}
    	else
    	{
    		$ret=substr($string,$start,$length);
    	}
    	return $ret;
    }
    
    
    /* For backward compatibility */
    function dolibarr_trunc($string,$size=40,$trunc='right',$stringencoding='')
    {
    	return dol_trunc($string,$size,$trunc,$stringencoding);
    }
    
    /**
     *	\brief      Truncate a string to a particular length adding '...' if string larger than length
     *	\param      string				String to truncate
     *	\param      size				Max string size. 0 for no limit.
     *	\param		trunc				Where to trunc: right, left, middle
     *	\return     string				Truncated string
     *	\remarks	USE_SHORT_TITLE=0 can disable all truncings
     */
    function dol_trunc($string,$size=40,$trunc='right',$stringencoding='')
    {
    	global $conf;
    
    	if ($size==0) return $string;
    	if (! defined('USE_SHORT_TITLE') || (defined('USE_SHORT_TITLE') && USE_SHORT_TITLE))
    	{
    		// We go always here
    		if ($trunc == 'right')
    		{
    			if (dol_strlen($string,$stringencoding) > $size)
    			return dol_substr($string,0,$size,$stringencoding).'...';
    			else
    			return $string;
    		}
    		if ($trunc == 'middle')
    		{
    			if (dol_strlen($string,$stringencoding) > 2 && dol_strlen($string,$stringencoding) > $size)
    			{
    				$size1=round($size/2);
    				$size2=round($size/2);
    				return dol_substr($string,0,$size1,$stringencoding).'...'.dol_substr($string,dol_strlen($string,$stringencoding) - $size2,$size2,$stringencoding);
    			}
    			else
    			return $string;
    		}
    		if ($trunc == 'left')
    		{
    			if (dol_strlen($string,$stringencoding) > $size)
    			return '...'.dol_substr($string,dol_strlen($string,$stringencoding) - $size,$size,$stringencoding);
    			else
    			return $string;
    		}
    	}
    	else
    	{
    		return $string;
    	}
    }
    
    
    /**
     *	\brief      Affiche picto propre a une notion/module (fonction g�n�rique)
     *	\param      alt         Texte sur le alt de l'image
     *	\param      object      Objet pour lequel il faut afficher le logo (exemple: user, group, action, bill, contract, propal, product, ...)
     *	\return     string      Retourne tag img
     */
    function img_object($alt, $object)
    {
    	global $conf,$langs;
    	return '<img src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/object_'.$object.'.png" border="0" alt="'.$alt.'" title="'.$alt.'">';
    }
    
    /**
     *	\brief      Affiche picto (fonction generique)
     *	\param      alt         		Texte sur le alt de l'image
     *	\param      picto       		Nom de l'image a afficher (Si pas d'extension, on met '.png')
     *	\param		options				Attribut supplementaire a la balise img
     *	\param		pictoisfullpath		If 1, image path is a full path
     *	\return     string      		Retourne tag img
     */
    function img_picto($alt, $picto, $options='', $pictoisfullpath=0)
    {
    	global $conf;
    	if (! eregi('(\.png|\.gif)$',$picto)) $picto.='.png';
    	if ($pictoisfullpath) return '<img src="'.$picto.'" border="0" alt="'.$alt.'" title="'.$alt.'"'.($options?' '.$options:'').'>';
    	return '<img src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/'.$picto.'" border="0" alt="'.$alt.'" title="'.$alt.'"'.($options?' '.$options:'').'>';
    }
    
    /**
     \brief      Affiche picto (fonction g�n�rique)
     \param      alt         		Texte sur le alt de l'image
     \param      picto       		Nom de l'image a afficher (Si pas d'extension, on met '.png')
     \param		options				Attribut supplementaire a la balise img
     \param		pictoisfullpath		If 1, image path is a full path
     \return     string      		Retourne tag img
     */
    function img_picto_common($alt, $picto, $options='', $pictoisfullpath=0)
    {
    	global $conf;
    	if (! eregi('(\.png|\.gif)$',$picto)) $picto.='.png';
    	if ($pictoisfullpath) return '<img src="'.$picto.'" border="0" alt="'.$alt.'" title="'.$alt.'"'.($options?' '.$options:'').'>';
    	return '<img src="'.DOL_URL_ROOT.'/theme/common/'.$picto.'" border="0" alt="'.$alt.'" title="'.$alt.'"'.($options?' '.$options:'').'>';
    }
    
    /**
     *	\brief      Affiche logo action
     *	\param      alt         Texte sur le alt de l'image
     *	\param      numaction   Determine image action
     *	\return     string      Retourne tag img
     */
    function img_action($alt = "default", $numaction)
    {
    	global $conf,$langs;
    	if ($alt=="default") {
    		if ($numaction == -1) $alt=$langs->trans("ChangeDoNotContact");
    		if ($numaction == 0)  $alt=$langs->trans("ChangeNeverContacted");
    		if ($numaction == 1)  $alt=$langs->trans("ChangeToContact");
    		if ($numaction == 2)  $alt=$langs->trans("ChangeContactInProcess");
    		if ($numaction == 3)  $alt=$langs->trans("ChangeContactDone");
    	}
    	return '<img align="absmiddle" src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/stcomm'.$numaction.'.png" border="0" alt="'.$alt.'" title="'.$alt.'">';
    }
    
    
    /**
     \brief      Affiche logo fichier
     \param      alt         Texte sur le alt de l'image
     \return     string      Retourne tag img
     */
    function img_file($alt = "default")
    {
    	global $conf,$langs;
    	if ($alt=="default") $alt=$langs->trans("Show");
    	return '<img src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/file.png" border="0" alt="'.$alt.'" title="'.$alt.'">';
    }
    
    /**
     \brief      Affiche logo refresh
     \param      alt         Texte sur le alt de l'image
     \return     string      Retourne tag img
     */
    function img_refresh($alt = "default")
    {
    	global $conf,$langs;
    	if ($alt=="default") $alt=$langs->trans("Refresh");
    	return '<img src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/refresh.png" border="0" alt="'.$alt.'" title="'.$alt.'">';
    }
    
    /**
     *	\brief      Affiche logo dossier
     *	\param      alt         Texte sur le alt de l'image
     *	\return     string      Retourne tag img
     */
    function img_folder($alt = "default")
    {
    	global $conf,$langs;
    	if ($alt=="default") $alt=$langs->trans("Dossier");
    	return '<img src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/folder.png" border="0" alt="'.$alt.'" title="'.$alt.'">';
    }
    
    /**
     *	\brief      Affiche logo nouveau fichier
     *	\param      alt         Texte sur le alt de l'image
     *	\return     string      Retourne tag img
     */
    function img_file_new($alt = "default")
    {
    	global $conf,$langs;
    	if ($alt=="default") $alt=$langs->trans("Show");
    	return '<img src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/filenew.png" border="0" alt="'.$alt.'" title="'.$alt.'">';
    }
    
    /**
     \brief      Affiche logo pdf
     \param      alt         Texte sur le alt de l'image
     \param      $size       Taille de l'icone : 3 = 16x16px , 2 = 14x14px
     \return     string      Retourne tag img
     */
    function img_pdf($alt = "default",$size=3)
    {
    	global $conf,$langs;
    	if ($alt=="default") $alt=$langs->trans("Show");
    	return '<img src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/pdf'.$size.'.png" border="0" alt="'.$alt.'" title="'.$alt.'">';
    }
    
    /**
     *	\brief      Affiche logo +
     *	\param      alt         Texte sur le alt de l'image
     *	\return     string      Retourne tag img
     */
    function img_edit_add($alt = "default")
    {
    	global $conf,$langs;
    	if ($alt=="default") $alt=$langs->trans("Add");
    	return '<img src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/edit_add.png" border="0" alt="'.$alt.'" title="'.$alt.'">';
    }
    /**
     *	\brief      Affiche logo -
     *	\param      alt         Texte sur le alt de l'image
     *	\return     string      Retourne tag img
     */
    function img_edit_remove($alt = "default")
    {
    	global $conf,$langs;
    	if ($alt=="default") $alt=$langs->trans("Remove");
    	return '<img src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/edit_remove.png" border="0" alt="'.$alt.'" title="'.$alt.'">';
    }
    
    /**
     *	\brief      Affiche logo editer/modifier fiche
     *	\param      alt         Texte sur le alt de l'image
     *	\param      float       Si il faut y mettre le style "float: right"
     *	\return     string      Retourne tag img
     */
    function img_edit($alt = "default", $float=0, $other='')
    {
    	global $conf,$langs;
    	if ($alt=="default") $alt=$langs->trans("Modify");
    	$img='<img src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/edit.png" border="0" alt="'.$alt.'" title="'.$alt.'"';
    	if ($float) $img.=' style="float: right"';
    	if ($other) $img.=' '.$other;
    	$img.='>';
    	return $img;
    }
    
    /**
     *	\brief      Affiche logo voir fiche
     *	\param      alt         Texte sur le alt de l'image
     *	\param      float       Si il faut y mettre le style "float: right"
     *	\return     string      Retourne tag img
     */
    function img_view($alt = "default", $float=0, $other='')
    {
    	global $conf,$langs;
    	if ($alt=="default") $alt=$langs->trans("View");
    	$img='<img src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/view.png" border="0" alt="'.$alt.'" title="'.$alt.'"';
    	if ($float) $img.=' style="float: right"';
    	if ($other) $img.=' '.$other;
    	$img.='>';
    	return $img;
    }
    
    /**
     \brief      Affiche logo effacer
     \param      alt         Texte sur le alt de l'image
     \return     string      Retourne tag img
     */
    function img_delete($alt = "default")
    {
    	global $conf,$langs;
    	if ($alt=="default") $alt=$langs->trans("Delete");
    	return '<img src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/delete.png" border="0" alt="'.$alt.'" title="'.$alt.'">';
    }
    
    
    /**
     *	\brief      Affiche logo help avec curseur "?"
     *	\return     string      Retourne tag img
     */
    function img_help($usehelpcursor=1,$usealttitle=1)
    {
    	global $conf,$langs;
    	$s ='<img ';
    	if ($usehelpcursor) $s.='style="cursor: help;" ';
    	$s.='src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/info.png" border="0"';
    	if ($usealttitle) $s.=' alt="'.$langs->trans("Info").'" title="'.$langs->trans("Info").'"';
    	$s.='>';
    	return $s;
    }
    
    /**
     *	\brief      Affiche logo info
     *	\param      alt         Texte sur le alt de l'image
     *	\return     string      Retourne tag img
     */
    function img_info($alt = "default")
    {
    	global $conf,$langs;
    	if ($alt=="default") $alt=$langs->trans("Informations");
    	return '<img src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/info.png" border="0" alt="'.$alt.'" title="'.$alt.'">';
    }
    
    /**
     *	\brief      Affiche logo warning
     *	\param      alt         Texte sur le alt de l'image
     *	\param      float       Si il faut afficher le style "float: right"
     *	\return     string      Retourne tag img
     */
    function img_warning($alt = "default",$float=0)
    {
    	global $conf,$langs;
    	if ($alt=="default") $alt=$langs->trans("Warning");
    	$img='<img src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/warning.png" border="0" alt="'.$alt.'" title="'.$alt.'"';
    	if ($float) $img.=' style="float: right"';
    	$img.='>';
    
    	return $img;
    }
    
    /**
     \brief      Affiche logo error
     \param      alt         Texte sur le alt de l'image
     \return     string      Retourne tag img
     */
    function img_error($alt = "default")
    {
    	global $conf,$langs;
    	if ($alt=="default") $alt=$langs->trans("Error");
    	return '<img src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/error.png" border="0" alt="'.$alt.'" title="'.$alt.'">';
    }
    
    /**
     *	\brief      Affiche logo telephone
     *	\param      alt         Texte sur le alt de l'image
     *	\param		option		Choose of logo
     *	\return     string      Retourne tag img
     */
    function img_phone($alt = "default",$option=0)
    {
    	global $conf,$langs;
    	if ($alt=="default") $alt=$langs->trans("Call");
    	$img='call_out';
    	if ($option == 1) $img='call';
    	$img='object_commercial';
    	return '<img src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/'.$img.'.png" border="0" alt="'.$alt.'" title="'.$alt.'">';
    }
    
    
    /**
     \brief      Affiche logo suivant
     \param      alt         Texte sur le alt de l'image
     \return     string      Retourne tag img
     */
    function img_next($alt = "default")
    {
    	global $conf,$langs;
    	if ($alt=="default") {
    		$alt=$langs->trans("Next");
    	}
    	return '<img src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/next.png" border="0" alt="'.$alt.'" title="'.$alt.'">';
    }
    
    /**
     \brief      Affiche logo pr�c�dent
     \param      alt     Texte sur le alt de l'image
     \return     string      Retourne tag img
     */
    function img_previous($alt = "default")
    {
    	global $conf,$langs;
    	if ($alt=="default") $alt=$langs->trans("Previous");
    	return '<img src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/previous.png" border="0" alt="'.$alt.'" title="'.$alt.'">';
    }
    
    /**
     \brief      Affiche logo bas
     \param      alt         Texte sur le alt de l'image
     \param      selected    Affiche version "selected" du logo
     \return     string      Retourne tag img
     */
    function img_down($alt = "default", $selected=0)
    {
    	global $conf,$langs;
    	if ($alt=="default") $alt=$langs->trans("Down");
    	if ($selected) return '<img src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/1downarrow_selected.png" border="0" alt="'.$alt.'" title="'.$alt.'">';
    	else return '<img src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/1downarrow.png" border="0" alt="'.$alt.'" title="'.$alt.'">';
    }
    
    /**
     \brief      Affiche logo haut
     \param      alt         Texte sur le alt de l'image
     \param      selected    Affiche version "selected" du logo
     \return     string      Retourne tag img
     */
    function img_up($alt = "default", $selected=0)
    {
    	global $conf,$langs;
    	if ($alt=="default") $alt=$langs->trans("Up");
    	if ($selected) return '<img src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/1uparrow_selected.png" border="0" alt="'.$alt.'" title="'.$alt.'">';
    	else return '<img src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/1uparrow.png" border="0" alt="'.$alt.'" title="'.$alt.'">';
    }
    
    /**
     \brief      Affiche logo gauche
     \param      alt         Texte sur le alt de l'image
     \param      selected    Affiche version "selected" du logo
     \return     string      Retourne tag img
     */
    function img_left($alt = "default", $selected=0)
    {
    	global $conf,$langs;
    	if ($alt=="default") $alt=$langs->trans("Left");
    	if ($selected) return '<img src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/1leftarrow_selected.png" border="0" alt="'.$alt.'" title="'.$alt.'">';
    	else return '<img src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/1leftarrow.png" border="0" alt="'.$alt.'" title="'.$alt.'">';
    }
    
    /**
     \brief      Affiche logo droite
     \param      alt         Texte sur le alt de l'image
     \param      selected    Affiche version "selected" du logo
     \return     string      Retourne tag img
     */
    function img_right($alt = "default", $selected=0)
    {
    	global $conf,$langs;
    	if ($alt=="default") $alt=$langs->trans("Right");
    	if ($selected) return '<img src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/1rightarrow_selected.png" border="0" alt="'.$alt.'" title="'.$alt.'">';
    	else return '<img src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/1rightarrow.png" border="0" alt="'.$alt.'" title="'.$alt.'">';
    }
    
    /**
     \brief      Affiche logo tick
     \param      alt         Texte sur le alt de l'image
     \return     string      Retourne tag img
     */
    function img_tick($alt = "default")
    {
    	global $conf,$langs;
    	if ($alt=="default") $alt=$langs->trans("Active");
    	return '<img src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/tick.png" border="0" alt="'.$alt.'" title="'.$alt.'">';
    }
    
    /**
     \brief      Affiche le logo tick si allow
     \param      allow       Authorise ou non
     \return     string      Retourne tag img
     */
    function img_allow($allow)
    {
    	global $conf,$langs;
    	if ($alt=="default") $alt=$langs->trans("Active");
    
    	if ($allow == 1)
    	{
    		return '<img src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/tick.png" border="0" alt="'.$alt.'" title="'.$alt.'">';
    	}
    	else
    	{
    		return "-";
    	}
    }
    
    
    /**
     *	\brief      Show mime picto
     *	\param      file		Filename
     * 	\param		alt			Alternate text
     *	\return     string     	Return img tag
     */
    function img_mime($file,$alt='')
    {
    	$mime='other';
    	if (eregi('\.pdf',$file))        { $mime='pdf'; }
    	if (eregi('\.(html|htm)',$file)) { $mime='html'; }
    	if (eregi('\.txt',$file))        { $mime='other'; }
    	if (eregi('\.php',$file))        { $mime='php'; }
    	if (eregi('\.pl',$file))         { $mime='pl'; }
    	if (eregi('\.js',$file))         { $mime='jscript'; }
    	if (eregi('\.(png|bmp|jpg|jpeg|gif)',$file)) $mime='image';
    	if (eregi('\.(mp3|ogg|au)',$file))           $mime='audio';
    	if (eregi('\.(avi|mvw|divx|xvid)',$file))    $mime='video';
    	if (eregi('\.(zip|rar|gz|tgz|z|cab|bz2)',$file))       $mime='archive';
    	if (empty($alt)) $alt='Mime type: '.$mime;
    
    	$mime.='.png';
    	return '<img src="'.DOL_URL_ROOT.'/theme/common/mime/'.$mime.'" border="0" alt="'.$alt.'" title="'.$alt.'">';
    }
    
    
    /**
     *	\brief      Affiche info admin
     *	\param      text			Text info
     *	\param      infoonimgalt	Info is shown on alt of star picto
     *	\return		string			String with info text
     */
    function info_admin($texte,$infoonimgalt=0)
    {
    	global $conf,$langs;
    	$s='';
    	if ($infoonimgalt)
    	{
    		$s.=img_picto($texte,'star');
    	}
    	else
    	{
    		$s.='<div class="info">';
    		$s.=img_picto($langs->trans("InfoAdmin"),'star');
    		$s.=' ';
    		$s.=$texte;
    		$s.='</div>';
    	}
    	return $s;
    }
    
    
    /**
     *	\brief      Check permissions of a user to show a page and an object.
     *	\param      user      	  	User to check
     *	\param      feature			Feature to check (in most cases, it's module name)
     *	\param      objectid      	Object ID if we want to check permission on on object (optionnal)
     *	\param      dbtablename    	Table name where object is stored. Not used if objectid is null (optionnel)
     *	\param      feature2		Feature to check (second level of permission)
     *  \param      dbt_socfield    Field name for socid foreign key if not fk_soc. (optionnel)
     */
    function restrictedArea($user, $feature='societe', $objectid=0, $dbtablename='',$feature2='',$dbt_socfield='fk_soc')
    {
    	global $db;
    
    	//print "$user->id, $feature, $objectid, $dbtablename, ".$user->rights->societe->contact->lire;
    
    	// Check read permission from module
    	// TODO Replace "feature" param by permission for reading
    	$readok=1;
    	if ($feature == 'societe')
    	{
    		if (! $user->rights->societe->lire && ! $user->rights->fournisseur->lire) $readok=0;
    	}
    	else if ($feature == 'contact')
    	{
    		if (! $user->rights->societe->contact->lire) $readok=0;
    	}
    	else if ($feature == 'prelevement')
    	{
    		if (! $user->rights->prelevement->bons->lire) $readok=0;
    	}
    	else if ($feature == 'commande_fournisseur')
    	{
    		if (! $user->rights->fournisseur->commande->lire) $readok=0;
    	}
    	else if ($feature == 'cheque')
    	{
    		if (! $user->rights->banque->cheque) $readok=0;
    	}
    	else if (! empty($feature2))	// This should be used for future changes
    	{
    		if (empty($user->rights->$feature->$feature2->lire)
    		&& empty($user->rights->$feature->$feature2->read)) $readok=0;
    	}
    	else if (! empty($feature))		// This is for old permissions
    	{
    		if (empty($user->rights->$feature->lire)
    		&& empty($user->rights->$feature->read)) $readok=0;
    	}
    	if (! $readok) accessforbidden();
    	//print "Read access is ok";
    
    	// Check write permission from module
    	$createok=1;
    	if ($_GET["action"] == 'create' || $_POST["action"] == 'create')
    	{
    		if ($feature == 'societe')
    		{
    			if (! $user->rights->societe->creer && ! $user->rights->fournisseur->creer) $createok=0;
    		}
    		else if ($feature == 'contact')
    		{
    			if (! $user->rights->societe->contact->creer) $createok=0;
    		}
    		else if ($feature == 'prelevement')
    		{
    			if (! $user->rights->prelevement->bons->creer) $createok=0;
    		}
    		else if ($feature == 'commande_fournisseur')
    		{
    			if (! $user->rights->fournisseur->commande->creer) $createok=0;
    		}
    		else if ($feature == 'banque')
    		{
    			if (! $user->rights->banque->modifier) $createok=0;
    		}
    		else if ($feature == 'cheque')
    		{
    			if (! $user->rights->banque->cheque) $createok=0;
    		}
    		else if (! empty($feature2))	// This should be used for future changes
    		{
    			if (empty($user->rights->$feature->$feature2->creer)
    			&& empty($user->rights->$feature->$feature2->write)) $createok=0;
    		}
    		else if (! empty($feature))		// This is for old permissions
    		{
    			if (empty($user->rights->$feature->creer)
    			&& empty($user->rights->$feature->write)) $createok=0;
    		}
    		if (! $createok) accessforbidden();
    		//print "Write access is ok";
    	}
    
    	// If we have a particular object to check permissions on
    	if ($objectid)
    	{
    		$sql='';
    		// Check permission for external users
    		if ($user->societe_id > 0)
    		{
    			if ($feature == 'societe')
    			{
    				if ($user->societe_id <> $objectid) accessforbidden();
    			}
    			else
    			{
    				// If dbtable not defined, we use same name for table than module name
    				if (!$dbtablename) $dbtablename = $feature;
    
    				$sql = "SELECT dbt.fk_soc";
    				$sql.= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
    				$sql.= " WHERE dbt.rowid = ".$objectid;
    				$sql.= " AND dbt.fk_soc = ".$user->societe_id;
    			}
    		}
    		// Check permission for internal users that are restricted on their objects
    		else if (! $user->rights->societe->client->voir)
    		{
    			if ($feature == 'societe')
    			{
    				$sql = "SELECT sc.fk_soc";
    				$sql.= " FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc";
    				$sql.= " WHERE sc.fk_soc = ".$objectid." AND sc.fk_user = ".$user->id;
    			}
    			else
    			{
    				if (!$dbtablename) $dbtablename = $feature;	// Si dbtable non defini, meme nom que le module
    
    				$sql = "SELECT sc.fk_soc";
    				$sql.= " FROM ".MAIN_DB_PREFIX.$dbtablename." as dbt";
    				$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON sc.fk_soc = dbt.".$dbt_socfield;
    				$sql.= " WHERE dbt.rowid = ".$objectid;
    				$sql.= " AND IFNULL(sc.fk_user, ".$user->id.") = ".$user->id;
    			}
    		}
    
    		//print $sql;
    		if ($sql)
    		{
    			$resql=$db->query($sql);
    			if ($resql)
    			{
    				if ($db->num_rows($resql) == 0)	accessforbidden();
    			}
    			else
    			{
    				dolibarr_syslog("functions.lib::restrictedArea sql=".$sql, LOG_ERR);
    				accessforbidden();
    			}
    		}
    	}
    
    	return 1;
    }
    
    
    /**
     \brief      Affiche message erreur de type acces interdit et arrete le programme
     \param		message			Force error message
     \param		printheader		Affiche avant le header
     \remarks    L'appel a cette fonction termine le code.
     */
    function accessforbidden($message='',$printheader=1)
    {
    	global $user, $langs;
    	$langs->load("other");
    
    	if ($printheader && function_exists("llxHeader")) llxHeader();
    	print '<div class="error">';
    	if (! $message) print $langs->trans("ErrorForbidden");
    	else print $message;
    	print '</div>';
    	print '<br>';
    	if ($user->login)
    	{
    		print $langs->trans("CurrentLogin").': <font class="error">'.$user->login.'</font><br>';
    		print $langs->trans("ErrorForbidden2",$langs->trans("Home"),$langs->trans("Users"));
    	}
    	elseif (! empty($_SERVER["REMOTE_USER"]))
    	{
    		print $langs->trans("CurrentLogin").': <font class="error">'.$_SERVER["REMOTE_USER"]."</font><br>";
    		print $langs->trans("ErrorForbidden2",$langs->trans("Home"),$langs->trans("Users"));
    	}
    	else
    	{
    		print $langs->trans("ErrorForbidden3");
    	}
    	if (function_exists("llxFooter")) llxFooter();
    	exit(0);
    }
    
    
    /* For backward compatibility */
    function dolibarr_print_error($db='',$error='')
    {
    	return dol_print_error($db, $error);
    }
    
    /**
     *	\brief      Affiche message erreur system avec toutes les informations pour faciliter le diagnostic et la remont�e des bugs.
     *				On doit appeler cette fonction quand une erreur technique bloquante est rencontree.
     *				Toutefois, il faut essayer de ne l'appeler qu'au sein de pages php, les classes devant
     *				renvoyer leur erreur par l'intermediaire de leur propriete "error".
     *				\param      db      Database handler
     *				\param      error	Chaine erreur ou tableau de chaines erreur complementaires a afficher
     */
    function dol_print_error($db='',$error='')
    {
    	global $conf,$langs,$argv;
    	$syslog = '';
    
    	// Si erreur intervenue avant chargement langue
    	if (! $langs)
    	{
    		require_once(DOL_DOCUMENT_ROOT ."/translate.class.php");
    		$langs = new Translate("", $conf);
    	}
    	$langs->load("main");
    
    	if ($_SERVER['DOCUMENT_ROOT'])    // Mode web
    	{
    		print $langs->trans("DolibarrHasDetectedError").".<br>\n";
    		print $langs->trans("InformationToHelpDiagnose").":<br><br>\n";
    
    		print "<b>".$langs->trans("Dolibarr").":</b> ".DOL_VERSION."<br>\n";;
    		print "<b>".$langs->trans("Date").":</b> ".dolibarr_print_date(time(),'dayhourlog')."<br>\n";;
    		if (isset($conf->global->MAIN_FEATURES_LEVEL)) print "<b>".$langs->trans("LevelOfFeature").":</b> ".$conf->global->MAIN_FEATURES_LEVEL."<br>\n";;
    		print "<b>".$langs->trans("Server").":</b> ".$_SERVER["SERVER_SOFTWARE"]."<br>\n";;
    		print "<b>".$langs->trans("RequestedUrl").":</b> ".$_SERVER["REQUEST_URI"]."<br>\n";;
    		print "<b>".$langs->trans("Referer").":</b> ".$_SERVER["HTTP_REFERER"]."<br>\n";;
    		$syslog.="url=".$_SERVER["REQUEST_URI"];
    		$syslog.=", query_string=".$_SERVER["QUERY_STRING"];
    	}
    	else                              // Mode CLI
    	{
    		print '> '.$langs->transnoentities("ErrorInternalErrorDetected").":\n".$argv[0]."\n";
    		$syslog.="pid=".getmypid();
    	}
    
    	if (is_object($db))
    	{
    		if ($_SERVER['DOCUMENT_ROOT'])  // Mode web
    		{
    			print "<br>\n";
    			print "<b>".$langs->trans("DatabaseTypeManager").":</b> ".$db->type."<br>\n";
    			print "<b>".$langs->trans("RequestLastAccessInError").":</b> ".($db->lastqueryerror()?$db->lastqueryerror():$langs->trans("ErrorNoRequestInError"))."<br>\n";
    			print "<b>".$langs->trans("ReturnCodeLastAccessInError").":</b> ".($db->lasterrno()?$db->lasterrno():$langs->trans("ErrorNoRequestInError"))."<br>\n";
    			print "<b>".$langs->trans("InformationLastAccessInError").":</b> ".($db->lasterror()?$db->lasterror():$langs->trans("ErrorNoRequestInError"))."<br>\n";
    		}
    		else                            // Mode CLI
    		{
    			print '> '.$langs->transnoentities("DatabaseTypeManager").":\n".$db->type."\n";
    			print '> '.$langs->transnoentities("RequestLastAccessInError").":\n".($db->lastqueryerror()?$db->lastqueryerror():$langs->trans("ErrorNoRequestInError"))."\n";
    			print '> '.$langs->transnoentities("ReturnCodeLastAccessInError").":\n".($db->lasterrno()?$db->lasterrno():$langs->trans("ErrorNoRequestInError"))."\n";
    			print '> '.$langs->transnoentities("InformationLastAccessInError").":\n".($db->lasterror()?$db->lasterror():$langs->trans("ErrorNoRequestInError"))."\n";
    
    		}
    		$syslog.=", sql=".$db->lastquery();
    		$syslog.=", db_error=".$db->lasterror();
    	}
    
    	if ($error)
    	{
    		$langs->load("errors");
    			
    		if (is_array($error)) $errors=$error;
    		else $errors=array($error);
    
    		foreach($errors as $msg)
    		{
    			$msg=$langs->trans($msg);
    			if ($_SERVER['DOCUMENT_ROOT'])  // Mode web
    			{
    				print "<b>".$langs->trans("Message").":</b> ".$msg."<br>\n" ;
    			}
    			else                            // Mode CLI
    			{
    				print '> '.$langs->transnoentities("Message").":\n".$msg."\n" ;
    			}
    			$syslog.=", msg=".$msg;
    		}
    	}
    
    	dolibarr_syslog("Error ".$syslog, LOG_ERR);
    }
    
    
    /**
     *	\brief  Deplacer les fichiers telecharg�s, apres quelques controles divers
     *	\param	src_file			Source filename
     *	\param	dest_file			Target filename
     * 	\param	allowoverwrite		Overwrite if exists
     *	\return int         		>0 if OK, <0 if KO, Name of virus if virus found
     */
    function dol_move_uploaded_file($src_file, $dest_file, $allowoverwrite)
    {
    	global $conf;
    
    	$file_name = $dest_file;
    
    	// If we need to make a virus scan
    	if ($conf->global->MAIN_USE_AVSCAN)
    	{
    		require_once(DOL_DOCUMENT_ROOT.'/lib/security.lib.php');
    		$malware = dol_avscan_file($src_file);
    		if ($malware) return $malware;
    	}
    
    	// Security:
    	// On renomme les fichiers avec extention script web car si on a mis le rep
    	// documents dans un rep de la racine web (pas bien), cela permet d'executer
    	// du code a la demande.
    	if (eregi('\.htm|\.html|\.php|\.pl|\.cgi$',$file_name))
    	{
    		$file_name.= '.noexe';
    	}
    
    	// Security:
    	// On interdit fichiers caches, remontees de repertoire ainsi que les pipes dans
    	// les noms de fichiers.
    	if (eregi('^\.',$src_file) || eregi('\.\.',$src_file) || eregi('[<>|]',$src_file))
    	{
    		dolibarr_syslog("Refused to deliver file ".$src_file);
    		return -1;
    	}
    
    	// Security:
    	// On interdit fichiers caches, remontees de repertoire ainsi que les pipe dans
    	// les noms de fichiers.
    	if (eregi('^\.',$dest_file) || eregi('\.\.',$dest_file) || eregi('[<>|]',$dest_file))
    	{
    		dolibarr_syslog("Refused to deliver file ".$dest_file);
    		return -1;
    	}
    
    	// Check if destination file already exists
    	if (! $allowoverwrite)
    	{
    		if (file_exists($file_name))
    		{
    			dolibarr_syslog("Functions.lib::dol_move_uploaded_file File ".$file_name." already exists", LOG_WARNING);
    			return -2;
    		}
    	}
    
    	// Move file
    	$return=move_uploaded_file($src_file, $file_name);
    	if ($return)
    	{
    		if (! empty($conf->global->MAIN_UMASK)) @chmod($file_name, octdec($conf->global->MAIN_UMASK));
    		dolibarr_syslog("Functions.lib::dol_move_uploaded_file Success to move ".$src_file." to ".$file_name." - Umask=".$conf->global->MAIN_UMASK, LOG_DEBUG);
    		return 1;
    	}
    	else
    	{
    		dolibarr_syslog("Functions.lib::dol_move_uploaded_file Failed to move ".$src_file." to ".$file_name, LOG_ERR);
    		return -3;
    	}
    }
    
    
    /**
     *	\brief      Show title line of an array
     *	\param	    name        libelle champ
     *	\param	    file        url pour clic sur tri
     *	\param	    field       champ de tri
     *	\param	    begin       ("" par defaut)
     *	\param	    options     ("" par defaut)
     *	\param      td          options de l'attribut td ("" par defaut)
     *	\param      sortfield   nom du champ sur lequel est effectue le tri du tableau
     *	\param      sortorder   ordre du tri
     */
    function print_liste_field_titre($name, $file, $field, $begin="", $options="", $td="", $sortfield="", $sortorder="")
    {
    	global $conf;
    	//print "$name, $file, $field, $begin, $options, $td, $sortfield, $sortorder<br>\n";
    
    	// Le champ de tri est mis en evidence.
    	// Exemple si (sortfield,field)=("nom","xxx.nom") ou (sortfield,field)=("nom","nom")
    	if ($sortfield == $field || $sortfield == ereg_replace("^[^\.]+\.","",$field))
    	{
    		print '<td class="liste_titre_sel" '. $td.'>';
    	}
    	else
    	{
    		print '<td class="liste_titre" '. $td.'>';
    	}
    	print $name;
    
    	// If this is a sort field
    	if ($field)
    	{
    		//print "&nbsp;";
    		print '<img width="2" src="'.DOL_URL_ROOT.'/theme/common/transparent.png">';
    		if (! $sortorder)
    		{
    			print '<a href="'.$file.'?sortfield='.$field.'&amp;sortorder=asc&amp;begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
    			print '<a href="'.$file.'?sortfield='.$field.'&amp;sortorder=desc&amp;begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
    		}
    		else
    		{
    			if ($field != $sortfield)
    			{
    				print '<a href="'.$file.'?sortfield='.$field.'&amp;sortorder=asc&amp;begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
    				print '<a href="'.$file.'?sortfield='.$field.'&amp;sortorder=desc&amp;begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
    			}
    			else {
    				$sortorder=strtoupper($sortorder);
    				if ($sortorder == 'DESC' ) {
    					print '<a href="'.$file.'?sortfield='.$field.'&amp;sortorder=asc&amp;begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
    					print '<a href="'.$file.'?sortfield='.$field.'&amp;sortorder=desc&amp;begin='.$begin.$options.'">'.img_up("Z-A",1).'</a>';
    				}
    				if ($sortorder == 'ASC' ) {
    					print '<a href="'.$file.'?sortfield='.$field.'&amp;sortorder=asc&amp;begin='.$begin.$options.'">'.img_down("A-Z",1).'</a>';
    					print '<a href="'.$file.'?sortfield='.$field.'&amp;sortorder=desc&amp;begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
    				}
    			}
    		}
    	}
    	print "</td>";
    }
    
    /**
     *	\brief  Affichage d'un titre
     *	\param	titre			Le titre a afficher
     */
    function print_titre($titre)
    {
    	print '<div class="titre">'.$titre.'</div>';
    }
    
    /**
     *	\brief  Affichage d'un titre d'une fiche, align� a gauche
     *	\param	titre				Le titre a afficher
     *	\param	mesg				Message suplementaire a afficher a droite
     *	\param	picto				Icon to use before title (should be a 32x32 transparent png file)
     *	\param	pictoisfullpath		1=Icon name is a full absolute url of image
     */
    function print_fiche_titre($titre, $mesg='', $picto='title.gif', $pictoisfullpath=0)
    {
    	print "\n";
    	print '<table width="100%" border="0" class="notopnoleftnoright" style="margin-bottom: 2px;"><tr>';
    	if ($picto && $titre) print '<td class="nobordernopadding" width="40" align="left" valign="middle">'.img_picto('',$picto, '', $pictoisfullpath).'</td>';
    	print '<td class="nobordernopadding" valign="middle">';
    	print '<div class="titre" valign="middle">'.$titre.'</div>';
    	print '</td>';
    	if (strlen($mesg))
    	{
    		print '<td class="nobordernopadding" align="right" valign="middle"><b>'.$mesg.'</b></td>';
    	}
    	print '</tr></table>'."\n";
    }
    
    /**
     *	\brief  Print a title with navigation controls for pagination
     *	\param	titre				Title to show (required)
     *	\param	page				Numero of page (required)
     *	\param	file				Url of page (required)
     *	\param	options         	parametres complementaires lien ('' par defaut)
     *	\param	sortfield       	champ de tri ('' par defaut)
     *	\param	sortorder       	ordre de tri ('' par defaut)
     *	\param	center          	chaine du centre ('' par defaut)
     *	\param	num					number of records found by select with limit+1
     *	\param	totalnboflines		Total number of records/lines for all pages (if known)
     *	\param	picto				Icon to use before title (should be a 32x32 transparent png file)
     *	\param	pictoisfullpath		1=Icon name is a full absolute url of image
     */
    function print_barre_liste($titre, $page, $file, $options='', $sortfield='', $sortorder='', $center='', $num=-1, $totalnboflines=0, $picto='title.gif', $pictoisfullpath=0)
    {
    	global $conf,$langs;
    
    	if ($num > $conf->liste_limit or $num == -1)
    	{
    		$nextpage = 1;
    	}
    	else
    	{
    		$nextpage = 0;
    	}
    
    	print '<table width="100%" border="0" class="notopnoleftnoright" style="margin-bottom: 2px;"><tr>';
    	
    	$pagelist = '';
    
    	// Left
    	if ($page > 0 || $num > $conf->liste_limit)
    	{
    		if ($totalnboflines)
    		{
    			if ($picto && $titre) print '<td class="nobordernopadding" width="40" align="left" valign="middle">'.img_picto('',$picto, '', $pictoisfullpath).'</td>';
    			print '<td class="nobordernopadding">';
    			print '<div class="titre">'.$titre.'</div>';
    			print '</td>';
    
    			$maxnbofpage=10;
    
    			$nbpages=ceil($totalnboflines/$conf->liste_limit);
    			$cpt=($page-$maxnbofpage);
    			if ($cpt < 0) { $cpt=0; }
    			$pagelist.=$langs->trans('Page');
    			if ($cpt>=1)
    			{
    				$pagelist.=' <a href="'.$file.'?page=0'.$options.'&amp;sortfield='.$sortfield.'&amp;sortorder='.$sortorder.'">1</a>';
    				if ($cpt >= 2) $pagelist.=' ...';
    			}
    			do
    			{
    				if($cpt==$page)
    				{
    					$pagelist.= ' <u>'.($page+1).'</u>';
    				}
    				else
    				{
    					$pagelist.= ' <a href="'.$file.'?page='.$cpt.$options.'&amp;sortfield='.$sortfield.'&amp;sortorder='.$sortorder.'">'.($cpt+1).'</a>';
    				}
    				$cpt++;
    			}
    			while ($cpt < $nbpages && $cpt<=$page+$maxnbofpage);
    			if ($cpt<$nbpages)
    			{
    				if ($cpt<$nbpages-1) $pagelist.= ' ...';
    				$pagelist.= ' <a href="'.$file.'?page='.($nbpages-1).$options.'&amp;sortfield='.$sortfield.'&amp;sortorder='.$sortorder.'">'.$nbpages.'</a>';
    			}
    		}
    		else
    		{
    			if ($picto && $titre) print '<td class="nobordernopadding" width="40" align="left" valign="middle">'.img_picto('',$picto, '', $pictoisfullpath).'</td>';
    			print '<td class="nobordernopadding">';
    			print '<div class="titre">'.$titre.'</div>';
    			$pagelist.= $langs->trans('Page').' '.($page+1);
    			print '</td>';
    		}
    	}
    	else
    	{
    		if ($picto && $titre) print '<td class="nobordernopadding" width="40" align="left" valign="middle">'.img_picto('',$picto, '', $pictoisfullpath).'</td>';
    		print '<td class="nobordernopadding"><div class="titre">'.$titre.'</div></td>';
    	}
    
    	// Center
    	if ($center)
    	{
    		print '<td class="nobordernopadding" align="left" valign="middle">'.$center.'</td>';
    	}
    
    	// Right
    	print '<td class="nobordernopadding" align="right" valign="middle">';
    	if ($sortfield) $options .= "&amp;sortfield=".$sortfield;
    	if ($sortorder) $options .= "&amp;sortorder=".$sortorder;
    	// Affichage des fleches de navigation
    	print_fleche_navigation($page,$file,$options,$nextpage,$pagelist);
    	print '</td>';
    
    	print '</tr></table>';
    }
    
    /**
     *	\brief  	Fonction servant a afficher les fleches de navigation dans les pages de listes
     *	\param	page				Numero of page
     *	\param	file				Lien
     *	\param	options         	Autres parametres d'url a propager dans les liens ("" par defaut)
     *	\param	nextpage	    	Faut-il une page suivante
     *	\param	betweenarraows		HTML Content to show between arrows
     */
    function print_fleche_navigation($page,$file,$options='',$nextpage,$betweenarrows='')
    {
    	global $conf, $langs;
    	if ($page > 0)
    	{
    		print '<a href="'.$file.'?page='.($page-1).$options.'">'.img_previous($langs->trans("Previous")).'</a>';
    	}
    	if ($betweenarrows) print ($page > 0?' ':'').$betweenarrows.($nextpage>0?' ':'');
    	if ($nextpage > 0)
    	{
    		print '<a href="'.$file.'?page='.($page+1).$options.'">'.img_next($langs->trans("Next")).'</a>';
    	}
    }
    
    
    /**
     *	\brief  Effacement d'un fichier
     *	\param	file			Fichier a effacer ou masque de fichier a effacer
     *	\param	boolean			true if file deleted, false if error
     */
    function dol_delete_file($file)
    {
    	$ok=true;
    	foreach (glob($file) as $filename)
    	{
    		$ok=unlink($filename);
    		if ($ok) dolibarr_syslog("Removed file $filename",LOG_DEBUG);
    		else dolibarr_syslog("Failed to remove file $filename",LOG_ERR);
    	}
    	return $ok;
    }
    
    /**
     *	\brief  	Remove a directory
     *	\param		file			Directory to delete
     * 	\return		boolean			True if success, false if error
     * 	\remarks	If directory is not empty, return false
     */
    function dol_delete_dir($dir)
    {
    	return rmdir($dir);
    }
    
    /**
     *	\brief  	Remove a directory $dir and its subdirectories
     *	\param		file			Dir to delete
     *	\param		count			Counter to count nb of deleted elements
     *	\return		int				Number of files and directory removed
     */
    function dol_delete_dir_recursive($dir,$count=0)
    {
    	if ($handle = opendir("$dir"))
    	{
    		while (false !== ($item = readdir($handle)))
    		{
    			if ($item != "." && $item != "..")
    			{
    				if (is_dir("$dir/$item"))
    				{
    					$count=dol_delete_dir_recursive("$dir/$item",$count);
    				}
    				else
    				{
    					unlink("$dir/$item");
    					$count++;
    					//echo " removing $dir/$item<br>\n";
    				}
    			}
    		}
    		closedir($handle);
    		rmdir($dir);
    		$count++;
    		//echo "removing $dir<br>\n";
    	}
    
    	//echo "return=".$count;
    	return $count;
    }
    
    
    /**
     *		\brief      Fonction qui retourne un taux de tva formate pour visualisation
     *		\remarks    Fonction utilisee dans les pdf et les pages html
     *		\param	    rate			Rate value to format (19.6 19,6 19.6% 19,6%,...)
     *		\param		foundpercent	Add a percent % sign in output
     *		\param		info_bits		Miscellanous information on vat
     *		\return		string			Chaine avec montant formate (19,6 ou 19,6% ou 8.5% *)
     */
    function vatrate($rate,$addpercent=false,$info_bits=0)
    {
    	// Test for compatibility
    	if (eregi('%',$rate))
    	{
    		$rate=eregi_replace('%','',$rate);
    		$addpercent=true;
    	}
    	if (eregi('\*',$rate) || eregi(MAIN_LABEL_MENTION_NPR,$rate))
    	{
    		$rate=eregi_replace('\*','',$rate);
    		$info_bits |= 1;
    	}
    
    	$ret=price($rate,0,'',0,0).($addpercent?'%':'');
    	if ($info_bits & 1) $ret.=' '.MAIN_LABEL_MENTION_NPR;
    	return $ret;
    }
    
    
    /**
     *		\brief      Fonction qui formate un montant pour visualisation
     *		\remarks    Fonction utilisee dans les pdf et les pages html
     *		\param	    amount			Montant a formater
     *		\param	    html			Type de formatage, html ou pas (par defaut)
     *		\param	    outlangs		Objet langs pour formatage text
     *		\param		trunc			1=Tronque affichage si trop de decimales,0=Force le non troncage
     *		\param		rounding		Nbre decimals minimum.
     *		\return		string			Chaine avec montant formate
     *		\seealso	price2num		Revert function of price
     */
    function price($amount, $html=0, $outlangs='', $trunc=1, $rounding=2)
    {
    	global $langs,$conf;
    
    	$nbdecimal=$rounding;
    
    	// Output separators by default (french)
    	$dec=','; $thousand=' ';
    
    	// If $outlangs not forced, we use use language
    	if (! is_object($outlangs)) $outlangs=$langs;
    
    	if ($outlangs->trans("SeparatorDecimal") != "SeparatorDecimal")  $dec=$outlangs->trans("SeparatorDecimal");
    	if ($outlangs->trans("SeparatorThousand")!= "SeparatorThousand") $thousand=$outlangs->trans("SeparatorThousand");
    	//print "amount=".$amount." html=".$html." trunc=".$trunc." nbdecimal=".$nbdecimal." dec=".$dec." thousand=".$thousand;
    
    	//print "amount=".$amount."-";
    	$amount = ereg_replace(',','.',$amount);	// should be useless
    	//print $amount."-";
    	$datas = split('\.',$amount);
    	$decpart = $datas[1];
    	$decpart = eregi_replace('0+$','',$decpart);	// Supprime les 0 de fin de partie decimale
    	//print "decpart=".$decpart."<br>";
    	$end='';
    
    	// We increase nbdecimal if there is more decimal than asked (to not loose information)
    	if (strlen($decpart) > $nbdecimal) $nbdecimal=strlen($decpart);
    	// Si on depasse max
    	if ($trunc && $nbdecimal > $conf->global->MAIN_MAX_DECIMALS_SHOWN)
    	{
    		$nbdecimal=$conf->global->MAIN_MAX_DECIMALS_SHOWN;
    		if (eregi('\.\.\.',$conf->global->MAIN_MAX_DECIMALS_SHOWN))
    		{
    			// Si un affichage est tronque, on montre des ...
    			$end='...';
    		}
    	}
    
    	// Format number
    	if ($html)
    	{
    		$output=ereg_replace(' ','&nbsp;',number_format($amount, $nbdecimal, $dec, $thousand));
    	}
    	else
    	{
    		$output=number_format($amount, $nbdecimal, $dec, $thousand);
    	}
    	$output.=$end;
    
    	return $output;
    }
    
    /**
     *	\brief     		Fonction qui retourne un numerique conforme SQL, depuis un montant issu d'une saisie
     *					utilisateur.
     *	\remarks   		Fonction a appeler sur montants saisis avant un insert en base
     *	\param	    	amount		Montant a formater
     *	\param	    	rounding	'MU'=Round to Max unit price (MAIN_MAX_DECIMALS_UNIT)
     *								'MT'=Round to Max for totals with Tax (MAIN_MAX_DECIMALS_TOT)
     *								'MS'=Round to Max Shown (MAIN_MAX_DECIMALS_SHOWN)
     *								''=No rounding
     *	\return			string		Montant au format numerique universel et SQL (Exemple: '99.99999')
     *	\seealso		price		Fonction inverse de price2num
     */
    function price2num($amount,$rounding='',$alreadysqlnb=-1)
    {
    	global $langs,$conf;
    
    	// Round PHP function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56'
    	// Numbers must be '1234.56'
    	// Decimal delimiter for database SQL request must be '.'
    	$dec=','; $thousand=' ';
    	if ($langs->trans("SeparatorDecimal") != "SeparatorDecimal")  $dec=$langs->trans("SeparatorDecimal");
    	if ($langs->trans("SeparatorThousand")!= "SeparatorThousand") $thousand=$langs->trans("SeparatorThousand");
    
    	// Convert value to universal number format (no thousand separator, '.' as decimal separator)
    	if ($alreadysqlnb != 1)	// If not a PHP number or unknown, we change format
    	{
    		//print 'ZZ'.$nbofdec.'=>'.$amount.'<br>';		
    		
    		// Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
    		// to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup.
    		if (is_numeric($amount)) 
    		{
    			$nbofdec=max(0,strlen($amount-intval($amount))-2);
    			$amount=number_format($amount,$nbofdec,$dec,$thousand);
    		}
    		//print "QQ".$amount.'<br>';
    		
    		// Now make replace (the main goal of function)
    		if ($thousand != ',' && $thousand != '.') $amount=str_replace(',','.',$amount);	// To accept 2 notations for french users
    		$amount=str_replace(' ','',$amount);		// To avoid spaces
    		$amount=str_replace($thousand,'',$amount);	// Replace of thousand before replace of dec to avoid pb if thousand is .
    		$amount=str_replace($dec,'.',$amount);
    	}
    
    	// Now if rounding required
    	if ($rounding)
    	{
    		$nbofdectoround='';
    		if ($rounding == 'MU')     $nbofdectoround=$conf->global->MAIN_MAX_DECIMALS_UNIT;
    		elseif ($rounding == 'MT') $nbofdectoround=$conf->global->MAIN_MAX_DECIMALS_TOT;
    		elseif ($rounding == 'MS') $nbofdectoround=$conf->global->MAIN_MAX_DECIMALS_SHOWN; 
    		elseif ($rounding == '2')  $nbofdectoround=2; 	// For admin info page
    		if (strlen($nbofdectoround)) $amount = round($amount,$nbofdectoround);	// $nbofdectoround can be 0.
    		else return 'ErrorBadParameterProvidedToFunction';
    		//print 'ZZ'.$nbofdec.'-'.$nbofdectoround.'=>'.$amount.'<br>';		
    		
    		// Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
    		// to format defined by LC_NUMERIC after a calculation and we want source format to be defined by Dolibarr setup.
    		if (is_numeric($amount))
    		{
    			$nbofdec=max(0,strlen($amount-intval($amount))-2);
    			$amount=number_format($amount,min($nbofdec,$nbofdectoround),$dec,$thousand);		// Convert amount to format with dolibarr dec and thousand
    		}
    		//print "RR".$amount.'<br>';
    
    		// Always make replace because each math function (like round) replace
    		// with local values and we want a number that has a SQL string format x.y
    		if ($thousand != ',' && $thousand != '.') $amount=str_replace(',','.',$amount);	// To accept 2 notations for french users
    		$amount=str_replace(' ','',$amount);		// To avoid spaces
    		$amount=str_replace($thousand,'',$amount);	// Replace of thousand before replace of dec to avoid pb if thousand is .
    		$amount=str_replace($dec,'.',$amount);
    	}
    	
    	return $amount;
    }
    
    
    /**
     *	\brief	Return vat rate of a product in a particular selling country
     */
    function get_product_vat_for_country($idprod, $countrycode)
    {
    	global $db;
    
    	$product=new Product($db);
    	$product->fetch($idprod);
    
    	// \TODO Read rate according to countrycode
    	// For the moment only one rate supported
    
    	return $product->tva_tx;
    }
    
    
    /**
     *	\brief      	Fonction qui renvoie la tva d'une ligne (en fonction du vendeur, acheteur et taux du produit)
     *	\remarks    	Si vendeur non assujeti a TVA, TVA par d�faut=0. Fin de r�gle.
     *					Si le (pays vendeur = pays acheteur) alors TVA par d�faut=TVA du produit vendu. Fin de r�gle.
     *					Si (vendeur et acheteur dans Communaute europeenne) et (bien vendu = moyen de transports neuf comme auto, bateau, avion) alors TVA par d�faut=0 (La TVA doit �tre pay� par acheteur au centre d'impots de son pays et non au vendeur). Fin de r�gle.
     *					Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = particulier ou entreprise sans num TVA intra) alors TVA par d�faut=TVA du produit vendu. Fin de r�gle
     *					Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = entreprise avec num TVA) intra alors TVA par d�faut=0. Fin de r�gle
     *					Sinon TVA proposee par defaut=0. Fin de regle.
     *	\param      	societe_vendeuse    	Objet societe vendeuse
     *	\param      	societe_acheteuse   	Objet societe acheteuse
     *	\param      	taux_produit        	Taux par defaut du produit vendu (old way to get product vat rate)
     *	\param      	idprod					Id product (new way to get product vat rate)
     *	\return     	float               	Taux de tva a appliquer, -1 si ne peut etre d�termin�
     */
    function get_default_tva($societe_vendeuse, $societe_acheteuse, $taux_produit, $idprod=0)
    {
    	if (!is_object($societe_vendeuse)) return -1;
    	if (!is_object($societe_acheteuse)) return -1;
    
    	dolibarr_syslog("get_default_tva vendeur_assujeti=".$societe_vendeuse->tva_assuj." pays_vendeur=".$societe_vendeuse->pays_code.", seller in cee=".$societe_vendeuse->isInEEC().", pays_acheteur=".$societe_acheteuse->pays_code.", buyer in cee=".$societe_acheteuse->isInEEC().", taux_produit(deprecated)=".$taux_produit.", idprod=".$idprod);
    
    	// Si vendeur non assujeti a TVA (tva_assuj vaut 0/1 ou franchise/reel)
    	if (is_numeric($societe_vendeuse->tva_assuj) && ! $societe_vendeuse->tva_assuj) return 0;
    	if (! is_numeric($societe_vendeuse->tva_assuj) && $societe_vendeuse->tva_assuj=='franchise') return 0;
    
    	// Si le (pays vendeur = pays acheteur) alors la TVA par d�faut=TVA du produit vendu. Fin de r�gle.
    	//if (is_object($societe_acheteuse) && ($societe_vendeuse->pays_id == $societe_acheteuse->pays_id) && ($societe_acheteuse->tva_assuj == 1 || $societe_acheteuse->tva_assuj == 'reel'))
    	// Le test ci-dessus ne devrait pas etre necessaire. Me signaler l'exemple du cas juridique concercn� si le test suivant n'est pas suffisant.
    	if ($societe_vendeuse->pays_id == $societe_acheteuse->pays_id)
    	{
    		if ($idprod) return get_product_vat_for_country($idprod,$societe_vendeuse->pays_code);
    		if (strlen($taux_produit) == 0) return -1;	// Si taux produit = '', on ne peut d�terminer taux tva
    		return $taux_produit;
    	}
    
    	// Si (vendeur et acheteur dans Communaut� europ�enne) et (bien vendu = moyen de transports neuf comme auto, bateau, avion) alors TVA par d�faut=0 (La TVA doit �tre pay� par l'acheteur au centre d'impots de son pays et non au vendeur). Fin de r�gle.
    	// Non g�r�
    
    	// Si (vendeur et acheteur dans Communaut� europ�enne) et (acheteur = particulier ou entreprise sans num TVA intra) alors TVA par d�faut=TVA du produit vendu. Fin de r�gle
    	if (($societe_vendeuse->isInEEC() && $societe_acheteuse->isInEEC()) && ! $societe_acheteuse->tva_intra)
    	{
    		if ($idprod) return get_product_vat_for_country($idprod,$societe_vendeuse->pays_code);
    		if (strlen($taux_produit) == 0) return -1;	// Si taux produit = '', on ne peut d�terminer taux tva
    		return $taux_produit;
    	}
    
    	// Si (vendeur et acheteur dans Communaut� europ�enne) et (acheteur = entreprise avec num TVA intra) alors TVA par d�faut=0. Fin de r�gle
    	if (($societe_vendeuse->isInEEC() && $societe_acheteuse->isInEEC()) && $societe_acheteuse->tva_intra)
    	{
    		return 0;
    	}
    
    	// Sinon la TVA propos�e par d�faut=0. Fin de r�gle.
    	// Rem: Cela signifie qu'au moins un des 2 est hors Communaut� europ�enne et que le pays diff�re
    	return 0;
    }
    
    
    /**
     \brief      	Fonction qui renvoie si tva doit etre tva percue r�cup�rable
     \remarks    	Si vendeur non assujeti a TVA, TVA par d�faut=0. Fin de r�gle.
     Si le (pays vendeur = pays acheteur) alors TVA par d�faut=TVA du produit vendu. Fin de r�gle.
     Si (vendeur et acheteur dans Communaut� europ�enne) et (bien vendu = moyen de transports neuf comme auto, bateau, avion) alors TVA par d�faut=0 (La TVA doit �tre pay� par acheteur au centre d'impots de son pays et non au vendeur). Fin de r�gle.
     Si (vendeur et acheteur dans Communaut� europ�enne) et (acheteur = particulier ou entreprise sans num TVA intra) alors TVA par d�faut=TVA du produit vendu. Fin de r�gle
     Si (vendeur et acheteur dans Communaut� europ�enne) et (acheteur = entreprise avec num TVA) intra alors TVA par d�faut=0. Fin de r�gle
     Sinon TVA propos�e par d�faut=0. Fin de r�gle.
     \param      	societe_vendeuse    	Objet soci�t� vendeuse
     \param      	societe_acheteuse   	Objet soci�t� acheteuse
     \param      	taux_produit        	Taux par defaut du produit vendu
     \return     	float               	0 or 1
     */
    function get_default_npr($societe_vendeuse, $societe_acheteuse, $taux_produit)
    {
    
    	return 0;
    }
    
    
    /**
     \brief  Renvoie oui ou non dans la langue choisie
     \param	yesno			Variable pour test si oui ou non
     \param	case			1=Yes/No, 0=yes/no
     \param	color			0=texte only, 1=Text is format with a color font style
     */
    function yn($yesno, $case=1, $color=0)
    {
    	global $langs;
    	$result='unknown';
    	if ($yesno == 1 || strtolower($yesno) == 'yes' || strtolower($yesno) == 'true') 	// A mettre avant test sur no a cause du == 0
    	{
    		$result=($case?$langs->trans("Yes"):$langs->trans("yes"));
    		$class='ok';
    	}
    	elseif ($yesno == 0 || strtolower($yesno) == 'no' || strtolower($yesno) == 'false')
    	{
    		$result=($case?$langs->trans("No"):$langs->trans("no"));
    		$class='error';
    	}
    	if ($color) return '<font class="'.$class.'">'.$result.'</font>';
    	return $result;
    }
    
    
    /**
     *	\brief      Return a path to class a directory according to an id
     *	\remarks    Examples: 1->"0/0/1/", 15->"0/1/5/"
     *	\param      $num        Id to develop
     *	\param      $level		Level of development (1, 2 or 3 level)
     */
    function get_exdir($num,$level=3)
    {
    	$num = eregi_replace('[^0-9]','',$num);
    	$num = substr("000".$num, -$level);
    	if ($level == 1) return substr($num,0,1).'/';
    	if ($level == 2) return substr($num,1,1).'/'.substr($num,0,1).'/';
    	if ($level == 3) return substr($num,2,1).'/'.substr($num,1,1).'/'.substr($num,0,1).'/';
    	return '';
    }
    
    /**
     *	\brief      Creation of a directory (recursive)
     *	\param      $dir        Directory to create
     *	\return     int         < 0 if KO, >= 0 if OK
     */
    function create_exdir($dir)
    {
    	global $conf;
    
    	dolibarr_syslog("functions.lib::create_exdir: dir=".$dir,LOG_INFO);
    
    	if (@is_dir($dir)) return 0;
    
    	$nberr=0;
    	$nbcreated=0;
    
    	$ccdir = '';
    	$cdir = explode("/",$dir);
    	for ($i = 0 ; $i < sizeof($cdir) ; $i++)
    	{
    		if ($i > 0) $ccdir .= '/'.$cdir[$i];
    		else $ccdir = $cdir[$i];
    		if (eregi("^.:$",$ccdir,$regs)) continue;	// Si chemin Windows incomplet, on poursuit par rep suivant
    
    		// Attention, le is_dir() peut echouer bien que le rep existe.
    		// (ex selon config de open_basedir)
    		if ($ccdir)
    		{
    			if (! @is_dir($ccdir))
    			{
    				dolibarr_syslog("functions.lib::create_exdir: Directory '".$ccdir."' does not exists or is outside open_basedir PHP setting.",LOG_DEBUG);
    
    				umask(0);
    				$dirmaskdec=octdec('0755');
    				if (! empty($conf->global->MAIN_UMASK)) $dirmaskdec=octdec($conf->global->MAIN_UMASK);
    				$dirmaskdec |= octdec('0110');
    				if (! @mkdir($ccdir, $dirmaskdec))
    				{
    					// Si le is_dir a renvoye une fausse info, alors on passe ici.
    					dolibarr_syslog("functions.lib::create_exdir: Fails to create directory '".$ccdir."' or directory already exists.",LOG_WARNING);
    					$nberr++;
    				}
    				else
    				{
    					dolibarr_syslog("functions.lib::create_exdir: Directory '".$ccdir."' created",LOG_DEBUG);
    					$nberr=0;	// On remet a zero car si on arrive ici, cela veut dire que les �checs pr�c�dents peuvent etre ignor�s
    					$nbcreated++;
    				}
    			}
    			else
    			{
    				$nberr=0;	// On remet a zero car si on arrive ici, cela veut dire que les �checs pr�c�dents peuvent etre ignor�s
    			}
    		}
    	}
    	return ($nberr ? -$nberr : $nbcreated);
    }
    
    
    /**
     *	\brief   Retourne le picto champ obligatoire
     *	\return  string		Chaine avec picto obligatoire
     */
    function picto_required()
    {
    	return '<b>*</b>';
    }
    
    
    /**
     *	\brief   	Clean an url string
     *	\param   	url			Url
     *	\param   	http		1: keep http, 0: remove also http
     *	\return  	string	    CleanUrl
     */
    function clean_url($url,$http=1)
    {
    	// Fixed by Matelli (see http://matelli.fr/showcases/patchs-dolibarr/fix-cleaning-url.html)
    	// To include the minus sign in a char class, we must not escape it but put it at the end of the class
    	// Also, there's no need of escape a dot sign in a class
    	if (eregi('^(https?:[\\\/]+)?([0-9A-Z.-]+\.[A-Z]{2,4})(:[0-9]+)?',$url,$regs))
    	{
    		$proto=$regs[1];
    		$domain=$regs[2];
    		$port=$regs[3];
    		//print $url." -> ".$proto." - ".$domain." - ".$port;
    		//$url = dol_string_nospecial(trim($url));
    		$url = trim($url);
    		
    		// Si http: defini on supprime le http (Si https on ne supprime pas)
    		$newproto=$proto;
    		if ($http==0)
    		{
    			if (eregi('^http:[\\\/]+',$url))
    			{
    				$url = eregi_replace('^http:[\\\/]+','',$url);
    				$newproto = '';
    			}
    		}
    
    		// On passe le nom de domaine en minuscule
    		$url = eregi_replace('^'.$proto.$domain, $newproto.strtolower($domain), $url);
    
    		return $url;
    	}
    }
    
    
    
    /**
     *	\brief   	Clean a string from all HTML tags and entities
     *	\param   	StringHtml			String to clean
     *	\param		removelinefeed		Replace also all lines feeds by a space
     *	\return  	string	    		String cleaned
     */
    function dol_string_nohtmltag($StringHtml,$removelinefeed=1)
    {
    	$pattern = "<[^>]+>";
    	$temp = dol_entity_decode($StringHtml);
    	$temp = ereg_replace($pattern,"",$temp);
    
    	// Supprime aussi les retours
    	if ($removelinefeed) $temp=str_replace("\n"," ",$temp);
    
    	// et les espaces doubles
    	while(strpos($temp,"  "))
    	{
    		$temp = str_replace("  "," ",$temp);
    	}
    	$CleanString = $temp;
    	return $CleanString;
    }
    
    
    /**
     *	\brief		Replace CRLF in string with a HTML BR tag.
     *	\param		string2encode		String to encode
     *	\param		nl2brmode			0=Adding br before \n, 1=Replacing \n by br
     *	\return		string				String encoded
     */
    function dol_nl2br($stringtoencode,$nl2brmode=0)
    {
    	if (! $nl2brmode) return nl2br($stringtoencode);
    	else
    	{
    		$ret=ereg_replace("\r","",$stringtoencode);
    		$ret=ereg_replace("\n","<br>",$ret);
    		return $ret;
    	}
    }
    
    /**
     *	\brief		This function is called to encode a string into a HTML string
     *	\param		stringtoencode		String to encode
     *	\param		nl2brmode			0=Adding br before \n, 1=Replacing \n by br (for use with FPDF writeHTMLCell function for example)
     *	\remarks	For PDF usage, you can show text by 2 ways:
     *				- writeHTMLCell -> param must be encoded into HTML.
     *				- MultiCell -> param must not be encoded into HTML.
     *				Because writeHTMLCell convert also \n into <br>, if function
     *				is used to build PDF, nl2brmode must be 1.
     */
    function dol_htmlentitiesbr($stringtoencode,$nl2brmode=0,$pagecodefrom='UTF-8')
    {
    	if (dol_textishtml($stringtoencode))
    	{
    		// Replace "<br type="_moz" />" by "<br>". It's same and avoid pb with FPDF.
    		$stringtoencode=eregi_replace('<br( [ a-zA-Z_="]*)?/?>','<br>',$stringtoencode);
    		return $stringtoencode;
    	}
    	else {
    		// We use @ to avoid warning on PHP4 that does not support entity decoding to UTF8;
    		$newstring=dol_nl2br(@htmlentities($stringtoencode,ENT_COMPAT,$pagecodefrom),$nl2brmode);
    		// Other substitutions that htmlentities does not do
    		$newstring=str_replace(chr(128),'&euro;',$newstring);	// 128 = 0x80
    		return $newstring;
    	}
    }
    
    /**
     *	\brief		This function is called to decode a HTML string (it decodes entities and br tags)
     *	\param		stringtodecode		String to decode
     */
    function dol_htmlentitiesbr_decode($stringtodecode,$pagecodeto='UTF-8')
    {
    	// We use @ to avoid warning on PHP4 that does not support entity decoding to UTF8;
    	$ret=@html_entity_decode($stringtodecode,ENT_COMPAT,$pagecodeto);
    	$ret=eregi_replace("\r\n".'<br( [ a-zA-Z_="]*)?/?>',"<br>",$ret);
    	$ret=eregi_replace('<br( [ a-zA-Z_="]*)?/?>'."\r\n","\r\n",$ret);
    	$ret=eregi_replace('<br( [ a-zA-Z_="]*)?/?>'."\n","\n",$ret);
    	$ret=eregi_replace('<br( [ a-zA-Z_="]*)?/?>',"\n",$ret);
    	return $ret;
    }
    
    /**
     *	\brief		This function remove all ending \n and br at end
     *	\param		stringtodecode		String to decode
     */
    function dol_htmlcleanlastbr($stringtodecode)
    {
    	$ret=eregi_replace('(<br>|<br( [ a-zA-Z_="]*)?/?>|\n|\r)+$',"",$stringtodecode);
    	return $ret;
    }
    
    /**
     *	\brief		This function is called to decode a string with HTML entities (it decodes entities tags)
     * 	\param   	string      stringhtml
     * 	\return  	string	  	decodestring
     */
    function dol_entity_decode($stringhtml,$pagecodeto='UTF-8')
    {
    	// We use @ to avoid warning on PHP4 that does not support entity decoding to UTF8;
    	$ret=@html_entity_decode($stringhtml,ENT_COMPAT,$pagecodeto);
    	return $ret;
    }
    
    /**
     \brief		Check if a string is a correct iso string
     If not, it will we considered not HTML encoded even if it is by FPDF.
     \remarks	Example, if string contains euro symbol that has ascii code 128.
     \param		s		String to check
     \return	int		0 if bad iso, 1 if good iso
     */
    function dol_string_is_good_iso($s)
    {
    	$len=strlen($s);
    	$ok=1;
    	for($scursor=0;$scursor<$len;$scursor++)
    	{
    		$ordchar=ord($s{$scursor});
    		//print $scursor.'-'.$ordchar.'<br>';
    		if ($ordchar < 32 && $ordchar != 13 && $ordchar != 10) { $ok=0; break; }
    		if ($ordchar > 126 && $ordchar < 160) { $ok=0; break; }
    	}
    	return $ok;
    }
    
    
    /**
     *	\brief		Return nb of lines of a text
     *	\param		s			String to check
     * 	\param		maxchar		Not yet used
     *	\return		int			0 if bad iso, 1 if good iso
     */
    function dol_nboflines($s,$maxchar=0)
    {
    	$arraystring=split("\n",$s);
    	$nb=sizeof($arraystring);
    
    	return $nb;
    }
    
    
    /**
     *	\brief     Fonction retournant le nombre de lignes dans un texte formate
     *	\param	   texte      	Texte
     *	\param	   maxlinesize  Largeur de ligne en caracteres(ou 0 si pas de limite - defaut)
     *	\return    nblines    	Nombre de lignes
     */
    function num_lines($texte,$maxlinesize=0)
    {
    	$repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
    	$texte = strtr($texte, $repTable);
    	$pattern = '/(<[^>]+>)/Uu';
    	$a = preg_split($pattern, $texte, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
    	$nblines = ((count($a)+1)/2);
    	// count possible auto line breaks
    	if($maxlinesize)
    	{
    		foreach ($a as $line)
    		{
    			if (strlen($line)>$maxlinesize)
    			{
    				//$line_dec = html_entity_decode(strip_tags($line));
    				$line_dec = html_entity_decode($line);
    				if(strlen($line_dec)>$maxlinesize)
    				{
    					$line_dec=wordwrap($line_dec,$maxlinesize,'\n',true);
    					$nblines+=substr_count($line_dec,'\n');
    				}
    			}
    		}
    	}
    	return $nblines;
    }
    
    /**
     *	\brief		Fonction simple identique a microtime de PHP 5 mais compatible PHP 4
     *	\return		float		Time en millisecondes avec decimal pour microsecondes
     */
    function dol_microtime_float()
    {
    	list($usec, $sec) = explode(" ", microtime());
    	return ((float)$usec + (float)$sec);
    }
    
    /*
     *		\brief		Return if a text is a html content
     *		\param		msg			Content to check
     *		\param		option		0=Full detection, 1=Fast check
     *		\return		boolean		true/false
     */
    function dol_textishtml($msg,$option=0)
    {
    	if ($option == 1)
    	{
    		if (eregi('<html',$msg))     return true;
    		elseif (eregi('<body',$msg)) return true;
    		elseif (eregi('<br',$msg))   return true;
    		return false;
    	}
    	else
    	{
    		if (eregi('<html',$msg))                 return true;
    		elseif (eregi('<body',$msg))             return true;
    		elseif (eregi('<br',$msg))               return true;
    		elseif (eregi('<span',$msg))             return true;
    		elseif (eregi('<div',$msg))              return true;
    		elseif (eregi('<table',$msg))            return true;
    		elseif (eregi('<font',$msg))             return true;
    		elseif (eregi('<strong',$msg))           return true;
    		elseif (eregi('<img',$msg))              return true;
    		elseif (eregi('<i>',$msg))               return true;
    		elseif (eregi('<b>',$msg))               return true;
    		elseif (eregi('&[A-Z0-9]{1,6};',$msg))   return true;
    		return false;
    	}
    }
    
    /**
     *    \brief      Effectue les substitutions des mots cl�s par les donn�es en fonction du tableau
     *    \param      chaine      			Chaine dans laquelle faire les substitutions
     *    \param      substitutionarray		Tableau cl� substitution => valeur a mettre
     *    \return     string      			Chaine avec les substitutions effectu�es
     */
    function make_substitutions($chaine,$substitutionarray)
    {
    	foreach ($substitutionarray as $key => $value)
    	{
    		$chaine=ereg_replace($key,$value,$chaine);
    	}
    	return $chaine;
    }
    
    
    /**
     *    \brief      	Format output for start and end date
     *    \param      	date_start    Start date
     *    \param      	date_end      End date
     *    \param      	format        Output format
     *    \remarks   	Updated by Matelli : added format paramter
     *    \remarks   	See http://matelli.fr/showcases/patchs-dolibarr/update-date-range-format.html for details
     */
    function print_date_range($date_start,$date_end,$format = '',$outputlangs='')
    {
    	global $langs;
    
    	if (! is_object($outputlangs)) $outputlangs=$langs;
    
    	if ($date_start && $date_end)
    	{
    		print ' ('.$langs->trans('DateFromTo',dolibarr_print_date($date_start, $format, false, $outputlangs),dolibarr_print_date($date_end, $format, false, $outputlangs)).')';
    	}
    	if ($date_start && ! $date_end)
    	{
    		print ' ('.$langs->trans('DateFrom',dolibarr_print_date($date_start, $format, false, $outputlangs)).')';
    	}
    	if (! $date_start && $date_end)
    	{
    		print ' ('.$langs->trans('DateUntil',dolibarr_print_date($date_end, $format, false, $outputlangs)).')';
    	}
    }
    
    
    
    /**
     *	\brief   Retourne un tableau des mois ou le mois selectionne
     *	\param   selected			Mois a selectionner ou -1
     *	\return  string or array	Month string or array if selected < 0
     */
    function monthArrayOrSelected($selected=0)
    {
    	global $langs;
    	$langs->load("main");
    
    	$month = array (1  => $langs->trans("January"),
    	2  => $langs->trans("February"),
    	3  => $langs->trans("March"),
    	4  => $langs->trans("April"),
    	5  => $langs->trans("May"),
    	6  => $langs->trans("June"),
    	7  => $langs->trans("July"),
    	8  => $langs->trans("August"),
    	9  => $langs->trans("September"),
    	10 => $langs->trans("October"),
    	11 => $langs->trans("November"),
    	12 => $langs->trans("December")
    	);
    
    	if ($selected >=0)
    	{
    		$return='';
    		foreach ($month as $key => $val)
    		{
    			if ($selected == $key)
    			{
    				$return = $val;
    			}
    		}
    		return $return;
    	}
    	else
    	{
    		return $month;
    	}
    }
    
    
    /**
     *	\brief  	Returns formated reduction
     *	\param		reduction		Reduction percentage
     *	\return		int				Return number of error messages shown
     */
    function dol_htmloutput_errors($mesgstring='',$mesgarray='')
    {
    	global $langs;
    
    	$ret = 0;
    	$langs->load("errors");
    
    	if (is_array($mesgarray) && sizeof($mesgarray))
    	{
    		print '<div class="error">';
    		foreach($mesgarray as $message)
    		{
    			$ret++;
    			print $langs->trans($message)."<br>\n";
    		}
    		print '</div>';
    	}
    	if ($mesgstring)
    	{
    		$ret++;
    		print '<div class="error">';
    		print $mesgstring;
    		print '</div>';
    	}
    
    	return $ret;
    }
    
    
    /**
     *	\brief		This function output memory used by PHP and exit everything. Used for debugging purpose.
     */
    function stopwithmem()
    {
    	print memory_get_usage();
    	llxFooter();
    	exit;
    }
    
    
    /**
     * 	\brief	Advanced sort array by second index function, which produces
     *			ascending (default) or descending output and uses optionally
     *			natural case insensitive sorting (which can be optionally case
     *			sensitive as well).
     */
    function dol_sort_array($array, $index, $order='asc', $natsort, $case_sensitive)
    {
    	// Clean parameters
    	$order=strtolower($order);
    
    	if (is_array($array) && count($array)>0)
    	{
    		foreach(array_keys($array) as $key) $temp[$key]=$array[$key][$index];
    		if (!$natsort) ($order=='asc') ? asort($temp) : arsort($temp);
    		else
    		{
    			($case_sensitive) ? natsort($temp) : natcasesort($temp);
    			if($order!='asc') $temp=array_reverse($temp,TRUE);
    		}
    		foreach(array_keys($temp) as $key) (is_numeric($key))? $sorted[]=$array[$key] : $sorted[$key]=$array[$key];
    		return $sorted;
    	}
    	return $array;
    }
    
    
    /**
     *      \brief      Check if a string is in UTF8
     *      \param      $Str        String to check
     * 		\return		boolean		True if string is UTF8 or ISO compatible with UTF8, False if not (ISO with special char or Binary)
     */
    function utf8_check($Str)
    {
    	for ($i=0; $i<strlen($Str); $i++)
    	{
    		if (ord($Str[$i]) < 0x80) continue; # 0bbbbbbb
    		elseif ((ord($Str[$i]) & 0xE0) == 0xC0) $n=1; # 110bbbbb
    		elseif ((ord($Str[$i]) & 0xF0) == 0xE0) $n=2; # 1110bbbb
    		elseif ((ord($Str[$i]) & 0xF8) == 0xF0) $n=3; # 11110bbb
    		elseif ((ord($Str[$i]) & 0xFC) == 0xF8) $n=4; # 111110bb
    		elseif ((ord($Str[$i]) & 0xFE) == 0xFC) $n=5; # 1111110b
    		else return false; # Does not match any model
    		for ($j=0; $j<$n; $j++) { # n bytes matching 10bbbbbb follow ?
    			if ((++$i == strlen($Str)) || ((ord($Str[$i]) & 0xC0) != 0x80))
    			return false;
    		}
    	}
    	return true;
    }
    
    
    ?>