Your IP : 216.73.216.43


Current Path : /home/cbequiperp/www/administrator/components/com_flexicontent/controllers/
Upload File :
Current File : /home/cbequiperp/www/administrator/components/com_flexicontent/controllers/filemanager.php

<?php
/**
 * @package         FLEXIcontent
 * @version         3.3
 *
 * @author          Emmanuel Danan, Georgios Papadakis, Yannick Berges, others, see contributor page
 * @link            https://flexicontent.org
 * @copyright       Copyright © 2018, FLEXIcontent team, All Rights Reserved
 * @license         http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
 */

defined('_JEXEC') or die;

use Joomla\String\StringHelper;
use Joomla\Utilities\ArrayHelper;

JLoader::register('FlexicontentControllerBaseAdmin', JPATH_ADMINISTRATOR . DS . 'components' . DS . 'com_flexicontent' . DS . 'controllers' . DS . 'base' . DS . 'baseadmin.php');

// Manually import models in case used by frontend, then models will not be autoloaded correctly via getModel('name')
require_once JPATH_ADMINISTRATOR . DS . 'components' . DS . 'com_flexicontent' . DS . 'models' . DS . 'file.php';
require_once JPATH_ADMINISTRATOR . DS . 'components' . DS . 'com_flexicontent' . DS . 'models' . DS . 'filemanager.php';

/**
 * FLEXIcontent Files Controller
 *
 * NOTE: -Only- if this controller is needed by frontend URLs, then create a derived controller in frontend 'controllers' folder
 *
 * @since 3.3
 */
class FlexicontentControllerFilemanager extends FlexicontentControllerBaseAdmin
{
	var $records_dbtbl  = 'flexicontent_files';
	var $records_jtable = 'flexicontent_files';

	var $record_name = 'file';
	var $record_name_pl = 'filemanager';

	var $_NAME = 'FILE';
	var $record_alias = 'filename';

	var $runMode = 'standalone';

	var $exitHttpHead = null;
	var $exitMessages = array();
	var $exitLogTexts = array();
	var $exitSuccess  = true;

	/**
	 * Constructor
	 *
	 * @param   array   $config    associative array of configuration settings.
	 *
	 * @since 3.3
	 */
	public function __construct($config = array())
	{
		parent::__construct($config);

		/**
		 * Register task aliases
		 */
		$this->registerTask('uploads', 	'upload');
		$this->registerTask('apply',        'save');
		$this->registerTask('apply_ajax',   'save');
		$this->registerTask('save2new',     'save');
		$this->registerTask('save2copy',    'save');

		$this->option = $this->input->get('option', '', 'cmd');
		$this->task   = $this->input->get('task', '', 'cmd');
		$this->view   = $this->input->get('view', '', 'cmd');
		$this->format = $this->input->get('format', '', 'cmd');

		// Can manage ACL
		$this->canManage = FlexicontentHelperPerm::getPerm()->CanFiles;
	}


