<?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/searchoutputmanager.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/searchprofile.class.php");
require_once(dirname(__FILE__)."/../common/console.class.php");
require_once(dirname(__FILE__)."/../common/comments.inc.php");
require_once(dirname(__FILE__)."/../common/categories.inc.php");
require_once(dirname(__FILE__)."/../common/properties.inc.php");
require_once(dirname(__FILE__)."/../common/dependencies.inc.php");
require_once(dirname(__FILE__)."/../common/infoxml.class.php");
require_once(dirname(__FILE__)."/../common/nectil_user.class.php");
require_once(dirname(__FILE__)."/../common/omnilinks.class.php");

class NQLSearchOutputManager extends SusheeObject{
	var $profile_config;
	var $search_profile;
	var $moduleID;
	var $depth;
	var $main_rs;
	var $sysconsole;
	var $files_collect=false;
	var $used_files;
	
	var $operationNode = false; // at the moment only used in search.inc.php, and only useful for postprocessing
	
	function NQLSearchOutputManager(){
		$this->profile_config = array('profile_name'=>'mini','profile_xml'=>false,'profile_path'=>false);
		$this->depth = false;
		$this->sysconsole = new LogConsole();
	}
	
	function setOperationnode($node){
		$this->operationNode = $node;
	}
	
	function getOperationnode(){
		return $this->operationNode;
	}
	
	function setFilesCollect($bool){
		$this->files_collect = $bool;
		if($bool && !is_array($this->used_files))
			$this->used_files = array();
	}
	
	function getFiles(){
		return $this->used_files;
	}
	
	function setResultSet(/* SQL ResultSet */$rs){
		$this->main_rs = $rs;
	}
	function setProfileConfig(/* array */$profile_config){
		$this->profile_config = $profile_config;
	}
	
	function resolveProfile(){
		$this->profile_config['depth']=$this->depth;
		
		$resolver = new NQLSearchProfileResolver($this->profile_config);
		$this->search_profile = $resolver->execute();
	}
	
	function setDepth($depth){
		$this->depth = $depth;
	}
	
	function setModule($moduleID){
		$this->moduleID = $moduleID;
	}
	
