<?php
/*
Officity - Web application platform - Version 6.0 - 2011-07-05

François Dispaux, Boris Verdeyen, Thomas Hermant,
Jérémie Roy, Grégory Meurice, Abdelila Harbi, 
Marc Mignonsin, Jonathan Sanchez, Julien Gonzalez, Pierre Fouchez

Sushee and Officity is © Copyright 2011 Nectil SA.

`/sushee-source/sushee/common/module.class.php` is part of Officity.

Sushee is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Officity and Sushee are distributed in the hope that they will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Sushee. If not, see <http://www.gnu.org/licenses/>.
*/
require_once(dirname(__FILE__)."/../common/nectil_object.class.php");
require_once(dirname(__FILE__)."/../common/nectil_user.class.php");
require_once(dirname(__FILE__)."/../common/datas_structure.class.php");
require_once(dirname(__FILE__)."/../common/processor.class.php");
require_once(dirname(__FILE__)."/../common/susheesession.class.php");

define('SUSHEE_POSTPROCESSOR',"postprocessor");
define('SUSHEE_PREPROCESSOR',"preprocessor");
define('NECTIL_SEARCHTEXT',"searchtext");


class modules extends SusheeObject{
    var $list;
    var $loaded;
    var $db_conn;
    var $modulesNames;
	var $index=0;
    
    function modules(){
		
         $this->list = array();
         $modulesNames = $this->modulesNames();
         foreach($modulesNames as $moduleName){
			 $moduleInfo = moduleInfo($moduleName);
			 if ($moduleInfo->loaded)
				$this->list[$moduleName] = $moduleInfo;
         }
    }
    function modulesNames(){
		$request = new Sushee_Request();
		$db_conn = $request->getModuleDbConn();
        $recordSet = $db_conn->Execute('SELECT `Denomination` FROM `modules` ORDER BY `Denomination`');
        $modulesNames = array();
        while( $module = $recordSet->FetchRow() ){
            $modulesNames[] = $module['Denomination'];
        }
        return $modulesNames;
    }

	function reset(){
		reset($this->list);
	}

	function next(){
		$moduleInfo = next($this->list);
		return $moduleInfo;
	}
	
	function getLoginXML(){
		$this->reset();
		$xml='<MODULES>';
		while($moduleInfo = $this->next()){
			$xml.=$moduleInfo->getLoginXML();
		}
		$xml.='</MODULES>';
		return $xml;
	}
}
/*
Cette classe contient les infos d'un module et les infos de securite !!
*/
class moduleInfo extends SusheeObject{
    var $ID;
    var $name;
    var $tableName;
	var $tableMasterName;
	var $privateModule = false;
    var $forbiddenFields; // some fields are never allowed for modification
	var $actions;
    var $tableFields;
    var $_lastError;
    var $loaded;
	var $fieldsBySecurity;
	var $fieldsWithXML;
	var $isMandatory;
	var $services;
	var $depTypes;
	var $composite;
	var $virtualIDs;
	var $virtualKey;
	var $teamIDs;
	var $advancedSecurity = false; // multigroups and multiowners security
	
    //var $tag2DBexceptions;
    //var $db_conn;
    //var $xmlSecurityStr;

	function getName(){
		return $this->name;
	}
	
	function getxSusheeName(){
		return strtoupper($this->getName());
	}
	
	function getTablename(){
		return $this->tableName;
	}
    
    function getLastError(){
        return $this->_lastError;
    }

	function getID(){
		return $this->ID;
	}
	
	function isNative(){
		return ($this->getID() < 1024);
	}
	
	function isExtension(){
		return ($this->extends != '');
	}
	
	function getParentModule(){
		if($this->extends != ''){
			return moduleInfo($this->extends);
		}else{
			return false;
		}
	}
	
	function getTable(){
		require_once(dirname(__FILE__).'/../common/db_manip.class.php');
		$table = new ModuleDatabaseTable($this->tableName);
		$table->setModule($this);
		return $table;
	}
	
	function getNameSpace(){
		require_once(dirname(__FILE__).'/../common/namespace.class.php');
		$explosion = explode(':',$this->name);
		if(sizeof($explosion)>1){
			$ns = new SusheeNamespace($explosion[0]);
			return $ns;
		}else
			return false;
	}
	
