<?php
/**
* @version	$Id: image_event_handler.php 14241 2011-03-16 20:24:35Z alex $
* @package	In-Portal
* @copyright	Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license      GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/

defined('FULL_PATH') or die('restricted access!');

class ImageEventHandler extends kDBEventHandler {

	function mapPermissions()
	{
		parent::mapPermissions();

		$permissions = Array (
			'OnCleanImages' => Array ('subitem' => true),
			'OnCleanResizedImages' => Array ('subitem' => true),
		);

		$this->permMapping = array_merge($this->permMapping, $permissions);
	}


	function mapEvents()
	{
		parent::mapEvents();	// ensure auto-adding of approve/decine and so on events
		$image_events = Array(
			'OnAfterCopyToTemp'=>'ImageAction',
			'OnBeforeDeleteFromLive'=>'ImageAction',
			'OnBeforeCopyToLive'=>'ImageAction',
			'OnBeforeItemDelete'=>'ImageAction',
			'OnAfterClone'=>'ImageAction',
		);

		$this->eventMethods = array_merge($this->eventMethods, $image_events);
	}

	/**
	 * Get's special of main item for linking with subitem
	 *
	 * @param kEvent $event
	 * @return string
	 */
	function getMainSpecial(&$event)
	{
		if ($event->Special == 'list' && !$this->Application->isAdmin) {
			// ListImages aggregated tag uses this special
			return '';
		}

		return parent::getMainSpecial($event);
	}

	function customProcessing(&$event, $type)
	{
		$object =& $event->GetObject();
		switch ($type)
		{
			case 'before' :
				if ($object->GetDBField('LocalImage'))
				{
					$object->SetDBField('Url', '');
				}
				else
				{
					$object->SetDBField('LocalPath', '');
				}

				if ($object->GetDBField('LocalThumb'))
				{
					$object->SetDBField('ThumbUrl', '');
				}
				else
				{
					$object->SetDBField('ThumbPath', '');
				}

				if ($object->GetDBField('SameImages'))
				{
					$object->SetDBField('LocalImage', 1);
					$object->SetDBField('LocalPath', '');
					$object->SetDBField('Url', '');
				}
				break;
			case 'after' :
				if ($object->GetDBField('DefaultImg') )
				{
					$sql = 'UPDATE '.$object->TableName.' SET DefaultImg=0 WHERE ResourceId='.
							$object->GetDBField('ResourceId').' AND ImageId<>'.
							$object->GetId();
					$res = $this->Conn->Query($sql);
				}
				break;
			default:
		}
	}

	function ImageAction(&$event)
	{
		$id = $event->getEventParam('id');
		$object =& $this->Application->recallObject($event->Prefix.'.-item', $event->Prefix, Array ('skip_autoload' => true));
		if (in_array($event->Name, Array('OnBeforeDeleteFromLive','OnAfterClone')) ) {
			$object->SwitchToLive();
		}
		elseif ($event->Name == 'OnBeforeItemDelete') {
			// keep current table
		}
		else {
			$object->SwitchToTemp();
		}

		$object->Load($id);

		$fields = Array('LocalPath' => 'LocalImage', 'ThumbPath' => 'LocalThumb');
		foreach ($fields as $a_field => $mode_field) {
			$file = $object->GetField($a_field);
			if (!$file) continue;
			$source_file = FULL_PATH.$file;

			switch ($event->Name) {
				// Copy image files to pending dir and update corresponding fields in temp record
				// Checking for existing files and renaming if nessessary - two users may upload same pending files at the same time!
				case 'OnAfterCopyToTemp':
					$new_file = IMAGES_PENDING_PATH . $this->ValidateFileName(FULL_PATH.IMAGES_PENDING_PATH, basename($file));
					$dest_file = FULL_PATH.$new_file;
					copy($source_file, $dest_file);
					$object->Fields[$a_field]['skip_empty'] = false;
					$object->SetDBField($a_field, $new_file);
					break;

				// Copy image files to live dir (checking if fileexists and renameing if nessessary)
				// and update corresponding fields in temp record (which gets copied to live automatically)
				case 'OnBeforeCopyToLive':
					if ( $object->GetDBField($mode_field) )  { // if image is local
						// rename file if it exists in live folder
						$new_file = IMAGES_PATH . $this->ValidateFileName(FULL_PATH.IMAGES_PATH, basename($file));
						$dest_file = FULL_PATH.$new_file;
						rename($source_file, $dest_file);
					}
					else { // if image is remote url - remove local file (if any), update local file field with empty value
						if (file_exists($source_file)) @unlink($source_file);
						$new_file = '';
					}
					$object->Fields[$a_field]['skip_empty'] = false;
					$object->SetDBField($a_field, $new_file);
					break;

				case 'OnBeforeDeleteFromLive': // Delete image files from live folder before copying over from temp
				case 'OnBeforeItemDelete': // Delete image files when deleteing Image object
					@unlink(FULL_PATH.$file);
					break;

				case 'OnAfterClone': // Copy files when cloning objects, renaming it on the fly
					$path_info = pathinfo($file);
					$new_file = $path_info['dirname'].'/'.$this->ValidateFileName(FULL_PATH.$path_info['dirname'], $path_info['basename']);
					$dest_file = FULL_PATH . $new_file;
					copy($source_file, $dest_file);
					$object->Fields[$a_field]['skip_empty'] = false;
					$object->SetDBField($a_field, $new_file);
					break;
			}
		}
		if ( in_array($event->Name, Array('OnAfterClone', 'OnBeforeCopyToLive', 'OnAfterCopyToTemp')) ) {
			$object->Update(null, true);
		}
	}

	function ValidateFileName($path, $name)
	{
		$parts = pathinfo($name);
		$ext = '.'.$parts['extension'];
		$filename = mb_substr($parts['basename'], 0, -mb_strlen($ext));
		$new_name = $filename.$ext;
		while ( file_exists($path.'/'.$new_name) )
		{
	   		if ( preg_match('/('.preg_quote($filename, '/').'_)([0-9]*)('.preg_quote($ext, '/').')/', $new_name, $regs) ) {
				$new_name = $regs[1].($regs[2]+1).$regs[3];
			}
			else {
				$new_name = $filename.'_1'.$ext;
			}
		}
		return $new_name;
	}

		/**
	 * Enter description here...
	 *
	 * @param kEvent $event
	 */
	function OnSetPrimary(&$event)
	{
		$object =& $event->getObject();
		$object->SetDBField('DefaultImg', 1);
		$object->Update();
	}

	/**
	 * Enter description here...
	 *
	 * @param kEvent $event
	 */
	function OnBeforeItemUpdate(&$event)
	{
		$object =& $event->getObject();
//		$parent_info = $object->getLinkedInfo();
		$id = $object->GetDBField('ResourceId');
//		$id = $parent_info['ParentId'] ? $parent_info['ParentId'] : $this->Application->GetVar('p_id');
		$sql = 'SELECT ImageId FROM '.$object->TableName.' WHERE ResourceId='.$id.' AND DefaultImg=1';
		if(!$this->Conn->GetOne($sql))
		{
			$object->SetDBField('DefaultImg', 1);
		}
		if($object->GetDBField('DefaultImg') && $object->Validate())
		{

			$sql = 'UPDATE '.$object->TableName.'
					SET DefaultImg = 0
					WHERE ResourceId = '.$id.' AND ImageId <> '.$object->GetDBField('ImageId');
			$this->Conn->Query($sql);
			$object->SetDBField('Enabled', 1);
		}
	}

	function OnAfterItemCreate(&$event)
	{
		$event->CallSubEvent('OnBeforeItemUpdate');
		$object =& $event->getObject();
		$object->Update();
	}

	/**
	 * Deletes all selected items.
	 * Automatically recurse into sub-items using temp handler, and deletes sub-items
	 * by calling its Delete method if sub-item has AutoDelete set to true in its config file
	 *
	 * @param kEvent $event
	 */
	function OnMassDelete(&$event)
	{
		if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
			$event->status = erFAIL;
			return;
		}

		$event->status=erSUCCESS;

		$temp =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler');

		$event->setEventParam('ids', $this->StoreSelectedIDs($event) );
		$this->customProcessing($event, 'before');
		$ids = $event->getEventParam('ids');

		$object =& $event->getObject();
		$sql = 'SELECT ImageId FROM '.$object->TableName.' WHERE DefaultImg=1';
		$primary = $this->Conn->GetOne($sql);
		if( $primary && ($key = array_search($primary, $ids)) )
		{
			$sql = 'SELECT ImageId FROM '.$object->TableName.' WHERE DefaultImg=0';
			$res = $this->Conn->Query($sql);
			if($res)
			{
				unset($ids[$key]);
			}
		}

		if($ids)
		{
			$temp->DeleteItems($event->Prefix, $event->Special, $ids);
		}
		$this->clearSelectedIDs($event);
	}

	/*function OnAfterItemLoad(&$event)
	{
		$object =& $event->getObject();

		if ( $object->GetDBField('ThumbPath') || $object->GetDBField('SameImages') )
		{
			// return local image or url
			$path = $object->GetDBField('LocalThumb') ? PROTOCOL.SERVER_NAME.BASE_PATH.$object->GetDBField('ThumbPath') : $object->GetDBField('ThumbUrl');
			if ( $object->GetDBField('LocalThumb') && !file_exists(FULL_PATH.$object->GetDBField('ThumbPath')) ) $path = '';
		}
		else { // if we need full which is not the same as thumb
			$path = $object->GetDBField('LocalImage') ? PROTOCOL.SERVER_NAME.BASE_PATH.$object->GetDBField('LocalPath') : $object->GetDBField('Url');
			if ( $object->GetDBField('LocalImage') && !file_exists(FULL_PATH.$object->GetDBField('LocalPath')) ) $path = '';
		}

		$object->SetDBField('ImageUrl', $path);
	}*/

	function SetCustomQuery(&$event)
	{
		parent::SetCustomQuery($event);

		$types=$event->getEventParam('types');
		$except_types=$event->getEventParam('except');
		$object =& $event->getObject();
		$type_clauses = Array();

		if (!$this->Application->isAdminUser) {
			$object->addFilter('active', '%1$s.Enabled = 1');
		}

		if($product_id = $event->getEventParam('product_id'))
		{
			$object->removeFilter('parent_filter');
			$sql = 'SELECT ResourceId FROM '.$this->Application->getUnitOption('p', 'TableName').'
					WHERE ProductId = '.$product_id;
			$resource_id = (int) $this->Conn->GetOne($sql);
			$object->addFilter('product_images', '%1$s.ResourceId = '.$resource_id);
		}

		$type_clauses['additional']['include'] = '%1$s.DefaultImg != 1';
		$type_clauses['additional']['except'] = '%1$s.DefaultImg = 1';
		$type_clauses['additional']['having_filter'] = false;

		/********************************************/

		$includes_or_filter =& $this->Application->makeClass('kMultipleFilter');
		$includes_or_filter->setType(FLT_TYPE_OR);

		$excepts_and_filter =& $this->Application->makeClass('kMultipleFilter');
		$excepts_and_filter->setType(FLT_TYPE_AND);

		$includes_or_filter_h =& $this->Application->makeClass('kMultipleFilter');
		$includes_or_filter_h->setType(FLT_TYPE_OR);

		$excepts_and_filter_h =& $this->Application->makeClass('kMultipleFilter');
		$excepts_and_filter_h->setType(FLT_TYPE_AND);

		$except_types_array=explode(',', $types);

		if ($types){
			$types_array=explode(',', $types);
			for ($i=0; $i<sizeof($types_array); $i++){
				$type=trim($types_array[$i]);
				if (isset($type_clauses[$type])){
					if ($type_clauses[$type]['having_filter']){
						$includes_or_filter_h->removeFilter('filter_'.$type);
						$includes_or_filter_h->addFilter('filter_'.$type, $type_clauses[$type]['include']);
					}else{
						$includes_or_filter->removeFilter('filter_'.$type);
						$includes_or_filter->addFilter('filter_'.$type, $type_clauses[$type]['include']);
					}
				}
			}
		}

		if ($except_types){
			$except_types_array=explode(',', $except_types);
			for ($i=0; $i<sizeof($except_types_array); $i++){
				$type=trim($except_types_array[$i]);
				if (isset($type_clauses[$type])){
					if ($type_clauses[$type]['having_filter']){
						$excepts_and_filter_h->removeFilter('filter_'.$type);
						$excepts_and_filter_h->addFilter('filter_'.$type, $type_clauses[$type]['except']);
					}else{
						$excepts_and_filter->removeFilter('filter_'.$type);
						$excepts_and_filter->addFilter('filter_'.$type, $type_clauses[$type]['except']);
					}
				}
			}
		}

		$object->addFilter('includes_filter', $includes_or_filter);
		$object->addFilter('excepts_filter', $excepts_and_filter);

		$object->addFilter('includes_filter_h', $includes_or_filter_h, HAVING_FILTER);
		$object->addFilter('excepts_filter_h', $excepts_and_filter_h, HAVING_FILTER);
	}

	/**
	 * [AGENT] Remove unused images from "/system/images" and "/system/images/pending" folders
	 *
	 * @param kEvent $event
	 */
	function OnCleanImages(&$event)
	{
		// 1. get images, that are currently in use
		$active_images = $this->_getActiveImages( $this->Application->getUnitOption('img', 'TableName') );
		$active_images[] = 'noimage.gif';

		// 2. get images on disk
		$this->_deleteUnusedImages(FULL_PATH . IMAGES_PATH, $active_images);

		// 3. get images in use from "images/pending" folder
		$active_images = $this->_getPendingImages();

		// 4. get image on disk
		$this->_deleteUnusedImages(FULL_PATH . IMAGES_PENDING_PATH, $active_images);
	}

	/**
	 * Gets image filenames (no path) from given table
	 *
	 * @param string $image_table
	 * @return Array
	 */
	function _getActiveImages($image_table)
	{
		$sql = 'SELECT LocalPath, ThumbPath
				FROM ' . $image_table . '
				WHERE COALESCE(LocalPath, "") <> "" OR COALESCE(ThumbPath) <> ""';
		$images = $this->Conn->Query($sql);

		$active_images = Array ();
		foreach ($images as $image) {
			if ($image['LocalPath']) {
				$active_images[] = basename($image['LocalPath']);
			}

			if ($image['ThumbPath']) {
				$active_images[] = basename($image['ThumbPath']);
			}
		}

		return $active_images;
	}

	/**
	 * Gets active images, that are currently beeing edited inside temporary tables
	 *
	 * @return Array
	 */
	function _getPendingImages()
	{
		$tables = $this->Conn->GetCol('SHOW TABLES');
		$mask_edit_table = '/'.TABLE_PREFIX.'ses_(.*)_edit_' . TABLE_PREFIX . 'Images/';

		$active_images = Array ();

		foreach ($tables as $table) {
			if (!preg_match($mask_edit_table, $table)) {
				continue;
			}

			$active_images = array_unique( array_merge($active_images, $this->_getActiveImages($table)) );
		}

		return $active_images;
	}

	/**
	 * Deletes all files in given path, except of given $active_images
	 *
	 * @param string $path
	 * @param Array $active_images
	 */
	function _deleteUnusedImages($path, &$active_images)
	{
		$images = glob($path . '*.*');
		if ($images) {
			$images = array_map('basename', $images);

			// delete images, that are on disk, but are not mentioned in Images table
			$delete_images = array_diff($images, $active_images);
			foreach ($delete_images as $delete_image) {
				unlink($path . $delete_image);
			}
		}
	}

	/**
	 * [AGENT] Remove all images from "/system/images/resized" and "/system/images/pending/resized" folders
	 *
	 * @param kEvent $event
	 */
	function OnCleanResizedImages(&$event)
	{
		$images = glob(FULL_PATH . IMAGES_PATH . 'resized/*.*');
		if ($images) {
			foreach ($images as $image) {
				unlink($image);
			}
		}

		$images = glob(FULL_PATH . IMAGES_PENDING_PATH . 'resized/*.*');
		if ($images) {
			foreach ($images as $image) {
				unlink($image);
			}
		}
	}
}