	function getXML(){
		if (is_string($this->main_rs) || !$this->main_rs )
			return '';
		if (is_object($this->main_rs) && $this->main_rs->RecordCount()==0)
			return '';
		
		// transform profile_config in a usable form
		$this->resolveProfile();
		
		//-------------------------------------------
		// Preparation of the structure
		//-------------------------------------------
		// the goal of the redesign is one depth == one sql query == one profile handling
		// contains the elements at each depth
		//$elements_for_depth = array();
		// all the elements in the tree by module
		$elements_by_module = array();
		// elements that we will register to have their descriptions
		$elements_wdesc_by_module = array();
		// elements that we will register to have their categories
		$elements_wcateg_by_module = array();
		// elements that we will register to have their comments
		$elements_wcomments_by_module = array();
		// elements that we will register to have their properties
		$elements_wproperties_by_module = array();
		

		$depTypes_by_module = array();
		$profile_at_depth = $this->search_profile->profile_at_depth;
		$depth = $this->search_profile->depth;
		$main_rs = $this->main_rs;
		$moduleInfo = moduleInfo($this->moduleID);
		//-------------------------------------------
		// Handling the first depth
		//-------------------------------------------
		if (is_array($main_rs))
			$elem = $main_rs;
		else
			$elem = $main_rs->FetchRow();
		$moduleName = strtoupper($moduleInfo->name);
		$index=0;

		while($elem){
			$profile = $profile_at_depth[0];
			$this_depth = 0;
			$attributes='';
			$ID = $elem['ID'];
			$elemIDs[$moduleInfo->ID][]=$ID;
			$completeID = $moduleInfo->name.'_'.$ID;
			$elements_for_depth[0][$completeID]['ID']=$ID;
			$elt_obj = &$elements_for_depth[$this_depth][$completeID];
			if(is_array($IDs_list)){
				$IDs_list[]=$ID;
			}
			//$attributes.=$add_info;
			if ($elem['DependencyTypeID']!=0){
				$dependencyType = depType($elem['DependencyTypeID']);
				if ($dependencyType->loaded)
					$attributes.= ' linkType="'.$dependencyType->name.'"';
			}
			
			$elt_obj['module']=$moduleName;
			$elt_obj['moduleInfo']=&$moduleInfo;
			$elt_obj['moduleID']=$moduleInfo->ID;
			$elt_obj['depth']=0;
			
			// saving the security on the element to display it in the XML
			if(Sushee_Request::isSecured()){
				// private by default
				$security = '0';
				// writable element
				if($moduleInfo->isElementAuthorized($elem,'W')){
					$security = 'W';
				// readonly element
				}else if($moduleInfo->isElementAuthorized($elem,'R')){
					$security = 'R';
				}
				$elt_obj['security']=$security;
			}
			
			// saving the position of the element in the whole resultset (if we paginate the resultset, might be interesting to know the global position of the element)
			if ($main_rs->result_page){
				$this_index = $main_rs->startIndex+$index+1;
				$attributes.=" onTotalCount='$this_index'";
			}
			if($moduleInfo->name=='mail' && $elem['html']==1)
				$attributes.=" html='true'";
			$elt_obj['attributes']=$attributes;
			
			if($profile['get_info']){
				$elt_obj['info_xml'] = $this->generateInfoXML($moduleInfo,$elem,$profile);
			}
			$elt_obj['instances']++;
			
			if($depth==='all'){
				// to check we are not in a cycle
				$paths = array();
				$paths[0][]=$completeID;
				$elt_obj['paths']=$paths;
			}
			$elements_by_module[$moduleInfo->ID][$ID]=&$elt_obj;
			if ($profile['get_descriptions'] && $moduleInfo->getServiceSecurity('description',$elem)!=='0')
				$elements_wdesc_by_module[$moduleInfo->ID][$ID]=&$elt_obj;
			if ($profile['get_categories'] && $moduleInfo->getServiceSecurity('category',$elem)!=='0')
				$elements_wcateg_by_module[$moduleInfo->ID][$ID]=&$elt_obj;
			if ($profile['get_comments'] && $moduleInfo->getServiceSecurity('comment',$elem)!=='0')
				$elements_wcomments_by_module[$moduleInfo->ID][$ID]=&$elt_obj;
			if ($profile['get_properties'] && $moduleInfo->getServiceSecurity('property',$elem)!=='0')
				$elements_wproperties_by_module[$moduleInfo->ID][$ID]=&$elt_obj;
			if ($profile['get_freelinks'] && $moduleInfo->getServiceSecurity('freelink',$elem)!=='0')
				$elt_obj['freelinks_xml']=$this->handleFreelinks($moduleInfo,$ID,$profile['freelinks_config']);
			if ($profile['get_omnilinks'] && $moduleInfo->getServiceSecurity('omnilink',$elem)!=='0')
				$elt_obj['omnilinks_xml']=$this->handleOmnilinks($moduleInfo,$ID,$profile);
			$process = $moduleInfo->postProcess('SEARCH',$ID,$this->getOperationnode(),$elem,$elem,$return_values /* fake array because, return values is not used */);
			
			$elt_obj['postprocess'] = $process->getResponse();
			
			$GLOBALS["TotalNectilElements"]++;
			if (is_array($main_rs))
				$elem = FALSE;
			else
				$elem = $main_rs->FetchRow();
			$index++;
		}

		//-------------------------------------------
		// Handling the depth after (depth>0)
		//-------------------------------------------
		$modules_at_precDepth[$moduleInfo->ID]=$moduleInfo;

		for($this_depth=1;$this_depth<$depth || $depth==='all';$this_depth++){
			$dependencies_infos = array();
			if(sizeof($elemIDs)>0){
				foreach($modules_at_precDepth as $prec_moduleInfo){
					if($depth==='all'){
						if($profile_at_depth['all']['depTypes_by_module'][$prec_moduleInfo->ID]!==false){
							$depTypes_by_module[$prec_moduleInfo->ID] = $profile_at_depth['all']['depTypes_by_module'][$prec_moduleInfo->ID];
						}
					}else{
						if($profile_at_depth[$this_depth-1]['depTypes_by_module'][$prec_moduleInfo->ID]!==false){
							$depTypes_by_module[$prec_moduleInfo->ID] = $profile_at_depth[$this_depth-1]['depTypes_by_module'][$prec_moduleInfo->ID];
						}
					}
					if(!isset($depTypes_by_module[$prec_moduleInfo->ID])){
						$depType_rs = getDependencyTypesFrom($prec_moduleInfo->ID);// careful : must be more controlled
						while($row = $depType_rs->FetchRow()){
							$dependencyType = depType($row['ID']);
							if ($dependencyType->loaded){
								$depTypes_by_module[$prec_moduleInfo->ID][$row['ID']]=array('depType'=>$dependencyType,'profile'=>false);
							}
						}
					}
					if (is_array($depTypes_by_module[$prec_moduleInfo->ID])){
						foreach($depTypes_by_module[$prec_moduleInfo->ID] as $dependencyType_and_profile){
							$dependencyType = $dependencyType_and_profile['depType'];
							// replaced 
							// $elemIDs[$dependencyType->ModuleOriginID] 
							// by 
							//$elemIDs[$prec_moduleInfo->ID]
							// to allow native module to list the deoendencies of their extension
							$dep_rs = getDepTargets($elemIDs[$prec_moduleInfo->ID],$dependencyType->ID);
							if(is_object($dep_rs) && $dep_rs->RecordCount()>0){
								$this->logFunction('adding a type '.$dependencyType->getName().' '.sizeof($dependencyType_and_profile));
							// keeping the ModuleOriginID because it can be different from the deptype originID, as the native module can list its extension's dependencies
								$dependencies_infos[]=array('depType'=>$dependencyType,'rs'=>$dep_rs,'profile'=>$dependencyType_and_profile['profile'],'ModuleOriginID'=>$prec_moduleInfo->ID);
							}
								
						}
					}
				}
			}else
				break;
			// initializing every element of preceding depth with its dependencyTypes array (empty)
			foreach($elements_for_depth[$this_depth-1] as $completeID=>$element_prec_obj/* not used */){
				$real_element_obj = &$elements_for_depth[$this_depth-1][$completeID];
				if(!is_array($real_element_obj['dependencies'])){
					$real_element_obj['dependencies']= array();
				}
				if (is_array($depTypes_by_module[$real_element_obj['moduleID']])){
					foreach($depTypes_by_module[$real_element_obj['moduleID']] as $dependencyType_and_profile){
						$dependencyType = $dependencyType_and_profile['depType'];
						if(!is_array($real_element_obj['dependencies'][$dependencyType->ID])){
							$real_element_obj['dependencies'][$dependencyType->ID]=array();
						}
					}
				}
			}
			$elemIDs = array();
			$modules_at_precDepth = array();

			


			foreach($dependencies_infos as $dep_info){
				$dep_rs = $dep_info['rs'];
				$dependencyType = $dep_info['depType'];
				if (isset($profile_at_depth[$this_depth])){
					$profile = $profile_at_depth[$this_depth];
				}
				if($depth==='all')
					$profile = $profile_at_depth['all'];
				if($dep_info['profile']){
					$profile = $dep_info['profile'];
					$this->logFunction('taking dedicated profile ');
				}
					
					
				$depID = $dependencyType->ID;
				// using the saved module from above, because it can be the native module instead of the extension
				$moduleOriginInfo = moduleInfo($dep_info['ModuleOriginID']);
				$moduleTargetInfo = moduleInfo($dependencyType->ModuleTargetID);
				
				$modules_at_precDepth[$moduleTargetInfo->ID]=$moduleTargetInfo;

				$moduleName = strtoupper($moduleTargetInfo->name);
				if($moduleTargetInfo->loaded){
					
					while ($elem = $dep_rs->FetchRow()) {
						$ID = $elem['ID'];
						$completeID = $moduleTargetInfo->name.'_'.$ID;
						if (!isset($elements_by_module[$moduleTargetInfo->ID][$ID])){
							$elements_for_depth[$this_depth][$completeID] = array();
							$element_obj = &$elements_for_depth[$this_depth][$completeID];
							$element_obj['ID']=$ID;
							$attributes.=$add_info;
							$element_obj['module']=$moduleName;
							$element_obj['moduleInfo']=&$moduleTargetInfo;
							$element_obj['moduleID']=$moduleTargetInfo->ID;
							$element_obj['depth']=$this_depth;
							
							// saving the security on the element to display it in the XML
							if(Sushee_Request::isSecured()){
								// private by default
								$security = '0';
								// writable element
								if($moduleTargetInfo->isElementAuthorized($elem,'W')){
									$security = 'W';
								// readonly element
								}else if($moduleTargetInfo->isElementAuthorized($elem,'R')){
									$security = 'R';
								}
								$element_obj['security']=$security;
							}
							
								
						}else{
							$elements_for_depth[$this_depth][$completeID] = &$elements_by_module[$moduleTargetInfo->ID][$ID];
							$element_obj = &$elements_by_module[$moduleTargetInfo->ID][$ID];
						}
						if($profile['get_info'] && !isset($element_obj['info_xml'])){
							$element_obj['info_xml'] = $this->generateInfoXML($moduleTargetInfo,$elem,$profile);
						}
						$element_obj['instances']++;

						$more='<DEPINFO>'.$elem['DepInfo'].'</DEPINFO>';
						$more.='<COMMENT>'.$elem['DepComment'].'</COMMENT>'; // DepComment is not the real name of the field, but it was aliased like that in the SQL request in order to avoid confusion with any Comment field in the element
						$origin_completeID = $moduleOriginInfo->name.'_'.$elem[$dependencyType->getOriginFieldname()];
						// check we are not in private mode
						$privacy = $element_obj['IsPrivate'] = $this->getPrivacy($moduleTargetInfo,$elem);
						$cycle=false;
						if($depth==='all'){
							// to check we are not in a cycle
							$paths = $elements_for_depth[$this_depth-1][$origin_completeID]['paths'];
							for($h=0;$h<sizeof($paths);$h++){
								if ( in_array($completeID,$paths[$h]) ){
									$cycle=true;
								}else
									$paths[$h][]=$completeID;
							}
							if (is_array($element_obj['paths']))
								$paths = array_merge($paths,$element_obj['paths']);
							$element_obj['paths']=$paths;
						}
						$parent_obj = &$elements_for_depth[$this_depth-1][$origin_completeID];
						$parent_obj['dependencies'][$depID][$completeID]=array('type'=>'dep','cycle'=>$cycle,'element'=>&$element_obj,'more'=>$more);
					
						
						$elements_by_module[$moduleTargetInfo->ID][$ID]=&$element_obj;
						// Services
						if($privacy==false){
							if ($profile['get_descriptions'] && $moduleTargetInfo->getServiceSecurity('description',$elem)!=='0')
								$elements_wdesc_by_module[$moduleTargetInfo->ID][$ID]=&$element_obj;
							if ($profile['get_categories'] && $moduleTargetInfo->getServiceSecurity('category',$elem)!=='0')
								$elements_wcateg_by_module[$moduleTargetInfo->ID][$ID]=&$element_obj;
							if ($profile['get_comments'] && $moduleTargetInfo->getServiceSecurity('comment',$elem)!=='0')
								$elements_wcomments_by_module[$moduleTargetInfo->ID][$ID]=&$element_obj;
							if ($profile['get_properties'] && $moduleTargetInfo->getServiceSecurity('property',$elem)!=='0')
								$elements_wproperties_by_module[$moduleTargetInfo->ID][$ID]=&$element_obj;
							if ($profile['get_freelinks'] && $moduleTargetInfo->getServiceSecurity('freelink',$elem)!=='0')
								$element_obj['freelinks_xml']=$this->handleFreelinks($moduleInfo,$ID,$profile['freelinks_config']);
							if ($profile['get_omnilinks'] && $moduleTargetInfo->getServiceSecurity('omnilink',$elem)!=='0')
								$element_obj['omnilinks_xml']=$this->handleOmnilinks($moduleInfo,$ID,$profile);
						}
						$element_obj['parents'][$origin_completeID]=&$parent_obj;
						if($cycle===false)
							$elemIDs[$moduleTargetInfo->ID][]=$ID;
						else
							$this->logError('cycle at depth '.$this_depth.' for element of ID '.$ID.' with parent '.$origin_completeID);
						$GLOBALS['TotalNectilElements']++;
					}
				}else
					$this->logError("error loading deptype ".$moduleTargetInfo->getLastError()." ".$dependencyType->getLastError());
			}
		}

		// now foreach modules we get the services of all the elements
		$descriptionProfileManager = new descriptionProfileManager();
		foreach($profile_at_depth as $key=>$prf){
			$descriptionProfileManager->setProfile($key,$prf['desc_profile']);
		}
		foreach($elements_wdesc_by_module as $moduleID=>$elements){
			$descManager = new descriptionsOutputManager($moduleID,$elements);
			$descManager->setProfileManager($descriptionProfileManager);
			$descriptionSetVector = &$descManager->getDescriptionSets();
			$descriptionSetVector->reset();
			while($descSet = &$descriptionSetVector->next()){
				$xml = $descSet->getXML();
				$elements_wdesc_by_module[$moduleID][$descSet->elementID]['descriptions_xml']=$xml;
			}
			if($this->files_collect===true){
				$this->used_files=array_merge($this->used_files,$descManager->getFiles());
			}
				
		}

		foreach($elements_wcateg_by_module as $moduleID=>$elements){
			$moduleInfo = moduleInfo($moduleID);
			$rs = getCategories($moduleID,$elements);
			if (is_object($rs)){
				while($service = $rs->FetchRow()){
					$serviceElemID = $service['TargetID'];
					$elements_wcateg_by_module[$moduleID][$serviceElemID]['categories_xml'].=generateCategoriesXML($service);
				}
			}
		}
		foreach($elements_wcomments_by_module as $moduleID=>$elements){
			$moduleInfo = moduleInfo($moduleID);
			$rs = getComments($moduleInfo->ID,$elements);
			if (is_object($rs)){
				while($service = $rs->FetchRow()){
					$serviceElemID = $service['TargetID'];
					$elements_wcomments_by_module[$moduleID][$serviceElemID]['comments_xml'].=generateCommentsXML($service,$desc_output);
				}
			}
		}
		foreach($elements_wproperties_by_module as $moduleID=>$elements){
			$moduleInfo = moduleInfo($moduleID);
			$rs = getProperties($moduleInfo->ID,$elements);
			if (is_object($rs)){
				while($service = $rs->FetchRow()){
					$serviceElemID = $service['TargetID'];
					$elements_wproperties_by_module[$moduleID][$serviceElemID]['properties_xml'].=generatePropertyXML($service);
				}
			}
		}

		$query_result = '';
		foreach($elements_for_depth[0] as $element_start){
			$query_result.=$this->generateElementXML($depth,$profile_at_depth,0,$element_start,$isGet);
		}

		return $query_result;
	}
	
	
	function getPrivacy($moduleInfo,$elem){
		// check we are not in private mode
		$privacy = false;
		if($moduleInfo->IsPrivacySensitive){
			if($moduleInfo->isElementAuthorized($elem)){
				$privacy = false;
			}else
				$privacy = true;
		}
		// check we are not limited to a subset of this module (virtual module)
		if($moduleInfo->composite===true){
			if($moduleInfo->getActionSecurity('SEARCH',$elem)===false){
				$privacy = true;
			}
		}
		// if it's the user's own contact, we can show it
		if( $moduleInfo->name=='contact' && $elem['ID']==$_SESSION[$GLOBALS["nectil_url"]]['SESSIONuserID']){
			$privacy = false;
		}
		return $privacy;
	}
	
