<?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.

`/var/www/installer/public_html/officity-source/apps/officity/shared/libitem/libitem-op.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/>.
*/
	class Officity_LibitemElementOperation extends AbstractElementOperation
	{
		function __construct()
		{
			parent::__construct(new Officity_LibitemElement());	
		}
		
		function __destruct()
		{
			
		}
		
		/* *** */
		
		// CREATION: disk, folder, book, chapter, page		
		// NB: disks are attached to virtual server(s) in the postprocessor
		public function _create()
		{
			/* 1st: parse, creation and upload */
			$this->element->parse($_POST);
			
			$type = $this->element->getInfo('TYPE');
			if ($type == 'file')
			{
				return $this->_createFile();
			}

			$title = ($this->element->isWiki() == true) 
							? $this->element->getDescription($this->fetchURLParam('language'), 'TITLE')
							: $this->element->getInfo('TITLE');
			if (empty($title))
			{
				$this->abort('Please enter a title!');
			}
			
			// dependency : optional
			if (URLParam::isNotEmpty('dependency') &&
				URLParam::isNotEmpty('targetModule') &&
				URLParam::isNotEmpty('targetID'))
			{
				$this->element->setDependencyByName(
					$this->fetchURLParam('dependency'),
					$this->fetchURLParam('targetModule'),
					$this->fetchURLParam('targetID'),
					$this->fetchURLParam('mode', 'reverse'),
	               	'append'
	               	);
			}
			else
			{
				if (URLParam::isNotEmpty('dependency') ||
					URLParam::isNotEmpty('targetModule') ||
					URLParam::isNotEmpty('targetID'))
				{
					// throw exception
					$this->fetchURLParam('dependency');
					$this->fetchURLParam('targetModule');
					$this->fetchURLParam('targetID');
				}
			}

			if ($this->element->create() == false)
			{
				$this->error();
				return false;
			}
			
			$this->setResultData('ID', $this->element->getID());
			
			/* 2nd: container's security inheritance */
            if ($this->applySecurityInheritance(array($this->element->getID())) == false)
            {
            	return false;
            }

			return true;
		}
		
		// Batch creation (quick adds) using TYPE and a TITLE(s) list
		// Works for disks, folders, books, chapters, pages
		// Titles are separated by a comma
		public function _batchCreate()
		{
			/* 1st: use element to retrieve type & titles */
			$this->element->parse($_POST);
			
			$type = $this->element->getInfo('TYPE');
			if (!in_array($type, array('disk', 'folder', 'book', 'chapter', 'page')))
			{
				$this->abort('Batch creation is not authorized on "'.$type.'" items!');
			}
			
			$titles_string = ($this->element->isWiki() == true) 
								? $this->element->getDescription($this->fetchURLParam('language'), 'TITLE')
								: $this->element->getInfo('TITLE');
								
			$titles_string = trim($titles_string, ' ,');
			$titles = explode(',', $titles_string);
			if (empty($titles_string) || (count($titles) == 0))
			{
				$this->abort('Please enter at least one title!');
			}			
		
			// dependency : optional
			if (URLParam::isNotEmpty('dependency') &&
				URLParam::isNotEmpty('targetModule') &&
				URLParam::isNotEmpty('targetID'))
			{
				$this->element->setDependencyByName(
					$this->fetchURLParam('dependency'),
					$this->fetchURLParam('targetModule'),
					$this->fetchURLParam('targetID'),
					$this->fetchURLParam('mode', 'reverse'),
	               	'append'
               	);
			}
			else
			{
				if (URLParam::isNotEmpty('dependency') ||
					URLParam::isNotEmpty('targetModule') ||
					URLParam::isNotEmpty('targetID'))
				{
					// throw exception
					$this->fetchURLParam('dependency');
					$this->fetchURLParam('targetModule');
					$this->fetchURLParam('targetID');
				}
			}
			
			/* 2nd : create each element */
			$success_IDs = array();
			
			if ($this->element->isWiki() == true) // title is in DESCRIPTION
			{
				$languageID = $this->fetchURLParam('language');
				
				foreach ($titles as $title)
				{				
					$this->element->setDescription($languageID, 'TITLE', $title);
					$result = $this->element->create();
					if ($result == false)
					{					
						$this->setResultData('Success IDs', $success_IDs);
						$this->setResultData('Error title', $title);
						
				        $this->error('Create failed on '.$this->element->getDenomination().' title="'.$title.'": '.$this->element->getLastError().' / '.count($success_IDs).'element(s) created.');
				        return false;
					}
					$success_IDs []= $this->element->getID();
					$this->element->removeID();
				}
			}
			else // title is in INFO
			{
				foreach ($titles as $title)
				{				
					$this->element->setInfo('TITLE', $title);
					$result = $this->element->create();
					if ($result == false)
					{					
						$this->setResultData('Success IDs', $success_IDs);
						$this->setResultData('Error title', $title);
						
				        $this->error('Create failed on '.$this->element->getDenomination().' title="'.$title.'": '.$this->element->getLastError().' / '.count($success_IDs).'element(s) created.');
				        return false;
					}
					$success_IDs []= $this->element->getID();
					$this->element->removeID();
				}
			}
								
			/* 3rd: container's security inheritance */
			if ($this->applySecurityInheritance($success_IDs) == false)
            {
            	return false;
            }

		    $this->setResultData('Success IDs', $success_IDs);
		    $this->setResultData('Success titles', $titles);
		    $this->success(count($success_IDs).' element(s) successfully created!');
		    
		    return true;
		}
		
		protected function applySecurityInheritance($targetIDs)
		{
			$e = new Officity_LibitemElement($targetIDs[0]); // same for all
			
			$e->setReturn(array(
               	'DEPENDENCY[@type="officity:container"]/OWNERS',
               	'DEPENDENCY[@type="officity:container"]/GROUPS',
               	'DEPENDENCY[@type="officity:container"]/OWNERID',
               	'DEPENDENCY[@type="officity:container"]/GROUPID',
			), 2);
			
			if ($e->get() == false)
			{
				// major failure
				$this->abort('Error applying security inheritance, cannot retrieve container element: '.$this->element->getLastError());
			}
			
			$container_node = $e->getNQLObject()->getElement('DEPENDENCIES/DEPENDENCY[@type="officity:container"]/OFFICITY:LIBITEM');
			if ($container_node !== false)
			{
				$nql = new Officity_Shell(false);
				$command = '
		               		<UPDATE>
		               			<OFFICITY:LIBITEM>
	               					'.$container_node->copyOf('INFO').'
	               					<WHERE>
	               						<INFO>
	               							<ID operator="IN">'.implode(',', $targetIDs).'</ID>
	               						</INFO>
	               					</WHERE>
		               			</OFFICITY:LIBITEM>
		               		</UPDATE>	
		               	';
				$nql->addCommand($command);
				$nql->execute();
				
				$message = $nql->getElement("/RESPONSE/MESSAGE");
				if (($message === false) || ($message->valueOf("@msgType") != '0'))
				{
					$msg = ($message) ? ' '.$message->valueOf('.') : '';
					$this->error('Error after creation: the security inheritance has not been applied!'.$msg);
					return false;
				}
			}
			
			return true;
		}
		
		// FILE CREATION
		// NB: files properties are computed in the create/update preprocessor
		public function _createFile()
		{
			/* 1st: parse, creation and upload */
			$this->element->parse($_POST);
			
			$type = $this->element->getInfo('TYPE'); // for later...
			if ($type != 'file')
			{
				$this->abort('Operation "createFile" cannot be called on "'.$type.'" items!');
			}
			
			// dependency : optional
			if (URLParam::isNotEmpty('dependency') &&
				URLParam::isNotEmpty('targetModule') &&
				URLParam::isNotEmpty('targetID'))
			{
				$this->element->setDependencyByName(
					$this->fetchURLParam('dependency'),
					$this->fetchURLParam('targetModule'),
					$this->fetchURLParam('targetID'),
					$this->fetchURLParam('mode', 'reverse'),
	               	'append'
	               	);
			}
			else
			{
				if (URLParam::isNotEmpty('dependency') ||
					URLParam::isNotEmpty('targetModule') ||
					URLParam::isNotEmpty('targetID'))
				{
					// throw exception
					$this->fetchURLParam('dependency');
					$this->fetchURLParam('targetModule');
					$this->fetchURLParam('targetID');
				}
			}
						
			if ($this->element->noFiles2Upload() == true)
			{
				$this->abort('Please provide a file to upload!');
			}
			// special case : auto-unzip file content
			if ($this->fetchURLParam('auto-unzip', '0') == '1')
			{
				return $this->unzipFileAndCreateContent();
			}

			if ($this->element->create() == false)
			{
				$this->error();
				return false;
			}
			
			$this->setResultData('ID', $this->element->getID());
			
			/* 2nd: container's security inheritance */
            if ($this->applySecurityInheritance(array($this->element->getID())) == false)
            {
            	return false;
            }
			
        	/* 3rd: upload files and update */
            $upload_result = $this->element->uploadFiles(true);               	
            if ($upload_result == false)
            {
             	$this->error('Error after file creation: '.$this->element->getLastError());
             	return false;
            }

            /* 4th: create initial file version */
			$this->createNewVersion($this->element->getID(), 1, 'Initial version');
		}
		
		protected function unzipFileAndCreateContent()
		{
			/* 1st: uncompress zip file */
			$tmp_folder = new TempDirectory();
			$tmp_folder_path = $tmp_folder->getCompletePath();
			$tmp_folder->create();
			if (!$tmp_folder->exists())
            {
	             $this->error('Creation error: cannot create folder '.$tmp_folder_path.'!');
	             return false;
            }
								    
			$uploader = new FilesUploader();
            $uploader->setTarget($tmp_folder);
            
            $files_2_upload = $this->element->getFiles2Upload();
            foreach ($files_2_upload as $input_name => $field_name)
            {
            	$uploader->addFile($_FILES[$input_name]);
            }
            
            if ($uploader->execute() == false)
            {
                $file_infos = '"'.$tmp_folder_path.$_FILES[$input_name]['name'];
                $file_infos .= '" ('.$_FILES[$input_name]['type'];
                $file_infos .= ' - '.$_FILES[$input_name]['size'].' bytes)';
                
                $this->error('Creation error: upload failed => '.PHP_EOL.PHP_EOL.$file_infos); 
                return false;
            }

            $files = $uploader->getFiles();
            $archive_file = $files[0];
            if (!is_object($archive_file))
            {
                $this->error('Creation error: cannot retrieve uploaded file!');
                return false;
            }
            
            if ($archive_file->uncompress() == false)
            {
            	$this->error('Creation error: cannot uncompress file "'.$archive_file->getCompletePath().'"!');
            	return false;
            }
                       
            /* 2nd: create structure using archive content */
            
            // Strategy: list all direct children in order to create the corresponding elements
            // using the dependency already set on $this->element (officity:contactFavorites or officity:container)
            // then for each folder, we'll use recursion only on dependency officity:container
            $children = array();
            
            $tmp_folder = $tmp_folder->casttoclass('Folder'); // in order to have the "getNextFile" method
			while ($file = $tmp_folder->getNextFile())
			{
				$current_name = $file->getName();
		  		if ($current_name == $archive_file->getName())
		  		{
		  			continue;
		  		}
		  		if ($current_name[0] == '.')
		  		{
		  			continue;
		  		}
		  		$children []= $file;
			}
						
			if (count($children) == 0)
			{
				$this->error('Archive content is empty!');
				return false;
			}
			
			/* 2a: special case: single file, keep TITLE, DOCUMENTTYPE, SUMMARY */
			if ((count($children) == 1) && (!$children[0]->isFolder()))
			{
				$current_ID  = $this->createFileAtDest($this->element, $children[0]);
				$this->setResultData('ID', $current_ID);
				return true;
			}
			
			/* 2b: general case: tree structure */
			// sort extracted files
			usort($children, array($this, "cmpFilenames"));
			
			$success_IDs = array();
			
			$target_ID = $this->fetchURLParam('targetID', '');
			$dependency = $this->fetchURLParam('dependency', '');
			$target_module = $this->fetchURLParam('targetModule', '');
			$mode = $this->fetchURLParam('mode', 'reverse');
			
			foreach ($children as $child)
			{
				$this->browseAndCreateElements($child, $success_IDs, $target_ID, $dependency, $target_module, $mode);
			}
            
            $this->setResultData('Success IDs', $success_IDs);		    
		    $this->success(count($success_IDs).' element(s) successfully created!');
		    return true;
		}
		
	    static function cmpFilenames($a, $b)
	    {
	        return strcmp($a->getName(), $b->getName());
	    }
		
		protected function browseAndCreateElements(&$file_or_folder, &$success_IDs, $container_ID=0, $dependency='officity:container', $target_module='OFFICITY:LIBITEM', $mode='normal')
		{
			$current_name = $file_or_folder->getName();
	  		if ($current_name[0] == '.')
	  		{
	  			return;
	  		}
		  		
			$e = new Officity_LibitemElement();
			if (!empty($dependency) && !empty($target_module) && !empty($container_ID))
			{
				$e->setDependencyByName($dependency, $target_module, $container_ID, $mode, 'append');
			}
			
			if ($file_or_folder->isFolder())
			{
				$e->setInfo('TYPE', 'folder');
				$e->setInfo('TITLE', $current_name);
				$e->setInfo('PATH', '');
				
				if ($e->create() == false)
				{
					$this->setResultData('Success IDs', $success_IDs);
					$this->setResultData('Error ID', $e->getID());
					
					// abort in order to stop recursion (will send an exception)
			        $this->abort('Create failed on '.$e->getDenomination().' folder ID="'.$e->getID().'": '.$e->getLastError().' / '.count($success_IDs).'element(s) created.');
				}
				
				$current_ID = $e->getID();            
				$success_IDs []= $current_ID;				
	            
				// container's security inheritance
				$this->applySecurityInheritance(array($current_ID));
				
				// rec
				$children = array();				
				while ($file = $file_or_folder->getNextFile())
				{
					$children []= $file;						
				}
				
				// sort extracted files
				usort($children, array($this, "cmpFilenames"));
				
				foreach ($children as $child)
				{
					$this->browseAndCreateElements($child, $success_IDs, $current_ID);
				}
			}	
			else // file
			{
				$e->setInfo('TYPE', 'file');
				$e->setInfo('TITLE', '');
				$e->setInfo('PATH', '');
				
				$current_ID = $this->createFileAtDest($e, $file_or_folder);
				$success_IDs []= $current_ID;
			}
		}
		
		protected function createFileAtDest(&$element, &$file)
		{
			if ($element->create() == false)
			{
				$this->error();
				return false;
			}
			
			// container's security inheritance
            if ($this->applySecurityInheritance(array($element->getID())) == false)
            {
            	return false;
            }
			
        	// move file and update
            $dest_path = '/'.strtolower($element->getDenomination()).'/'.$element->getID(); 
            $dest_folder = new Folder($dest_path);
			$dest_folder->create();
			if (!$dest_folder->exists())
            {
	             $this->error('Error after file creation: cannot create folder '.$dest_path.'!');
	             return false;
            }
            
            $dest_file = $file->copy($dest_path);
            if ($dest_file == false)
            {
             	$this->error('Error after file creation: cannot copy uncompressed file "'.$file->getCompletePath().'" to "'.$dest_path.'"');
             	return false;
            }	       

            $element->reset();
            $element->setInfo('PATH', $dest_file->getPath());
		    if ($element->update() == false)
            {
                $this->error('Error after file creation: '.$element->getLastError());
                return false;
	        }

            // create initial file version
			$this->createNewVersion($element->getID(), 1, 'Initial version');
			
			return $element->getID();
		}
		
		protected function createNewVersion($element_ID, $number, $notes)
		{
			$e = new Officity_LibitemElement($element_ID);
			if ($e->get() == false)
			{
				$this->warning('Warning: error creating new file version (get error)! '.$e->getLastError());
			}
				
			$nql = new Sushee_Shell(false);
			$nql->addCommand('
				<CREATE>
					<OFFICITY:LIBVERSION>
						<INFO>
							<LIBITEMID>'.$e->getID().'</LIBITEMID>
							<PATH>'.$e->getInfo('PATH').'</PATH>
							<FILENAME>'.$e->getInfo('FILENAME').'</FILENAME>
							<KIND>'.$e->getInfo('KIND').'</KIND>
							<SIZE>'.$e->getInfo('SIZE').'</SIZE>
							<SIZEHUMAN>'.$e->getInfo('SIZEHUMAN').'</SIZEHUMAN>
							<TITLE>'.$e->getInfo('TITLE').'</TITLE>
							<VERSIONNOTES>'.$notes.'</VERSIONNOTES>
							<VERSIONNUMBER>'.$number.'</VERSIONNUMBER>
						</INFO>
		    			<DEPENDENCIES>
		    				<DEPENDENCY type="officity:versions" mode="reverse">
		    					<OFFICITY:LIBITEM ID="'.$e->getID().'" />
		    				</DEPENDENCY>
		    			</DEPENDENCIES>
		    		</OFFICITY:LIBVERSION>
				</CREATE>
			');
			$nql->execute();
			
			$message = $nql->getElement("/RESPONSE/MESSAGE");
			if (($message === false) || ($message->valueOf("@msgType") != '0'))
			{
				$msg = ($message) ? ' '.$message->valueOf('.') : '';
				$this->warning('Warning: error creating new file version (create error)! '.$msg);
			}
			
			return true; // the file has been created...
		}
		
		// ADD/REMOVE DEPENDENCY
		public function _link()
		{
			if ($this->fetchURLParam('ID') == $this->fetchURLParam('targetID'))
			{
				$this->abort('Cannot link this element to itself!');
			}
			
			return parent::_link();
		}
		
		// COUNT
		public function _count()
		{
			session_write_close(); // optim: unlock session data and allow concurrent accesses to this script
			
            $TYPE = $this->fetchURLParam('type');
            switch ($TYPE)
            {
            	case 'trashed':
            		$this->element->removeInfo('TRASHED');
            		$this->element->setSearchInfo('TRASHED', '=', 'intrash');
            		$this->element->setSearchInfo('TRASHED', '=', 'intrasheditem');
            		break;
            		
            	case 'content':
	            	$CONTAINER_ID = $this->fetchURLParam('ID');
	            	
	            	$nql = new Officity_Shell(false);
	            	
	            	$nql->addCommand('
						<SEARCH name="files">
						  <OFFICITY:LIBITEM>
						  	<INFO>
						  		<TRASHED operator="=">no</TRASHED>
						  		<TYPE operator="=">file</TYPE>
						  		<TYPE operator="=">page</TYPE>
						  	</INFO>
						    <ANCESTOR type="officity:content">
						      <OFFICITY:LIBITEM ID="'.$CONTAINER_ID.'"/>
						    </ANCESTOR>
						  </OFFICITY:LIBITEM>
							<RETURN>
								<INFO>
									<SIZE />
								</INFO>
							</RETURN>
						</SEARCH>
	            	');
	            	$nql->addCommand('
						<COUNT name="itemsCount">
						  <OFFICITY:LIBITEM>
						  	<INFO>
						  		<ID operator="NE">'.$CONTAINER_ID.'</ID>
						  		<TRASHED operator="=">no</TRASHED>
						  	</INFO>
						    <ANCESTOR type="officity:content">
						      <OFFICITY:LIBITEM ID="'.$CONTAINER_ID.'"/>
						    </ANCESTOR>
						  </OFFICITY:LIBITEM>
						</COUNT>
	            	');
	            	
	            	$nql->execute();
	            	
	            	$items_count = $nql->valueOf('/RESPONSE/RESULTS[@name="itemsCount"]/@hits');
	            	$this->setResultData('items', $items_count);
	            	
	            	$total_size = $nql->transform(dirname(__FILE__).'/compute-totalsize.xsl');
	            	//var_dump($total_size);
	            	
	            	$human_size = sizeToHuman($total_size).' ('.$total_size.' bytes)';
	            	$this->setResultData('size', $human_size);
	            	
	            	return true;
            		break;
            		
            	default:
            		break;
            }
             
            return parent::_count();
		}
				
		// APPLY SECURITY TO FOLDERS/FILES
		public function _applySecurity()
		{	
			$ID = $this->fetchURLParam('ID');
			$this->element->setID($ID);			
			$this->element->setReturn(array(
				'INFO/GROUPS',
				'INFO/OWNERS',
				'INFO/OWNERID',
				'INFO/GROUPID'
			));		
			if ($this->element->get() == false)
			{
				$this->abort();
			}
			
			$update_info = $where_info ='';
			
			$info_node = $this->element->getNQLObject()->getElement('/OFFICITY:LIBITEM/INFO');
						
			if ($this->fetchURLParam('apply-allusers') == '1')
			{
				$update_info .= $info_node->copyOf('OWNERS');
			}
			if ($this->fetchURLParam('apply-allgroups') == '1')
			{
				$update_info .= $info_node->copyOf('GROUPS');
			}
			if ($this->fetchURLParam('apply-mainowner') == '1')
			{
				$update_info .= $info_node->copyOf('OWNERID');
			}
			if ($this->fetchURLParam('apply-maingroup') == '1')
			{
				$update_info .= $info_node->copyOf('GROUPID');
			}
			
			if (empty($update_info))
			{
				$this->abort('No security settings selected!');
			}
							
			switch ($this->fetchURLParam('apply-type'))
			{
				case 'filesfolders';
					$where_info .= '<TYPE operator="IN">file,folder</TYPE>';
					break;
					
				case 'folders';
					$where_info .= '<TYPE operator="=">folder</TYPE>';
					break;
					
				case 'files';
					$where_info .= '<TYPE operator="=">file</TYPE>';
			 		break;
			 		
				case 'wikis';
					$where_info .= '<TYPE operator="=">book,chapter,page</TYPE>';
			 		break;
			 		
				default:
					break;
			}
			
			switch ($this->fetchURLParam('apply-to'))
			{
				case 'publicitems':
					$where_info = $where_info.'<OWNERS operator="="></OWNERS>
												<GROUPS operator="="></GROUPS>
												<OWNERID operator="=">0</OWNERID>
												<GROUPID operator="=">0</GROUPID>';
					break;
					
				case 'secureditems':
					$where_info = $where_info.'<OWNERS operator="NE"></OWNERS></INFO>'.
									'<INFO>'.$where_info.'<GROUPS operator="NE"></GROUPS></INFO>'.
									'<INFO>'.$where_info.'<OWNERID operator="NE">0</OWNERID></INFO>'.
									'<INFO>'.$where_info.'<GROUPID operator="NE">0</GROUPID>';
					break;
				
				default: // all		
					break;
			}				
			
			$nql = new Officity_Shell(false);
						
			$DEPTH = $this->fetchURLParam('apply-depth');
			if ($DEPTH == 'all')
			{
				$nql_command = '
					<UPDATE>
						<OFFICITY:LIBITEM>
							<WHERE>
								<INFO>'.$where_info.'</INFO>
								<ANCESTOR type="officity:content">
									<OFFICITY:LIBITEM ID="'.$ID.'" />
								</ANCESTOR>
							</WHERE>			
							<INFO>'.$update_info.'</INFO>
						</OFFICITY:LIBITEM>
					</UPDATE>
				';	
			}
			else
			{				
				$nql_command = '
					<GET name="item">
						<OFFICITY:LIBITEM ID="'.$ID.'" />
						<RETURN depth="'.($DEPTH+1).'">
							<NOTHING />
							<DEPENDENCY type="officity:content" />
						</RETURN>
					</GET>
				';
				$nql->addCommand($nql_command);
				$targets_IDs = $nql->transform(dirname(__FILE__).'/apply-security-updates.xsl');
				
				if (empty($targets_IDs))
				{
					$this->warning('No content items to update!');
					return true;
				}
								
				$where_info .= '<ID operator="IN">'.$targets_IDs.'</ID>';
				
				$nql_command = '
					<UPDATE>
						<OFFICITY:LIBITEM>
							<WHERE>
								<INFO>'.$where_info.'</INFO>
							</WHERE>			
							<INFO>'.$update_info.'</INFO>
						</OFFICITY:LIBITEM>
					</UPDATE>
				';
			}
						
			$nql->addCommand($nql_command);
			//die($nql->getQuery());
			
			if (($result = $nql->execute()) == true)
			{			
				$msg_node = $nql->getElement('/RESPONSE/MESSAGE');
				//die($msg_node->toString());
				if ($msg_node !== false)
				{
					if ($msg_node->valueOf('@msgType') == 0)
					{						
						$this->setResultData('hits', $msg_node->valueOf('@hits'));
						return true;
					}
					else
					{
						if ($msg_node->valueOf('@errorCode') == SUSHEE_ERRORCODE_UPDATEONEMPTYELEMSET)
						{
							$this->info($msg_node->valueOf('.'));
							return true;
						}
						$this->error($msg_node->valueOf('.'));
						return false;
					}
				}
			}
			
			return false;
		}
		
		// COMPRESS/DOWNLOAD FOLDER
		public function _downloadFolder()
		{
			$ID = $this->fetchURLParam('ID');
			$nql = new Officity_Shell(false);
			$nql_command = '
				<GET>
					<OFFICITY:LIBITEM ID="'.$ID.'" />
					<RETURN depth="all">
						<INFO>
							<TYPE />
							<TITLE />
							<EXT />
							<PATH />							
						</INFO>
						<DEPENDENCIES>
							<DEPENDENCY type="officity:content" />
						</DEPENDENCIES>
					</RETURN>
				</GET>
			';
			$nql->addCommand($nql_command);
			$nql->execute();
			
			$folder_node = $nql->getElement("/RESPONSE/RESULTS/OFFICITY:LIBITEM");
			if ($folder_node === false)
			{
				$this->abort('Cannot retrieve element data (ID='.$ID.')');
			}
			
			$tmp_folder = new TempDirectory();
			$tmp_path = $tmp_folder->getCompletePath().'/';
			$folder_name = clean_filename($folder_node->valueOf('./INFO/TITLE'));
			$folder_path = $tmp_path.$folder_name;
			
			$main_folder = new Folder($folder_path);
			if ($main_folder->create() == false)
            {
                $this->error('Folder creation failed! ('.$folder_path.')');
                return false;
            }
            
            $content_nodes = $folder_node->getElements('./DEPENDENCIES/DEPENDENCY[@type="officity:content"]/OFFICITY:LIBITEM');
            if ($this->createFolderStructure($content_nodes, $folder_path) == false)
            {
            	return false;
            }
            
            $main_folder_compressed_file = $main_folder->compress();
            $this->setResultData('path', $main_folder_compressed_file->getCompletePath());
            
            return true;
		}
		
		private function createFolderStructure($content_nodes, $current_folder_path)
		{
			foreach ($content_nodes as $node)
			{
				if ($node->valueOf('./INFO/TYPE') == 'folder')
				{					
					$current_name = clean_filename($node->valueOf('./INFO/TITLE'));
					$new_path = $current_folder_path.'/'.$current_name;
				
					$new_folder = new Folder($new_path);
					if ($new_folder->create() == false)
		            {
		                $this->error('Folder creation failed! ('.$new_path.')');
		                return false;
		            }
		            
		            $new_content_nodes = $node->getElements('./DEPENDENCIES/DEPENDENCY[@type="officity:content"]/OFFICITY:LIBITEM');
		            $this->createFolderStructure($new_content_nodes, $new_path);
				}
				else if ($node->valueOf('./INFO/TYPE') == 'file')
				{
					$current_name = clean_filename($node->valueOf('./INFO/TITLE'));
					$new_path = $current_folder_path.'/'.$current_name;
					
					$current_file = new File($node->valueOf('./INFO/PATH'));
					$dest_file = new File($new_path.'.'.$node->valueOf('./INFO/EXT'));
					if ($current_file->copy($dest_file) == false)
					{
						$this->error('File copy failed! ("'.$current_file->getPath().'" => "'.$dest_file->getCompletePath().'")');
						return false;
					}
				}
			}
		}
		
		// CREATE NEW FILE VERSION
		public function _newVersion()
		{	
			$VERSION_NO = $this->fetchURLParam('version-number');
			$VERSION_NOTES = $this->fetchURLParam('version-notes', '');
			
			// 1st: upload new file
			$this->element->setID($this->fetchURLParam('ID'));
			$this->element->parse($_POST);
			
        	// upload files and update
            if ($this->element->uploadFiles(true) == false)
            {
             	$this->error('Fatal error: '.$this->element->getLastError());
             	return false;
            }
            
            // 2nd : create new version
            $this->createNewVersion($this->element->getID(), $VERSION_NO, $VERSION_NOTES);
			
			return true;
		}
		
		// RESTORE PREVIOUS FILE VERSION
		public function _restoreVersion()
		{	
			// 1st: copy version PATH to file in order to trigger the computeFileProperties preprocessor when update			
			$version = new Officity_LibversionElement($this->fetchURLParam('versionID'));
			$version->setReturn(array(
				'INFO/PATH',
				'INFO/VERSIONNUMBER'
			));
			if ($version->get() == false)
			{
				$this->abort('Cannot retrieve version data!');
			}
			
			// NB : the preprocessor will be triggered by a file
			// ideally, a dedicated FileOperation class should be used
			$this->element->setInfo('TYPE', 'file');
			
			$this->element->setID($this->fetchURLParam('fileID'));	
			$this->element->setInfo('PATH', $version->getInfo('PATH'));
			if ($this->element->update() == false)
			{
				$this->error('Cannot update file data!');
				return false;
			}
			
			// 2nd : create a new version
			$this->createNewVersion($this->element->getID(), $this->fetchURLParam('version-number'), 'Restored from v'.$version->getInfo('VERSIONNUMBER'));
			
			return true;
		}
		
		/* *** */
		
		public function _emptyTrash()
		{
			$nql = new Sushee_Shell(false);
			$nql->addCommand('
				<KILL name="empty-trash">
					<OFFICITY:LIBITEM>
						<WHERE>
							<INFO>
								<TRASHED>intrash</TRASHED>
							</INFO>
						</WHERE>
					</OFFICITY:LIBITEM>
				</KILL>
			');	
			
			$nql->execute();
					
			$message = $nql->getElement("/RESPONSE/MESSAGE");
			if (($message !== false) && ($message->valueOf("@msgType") == '0'))
			{
				return true;
			}
			
			if ($message->valueOf("@errorCode") == SUSHEE_ERRORCODE_KILLONEMPTYELEMSET)
			{
				$this->setResultData('count', 0);
				$this->info('The trash was already empty.');
				return true;
			}
			
			return false;
		}
		
		/* *** */
	}
?>