	/**
	 * Logic to save a record
	 *
	 * @return void
	 *
	 * @since 3.3
	 */
	public function save()
	{
		// Check for request forgeries
		JSession::checkToken('request') or die(JText::_('JINVALID_TOKEN'));

		// Initialize variables
		$app     = JFactory::getApplication();
		$user    = JFactory::getUser();
		$params  = JComponentHelper::getParams('com_flexicontent');

		$original_task = $this->task;

		// Retrieve form data these are subject to basic filtering
		$data  = $this->input->get('jform', array(), 'array');  // Unfiltered data, validation will follow via jform

		// Set into model: id (needed for loading correct item), and type id (e.g. needed for getting correct type parameters for new items)
		$data['id'] = $data ? (int) $data['id'] : $this->input->get('id', 0, 'int');
		$isnew = $data['id'] == 0;

		// Extra steps before creating the model
		if ($isnew)
		{
			// Nothing needed
		}

		// Get the model
		$model = $this->getModel($this->record_name);

		// Make sure Primary Key is correctly set into the model ... (needed for loading correct item)
		$model->setId($data['id']);
		$record = $model->getItem();

		// The save2copy task needs to be handled slightly differently.
		if ($this->task === 'save2copy')
		{
			// Check-in the original row.
			if ($model->checkin($data['id']) === false)
			{
				// Check-in failed
				$this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_CHECKIN_FAILED', $model->getError()));
				$this->setMessage($this->getError(), 'error');

				/**
				 * For errors, redirect back to referer, instead of the return URL, but skip redirection if inside a
				 * component-area-only view, showing error using current page, since usually we are inside a iframe modal
				 */
				if ($this->input->getCmd('tmpl') !== 'component')
				{
					$this->setRedirect($this->refererURL);
				}

				if ($this->input->get('fc_doajax_submit'))
				{
					jexit(flexicontent_html::get_system_messages_html());
				}
				else
				{
					return false;
				}
			}

			// Reset the ID, the multilingual associations and then treat the request as for Apply.
			$isnew = 1;
			$data['id'] = 0;
			$data['associations'] = array();
			$this->task = 'apply';

			// Keep existing model data (only clear ID)
			$model->set('id', 0);
			$model->setProperty('_id', 0);
		}

		// The apply_ajax task is treat same as apply (also same redirection in case that AJAX submit is skipped)
		elseif ($this->task === 'apply_ajax')
		{
			$this->task = 'apply';
		}

		// Calculate access
		$is_authorised = $model->canEdit($record);

		// Check access
		if (!$is_authorised)
		{
			$app->setHeader('status', '403 Forbidden', true);
			$app->enqueueMessage(JText::_('FLEXI_ALERTNOTAUTH_TASK'), 'error');

			// Skip redirection back to return url if inside a component-area-only view, showing error using current page, since usually we are inside a iframe modal
			if ($this->input->getCmd('tmpl') !== 'component')
			{
				$this->setRedirect($this->returnURL);
			}

			if ($this->input->get('fc_doajax_submit'))
			{
				jexit(flexicontent_html::get_system_messages_html());
			}
			else
			{
				return false;
			}
		}

		// Validation with JForm
		$data = $this->input->post->getArray();  // Default filtering will remove HTML
		$data['description'] = flexicontent_html::dataFilter($data['description'], 32000, 'STRING', 0);  // Limit description to 32000 characters
		$data['hits'] = (int) $data['hits'];
		$data['secure'] = $data['secure'] ? 1 : 0;   // Only allow 1 or 0
		$data['stamp']  = $data['stamp'] ? 1 : 0;   // only allow 1 or 0
		$data['url']    = in_array((int) $data['url'], array(0, 1, 2)) ? (int) $data['url'] : 0;   // only allow 2 or 1 or 0

		// Get extensions allowed by configuration, and intersect them with desired extensions
		$allowed_exts = preg_split("/[\s]*,[\s]*/", strtolower($params->get('upload_extensions', 'bmp,wbmp,csv,doc,docx,webp,gif,ico,jpg,jpeg,odg,odp,ods,odt,pdf,png,ppt,pptx,txt,xcf,xls,xlsx,zip,ics')));
		$allowed_exts = array_flip($allowed_exts);

		// Get the extension to record it in the DB
		$ext = strtolower(flexicontent_upload::getExt($data['filename']));

		if (!isset($allowed_exts[$ext]))
		{
			$app->setHeader('status', '403 Forbidden', true);
			$app->enqueueMessage(JText::_('File extension not allowed'), 'error');

			// Skip redirection back to return url if inside a component-area-only view, showing error using current page, since usually we are inside a iframe modal
			if ($this->input->getCmd('tmpl') !== 'component')
			{
				$this->setRedirect($this->returnURL);
			}

			if ($this->input->get('fc_doajax_submit'))
			{
				jexit(flexicontent_html::get_system_messages_html());
			}
			else
			{
				return false;
			}
		}

		switch ($data['url'])
		{
			// CASE local file
			case 0:
				$path = $data['secure'] ? COM_FLEXICONTENT_FILEPATH . DS : COM_FLEXICONTENT_MEDIAPATH . DS;  // JPATH_ROOT . DS . <media_path | file_path> . DS
				$file_path = JPath::clean($path . $data['filename']);

				// Get file size from filesystem (local file)
				$data['size'] = file_exists($file_path) ? filesize($file_path) : 0;
				break;

			// CASE file URL
			case 1:

				// Validate file URL
				$data['filename_original'] = flexicontent_html::dataFilter($data['filename_original'], 4000, 'STRING', 0);  // Clean bad text/html
				$data['filename'] = $url = flexicontent_html::dataFilter($data['filename'], 4000, 'URL', 0);  // Clean bad text/html

				// Get file size from submitted field (file URL), set to zero if no size unit specified
				if (!empty($data['size']))
				{
					$arr_sizes = array('KBs' => 1024, 'MBs' => (1024 * 1024), 'GBs' => (1024 * 1024 * 1024));
					$size_unit = (int) @ $arr_sizes[$data['size_unit']];
					$data['size'] = ((int) $data['size']) * $size_unit;
				}

				else
				{
					$data['size'] = $model->get_file_size_from_url($url);

					if ($data['size'] === -999)
					{
						$app->enqueueMessage($url . ' -- ' . $model->getError(), 'warning');
					}

					$data['size'] = $data['size'] < 0 ? 0 : $data['size'];
				}
				break;

			// CASE JMedia file PATH
			case 2:

				// Validate file PATH
				$data['filename_original'] = flexicontent_html::dataFilter($data['filename_original'], 4000, 'STRING', 0);  // Clean bad text/html
				$data['filename'] = flexicontent_html::dataFilter($data['filename'], 4000, 'PATH', 0);  // Clean bad text/html

				$file_path = JPath::clean(JPATH_ROOT . DS . $data['filename']);

				// Get file size from filesystem (local file)
				$data['size'] = file_exists($file_path) ? filesize($file_path) : 0;
				break;
		}

		// Validate access level exists (set to public otherwise)
		$data['access'] = flexicontent_html::dataFilter($data['access'], 11, 'ACCESSLEVEL', 0);

		if (!$model->store($data))
		{
			// Set error message and the redirect URL (back to the record form)
			$app->setHeader('status', '500 Internal Server Error', true);
			$this->setError($model->getError() ?: JText::_('FLEXI_ERROR_SAVING_' . $this->_NAME));
			$this->setMessage($this->getError(), 'error');

			// For errors, skip redirection if in a component-area-only view, showing error using current page, since usually we are inside a iframe modal
			if ($this->input->getCmd('tmpl') !== 'component')
			{
				$this->setRedirect($this->refererURL);
			}

			// Try to check-in the record, but ignore any new errors
			try
			{
				!$isnew ? $model->checkin() : true;
			}
			catch (Exception $e)
			{
			}

			if ($this->input->get('fc_doajax_submit'))
			{
				jexit(flexicontent_html::get_system_messages_html());
			}
			else
			{
				return false;
			}
		}

		// Clear dependent cache data
		$this->_cleanCache();

		// Check in the record and get record id in case of new item
		$model->checkin();


		/**
		 * Saving is done, decide where to redirect
		 */

		$msg = JText::_('FLEXI_' . $this->_NAME . '_SAVED');

		switch ($this->task)
		{
			// REDIRECT CASE FOR APPLY / SAVE AS COPY: Save and reload the edit form
			case 'apply':
				$link = 'index.php?option=com_flexicontent&view=' . $this->record_name . '&id=' . (int) $model->get('id');
				break;

			// REDIRECT CASE FOR SAVE and NEW: Save and load new record form
			case 'save2new':
				$link = 'index.php?option=com_flexicontent&view=' . $this->record_name
					. '';
				break;

			// REDIRECT CASES FOR SAVING
			default:
				$link = $this->returnURL;
				break;
		}

		$app->enqueueMessage($msg, 'message');
		$this->setRedirect($link);

		// return;  // comment above and decomment this one to profile the saving operation

		if ($this->input->get('fc_doajax_submit'))
		{
			jexit(flexicontent_html::get_system_messages_html());
		}
	}


	/**
	 * Check in a record
	 *
	 * @since	3.3
	 */
	public function checkin()
	{
		parent::checkin();
	}


	/**
	 * Cancel the edit, check in the record and return to the records manager
	 *
	 * @return bool
	 *
	 * @since 3.3
	 */
	public function cancel()
	{
		return parent::cancel();
	}


	/**
	 * Logic to publish records, this WRAPPER for changestate method
	 *
	 * @return void
	 *
	 * @since 3.3
	 */
	public function publish()
	{
		$this->changestate(1);   // Security checks are done by the called method
	}


	/**
	 * * Logic to unpublish records, this WRAPPER for changestate method
	 *
	 * @return void
	 *
	 * @since 3.3
	 */
	public function unpublish()
	{
		$this->changestate(0);   // Security checks are done by the called method
	}