	function generateInfoXML($moduleInfo,$elem,$profile){
		$fields_array=$moduleInfo->getFieldsBySecurity('R');
		$this_profile = $profile['profileFields_by_module'][$moduleInfo->ID];
		if (is_object($this_profile)){
			$profile_array = $this_profile->getIncludedFields();
		}else{
			$profile_array = false;
		}
		// his own contact fiche
		if($moduleInfo->name=='contact' && $elem['ID']===$_SESSION[$GLOBALS["nectil_url"]]['SESSIONuserID']){
			$normal_fields_array = $fields_array;
			$personal_fields_array = $moduleInfo->getFieldsBySecurity('0');

			if(!isset($normal_profile_array['ADMINNOTES'])){
				unset($fields_array['ADMINNOTES']);
			}
			if($profile['mode']=='read-only'){
				return $this->generateInfoXMLReadOnly($moduleInfo,$elem,$personal_fields_array,$profile_array,$profile['desc_output']);
			}else{
				$infoGenerator = new NectilElementInfo($moduleInfo->getID(),$elem['ID'],$elem);
				if(is_object($this_profile)){
					$infoGenerator->setProfile($this_profile);
				}
				$infoGenerator->setSecurityProfile($personal_fields_array);
				return $infoGenerator->getXML();
			}	
		}else{
			if($profile['mode']=='read-only'){
				return $this->generateInfoXMLReadOnly($moduleInfo,$elem,$fields_array,$profile_array,$profile['desc_output']);
			}else{
				$infoGenerator = new NectilElementInfo($moduleInfo->getID(),$elem['ID'],$elem);
				if(is_object($this_profile)){
					$infoGenerator->setProfile($this_profile);
				}
				$infoGenerator->setSecurityProfile($fields_array);
				return $infoGenerator->getXML();
			}
		}
	}
	
