<?php
/**
* @version	$Id: upload_formatter.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.
*/

class kUploadFormatter extends kFormatter
{
	var $DestinationPath;
	var $FullPath;

	/**
	 * File helper reference
	 *
	 * @var FileHelper
	 */
	var $fileHelper = null;

	function kUploadFormatter()
	{
		parent::kFormatter();

		$this->fileHelper =& $this->Application->recallObject('FileHelper');

		if ($this->DestinationPath) {
			$this->FullPath = FULL_PATH.$this->DestinationPath;
		}
	}

	/**
	 * Processes file uploads from form
	 *
	 * @param mixed $value
	 * @param string $field_name
	 * @param kDBItem $object
	 * @return string
	 */
	function Parse($value, $field_name, &$object)
	{
		$ret = !is_array($value) ? $value : '';
		$options = $object->GetFieldOptions($field_name);

		if (getArrayValue($options, 'upload_dir')) {
			$this->DestinationPath = $options['upload_dir'];
			$this->FullPath = FULL_PATH.$this->DestinationPath;
		}

		$this->fileHelper->CheckFolder($this->FullPath);

		// SWF Uploader
		if (is_array($value) && isset($value['tmp_ids'])) {
			if ($value['tmp_deleted']) {
				$deleted = explode('|', $value['tmp_deleted']);
				$upload = explode('|', $value['upload']);
				$n_upload = array();
//				$n_ids = array();
				foreach ($upload as $name) {
					if (in_array($name, $deleted)) continue;
					$n_upload[] = $name;
//					$n_ids[] = $name;
				}
				$value['upload'] = implode('|', $n_upload);
//				$value['tmp_ids'] = implode('|', $n_ids);
			}

			if (!$value['tmp_ids']) {
				// no pending files -> return already uploded files
				return getArrayValue($value, 'upload');
			}
			$swf_uploaded_ids = explode('|', $value['tmp_ids']);
			$swf_uploaded_names = explode('|', $value['tmp_names']);
			$existing = $value['upload'] ? explode('|', $value['upload']) : array();
			if (isset($options['multiple'])) {
				$max_files = $options['multiple'] == false ? 1 : $options['multiple'];
			}
			else {
				$max_files = 1;
			}
			$fret = array();

			// don't delete uploaded file, when it's name matches delete file name
			$var_name = $object->getPrefixSpecial().'_file_pending_actions'.$this->Application->GetVar('m_wid');
			$schedule = $this->Application->RecallVar($var_name);
			$schedule = $schedule ? unserialize($schedule) : Array();
			$files2delete = Array();
			foreach ($schedule as $data) {
				if ($data['action'] == 'delete') {
					$files2delete[] = $data['file'];
				}
			}

			for ($i=0; $i<min($max_files, count($swf_uploaded_ids)); $i++) {
				$real_name = $swf_uploaded_names[$i];
				$real_name = $this->_ensureUniqueFilename($this->FullPath, $real_name, $files2delete);
				$file_name = $this->FullPath.$real_name;

				$tmp_file = WRITEABLE . '/tmp/' . $swf_uploaded_ids[$i].'_'.$swf_uploaded_names[$i];
				rename($tmp_file, $file_name);

				@chmod($file_name, 0666);
				$fret[] = getArrayValue($options, 'upload_dir') ? $real_name : $this->DestinationPath.$real_name;
			}
			$fret = array_merge($existing, $fret);
			return implode('|', $fret);
		}

		// SWF Uploader END

		if (getArrayValue($value, 'upload') && getArrayValue($value, 'error') == UPLOAD_ERR_NO_FILE) {
			// file was not uploaded this time, but was uploaded before, then use previously uploaded file (from db)
			return getArrayValue($value, 'upload');
		}

		if (is_array($value) && count($value) > 1 && $value['size']) {
			if (is_array($value) && $value['error'] === UPLOAD_ERR_OK) {
				$max_filesize = isset($options['max_size']) ? $options['max_size'] : MAX_UPLOAD_SIZE;

				if (getArrayValue($options, 'allowed_types') && !in_array($value['type'], $options['allowed_types'])) {
					$error_params = Array (
						'file_type' => $value['type'],
						'allowed_types' => $options['allowed_types'],
					);
					$object->SetError($field_name, 'bad_file_format', 'la_error_InvalidFileFormat', $error_params);
				}
				elseif ($value['size'] > $max_filesize) {
					$object->SetError($field_name, 'bad_file_size', 'la_error_FileTooLarge');
				}
				elseif (!is_writable($this->FullPath)) {
					$object->SetError($field_name, 'cant_save_file', 'la_error_cant_save_file');
				}
				else {
					$real_name = $this->_ensureUniqueFilename($this->FullPath, $value['name']);
					$file_name = $this->FullPath.$real_name;
					if (!move_uploaded_file($value['tmp_name'], $file_name)) {
						$object->SetError($field_name, 'cant_save_file', 'la_error_cant_save_file');
					}
					else {
						@chmod($file_name, 0666);
						if (getArrayValue($options, 'size_field')) {
							$object->SetDBField($options['size_field'], $value['size']);
						}
						if (getArrayValue($options, 'orig_name_field')) {
							$object->SetDBField($options['orig_name_field'], $value['name']);
						}
						if (getArrayValue($options, 'content_type_field')) {
							$object->SetDBField($options['content_type_field'], $value['type']);
						}
						$ret = getArrayValue($options, 'upload_dir') ? $real_name : $this->DestinationPath.$real_name;

						// delete previous file, when new file is uploaded under same field
						/*$previous_file = isset($value['upload']) ? $value['upload'] : false;
						if ($previous_file && file_exists($this->FullPath.$previous_file)) {
							unlink($this->FullPath.$previous_file);
						}*/
					}
				}
			}
			else {
				$object->SetError($field_name, 'cant_save_file', 'la_error_cant_save_file');
			}
		}

		if ((count($value) > 1) && $value['error'] && ($value['error'] != UPLOAD_ERR_NO_FILE)) {
			$object->SetError($field_name, 'cant_save_file', 'la_error_cant_save_file', $value);
		}

		return $ret;
	}

