<?php
/**
 * Main functions library
 * 
 * Provides a bunch of useful functions
 * That file is included in both public / admin UIs
 * 
 * @package linea21\utils\lib
 * @author $Author$ - linea21 <info@linea21.com>
 * @version $Id$ 
 * @access public
 * @license http://opensource.org/licenses/gpl-3.0.html
 */

/**
 * convertBase64Images()
 * get all img src from html and convert them
 * @param string $content
 * @param string $prefix
 * @param string $folder
 * @return string
 */
function convertBase64Images($content, $prefix = '', $folder = 'autosave') {
    
    // $flag = false; // flag base64 encoded image 
    
    // if disabled, we return given content 
    if(!defined('BASE64IMAGE_AUTOSAVE') || BASE64IMAGE_AUTOSAVE == 0) return $content;
    
    $html = new simple_html_dom();
    
    $html->load($content);
    
    if($html->find('img', 0)) { 
        foreach($html->find('img') as $element) {
            
            // base64 encoded images -> we convert them and replace source
            if(Stringy\Stringy::create($element->src, CHARSET)->startsWith('data:image/')) {
                
                $flag = true; // we update flag
                // echo $element->src . "\n<br/>";
                // logfile(LOG_MAINFILE, array('[DEBUG] '. $element->src));
                // we save image to file
                $r = saveImage($element->src, $prefix, $folder);
                
                if($r !== false) {
                    // we change source
                    $element->src = str_replace(SITE_PATH, SITE_ROOT_URL, $r);
                }
            }
        }
    }
    
    // @todo remove debug - if everything allright with data ! lots of errors due to addslashes / stripslashes
    //     $id = generateRandomString(3);
    //     file_put_contents('../tmp/__'.$id.'.before.txt', $content);
    //     file_put_contents('../tmp/__'.$id.'.after.txt', $html);
    
    // if no change, we return original content, else modified content
//     if($flag == false) return $content;
//     else return addslashes($html);
    
    return addslashes($html);
    
}

/**
 * is_localhost()
 * @return boolean
 */
function is_localhost()
{
    return in_array($_SERVER['REMOTE_ADDR'], array('127.0.0.1', '::1', 'localhost')) ? true : false;
}

/**
 * set_flash_msg()
 * @param string $str
 * @param string $scope
 * @return boolean
 */
function set_flash_msg($str, $scope = true) {
    
    $_SESSION['message'] = $str;
    $_SESSION['message-scope'] = $scope;
    return true;
    
}
    
/**
 * display_flash_msg()
 * @param string $wrap
 * @param string $class
 * @param string $scope
 * @return boolean
 */
function display_flash_msg( $wrap = false, $class = '', $scope = true) {
    
    if(isset($_SESSION['message']) && $scope == $_SESSION['message-scope']) {
        $str = $_SESSION['message'];
        unset($_SESSION['message']);
        unset($_SESSION['message-scope']);
        
        if($wrap) echo '<div class="'.$class.'">'.$str.'</div>';
        else echo $str;
    }
    return true;    
}

/**
 * includeHighCharts()
 * generate code to include HighCharts JS library 
 * @param array optional $modules
 * @return bool true
 */
function includeHighCharts($modules = []) {
    
    $folder = '../lib/js/Highcharts-7.0.3/code/';
    $folder = '../lib/js/Highcharts-11.2.0/code/';
    
    footerAddJS($folder. 'highcharts.js');

    if(count($modules) > 0) {
        foreach ($modules as $m) {
            footerAddJS($folder. 'modules/' . $m . '.js');
        }
    }

    return true;
}

/**
 * removeDomElement()
 * remove a given element such as 'img' in given html
 * @param string $element
 * @param string $content
 * @param boolean $removeEmptyTags
 * @return string
 */
function removeDomElement($element, $content, $removeEmptyTags = 'p, div') {
    
    $html = new simple_html_dom();

    $html->load($content);

    foreach ($html->find('img') as $el) {
        $images = $el->outertext = '';
    }
    
    // we also remove empy p
    if($removeEmptyTags != '' && is_string($removeEmptyTags)) {
        $tags = $html->find($removeEmptyTags);
       
        foreach($tags as $t)
        {
            if(trim($t->plaintext) == '')
            {
                // Remove an element, set it's outertext as an empty string
                $t->outertext = '';
            }
        }
    }

    $content = $html;

    unset($html);

    return $content;
}


/**
 * saveImage()
 * save base64 image to flat file
 * @param string $img
 * @param string $prefix
 * @param string $folder
 * @return string | false
 */
function saveImage($img, $prefix, $folder) {
    
    $target = SITE_PATH . 'library/userfiles/' . $folder. '/';
    if(!file_exists($target)) mkdir($target, 0777);
    // split the string on commas
    // $data[0] == "data:image/png;base64" or "data:image/jpeg;base64"
    // $data[1] == <actual base64 string>
    $data = explode( ',', $img );
    
    $filename = $target . $prefix . generateRandomString(12) . '.png';
    
    if(strlen($data[1]) > 1) {
        
        logfile(LOG_MAINFILE, array('based64 image encoded saved into file '. $filename));
        $r = file_put_contents($filename, base64_decode($data[1]));
        chmod($filename, 0777);
        
        if($r == false) logfile(LOG_MAINFILE, array('[ERROR] when writing based64 image into file '. $filename));
        return $filename;
    }
    
    return false;
}

/**
 * implode_with_keys()
 * implode avec clefs associées renvoyées
 * sous forme de chaîne de caractères
 * @param string $glue
 * @param string $array
 * @return string
 */
function implode_with_keys($glue, $array) {
	$output = array();
	foreach( $array as $key => $item )
		$output[] = $key . "=" . $item;

	return implode($glue, $output);
}



/**
 * is_filled()
 * check if given field is filled up in array
 * @param array $array
 * @param string $fieldname
 * @return boolean
 */
function is_filled($array, $fieldname) {
    
    foreach ($array as $el) {
        if(!empty($el[$fieldname])) return true;
    }
    return false;
    
}

/**
 * safe_redirect()
 * check if given url is safe
 * if not, return CURRENT_APP_URL
 * @param string $url
 * @return string
 */
function safe_redirect( $url ) {
	
	if(Stringy\Stringy::create($url, CHARSET)->startsWith(SITE_ROOT_URL)) return $url;
	if(Stringy\Stringy::create($url, CHARSET)->startsWith('index.php')) return $url;
	if(Stringy\Stringy::create($url, CHARSET)->startsWith('login.php')) return $url;
	if(Stringy\Stringy::create($url, CHARSET)->startsWith('logout.php')) return $url;
	if(Stringy\Stringy::create($url, CHARSET)->startsWith('confirm.php')) return $url;
	// provided url is not safe, we route to default CURRENT_APP_URL
	return CURRENT_APP_URL;
}

/**
 * getHttpParameters()
 * Renvoie les paramètres HTTP
 * sous forme de chaîne de caractères
 * @return string
 */
function getHttpParameters($prefix = '?') {

	return $prefix. (string) implode_with_keys('&', $_REQUEST);
}

/**
 * is_module()
 * Test if the current page matches the given module and action (optionnal)
 * examples :  (1) is_module('system'), (2) is_module('system/edit_files')
 * interpreted as : (1) rub=system, (2) rub=system and todo=edit_files
 * @param string $str
 * @return boolean
 */
// @todo simo
function is_module($str) {

	$a = explode('/', $str);

	_debug('is_module() values : ' .join(', ', $a). ' '. count($a));

	// handle simple case :  is_module('system')
	if(!isset($_REQUEST['rub']) || strpos($a[0], $_REQUEST['rub']) === false) return false;

	// handle strict case : is_module('system$') $ - return false is $todo is set
	if(strpos($a[0], '$') !== false && isset($_REQUEST['todo'])) return false;

	// handle case is_module('system/list')
	if(isset($a[1]) && !empty($a[1])) {
		if(!isset($_REQUEST['todo']) || $_REQUEST['todo'] != $a[1]) return false;
	}

	return true;
}

/**
 * Remove DOM Nodes
 * @param string $html
 * @param string $tag
 * @return string
 */
function removeDomNodes($html, $tag)
{
	if(empty($html)) return $html;
	
    $html = str_get_html($html);
   
    foreach($html->find($tag) as $element) {
            $scripts = $element->outertext = '';
    }
    return $html;
}


function cleanString($string, $allowed = array()) {
	
	$allow = null;

	if (!empty($allowed)) {
		foreach ($allowed as $value) {
			$allow .= "\\$value";
		}
	}

	$mapping = array(
			'Š'=>'S', 'š'=>'s', 'Đ'=>'Dj', 'đ'=>'dj', 'Ž'=>'Z', 'ž'=>'z', 'Č'=>'C', 'č'=>'c', 'Ć'=>'C', 'ć'=>'c',
			'À'=>'A', 'Á'=>'A', 'Â'=>'A', 'Ã'=>'A', 'Ä'=>'A', 'Å'=>'A', 'Æ'=>'A', 'Ç'=>'C', 'È'=>'E', 'É'=>'E',
			'Ê'=>'E', 'Ë'=>'E', 'Ì'=>'I', 'Í'=>'I', 'Î'=>'I', 'Ï'=>'I', 'Ñ'=>'N', 'Ò'=>'O', 'Ó'=>'O', 'Ô'=>'O',
			'Õ'=>'O', 'Ö'=>'O', 'Ő'=>'O', 'Ø'=>'O', 'Ù'=>'U', 'Ú'=>'U', 'Û'=>'U', 'Ü'=>'U', 'Ű'=>'U', 'Ý'=>'Y',
			'Þ'=>'B', 'ß'=>'Ss','à'=>'a', 'á'=>'a', 'â'=>'a', 'ã'=>'a', 'ä'=>'a', 'å'=>'a', 'æ'=>'a', 'ç'=>'c',
			'è'=>'e', 'é'=>'e', 'ê'=>'e', 'ë'=>'e', 'ì'=>'i', 'í'=>'i', 'î'=>'i', 'ï'=>'i', 'ð'=>'o', 'ñ'=>'n',
			'ò'=>'o', 'ó'=>'o', 'ô'=>'o', 'õ'=>'o', 'ö'=>'o', 'ő'=>'o', 'ø'=>'o', 'ù'=>'u', 'ú'=>'u', 'ű'=>'u',
			'û'=>'u', 'ü'=>'u', 'ý'=>'y', 'ý'=>'y', 'þ'=>'b', 'ÿ'=>'y', 'Ŕ'=>'R', 'ŕ'=>'r'
	);

	if (is_array($string)) {

		$cleaned = array();

		foreach ($string as $key => $clean) {
			$clean = strtr($clean, $mapping);
		}
	} else {
		$cleaned = strtr($string, $mapping);
	}
	
	return $cleaned;
}

/**
 * sanitize_string()
 * Convert html special chars  
 * @param string $str
 * @return string
 */
function sanitize_string($str) {
    if(is_string($str))  $str = htmlspecialchars($str, ENT_COMPAT, CHARSET);
   
    return $str;
}

/**
 * strip_input()
 * Remove PHP and HTML code
 * @param string $str
 * @param string $exceptions
 * @return string
 */
function strip_input($str, $exceptions = false) {

    
    if(CURRENT_APP != 'admin') {
        $str = removeDomNodes($str, 'script');
        $str = strip_tags($str, '<p><strong><em><u><blockquote><table><tbody><tr><td><th><thead><tfoot><a><ul><ol><li><img><br>');
    }
    
	if(defined('RICH_TEXT_EDITOR') && RICH_TEXT_EDITOR != (string) 0) {
		return $str;
	}
	else {
		if(is_string($exceptions))  {
			return strip_tags($str, $exceptions);
		}
		if($exceptions === true)  {
			return strip_tags($str, ALLOWABLE_TAGS);
		}
		return strip_tags($str);
	}
	
}

/**
 * IncludeLightRte()
 * Include nicEdit as light RTE
 * param : array $a
 * @return void
 */
function IncludeLightRte($a) {


	$js = '$("#post-body").ckeditor(function() {
					// Instance loaded callback.
				}, {
					customConfig: "l21_light_config.js",
					language: "'.U_L.'",
					width: $(\'#post-title\').width() + 12
				});
				$("#post-body").after("<div class=\"form-info\">'._t('workshop', 'image_insert').'</div>");
				';

	echo '<script src="'.override(SITE_ROOT_URL. 'lib/js/ckeditor/ckeditor.js', PUBLIC_THEME_URL).'"></script>'.PHP_EOL;
	echo '<script src="'.override(SITE_ROOT_URL. 'lib/js/ckeditor/adapters/jquery.js', PUBLIC_THEME_URL).'"></script>'.PHP_EOL;

	footerAddInlineJS($js);
	
	$js = 'CKEDITOR.on("instanceReady", function(evt) {
                  var editor = evt.editor;
                  editor.on("afterPasteFromWord", function(evt) {
                        // alert("just pasted word content !");
                        
                        // Clone the filter, so that it is based on current editor filter settings.
                        // Note that this function is added in CKE 4.7.3.
                        var filter = evt.editor.filter.clone(),
                            fragment = CKEDITOR.htmlParser.fragment.fromHtml( evt.data.dataValue ),
                            writer = new CKEDITOR.htmlParser.basicWriter();
            
                        // Disallow certain styles.
                        filter.disallow( \'span{font-family,color,font-size}\' );
    
                        // Disallow image copy/paste
                        filter.disallow( \'img\' );
            
                        // Process, and overwrite evt.data.dataValue.
                        filter.applyTo( fragment );
                        fragment.writeHtml( writer );
                        evt.data.dataValue = writer.getHtml();
                        });
                    });';
			
			footerAddInlineJS($js);
	
}

function write_curver() {
	file_put_contents('../config/.curver', LINEA_VERSION);
}

function get_curver() {
	return file_get_contents('../config/.curver');
}