	function generateElementXML($depth,&$profile_at_depth,$this_depth,&$element_obj,$isGet=false){
		if($element_obj['type']==='dep'){
			$dep_obj=&$element_obj;
			$element_obj=&$dep_obj['element'];
		}
		//debug_log('generateElementXML '.$this_depth.' '.$element_obj['ID']);
		$element_str='';
		$discard = false;
		if ($dep_obj['cycle']===true)
			$more_attributes='cycle="true" ';
		//instances="'.$element_obj['instances'].'"
		//
		if($element_obj['IsPrivate']===true)
			$more_attributes.='private="true" ';

		$element_str.='<'.$element_obj['module'].' depth="'.($this_depth+1).'" '.$more_attributes.'ID="'.$element_obj['ID'].'"'.$element_obj['attributes'];
		
		if(Sushee_Request::isSecured()){
			$element_str.=' security="'.$element_obj['security'].'"';
		}
		$element_str.='>';
		if ($dep_obj['cycle']!==true and $element_obj['IsPrivate']!==true){
			$profile = $profile_at_depth[$this_depth];
			if($depth==='all')
				$profile = $profile_at_depth['all'];
			$element_str.=$element_obj['info_xml'];

			if($profile['get_descriptions'] ){
				if($profile['desc_discarding']==='absent' && $element_obj['descriptions_xml']==''){
					return '';
				}
				$element_str.='<DESCRIPTIONS>';
				$element_str.=$element_obj['descriptions_xml'];
				$element_str.='</DESCRIPTIONS>';
			}
			if($profile['get_categories'] ){
				$element_str.='<CATEGORIES>';
				$element_str.=$element_obj['categories_xml'];
				$element_str.='</CATEGORIES>';
			}
			if($profile['get_comments'] ){
				$element_str.='<COMMENTS>';
				$element_str.=$element_obj['comments_xml'];
				$element_str.='</COMMENTS>';
			}
			if($profile['get_properties'] ){
				$element_str.='<PROPERTIES>';
				$element_str.=$element_obj['properties_xml'];
				$element_str.='</PROPERTIES>';
			}
			
			if($this_depth<($depth-1) || $depth==='all'){
				$element_str.='<DEPENDENCIES>';
				if(is_array($element_obj['dependencies'])){
					foreach($element_obj['dependencies'] as $depID=>$deps){
						$dependencyType = depType($depID);
						$moduleTargetInfo = moduleInfo($dependencyType->ModuleTargetID);
						
						$element_str.='<DEPENDENCY type="'.$dependencyType->name.'" module="'.$moduleTargetInfo->name.'"  hits="'.sizeof($deps).'">';
						//debug_log('before deps '.$depID.' foreach '.sizeof($deps));
						foreach($deps as $dep){
							$element_str.=$this->generateElementXML($depth,$profile_at_depth,$this_depth+1,$dep);
						}
						$element_str.='</DEPENDENCY>';
					}
				}
				$element_str.='</DEPENDENCIES>';
			}
			if($profile['get_freelinks'])
				$element_str.=$element_obj['freelinks_xml'];
				
			if($profile['get_omnilinks'])
				$element_str.=$element_obj['omnilinks_xml'];
				
			$element_str.=$dep_obj['more'];
			
			if($element_obj['postprocess']){
				$element_str.=$element_obj['postprocess'];
			}
			
		}
		$element_str.='</'.$element_obj['module'].'>';

		return $element_str;
	}
	function generateInfoXMLReadOnly(&$moduleInfo,&$elem,&$fields_array,$profile_array){
		$str='<INFO ';
		if ($profile_array===FALSE || $profile_array===NULL){
			$fields_nbr = count($fields_array);

			for($i=0;$i<$fields_nbr;$i++){
				$n=$fields_array[$i];
				$value=$elem[$n];
				$value = str_replace(array("\r\n", "\r", "\n"), '', $value);
				if (!$moduleInfo->isXMLField($fields_array[$i])){
					$str.=strtoupper($n).'="'.encode_to_XML($value).'" ';
				}
			}
		}else{
			$fields_nbr = count($fields_array);
			$profile_nbr = count($profile_array);
			$fields_ok = 0;
			for($i=0;$i<$fields_nbr;$i++){
				$n=$fields_array[$i];
				$value=$elem[$n];
				$value = str_replace(array("\r\n", "\r", "\n"), '', $value);
				//$n=strtoupper($n);
				$asked = FALSE;
				if (isset($profile_array[strtoupper($n)]))
					$asked = TRUE;
				if ($asked){
					if (!$moduleInfo->isXMLField($fields_array[$i])){
						$str.=strtoupper($n).'="'.encode_to_XML($value).'" ';
					}
				}
			}
		}
		$str.='/>';
		return $str;
	}
	