	function getSingleFormat($format)
	{
		$single_mapping = Array (
			'file_urls' => 'full_url',
			'file_paths' => 'full_path',
			'file_sizes' => 'file_size',
			'files_resized' => 'resize',
			'img_sizes' => 'img_size',
			'wms' => 'wm',
		);

		return $single_mapping[$format];
	}

	/**
	 * Return formatted file url,path or size (or same for multiple files)
	 *
	 * @param string $value
	 * @param string $field_name
	 * @param kDBItem $object
	 * @param string $format
	 * @return string
	 */
	function Format($value, $field_name, &$object, $format = null)
	{
		if (is_null($value)) {
			return '';
		}

		$options = $object->GetFieldOptions($field_name);
		if (!isset($format)) {
			$format = isset($options['format']) ? $options['format'] : false;
		}

		if ($format && preg_match('/(file_urls|file_paths|file_names|file_sizes|img_sizes|files_resized|wms)(.*)/', $format, $regs)) {
			if (!$value || $format == 'file_names') {
				// storage format matches display format OR no value
				return $value;
			}

			$ret = Array ();
			$files = explode('|', $value);
			$format = $this->getSingleFormat($regs[1]).$regs[2];

			foreach ($files as $a_file) {
				$ret[] = $this->GetFormatted($a_file, $field_name, $object, $format);
			}

			return implode('|', $ret);
		}

		$tc_value = $this->TypeCast($value, $options);
		if( ($tc_value === false) || ($tc_value != $value) ) return $value; // for leaving badly formatted date on the form

		$object->Fields[$field_name]['direct_links'] = true; // for case, when non-swf uploader is used
		return $this->GetFormatted($tc_value, $field_name, $object, $format);
	}

	/**
	 * Return formatted file url,path or size
	 *
	 * @param string $value
	 * @param string $field_name
	 * @param kDBItem $object
	 * @param string $format
	 * @return string
	 */
	function GetFormatted($value, $field_name, &$object, $format = null)
	{
		if (!$format) {
			return $value;
		}

		$options = $object->GetFieldOptions($field_name);
		$upload_dir = isset($options['upload_dir']) ? $options['upload_dir'] : $this->DestinationPath;

		if (preg_match('/resize:([\d]*)x([\d]*)/', $format, $regs)) {
			$image_helper =& $this->Application->recallObject('ImageHelper');
			/* @var $image_helper ImageHelper */

			if (array_key_exists('include_path', $options) && $options['include_path']) {
				// relative path is already included in field
				$upload_dir = '';
			}

			return $image_helper->ResizeImage($value ? FULL_PATH . str_replace('/', DIRECTORY_SEPARATOR, $upload_dir) . $value : '', $format);
		}

		switch ($format) {
			case 'full_url':
				if (isset($options['direct_links']) && $options['direct_links']) {
					return $this->fileHelper->pathToUrl(FULL_PATH . $upload_dir . $value);
				}
				else {
					$url_params = Array (
						'no_amp' => 1, 'pass' => 'm,'.$object->Prefix,
						$object->Prefix . '_event' => 'OnViewFile',
						'file' => rawurlencode($value), 'field' => $field_name
					);

					return $this->Application->HREF('', '', $url_params);
				}
				break;

			case 'full_path':
				return FULL_PATH . str_replace('/', DIRECTORY_SEPARATOR, $upload_dir) . $value;
				break;

			case 'file_size':
				return filesize(FULL_PATH . str_replace('/', DIRECTORY_SEPARATOR, $upload_dir) . $value);
				break;

			case 'img_size':
				$image_helper =& $this->Application->recallObject('ImageHelper');
				/* @var $image_helper ImageHelper */

				$image_info = $image_helper->getImageInfo(FULL_PATH . str_replace('/', DIRECTORY_SEPARATOR, $upload_dir) . $value);
				return $image_info ? $image_info[3] : '';
				break;
		}

		return sprintf($format, $value);
	}

	/**
	 * Ensures, that uploaded file will not overwrite any of previously uploaded files with same name
	 *
	 * @param string $path
	 * @param string $name
	 * @param Array $forbidden_names
	 * @return string
	 */
	function _ensureUniqueFilename($path, $name, $forbidden_names = Array())
	{
		$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) || in_array(rtrim($path, '/') . '/' . $new_name, $forbidden_names)) {
	   		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;
	}
}

class kPictureFormatter extends kUploadFormatter
{
	function kPictureFormatter()
	{
		$this->NakeLookupPath = IMAGES_PATH; // used ?
		$this->DestinationPath = constOn('ADMIN') ? IMAGES_PENDING_PATH : IMAGES_PATH;

		parent::kUploadFormatter();
	}

	function GetFormatted($value, $field_name, &$object, $format = null)
	{
		if ($format == 'img_size') {
			$upload_dir = isset($options['upload_dir']) ? $options['upload_dir'] : $this->DestinationPath;
			$img_path = FULL_PATH.'/'.$upload_dir.$value;

			$image_info = @getimagesize($img_path);
			return ' width="'.$image_info[0].'" height="'.$image_info[1].'"';
		}

		return parent::GetFormatted($value, $field_name, $object, $format);
	}
}