	/**
	 * Upload files
	 *
	 * @since 1.0
	 */
	function upload($Fobj = null, & $exitMessages = null)
	{
		// Check for request forgeries
		JSession::checkToken('request') or die(JText::_('JINVALID_TOKEN'));

		$app   = JFactory::getApplication();
		$user  = JFactory::getUser();
		$db    = JFactory::getDbo();
		$session = JFactory::getSession();

		// Force interactive run mode, if given parameters
		$this->runMode = $Fobj ? 'interactive' : $this->runMode;
		$file_id = 0;

		// Force JSON format for 'uploads' task
		$this->format = $this->format != '' ? $this->format : ($this->task === 'uploads' ? 'json' : 'html');

		// Calculate access
		$canuploadfile = $user->authorise('flexicontent.uploadfiles', 'com_flexicontent');
		$is_authorised = $canuploadfile;

		// Check access
		if (!$is_authorised)
		{
			$this->exitHttpHead = array( 0 => array('status' => '403 Forbidden') );
			$this->exitMessages = array( 0 => array('error' => 'FLEXI_YOUR_ACCOUNT_CANNOT_UPLOAD') );
			$this->exitLogTexts = array();
			$this->exitSuccess  = false;

			return $this->terminate($file_id, $exitMessages);
		}

		$file_row_id = $data_from_sess = $uploader_file_data = false;

		if ($this->task === 'uploads')
		{
			$file = $this->input->files->get('file', '', 'array');
			$file_row_id = $this->input->get('file_row_id', '', 'string');

			$uploader_file_data = $session->get('uploader_file_data', array(), 'flexicontent');

			$data_from_sess = @ $uploader_file_data[$file_row_id];

			if ($data_from_sess)
			{
				$filetitle  = $data_from_sess['filetitle'];
				$filedesc   = $data_from_sess['filedesc'];
				$filelang   = $data_from_sess['filelang'];
				$fileaccess = $data_from_sess['fileaccess'];
				$secure     = $data_from_sess['secure'];
				$stamp      = $data_from_sess['stamp'];

				$fieldid    = $data_from_sess['fieldid'];
				$u_item_id  = $data_from_sess['u_item_id'];
				$file_mode  = $data_from_sess['file_mode'];
			}

			// Print_r($file_row_id); echo "\n"; print_r($uploader_file_data); exit();
		}
		else
		{
			// Default field <input type="file" is name="Filedata" ... get the file
			$ffname = $this->input->get('file-ffname', 'Filedata', 'cmd');
			$file   = $this->input->files->get($ffname, '', 'array');

			// Refactor the array swapping positions
			$file = $this->refactorFilesArray($file);

			// Get nested position, and reach the final file data array
			$fname_level1 = $this->input->get('fname_level1', null, 'string');
			$fname_level2 = $this->input->get('fname_level2', null, 'string');
			$fname_level3 = $this->input->get('fname_level3', null, 'string');

			if (strlen($fname_level1))
			{
				$file = $file[$fname_level1];
			}

			if (strlen($fname_level2))
			{
				$file = $file[$fname_level2];
			}

			if (strlen($fname_level3))
			{
				$file = $file[$fname_level3];
			}
		}

		if (empty($data_from_sess))
		{
			$secure  = $this->input->get('secure', 1, 'int');
			$secure  = $secure ? 1 : 0;

			$stamp = $this->input->get('stamp', 1, 'int');
			$stamp = $stamp ? 1 : 0;

			$filetitle  = $this->input->get('file-title', '', 'string');
			$filedesc   = flexicontent_html::dataFilter($this->input->get('file-desc', '', 'string'), 32000, 'STRING', 0);  // Limit number of characters
			$filelang   = $this->input->get('file-lang', '*', 'string');
			$fileaccess = $this->input->get('file-access', 1, 'int');
			$fileaccess = flexicontent_html::dataFilter($fileaccess, 11, 'ACCESSLEVEL', 0);  // Validate access level exists (set to public otherwise)

			$fieldid    = $this->input->get('fieldid', 0, 'int');
			$u_item_id  = $this->input->get('u_item_id', 0, 'cmd');
			$file_mode  = $this->input->get('folder_mode', 0, 'int') ? 'folder_mode' : 'db_mode';
		}

		$model = $this->getModel($this->record_name_pl);
		$field = false;

		if ($fieldid)
		{
			$field = $db->setQuery('SELECT * FROM #__flexicontent_fields WHERE id=' . $fieldid)->loadObject();
			$field->parameters = new JRegistry($field->attribs);
			$field->item_id = $u_item_id;
		}

		$default_dir = 2;

		if ($field)
		{
			if (in_array($field->field_type, array('file', 'image')))
			{
				$default_dir = 1;  // 'secure' folder
			}
			elseif (in_array($field->field_type, array('mediafile')))
			{
				$default_dir = 0;  // 'media' folder
			}

			$target_dir = $field->parameters->get('target_dir', $default_dir);

			// Force secure / media DB folder according to field configuration
			if (strlen($target_dir) && $target_dir != 2)
			{
				$secure = $target_dir ? 1 : 0;
			}
		}

		if ($field && $field->field_type == 'image' && $field->parameters->get('image_source') == 1)
		{
			$file_mode = 'folder_mode';
		}
		else
		{
			$file_mode = 'db_mode';
		}

		$estorage_mode = $field
			? $field->parameters->get('estorage_mode', '0')
			: '0';


		// *****************************************
		// Check that a file was provided / uploaded
		// *****************************************
		if (!isset($file['name']))
		{
			$this->exitHttpHead = array( 0 => array('status' => '400 Bad Request') );
			$this->exitMessages = array( 0 => array('error' => 'Filename has invalid characters (or other error occured)') );
			$this->exitLogTexts = array();
			$this->exitSuccess  = false;

			return $this->terminate($file_id, $exitMessages);
		}

		// Chunking might be enabled
		$chunks = $this->input->get('chunks', 0, 'int');

		if ($chunks)
		{
			$chunk = $this->input->get('chunk', 0, 'int');

			// Get / Create target directory
			$targetDir = $app->getCfg('tmp_path') . DIRECTORY_SEPARATOR . "fc_fileselement";

			if (!file_exists($targetDir))
			{
				@ mkdir($targetDir);
			}

			// Create name of the unique temporary filename to use for concatenation of the chunks, or get the filename from session
			$fileName = $this->input->get('filename', '', 'string');

			$fileName_tmp = $app->getUserState($fileName, date('Y_m_d_') . uniqid());
			$app->setUserState($fileName, $fileName_tmp);
			$filePath_tmp = $targetDir . DIRECTORY_SEPARATOR . $fileName_tmp;

			// CREATE tmp file inside the Joomla temporary folder, but if this FAILS, then CREATE tmp file inside SERVER tmp directory
			if (!$out = @fopen("{$filePath_tmp}", "ab"))
			{
				$targetDir = (ini_get("upload_tmp_dir") ? ini_get("upload_tmp_dir") : sys_get_temp_dir()) . DIRECTORY_SEPARATOR . "fc_fileselement";

				if (!file_exists($targetDir))
				{
					@ mkdir($targetDir);
				}

				$filePath_tmp = $targetDir . DIRECTORY_SEPARATOR . $fileName_tmp;

				if (!$out = @fopen("{$filePath_tmp}", "ab"))
				{
					// Die("{'jsonrpc' : '2.0', 'error' : {'code': 102, 'message': 'Failed to open output stream: " . json_encode($filePath_tmp) . " fopen failed. reason: " . json_encode(implode(' ', error_get_last())) . "'}, 'data' : null}");
					die('{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream. Temporary path not writable."}, "data" : null}');
				}
			}

			if (!empty($_FILES))
			{
				if ($_FILES["file"]["error"] || !is_uploaded_file($_FILES["file"]["tmp_name"]))
				{
					die('{"jsonrpc" : "2.0", "error" : {"code": 103, "message": "Failed to move uploaded file."}, "data" : null}');
				}

				if (!$in = @fopen($_FILES["file"]["tmp_name"], "rb"))
				{
					die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "data" : null}');
				}
			}
			else
			{
				if (!$in = @fopen("php://input", "rb"))
				{
					die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "data" : null}');
				}
			}

			// Read binary input stream and append it to temp file
			while ($buff = fread($in, 4096))
			{
				fwrite($out, $buff);
			}

			@ fclose($out);
			@ fclose($in);

			// If not last chunk terminate further execution
			if ($chunk < $chunks - 1)
			{
				// Return Success JSON-RPC response
				die('{"jsonrpc" : "2.0", "result" : null, "data" : null}');
			}

			// Remove no longer needed file properties from session data
			elseif ($file_row_id && isset($uploader_file_data[$file_row_id]))
			{
				unset($uploader_file_data[$file_row_id]);
				$session->set('uploader_file_data', $uploader_file_data, 'flexicontent');
			}

			// Clear the temporary filename, from user state
			$app->setUserState($fileName, null);

			// Cleanup left-over files
			if (file_exists($targetDir))
			{
				foreach (new DirectoryIterator($targetDir) as $fileInfo)
				{
					if ($fileInfo->isDot())
					{
						continue;
					}

					if (time() - $fileInfo->getCTime() >= 60)
					{
						unlink($fileInfo->getRealPath());
					}
				}
			}

