<?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-processors.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_LibitemProcessor
	{	
		public static $ext_lookup = array(
			'jpg'=>'image','png'=>'image','pes'=>'image','gif'=>'image','bmp'=>'image','tif'=>'image','tiff'=>'image',
			'pdf'=>'pdf','pdfx'=>'pdf',
			'mpg'=>'video','mp4'=>'video','flv'=>'video','mov'=>'video','avi'=>'video','dv' =>'video','cin'=>'video','mpeg'=>'video',
			'mp3'=>'audio','ogg' =>'audio','aiff'=>'audio','wav'=>'audio','m4a'=>'audio','aac'=>'audio',
			'zip'=>'archive','tar'=>'archive','gz'=>'archive','gz7'=>'archive','sit'=>'archive',
			'svg'=>'vectors','ai'=>'vectors','eps'=>'vectors',
			'txt'=>'text',
			'xls'=>'spreadsheet','xlsx'=>'spreadsheet','numbers'=>'spreadsheet','csv'=>'spreadsheet',
			'doc'=>'document','docx'=>'document','rtf'=>'document','pages'=>'document', 'ppt' => 'document'
		);
		
		protected function executeUpdateQuery($command_or_shell, $success_msg, $error_msg)
		{
			if (is_string($command_or_shell))
			{
				$nql = new Sushee_Shell(false);			
				$nql->addCommand($command_or_shell);
			}
			else
			{
				$nql = $command_or_shell;
			}
			
			//var_dump($nql->getQuery());
			$nql->execute();
			
			$message = $nql->getElement("/RESPONSE/MESSAGE");
			if (($message !== false) && ($message->valueOf("@msgType") == '0'))
			{
				return new SusheeProcessorMessage($success_msg);
			}
			
			//var_dump($message->toString());
			// special case: update search has not given any results => no error
			if ($message->valueOf("@errorCode") == SUSHEE_ERRORCODE_UPDATEONEMPTYELEMSET)
			{
				return new SusheeProcessorMessage($success_msg.' (no update processed)');
			}
			
			return new SusheeProcessorException($error_msg.$message->valueOf('.'));
		}
			
		// PRE-CREATE/UPDATE #1
		// for files & pages : if Path has changed, compute file properties & create image preview 
		public function computeFileProperties($data)
		{	
			if (!in_array($data->getValue('Type'), array('file', 'page')))
			{
				return new SusheeProcessorMessage('computeFileProperties: nothing to do');
			}
			
			$path = $data->getValue('Path');
			if ($path === $data->getFormerValue('Path'))
			{
				return new SusheeProcessorMessage('computeFileProperties: no file uploaded, nothing to do');
			}
			
			$file = new File($path);			

			$pathinfos = pathinfo($path);
	    	if (!$data->getValue('Title'))
			{
	    		$data->setValue('Title', $pathinfos['filename']);
	    	}
	    	
			$fileName = $pathinfos['basename'];
			$data->setValue('Filename', $fileName);
			
			$ext = strtolower($pathinfos['extension']);
			$data->setValue('ext', $ext);
			
			$size = $file->getSize();
			$data->setValue('size', $size);
			if ($size !== $data->getFormerValue('Size'))
			{
				$size_human = sizeToHuman($size);
				$data->setValue('sizeHuman', $size_human);
			}
			
			$kind = !empty(self::$ext_lookup[$ext]) ? self::$ext_lookup[$ext] : 'unknown';
			$data->setValue('kind', $kind);
	
			// image/pdf kinds: create icon/preview
			if (($kind == 'image') || ($kind == 'pdf'))
			{
				// -- icon --
				$icon_path = imageTransform('<IMAGE path="'.$path.'"><vertical><resize height="16"/></vertical><horizontal><resize width="16"/></horizontal><convert format="jpg"/></IMAGE>',false);
				if ($icon_path)
				{
					// -- if one transformation works, others will work --

					// -- 1. create resources directory --
					$file = new File($path);
					$folder = $file->getParent();
					$resources = $folder->createDirectory('resources');
					
					// -- 2. create target files --
					$icon = $resources->createFile('icon.jpg');
					$thumbnail = $resources->createFile('thumbnail.jpg');
					$preview = $resources->createFile('preview.jpg');
					
					// -- 3. create source files and copy content in target --
					$icon_tmp = new File($icon_path);
					$icon_tmp->copy($icon);
					
					$thumb_tmp = new File(imageTransform('<IMAGE path="'.$path.'"><vertical><resize height="32"/></vertical><horizontal><resize width="32"/></horizontal><convert format="jpg"/></IMAGE>',false));
					$thumb_tmp->copy($thumbnail);
					
					$preview_tmp = new File(imageTransform('<IMAGE path="'.$path.'"><vertical><resize height="128"/></vertical><horizontal><resize width="128"/></horizontal><convert format="jpg"/></IMAGE>',false));
					$preview_tmp->copy($preview);

					$data->setValue('Icon', 1);
					$data->setValue('Preview', 1);
				}
			}
			else
			{
				$data->setValue('Icon', 0);
				$data->setValue('Preview', 0);
			}
			
			return new SusheeProcessorMessage('File properties computed');
		}
		
		// PRE-CREATE/UPDATE #2
		// for pages, chapters & books : copy description to info for SEARCHTEXT support
		public function copyWikitemDesc($data)
		{
			if (!in_array($data->getValue('Type'), array('page', 'chapter', 'book')))
			{
				return new SusheeProcessorMessage('copyWikitemDesc: nothing to do');
			}
			
			$desc = $data->getElementNode()->getElement('DESCRIPTIONS/DESCRIPTION');
			if ($desc === false)
			{
				return new SusheeProcessorMessage('copyWikitemDesc: no description, nothing to do');
			}
			
			$new_title = $desc->valueOf('TITLE');
			if (!empty($new_title) && ($new_title != $data->getValue('Title')) )
			{
				$data->setValue('Title', trim($new_title.' '.$data->getValue('Title')));
			}
			
			$new_summary = $desc->valueOf('SUMMARY');
			if (!empty($new_summary) && ($new_summary != $data->getValue('Summary')))
			{
				$data->setValue('Summary', trim($new_summary.' '.$data->getValue('Summary')));
			}
			
			return new SusheeProcessorMessage('Descriptions copied');
		}
		
		// POST-CREATE #1
		// for disks only, link them to the virtual server(s)
		public function linkDisk2VirtualServers($data)
		{
			if ($data->getValue('Type') != 'disk')
			{
				return new SusheeProcessorMessage('linkDisk2VirtualServers: nothing to do');
			}
			
			return $this->executeUpdateQuery('
               	<UPDATE processors="do-not-exec">
               		<OFFICITY:LIBITEM>
						<DEPENDENCIES>
							<DEPENDENCY type="officity:content" operation="append">
								<OFFICITY:LIBITEM ID="'.$data->getID().'" />
							</DEPENDENCY>
						</DEPENDENCIES>
						<WHERE>
							<INFO>
								<TYPE>server</TYPE>
							</INFO>
						</WHERE>
               		</OFFICITY:LIBITEM>
               	</UPDATE>	
				',
				'Disk successfully linked to virtual server(s)',
				'Error linking disk to virtual server(s): '
			);
			
			return new SusheeProcessorMessage('Disk linked to virtual server');
		}
		
		// POST-CREATE/UPDATE #2 + PRE KILL/DELETE #1
		// for all : add/remove 1 item in the container's items count
		public function updateContainerItemsCount($data)
		{	
			$node = $data->getNode();
			if ($node->valueOf('@processors') == 'do-not-exec')
			{
				return new SusheeProcessorMessage('updateContainerItemsCount: discarded');
			}
			
			//var_dump($node->toString());
			$command_name = $node->nodeName('.');
			switch ($command_name)
			{
				// +1 on the containers
				case 'CREATE':
					return $this->executeUpdateQuery('
		               	<UPDATE processors="do-not-exec">
		               		<OFFICITY:LIBITEM>
								<INFO>
									<ITEMS operator="++" />
								</INFO>
								<WHERE>
									<DEPENDENCIES>
										<DEPENDENCY type="officity:content">
											<OFFICITY:LIBITEM ID="'.$data->getID().'" />
										</DEPENDENCY>
									</DEPENDENCIES>
								</WHERE>
		               		</OFFICITY:LIBITEM>
		               	</UPDATE>	
						',
						'Items count successfully incremented',
						'Error updating items count: '
					);
					break;
					
				// -1 on the containers	
				case 'KILL':
				case 'DELETE':
					// special case: only if not yet in trash (see UPDATE below)
					if ($data->getValue('Trashed') == 'no')
					{
						return $this->executeUpdateQuery('
			               	<UPDATE processors="do-not-exec">
			               		<OFFICITY:LIBITEM>
									<INFO>
										<ITEMS operator="--" />
									</INFO>
									<WHERE>
										<DEPENDENCIES>
											<DEPENDENCY type="officity:content">
												<OFFICITY:LIBITEM ID="'.$data->getID().'" />
											</DEPENDENCY>
										</DEPENDENCIES>
									</WHERE>
			               		</OFFICITY:LIBITEM>
				               	</UPDATE>	
							',
							'Items count successfully decremented',
							'Error updating items count: '
						);
					}
					break;
					
				case 'UPDATE':
					// 1st: handle trashed item
					if ($data->getValue('Trashed') !== $data->getFormerValue('Trashed'))
					{
						// out of the trash
						if ($data->getValue('Trashed') == 'no')
						{
							return $this->executeUpdateQuery('
				               	<UPDATE processors="do-not-exec">
				               		<OFFICITY:LIBITEM>
										<INFO>
											<ITEMS operator="++" />
										</INFO>
										<WHERE>
											<DEPENDENCIES>
												<DEPENDENCY type="officity:content">
													<OFFICITY:LIBITEM ID="'.$data->getID().'" />
												</DEPENDENCY>
											</DEPENDENCIES>
										</WHERE>
				               		</OFFICITY:LIBITEM>
				               	</UPDATE>	
								',
								'Items count successfully incremented',
								'Error updating items count: '
							);
						}
						else if ($data->getValue('Trashed') == 'intrash')
						{
							// to the trash
							return $this->executeUpdateQuery('
				               	<UPDATE processors="do-not-exec">
				               		<OFFICITY:LIBITEM>
										<INFO>
											<ITEMS operator="--" />
										</INFO>
										<WHERE>
											<DEPENDENCIES>
												<DEPENDENCY type="officity:content">
													<OFFICITY:LIBITEM ID="'.$data->getID().'" />
												</DEPENDENCY>
											</DEPENDENCIES>
										</WHERE>
				               		</OFFICITY:LIBITEM>
				               	</UPDATE>	
								',
								'Items count successfully decremented',
								'Error updating items count: '
							);
						}
						break;
					}
					
					// 2nd: check dependency to determine what 2 do...
					// handle only the single-dependency-case : append or remove only
					$dependency_updated = $node->getElement('./OFFICITY:LIBITEM/DEPENDENCIES/DEPENDENCY[@type="officity:content" or @type="officity:container"]');
					if ($dependency_updated === false)
					{
						return new SusheeProcessorMessage('updateContainerItemsCount: nothing to do');
					}
					if (count($dependency_updated) > 1)
					{
						return new SusheeProcessorMessage('updateContainerItemsCount: discarded');
					}
					
					$operation = $dependency_updated->valueOf('@operation');
					if (!in_array($operation, array('append', 'remove')))
					{
						return new SusheeProcessorMessage('updateContainerItemsCount: discarded');
					}
					
					$dependency_type = $dependency_updated->valueOf('@type');
					$mode = $dependency_updated->valueOf('@mode');
					if (empty($mode)) $mode = 'normal';
					/*var_dump($dependency_type);
					var_dump($operation);
					var_dump($mode);*/
					
					// 1/4: +1 on the item updated
					if (
						(($dependency_type == 'officity:content') 
							&& ($operation == 'append') 
							&& ($mode == 'normal'))
						||
						(($dependency_type == 'officity:container') 
								&& ($operation == 'append') 
								&& ($mode == 'reverse'))
						)
					{
						return $this->executeUpdateQuery('
			               	<UPDATE processors="do-not-exec">
			               		<OFFICITY:LIBITEM ID="'.$data->getID().'">
									<INFO>
										<ITEMS operator="++" />
									</INFO>
			               		</OFFICITY:LIBITEM>
			               	</UPDATE>	
							',
							'Items count successfully incremented',
							'Error updating items count: '
						);
						break;
					}
					
					// 2/4: -1 on the item updated
					if (
						(($dependency_type == 'officity:content') 
							&& ($operation == 'remove') 
							&& ($mode == 'normal'))
						||
						(($dependency_type == 'officity:container') 
								&& ($operation == 'remove') 
								&& ($mode == 'reverse'))
						)
					{								
						return $this->executeUpdateQuery('
			               	<UPDATE processors="do-not-exec">
			               		<OFFICITY:LIBITEM ID="'.$data->getID().'">
									<INFO>
										<ITEMS operator="--" />
									</INFO>
			               		</OFFICITY:LIBITEM>
			               	</UPDATE>	
							',
							'Items count successfully decremented',
							'Error updating items count: '
						);
						break;
					}
					
					// 3/4: +1 on the item's container
					if (
						(($dependency_type == 'officity:content') 
							&& ($operation == 'append') 
							&& ($mode == 'reverse'))
						||
						(($dependency_type == 'officity:container') 
								&& ($operation == 'append') 
								&& ($mode == 'normal'))
						)
					{
						if ($data->getValue('Trashed') != 'no')
						{
							return new SusheeProcessorMessage('updateContainerItemsCount: nothing to do');
						}
						
						return $this->executeUpdateQuery('
			               	<UPDATE processors="do-not-exec">
			               		<OFFICITY:LIBITEM ID="'.$dependency_updated->valueOf('OFFICITY:LIBITEM/@ID').'">
									<INFO>
										<ITEMS operator="++" />
									</INFO>
			               		</OFFICITY:LIBITEM>
			               	</UPDATE>	
							',
							'Items count successfully incremented',
							'Error updating items count: '
						);
						break;
					}
					
					// 4/4: -1 on the item's container
					if (
						(($dependency_type == 'officity:content') 
							&& ($operation == 'remove') 
							&& ($mode == 'reverse'))
						||
						(($dependency_type == 'officity:container') 
								&& ($operation == 'remove') 
								&& ($mode == 'normal'))
						)
					{
						if ($data->getValue('Trashed') != 'no')
						{
							return new SusheeProcessorMessage('updateContainerItemsCount: nothing to do');
						}
						
						return $this->executeUpdateQuery('
			               	<UPDATE processors="do-not-exec">
			               		<OFFICITY:LIBITEM ID="'.$dependency_updated->valueOf('OFFICITY:LIBITEM/@ID').'">
									<INFO>
										<ITEMS operator="--" />
									</INFO>
			               		</OFFICITY:LIBITEM>
			               	</UPDATE>	
							',
							'Items count successfully decremented',
							'Error updating items count: '
						);
					}
					break;
					
				default:
					return new SusheeProcessorException('updateContainerItemsCount error: Unknown command "'.$command_name.'"');
					break;
			}
		}
		
		// POST UPDATE #3
		public function handleTrashedItems($data)
		{
			if ($data->getValue('Trashed') === $data->getFormerValue('Trashed'))
			{
				return new SusheeProcessorMessage('handleTrashedItems: nothing to do');
			}
			
			$nql = new Sushee_Shell(false);
			
			if (in_array($data->getValue('Trashed'), array('intrash', 'intrasheditem')))
			{
				// -- set children as intrashed items --
				// -- !! only children with no trashed parents !! --
				// -- the processors will update recursively their own childs --
				$nql->addCommand('
					<SEARCH>
						<OFFICITY:LIBITEM>
							<DEPENDENCIES>
								<DEPENDENCY type="officity:container">
									<OFFICITY:LIBITEM ID="'.$data->getID().'" />
								</DEPENDENCY>
							</DEPENDENCIES>
						</OFFICITY:LIBITEM>
						<RETURN>
							<DEPENDENCIES>
								<DEPENDENCY type="officity:container">
									<INFO>
										<TRASHED />
									</INFO>
								</DEPENDENCY>
							</DEPENDENCIES>
						</RETURN>
					</SEARCH>
				');
				$nql->execute();

				$updateNQL = new Sushee_Shell(false);
				$execute_update = false;

				// -- select each child which have all parents in trash --
				$children = $nql->getElements('/RESPONSE/RESULTS/OFFICITY:LIBITEM[not(DEPENDENCIES/DEPENDENCY[@type="officity:container"]/OFFICITY:LIBITEM/INFO/TRASHED = "no")]');
				foreach ($children as $child)
				{	
					$updateNQL->addCommand('
						<UPDATE>
							<OFFICITY:LIBITEM ID="'.$child->valueOf('@ID').'">
								<INFO>
									<TRASHED>intrasheditem</TRASHED>
								</INFO>
							</OFFICITY:LIBITEM>
						</UPDATE>
					');
					$execute_update = true;
				}
				
				if ($execute_update)
				{
					//var_dump($updateNQL->getQuery());
					return $this->executeUpdateQuery(
						$updateNQL,
						'Trashed items successfully handled',
						'Error handling trashed items: '
					);
				}
				
				return new SusheeProcessorMessage('handleTrashedItems: nothing to do');
			}
			else
			{
				// -- set ALL descendants as not in trash --				
				return $this->executeUpdateQuery('
					<UPDATE processors="do-not-exec">
						<OFFICITY:LIBITEM>
							<INFO>
								<TRASHED>no</TRASHED>
							</INFO>
							<WHERE>
								<ANCESTOR type="officity:content">
									<OFFICITY:LIBITEM ID="'.$data->getID().'" />
								</ANCESTOR>
							</WHERE>
						</OFFICITY:LIBITEM>
					</UPDATE>
					',
					'Trashed items successfully handled',
					'Error handling trashed items: '
				);
			}
		}
		
		// PRE KILL/DELETE #2
		public function deleteFileOnServer($data)
		{
			$path = $data->getValue('Path');
			if (empty($path))
			{
				return new SusheeProcessorMessage('deleteFileOnServer: nothing to do');
			}
			
			$file = new File($path);
			if (!$file->exists())
			{
				return new SusheeProcessorException('Error deleting file on server: "'.$path.'" does not exist!');
			}
			
			$folder = $file->getParent();
			if (!is_object($folder)) 
			{
				return new SusheeProcessorException('Error deleting file on server: cannot retrieve parent folder for file "'.$path.'"!');
			}
			
			$folder->delete();
			if ($folder->exists())
			{
				return new SusheeProcessorException('Error deleting file on server: cannot delete parent folder for file "'.$path.'"!');
			}
			
			return new SusheeProcessorMessage('File deleted on server');
		}
	}
?>