	function getXML(){
		require_once(dirname(__FILE__).'/../common/nql.class.php');
		$xml ='<MODULE ID="'.$this->getID().'">';
		$xml.=	'<DENOMINATION>'.$this->getName().'</DENOMINATION>';
		$xml.=	'<FIELDS>';
		$nql = new NQL(false);
		$nql->addCommand(
			'<SEARCH>
				<FIELD module="'.$this->getName().'"/>
				<RETURN>
					<INFO>
						<DENOMINATION/>
						<FIELDNAME/>
						<TYPE/>
						<SQLTYPE/>
						<DEFAULTVALUE/>
						<LISTNAME/>
						<SEARCHABLE/>
						<PRESET/>
					</INFO>
					<DESCRIPTION>
						<TITLE/>
						<SUMMARY/>
					</DESCRIPTION>
				</RETURN>
			</SEARCH>');
		$nql->execute();
		$result = $nql->getElement('/RESPONSE/RESULTS');
		$xml.=$result->toString('./*');
		$xml.=	'</FIELDS>';
		$xml.=	'<DEPENDENCIES>';
		
		$depTypesSet = new DependencyTypeSet($this->getID());
		while($depType = $depTypesSet->next()){
			$moduleTarget = $depType->getModuleTarget();
			$xml.='<DEPENDENCY type="'.$depType->getName().'" module="'.$moduleTarget->getName().'"/>';
		}
		$xml.=	'</DEPENDENCIES>';
		$xml.='</MODULE>';
		return $xml;
	}
	
	function getLoginXML(){
		$fields_array=array('ID','Denomination','IsPrivate','ModuleToID');
		
		$xml ='<MODULE ID="'.$this->getID().'">';
		$xml.=	'<INFO>';
		$xml.=		'<DENOMINATION>'.$this->getName().'</DENOMINATION>';
		$xml.=	'</INFO>';
		$xml.=	$this->getXMLSecurity();
		$xml.='</MODULE>';
		return $xml;
	}
	
	function registerFields(){ // save the definition of the fields in the fields table
		require_once(dirname(__FILE__)."/../common/nectil_element.class.php");
		$fields = $this->getFields();
		$db_conn = db_connect();
		foreach($fields as $field){
			$field->register();
		}
	}
	
	function registerProcessors(){
		$db_conn = db_connect();
		$sql = 'SELECT `ID` FROM  `modules_processors` WHERE `ModuleID`=\''.$this->getID().'\' ';
		$rs = $db_conn->execute($sql);
		if($rs){
			while($row = $rs->fetchRow()){
				$type = new dependencyType('processors');
				$dep = new Dependency($type,$this->getID(),$row['ID']);
				$dep->create();
			}
		}
		
		
	}
	
	function getFields(){
		require_once(dirname(__FILE__).'/../common/db_manip.class.php');
		$fields = array();
		$table = $this->getTable();
		foreach($this->tableFields as $field_def){
			$field = new TableField();
			$field->setTable($table);
			$field->setName($field_def['REALNAME']);
			$field->setModule($this);
			$ado_type = $field_def['TYPE'];
			
			//$field->setAdoType($ado_type);
            $fields[$field->getName()] = $field;
        }
		return $fields;
	}
	
	function getField($name){
		require_once(dirname(__FILE__).'/../common/db_manip.class.php');
		$table = $this->getTable();
		$name = strtoupper($name);
		foreach($this->tableFields as $tag=>$field_def){
			if($field_def['REALNAME']==$name || $tag==$name){
				$field = new TableField();
				$field->setTable($table);
				$field->setName($field_def['REALNAME']);
				$field->setExistence(true); // for optim, cause we are sure the field exists, and it avoids a SQL request to check its existence
				$ado_type = $field_def['TYPE'];
				$field->setAdoType($ado_type);
				
				return $field;
			}
		}
		return false;
	}
	
	// returns the field indicating if an element is part of an extension
	// usually its name is the name of the extension : lig:family, boris:friend
	function getExtensionField(){
		return $this->getField($this->getName());
	}
    
    function moduleInfo($moduleName/* can be the ID too */,$userID=0){
		
        $specific_db_conn = db_connect(FALSE);
        // taking the userID in the session if not given in argument
        if ($userID==0 && isset($_SESSION[$GLOBALS['nectil_url']]['SESSIONuserID']))
           $userID = $_SESSION[$GLOBALS['nectil_url']]['SESSIONuserID'];
       
		$request = new Sushee_Request();
		$db_conn = $request->getModuleDbConn();
        
        
        // getting the tableName,moduleName and moduleID
		if ( !is_numeric($moduleName) ){
			$moduleName = strtolower($moduleName);
			$sql = "SELECT * from `modules` WHERE `Denomination`=\"$moduleName\";";
		}else{
			$sql = "SELECT * from `modules` WHERE `ID`=\"$moduleName\";";
		}
		
        if (!$row = $db_conn->GetRow($sql))
		{
            $this->_lastError = "No module $moduleName in the modules table.";
            $this->loaded = FALSE;
            return FALSE;    
        }
		else
		{
			$this->ID = $row["ID"];
		   	$this->name = $row["Denomination"];
			$this->extends = $row['Extends'];
			if($row['AdvancedSecurity']==1)
			{
				$this->advancedSecurity = true;
			}
	
			if ($row['PrivateModule'] == 1)
			{
				$this->privateModule = true;
			}

		   	$this->tableMasterName = $row['TableName'];
			$this->tableName = $this->tableMasterName;
			
			if ($this->privateModule == true && $userID != 0)
			{

				$this->tableName = $this->tableMasterName.'_'.$_SESSION[$GLOBALS['nectil_url']]['SESSIONuserID'];

				// check existence of the private table
				$sql = 'SELECT `ID` FROM `'.$this->tableName.'` LIMIT 1;';
				$res = $db_conn->Execute($sql);

				if (!$res)
				{
					// table doesn't exist
					$sql = 'CREATE TABLE `'.$this->tableName.'` LIKE `'.$this->tableMasterName.'`;';
					$res = $db_conn->Execute($sql);
					
					if (!$sql)
					{
						// error creating the private table
						throw new SusheeException('Unable to initialize private module table in: '.$this->name);
					}

					// add default elements ID = 0
					$sql = 'INSERT `'.$this->tableName.'` SELECT * FROM `'.$this->tableMasterName.'` WHERE `ID` = 1;';
					$res = $db_conn->Execute($sql);
				}
			}
		}		

		if ($this->name=='contact' || $this->name=='modulekey' || $this->name=='keyring' || $this->name=='applicationkey' || $this->name=='template' || $this->name == 'field')
			$this->isMandatory = true;
		else
			$this->isMandatory = false;
		
		
		// allows to know whether the security is composed of several  virtual moduleKey
		$this->composite = false;
		$this->virtualIDs = array();
		//////////////////////////////////////////////////
        // getting the fields informations
        //////////////////////////////////////////////////
        // some fields are never allowed in modification
        $this->forbiddenFields = array('IsLocked'=>'R','CreationDate'=>'R','ModificationDate'=>'R','SearchText'=>'0');//'ID'=>'R','Activity'=>'R'
		
		if ($this->name=="mailing")
			$this->forbiddenFields['DataToSendXML']='0';
		if($this->name=="mail"){
			$this->forbiddenFields['RichText']='0';
			$this->forbiddenFields['MessageSource']='0';
			$this->forbiddenFields['UniqueID']='0';
		}
		
		
		$this->fieldsWithXML=array();
		if($this->name=='modulekey'){
			$this->fieldsWithXML=array('Fields'=>true,'Services'=>true,'Dependencies'=>true);
		}else if ($this->name=='applicationkey'){
			$this->fieldsWithXML=array('Permissions'=>true);
		}else if ($this->name=='resident'){
			$this->fieldsWithXML=array('Profile'=>true);
		}else if ($this->name=='mailing'){
			$this->fieldsWithXML=array('RecipientsSearch'=>true,'RecipientsXML'=>true,'MediasXML'=>true,'ExcludeRecipients'=>true);
		}else if ($this->name=='contact'){
			$this->fieldsWithXML=array('Prefs'=>true);
		}else if ($this->name=='mail'){
			$this->fieldsWithXML=array('StyledText'=>true);
		}else if ($this->name=='mailsaccount'){
			$this->fieldsWithXML=array('Config'=>true,'Signature'=>true);
		}else if ($this->name=='document'){
			$this->fieldsWithXML=array('BodyText'=>true);
		}else if($this->name=='batch'){
			$this->fieldsWithXML=array('Command'=>true,'Response'=>true);
		}else if($this->name=='cron'){
			$this->fieldsWithXML=array('Command'=>true);
		}else if($this->name=='cronlog'){
			$this->fieldsWithXML=array('Response'=>true);
		}
        // determining virtualKeys for each module
		if($this->name=='contact')
			$this->virtualKey ='ContactType';
		else if($this->name=='media')
			$this->virtualKey ='MediaType';
		else if($this->name=='group')
			$this->virtualKey ='IsTeam';
		else if($this->name=='mailing')
			$this->virtualKey ='Status';
		else if($this->name=='sound_art')
			$this->virtualKey ='Type';
		else if($this->name=='mail')
			$this->virtualKey ='Type';
			
        $field_array = $specific_db_conn->MetaColumns($this->tableName);
        //Returns an array of ADOFieldObject's, one field object for every column of $table. A field object is a class instance with (name, type, max_length)
		
        //////////////////////////////////////////////////
        // getting the permissions in the modulekeys
        //////////////////////////////////////////////////
		
		$count_sql = "SELECT COUNT(DISTINCT `ID`) AS ct FROM `modulekeys` WHERE `ModuleToID`=".$this->ID.";";
		$count_row = $specific_db_conn->GetRow($count_sql);
		$this->IsPrivacySensitive = false;
		
		// initializing security arrays
        $this->tableFields = array();
		$this->actions = array();
		$this->privacies = array();
		$service_sql = "SELECT `Denomination` FROM `services`;";
		$service_rs = $db_conn->Execute($service_sql);
		$this->services = array();
		if ($service_rs){
			while($service_row = $service_rs->FetchRow()){
				$this->services[$service_row["Denomination"]]=array();
				$this->services[$service_row["Denomination"]]["SECURITY"][0]="W";
			}
		}
		// ---------
		// DEPTYPES
		// ---------
		$sql = "SELECT `ID`,`Denomination` FROM `dependencytypes` WHERE `ModuleOriginID`='".$this->ID."';";
		$deptypes_rs = $specific_db_conn->Execute($sql);
		$this->depTypes = getDependencyTypesArray($deptypes_rs);
		
		// ---------
		// FIELDS
		// ---------
		$index = 0;
		if($field_array){
            foreach($field_array as $field){
				 $tag = strtoupper($field->name);
				 $type = $specific_db_conn->MetaType($field->type);
				 $security_array = array();
				 $security_array[$index] = 'W';
				 $this->tableFields[$tag] = array('SECURITY'=>$security_array,'REALNAME'=>$field->name,'TYPE'=>$type);
				
				 // if the name of the field ends with XML, assuming its an XML Field (handling of these fields is different)
				 if(substr($field->name,-3)=='XML'){
					$this->fieldsWithXML[$field->name]=true;
				}
            }
        }
		
		if (!$GLOBALS["dev_request"]===TRUE && !$count_row['ct']=='0'){
			if($userID===NULL){
				$xmlSecurityStr='<SECURITY/>';
				$this->buildSecurity($index,$xmlSecurityStr);
			}else{
				
				$sql = "SELECT perm.* FROM `modulekeys` AS perm LEFT JOIN `dependencies` AS dep ON (dep.`TargetID`=perm.`ID`) LEFT JOIN `dependencies` AS dep2 ON (dep2.`OriginID`=dep.`OriginID`) WHERE dep2.`TargetID`=$userID AND dep2.`DependencyTypeID`=3 AND dep.`DependencyTypeID` = 4 AND perm.`ModuleToID`=".$this->ID.";";
				// only checking if a userID was precised (user already loggued) otherwise it has no sense
				$rs = $specific_db_conn->Execute($sql);
				$nb_modulekeys = 0;
				
				while($row = $rs->FetchRow()){
					// this way, we can't have a partial key on modulekey or application key (it would be a security hole)
					if ($this->name=='modulekey' || $this->name=='applicationkey')
						$row['Fields']='<ID>W</ID><ACTIVITY>W</ACTIVITY>';
					$xmlSecurityStr='<SECURITY IsVirtual="'.(($row['IsVirtual']==1)?'true':'false').'" VirtualID="'.$row['VirtualID'].'"><FIELDS>'.$row['Fields'].'</FIELDS><SERVICES>'.$row['Services'].'</SERVICES><DEPENDENCIES>'.$row['Dependencies'].'</DEPENDENCIES></SECURITY>';
					if($row['IsVirtual']==0){
						//$this->xmlSecurityStr=$xmlSecurityStr.$this->xmlSecurityStr;
						
						$this->buildSecurity($index,$xmlSecurityStr,$row['IsPrivate']);
					}else{
						
						//$this->xmlSecurityStr=$xmlSecurityStr.$this->xmlSecurityStr;
						$this->buildSecurity($row['VirtualID'],$xmlSecurityStr,$row['IsPrivate']);
						$this->composite = true;
					}
					$nb_modulekeys++;
				}
				
				if($nb_modulekeys==0){
					$xmlSecurityStr='<SECURITY/>';
					$this->buildSecurity($index,$xmlSecurityStr);
				}
			}
		}else{
			$services_array = getServicesArray();
			$services = "";
			foreach($services_array as $service){
				$n = $service['Denomination'];
				$services.='<'.$n.'>'.$service['SECURITY'].'</'.$n.'>';
			}
			$xmlSecurityStr='<SECURITY><FIELDS><ID>W</ID><ACTIVITY>W</ACTIVITY></FIELDS><SERVICES>'.$services.'</SERVICES><DEPENDENCIES/></SECURITY>';
			$this->buildSecurity($index,$xmlSecurityStr);
        }
		if( ($this->name=='mail' || $this->name=='mailsaccount' || $this->name=='event' || $this->name=='ticket') && !$GLOBALS["dev_request"]===TRUE){
			$this->IsPrivacySensitive = true;
			if(!isset($this->privacies[0]))
				$this->privacies[0] = true;
		}
		if($this->name=='group' && $userID){
			$this->IsPrivacySensitive = true;
			if(!isset($this->privacies[0]))
				$this->privacies[0] = true;
			$this->teamIDs = array();
			$group_sql = 'SELECT dep.`OriginID` FROM `dependencies` AS dep LEFT JOIN `groups` AS gp ON gp.`ID`=dep.`OriginID` WHERE gp.`IsTeam`=1 AND dep.`TargetID`='.$userID.' AND dep.`DependencyTypeID`=1;';
			sql_log($group_sql);
			$rs = $specific_db_conn->Execute($group_sql);
			while($row = $rs->FetchRow()){
				$this->teamIDs[]=$row['OriginID'];
			}
		}
		
        $this->loaded = TRUE;
    }

	function exists(){
		return $this->loaded;
	}
	
	function buildSecurity($index,$xmlSecurityStr,$privacy=''){
		$xmlSecurity = new XML($xmlSecurityStr);
        
// privacy is the content of the IsPrivate field. 0 means we have to deal about privacy. D or nothing means we do not care
		if($privacy==='0' && isset($_SESSION[$GLOBALS["nectil_url"]]['SESSIONuserID'])){
			$this->IsPrivacySensitive = true;
		}
		$this->privacies[$index]=true;
		
		// first parsing the xml to have all field security and use it if necessary
		$xml_fields_array = $xmlSecurity->match('/SECURITY/FIELDS[1]/*');
		$xml_fields_security = array();
		foreach($xml_fields_array as $xml_security_path){
			$xml_fields_security[$xmlSecurity->nodeName($xml_security_path)]=$xmlSecurity->getData($xml_security_path);
		}
		
        foreach($this->tableFields as $tag=>$config){
			$fieldName = $config['REALNAME'];
			 if (!isset($this->forbiddenFields[$fieldName])){
				 $security = $xml_fields_security[$tag];
				 if ($security!='R' && $security!='W' && $security!='0')
					$security = 'W';
			 }else{
				 $security = $this->forbiddenFields[$fieldName];
			 }
			 $this->tableFields[$tag]['SECURITY'][$index] = $security;
		}
		// determine which actions are authorized for this user
		
		
		$this->actions[$index] = array('CREATE'=>false,'UPDATE'=>false,'DELETE'=>false,'SEARCH'=>false,'GET'=>false);// defaults
		if ($xmlSecurity->getData('/SECURITY[1]/FIELDS[1]/ID[1]')=='W'){
			$this->actions[$index]['CREATE']=true;
			$this->actions[$index]['UPDATE']=true;
			
			if($index!==0 ){
				if($this->actions[0]['CREATE']!==true )
					$this->actions[0]['CREATE'] = true;
				if($this->actions[0]['UPDATE']!==true )
					$this->actions[0]['UPDATE'] = true;
			}
		}
		if ($xmlSecurity->getData('/SECURITY[1]/FIELDS[1]/ACTIVITY[1]')=='W'){
			$this->actions[$index]['DELETE']=true;
			
			if($index!==0 ){
				if($this->actions[0]['DELETE']!==true )
					$this->actions[0]['DELETE'] = true;
			}
		}
		if ($xmlSecurity->getData('/SECURITY[1]/FIELDS[1]/ID[1]')=='R' || $xmlSecurity->getData('/SECURITY[1]/FIELDS[1]/ID[1]')=='W'){
			$this->actions[$index]['SEARCH']=true;
			if($index!==0){
				//$this->virtualIDs[]=$index;
				array_push($this->virtualIDs,$index);
				
			}
			
			$this->actions[$index]['GET']=true;
			
			if($index!==0 ){
				if($this->actions[0]['GET']!==true )
					$this->actions[0]['GET'] = true;
				if($this->actions[0]['SEARCH']!==true )
					$this->actions[0]['SEARCH'] = true;
			}
		}
		//debug_log("sizeof  virtualIDs".sizeof($this->virtualIDs));
		// getting the security for the services : by default everything is authorized
		
		$xml_services_array = $xmlSecurity->match('/SECURITY[1]/SERVICES[1]/*');
		foreach($xml_services_array as $service_security_path){
			$nodeName =  $xmlSecurity->nodeName($service_security_path);
			if (isset($this->services[$nodeName])){
				$ok = true;
				// if on generic backoffice must check the service availability (freelink and dependencies are mandatory)
				if ($nodeName!='dependencies' && $nodeName!='freelink' && !isNectilMaster($GLOBALS["nectil_url"]) && $GLOBALS['generic_backoffice']==TRUE && is_object($GLOBALS['resident_profile']) && $GLOBALS['resident_profile']->loaded ){
					if (!$GLOBALS['resident_profile']->match('/PROFILE/SERVICES/'.$nodeName)){
						$this->services[$nodeName]['SECURITY'][$index]="0";
						$ok = false;
					}
				}
				if ($ok){
					$this->services[$nodeName]['SECURITY'][$index]=$xmlSecurity->getData($service_security_path);
					if($index!==0 ){
						if($this->getServiceSecurityLevel($nodeName,$this->services[$nodeName]['SECURITY'][$index]) > $this->getServiceSecurityLevel($nodeName,$this->services[$nodeName]['SECURITY'][0]))
							$this->services[$nodeName]['SECURITY'][0] = $this->services[$nodeName]['SECURITY'][$index];
					}
					if($nodeName=='description'){
						
						$serviceSecurity = $this->services[$nodeName]['SECURITY'][$index];
						$serviceSecurityNum = $this->getServiceSecurityLevel('description',$serviceSecurity);
						$translator =  $xmlSecurity->getData($service_security_path.'/@translator');
						if ( $this->getServiceSecurityLevel('description','publisher') > $serviceSecurityNum && $serviceSecurity!=='0' ){
							//$this->workflow_forward[$index]=array();
							
							$wf_sql = "SELECT ct.`Email1`,perm.`Services` FROM `modulekeys` AS perm LEFT JOIN `dependencies` AS dep ON (dep.`TargetID`=perm.`ID`) LEFT JOIN `dependencies` AS dep2 ON (dep2.`OriginID`=dep.`OriginID`) LEFT JOIN `contacts` AS ct ON (ct.`ID`=dep2.`TargetID`) WHERE dep2.`DependencyTypeID`=3 AND dep.`DependencyTypeID` = 4 AND perm.`ModuleToID`=".$this->ID." ".(($index!==0)?"AND perm.`VirtualID`=\"$index\"":"AND perm.`VirtualID`=\"\"")." AND perm.`Services` NOT LIKE \"%<description>0</description>%\"";
							//debug_log($wf_sql);
							$db_conn = db_connect();
							$wf_rs = $db_conn->Execute($wf_sql);
							$publishers = array();
							$revisers = array();
							if($wf_rs){
								while($wf_forward = $wf_rs->FetchRow()){
									if(strpos($wf_forward['Services'],'>W</description>')!==false || strpos($wf_forward['Services'],'>publisher</description>')!==false){
										$publishers[]=$wf_forward['Email1'];
										//debug_log($wf_forward['Email1']);
									}else if(strpos($wf_forward['Services'],'>reviser</description>')!==false){
										$revisers[]=$wf_forward['Email1'];
										//debug_log($wf_forward['Email1']);
									}
								}
							}
							// if we didn't find any revisers not publishers, we try without imposing a virtualID
							if(sizeof($revisers)==0 && sizeof($publishers)==0){
								$wf_sql = "SELECT ct.`Email1`,perm.`Services` FROM `modulekeys` AS perm LEFT JOIN `dependencies` AS dep ON (dep.`TargetID`=perm.`ID`) LEFT JOIN `dependencies` AS dep2 ON (dep2.`OriginID`=dep.`OriginID`) LEFT JOIN `contacts` AS ct ON (ct.`ID`=dep2.`TargetID`) WHERE dep2.`DependencyTypeID`=3 AND dep.`DependencyTypeID` = 4 AND perm.`ModuleToID`=".$this->ID." AND (perm.`VirtualID`=\"\" OR `IsVirtual`=0) AND perm.`Services` NOT LIKE \"%<description>0</description>%\"";
								$wf_rs = $db_conn->Execute($wf_sql);
								//debug_log($wf_sql);
								if($wf_rs){
									while($wf_forward = $wf_rs->FetchRow()){
										if(strpos($wf_forward['Services'],'<description>W</description>')!==false || strpos($wf_forward['Services'],'<description>publisher</description>')!==false){
											$publishers[]=$wf_forward['Email1'];
										}else if(strpos($wf_forward['Services'],'<description>reviser</description>')!==false){
											$revisers[]=$wf_forward['Email1'];
										}
									}
								}
							}
							if($serviceSecurity=='writer'){
								
								if(sizeof($revisers)==0)
									$revisers = $publishers;
								$this->workflow_forward[$index]=$revisers;
								
							}else if($serviceSecurity=='reviser'){
								$this->workflow_forward[$index]=$publishers;
							}
						}
						if($translator==='true' && isset($_SESSION[$GLOBALS['nectil_url']]['SESSIONuserID'])){
							$db_conn = db_connect();
							$desc_lg_sql = 'SELECT `LanguageID` FROM `contacts` WHERE `ID`=\''.$_SESSION[$GLOBALS['nectil_url']]['SESSIONuserID'].'\'';
							$visitor_row = $db_conn->GetRow($desc_lg_sql);
							$this->descriptionLanguage[$index] = $visitor_row['LanguageID'];
						}
					}
				}
			}
		}
		// getting the security for the dependencyTypes
		
		$xml_depTypes_array = $xmlSecurity->match('/SECURITY/DEPENDENCIES[1]/*');
		foreach($xml_depTypes_array as $depType_security_path){
			$nodeName =  $xmlSecurity->nodeName($depType_security_path);
			
			if (isset($this->depTypes[$nodeName])){
				$this->depTypes[$nodeName]['SECURITY'][$index]=$xmlSecurity->getData($depType_security_path);
				if($this->getServiceSecurityLevel($nodeName,$this->depTypes[$nodeName]['SECURITY'][$index]) > $this->getServiceSecurityLevel($nodeName,$this->depTypes[$nodeName]['SECURITY'][0]))
					$this->depTypes[$nodeName]['SECURITY'][0] = $this->depTypes[$nodeName]['SECURITY'][$index];
			}
		}
	}
	//------------------------------------------------------
	function checkDescriptionLanguage($language,$element=false){
		$index = $this->determineVirtualID($element);
		if(isset($this->descriptionLanguage[$index])){
			return ($this->descriptionLanguage[$index]==$language);
		}else
			return $this->getServiceSecurity('description',$element);
	}
	
	//------------------------------------------------------
	function isElementAuthorized($element,$requiredPermission='R'){
		$user = new NectilUser();
		$request = new Sushee_Request();
		if(!$user->isAuthentified() && !$request->isSecured()){ // considering all access are allowed
			return true;
		}
		if(!$user->isAuthentified() && $request->isSecured()){
			return false;
		}
		$index = 0; // index of the virtual security (virtual security are used for having different security on medias depending of the mediatype)
		
		$userID = $user->getID();
		// if the element is the contact of the connected user, returning true, always
		if($this->getName()=='contact' && $element['ID']==$userID){
			return true;
		}
		
		if($this->composite){
			// composite means we have different keys for a same module (ex: for different mediatypes). At the moment, only Media allows that
			$index = $element[$this->virtualKey];
			if(!in_array($index,$this->virtualIDs))
				$index = 0;
		}
		
		if(!isset($this->privacies[$index])){
			return false;
		}else{
			if($this->IsPrivacySensitive == true /*$this->privacies[$index]==true*/){
				// ---------------
				// Simple security
				// ---------------
				if($requiredPermission=='W' && !$this->actions[$index]['UPDATE']){ // if user asks for write permission and module is read-only returning false
					// read-only access is signaled by setting permission on ID to R
					return false;
				}
				
				// the user is the owner of the element
				if($element['OwnerID']==$userID)
					return true;
				// the user is member of one the team that have access to the element
				if($user->isInTeam($element['GroupID'])){
					return true;
				}
				// ---------------
				// Advanced security
				// ---------------
				$advancedSecurity = $this->isAdvancedSecurityEnabled();
				if($advancedSecurity){
					// in advanced security all these fields have to be empty for the element to be public
					if($element['OwnerID']==0 && $element['GroupID']==0 && !$element['Owners'] && !$element['Groups'])
						return true;
					
					// clean the owners and groups as saved in the database  : we have after that 1857_R,2_W,334_R
					$cleanGroups = str_replace('g_','',$element['Groups']);
					$cleanOwners = str_replace('c_','',$element['Owners']);
					
					// making a vector with the Ids of each team and each owner authorized
					$groups = explode(',',$cleanGroups);
					$owners = explode(',',$cleanOwners);
					
					// CHECKING OWNERS
					// user is one of the owner and has write permissions (so of course read permissions too)
					if( in_array($userID.'_W',$owners) ){ 
						return true;
					}
					// use has read permission and that's the security level required
					if( in_array($userID.'_R',$owners) && $requiredPermission=='R' ){
						return true;
					}
					
					// CHECKING GROUPS
					// user is in one of the team owning the element
					foreach($groups as $groupSecurity){
						list($groupID,$groupPermission) = explode('_',$groupSecurity);
						if($user->isInTeam($groupID)){
							// group has write permission
							if($groupPermission=='W'){
								return true;
							}
							// group has read permission and that's the security level required
							if($groupPermission=='R' && $requiredPermission=='R'){
								return true;
							}
						}
					}
					
					
				}else{
					// element is public (simple security)
					if($element['OwnerID']==0 && $element['GroupID']==0)
						return true;
				}
				
				
				return false;
			}else{
				return true;
			}
		}
		
	}
	
	
	
	//------------------------------------------------------
	function getServiceSecurityLevel($name,$value){
		switch($name){
			case 'description':
				switch($value){
					case 'W': return 100;
					case 'publisher': return 100;
					case 'reviser': return 75;
					case 'writer': return 50;
					case 'R': return 25;
					default: return 0;
				}
				break;
			default:
				switch($value){
					case 'W': return 100;
					case 'R': return 50;
					default: return 0;
				}
		}
	}
	function getWorkflowForwards($element=false){
		//return array('boris@nectil.com');
		$index = $this->determineVirtualID($element);
		if (is_array($this->workflow_forward[$index]))
	   		return $this->workflow_forward[$index];
		else
			return $this->workflow_forward[0];
	}
	//------------------------------------------------------
	function getSQLSecurity($element_name,$requestName='SEARCH'){
		// Security can be managed through 4 fields : OWNERID,GROUPID,OWNERS,GROUPS
		// OWNERID: one owner
		// OWNERS : multi owners (both can be used in parallel)
		
		// GROUPID: one owner group
		// GROUPS : multi owner groups (both can be used in parallel)
		
		// -------------------------------------------
		// general variables
		// -------------------------------------------
		$userID = $_SESSION[$GLOBALS["nectil_url"]]['SESSIONuserID'];
		$isConnected = isset($_SESSION[$GLOBALS["nectil_url"]]['SESSIONuserID']);
		
		$where_string = '';
		if($this->name=='mail' && $requestName!='GET' && $isConnected){
			return " AND ($element_name.`OwnerID`='".$userID."')";
		}
		// the security is not applicable on module templates
		$template = " OR $element_name.`ID`='1'";
		if($this->name=='contact' && $isConnected){
			$own_contact = " OR $element_name.`ID`='".$userID."'";
		}
		// SQL indicating that the element has no owner and no group
		$no_owner_no_group = "$element_name.`OwnerID`=0 AND $element_name.`GroupID`=0";
		
		// SQL indicating groupID is one of the team of the connected user
		if($this->IsPrivacySensitive && $isConnected){
			$groupModuleInfo = moduleInfo('group');
			if(sizeof($groupModuleInfo->teamIDs)>0){
				$group_cond = ' OR ('.$element_name.'.`GroupID` IN ('.implode(",",$groupModuleInfo->teamIDs).'))';
			}
		}
		// -------------------------------------------
		// advanced security : fields OWNER AND GROUPS
		// -------------------------------------------
		if($this->isAdvancedSecurityEnabled()){
			// owners and groups are written like this in the database : c_{ID}_{R/W},c_{ID}_{R/W}
			$groupModuleInfo = moduleInfo('group');
			$groups_cond='';
			if(sizeof($groupModuleInfo->teamIDs)>0){
				$groups_cond = ' g_'.implode('_* g_',$groupModuleInfo->teamIDs).'_*';
			}
			// if advanced security is enabled, owners and groups have precedence over the OwnerID and GroupID 
			// it means a OWNERID=0 and GROUPID wont give automatic access to the element, Owners and Groups must also be empty (these variables are used lower)
			$owners_groups_isnull = 
				$element_name.'.`Owners` = "" AND '.
				$element_name.'.`Groups` = "" ';
			$advancedSecurity_isnull = ' AND '.$owners_groups_isnull;
			
			$advancedSecurity = " OR MATCH ($element_name.`Owners`,$element_name.`Groups`) AGAINST (' c_".$userID."_*".$groups_cond."' IN BOOLEAN MODE) OR ($owners_groups_isnull AND $no_owner_no_group)";
			
		}else{
			$advancedSecurity = '';
		}
		
		// -------------------------------------------
		// privacy applies and NO composite (composite is for having different security for different mediatypes)
		// -------------------------------------------
		if($this->IsPrivacySensitive && $isConnected && !$this->composite){
			$where_string.=" AND ($element_name.`OwnerID`='".$userID."' OR ($no_owner_no_group $advancedSecurity_isnull)$group_cond $own_contact $template $advancedSecurity) ";
		}
		// only restrictive virtualKey applies
		if($this->composite && sizeof($this->virtualIDs)>0 && !$this->IsPrivacySensitive){
			$element_names = array();
			foreach($this->virtualIDs as $key){
				$element_names[]="'".$key."'";
			}
			$where_string.="/* Security - not private - virtual */ AND (".$element_name.".".$this->virtualKey.' IN ('.implode(',',$element_names).")$own_contact$template$advancedSecurity) ";
		}
		// -------------------------------------------
		// privacy applies and composite too (composite is for having different security for different mediatypes)
		// -------------------------------------------
		if($this->IsPrivacySensitive && $isConnected && $this->composite && sizeof($this->virtualIDs)>0){
			// privacy and virtualKey apply
			$privacy_cond = " AND ($element_name.`OwnerID`='".$userID."' OR ($no_owner_no_group $advancedSecurity_isnull) $group_cond) ";
			$element_names = array();
			foreach($this->virtualIDs as $key){
				$cond=$element_name.".".$this->virtualKey."='".$key."'";
				if($this->privacies[$key])
				    $cond.=$privacy_cond;
				$element_names[]='('.$cond.')';
				
			}
			$where_string.="/* Security - private - virtual */ AND (".implode(" OR ",$element_names)." $own_contact $template) ";
		}
		return $where_string;
	}
    //------------------------------------------------------
    function getXMLSecurity(){
		// -------
		// RETURNS A XML STRING REPRESENTING THE SECURITY FOR THE USER CONNECTED FOR THE MODULE
		// -------
		
        //return $this->xmlSecurityStr;
		$xml = '';
		$xml.='<SECURITY>';
		// -------
		// FIELDS
		// -------
		$fields = $this->getFields();
		foreach($fields as $field){
			$fieldname = $field->getNQLName();
			$fieldsStr.='<'.$fieldname.'>'.$this->getFieldSecurity($fieldname).'</'.$fieldname.'>';
		}
		$xml.='<FIELDS>'.$fieldsStr.'</FIELDS>';
		// -------
		// SERVICES
		// -------
		$services = "";
		foreach($this->services as $serviceName=>$security){
			$services.='<'.$serviceName.'>'.$this->getServiceSecurity($serviceName).'</'.$serviceName.'>';
		}
		$xml.='<SERVICES>'.$services.'</SERVICES>';
		// -------
		// DEPENDENCY TYPES
		// -------
		$depStr = '';
		$deps = new DependencyTypeSet($this->getID());
		while($depType = $deps->next()){
			$depTypeName = $depType->getName();
			if($depTypeName){
				$depStr.='<'.$depTypeName.'>';
				$depStr.=$this->getDepTypeSecurity($depTypeName);
				$depStr.='</'.$depTypeName.'>';
			}
		}
		$xml.='<DEPENDENCIES>'.$depStr.'</DEPENDENCIES>';
		$xml.='</SECURITY>';
		return $xml;
    }
	function isXMLField($fieldName){
		return isset($this->fieldsWithXML[$fieldName]);
	}
	
	function determineVirtualID($element){
		if(!is_array($element) || $this->composite==false)
			return 0;
		else if(isset($element[$this->virtualKey]))
			return $element[$this->virtualKey];
		else
			return 0;
	}
    //------------------------------------------------------
    function getFieldsBySecurity($minSecurity,$element=false){
       /* O < R < W
       0 -> get all fields
       R -> all readable fields
       w -> all writable fields
       */
	   $index = $this->determineVirtualID($element);
	   // if we already asked for this minSecurity, we have kept it and return it directly
	   if (isset($this->fieldsBySecurity[$index][$minSecurity]))
	   		return $this->fieldsBySecurity[$index][$minSecurity];
       $ret_array = array();
	   if (!is_array($this->tableFields)){
		   debug_log("What the heck is going on ??? tableFields array couldn't be determined.".$this->name." ".($this->loaded==false));
	   	   return $ret_array;
	   }
       if($minSecurity == '0'){
          foreach($this->tableFields as $field){
             array_push($ret_array,$field['REALNAME']);
          }
       }else if($minSecurity == 'R'){
          foreach($this->tableFields as $field){
             if($field['SECURITY'][$index]=='R' ||$field['SECURITY'][$index]=='W')
               array_push($ret_array,$field["REALNAME"]);
          }
       }else if($minSecurity == 'W'){
          foreach($this->tableFields as $field){
             if($field['SECURITY'][$index]=='W')
               array_push($ret_array,$field['REALNAME']);
          }
       }
	   $this->fieldsBySecurity[$index][$minSecurity]=$ret_array;
       return $ret_array;
    }
    //------------------------------------------------------
    function getFieldSecurity($fieldName,$element=false){
       $fieldName=strtoupper($fieldName);
	   $index = $this->determineVirtualID($element);
      if(isset($this->tableFields[$fieldName]['SECURITY'][$index]))
         return $this->tableFields[$fieldName]['SECURITY'][$index];
      else
         return "0";
    }
    //------------------------------------------------------
    function getFieldName($fieldName){
       $fieldName=strtoupper($fieldName);
       if(isset($this->tableFields[$fieldName]['REALNAME']))
         return $this->tableFields[$fieldName]['REALNAME'];
       else
         return false;
    }
	function existField($fieldName){
       $fieldName=strtoupper($fieldName);
       if(isset($this->tableFields[$fieldName]['REALNAME']))
         return true;
       else
         return false;
    }
    //------------------------------------------------------
    function getFieldType($fieldName){
      $fieldName=strtoupper($fieldName);
      if(isset($this->tableFields[$fieldName]['TYPE']))
         return $this->tableFields[$fieldName]['TYPE'];
      else
         return false;
    }
	//------------------------------------------------------
	function getActionSecurity($actionName,$element=false){
		$index = $this->determineVirtualID($element);
		if(isset($this->actions[$index][$actionName]))
			return $this->actions[$index][$actionName];
		else
			return false;
	}
	//------------------------------------------------------
	function getServiceSecurity($serviceName,$element=false){
		// variants for the services names
		if($serviceName=='dependency'){
			$serviceName='dependencies';
		}
		if($serviceName=='comments'){
			$serviceName='comment';
		}
		if($serviceName=='categories'){
			$serviceName='category';
		}
		if($serviceName=='descriptions'){
			$serviceName='description';
		}
		
		
		$index = $this->determineVirtualID($element);
		if(isset($this->services[$serviceName]['SECURITY'][$index]))
			return $this->services[$serviceName]['SECURITY'][$index];
		else
			return "0";
	}
	//------------------------------------------------------
	function getDepTypeSecurity($depTypeName,$element=false){
		$index = $this->determineVirtualID($element);
		if(isset($this->depTypes[$depTypeName]['SECURITY'][$index]))
			return $this->depTypes[$depTypeName]['SECURITY'][$index];
		else
			return "0";
	}
	function generateSearchText(&$new_values,$elementID=false){
		switch($this->name){
				case "mail":
				$SearchTxt = '';
				if ($new_values['Type']=='in')
				{
					$SearchTxt .= $new_values['From'].' ';
					$SearchTxt .= $new_values['Subject'].' '.$new_values['PlainText'];
				}
				else
				{
					$SearchTxt .= $new_values['To'].' ';
					$SearchTxt .= $new_values['Subject'].' '.Sushee_getRichText($new_values['StyledText']);
				}

				if ($new_values['Attachments'])
				{
					$attachments_array = explode(',',$new_values['Attachments']);
					$SearchTxt .= ' '.implode(' ',$attachments_array);
				}
			
				break;
			case "contact":
				//$new_values = array_merge($former_values,$values);
				/* Generate FullTxt Search champ */
				$SearchTxt="";
				if($new_values['ContactType']=='PP')
				$SearchTxt.=$new_values['FirstName'].' '.$new_values['LastName'].' '.$new_values['Denomination'];
				else
				$SearchTxt.=$new_values['Denomination'].' '.$new_values['FirstName'].' '.$new_values['LastName'];
				$SearchTxt.=' '.$new_values['ClientCode'];
				$SearchTxt.=' '.$new_values['Address'].' '.$new_values['PostalCode'].' '.$new_values['City'].' '.$new_values['StateOrProvince'];
				$SearchTxt.=' '.$new_values['CountryID'].' '.$new_values['Purpose'];
				$SearchTxt.=' '.$new_values['Phone1'].' '.$new_values['MobilePhone'].' '.$new_values['Fax'];
				$SearchTxt.=' '.$new_values['LanguageID'];
				$SearchTxt.=' '.$new_values['Email1'];
				$SearchTxt.=' '.$new_values['Phone2'].' '.$new_values['Notes'];
				
				
				break;
			case "sound_art":
				//$new_values = array_merge($former_values,$values);
				/* Generate FullTxt Search champ */
				$SearchTxt="";
				$SearchTxt.=$new_values['Title'].' '.$new_values['Artist'].' '.$new_values['Place'];
				$SearchTxt.=' '.$new_values['Date'].' '.$new_values['Notes'].' '.$new_values['Context'].' '.$new_values['ContextNote'];
				if($new_values['Type']=='album')
					$SearchTxt.=" AlbumID".$new_values['AlbumID'];
				
				break;
			case "todo":
				$SearchTxt="";
				$SearchTxt.=$new_values['Task'];
				
				break;
			case "document":
				$SearchTxt="";
				$SearchTxt.=$new_values['DocType'];
				$SearchTxt.=' '.$new_values['SenderRef'];
				$SearchTxt.=' '.$new_values['RecipientRef'];
				$SearchTxt.=' '.$new_values['Subject'];
				$SearchTxt.=' '.$new_values['BodyText'];
				
				break;
			case "media":
				$SearchTxt="";
				$SearchTxt.=$new_values['Denomination'];
				$SearchTxt.=' '.$new_values['MediaType'];
				
				break;
			case "event":
				$SearchTxt="";
				$SearchTxt.=$new_values['Title'];
				if($new_values['Comment'])
					$SearchTxt.=' '.$new_values['Comment'];
				
				break;
			case 'batch':
				$SearchTxt='';
				$fields = array('Denomination','Domain','Notes','Response','Command','Status','Callback');
				$first = true;
				foreach($fields as $fieldname){
					if(!$first){
						$SearchTxt.=' ';
					}
					$SearchTxt.=$new_values[$fieldname];
					$first = false;
				}
				break;
			case 'cron':
				$SearchTxt='';
				$fields = array('Denomination','Domain','Notes','Command','Status','Callback');
				$first = true;
				foreach($fields as $fieldname){
					if(!$first){
						$SearchTxt.=' ';
					}
					$SearchTxt.=$new_values[$fieldname];
					$first = false;
				}
				break;
			case 'cronlog':
				$SearchTxt='';
				$fields = array('Status','Response');
				$first = true;
				foreach($fields as $fieldname){
					if(!$first){
						$SearchTxt.=' ';
					}
					$SearchTxt.=$new_values[$fieldname];
					$first = false;
				}
				break;
			case 'ticket':
				$SearchTxt='';
				$fields = array('Description','Url','Title');
				$first = true;
				foreach($fields as $fieldname){
					if(!$first){
						$SearchTxt.=' ';
					}
					$SearchTxt.=$new_values[$fieldname];
					$first = false;
				}
				break;
		}
		if(!$this->searchtextProcessor){ // if already initialized, using it
			$this->searchtextProcessor = &new ModuleProcessingQueue();
			$this->searchtextProcessor->setModule($this);
			$this->searchtextProcessor->setType(NECTIL_SEARCHTEXT);
		}
		
		$process_data = new ModuleProcessingData();
		$process_data->setElementID($elementID);
		$process_data->setNewValues($new_values);
		$this->searchtextProcessor->setData($process_data);
		$this->searchtextProcessor->execute();
		// getting back the value returned by the series of processors
		$SearchTxt.=$this->searchtextProcessor->getResponse();
		
		//default searchtext with all text fields if nothing is configured
		if(!$SearchTxt){
			$fields = $this->getFields();
			foreach($fields as $field){
				$fieldName = $field->getName();
				$value = $new_values[$fieldName];
				if(!isset($this->forbiddenFields[$field->getName()]) && $field->getType()=='text' && $fieldName != 'Owners' && $fieldName!='Groups'){ // Owners and Groups should not be written in the searchtext
					$SearchTxt.=$value.' ';
				}
			}
		}
		
		//return strtolower(removeAccents(decode_from_XML($SearchTxt)));
		return Sushee_getSearchText($SearchTxt);
	}

	function getPreProcessors($command)
	{
		
		$process = &new ModuleProcessingQueue();
		$process->setModule($this);
		$process->setCommand($command);
		$process->setType(SUSHEE_PREPROCESSOR);
		
		return $process;
	}
	
	function getPostProcessors($command)
	{
	
		$process = &new ModuleProcessingQueue();
		$process->setModule($this);
		$process->setCommand($command);
		$process->setType(SUSHEE_POSTPROCESSOR);
		
		return $process;
	}
	
	function preProcess($command,$elementID,&$node,&$former_values,&$new_values,&$return_values,$exclude = false){
		
		// getting the queue of preprocessors for this command
		$process = $this->getPreProcessors($command);
		
		$process_data = &new ModuleProcessingData();
		$process_data->setModule($this);
		$process_data->setElementID($elementID);
		$process_data->setNode($node);
		$process_data->setFormerValues($former_values);
		$process_data->setNewValues($new_values);
		$process_data->setNoticeableValues($return_values);
		
		$process->setData($process_data);
		
		$process->execute();
		
		$process_data = $process->getData();
		
		$new_values = $process_data->getNewValues();
		$former_values = $process_data->getFormerValues();
		$return_values = $process_data->getNoticeableValues();
		
		return $process;
		
	}
	
	function postProcess($command,$elementID,&$node,&$former_values,&$new_values,&$return_values){
		// getting the queue of postprocessors for this command
		$process = $this->getPostProcessors($command);
		
		$process_data = &new ModuleProcessingData();
		$process_data->setModule($this);
		$process_data->setElementID($elementID);
		$process_data->setNode($node);
		$process_data->setFormerValues($former_values);
		$process_data->setNewValues($new_values);
		$process_data->setNoticeableValues($return_values);
		
		$process->setData($process_data);
		
		$res = $process->execute();
		
		$process_data = $process->getData();
		
		$new_values = $process_data->getNewValues();
		$former_values = $process_data->getFormerValues();
		$return_values = $process_data->getNoticeableValues();
		
		return $process;
	}
	
	function isAdvancedSecurityEnabled(){
		return $this->advancedSecurity;
	}
	
	function enableAdvancedSecurity($boolean=true){
		$this->advancedSecurity = $boolean;
	}
	
	function clearInSession(){
		Sushee_Session::clearVariable('public'.$this->getName());
		Sushee_Session::clearVariable('public'.$this->getID());
		Sushee_Session::clearVariable('private'.$this->getName());
		Sushee_Session::clearVariable('private'.$this->getID());
	}
	
	
	// returns the direct extension, extending this module really
	function getDirectExtensions(){
		$vector = new Vector();
		$sql = 'SELECT `ID` FROM `modules` WHERE `Extends` = "'.$this->getName().'";';
		$db_conn = db_connect();
		$rs = $db_conn->execute($sql);
		while($row = $rs->fetchRow()){
			$vector->add($row['ID'],moduleInfo($row['ID']));
		}
		return $vector;
	}
	
	// returns the extensions, descending all the inheriting objects
	function getExtensions(){
		$to_include = new Vector();
		$to_handle[] = $this;
		while($handled = array_pop($to_handle)){
			$extensions = $handled->getDirectExtensions();
			while($extension = $extensions->next()){
				$to_handle[] = $extension;
				$to_include->add($extension->getID(),moduleInfo($extension->getID())); // we recall moduleInfo, because we need a reference to the object, and not the object itself
			}
		}
		return $to_include;
	}
	
	// returns all parent modules, until the top
	function getParents(){
		$parents = new Vector();
		$module = $this;
		while($module = $module->getParentModule()){
			$parents->add($module->getID(),moduleInfo($module->getID())); // we recall moduleInfo, because we need a reference to the object, and not the object itself
		}
		return $parents;
	}
	
	function getNextID(){
		if($this->privateModule){
			$db_conn = db_connect();
			// private module -> get the unique ID
			$sql = 'UPDATE `'.$this->tableMasterName.'_auto_increment` SET `ID`=`ID`+1 WHERE 1 LIMIT 1;';
			$db_conn->Execute($sql);
			$sql = 'SELECT `ID` FROM `'.$this->tableMasterName.'_auto_increment` WHERE 1 LIMIT 1;';
			$row = $db_conn->getRow($sql);
			return $row['ID'];
		}
		return false;
	}
	
}



?>