			// echo "-- chunk: $chunk \n-- chunks: $chunks \n-- targetDir: $targetDir \n--filePath_tmp: $filePath_tmp \n--fileName: $fileName";
			// echo "\n"; print_r($_REQUEST);
			$file['name'] = $fileName;
			$file['tmp_name'] = $filePath_tmp;
			$file['size'] = filesize($filePath_tmp);
			$file['error'] = 0;

			// echo "\n"; print_r($file);
		}

		if ($fieldid)
		{
			$_options = array('secure' => $secure);
			$path = $model->getFieldFolderPath($u_item_id, $fieldid, $_options);

			// Create field's folder if it does not exist already
			if (!is_dir($path))
			{
				mkdir($path, $mode = 0755, $recursive = true);
			}
		}
		else
		{
			$path = $secure ? COM_FLEXICONTENT_FILEPATH . DS : COM_FLEXICONTENT_MEDIAPATH . DS;
		}

		jimport('joomla.utilities.date');

		// Set FTP credentials, if given
		jimport('joomla.client.helper');
		JClientHelper::setCredentialsFromRequest('ftp');

		// Make the filename safe
		jimport('joomla.filesystem.file');

		// Sanitize filename further and make unique
		$params = null;
		$err_text = null;
		$filesize = $file['size'];
		$filename_original = strip_tags($file['name']);  // Store original filename before sanitizing the filename
		$upload_check = flexicontent_upload::check($file, $err_text, $params);  // Check that file contents are safe, and also make the filename safe, transliterating it according to given language (this forces lowercase)
		$filename     = flexicontent_upload::sanitize($path, $file['name']);    // Sanitize the file name (filesystem-safe, (this should have been done above already)) and also return an unique filename for the given folder
		$filepath 	  = JPath::clean($path . $filename);

		// Check if uploaded file is valid
		if (!$upload_check)
		{
			$this->exitHttpHead = array( 0 => array('status' => '415 Unsupported Media Type') );
			$this->exitMessages = array( 0 => array('error' => $err_text) );
			$this->exitLogTexts = array( 0 => array(JLog::ERROR => 'Invalid: ' . $filepath . ': ' . JText::_($err_text)) );
			$this->exitSuccess  = false;

			return $this->terminate($file_id, $exitMessages);
		}

		// Get the extension to record it in the DB
		$ext = strtolower(flexicontent_upload::getExt($filename));

		// echo "\n". $file['tmp_name'] ." => ". $filepath ."\n";

		if ($chunks)
		{
			$move_success = copy($file['tmp_name'], $filepath);
			$move_success ? unlink($file['tmp_name']) : false;
		}
		else
		{
			$move_success = JFile::upload($file['tmp_name'], $filepath, false, false,
				// - Valid extensions are checked by our helper function
				// - also we allow all extensions and php inside content, FLEXIcontent will never execute "include" files evening when doing "in-browser viewing"
				array('null_byte' => true, 'forbidden_extensions' => array('_fake_ext_'), 'php_tag_in_content' => true, 'shorttag_in_content' => true, 'shorttag_extensions' => array(), 'fobidden_ext_in_content' => false, 'php_ext_content_extensions' => array() )
			);
		}

		// Check of upload failed
		if (!$move_success)
		{
			$this->exitHttpHead = array( 0 => array('status' => '409 Conflict') );
			$this->exitMessages = array( 0 => array('error' => 'FLEXI_UNABLE_TO_UPLOAD_FILE') );
			$this->exitLogTexts = array( 0 => array(JLog::ERROR => JText::_('FLEXI_UNABLE_TO_UPLOAD_FILE') . ': ' . $filepath) );
			$this->exitSuccess  = false;

			return $this->terminate($file_id, $exitMessages);
		}



		// *****************
		// Upload Successful
		// *****************

		$fileObj = new stdClass;
		$fileObj->id = $file_id = 0;

		$fileObj->filename          = $filename;
		$fileObj->filename_original = $filename_original;
		$fileObj->altname           = $filetitle ? $filetitle : $filename_original;
		$fileObj->estorage_fieldid  = $estorage_mode === 'FTP' ? $fieldid : 0;

		$fileObj->url         = 0;
		$fileObj->secure      = $secure;
		$fileObj->stamp       = $stamp;
		$fileObj->ext         = $ext;

		$fileObj->description = $filedesc;
		$fileObj->language    = strlen($filelang) ? $filelang : '*';
		$fileObj->access      = strlen($fileaccess) ? $fileaccess : 1;

		$fileObj->hits        = 0;
		$fileObj->size        = $filesize;
		$fileObj->uploaded    = JFactory::getDate('now')->toSql();
		$fileObj->uploaded_by = $user->get('id');

		// A. Database mode
		if ($file_mode == 'db_mode')
		{
			// Insert file record in DB
			$db->insertObject('#__' . $this->records_dbtbl, $fileObj);

			// Get id of new file record
			$fileObj->id = $file_id = (int) $db->insertid();

			// Probe file to find if it is a supported media (audio or video) file
			$fileObj->full_path = $filepath;

			if ($field)
			{
				$res = $model->createMediaData($field, $fileObj);
				if (!$res)
				{
					$error_msg = JText::_("File uploaded successfully.\nBut got error reading media (audio/video) properties")
						. ":\n  " . $model->getError();
					$this->exitHttpHead = array( 0 => array('status' => '500 Error') );
					$this->exitMessages = array( 0 => array('warning' => $error_msg) );
					$this->exitLogTexts = array( 0 => array(JLog::WARNING => $error_msg) );
					$this->exitSuccess  = false;

					return $this->terminate($file_id, $exitMessages);
				}

				// Create audio preview file, if file is a media file
				if (!empty($fileObj->mediaData))
				{
					$res = $model->createAudioPreview($field, $fileObj);
					if (!$res)
					{
						$error_msg = JText::_("File uploaded successfully.\nBut got error during creating preview files")
							. ":\n  " . $model->getError();
						$this->exitHttpHead = array( 0 => array('status' => '500 Error') );
						$this->exitMessages = array( 0 => array('warning' => $error_msg) );
						$this->exitLogTexts = array( 0 => array(JLog::WARNING => $error_msg) );
						$this->exitSuccess  = false;

						return $this->terminate($file_id, $exitMessages);
					}
				}
			}
		}

		// B. Custom Folder mode
		else
		{
		}

		// Add information about uploaded file data into the session
		if ($this->input->get('history', 0, 'int'))
		{
			$upload_context = 'fc_upload_history.item_' . $u_item_id . '_field_' . $fieldid;

			$session_files = $session->get($upload_context, array());
			$session_files['ids'][] = $file_id;
			$session_files['names'][] = $filename;
			$session_files['ids_pending'][] = $file_id;
			$session_files['names_pending'][] = $filename;
			$session->set($upload_context, $session_files);
		}

		// Terminate with proper messaging
		$this->exitHttpHead = array( 0 => array('status' => '201 Created') );
		$this->exitMessages[] = array('message' => 'FLEXI_UPLOAD_COMPLETE');
		$this->exitLogTexts = array();
		$this->exitSuccess  = true;

		return $this->terminate($file_id, $exitMessages, $fileObj);
	}


	/**
	 * Upload a file by url
	 *
	 * @since 1.0
	 */
	function addurl($Fobj = null, & $exitMessages = null)
	{
		// Check for request forgeries
		JSession::checkToken('request') or die(JText::_('JINVALID_TOKEN'));

		// Initialize variables
		$app     = JFactory::getApplication();
		$session = JFactory::getSession();
		$model = $this->getModel($this->record_name);

		// Force interactive run mode, if given parameters
		$this->runMode = $Fobj ? 'interactive' : $this->runMode;
		$file_id = 0;

		// 2: File Link (JMedia) , 1: URL Link
		$linktype = $this->input->get('file-link-type', 1, 'int');
		$linktype = $linktype === 2 ? 2 : 1;

		if ($linktype === 1)
		{
			$url = $this->input->get('file-url-data', null, 'string');
			$url = flexicontent_html::dataFilter($url, 4000, 'URL', 0);  // Validate file URL
		}
		else
		{
			$url = $this->input->get('file-jmedia-data', null, 'string');
			$url = flexicontent_html::dataFilter($url, 4000, 'PATH', 0);  // Validate JMedia file PATH
		}

		$altname  = $this->input->get('file-url-title', null, 'string');

		$filedesc   = flexicontent_html::dataFilter($this->input->get('file-url-desc', '', 'string'), 32000, 'STRING', 0);  // Limit number of characters
		$filelang   = $this->input->get('file-url-lang', '*', 'string');
		$fileaccess = $this->input->get('file-url-access', 1, 'int');
		$fileaccess = flexicontent_html::dataFilter($fileaccess, 11, 'ACCESSLEVEL', 0);  // Validate access level exists (set to public otherwise)

		$fieldid   = $this->input->get('fieldid', 0, 'int');
		$ext       = $this->input->get('file-url-ext', null, 'cmd');
		$filesize  = $this->input->get('file-url-size', 0, 'int');
		$size_unit = $this->input->get('size_unit', 'KBs', 'cmd');

		jimport('joomla.utilities.date');

		// Check if the form fields are not empty
		if (!$url || !$altname)
		{
			$this->exitHttpHead = array( 0 => array('status' => '400 Bad Request') );
			$this->exitMessages = array( 0 => array('error' => 'FLEXI_WARNFILEURLFORM') );
			$this->exitLogTexts = array();
			$this->exitSuccess  = false;

			return $this->terminate($file_id, $exitMessages);
		}

		if (empty($filesize))
		{
			if ($linktype === 1)
			{
				$filesize = $model->get_file_size_from_url($url);

				if ($filesize === -999)
				{
					$app->enqueueMessage($url . ' -- ' . $model->getError(), 'warning');
				}

				$filesize = $filesize < 0 ? 0 : $filesize;
			}
			else  // $linktype === 2
			{
				$filesize = filesize($url);
			}
		}

		else
		{
			$arr_sizes = array('KBs' => 1024, 'MBs' => (1024 * 1024), 'GBs' => (1024 * 1024 * 1024));
			$size_unit = (int) @ $arr_sizes[$size_unit];

			if ($size_unit)
			{
				$filesize = ((int) $filesize) * $size_unit;
			}
			else
			{
				$filesize = 0;
			}
		}

		// We verifiy the url prefix and add http if any
		if ($linktype === 1 && !preg_match("#^http|^https|^ftp#i", $url))
		{
			$url	= 'http://' . $url;
		}

		$db 	= JFactory::getDbo();
		$user	= JFactory::getUser();
		$field  = false;

		if ($fieldid)
		{
			$field = $db->setQuery('SELECT * FROM #__flexicontent_fields WHERE id=' . $fieldid)->loadObject();
			$field->parameters = new JRegistry($field->attribs);
			$field->item_id = $u_item_id;
		}

		$default_dir = 2;
		$secure      = 1;

		if ($field)
		{
			if (in_array($field->field_type, array('file', 'image')))
			{
				$default_dir = 1;  // 'secure' folder
			}
			elseif (in_array($field->field_type, array('mediafile')))
			{
				$default_dir = 0;  // 'media' folder
			}

			$target_dir = $field->parameters->get('target_dir', $default_dir);

			// Force secure / media DB folder according to field configuration
			if (strlen($target_dir) && $target_dir != 2)
			{
				$secure = $target_dir ? 1 : 0;
			}
		}

		$fileObj = new stdClass;
		$fileObj->filename    = $url;
		$fileObj->filename_original = $url;
		$fileObj->altname     = $altname;

		$fileObj->url         = $linktype;
		$fileObj->secure      = $secure;
		$fileObj->stamp       = 0;
		$fileObj->ext         = $ext;

		$fileObj->description = $filedesc;
		$fileObj->language    = strlen($filelang) ? $filelang : '*';
		$fileObj->access      = strlen($fileaccess) ? $fileaccess : 1;

		$fileObj->hits        = 0;
		$fileObj->size        = $filesize;
		$fileObj->uploaded    = JFactory::getDate('now')->toSql();
		$fileObj->uploaded_by = $user->get('id');

		$db->insertObject('#__' . $this->records_dbtbl, $fileObj);

		// Get id of new file record
		$file_id = (int) $db->insertid();

		// Add information about added (URL) file data into the session
		if ($this->input->get('history', 0, 'int'))
		{
			$upload_context = 'fc_upload_history.item_' . $u_item_id . '_field_' . $fieldid;

			$session_files = $session->get($upload_context, array());
			$session_files['ids'][] = $file_id;
			$session_files['names'][] = $url;
			$session_files['ids_pending'][] = $file_id;
			$session_files['names_pending'][] = $url;
			$session->set($upload_context, $session_files);
		}

		// Terminate with proper messaging
		$this->exitHttpHead = array( 0 => array('status' => '201 Created') );
		$this->exitMessages = array( 0 => array('message' => 'FLEXI_FILE_ADD_SUCCESS') );
		$this->exitLogTexts = array();
		$this->exitSuccess  = true;

		return $this->terminate($file_id, $exitMessages);
	}


	/**
	 * Logic to delete records
	 *
	 * @return void
	 *
	 * @since 3.3
	 */
	public function remove()
	{
		// Check for request forgeries
		JSession::checkToken('request') or die(JText::_('JINVALID_TOKEN'));

		// Initialize variables
		$app   = JFactory::getApplication();
		$user  = JFactory::getUser();
		$db     = JFactory::getDbo();

		$fieldid    = $this->input->get('fieldid', 0, 'int');
		$u_item_id  = $this->input->get('u_item_id', 0, 'cmd');
		$file_mode  = $this->input->get('folder_mode', 0, 'int') ? 'folder_mode' : 'db_mode';

		// Check for zero selected records
		$cid = $this->input->get('cid', array(), 'array');

		if ($file_mode != 'folder_mode')
		{
			// These are file ids, for DB-mode
			$cid = ArrayHelper::toInteger($cid);
		}

		if (!is_array($cid) || count($cid) < 1)
		{
			$this->exitHttpHead = array( 0 => array('status' => '400 Bad Request') );
			$this->exitMessages = array( 0 => array('error' => 'FLEXI_SELECT_ITEM_DELETE') );
			$this->exitLogTexts = array();
			$this->exitSuccess  = false;

			return $this->terminate($file_id, $exitMessages);
		}

		// Different handling for folder_mode
		if ($file_mode == 'folder_mode')
		{
			$field = $db->setQuery('SELECT * FROM #__flexicontent_fields WHERE id=' . $fieldid)->loadObject();
			$field->parameters = new JRegistry($field->attribs);
			$field->item_id = $u_item_id;

			$failed_files = array();

			foreach ($cid as $filename)
			{
				$filename = rawurldecode($filename);

				// Default 'CMD' filtering is maybe too aggressive, but allowing UTF8 will not work in all filesystems, so we do not allow
				// $filename_original = iconv(mb_detect_encoding($filename, mb_detect_order(), true), "UTF-8", $filename);

				if (!FLEXIUtilities::call_FC_Field_Func($field->field_type, 'removeOriginalFile', array(&$field, $filename)))
				{
					$failed_files[] = $filename;
				}
			}

			$failed_msg = !count($failed_files) ? '' : JText::_('FLEXI_UNABLE_TO_CLEANUP_ORIGINAL_FILE') . ': ' . implode(', ', $failed_files);
			$delete_count = count($cid) - count($failed_files);

			if ($delete_count)
			{
				$this->exitHttpHead = array( 0 => array('status' => '200 OK') );
				$this->exitMessages = array( 0 => array('message' => $delete_count . ' ' . JText::_('FLEXI_FILES_DELETED')) );

				if (count($failed_files))
				{
					$app->enqueueMessage($failed_msg, 'warning');
				}
			}
			else
			{
				$this->exitHttpHead = array( 0 => array('status' => '500 Internal Server Error') );
				$this->exitMessages = array( 0 => array('error' => $failed_msg) );
			}

			$this->exitLogTexts = array();
			$this->exitSuccess  = count($failed_files) == 0;

			return $this->terminate($file_id, $exitMessages);
		}

		// Calculate access
		/**
		 * Because we do not have ACL for individual files, we will abort here if both of 'flexicontent.deletefile' or 'flexicontent.deleteownfile' are not granted at component level
		 *
		 * Note later we check: 'flexicontent.deleteownfile' or (ownership + 'flexicontent.deleteownfile') by using $model->getDeletable($cid)
		 */
		$candelete     = $user->authorise('flexicontent.deletefile', 'com_flexicontent');
		$candeleteown  = $user->authorise('flexicontent.deleteownfile', 'com_flexicontent');
		$is_authorised = $candelete || $candeleteown;

		// Check access
		if (!$is_authorised)
		{
			$this->exitHttpHead = array( 0 => array('status' => '403 Forbidden') );
			$this->exitMessages = array( 0 => array('error' => 'FLEXI_ALERTNOTAUTH_TASK') );
			$this->exitLogTexts = array();
			$this->exitSuccess  = false;

			return $this->terminate($file_id, $exitMessages);
		}

		$msg = '';

		$db->setQuery('SELECT * FROM #__' . $this->records_dbtbl . ' WHERE id IN (' . implode(',', $cid) . ')');
		$files = $db->loadObjectList('id');
		$cid = array_keys($files);

		$model = $this->getModel($this->record_name_pl);
		$deletable = $model->getDeletable($cid);

		// Find files that are currently in use
		if (count($cid) != count($deletable))
		{
			$_del = array_flip($deletable);
			$inuse_files = array();

			foreach ($files as $_id => $file)
			{
				if (isset($_del[$_id]))
				{
					continue;
				}

				$inuse_files[] = $file->filename_original ? $file->filename_original : $file->filename;
			}

			$app->enqueueMessage(JText::_('FLEXI_CANNOT_REMOVE_FILES_IN_USE') . ': ' . implode(', ', $inuse_files), 'warning');
			$cid = $deletable;
		}

		// Find files allowed to be deleted
		$allowed_files = array();
		$denied_files = array();

		foreach ($cid as $_id)
		{
			if (!isset($files[$_id]))
			{
				continue;
			}

			$filename = $files[$_id]->filename_original
				? $files[$_id]->filename_original
				: $files[$_id]->filename;

			// Note: component 'deleteownfile' was checked above
			if ($candelete || ($user->get('id') && $files[$_id]->uploaded_by == $user->get('id')))
			{
				$allowed_files[$_id] = $filename;
			}
			else
			{
				$denied_files[$_id] = $filename;
			}
		}

		if (count($denied_files))
		{
			$app->enqueueMessage(' You are not allowed to delete files: ' . implode(', ', $denied_files), 'warning');
		}

		$allowed_cid = array_keys($allowed_files);

		// Check for error during delete operation
		if (count($allowed_cid) && !$model->delete($allowed_cid))
		{
			$this->exitHttpHead = array( 0 => array('status' => '500 Internal Server Error') );
			$this->exitMessages = array( 0 => array('error' => JText::_('FLEXI_OPERATION_FAILED') . ' : ' . $model->getError()) );
			$this->exitLogTexts = array();
			$this->exitSuccess  = false;

			return $this->terminate($file_id, $exitMessages);
		}

		if (count($allowed_cid))
		{
			$msg .= count($allowed_cid) . ' ' . JText::_('FLEXI_FILES_DELETED');
		}

		// Clear cache and return
		$cache = JFactory::getCache('com_flexicontent');
		$cache->clean();

		$this->setRedirect($this->returnURL, $msg);
	}



	/**
	 * Logic to modify the state of records, other state modifications tasks are wrappers to this task
	 *
	 * @return void
	 *
	 * @since 3.3
	 */
	public function changestate($state = 1)
	{
		// Check for request forgeries
		JSession::checkToken('request') or jexit(JText::_('JINVALID_TOKEN'));

		$app   = JFactory::getApplication();
		$user  = JFactory::getUser();
		$db    = JFactory::getDbo();

		// Calculate access
		/**
		 * Because we do not have ACL for individual files, we will abort here if both of 'flexicontent.publishfile' or 'flexicontent.publishownfile' are not granted at component level
		 *
		 * Note: later we will check:  $canpublish || ($user->get('id') && $files[$_id]->uploaded_by == $user->get('id'))
		 */
		$canpublish = $user->authorise('flexicontent.publishfile', 'com_flexicontent');
		$canpublishown = $user->authorise('flexicontent.publishownfile', 'com_flexicontent');
		$is_authorised = $canpublish || $canpublishown;

		// Check access
		if (!$is_authorised)
		{
			$app->setHeader('status', '403 Forbidden', true);
			$this->setRedirect($this->returnURL, JText::_('FLEXI_ALERTNOTAUTH_TASK'), 'error');

			return;
		}

		$cid = $this->input->get('cid', array(), 'array');
		$cid = ArrayHelper::toInteger($cid);

		if (!is_array($cid) || count($cid) < 1)
		{
			$app->setHeader('status', '400 Bad Request', true);
			$this->setRedirect($this->returnURL, JText::_($state ? 'FLEXI_SELECT_ITEM_PUBLISH' : 'FLEXI_SELECT_ITEM_UNPUBLISH'), 'error');

			return;
		}

		$db->setQuery('SELECT * FROM #__flexicontent_files WHERE id IN (' . implode(',', $cid) . ')');
		$files = $db->loadObjectList('id');
		$cid = array_keys($files);

		$model = $this->getModel($this->record_name_pl);

		$msg = '';
		$allowed_files = array();
		$denied_files = array();

		foreach ($cid as $_id)
		{
			if (!isset($files[$_id]))
			{
				continue;
			}

			$filename = $files[$_id]->filename_original
				? $files[$_id]->filename_original
				: $files[$_id]->filename;

			if ($canpublish || ($user->get('id') && $files[$_id]->uploaded_by == $user->get('id')))
			{
				$allowed_files[$_id] = $filename;
			}
			else
			{
				$denied_files[$_id] = $filename;
			}
		}

		if (count($denied_files))
		{
			$app->enqueueMessage(' You are not allowed to change state of files: ' . implode(', ', $denied_files), 'warning');
		}

		$allowed_cid = array_keys($allowed_files);

		if (count($allowed_cid) && !$model->publish($allowed_cid, $state))
		{
			$app->setHeader('status', '500 Internal Server Error', true);
			$this->setRedirect($this->returnURL, JText::_('FLEXI_OPERATION_FAILED') . ' : ' . $model->getError(), 'error');

			return;
		}

		if (count($allowed_cid))
		{
			$msg .= JText::_($state ? 'FLEXI_PUBLISHED' : 'FLEXI_UNPUBLISHED') . ': ' . implode(', ', $allowed_files);
		}

		$cache = JFactory::getCache('com_flexicontent');
		$cache->clean();

		$this->setRedirect($this->returnURL, $msg);
	}


	/**
	 * Upload a file from a server directory
	 *
	 * @since 1.0
	 */
	function addlocal($Fobj = null, & $exitMessages = null)
	{
		// Check for request forgeries
		JSession::checkToken('request') or jexit(JText::_('JINVALID_TOKEN'));

		static $imported_files = array();
		$file_ids = array();

		$app    = JFactory::getApplication();
		$db 		= JFactory::getDbo();
		$user		= JFactory::getUser();
		$params = JComponentHelper::getParams('com_flexicontent');

		$is_importcsv = $this->task === 'importcsv';

		// Get file properties
		$secure  = $Fobj ? $Fobj->secure : $this->input->get('secure', 1, 'int');
		$secure  = $secure ? 1 : 0;

		$stamp  = $Fobj ? $Fobj->stamp : $this->input->get('stamp', 1, 'int');
		$stamp  = $stamp ? 1 : 0;

		$filedesc   = flexicontent_html::dataFilter($this->input->get('file-desc', '', 'string'), 32000, 'STRING', 0);  // Limit number of characters
		$filelang   = $this->input->get('file-lang', '*', 'string');
		$fileaccess = $this->input->get('file-access', 1, 'int');
		$fileaccess = flexicontent_html::dataFilter($fileaccess, 11, 'ACCESSLEVEL', 0);  // Validate access level exists (set to public otherwise)

		// Get folder path and filename regexp
		$filesdir = $Fobj ? $Fobj->file_dir_path : $this->input->get('file-dir-path', '', 'string');
		$regexp   = $Fobj ? $Fobj->file_filter_re : $this->input->get('file-filter-re', '.', 'string');

		// Delete after adding flag
		$keep = $Fobj ? $Fobj->keep : $this->input->get('keep', 1, 'int');

		// Get desired extensions from request
		$filter_ext = $this->input->get('file-filter-ext', '', 'string');
		$filter_ext	= $filter_ext ? explode(',', $filter_ext) : array();

		foreach ($filter_ext as $_i => $_ext)
		{
			$filter_ext[$_i] = strtolower($_ext);
		}

		// Get extensions allowed by configuration, and intersect them with desired extensions
		$allowed_exts = preg_split("/[\s]*,[\s]*/", strtolower($params->get('upload_extensions', 'bmp,wbmp,csv,doc,docx,webp,gif,ico,jpg,jpeg,odg,odp,ods,odt,pdf,png,ppt,pptx,txt,xcf,xls,xlsx,zip,ics')));
		$allowed_exts = $filter_ext ? array_intersect($filter_ext, $allowed_exts) : $allowed_exts;
		$allowed_exts = array_flip($allowed_exts);

		jimport('joomla.utilities.date');
		jimport('joomla.filesystem.file');
		jimport('joomla.filesystem.folder');

		// Get files
		$filesdir = JPath::clean(JPATH_SITE . $filesdir . DS);
		$filenames = JFolder::files($filesdir, $regexp);

		// Create the folder if it does not exists
		$destpath = $secure ? COM_FLEXICONTENT_FILEPATH . DS : COM_FLEXICONTENT_MEDIAPATH . DS;

		if (!JFolder::exists($destpath))
		{
			if (!JFolder::create($destpath))
			{
				$this->exitHttpHead = array( 0 => array('status' => '500 Internal Server Error') );
				$this->exitMessages = array( 0 => array('error' => 'Error. Unable to create folders') );
				$this->exitLogTexts = array();
				$this->exitSuccess  = false;

				return $this->terminate($file_ids, $exitMessages);
			}
		}

		// Check if the form fields are not empty
		if (!$filesdir)
		{
			$this->exitHttpHead = array( 0 => array('status' => '400 Bad Request') );
			$this->exitMessages = array( 0 => array('error' => 'FLEXI_WARN_NO_FILE_DIR') );
			$this->exitLogTexts = array();
			$this->exitSuccess  = false;

			return $this->terminate($file_ids, $exitMessages);
		}

		$added = array();
		$excluded = array();

		if ($filenames)
		{
			for ($n = 0; $n < count($filenames); $n++)
			{
				$ext = strtolower(flexicontent_upload::getExt($filenames[$n]));

				if (!isset($allowed_exts[$ext]))
				{
					$excluded[] = $filenames[$n];
					continue;
				}

				$source 		= $filesdir . $filenames[$n];
				$filename		= flexicontent_upload::sanitize($destpath, $filenames[$n]);
				$destination 	= $destpath . $filename;

				// Check for file already added by import task and do not re-add the file
				if ($is_importcsv && isset($imported_files[$source]))
				{
					$file_ids[$filename] = $imported_files[$source];
					continue;
				}

				// Copy or move the file
				$success = $keep ? JFile::copy($source, $destination) : JFile::move($source, $destination);

				if ($success)
				{
					$filesize = filesize($destination);

					$fileObj = new stdClass;
					$fileObj->filename    = $filename;
					$fileObj->altname     = $filename;

					$fileObj->url         = 0;
					$fileObj->secure      = $secure;
					$fileObj->stamp       = $stamp;
					$fileObj->ext         = $ext;

					$fileObj->description = $filedesc;
					$fileObj->language    = strlen($filelang) ? $filelang : '*';
					$fileObj->access      = strlen($fileaccess) ? $fileaccess : 1;

					$fileObj->hits        = 0;
					$fileObj->size        = $filesize;
					$fileObj->uploaded    = JFactory::getDate('now')->toSql();
					$fileObj->uploaded_by = $user->get('id');

					// Add the record to the DB
					$db->insertObject('#__' . $this->records_dbtbl, $fileObj);
					$file_ids[$filename] = $db->insertid();

					// Add file ID to files imported by import task
					if ($is_importcsv)
					{
						$imported_files[$source] = $file_ids[$filename];
					}

					$added[] = $filenames[$n];
				}
			}

			if (count($added))
			{
				$app->enqueueMessage(JText::sprintf('FLEXI_FILES_COPIED_SUCCESS', count($added)), 'message');
			}

			if (count($excluded))
			{
				$app->enqueueMessage(JText::sprintf('FLEXI_FILES_EXCLUDED_WARNING', count($excluded)) . ' : ' . implode(', ', $excluded), 'warning');
			}
		}

		else
		{
			$this->exitHttpHead = array( 0 => array('status' => '400 Bad Request') );
			$this->exitMessages = array( 0 => array('error' => 'FLEXI_WARN_NO_FILES_IN_DIR') );
			$this->exitLogTexts = array();
			$this->exitSuccess  = false;

			return $this->terminate($file_ids, $exitMessages);
		}

		// Terminate with proper messaging
		$this->exitHttpHead = array( 0 => array('status' => '201 Created') );
		$this->exitMessages = array( 0 => array('message' => 'FLEXI_FILE_ADD_SUCCESS') );
		$this->exitLogTexts = array();
		$this->exitSuccess  = true;

		return $this->terminate($file_ids, $exitMessages);
	}


	/**
	 * Logic to create the view for record editing
	 *
	 * @return void
	 *
	 * @since 3.3
	 */
	public function edit()
	{
		$app      = JFactory::getApplication();
		$user     = JFactory::getUser();
		$session  = JFactory::getSession();
		$document = JFactory::getDocument();

		$this->input->set('view', $this->record_name);
		$this->input->set('hidemainmenu', 1);

		// Get/Create the view
		$viewType   = $document->getType();
		$viewName   = $this->input->get('view', $this->default_view, 'cmd');
		$viewLayout = $this->input->get('layout', 'default', 'string');
		$view = $this->getView($viewName, $viewType, '', array('base_path' => $this->basePath, 'layout' => $viewLayout));

		// Get/Create the model
		$model = $this->getModel($this->record_name);

		// Try to load file by attributes in HTTP Request
		if (0)
		{
			$record = $model->getRecord(array(
				'filename' => '',
			));
		}

		// Try to load by unique ID or NAME
		else
		{
			$record = $model->getItem();
		}

		// Push the model into the view (as default), later we will call the view display method instead of calling parent's display task, because it will create a 2nd model instance !!
		$view->setModel($model, true);
		$view->document = $document;

		// Calculate access
		$is_authorised = $model->canEdit($record);

		// Check access
		if (!$is_authorised)
		{
			$app->setHeader('status', '403 Forbidden', true);
			$app->enqueueMessage(JText::_('FLEXI_ALERTNOTAUTH_TASK'), 'error');

			if ($this->input->getCmd('tmpl') !== 'component')
			{
				$this->setRedirect($this->returnURL);
			}

			return;
		}

		// Check if record is checked out by other editor
		if ($model->isCheckedOut($user->get('id')))
		{
			$app->setHeader('status', '400 Bad Request', true);
			$app->enqueueMessage(JText::_('FLEXI_EDITED_BY_ANOTHER_ADMIN'), 'warning');

			if ($this->input->getCmd('tmpl') !== 'component')
			{
				$this->setRedirect($this->returnURL);
			}

			return;
		}

		// Checkout the record and proceed to edit form
		if (!$model->checkout())
		{
			$app->setHeader('status', '400 Bad Request', true);
			$app->enqueueMessage(JText::_('FLEXI_OPERATION_FAILED') . ' : ' . $model->getError(), 'error');

			if ($this->input->getCmd('tmpl') !== 'component')
			{
				$this->setRedirect($this->returnURL);
			}

			return;
		}

		// Call display method of the view, instead of calling parent's display task, because it will create a 2nd model instance !!
		$view->display();
	}


	/**
	 * Method for clearing cache of data depending on records type
	 *
	 * @return void
	 *
	 * @since 3.2.0
	 */
	protected function _cleanCache()
	{
		$this->input->get('task', '', 'cmd') !== __FUNCTION__ or die(__FUNCTION__ . ' : direct call not allowed');

		$cache = JFactory::getCache('com_flexicontent');
		$cache->clean();
	}


	/**
	 * Logic to set the access level of the records
	 *
	 * @return void
	 *
	 * @since 3.3
	 */
	public function access()
	{
		// Check for request forgeries
		JSession::checkToken('request') or die(JText::_('JINVALID_TOKEN'));

		// Initialize variables
		$app   = JFactory::getApplication();
		$user  = JFactory::getUser();

		// Get model
		$model = $this->getModel($this->record_name_pl);

		// Get and santize records ids
		$cid = $this->input->get('cid', array(), 'array');
		$cid = ArrayHelper::toInteger($cid);

		// Check at least one item was selected
		if (!count($cid))
		{
			$app->setHeader('status', '500 Internal Server Error', true);
			$app->enqueueMessage(JText::_('FLEXI_NO_ITEMS_SELECTED'), 'error');
			$this->setRedirect($this->returnURL);

			return;
		}

		$file_id = (int) reset($cid);
		$row = JTable::getInstance('flexicontent_files', '');
		$row->load($file_id);

		// Calculate access
		$perms = FlexicontentHelperPerm::getPerm();
		$is_authorised = $perms->CanFiles && ($perms->CanViewAllFiles || $user->id == $row->uploaded_by);

		// Check access
		if (!$is_authorised)
		{
			$app->enqueueMessage(JText::_('FLEXI_ALERTNOTAUTH_TASK'), 'error');
			$app->setHeader('status', '403 Forbidden', true);
			$this->setRedirect($this->returnURL);

			return;
		}

		// Get new record access
		$accesses = $this->input->get('access', array(), 'array');
		$accesses = ArrayHelper::toInteger($accesses);
		$access = $accesses[$file_id];

		if (!$model->saveaccess($file_id, $access))
		{
			$app->setHeader('status', '500 Internal Server Error', true);
			$this->setRedirect($this->returnURL, JText::_('FLEXI_OPERATION_FAILED') . ' : ' . $model->getError(), 'error');

			return;
		}

		$cache = JFactory::getCache('com_flexicontent');
		$cache->clean();

		$this->setRedirect($this->returnURL);
	}


	/**
	 * START OF CONTROLLER SPECIFIC METHODS
	 */

	/**
	 * CONTROLLER specific Helper Methods (non-task methods)
	 */
	 

	/*
	 * Restructure a FILES array for easier usage
	 */
	function refactorFilesArray(&$f)
	{
		$this->input->get('task', '', 'cmd') !== __FUNCTION__ or die(__FUNCTION__ . ' : direct call not allowed');

		if (empty($f['name']) || !is_array($f['name']))
		{
			return $f; // Nothing more to do
		}

		$level0_keys = array_keys($f);
		$level1_keys = array_keys($f['name']);

		// Swap indexLevel_N with indexLeveL_N+1, until there are no more inner arrays
		foreach ($level0_keys  as  $i)  // level0_keys are: name, type, tmp_name, error, size
		{
			foreach ($level1_keys  as  $k1)  // Level1_keys are: the indexes of ... file['name']
			{
				$r1[$k1][$i] = $f[$i][$k1];

				if (!is_array($r1[$k1][$i]))
				{
					continue;
				}

				foreach (array_keys($r1[$k1][$i])  as  $k2)
				{
					$r2[$k1][$k2][$i] = $r1[$k1][$i][$k2];

					if (!is_array($r2[$k1][$k2][$i]))
					{
						continue;
					}

					foreach (array_keys($r2[$k1][$k2][$i])  as  $k3)
					{
						$r3[$k1][$k2][$k3][$i] = $r2[$k1][$k2][$i][$k3];
					}
				}
			}
		}

		if (isset($r3))
		{
			return $r3;
		}
		elseif (isset($r2))
		{
			return $r2;
		}
		else
		{
			return $r1;
		}
	}


	/**
	 * Set credentials for using FTP layer for file handling
	 */
	function ftpValidate()
	{
		$this->input->get('task', '', 'cmd') !== __FUNCTION__ or die(__FUNCTION__ . ' : direct call not allowed');

		// Set FTP credentials, if given
		jimport('joomla.client.helper');
		JClientHelper::setCredentialsFromRequest('ftp');
	}
}