	function handleFreelinks(&$moduleInfo,$elemID,$freelinks_config=array()){
		$moduleTarget = $freelinks_config['module'];
		$modules_array = array();
		if ($moduleTarget){
			$modules_array[]=$moduleTarget;
		}else{
			$modules = new modules();
			$modules_array = $modules->list;
		}
		$links_str='';

		foreach($modules_array as $moduleTargetInfo){
			$depTypes = new DependencyTypeSet($moduleTargetInfo->ID,$moduleInfo->ID);
			while($depType = $depTypes->next()){
				$rs = $this->getLinkTargets($moduleInfo,$elemID,$moduleTargetInfo,$depType->getID());
				if($rs){
					if(!$freelinks_config['profile'])
						$freelinks_config['profile']='mini';
					$freelinks_depth = false;
					if(is_object($freelinks_config['profile_xml']))
						$freelinks_depth = $freelink_profile_xml->getData($freelink_profile_path."/@depth");
					if($freelinks_depth===false)
						$freelinks_depth = 1;
					$manager = new NQLSearchOutputManager();
					$manager->setModule($moduleTargetInfo->ID);
					$manager->setResultSet($rs);

					$manager->setProfileConfig($freelinks_config['profile']);
					$manager->setDepth($freelinks_depth);

					$links_str.=$manager->getXML();
				}
			}
			
		}
		 return "<FREELINKS>$links_str</FREELINKS>";
	}
	