function sql_update($filepath, $sqlo) {
	
	$handle = fopen($filepath, "r");
	if ($handle) {
		while (($line = fgets($handle)) !== false) {
			$line = trim($line);
			// we remove empty and commented lines
			if (!empty($line) && substr($line, 0, 2) !='--' &&  substr($line, 0, 1) !='#' ) {
				// we execute the query ignoring errors
				// since FULLTEXT indexes are not supported in mysql v < 5.5.3 (50503) we replace utf8mb4 by utf8
			    if((int) $sqlo->DB_get_version() < (int) 50503) $line = str_ireplace('utf8mb4', 'utf8', $line);
				$r = $sqlo->DBQuery($line, true);
				// echo $line.'<br><br>';
			}
			
		}
	
		fclose($handle);
	} else {
		die('error opening SQL file : ' . $filepath);
	}
}

/**
 * extRegex()
 * Transform a list given in the form "jpg,png,gif"
 * into "(jpg)|(png)|(gif)" format
 * @return string
 */
function extRegex($list) {
	$a = explode(',', $list);
	$b = array();
	foreach($a as $el) {
		array_push($b, '('.$el.')');
	}
	return implode('|', $b);
}

/**
 * dragTableSettings()
 * Include jQuery DragTable plugin settings
 * @param array $params
 * @return void
 */
function dragTableSettings($params = false) {

    $rub = isset($_REQUEST['rub']) ? $_REQUEST['rub'] : '';

    $getparams = '';
    $sep ='';
    if(is_array($params)) {
        foreach($params as $k => $v) {
            $getparams .= $sep . $k . ': \'' . $v .'\'';
            $sep =', ';
        }
        $getparams = '{ '.$getparams . '}, ';
    }

	echo '<script>
				$(document).ready(function(){
					if($("table.sortable thead tr").length) $("table.sortable thead tr").prepend("<td>&nbsp;<\/td>");
					if($("table.sortable thead tr").length) $("table.sortable tfoot tr").prepend("<td>&nbsp;<\/td>");
					$("table.sortable tbody tr").prepend("<td class=\"dragHandle\">&nbsp;<\/td>");
					$("table.sortable").tableDnD({
                        onDragClass: "movingElement",
    					onDrop: function(table, row) {
    					   $.get("../admin/_ajax_sort.php?rub='.$rub.'&" + $.tableDnD.serialize(), ' .$getparams.'  function(data) {
    					   if(data.class == "succeed") {
    					   alertify.success(data.msg);
            				} else {
            					alertify.error(data.msg);
            				}
				        },
				        "json")
				        }
    				});
				});
				</script>'.PHP_EOL;
}

function includeCodeEditor() {

	// we exit function if not enabled
	if(!defined('EDITOR_HIGHLIGHT_CODE') || EDITOR_HIGHLIGHT_CODE == 0 )  return true;

	// we exit function if not the concerned module
	if(!is_module('system/edit_files')) return true;

	if(isset($_REQUEST['file'])) {
		$ext = pathinfo($_REQUEST['file'], PATHINFO_EXTENSION);
	} else {
		$ext = 'css';
	}

	$header = '';

	$header .=  '<link type="text/css" rel="stylesheet" href="'.override('../lib/js/CodeMirror/lib/codemirror.css', THEME_ADMIN_PATH).'" />'.PHP_EOL;
	$header .=  '<script src="'.override('../lib/js/CodeMirror/lib/codemirror.js', THEME_ADMIN_PATH).'"></script>'.PHP_EOL;
	$header .=  '<script src="'.override('../lib/js/codeMirror-fullscreen.js', THEME_ADMIN_PATH).'"></script>'.PHP_EOL;
	$header .=  '<script src="'.override('../lib/js/CodeMirror/addon/selection/active-line.js', THEME_ADMIN_PATH).'"></script>'.PHP_EOL;

	if($ext == 'css') {
		$header .=  '<script src="'.override('../lib/js/CodeMirror/mode/css/css.js', THEME_ADMIN_PATH).'"></script>'.PHP_EOL;

		$code = 'var editor = CodeMirror.fromTextArea(document.getElementById("file_content"), {
							styleActiveLine: true,
							viewportMargin: Infinity,
							lineNumbers: true,
                            lineWrapping: true,
							extraKeys: {
				        "F11": function(cm) {
				          setFullScreen(cm, !isFullScreen(cm));
				        },
				        "Esc": function(cm) {
				          if (isFullScreen(cm)) setFullScreen(cm, false);
				        }
				      }
						});';

		footerAddInlineJS($code);
	}
	
	if($ext == 'xml') {
		$header .=  '<script src="'.override('../lib/js/CodeMirror/mode/css/css.js', THEME_ADMIN_PATH).'"></script>'.PHP_EOL;
		$header .=  '<script src="'.override('../lib/js/CodeMirror/mode/xml/xml.js', THEME_ADMIN_PATH).'"></script>'.PHP_EOL;
	
	
		$code = 'var editor = CodeMirror.fromTextArea(document.getElementById("file_content"), {
							styleActiveLine: true,
							viewportMargin: Infinity,
							lineNumbers: true,
                            lineWrapping: true,
							extraKeys: {
				        "F11": function(cm) {
				          setFullScreen(cm, !isFullScreen(cm));
				        },
				        "Esc": function(cm) {
				          if (isFullScreen(cm)) setFullScreen(cm, false);
				        }
				      }
						});';
	
		footerAddInlineJS($code);
	}
	
	if($ext == 'js' || $ext == 'json') {
		$header .=  '<script src="'.override('../lib/js/CodeMirror/mode/css/css.js', THEME_ADMIN_PATH).'"></script>'.PHP_EOL;
		$header .=  '<script src="'.override('../lib/js/CodeMirror/addon/edit/matchbrackets.js', THEME_ADMIN_PATH).'"></script>'.PHP_EOL;
		$header .=  '<script src="'.override('../lib/js/CodeMirror/addon/comment/continuecomment.js', THEME_ADMIN_PATH).'"></script>'.PHP_EOL;
		$header .=  '<script src="'.override('../lib/js/CodeMirror/addon/comment/comment.js', THEME_ADMIN_PATH).'"></script>'.PHP_EOL;
		$header .=  '<script src="'.override('../lib/js/CodeMirror/mode/javascript/javascrpt.js', THEME_ADMIN_PATH).'"></script>'.PHP_EOL;
		
	
		$code = 'var editor = CodeMirror.fromTextArea(document.getElementById("file_content"), {
							styleActiveLine: true,
							viewportMargin: Infinity,
							lineNumbers: true,
                            lineWrapping: true,
							extraKeys: {
				        "F11": function(cm) {
				          setFullScreen(cm, !isFullScreen(cm));
				        },
				        "Esc": function(cm) {
				          if (isFullScreen(cm)) setFullScreen(cm, false);
				        }
				      }
						});';
	
		footerAddInlineJS($code);
	}

	if($ext == 'php') {
		$header .=  '<script src="'.override('../lib/js/CodeMirror/addon/edit/matchbrackets.js', THEME_ADMIN_PATH).'"></script>'.PHP_EOL;
		$header .=  '<script src="'.override('../lib/js/CodeMirror/mode/htmlmixed/htmlmixed.js', THEME_ADMIN_PATH).'"></script>'.PHP_EOL;
		$header .=  '<script src="'.override('../lib/js/CodeMirror/mode/xml/xml.js', THEME_ADMIN_PATH).'"></script>'.PHP_EOL;
		$header .=  '<script src="'.override('../lib/js/CodeMirror/mode/javascript/javascript.js', THEME_ADMIN_PATH).'"></script>'.PHP_EOL;
		$header .=  '<script src="'.override('../lib/js/CodeMirror/mode/css/css.js', THEME_ADMIN_PATH).'"></script>'.PHP_EOL;
		$header .=  '<script src="'.override('../lib/js/CodeMirror/mode/clike/clike.js', THEME_ADMIN_PATH).'"></script>'.PHP_EOL;
		$header .=  '<script src="'.override('../lib/js/CodeMirror/mode/php/php.js', THEME_ADMIN_PATH).'"></script>'.PHP_EOL;

		$code = 'var editor = CodeMirror.fromTextArea(document.getElementById("file_content"), {
							styleActiveLine: true,
			        lineNumbers: true,
                    lineWrapping: true,
			        matchBrackets: true,
			        mode: "application/x-httpd-php",
			        indentUnit: 4,
			        indentWithTabs: true,
			        enterMode: "keep",
			        viewportMargin: Infinity,
			        tabMode: "shift",
			        extraKeys: {
				        "F11": function(cm) {
				          setFullScreen(cm, !isFullScreen(cm));
				        },
				        "Esc": function(cm) {
				          if (isFullScreen(cm)) setFullScreen(cm, false);
				        }
				      }
			      });';
		
		footerAddInlineJS($code);
	}

	echo $header;

}

function IncludeRichTextEditor() {

	if(defined('RICH_TEXT_EDITOR') && RICH_TEXT_EDITOR != (string) 0 )  {
		if(strtolower(RICH_TEXT_EDITOR) == 'tinymce')
		{
			if(CURRENT_APP == 'admin') {
				if(isset($_REQUEST['rub']) && $_REQUEST['rub'] == 'newsletter' && file_exists(THEME_PUBLIC_PATH. 'css/newsletter.css')) {
					// Loads CSS files into the current document
					echo '<script>
					newslettercsspath = "'. THEME_PUBLIC_PATH . 'css/newsletter.css";
					</script>';
				}
			}
			
			echo '<script src="'.override('../lib/js/tinymce/js/tinymce/tinymce.min.js', THEME_ADMIN_PATH).'"></script>'.PHP_EOL;
			echo '<script src="'.override('../lib/js/tinymce/js/tinymce/config.js', THEME_ADMIN_PATH).'"></script>'.PHP_EOL;
		}
		
		if(strtolower(RICH_TEXT_EDITOR) == 'cke')
		{
		    echo '<script src="'.override('../lib/js/ckeditor/ckeditor.js', THEME_ADMIN_PATH).'"></script>'.PHP_EOL;
		    
			echo '<script src="'.override('../lib/js/ckeditor/adapters/jquery.js', THEME_ADMIN_PATH).'"></script>'.PHP_EOL;

			$extraConfig = '';
			
			echo '<script>
            CKEDITOR.disableAutoInline = true;
            CKE_windowSize = 760;</script>'.PHP_EOL;
			if(CURRENT_APP == 'admin') {
				if(isset($_REQUEST['rub']) && $_REQUEST['rub'] == 'newsletter') {
				    
				    // @todo A voir s'il faut inclure l'ensemble des balises incluses dans le template. Pas sûr 
				    // si oui, possibilité de récuper le contenu des balises <style> avec simplehtmldom et de générer un fichier dynamiquement 
				    // https://simplehtmldom.sourceforge.io/docs/1.9/manual/finding-html-elements/
				    // les marques sont contenus dans le template mais ne sont plus visibles lorsqu'on a cliqué une fois sur "Alimenter automatiquement"
				    $marks = '/** displayed only in back-office editor */ 
                              body.cke_show_borders #main, body.mce-content-body  #main {border:3px dashed red;}
                              body.cke_show_borders #inputs, body.mce-content-body  #inputs {border:2px dashed green;}';
				    $tmpCSS =  '../tmp/public/newsletter-' . uniqid() . '.css';
				    file_put_contents($tmpCSS, $marks);
				    
				    
				    echo '<script>  CKE_allowContent = true;
                                    CKE_allowExtraContent = "";
                          </script>'.PHP_EOL;
				    // @todo - supprimer ? 
				    // Le fichier css/newsletter.css est fourni jusqu'à la version v2.5. Après, il n'est plus utile
				    if(file_exists(THEME_PUBLIC_PATH. 'css/newsletter.css')) {
				        $extraConfig .= ',
				        contentsCss: ["'.THEME_PUBLIC_PATH. 'css/newsletter.css", "'.$tmpCSS.'"]';
				    } 
				    else {
				        $extraConfig .= ',
						contentsCss: "'.$tmpCSS.'"';
				    }
				    
				    
				} else {
					echo '<script>   CKE_allowContent = undefined; // to set config.allowedContent to automatic mode
                                        CKE_allowExtraContent = "'.CKE_ALLOW_EXTRACONTENT.'";
                            </script>'.PHP_EOL;
                    
                    if(file_exists(THEME_PUBLIC_PATH. 'css/admin-editor.css')) $pathtocss = THEME_PUBLIC_PATH. 'css/admin-editor.css';
                    else {
						$pathtocss = '../templates/public/2019/css/admin-editor.css';
						_debug('CKeditor rely on latest theme css file : '.$pathtocss, 'system');
					}
                    $extraConfig .= ',
						contentsCss: "'.$pathtocss. '"';
					}
				// https://docs.ckeditor.com/ckeditor4/latest/guide/dev_allowed_content_rules.html
			} else {
			    echo '<script>CKE_allowContent = false; // see also CKE_allowContent = undefined;</script>'.PHP_EOL;
			}
			

			if(defined('CKFINDER_ENABLED') && CKFINDER_ENABLED == true) {
				echo '<script src="'.override('../lib/vendor/ckfinder/ckfinder.js', THEME_ADMIN_PATH).'"></script>'.PHP_EOL;
				$callback = 'CKFinder.setupCKEditor( this, "../lib/vendor/ckfinder/");';
			} else {
				$callback = '';
				$extraConfig .= ',
					filebrowserBrowseUrl: "../library/access.php",
					filebrowserImageBrowseUrl: "../library/access.php?type=images",
					filebrowserWindowWidth: "80%",
					filebrowserWindowHeight: "80%"';
			}

			$js = '
				$(".largetextfield").ckeditor(function() {
					'.$callback.'
				}, {
					customConfig: "l21_config.js"'.$extraConfig.',
					footnotesPrefix: "a",
					footnotesTitle: "'._t('common', 'footnotes'). '"
				});';
			
			footerAddInlineJS($js);
			
			if(defined('BASE64IMAGE_AUTOSAVE') && BASE64IMAGE_AUTOSAVE == 1) $mask = '// ';
			else $mask = '';
			
			$js = '
CKEDITOR.on("instanceReady", function(evt) {
                  var editor = evt.editor;
                  editor.on("afterPasteFromWord", function(evt) {
                        // alert("just pasted word content !");
                        
                        // Clone the filter, so that it is based on current editor filter settings.
                        // Note that this function is added in CKE 4.7.3.
                        var filter = evt.editor.filter.clone(),
                            fragment = CKEDITOR.htmlParser.fragment.fromHtml( evt.data.dataValue ),
                            writer = new CKEDITOR.htmlParser.basicWriter();
            
                        // Disallow certain styles.
                        filter.disallow( \'span{font-family,color,font-size}\' );
    
                        // Disallow image copy/paste
                        '.$mask.'filter.disallow( \'img\' );
            
                        // Process, and overwrite evt.data.dataValue.
                        filter.applyTo( fragment );
                        fragment.writeHtml( writer );
                        evt.data.dataValue = writer.getHtml();
                        });
                    });';
			
			footerAddInlineJS($js);
				
		}
	}

}


/**
 * IncludeColorboxJs()
 * Include Colorbox jQuery plugin
 * @param string £selector
 * @return void
 */
function IncludeColorboxJs($selector) {

	$str='';
		
	if(is_string($selector)) {
		$str .= '$("'.$selector.'").not(".dlfile").colorbox({ transition:"fade", maxWidth: "95%", maxHeight: "95%" });'.PHP_EOL;
	} else {
		foreach($selector as $sel) {
			$str .= '$("'.$sel.'").not(".dlfile").colorbox({ transition:"fade", maxWidth: "95%", maxHeight: "95%" });'.PHP_EOL;
		}
	}
	echo '<script src="'.SITE_ROOT_URL.'lib/js/colorbox/jquery.colorbox-min.js"></script>'.PHP_EOL;
	echo '<link type="text/css" rel="stylesheet" href="'.SITE_ROOT_URL.'/lib/js/colorbox/colorbox.css" />'.PHP_EOL;
	echo '<script>
				$(document).ready(function() {
				'.$str.'
			});
</script>'. PHP_EOL;

}

/**
 * includeLeafletHeader()
 * Include the leaflet header
 * if needed
 * @return void
 */
function includeLeafletHeader() {

	$GLOBALS['mapping_includes'] = array('directory-detail|', 'yellowpages|det');

	$rub = isset($_REQUEST['rub']) ? $_REQUEST['rub'] : '';
	$todo = isset($_REQUEST['todo']) ? $_REQUEST['todo'] : '';

	if(ActiveItemKey($rub)) $rub = ActiveItemKey($rub);
	$s = $rub . '|' . $todo;
	$a = $GLOBALS['mapping_includes'];

	if(in_array($s, $a)) {
		echo '<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/leaflet.min.js"></script>'.PHP_EOL;
		echo '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/leaflet.min.css" />'.PHP_EOL;
		echo '<style>
					.leaflet-popup-content-wrapper {
						-webkit-border-radius: 0 ! important;
						-moz-border-radius: 0 ! important;
						border-radius: 0 ! important;
					}
					</style>';
	}
}

/**
 * SureCreateDir()
 * Créer un dossier s'il n'existe pas.
 * @param string $pathname
 * @param integer $perms
 * @return integer $ver_num
 */
function SureCreateDir($pathname, $perms) {
	if(!file_exists($pathname)) {
		$r =mkdir ($pathname, $perms, true);
		return $r;
	} else {
		return true;
	}
}


/**
 * SureRemoveDir()
 * Supprime le contenu d'un dossier et le dossier lui-même si précisé.
 *
 * @return integer $ver_num
 */
function SureRemoveDir($dir, $DeleteMe) {
	if(!$dh = @opendir($dir)) return;
	while (($obj = readdir($dh))) {
		if($obj=='.' || $obj=='..') continue;
		if (!@unlink($dir.'/'.$obj)) SureRemoveDir($dir.'/'.$obj, true);
	}
	if ($DeleteMe){
		closedir($dh);
		@rmdir($dir);
	}
}

/**
 * num_phpversion()
 * Retourne un entier comme numéro de version PHP
 *
 * @return integer $ver_num
 */
function num_phpversion() {
	$ver = explode( '.', phpversion() );
	$ver_num = $ver[0] . $ver[1] . $ver[2];

	return substr($ver_num, 0, 3);
}

/** mb_ucfirst()
 * UTF-8 ucfirst function
 * @param string $string
 * @param string $encoding
 * @return string
 */
function mb_ucfirst($string, $encoding = CHARSET)
{
	if(function_exists('mb_strlen')) {
		$strlen = mb_strlen($string, $encoding);
		$firstChar = mb_substr($string, 0, 1, $encoding);
		$then = mb_substr($string, 1, $strlen - 1, $encoding);
		return mb_strtoupper($firstChar, $encoding) . $then;
	} else {
		_debug('mb_string module not loaded', 'warning');
		return ucfirst($string);
	}
}

/**
 * cutText()
 * Découpe un texte à une longeur donnée.
 *
 * @param string $content
 * @param integer $length
 * @param integer $abbr
 * @param string $end
 * @return
 */
function cutText($content, $length, $abbr = 0, $end = ' ...')
{
	
	$content_light =  Stringy\Stringy::create($content, CHARSET)->safeTruncate($length, $end);
	
	if (!empty($content) && strlen($content) > $length && $abbr == 1) {
		$content_light = "<abbr title=\"" . $content . "\">" . $content_light . "</abbr>\n";
	}
	
	return $content_light;
}

/**
 * error_redirect()
 * Redirect to error page
 */
function error_redirect($file ='error.php') {

	header("Location: " . CURRENT_APP_URL . $file);
    exit();
}

/**
 * removeEmptyP()
 * Remove empty P tags
 *
 * @param string $text
 * @return array $body
 */
function removeEmptyP($str) {
	$str = preg_replace('#<p[^>]*>(\s|&nbsp;?)*</p>#', '', $str);

	return $str;
}

/**
 * formatNavTitle()
 * Formatage des titres ( interface admin )
 *
 * @param string $content
 * @return string $content
 */
function formatNavTitle($content)
{
	$content = formatText($content, '2HTML');
	$content = cutText($content, 70, 1);

	return $content;
}

/**
 * formatTextli()
 * Formatage des listes ( interface admin )
 *
 * @param string $content
 * @return string $content
 */
function formatTextli($content)
{
	$content = formatText($content, '2HTML');
	$content=  Stringy\Stringy::create($content, CHARSET)->safeTruncate(90, ' ...');
	return $content;
}

/**
 * formatTitleh2()
 * Formatage des titres h2 ( interface admin )
 *
 * @param string $content
 * @return string $content
 */
function formatTitleh2($content)
{
	$content = formatText($content, '2HTML');
	return $content;
}

/**
 * isRawText()
 * check if raw text or html text
 *
 * @param string $t
 * @return bool
 */
function isRawText($t) {
	if (strcmp($t, strip_tags($t)) == 0) {
		return true;
	} else {
		return false;
	}
}

/**
 * formatText()
 * Formatage de texte pour affichage
 *
 * @param  $content
 * @param string $format
 * @return string $content
 */
function formatText($content, $format = -1)
{
    if(empty($content)) return $content;
	$content = stripslashes(trim($content));
	switch ($format) {
		case '2HTML':
			if(RICH_TEXT_EDITOR === 0 || isRawText($content)) $content = nl2br($content);
			break;
		case '2FIELD':
			$content = htmlentities($content, ENT_QUOTES, CHARSET);
			break;
		case '2ATT':
		    $content = htmlentities($content, ENT_QUOTES, CHARSET);
			break;
		case '2XML':
			$content = htmlspecialchars($content);
			break;
		case '2FILE':
			//$content = addslashes(trim($content));
		    $content = htmlspecialchars($content, ENT_QUOTES, CHARSET);
			break;
		default:
	}
	return $content;
}

function stripText($text) {

	$text = strtolower($text);

	// strip all non word chars
	$text = preg_replace('/\W/', ' ', $text);
	// replace all white space sections with a dash
	$text = preg_replace('/\ +/', '-', $text);
	// trim dashes
	$text = preg_replace('/\-$/', '', $text);
	$text = preg_replace('/^\-/', '', $text);

	return $text;
}

function get_year( $date ) {
    
    $arrayDate = explode('-', $date);
    
    if(DATE_FORMAT == 'dd-mm-yyyy') return $arrayDate[2];
    else return $arrayDate[0];
}

/**
 * toStringSqlDate()
 * Renvoie la date au format SQL
 *
 * @param string $format
 * @return string $s
 */
function toStringSqlDate($format = 'short')
{
	$date_format =   array(
			'dd-mm-yyyy' => array(
					'mysql' => array('short'=> '%d-%m-%Y', 'long'=>
							array('12' => '%d-%m-%Y  %r', '24' => '%d-%m-%Y  %T')),
					'pgsql' => array('short'=> 'DD-MM-YYYY', 'long'=>
							array('12' => 'DD-MM-YYYY HH12:MI:SS', '24' => 'DD-MM-YYYY HH24:MI:SS'))),
			'yyyy-mm-dd' => array(
					'mysql' => array('short'=> '%Y-%m-%d', 'long'=>
							array('12' => '%Y-%m-%d  %r', '24' => '%Y-%m-%d  %T')),
					'pgsql' => array('short'=> 'YYYY-MM-DD', 'long'=>
							array('12' => 'YYYY-MM-DD HH12:MI:SS', '24' => 'YYYY-MM-DD HH24:MI:SS'))),

	);

	if($format == 'long') $s = $date_format[DATE_FORMAT][SQL][$format][TIME_FORMAT];
	else $s = $date_format[DATE_FORMAT][SQL][$format];

	return $s;
}


/**
 * date_compare()
 * Compare 2 dates with a given operator.
 * @param  $date1
 * @param  $date2
 * @param  $op
 * @return boolean
 */
function date_compare($date1, $date2, $op) {

	$date1= strtotime(formatDate($date1, true));
	$date2= strtotime(formatDate($date2, true));

	switch($op) {
		case '>':
			if($date1 > $date2) return true;
			else return false;
			break;
		case '<':
			if($date1 < $date2) return true;
			else return false;
		case '>=':
			if($date1 >= $date2) return true;
			else return false;
			break;
		case '<=':
			if($date1 <= $date2) return true;
			else return false;
		case '==':
			if($date1 == $date2) return true;
			else return false;
		default:
			return false;
	}
}

/**
 * ln10filename()
 * Build a localized filename
 * according to the current language
 *
 * @param string $file
 * @return string
 */
function ln10filename($file)
{

	$tmp=@explode(".", $file);
	$total = count($tmp) - 1;
	$ext = $tmp[$total];
	unset($tmp[$total]);

	return @implode(".", $tmp). '.' . U_L. '.' .$ext;
}


/**
 * distInclude()
 * Include the required file
 * if no user file is found,
 * includes the dist/ version file.
 * Localized files have the priority
 *
 * @param string $file
 * @return void
 */
function distInclude($file, $default_dist)
{
    // If tpl_ file, we first check if file is handled by a plugin
    if(Stringy\Stringy::create(basename($file), CHARSET)->startsWith('tpl_', false)) {
        $file_plugin = str_replace(THEME_PUBLIC_PATH, '../', $file);
        $paths = getOverridePluginsFiles(enabledPlugins(), $file_plugin);
        
        if(count($paths) > 0) {
            if(count($paths) > 1) {
                _debug('[distInclude() plugin override] possible conflicts : '.count($paths). ' occurences of the same file. Loaded from '. $paths[0], 'warning');
            }
            _debug('[distInclude() plugin override] : '.$paths[0]);
            include_once($paths[0]);
            return true;
        }
    }

	$l10n_file = ln10filename($file);
	$l10n_file_dist =  dirname($l10n_file).'/dist/'.basename($l10n_file);

	$file_dist_default =  $default_dist.'dist/'.basename($file);
	$l10n_file_dist_default = $default_dist.'dist/'.basename($l10n_file);

	if(file_exists($l10n_file)) {
		_debug('distInclude() file Inclusion : '.$l10n_file);
		include_once($l10n_file);

	} elseif(file_exists($l10n_file_dist))  {
		_debug('distInclude() file Inclusion : '.$l10n_file_dist);
		include_once($l10n_file_dist);
	}
	elseif(file_exists($file)) {
		_debug('distInclude() file Inclusion : '.$file);
		include_once($file);
	}
	elseif(file_exists($l10n_file_dist_default)) {
		_debug('distInclude() file Inclusion : '.$l10n_file_dist_default);
		include_once($l10n_file_dist_default);
	}
	elseif(file_exists($file_dist_default)) {
		_debug('distInclude() file Inclusion : '.$file_dist_default);
		include_once($file_dist_default);
	}
	else {
		_debug('distInclude() file Inclusion : '.dirname($file).'/dist/'.basename($file));
		include_once(dirname($file).'/dist/'.basename($file));
	}

}

/**
 * getOverridePluginsFiles()
 * return an array of paths
 * corresponding to the given filename
 * and matching the array of given plugins 
 *
 * @param array $a
 * @param string $filename
 * @return array
 */
function getOverridePluginsFiles($a, $filename) {

	$o = array();
	if(count($a) == 0) return $o;

	foreach ($a as &$v) {
		$path = '../plugins/'.$v.'/override/'.str_replace('../', '', $filename);

		// @todo - Voir s'il ne faut pas renforcer le test is_file($path) / is_dir($path)
		// le test a été modifié pour permettre l'accès à de nouveaux modules via plugins : exemple : http://localhost/linea21_v2.2/admin/index.php?rub=contrib
		if(file_exists($path)) {
// 		if(is_file($path) && file_exists($path)) {
			array_push($o, $path);
		}
	}
	return $o;

}

/**
 * override()
 * check if override version exists for the whole app
 * then if a template file version exists or not
 * finally, if no user file is found in theme,
 * return the default module version file.
 *
 * @param string $file
 * @param string $path
 * @return string
 */
function override($file, $path = null, $default = true)
{
	// we first check if file is handled by a plugin
	$paths = getOverridePluginsFiles(enabledPlugins(), $file);

	if(count($paths) > 0) {
		if(count($paths) > 1 && is_file($file)) {
			_debug('[plugins override] possible conflicts : '.count($paths). ' occurences of the same file. Loaded from '. $paths[0], 'warning');
		}
		_debug('[plugins override] : '.$paths[0]);
		return $paths[0];
	}

	// if not, we check if file exists in '../templates/override/'
	$filename = '../templates/override/' . str_replace('../', '', $file);
	if(file_exists($filename)) {
		_debug('[general override] : '.$filename);
		return $filename;

	}

	// if not again we are checking into themes folders
	if(is_null($path)) {
		$path = THEME_PUBLIC_PATH;
	}

	$theme_file = $path.'override/'.str_replace('../', '', $file);

	if(file_exists($theme_file)) {
		_debug('[thematic override] : '.$theme_file);
		return $theme_file;

		// finally we include the default one if asked
	}
	if($default) {
		_debug('[no override] : '.$file);
		return $file;
	}

	return true;

}


/**
 * formatDate()
 * Renvoie la date aux formats yyyy-mm-dd ou dd-mm-yyyy suivant le cas de départ
 * Si $db == true renvoie toujours la date au format yyyy-mm-dd
 *
 * @param string $date
 * @param boolean $db
 * @return string $new_date
 */
function formatDate($date, $db = false)
{
	@list($part1, $part2, $part3) = explode('-', $date);
	if(strlen($part1) == 2) {
		$new_date = $part3 . '-' . $part2 . '-' . $part1;
	} else {
		$new_date = $part1 . '-' . $part2 . '-' . $part3;
	}
	if($db == true) {
		// always return yyyy-mm-dd format
		if(strlen($part1) == 2) {
			$new_date = $part3 . '-' . $part2 . '-' . $part1;
		} else {
			$new_date = $part1 . '-' . $part2 . '-' . $part3;
		}
	}

	return $new_date;
}


/**
 * date_rfc2822()
 * Format date to RFC 2822 date format
 * @param string $date
 * @return string (exemple : Thu, 21 Dec 2000 16:01:07 +0200)
 */
function date_rfc2822($date) {
	if(!isNullDate($date)) {
		$tmp_date = formatDate($date, true);
		@list($y, $m, $d) = explode('-', $tmp_date);
		return date("r", mktime(3, 0, 0, $m, $d, $y));
	} else {
		return false;
	}
}


function isNullDate($date)
{
	if(substr($date, 0, 10) == '0001-01-01' || substr($date, 0, 10) == '01-01-0001') {
		return true;
	} else return false;

}

/**
 * empty_nc()
 * retourne le contenu ou N.C
 *
 * @param string $content
 * @return string $content
 */
function empty_nc($content)
{
	if(!empty($content)) $content = trim($content);
	if ($content=='' || isNullDate($content)) $content = _t('divers','nc');

	return $content;
}

/**
 * zero_asNC()
 * retourne le chiffre ou N.C
 *
 * @param string $content
 * @return string $content
 */
function zero_asNC($content)
{
    if($content != 0) $content = trim($content);
    else $content = _t('divers','nc');
    
    return $content;
}

/**
 * empty_none()
 * retourne le contenu ou 'aucun'
 *
 * @param string $content
 * @return string $content
 */
function empty_none($content)
{
    if(!empty($content)) $content = trim($content);
	if (empty($content)) $content = _t('divers','none');

	return $content;
}

/**
 * empty_none()
 * retourne le contenu ou 0
 *
 * @param string $content
 * @return string $content
 */
function empty_numeric($content)
{
	$content = trim($content);
	if (empty($content)) $content = 0;

	return $content;
}

/**
 * checkdate_validity()
 * Vérifie la validité d'une date
 *
 * @param string $date
 * @param string $msg (optionnal)
 * @return boolean true or error message (string)
 */
function checkdate_validity($date, $msg= '')
{

	if(empty($date)) return  $msg ._t('date','do_provide');

	$date=formatDate($date, true);
	@list($year, $month, $day) = explode('-', $date);

	if(!preg_match('/^\d{4}-\d\d-\d\d$/', $date)) {
		return $msg . _t('date','not_valid');
	}

	if (!@checkdate($month , $day , $year)) return $msg . _t('date','date_do_not_exist');
	return true;
}

/**
 * setCookieLinea21()
 * Vérifie la validité d'une date
 *
 * @param string $cname
 * @param string $cvalue
 * @param integer $expires
 * @return boolean true or error message (string)
 */
function setCookieLinea21($cname, $cvalue, $expires) {
    
    
    if (version_compare(phpversion(), '7.3.0', '<')) {
        if(Stringy\Stringy::create(SITE_ROOT_URL, CHARSET)->startsWith('https', false))
            setcookie($cname, $cvalue, $expires, '/; SameSite=Strict; Secure');
        else 
            setcookie($cname, $cvalue, $expires, '/; SameSite=Strict;');
    } else {
		// use 'secure' => true to allow cookie for https use only
        if(Stringy\Stringy::create(SITE_ROOT_URL, CHARSET)->startsWith('https', false))
            setcookie($cname, $cvalue, ['expires' => $expires, 'path' => '/', 'httponly' => true, 'samesite' => 'Strict', 'secure' => 'true']);
        else 
            setcookie($cname, $cvalue, ['expires' => $expires, 'path' => '/', 'httponly' => true, 'samesite' => 'Strict']);
    }
}

/**
 * sortItems()
 * Return an array with key and value
 * with ID as key and RANGE as value
 * @return array
 */
function sortItems($rub, $data) {

	$a = array();

	for($i=0; $i<count($data); $i++) {
		$v = $data[$i];
		$a[$v]= $i+1;
	}

	return $a;

}

/**
 * display_errors()
 * Affichage d'un message d'erreur utilisateur
 *
 * @param string $msg
 * @return void (echo)
 */
function display_errors($msg)
{
	$display_it = "<div class=\"error\">\n";
	$display_it .= $msg;
	$display_it .= "</div>\n";
	echo $display_it;
}

/**
 * system_error()
 * Affichage d'un message d'erreur système
 *
 * @param string $msg
 * @return void (echo)
 */
function system_error($msg = ERROR_SYSTEM)
{
	$display_it = "<div class=\"systemerror\">\n";
	$display_it .= $msg;
	$display_it .= "</div>\n";
	echo $display_it;
	exit;
}

/**
 * is_https()
 * test if current request use https
 * https://stackoverflow.com/questions/1175096/how-to-find-out-if-youre-using-https-without-serverhttps
 * @return boolean
 */
function is_https() {
	if ( (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || (!empty($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443) )
		return true;
		else
			return false;
}

/**
 * Converts a number into a short version, eg: 1000 -> 1k
 * Based on: http://stackoverflow.com/a/4371114
 * @param $n
 * @param $precision
 * @return string
 */
function number_format_short( $n, $precision = 1 ) {
	if ($n < 900) {
		// 0 - 900
		$n_format = number_format($n, $precision);
		$suffix = '';
	} else if ($n < 900000) {
		// 0.9k-850k
		$n_format = number_format($n / 1000, $precision);
		$suffix = 'K';
	} else if ($n < 900000000) {
		// 0.9m-850m
		$n_format = number_format($n / 1000000, $precision);
		$suffix = 'M';
	} else if ($n < 900000000000) {
		// 0.9b-850b
		$n_format = number_format($n / 1000000000, $precision);
		$suffix = 'B';
	} else {
		// 0.9t+
		$n_format = number_format($n / 1000000000000, $precision);
		$suffix = 'T';
	}

	// Remove unecessary zeroes after decimal. "1.0" -> "1"; "1.00" -> "1"
	// Intentionally does not affect partials, eg "1.50" -> "1.50"
	if ( $precision > 0 ) {
		$dotzero = '.' . str_repeat( '0', $precision );
		$n_format = str_replace( $dotzero, '', $n_format );
	}

	return $n_format . $suffix;
}

/**
 * cast_number()
 * @param string $v
 * @return float|int|string
 */
function cast_number(string $v) {

	if (is_numeric($v)) {
		if(strpos($v , '.' ) !== false) return (float) $v;
		else return (int) $v;
	} else return 'NaN';

}

/**
 * url_exists()
 * check if url exists and responds
 * @param string $url
 * @return boolean|number
 */
function url_exists($url) {
    $headers = @get_headers($url);
    return is_array($headers) ? preg_match('/^HTTP\/\d+\.\d+\s+2\d\d\s+.*$/',$headers[0]) : false;
}


/**
 * get_min_name()
 * obtenir le nom de la miniature d'un fichier
 *
 * @param string $path
 * @return string $min_path
 * @todo can be removed later / still used in class.upload.php
 */
function get_min_name($path)
{
	$short_path = dirname($path);
	$filename = basename($path);
	$min_path = $short_path . "/min_" . $filename;
	return $min_path;
}

/**
 * ExcedMaxSize()
 * Teste si une image dépasse ou non la taille autorisée (en pixels)
 *
 * @param string $path
 * @param integer $width_max
 * @return boolean
 */
function ExcedMaxSize($path, $width_max)
{
	list($width, $height, $type, $attr) = getimagesize($path);
	if ($width > $width_max || $height > $width_max) return true;
	else return false;
}

/**
 * cancel_button()
 * génére un bouton de retour
 *
 * @param  $back_uri
 * @return string
 */
function cancel_button($back_uri)
{
	return '<input name="annuler" type="button" value="' . _t('btn','annul') . '" class="button" id="annuler" onclick="window.location=\'' . $back_uri . '\'" />';
}

/**
 * cancel_button_close_parent()
 * génére un bouton de retour
 *
 * @param  $back_uri
 * @return string
 */
function cancel_button_close_parent()
{
	return '<input name="annuler" type="button" value="' . _t('btn','annul') . '" class="button" id="annuler" onclick="parent.$.fn.colorbox.close();" />';
}

/**
 * GetDisplayUserRight()
 * renvoie les droits d'un utilisateur
 *
 * @param string $indice
 * @param string $module
 * @return string
 */
function GetDisplayUserRight($indice, $module = -1)
{
	$indice = strtoupper($indice);
	if ($indice == 'U') return _t('user','norights');
	if ($indice == 'A') return _t('user','adminrights');
	if ($indice == 'O' && $module == 'sdi') return _t('user','managerrights');
	if ($indice == 'O' && $module == 'dashboard') return _t('user','managerrights');
	if ($indice == 'O' && $module == 'project') return _t('user','managerrights');
	if ($indice == 'O' && $module == 'workshop') return _t('user','animatorrights');
	if ($indice == 'O' && ($module != 'workshop' && $module != 'dashboard')) return _t('user','redactorrights');
}


function getWorkgroupSmartFolder($id, $only_basename = false) {
    
    $rootpath = '../library/userfiles/workgroups/';
    $path = $rootpath. $id.'/';
    
    // simple syntax, we set path 
    if(file_exists($path)) {
        if($only_basename) return basename($path);
        else return $path;
    } else {
        // smart syntax - we set path, only one should be returned
        $paths = glob($rootpath . $id .'-*',  GLOB_ONLYDIR);
        $path =  $paths[0];
    }
    
    // we return path or folder name depending on option
    if($only_basename) return basename($path);
    else return $path;
}

/**
 * get_linkin_page()
 *
 * @param array $table_link
 * @param integer $total
 * @param integer $debut
 * @param integer $pas
 * @return void
 **/
function get_linkin_page($table_link, $total, $debut, $pas = SELECT_LIMIT)
{
	$result = ceil($total / $pas);
	if ($result <= 1) return '';
	else {
		$link = '<div class="pagination">'.PHP_EOL;

		// Previous arrow pagination
		if(($debut - $pas) >= 0) {
		$array_pos = array ('debut' => $debut - $pas);
		$new_table_link = array_merge ($table_link, $array_pos);
		$link .= '<a href="'. HrefMaker($new_table_link) . '">«</a>'.PHP_EOL;
		} else {
			$link .= '« ';
		}
		$sep='';
		for($i = 0; $i < $result; $i++) {
			$current_pos = ($pas * $i);
			if ($debut == $current_pos) $link .= $sep."<span>" . ($i + 1) . "</span> \n";
			else {
				$array_pos = array ('debut' => $current_pos);
				$new_table_link = array_merge ($table_link, $array_pos);
				$link .= $sep.'<a href="'. HrefMaker($new_table_link) . '">' . ($i + 1) . '</a>'.PHP_EOL;
			}
			// $sep=' | ';
		}
		// Next arrow pagination 
		if(($debut + $pas) < $total) {
			$array_pos = array ('debut' => $debut + $pas);
			$new_table_link = array_merge ($table_link, $array_pos);
			$link .= '<a href="'. HrefMaker($new_table_link) . '">»</a>'.PHP_EOL;
		} else {
			$link .= ' »';
		}
		$link .= '</div>'.PHP_EOL;

		return $link;
	}
}

/**
 * linkin_page()
 * création d'un navigateur de pages numérotées
 *
 * @param string $string_uri
 * @param integer $total
 * @param integer $debut
 * @param integer $pas
 * @return string $link
 */
function linkin_page($string_uri, $total, $debut, $pas = SELECT_LIMIT)
{
	$result = ceil($total / $pas);
	if ($result <= 1) return '&nbsp;';
	else {
		if (strpos($string_uri, '?') === false) $string_uri .= '?';
		else $string_uri .= '&amp;';
		$link = '<div class="pagination">';

		// Previous arrow pagination
		if(($debut - $pas) >= 0) {
			$pos = $debut - $pas;
			$link .= '<a href="'. $string_uri . 'debut=' . $pos . '">«</a>'.PHP_EOL;
		} else {
			$link .= '« '.PHP_EOL;
		}

		for($i = 0; $i < $result; $i++) {
			$current_pos = ($pas * $i);
			if ($debut == $current_pos) $link .= "<span>" . ($i + 1) . "</span> \n";
			else $link .= "<a href=\"" . $string_uri . "debut=" . $current_pos . "\">" . ($i + 1) . "</a> \n";
		}

		// Next arrow pagination
		if(($debut + $pas) < $total) {
			$pos = $debut + $pas;
			$link .= '<a href="'. $string_uri . 'debut=' . $pos . '">»</a>'.PHP_EOL;
		} else {
			$link .= ' »'.PHP_EOL;
		}

		$link .= '</div>';
		return $link;
	}
}

/**
 * display_statut()
 * renvoie le statut en pleines lettres
 *
 * @param string $statut
 * @return string $result
 */
function display_statut($statut)
{
	switch ($statut) {
		case 'P':
			$result = _t('statut','public');
			break;
		case 'D':
			$result = _t('statut','draft');
			break;
		case 'E':
			$result = _t('statut','E');
			break;
		case 'AA':
			$result = _t('statut','AA');
			break;
		case 'PA':
			$result = _t('statut','PA');
			break;
		case 'C':
			$result = _t('statut','C');
			break;
		case 'U':
			$result = _t('statut','U');
			break;
		case 'O':
			$result = _t('statut','O');
			break;
		case 'A':
			$result = _t('statut','A');
			break;
		case 'W':
			$result = _t('statut','W');
			break;
		default:
			$result = _t('statut','public');
	}
	return mb_ucfirst($result);
}

/**
 * get_class_status()
 * renvoie le statut sous forme de classe (css)
 *
 * @param string $status
 * @return string $result
 */
function get_class_status($status)
{
	switch ($status) {
		case 'P':
			$c = 'public';
			break;
		case 'D':
			$c = 'draft';
			break;
		case 'E':
			$c = 'deleted';
			break;
		case 'AA':
			$c = 'archive-private';
			break;
		case 'PA':
			$c = 'archive-public';
			break;
		default:
			$c = 'public';
	}
	return 'status-'.$c;
}

/**
 * sys_number_format()
 * returns a given number formatted the 'system' way
 * for example a french float 25,68 would become 25.68
 * @param string $statut
 * @return string $result
 */

function sys_number_format($number) {
	if(LANGUAGE == "fr") {
		$number = str_replace(',', '.', $number);
		$number = str_replace(' ', '', $number);
	}
	return $number;
}

function fnumber_format($number, $dec, $forceprefix = true, $floatcast = true) {

	if(!is_numeric($number)) return $number;
	
	$number = (float) $number; // double stored as scientific notation in db ar interpreted as double - fix bug #1148 
	
	$number = str_replace(' ', '', $number);

	// we determine decimal numbers
	// if 'auto' option is passed
	if($dec == 'auto') {
		$number = (string) $number;
		if(!strpos($number, '.')) {
			$dec = 0;
		} else {
			$dec = strlen(substr(strrchr($number, '.'), 1));
		}
	}

	if(!is_numeric($number)) return '';

	if($floatcast) $number = (string) floatval($number); // transform 65.00 to 65 --> remove trailing zero after decimals

	if(LANGUAGE == "fr" && strpos($number, '.') !== false) {
		$number = number_format($number, $dec, ',', ' ');
	} elseif(strpos($number, '.') !== false) { // only if number is a float -- is_float() does not work
		$number = number_format($number, $dec, '.', ',');
	}

	// adding space to negative symbol - removed jquery.tablesorter does not sort if there is one space
	//$number = str_replace('-', '- ', $number);

	// adding '+' symbol if positive
	if(is_numeric($number[0]) && $forceprefix) $number = '+' . $number;

	return $number;

}

/**
 * QuickBoxNow()
 * Génération de la quickbox
 *
 * @param string $module
 * @param string $h1
 * @param string $liste
 * @param string $suffixclass
 * @return string $quickbox
 */
function QuickBoxNow($module, $h1, $liste, $suffixclass = '')
{
	$quickbox = "<div class=\"entete\">\n<div class=\"qb_ico\" id=\"qbico" . $suffixclass . "\" title=\"" . $module . "\"></div>\n";
	$quickbox .= "<div class=\"quickbox\" id=\"qbbg" . $suffixclass . "\">\n";
	$quickbox .= "<h1>" . $h1 . "</h1>\n";
	$quickbox .= "<ul>";
	$quickbox .= $liste;
	$quickbox .= "</ul>";
	$quickbox .= "</div>";
	$quickbox .= "</div>";

	return $quickbox;
}

/**
 * redirect_to()
 * redirect to module's default page
 * or given module
 *
 * @param string $rub
 * @return void
 */
function redirect_to($module = '@module_default')
{
	if($module == '@module_default') {
		$module = $_REQUEST['rub'];
	}
	
	header("Location: " . CURRENT_APP_URL . "index.php?rub=".$module);
	exit;
	

}

/**
 * ReloadIndex()
 * Chargement de l'index après destruction de sessions
 *
 * @param string $item
 * @return void
 */
function ReloadIndex($item)
{
    global $l21auth;
    
    logfile(LOG_MAINFILE, array('ReloadIndex call', 'item=' . $item ));
	switch ($item) {
		case 'public':
			header("Location: " . SITE_ROOT_URL . "public/index.php");
			break;
		case 'admin':
		    if(!$l21auth->isAuthenticated()) header("Location: ../admin/login.php");
		    else header("Location: ../admin/logout.php?token=". $l21auth->GetSessionElement('logout_token'));
			break;
	}
	exit;
}

/**
 * getmicrotime()
 * renvoie le temps en microsecondes
 *
 * @return float
 */
function getmicrotime()
{
	list($usec, $sec) = explode(" ", microtime());
	return ((float)$usec + (float)$sec);
}


/**
 * availablePlugins()
 * Return available plugins
 * @return array
 **/
function availablePlugins() {
	$a = array();
	if ($handle = opendir('../plugins/')) {
		while (false !== ($file = readdir($handle))) {
			if (substr($file, 0, 4) == 'l21_' ) {
				array_push($a, $file);
			}
		}
		closedir($handle);
	}

	return $a;
}

/**
 * enabledPlugins()
 * Return available plugins
 * @return array
 **/
function enabledPlugins() {
	$a = array();
	if ($handle = opendir('../plugins/')) {
		while (false !== ($file = readdir($handle))) {
			if (substr($file, 0, 4) == 'l21_' && file_exists('../plugins/'.$file.'/.active')) {
				array_push($a, $file);
			}
		}
		closedir($handle);
	}

	return $a;
}

/**
 * getCurrentPlugin()
 * Return current plugin folder based on __FILE__ constant
 * @param $filepath : string
 * @return string
 **/
function getCurrentPluginFolder($filepath) {

    $tmp = Stringy\Stringy::create($filepath, CHARSET)->afterLast(SITE_PATH.'plugins/');

    list($pluginFolder, $crumbs) = explode('/', $tmp, 2);
    
    return $pluginFolder;
}


/**
 * array_extract()
 * 
 * @param array $inputArray
 * @param string $key
 * @return array
 * @todo Not that clean : would be nice to remove the RecursiveIteratorIterator part
 */
function array_extract($inputArray, $needle, $recursive = false, $identifier = false) {
	
	$simpleArray = array();
	$flattenArray = array();
	
	foreach ($inputArray as $key => &$entry) {
		
		// echo "$key <br ><br>";
		// where only interested in numeric items
		// as those are the actual children
		if( !is_numeric( $key ) )
		{
			// otherwise continue
			continue;
		}
		// echo $entry[$needle]. " / ";
		$simpleArray[] = $entry[$needle];
		//echo '<br> <br>Array : '. implode(' / ', $simpleArray).'<br> <br>';
		if($recursive && isset($entry[$identifier])) {
			$item = array_extract($entry[$identifier], $needle, $recursive, $identifier);
			if ($item) {
				$simpleArray[] = $item;
			}
		}
	}
	// return $simpleArray;
	// $simpleArray return Array ( [0] => 6 [1] => Array ( [0] => 56 [1] => Array ( [0] => 57 ) ) [2] => 5 [3] => 4 [4] => 3 ) 
	// we flatten it to Array ( [0] => 6 [1] => 56 [2] => 57 [3] => 5 [4] => 4 [6] => 3 ) 
	$it = new RecursiveIteratorIterator(new RecursiveArrayIterator($simpleArray));
	foreach($it as $v) {
		array_push($flattenArray, $v);
	}
	return $flattenArray;
}


function getLevelChildren($data, $id) {

	$res = array();

	foreach ($data as $item) {

		if($item['level_parent'] == $id) {
			$children = getLevelChildren($data, $item['level_id']);
			if ($children) {
				$item['children'] = $children;
			}
			$res[] = $item;
		}
	}
	return $res;
}


/**
 * sql_dump2array()
 * @param $url
 * @param $a
 * @return array
 * @link http://fr2.php.net/manual/fr/function.mysql-query.php
 */
function sql_dump2array($url, $a = -1) {

	$handle = @fopen($url, "r");
	$query = "";
	if($a == -1) $a = array();

	while(!feof($handle)) {
		$sql_line = fgets($handle);
		// we handle MySQL comments : # and -- see : https://dev.mysql.com/doc/refman/8.0/en/comments.html
		if (trim($sql_line) != "" && substr( $sql_line, 0, 2) !== "--" && substr( $sql_line, 0, 1) !== "#") {
			array_push($a, $sql_line);
		}
	}
	return $a;
}

/**
 * sql_status_filter()
 * @param $fieldname
 * @param $a array
 * @return string
 */
function sql_status_filter($fieldname, $a) {

	$str = '';
	$sep = '';

	foreach ($a as $value) {
		$str.= $sep. $fieldname . " = '". $value ."'";
		$sep = " OR ";
	}
	if(count($a) > 1) $str ='('. $str .')';

	return $str;
}

/**
 * getDynamicProgressbar()
 * Return a graphic progress bar generated by easy-pie-chart
 * @param $value integer
 * @param $size integer
 * @return string
 */
function getDynamicProgressbar($value, $size = 110) {


	// include easy-pie-chart js library
	footerAddJS('../lib/js/easy-pie-chart/dist/jquery.easypiechart.min.js');
	footerAddJS('../lib/js/waypoints/lib/jquery.waypoints.min.js');
	footerAddInlineJS("$('#progession-chart').waypoint( {
	handler: function(direction) {
		$(this.element).easyPieChart({
			size:'".$size."',
			lineWidth:'15',
			trackColor: '#ccc',
			barColor:'#456F7F',
			lineCap:'butt',
			onStep: function(from, to, percent) {
				$(this.el).find('.percent').text(Math.round(percent));
			}
		});
		this.destroy();
	}, 
	offset: 'bottom-in-view'  
});");
	
	$str   = 	'<span id="progession-chart" data-percent="'.$value.'">'.PHP_EOL;
	$str  .=	'<span class="percent"></span>'.PHP_EOL;
	$str  .=	'</span>'.PHP_EOL;

	return $str;
}

/**
 * geocode()
 * get Long/Latitude coordinates
 * @param $address string
 * @return object
 */
function geocode($address) {

	$o = new StdClass; // prevent PHP 'Warning: Creating default object from empty value'
	$o->{'status'} = 'geocoder_disabled';

	if(!defined('GEOCODER_ENABLED') || GEOCODER_ENABLED == 0) {
	    logfile(LOG_MAINFILE, array('[action] geocoding', 'geocoder is disabled'));
		return $o->{'status'};
	}

	$address=str_replace(" ","+",$address);
	
	// we clean string to prevent issue on char encoding (had problem with Châlons en Champagne (Windows environnement)
	$url = cleanString(GEOCODER_URL.$address);
	
	if ($address) {
	    $json = file_get_contents($url);

		$o = json_decode($json);
		
		logfile(LOG_MAINFILE, array('[action] geocoding', $url, count($o->{'features'}). ' results (first taken)'));
		if($json === false) logfile(LOG_MAINFILE, array('[action] geocoding', '!!! It seems we are not able to fetch the given URL : '. $url));
		
	}

	return $o;
}


/**
 * placeholderReplace()
 * format '{$key}'
 * @param $array
 * @param $input
 * @return array
 */
function placeholderReplace($array, $input) {
	foreach ($input as $key => $value)
	{
		$array = preg_replace("/{{$key}}/i", $value, $array);
	}
	return $array;
}

/**
 * generateRandomString()
 * @param integer $length
 * @param string $chars
 * @return string
 */
function generateRandomString($length = 10, $chars = false) {
	
    if($chars == false) $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    else $characters = $chars;
    
	$randomString = '';
	
	for ($i = 0; $i < $length; $i++) {
		$randomString .= $characters[rand(0, strlen($characters) - 1)];
	}
	
	return $randomString;
}

/**
 * generateHTMLPage()
 *
 * @param string $mainContent
 * @param string $app
 * @param array $cssfiles
 * @param array $jsfiles
 * @param string $title
 * @return string
 **/
function generateHTMLPage($mainContent, $app, $cssfiles = array(), $jsfiles = array(), $title = SITE_NAME)
{
    if(MOD_DEBUG == 1) $csuffix = '?v='. time(); else $csuffix= '';
    
    $css = $js = '';
    
    if($app == 'admin') {
        $themeurl = ADMIN_THEME_URL;
        $themepath = THEME_ADMIN_PATH;
    }
    else {
        $themeurl = PUBLIC_THEME_URL;
        $themepath = THEME_PUBLIC_PATH;
    }
    
    if(count($cssfiles) > 0) {
        foreach($cssfiles as $f) {
            if(file_exists($themepath.$f)) $css .= '<link rel="stylesheet" href="'.$themeurl.$f.$csuffix.'" />' . PHP_EOL;
            else $css .= '<link rel="stylesheet" href=../'.$f.$csuffix.'" />' . PHP_EOL;
        }
    }
    if(count($jsfiles) > 0) {
        foreach($jsfiles as $f) {
            if(file_exists($themepath.$f)) $js .= '<script src="'.$themeurl.$f.$csuffix.'"></script>' . PHP_EOL;
            else $js .= '<script src="../'.$f.$csuffix.'"></script>' . PHP_EOL;
        }
    }

	$page  = '<!doctype html>' . PHP_EOL;
	$page .= '<html lang="' . U_L . '">' . PHP_EOL;
	$page .= '<head>' . PHP_EOL;
	$page .= '<meta charset="' . CHARSET . '" />' . PHP_EOL;
	$page .= '<script src="'. SITE_ROOT_URL . 'lib/js/jquery-3.6.1.min.js"></script>' . PHP_EOL;
	$page .= $js;
	$page .= '<link rel="stylesheet" href="'. SITE_ROOT_URL . 'lib/vendor/font-awesome/css/font-awesome.min.css" />' . PHP_EOL;
	$page .= '<link rel="stylesheet" href="'. SITE_ROOT_URL . 'lib/vendor/hint.css-2.7.0/hint.min.css" />' . PHP_EOL;
	$page .= '<link rel="stylesheet" href="'. SITE_ROOT_URL . 'lib/js/foundation-datepicker/stylesheets/foundation-datepicker.css" />' . PHP_EOL;
	$page .= '<script src="'. SITE_ROOT_URL . 'lib/js/foundation-datepicker/js/foundation-datepicker.js"></script>' . PHP_EOL;
	$page .= $css;
	$page .= '<title>'.$title.'</title>' . PHP_EOL;
	$page .= '</head>' . PHP_EOL;
	$page .= '<body style="padding:1em;">' . PHP_EOL;
	$page .= $mainContent;
	$page .= '</body>' . PHP_EOL;
	$page .= '</html>' . PHP_EOL;

	return $page;
}

/**
 * _debug()
 * Display a debug message
 * @param string
 * @return void
 **/
function _debug($string, $messagetype = 'system')
{
	if(isset($GLOBALS['debugbar']))
	{
		global $debugbar;

		$debugbar["messages"]->addMessage($string, $messagetype);
		
	}
}

/**
 * get_absolute_url()
 * convert relative path to absolute url
 * @param string $path
 * @return string
 */
function get_absolute_url($path) {
	if($content_light =  Stringy\Stringy::create($path, CHARSET)->startsWith('../', false))
		$path = str_replace('../', SITE_ROOT_URL, $path);
		return $path;
}

/**
 * headerAddCSS()
 * Add CSS into header
 * @param string
 * @param string
 * @return boolean
 **/
function headerAddCSS($path, $pos = 'default')
{
	if(!fopen($path, 'r')) {
		_debug('<b>Problem loading CSS file</b> : '.$path . ' (position : '.$pos.')', 'error');
		return false;
	}
	$path = get_absolute_url($path);

	if(isset($GLOBALS['CSSheader']) && is_numeric(strpos($GLOBALS['CSSheader'], $path))) {
		_debug('<b>Warning : CSS file already loaded</b> :'.$path, 'warning');
		return false;
	}

	$str = '<link type="text/css" rel="stylesheet" href="'.$path.'" />'.PHP_EOL;

	if(isset($GLOBALS['CSSheader']))
	{
		if($pos == 'first') {
			$GLOBALS['CSSheader'] = $str . PHP_EOL . $GLOBALS['CSSheader'] ;
		} else {
			$GLOBALS['CSSheader'] .=  PHP_EOL . $str;
		}
	} else {
		$GLOBALS['CSSheader'] = $str;
		
	}
	_debug('Loading <b>CSS file</b> : '.$path . ' (position : '.$pos.')');

	return true;
}

/**
 * headerAddJS()
 * Add JS into header
 * @param string
 * @param string
 * @return boolean
 **/
function headerAddJS($path, $pos = 'default')
{
	if(!fopen($path, 'r')) {
		_debug('<b>Problem loading JS file</b> : '.$path . ' (position : '.$pos.')', 'error');
		return false;
	}
	$path = get_absolute_url($path);

	if(isset($GLOBALS['JSheader']) && in_array($path , $GLOBALS['JSheader'])) {
		_debug('<b>Warning : JS file already loaded</b> :'.$path, 'warning');
		return false;
	}

	$str= '<script src="'.$path .'"></script>'.PHP_EOL;

	if(isset($GLOBALS['JSheader']))
	{
		if($pos == 'first') {
			$GLOBALS['JSheader'] = $str . $GLOBALS['JSheader'];
		}
		else {
			$GLOBALS['JSheader'] .= $str . PHP_EOL ;
		}
	} else {
		$GLOBALS['JSheader'] = $str;
	}
	_debug('Loading <b>JS file</b> : '.$path . ' (position : '.$pos.')');

	return true;
}

/**
 * footerAddInlineJS()
 * Add inline JS into footer
 * the resulting js is wrapped with <script> tags and jQuery .ready() function if $wrap == true
 * @param string
 * @param string
 * @return boolean
 **/
function footerAddInlineJS($str, $pos = 'default', $wrap = true)
{
if($wrap == true) {
	if(isset($GLOBALS['JSInlinefooter']))
	{
		if($pos == 'first') {
			$GLOBALS['JSInlinefooter'] = $str . PHP_EOL. $GLOBALS['JSInlinefooter'];
		}
		else {
			$GLOBALS['JSInlinefooter'] .= $str . PHP_EOL ;
		}
	} else {
		$GLOBALS['JSInlinefooter'] = $str. PHP_EOL;
	}
} else {
	if(isset($GLOBALS['JSInlinefooter_nowrap']))
	{
		if($pos == 'first') {
			$GLOBALS['JSInlinefooter_nowrap'] = $str . PHP_EOL. $GLOBALS['JSInlinefooter_nowrap'];
		}
		else {
			$GLOBALS['JSInlinefooter_nowrap'] .= $str . PHP_EOL ;
		}
	} else {
		$GLOBALS['JSInlinefooter_nowrap'] = $str. PHP_EOL;
	}
}
	_debug('Loading <b>inline JS</b> : '.cutText($str, 90) . ' (position : '.$pos.')');

	return true;
}

/**
 * footerAddJS()
 * Add JS into footer
 * @param string
 * @param string
 * @return boolean
 **/
function footerAddJS($path, $pos = 'default')
{
	
	if(!fopen($path, 'r')) {
		_debug('<b>Problem loading JS file</b> : '.$path . ' (position : '.$pos.')', 'error');
		return false;
	}
	$path = get_absolute_url($path);

	if(isset($GLOBALS['JSfooter']) && is_numeric(strpos($GLOBALS['JSfooter'], $path))) {
		_debug('<b>Warning : JS file already loaded</b> :'.$path, 'warning');
		return false;
	}

	$str= '<script src="'.$path .'"></script>'.PHP_EOL;

	if(isset($GLOBALS['JSfooter']))
	{
		if($pos == 'first') {
			$GLOBALS['JSfooter'] = $str . $GLOBALS['JSheader'];
		}
		else {
			$GLOBALS['JSfooter'] .= $str . PHP_EOL ;
		}
	} else {
		$GLOBALS['JSfooter'] = $str . PHP_EOL ;
	}
	_debug('Loading <b>JS file</b> : '.$path . ' (position : '.$pos.')');

	return true;
}

function addDynamicCSS($url) {
	
	$url = get_absolute_url($url);

	// use of document.createStyleSheet for IE8 compatibility
	$str = 'if (document.createStyleSheet)
					{
						document.createStyleSheet("'.$url. '");
					}
					else
					{
						$(\'<link rel="stylesheet" type="text/css" href="'.$url. '" />\').appendTo("head");
					}';

	footerAddInlineJS($str);

}

/**
 * AddDynamicHeader()
 * Display JS and CSS header
 * @return void
 **/
function AddDynamicHeader() {
	if(isset($GLOBALS['JSheader'])) echo $GLOBALS['JSheader'];
	if(isset($GLOBALS['CSSheader']))  echo $GLOBALS['CSSheader'];
	
	if(defined('MAP_ENGINE') && MAP_ENGINE == 'leaflet') includeLeafletHeader();
}

/**
 * AddDynamicFooter()
 * Display JS footer
 * @return void
 **/
function AddDynamicFooter() {
	if(isset($GLOBALS['JSfooter'])) echo $GLOBALS['JSfooter'];
	if(isset($GLOBALS['JSInlinefooter_nowrap'])) echo $GLOBALS['JSInlinefooter_nowrap'];
	if(isset($GLOBALS['JSInlinefooter'])) {
		echo '<script>'.PHP_EOL;
		echo '	$(document).ready(function() {'.PHP_EOL;
		echo $GLOBALS['JSInlinefooter'];
		echo '  });'.PHP_EOL;
		echo '</script>'.PHP_EOL;
	}
}


function format_version_number($val) {
	$val = preg_replace("/(-rev|-r)([0-9])+/",'', $val); // remove revision number (ie "1.5-rev1815" will return "1.5")
	$val = preg_replace("/([^0-9])/",'', $val); // remove letter (if dev version)
	$val = str_replace('.', '', $val);
	
	if(strlen($val) == 2) $val = str_pad ($val ,3, '0', STR_PAD_RIGHT);
	
	return $val;
}

/**
 * newVersionNotifier()
 * Display a link to download new version if available
 * @return string
 **/
function newVersionNotifier() {
        
    
    $update = '';
    
    $current_version = format_version_number(LINEA_VERSION);
    
    if(file_exists('../tmp/update' )) {
        $latest_readable = file_get_contents ('../tmp/update');
        $latest_version = format_version_number($latest_readable);
        
        if((integer)$latest_version > (integer)$current_version) {
            $update = '<a id="download-link-header" href="'.CURRENT_APP_URL.'index.php?rub=system" title="'.sprintf(_t('check_update','dl'), $latest_readable).'"><i class="fa fa-cloud-download" aria-hidden="true"></i></a>';
        }
        
    }
    
    return $update;
}

/**
 * get_config_back_link())
 * @param $backlink
 * @param $rub
 * @param $todo
 * @param $id
 * @return mixed|void
 */
function get_config_back_link($backlink, $rub, $todo, $id) {

	if(defined('CONFIRM_LINKS')) {

		$links = explode('|', CONFIRM_LINKS);

		foreach($links as $link) {
			if(Stringy\Stringy::create($link, CHARSET)->startsWith($rub.'/'.$todo) && strpos($link, ':')) {
				list($where, $bl) = explode(':', $link);
				if(!is_null($id)) $bl = str_replace("{id}", $id, $bl);
				$bl = str_replace("{todo}", $todo, $bl);
				$bl = str_replace("{rub}", $rub, $bl);
				return $bl;
			}
		}
		// if no user backlink for given $rub/$todo, we return default value
		return $backlink;

	} else return $backlink;

}

/**
 * check4newVersion()
 * Display a link to download new version if available
 * @return string
 **/
function check4newVersion() {
	
	$url = '?name='.rawurlencode(SITE_NAME).'&url='.rawurlencode(SITE_ROOT_URL).'&email='.rawurlencode(SITE_MAIL);
			
	try{
	    if(defined('PROXY_PARAMS')) {
	        _debug('check4newVersion() - PROXY_PARAMS is defined : '.PROXY_PARAMS, 'system');
	        
	        if  (in_array  ('curl', get_loaded_extensions())) {
	            
	            // _debug('check4newVersion() - curl is enabled', 'system');
	            
	            //  list($purl,$pport) = explode(':', PROXY_PARAMS);
	            // $agent = "Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/532.4 (KHTML, like Gecko) Chrome/4.0.233.0 Safari/532.4";
	            
	            $referer = SITE_ROOT_URL;
	            
	            $ch = curl_init();
	            curl_setopt($ch, CURLOPT_URL, SITE_LINEA_URL.'/linea21-version-check.php'. $url);
	            curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1);
	            curl_setopt($ch, CURLOPT_PROXY, PROXY_PARAMS);
	            curl_setopt($ch, CURLOPT_REFERER, $referer);
	            curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
	            curl_setopt($ch, CURLOPT_MAXREDIRS, 2);
	            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
	            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
	            // curl_setopt($ch, CURLOPT_USERAGENT, $agent);
	            
	            $curldata = curl_exec($ch);
	            
	            // echo " curlpage : ". print_r($curldata);
	            // Check for errors.
                // if(curl_errno($ch)){
                //     throw new Exception('cURL error : '. curl_error($ch));
                // }
	            curl_close($ch);
	            
	            $xml = simplexml_load_string($curldata);
	            
	        } else {
	            _debug('check4newVersion() - curl is disabled', 'system');
	        }
	    }

		elseif(!$xml=@simplexml_load_file(SITE_LINEA_URL.'/linea21-version-check.php'. $url)) {
			throw new Exception('Checking for Linea21 update : xml file was not found');
		}

		if(isset($xml)) {
    		$current_version = format_version_number(LINEA_VERSION);
    		$latest_version = format_version_number($xml->num);
		}
		// for test
        // $xml->num="2.6.0";
        // $latest_version = 260;
		
		if(isset($xml->postlink) && !empty($xml->postlink)) $postlink = '<a href="'.(string)$xml->postlink.'" id="see-features" class="basic-links">'. _t('system', 'see-features') .'</a>';
		else $postlink ='';
		if(isset($xml->changelog) && !empty($xml->changelog)) $changelog = '<a href="'.(string)$xml->changelog.'" class="basic-links" id="changelog">'. _t('system', 'changelog') .'</a>';
		else $changelog ='';
		
 		// _debug('current ver : '.$current_version . ' latest ver : '.$latest_version);
        
		if(isset($xml)) {
    		if((integer)$latest_version > (integer)$current_version) {
    		    // we write temp file for further notification
    		    @file_put_contents ('../tmp/update', $xml->num);
    			$update = '<div id="version-check"><span>'._t('check_update','search').' : </span><a id="download-link" href="'.(string)$xml->link.'">'.sprintf(_t('check_update','dl'), $xml->num).'</a>' . $postlink . $changelog . '</div>';
    		} else {
    		    if(file_exists('../tmp/update')) unlink('../tmp/update');
    			$update = '<div id="version-check"><span class="no-update">'._t('check_update','search').' : '._t('check_update','ok').'</span></div>';
    		}
		} else {
		    $update = '<div id="version-check"><span class="error check-failed"">'._t('check_update','search').' : '._t('check_update','failed').'</span></div>';
		}
	}
	catch(Exception $e){
	    $update = '';
	    _debug($e->getMessage(), 'system');
	}
	// _debug('check4newVersion() : '.$update);

	return $update;
}

/**
 * loadThemeInfo()
 * Load theme info
 * @param string
 * @param string
 * @return string
 **/
function loadThemeInfo($type, $name) {
	$a = array();
	try{
		if(!@$flow=simplexml_load_file(SITE_PATH.'templates/'.$type.'/'.$name.'/theme.xml')){
			throw new Exception($name.' plugin : xml file was not found');
		}
		$a['name'] = $flow->themename;
		$a['description'] = $flow->description;
		$a['version'] = $flow->version;
		$a['date'] = $flow->date;
		$a['compatibility'] = $flow->compatibility;
		$a['author'] = $flow->author;
		$a['homepage'] = $flow->homepage;
		
		$a['restricted_edition']['files'] = array();
		$a['restricted_edition']['folders'] = array();
		
		if(isset($flow->restricted_edition) && isset($flow->restricted_edition->file)) {
			foreach($flow->restricted_edition->file as $el) {
				$a['restricted_edition']['files'][]=(string) '../templates/'. $type . '/'. $name. '/'. $el;
			}
		}
		if(isset($flow->restricted_edition) && isset($flow->restricted_edition->folder)) {
			foreach($flow->restricted_edition->folder as $el) {
				$a['restricted_edition']['folders'][]=(string) '../templates/'. $type . '/'. $name . '/'. $el;
			}
		}

		return $a;
	}
	catch(Exception $e){
		return $e->getMessage();
	}
}

/**
 Validate an email address.
 Provide email address (raw input)
 Returns true if the email address has the email
 address format and the domain exists.
  */
function validEmail($email)
{
    $isValid = true;
    
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $isValid = false;
    }
    return $isValid;
    
}

/**
 * voteEnabled($m)
 * Check if votes is enabled on
 * given module
 * @param string
 * @return boolean
 **/
function voteEnabled($m)
{
    if(!defined('MOD_VOTE') || MOD_VOTE == 0) {
        return false;
    } else {
        if(is_numeric(strpos(VOTE_MODULES, $m))) return true;
        else return false;
    }
}

/**
 * commentEnabled($m)
 * Check if comment is enabled on
 * given module
 * @param string
 * @return boolean
 **/
function commentEnabled($m)
{
	if(!defined('MOD_COMMENT') || MOD_COMMENT == 0) {
		return false;
	} else {
		if(is_numeric(strpos(COMMENT_MODULES, $m))) return true;
		else return false;
	}
}

/**
 * setBreadcrumb()
 * Set breadcrumb content
 * @param array
 * @return void
 **/
function setBreadcrumb($array)
{
	if(!isset($GLOBALS['breadcrumb'])) $GLOBALS['breadcrumb']= array();
	$GLOBALS['breadcrumb'] = array_merge($GLOBALS['breadcrumb'], $array);
}

/**
 * getBreadcrumb()
 * get the Breadcrumb for displaying
 * @param string (optional)
 * @return string
 **/
function getBreadcrumb($sep = '»')
{
	if(defined('BREADCRUMB_SEPARATOR')) $sep = BREADCRUMB_SEPARATOR;
	
	$str = '<div id="breadcrumb"><div class="breadcrumb-container">'.PHP_EOL;
	$str.= '<ul>'.PHP_EOL;
	$str.= '<li><span class="separator">'.$sep.' </span><a href="'.SITE_ROOT_URL.'public/">'._t('way', 'home').'</a></li>'.PHP_EOL;
	if(isset($GLOBALS['breadcrumb'])) {
		foreach($GLOBALS['breadcrumb'] as $key => $value) {
	
			if($value!=false) $str.= '<li><span class="separator">'.$sep.' </span><a href="'.$value.'">'.$key.'</a></li>'.PHP_EOL;
			else $str.= '<li><span class="separator">'.$sep.' </span>'.$key.'</li>'.PHP_EOL;
		}
	}
	$str.= '</ul>'.PHP_EOL;
	$str.= '</div></div>'.PHP_EOL;

	return $str;
}


/**
 * GetAllFiles()
 * Return an array of filenames
 * @param string
 * @param array
 * @param boolean
 * @param array
 * @return array
 **/
function GetAllFiles($folder, $exts=array('txt'), $recursif=true, $excludedDirs = array()) {

	$files = array();
	$dir=opendir($folder);
	while ($file = readdir($dir)) {
		if ($file == '.' || $file == '..') continue;

		$fullname = str_replace('//', '/', $folder .'/'. $file);
		
		if (is_dir($fullname)) {
			if ($recursif==true) {
				if(!in_array($fullname.'/', $excludedDirs))
					$files=array_merge($files, GetAllFiles($fullname, $exts, true, $excludedDirs));
			}
		} else {
			$path_parts = pathinfo($fullname);
			if(isset($path_parts['extension']) && in_array($path_parts['extension'], $exts)) {
				$files[] = $fullname;
			}

		}
	}
	closedir($dir);
	return $files;
}


/**
 * getDirContents()
 * Return an array of filenames
 * @url http://stackoverflow.com/questions/24783862/list-all-the-files-and-folders-in-a-directory-with-php-recursive-function
 * @param string
 * @param array
 * @return array
 **/
function getDirContents($dir, &$results = array()){
    $files = scandir($dir);

    foreach($files as $key => $value){
        $path = realpath($dir.DIRECTORY_SEPARATOR.$value);
        if(!is_dir($path)) {
            $results[] = $path;
        } elseif($value != "." && $value != "..") {
            getDirContents($path, $results);
            $results[] = $path;
        }
    }

    return $results;
}

/**
 * get_files()
 * Return an HTML list for a given folder
 * @param array
 * @param string
 * @return string
 **/
function get_files($dirFiles, $id) {

    $dirname = getWorkgroupSmartFolder($id, true);
    // we retrieve folders only to sort them alphabetically
    $ordered_dirs = array(SITE_PATH.'library/userfiles/workgroups/'. $dirname.'/');
	foreach($dirFiles as $item) {
	    // str_replace($ite, SITE_PATH.'library/userfiles/workgroups/'. $id, '');
	    if(is_dir($item)) array_push($ordered_dirs, $item);
	}
	
	// sort folders by name - no case-sensitive
	sort($ordered_dirs, SORT_STRING  | SORT_FLAG_CASE );
	

	// uncomment to sort on latest modification
    // 	   array_multisort(
	// 	     array_map( 'filemtime', $ordered_dirs ),
    // 	     SORT_NUMERIC,
    // 	     SORT_DESC,
    // 	     $dirFiles
    // 	   );
	   //var_dump($ordered_dirs);

	
	if(count($dirFiles)==0) $str =  '<ul><li>'._t('divers','no_files').'</li></ul>';
	
	else {
	    
	    $str = "";
	    
	    foreach($ordered_dirs as $dir)
	    {
	       $title = ltrim(str_replace(SITE_PATH . 'library/userfiles/workgroups/'.$dirname, '', $dir), '/');
	       $files = array_diff(scandir($dir), array('..', '.'));
	        
	        # if there is files in folders
	        if(count($files) > 0) {
	            
	            // display title only if subfolders
	            if ($title != '') $str .= '<div class="folder-name">'.$title.'</div>'.PHP_EOL;
	         
	            $list = '';
	            
	            foreach($files as $file) {
					
					// $fullpath = realpath($dir. '/' . $file); // fixing bug https://dev.linea21.com/issues/1440
	                $fullpath = str_replace('//', '/', $dir. '/' . $file);
	                $size= formatBytes(filesize($fullpath), 2);
	                
	                $tmpname = str_replace(SITE_PATH, '', $fullpath);
	                $urlfile = dirname($tmpname).'/'. rawurlencode(basename($tmpname));
	                $filename = basename(rawurldecode($urlfile));
	                
	                $comment_actions = '';
	                
	                if(MOD_COMMENT == 1 && commentEnabled('files')) {
	                    $id = str_replace('library/userfiles/', '', $urlfile);
	                    $nb = getFileComments($id);
	                    $comment_actions .= '<span class="comment_actions">';
	                    $comment_actions .= '<a class="comment_see" href="'.SITE_ROOT_URL.'comment/_ajax_view.php?id='.$id.'&amp;module=files&amp;order_by=ASC" title="' . $filename . ' - ' . _t('comment','go_to') . ' / ' . _t('comment','add') .'"><span class="cc">' . $nb . '</span><span class="textcc"> ' . _t('comment','comments').' - ' . _t('comment','add') .'</span></a>';
	                    $comment_actions .= '</span>';
	                }
	                
	                // we display files only
	                if(!is_dir($fullpath)) {
	                   $list .= '<li><a href="'. SITE_ROOT_URL . $urlfile .'">'.$filename.'</a> <em>('.$size.')</em> '.$comment_actions.'<a  class="dlfile" href="'. SITE_ROOT_URL .'library/dl.php?file='. $urlfile .'" title="'._t('divers','dl'). ' ' . $filename.'"><span>' ._t('divers','dl'). '</span></a></li>'.PHP_EOL;
	                }
	            }
	            
	            $str .= '<ul>' . $list . '</ul>';
	        }


	    }
	    

	}
	
	return $str;
}

/**
 * get_cron_job()
 * @param string $command
 * @param string $part (optionnal)
 * @return array
 */
function get_cron_job($command, $part = false) {

	$a = array();
	$command = trim($command);

	// we find the last occurence of space to split the string
	// and produce crontab date / crontab script
	$pos = strrpos($command, " ");
	if($pos === false) die('Invalid cronjob!');

	$a['date'] = substr ($command , 0, $pos);
	$a['script'] = substr ($command , $pos+1, strlen($command));
	
	if($part == false )	return $a;
	else return $a[$part];
}

/**
 * formatBytes()
 * Make File size readable
 * @param int
 * @param int (optional)
 * @return string
 **/
function formatBytes($bytes, $precision = 2) {
	$units = array('o', 'Ko', 'Mo', 'Go', 'To');

	$bytes = max($bytes, 0);
	$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
	$pow = min($pow, count($units) - 1);

	$bytes /= pow(1024, $pow);

	return round($bytes, $precision) . ' ' . $units[$pow];
}

/**
 * logfile()
 * Log into file
 * @param string
 * @param array
 * @return void
 **/
function logfile($src, $a) {

	$sep = '##';
	$fp = @fopen($src, 'a');

	@fwrite($fp, date('[d-m-y H:i:s]') . $sep);

	foreach($a as $value) {
		@fwrite($fp, $value . $sep);
	}
	
	@fwrite($fp, $_SERVER['REQUEST_URI'] . $sep);
	@fwrite($fp, $_SERVER['QUERY_STRING'] . $sep);

	@fwrite($fp, PHP_EOL);
	@fclose($fp);

	return true;
}

/**
 * getThemes()
 * Return installed themes
 * into an array
 * @param string
 * @return array
 **/
function getThemes($f) {

	$a = array();

	if ($handle = opendir('../templates/'.$f)) {
		$sep='';
		while (false !== ($file = readdir($handle))) {
			if ($file != "." && $file != ".." && $file != ".svn") {
				array_push($a, $file);
			}
		}
		closedir($handle);
	}

	return $a;
}

/*
 * @param $str : html string
 * @return $html html string
 */
function clean_html_body( $str ) {
	
	// See HTML safe tags for emails
	// @url : http://www.pinpointe.com/blog/email-campaign-html-and-css-support
	
	// Create DOM from string using SIMPLEHTMLDOM
	$html = str_get_html($str);
	
	$tags = array('iframe', 'object', 'embed', 'video', 'audio');
	
	foreach($tags as $tag) {
		foreach($html->find($tag) as $element) {
			$images = $element->outertext = _t('mail', 'online_content_only');
		}
	}
	
	return $html;
	
}

/**
 * get_pm_code()
 * @access public
 * @param string $default_login
 * @return string
 */
function get_pm_code( $default_login) {
	
	if (! $GLOBALS['l21auth']->isAuthenticated()) return false;
	
	$output  = '<div style="display:none">';
	$output .= '<div id="sendmessage-content" class="pam">';
	$output .= '<h2>'. sprintf(_t('contact', 'pm_send_to'), $default_login). '</h2>';
	$output .= '<form id="messageform" action="index.php" method="post">';
	$output .= '<label class="labelised" for="subject">'. mb_ucfirst(_t('contact', 'subject')). ' : </label>';
	$output .= '<input name="subject" id="subject" class="shorttextfield" size="30" maxlength="150" type="text" required>';
	$output .= '<br class="mtl" />';
	$output .= '<label class="labelised" for="body">' . mb_ucfirst(_t('contact', 'body')) .' : </label>';
	$output .= '<textarea name="body" id="body" cols="60" rows="10" required></textarea>';
	$output .= '<div class="label-leftmargin mtm">';
	$output .= '<input name="receive_copy" id="receive_copy" value="1" checked type="checkbox" class="checkbox">';
	$output .= '<label class="labelised autowidth" for="receive_copy">' . _t('contact', 'receive_copy') .'</label>';
	$output .= '<input name="show_myemail" id="show_myemail" value="1" checked type="checkbox" class="checkbox">';
	$output .= '<label class="labelised autowidth" for="show_myemail">' . _t('contact', 'show_myemail') .'</label>';
	$output .= '<p class="msg-no-store">'. _t('contact', 'pm_no_store'). '</p>';
	$output .= '</div>';
	$output .= '<div class="submit-container"><input value="'._t('btn', 'valid').'" class="submitbut" type="submit" id="submit-message"></div>';
	$output .= '<input name="recipient" value="'. $default_login.'" type="hidden">';
	$output .= '<input name="sender" value="'. $_SESSION['userlogin'] .'" type="hidden">';
	$output .= '<input name="token" value="'. SECRET_KEY .'" type="hidden">';
	$output .= '</form>';
	$output .= '</div>';
	$output .= '</div>';
	
	$js = '
			// change name and field values dynamically
            var cboxOptions = {
				onOpen:function(){ 
					var user = $(this).attr("data-user-login");
					var h2 = "'. _t('contact', 'pm_send_to'). '";
					$("#sendmessage-content h2").text(h2.replace("%s", user));
					$("#messageform input[name=\'recipient\']").val(user);
				},
				inline:true, innerWidth: "70%", innerHeight: "80%", maxWidth: "80%", maxHeight: "80%", fixed: true 
			};
			$(".send-message").colorbox(cboxOptions);
            $(window).resize(function(){ $.colorbox.resize({ width: window.innerWidth > parseInt(cboxOptions.maxWidth) ? cboxOptions.maxWidth : cboxOptions.width, height: window.innerHeight > parseInt(cboxOptions.maxHeight) ? cboxOptions.maxHeight : cboxOptions.height }); });
			$("#submit-message").click(function() {
			
				$.ajax({
					type: "POST",
					url: "'.SITE_ROOT_URL.'mail/_ajax_send.php",
					data:  $("#messageform").serialize(),
					dataType: "json",
					success: function(data){
						// alert(data.msg);
						$("#sendmessage-content p.msg-email").fadeOut();
						if(data.status==1) {
							$("<p class=\"confirmed msg-email\">" + data.msg + "</p>").insertAfter("#submit-message");
							$("#submit-message, #sendmessage-content input, #sendmessage-content textarea").attr("disabled", "disabled");
						} else {
							$("<p class=\"error msg-email\">" + data.msg + "</p>").insertAfter("#submit-message");
						}
					}
				});
							
				return false;
				});';
	
	footerAddInlineJS($js);
	
	return $output;
}

/**
 * notifyUsersMsg()
 * @access public
 * @param array $a
 * $a['owner'] : item owner
 * $a['user'] : the one who posted the item (can be an adminsitrator or animator)
 * @return boolean
 */
function notifyUsersMsg($a) {
	
		include_once(override('../mail/mail_actions.php'));
		include_once(override('../mail/send.php'));
		
		include_once('../class/class.emailq.php');
		$emailqo = new emailq;

		if($a['action'] == 'workgroup_request') {
			$m = array('O');
		
			$wg = $a['id'];
			$data = $GLOBALS['sql_object']->DBSelect(SQL_getWorkshopDenomination($a['id']));
			if(count($data)==1) $a['wgroups'] = $data[0]['workshop_denomination'];
			$a['comment'] = strip_tags($_POST ['comment']);
			
			// getting user's email 
			$r = $GLOBALS['sql_object']->DBSelect(SQL_get_UserInfo($a['user']));
			if(isset($r[0]['profile_email'])) $a['reply_to'] = $r[0]['profile_email'];
			
			$users = $GLOBALS['sql_object'] -> DBSelect(SQL_getWorkshopUserList($a['id'], array('O')), 'OBJECT');

		}
		
		if($a['action'] == 'vote_threshold') {
		    
		    $wg = $a['parentid'];
		    $data = $GLOBALS['sql_object']->DBSelect(SQL_getWorkshopDenomination($a['parentid']));
		    if(count($data)==1) $a['wname'] = $data[0]['workshop_denomination'];
		    $a['wlinkforum'] = array('rub'=> $GLOBALS['links'][U_L]['topic']['linkvalue'],'id'=> $a['tid'], 'parentid' => $a['parentid'], 'debut' => $a['debut'], 'name' => $a['wname'], 'refer' => 'self', '#' => 'msg-'.$a['id']);
		    $users = $GLOBALS['sql_object'] -> DBSelect(SQL_getWorkshopUsersforNotification($a['parentid'], 'post'), 'OBJECT');
		}
		
    if($a['action'] == 'post_alert') {
      if(ALERT_NEWPOST == 0) return true;
      if(ALERT_NEWPOST == 1) $m = array('O', 'A');
      if(ALERT_NEWPOST == 2) $m = array('O', 'A', 'U');

      $a['topic_body'] = clean_html_body($a['topic_body']);
      
      $wg = $a['parentid'];
      $data = $GLOBALS['sql_object']->DBSelect(SQL_getWorkshopDenomination($a['parentid']));
      if(count($data)==1) $a['wname'] = $data[0]['workshop_denomination'];
      $a['wlinkforum'] = array('rub'=> $GLOBALS['links'][U_L]['topic']['linkvalue'],'id'=> $a['tid'], 'parentid' => $a['parentid'], 'debut' => $a['debut'], 'name' => $a['wname'], 'refer' => 'self', '#' => 'msg-'.$a['id']);
      $users = $GLOBALS['sql_object'] -> DBSelect(SQL_getWorkshopUsersforNotification($a['parentid'], 'post'), 'OBJECT');
    }

    if($a['action'] == 'topic_alert') {
      if(ALERT_NEWTOPIC == 0) return true;
      if(ALERT_NEWTOPIC == 1) $m = array('O', 'A');
      if(ALERT_NEWTOPIC == 2) $m = array('O', 'A', 'U');

      $a['topic_body'] = clean_html_body($a['topic_body']);
      
      $data = $GLOBALS['sql_object']->DBSelect(SQL_getWorkshopDenomination($a['id']));
      if(count($data)==1) $a['wname'] =$data[0]['workshop_denomination'];

      $a['wlinkforum'] = array('rub'=> $GLOBALS['links'][U_L]['topic-list']['linkvalue'],'id'=> $a['id'], 'name' => $a['wname'], 'refer' => 'self');

      $users = $GLOBALS['sql_object'] -> DBSelect(SQL_getWorkshopUsersforNotification($a['id'], 'topic'), 'OBJECT');
    }

    if($a['action'] == 'file_alert') {
      if(ALERT_NEWFILE == 0) return true;
      if(ALERT_NEWFILE == 1) $m = array('O', 'A');
      if(ALERT_NEWFILE == 2) $m = array('O', 'A', 'U');

      $data = $GLOBALS['sql_object']->DBSelect(SQL_getWorkshopDenomination($a['id']));
      if(count($data)==1) $a['wname'] =$data[0]['workshop_denomination'];

      $a['wlinkfiles'] = array('rub'=> $GLOBALS['links'][U_L]['files']['linkvalue'],'id'=> $a['id'], 'name' => $a['wname'], 'refer' => 'self');
      $a['wlinkworkgroups'] = array('rub'=> $GLOBALS['links'][U_L]['workgroup']['linkvalue']);

      if(isset($a['is_shared']) && $a['is_shared'] === true) {
      	// we notify all users
      	$users = $GLOBALS['sql_object']->DBSelect(SQL_getAllWorkshopUsersforNotification('file'), 'OBJECT');
      } else {
      	// we notify only current workgroup
      	$users = $GLOBALS['sql_object']->DBSelect(SQL_getWorkshopUsersforNotification($a['id'], 'file'), 'OBJECT');
      }
    }

    if($a['action'] == 'event_alert') {
    	if(ALERT_NEWEVENT == 0) return true;
    	if(ALERT_NEWEVENT == 1) $m = array('O', 'A');
    	if(ALERT_NEWEVENT == 2) $m = array('O', 'A', 'U');
    
    	$a['task_body'] = clean_html_body($a['task_body']);
    	
    	$data = $GLOBALS['sql_object']->DBSelect(SQL_getWorkshopDenomination($a['id']));
    	if(count($data)==1) $a['wname'] = $data[0]['workshop_denomination'];
    	
    	$a['wlinkcalendar'] = array('rub'=> $GLOBALS['links'][U_L]['calendar']['linkvalue'],'id'=> $a['id'], 'name' => $a['wname'], 'refer' => 'self');
    	$users = $GLOBALS['sql_object']->DBSelect(SQL_getWorkshopUsersforNotification($a['id'], 'file'), 'OBJECT');
    }

    foreach($users as $user) {
      if($user->user_login != $a['user']) {
        if(in_array($user->jwu_user_right, $m)) {
        	
        	$a['recipient'] = $user->profile_email;
        	
          $emailContainer = prepare_email($a);
          
          // if no email queue, we send mail directly
          if(!defined('MAIL_BULK') || MAIL_BULK == 'nodelay') {
          	
          	$r = send_email($emailContainer);
          
          // we prepare the email queue and store it in database
          // emails are sent later using cron
          } else {
          	 
          	$queue = array();
          	$queue = $emailContainer;
          	$queue['module'] 		= 'workgroups';
          	
          	if($a['action'] == 'event_alert') $queue['priority'] = 1;
          	 
          	$r = $emailqo->addToQueue($queue, $GLOBALS['sql_object']);
          	
          }
          
        }
      }
    }
    
    return true;
  }

/**
 * get_referer()
 * @param force_url bool
 * @return string
 */
function get_referer($force_url = false) {
	
	$referer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '';
	
	// referer is safe
	if(Stringy\Stringy::create($referer, CHARSET)->startsWith(SITE_ROOT_URL) || $referer == '') return $referer;
	
	logfile(LOG_MAINFILE, array('[suspicious referer]','current='.$referer , 'force_url='. (int) $force_url, $_SERVER['HTTP_USER_AGENT'], $_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI'], $_SERVER['QUERY_STRING'], $_SERVER['HTTP_COOKIE'], i2c_realip()));

	// provided referer is suspect, we route to default CURRENT_APP_URL
	// if $force_url is set to true
	if ($force_url) return CURRENT_APP_URL;
	else return $referer;
}

// Returns the real IP address of the user
// from https://gist.github.com/ionutvmi/5945163
function i2c_realip()
{
	// No IP found (will be overwritten by for
	// if any IP is found behind a firewall)
	$ip = FALSE;
	
	// If HTTP_CLIENT_IP is set, then give it priority
	if (!empty($_SERVER["HTTP_CLIENT_IP"])) {
		$ip = $_SERVER["HTTP_CLIENT_IP"];
	}
	
	// User is behind a proxy and check that we discard RFC1918 IP addresses
	// if they are behind a proxy then only figure out which IP belongs to the
	// user.  Might not need any more hackin if there is a squid reverse proxy
	// infront of apache.
	if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {

		// Put the IP's into an array which we shall work with shortly.
		$ips = explode (", ", $_SERVER['HTTP_X_FORWARDED_FOR']);
		if ($ip) { array_unshift($ips, $ip); $ip = FALSE; }

		for ($i = 0; $i < count($ips); $i++) {
			// Skip RFC 1918 IP's 10.0.0.0/8, 172.16.0.0/12 and
			// 192.168.0.0/16
			if (!preg_match('/^(?:10|172\.(?:1[6-9]|2\d|3[01])|192\.168)\./', $ips[$i])) {
				if (version_compare(phpversion(), "5.0.0", ">=")) {
					if (ip2long($ips[$i]) != false) {
						$ip = $ips[$i];
						break;
					}
				} else {
					if (ip2long($ips[$i]) != -1) {
						$ip = $ips[$i];
						break;
					}
				}
			}
		}
	}

	// Return with the found IP or the remote address
	return ($ip ? $ip : $_SERVER['REMOTE_ADDR']);
}

/**
 * securityCheck()
 * Test and sanitize user input
 * from request
 * @return boolean
 **/
function securityCheck() {

	$passed = true;

	// we first sanitize vars
	if(isset($_REQUEST['rub']))
		$_REQUEST['rub']= strip_tags($_REQUEST['rub']);
	if(isset($_REQUEST['todo']))
		$_REQUEST['todo']= strip_tags($_REQUEST['todo']);
    if(isset($_REQUEST['pid']))
	    $_REQUEST['pid']= strip_tags($_REQUEST['pid']);
	if(isset($_REQUEST['plugin']))
	    $_REQUEST['plugin']= strip_tags($_REQUEST['plugin']);
	if(isset($_REQUEST['name']))
		$_REQUEST['name']= strip_tags($_REQUEST['name']);
	if(isset($_REQUEST['search']))
		$_REQUEST['search']= strip_tags($_REQUEST['search']);
	if(isset($_REQUEST['newsletteremail']))
		$_REQUEST['newsletteremail']= strip_tags($_REQUEST['newsletteremail']);
	// then do tests
	if(isset($_REQUEST['id']) && preg_match('/[^0-9A-Za-z]/',$_REQUEST['id']))
		$passed= false;
	if(isset($_REQUEST['parentid']) && !is_numeric($_REQUEST['parentid']))
		$passed= false;
	if(isset($_REQUEST['parentparentid']) && !is_numeric($_REQUEST['parentparentid']))
		$passed= false;
	if(isset($_REQUEST['debut']) && !is_numeric($_REQUEST['debut']))
		$passed= false;

	if($passed == false) die('no way!');
	else return true;
}
?>