	function getLinkTargets($moduleOriginInfo,$originID,$moduleTargetInfo,$dependencyTypeID){
		$db_conn = db_connect();
		$ModuleOriginID = $moduleOriginInfo->getID();
		$ModuleTargetID = $moduleTargetInfo->getID();
		$depType = depType($dependencyTypeID);
		
		$origin_cond = 'dep.`'.$depType->getTargetFieldname().'`='.$originID;
		
		$sql = "SELECT DISTINCT element.*,dep.`DependencyTypeID`,dep.`".$depType->getOriginFieldname()."` FROM `".$moduleTargetInfo->tableName."` AS element,dependencies AS dep WHERE $origin_cond AND element.ID=dep.`".$depType->getOriginFieldname()."` AND element.Activity=1";
		$sql.=" AND dep.`dependencyTypeID`=".$depType->getIDinDatabase();
		
		
		if ($GLOBALS["php_request"] && $moduleTargetInfo->name=='media' && !($GLOBALS["take_unpublished"]===true))
			$sql.=' AND element.`Published`=1 ';
		$sql.=' LIMIT 0,50'; // because freelinks can be outrageously numerous
		sql_log($sql);
		$rs = $db_conn->Execute($sql);
		return $rs;
	}
	
	function handleOmnilinks($moduleInfo,$elemID,$profile){
		// taking the different types of omnilink for this module
		$types = new sushee_omnilinkTypeSet(/*$moduleInfo->getID()*/);
		
		$links_str = '';
		
		
		$profile_node = new XMLNode($profile['xml'],$profile['path']);
		
		// user asked for specific types
		if($profile_node->exists('OMNILINKS/OMNILINK')){
			$types_nodes = $profile_node->getElements('OMNILINKS/OMNILINK');
			$types = new Vector();
			foreach($types_nodes as $type_node){
				$type = new sushee_OmnilinkType($type_node->getAttribute('type'));
				if($type->loaded){
					$types->add($type->getName(),$type);
				}
			}
		}
		
		$links_str.='<OMNILINKS>';
		// looping through the different types
		while($type = $types->next()){
			$hits = 0;
			$omnilink_profile_node = $profile_node->getElement('OMNILINKS');
			if($profile_node->getElement('OMNILINKS/OMNILINK[@type="'.$type->getName().'"]')){
				$omnilink_profile_node = $profile_node->getElement('OMNILINKS/OMNILINK[@type="'.$type->getName().'"]');
			}
			$elements_str = '';
			// taking all links with this type
			// first from the element to the multimodule 
			$elt_omnilinks = new sushee_ElementOmnilinkers($type,$moduleInfo,$elemID);
			while($omnilink = $elt_omnilinks->next()){
				$element = $this->handleOmnilink($type,$omnilink_profile_node,$type->getModule(),$omnilink->getOmnilinkerID());
				$elements_str.= $element;
				if($element)
					$hits++;
			}
			
			// now from the element to the omnilinker (of one specific type)
			$elt_omnilinks = new sushee_ElementOmnilinked($type,$elemID);
			while($omnilink = $elt_omnilinks->next()){
				$element=$this->handleOmnilink($type,$omnilink_profile_node,$omnilink->getModuleTarget(),$omnilink->getElementID());
				$elements_str.= $element;
				if($element)
					$hits++;
			}
			
			$links_str.='<OMNILINK type="'.$type->getName().'" hits="'.$hits.'">';
			$links_str.=$elements_str;
			$links_str.='</OMNILINK>';
		}
		$links_str.='</OMNILINKS>';
		
		return $links_str;
	}
	
	function handleOmnilink($type,$profile_node,$moduleInfo,$elementID){
		
		$output_manager = new NQLSearchOutputManager();
		$output_manager->setDepth(false);
		
		// composing a  xSushee request taking the target element
		$target_requestXML = new XML(
			'<GET name="omnilinked">
				<'.$moduleInfo->getxSusheeName().' ID="'.$elementID.'"/>
				<RETURN>'.$profile_node->copyOf('/*').'</RETURN>
			</GET>');
		$profile_path = '/GET/RETURN';
		
		// SQL request taking the target element
		$sql='';
		$rs = getResultSet($moduleInfo,$target_requestXML,'/*[1]',$sql);
		
		// asking an output manager to compose a xSushee response corresponding to the request
		$output_manager->setResultSet($rs);
		$output_manager->setOperationNode($target_requestXML->getFirstchild());
		$output_manager->setModule($moduleInfo->getID());
		$profile = array('profile_xml'=>$target_requestXML,'profile_path'=>$profile_path);
		$output_manager->setProfileConfig($profile);
		
		return $output_manager->getXML();
	}
}

?>