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/items.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');

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

	var $record_name = 'item';
	var $record_name_pl = 'items';

	var $_NAME = 'ITEM';
	var $record_alias = 'alias';

	var $runMode = 'standalone';

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

	// Only allow specific tasks in frontend. That have been reviewed for being both VALID and SAFE in frontend
	var $FE_tasks_proxied = array(
		'add', 'edit', 'save', 'cancel',
		'remove', 'approval',
		'apply_type', 'apply', 'apply_ajax',
		'save2new', 'save2copy', 'save_a_preview', 'download', 'download_file', 'weblink' ,
	);

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

		if (JFactory::getApplication()->isClient('site'))
		{
			$task = $this->input->get('task', '', 'cmd');

			if (!in_array($task, $this->FE_tasks_proxied))
			{
				throw new Exception('Direct usage of this controller is not allowed in frontend. Task: ' . $task, 403);
			}

			// Since we are in frontend we need to manually load the backend language files, (english and then  override with current language)
			JFactory::getLanguage()->load('com_flexicontent', JPATH_ADMINISTRATOR, 'en-GB', true);
			JFactory::getLanguage()->load('com_flexicontent', JPATH_ADMINISTRATOR, null, true);
		}

		/**
		 * Register task aliases
		 */
		$this->registerTask('apply_type',   'save');
		$this->registerTask('save_a_preview', 'save');

		if (JFactory::getApplication()->isClient('site'))
		{
			$this->registerTask('download_tree',  'download');

			$this->input  = empty($this->input) ? JFactory::getApplication()->input : $this->input;
			$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');

			// Get referer URL from HTTP request and validate it
			$this->refererURL = !empty($_SERVER['HTTP_REFERER']) && flexicontent_html::is_safe_url($_SERVER['HTTP_REFERER'])
				? $_SERVER['HTTP_REFERER']
				: JUri::base();

			// Get return URL from HTTP request and validate it
			$this->returnURL = $this->_getReturnUrl();

			// For frontend default return is refererURL
			$this->returnURL = $this->returnURL ?: $this->refererURL;
		}
		else
		{
			$this->registerTask('unfeatured',   'featured');
			$this->registerTask('copy',         'batch');
			$this->registerTask('translate',    'batch');
		}

		// Note: canManage, is not applicable for items manager, more specific ACL should be used
		$this->canManage = false;
	}


	/**
	 * Logic to set a record as up-to-date
	 *
	 * @return void
	 *
	 * @since 4.1
	 */
	public function set_uptodate()
	{
		// Check for request forgeries
		JSession::checkToken('request') or die(JText::_('JINVALID_TOKEN'));

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

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

		$values = array('set_uptodate' => 1, 'clear_uptodate' => 0);
		$value  = ArrayHelper::getValue($values, $this->task, 0, 'int');

		$query = $db->getQuery(true)
			->update('#__flexicontent_items_ext')
			->set($db->qn('is_uptodate') . ' = ' . (int) $value)
			->where($db->qn('item_id') . ' IN (' . implode(',', $cid) . ')');
		$db->setQuery($query)->execute();

		$message = $value == 1
			? JText::_('FLEXI_N_ITEMS_SET_UPTODATE', count($cid))
			: JText::_('FLEXI_N_ITEMS_CLEAR_UPTODATE', count($cid));
		$this->setRedirect($this->returnURL, $message);
	}

	/**
	 * 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();
		$db      = JFactory::getDbo();
		$user    = JFactory::getUser();
		$config  = JFactory::getConfig();
		$session = JFactory::getSession();
		$perms   = FlexicontentHelperPerm::getPerm();
		$isSite  = $app->isClient('site');

		$CFGsfx  = $isSite ? '_fe' : '_be';

		$ctrl_task = $isSite
			? 'task='
			: 'task=' . $this->record_name_pl . '.';
		$original_task = $this->task;

		// Retrieve form data these are subject to basic filtering
		$data   = $this->input->post->get('jform', array(), 'array');  // Unfiltered data, (Core Fields) validation will follow via jform
		$custom = $this->input->post->get('custom', array(), 'array');  // Unfiltered data, (Custom Fields) validation will be done onBeforeSaveField() of every field
		$jfdata = $this->input->post->get('jfdata', array(), 'array');  // Unfiltered data, (Core Fields) validation can be done via same jform as main data

		// 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'] = (int) $data['id'];
		$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 is is correct and then populate state
		$model->setId($data['id']);
		$model->getState();

		// Load the record
		$record = $model->getItem($data['id'], $check_view_access = false, $no_cache = true, $force_version = 0);


		/**
		 * Check if user can change item type, and other fields. Get component + type parameters
		 * ignoring modified type for existing and only using it for new items
		 * Note: Providing $force_type_id is not really needed, as the model should do it anyway
		 */
		$force_type_id = !$isnew ? $model->get('type_id') : (int) $data['type_id'];
		$params = new JRegistry( $model->getComponentTypeParams($force_type_id) );

		if (! (int) $params->get('usetitle' . $CFGsfx, 1) && (int) $params->get('auto_title', 0))
			unset($data['title']);

		if (! (int) $params->get('usealias' . $CFGsfx, 1))
			unset($data['alias']);

		if (! in_array((int) $params->get('uselang' . $CFGsfx, 1), array(1,2)))
			unset($data['language']);

		if (! (int) $params->get('useaccess' . $CFGsfx, 1))
			unset($data['access']);

		if (! (int) $params->get('usestate' . $CFGsfx, 1))
			unset($data['state']);


		/**
		 * Note: unsetting 'type_id' for new items is always off because it is meaningless
		 * Unsetting type_id has an effect for existing items only
		 * For new items model will use $data['type_id'] as default value if this is posted
		 * and if default category is not configured for this type then a validation error will occur bellow
		 */
		if (!$isnew && ! (int) $params->get('usetype' . $CFGsfx, ($isSite ? 0 : 1)))
			unset($data['type_id']);

		/**
		 * Note: unsetting 'catid' for new items will cause a validation error will occur below
		 * this will happen if a default category is not configured for current item type
		 */
		if (! (int) $params->get('usemaincat' . $CFGsfx, 1))
			unset($data['catid']);

		/**
		 * Due to legacy configuration, hidding main description text is done via a single parameter instead of 2 like the others above
		 */
		$hide = (int) $params->get('hide_maintext', 0);
		if ($hide === 1 || ($hide === 2 && $isSite) || ($hide === 3 && !$isSite))
			unset($data['text']);


		/**
		 * Make sure type is set into the given data,
		 * And if type was not given, use the type from model
		 * Then get AGAIN the component + type parameters
		 */
		$data['type_id'] = empty($data['type_id']) ? $model->get('type_id') : (int) $data['type_id'];
		$params = new JRegistry( $model->getComponentTypeParams($data['type_id']) );


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

				// Set the POSTed form data into the session, so that they get reloaded
				$app->setUserState('com_flexicontent.edit.' . $this->record_name . '.data', $data);      // Save the jform data in the session

				// 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;
				}
			}

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

			// Keep existing model data (only clear ID)
			$model->set('id', 0);
			$model->setProperty('_id', 0);
			$model->setState('item.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';
		}


		// For frontend merge the active menu parameters
		if ($isSite)
		{
			$menu = $app->getMenu()->getActive();

			if ($menu)
			{
				$params->merge($menu->getParams());
			}

			// Get some needed parameters
			$submit_redirect_url_fe = $params->get('submit_redirect_url_fe', '');
			$dolog = $params->get('print_logging_info');

			// Get submit configuration override
			if ($isnew && $original_task != 'save2copy')
			{
				$h = $data['submit_conf'];
				$item_submit_conf = $session->get('item_submit_conf', array(), 'flexicontent');

				$submit_conf      = @ $item_submit_conf[$h];
				$allowunauthorize = $params->get('allowunauthorize', 0);
				$autopublished    = @ $submit_conf['autopublished'];     // Override flag for both TYPE and CATEGORY ACL
				$overridecatperms = @ $submit_conf['overridecatperms'];  // Override flag for CATEGORY ACL
			}
			else
			{
				$submit_conf      = false;
				$allowunauthorize = false;
				$autopublished    = false;
				$overridecatperms = false;
			}

			// We use some strings from administrator part, load english language file
			// for 'com_flexicontent' component then override with current language file
			JFactory::getLanguage()->load('com_flexicontent', JPATH_ADMINISTRATOR, 'en-GB', true);
			JFactory::getLanguage()->load('com_flexicontent', JPATH_ADMINISTRATOR, null, true);
		}

		// Get some flags this will also trigger item loading if not already loaded
		$isOwner = $model->get('created_by') == $user->get('id');

		// Unique id for new items, needed by some fields for temporary data
		$unique_tmp_itemid = $this->input->get('unique_tmp_itemid', '', 'string');
		$unique_tmp_itemid = substr($unique_tmp_itemid, 0, 1000);


		/**
		 * Some default values (further checks for these will be done later)
		 */

		// Auto title for some content types, set it to pass validation. NOTE real value will be created via onBeforeSaveField event
		if ($params->get('auto_title', 0))
		{
			$data['title'] = (int) $data['id'];  // Item id or ZERO for new items
		}

		// Check of empty tags, (later we will check if these are allowed to be changed or they were not shown !)
		if (!isset($data['tag']))
		{
			$data['tag'] = array();
		}

		// Check of empty categories, (later we will check if these are allowed to be changed or they were not shown !)
		if (!isset($data['cid']))
		{
			$data['cid'] = array();
		}


		/**
		 * Check for zero tags posted (also considering if tags editing is permitted to current user)
		 */

		// No permission to change tags or tags were not displayed
		$tags_shown = $isSite
			? (int) $params->get('usetags_fe', 1) === 1
			: true;

		if (!$perms->CanUseTags || ! $tags_shown)
		{
			unset($data['tag']);
		}


		/**
		 * ENFORCE can change category ACL perms
		 */

		// Per content type change category permissions
		$current_type_id  = $model->get('type_id') ?: (int) @ $data['type_id'];
		$CanChangeFeatCat = $user->authorise('flexicontent.change.cat.feat', 'com_flexicontent.type.' . $current_type_id);
		$CanChangeSecCat  = $user->authorise('flexicontent.change.cat.sec', 'com_flexicontent.type.' . $current_type_id);
		$CanChangeCat     = $user->authorise('flexicontent.change.cat', 'com_flexicontent.type.' . $current_type_id);

		$AutoApproveChanges = $perms->AutoApproveChanges;

		$canchange_featcat = $perms->MultiCat && $CanChangeFeatCat;
		$canchange_seccat  = $perms->MultiCat && $CanChangeSecCat;

		// Enforce featured categories if user is not allowed to changed
		$featured_cats_parent = $params->get('featured_cats_parent', 0);
		$featured_cats = array();

		if ($featured_cats_parent && !$canchange_featcat)
		{
			$featured_tree = flexicontent_cats::getCategoriesTree($published_only = 1, $parent_id = $featured_cats_parent, $depth_limit = 0);
			$disabled_cats = $params->get('featured_cats_parent_disable', 1) ? array($featured_cats_parent) : array();

			$featured_cid = array();

			if (!$isnew)
			{
				foreach ($model->get('categories') as $item_cat)
				{
					if (isset($featured_tree[$item_cat]) && !isset($disabled_cats[$item_cat]))
					{
						$featured_cid[] = $item_cat;
					}
				}
			}

			$data['featured_cid'] = $featured_cid;
		}


		/**
		 * Enforce maintaining secondary categories if user is not allowed to change / set secondary cats
		 * NOTE: This DOES NOT ENFORCE SUBMIT MENU category configuration, this is done later by the model store()
		 */

		$show_seccats = (int) $params->get('show_seccats' . $CFGsfx, 2);

		if (!$canchange_seccat || $show_seccats === 0)
		{
			unset($data['cid']);

			// For new item use default secondary categories from type configuration
			if ($isnew)
			{
				$data['cid'] = $params->get('cid_default');
			}

			// Filter featured cats out of secondary cats
			else
			{
				$featured_cid_arr = isset($data['featured_cid']) ? array_flip($data['featured_cid']) : array();
				$sec_cid = array();

				// User cannot change secondary categories, reset them, excluding featured cats and existing (possibly changed) main category
				foreach ($model->get('cats') as $item_cat)
				{
					if (!isset($featured_cid_arr[$item_cat]) && $item_cat != $model->get('catid'))
					{
						$sec_cid[] = $item_cat;
					}
				}

				$data['cid'] = $sec_cid;
			}
		}
		//var_dump($data['cid']); exit;


		/**
		 * Enforce maintaining main category if user is not allowed to change / set main category
		 * NOTE: This DOES NOT ENFORCE SUBMIT MENU category configuration, this is done later by the model store()
		 * NOTE: data['catid'] has already been unset above in case that field is set to be hidden
		 */

		if (!$CanChangeCat || empty($data['catid']))
		{
			// For new item use default main category from type configuration
			if ($isnew && $params->get('catid_default'))
			{
				$data['catid'] = $params->get('catid_default');
			}

			// Use already assigned main category (existing item)
			elseif ($model->get('catid'))
			{
				$data['catid'] = $model->get('catid');
			}
		}

		// These need to be an array during validation
		if (!isset($data['rules']) || !is_array($data['rules']))
		{
			$data['rules'] = array();
		}


		/**
		 * Basic Form data validation
		 */

		// Get the JForm object, but do not pass any data we only want the form object,
		// in order to validate the data and not create a filled-in form
		$form = $model->getForm();


		/**
		 * Check custom-injected (non-JForm field) (frontend) captcha field
		 */
		if ($isSite)
		{
			$use_captcha    = $params->get('use_captcha', 1);     // 1 for guests, 2 for any user
			$captcha_formop = $params->get('captcha_formop', 0);  // 0 for submit, 1 for submit/edit (aka always)
			$is_submitop = ((int) $data['id']) == 0;
			$display_captcha = $use_captcha >= 2 || ( $use_captcha == 1 &&  $user->guest );
			$display_captcha = $display_captcha && ( $is_submitop || $captcha_formop);  // for submit operation we do not need to check 'captcha_formop' ...
			if ($display_captcha)
			{
				$c_plugin = $params->get('captcha', $app->getCfg('captcha')); // TODO add param to override default
				if ($c_plugin)
				{
					$c_name = 'captcha_response_field';
					$c_value = $this->input->get($c_name, '', 'string');
					$c_id = $c_plugin=='recaptcha' ? 'dynamic_recaptcha_1' : 'fc_dynamic_captcha';
					$c_namespace = 'fc_item_form';

					$captcha_obj = JCaptcha::getInstance($c_plugin, array('namespace' => $c_namespace));
					if (!$captcha_obj->checkAnswer($c_value))
					{
						// Get the captch validation message and push it out to the user
						//$error = $captcha_obj->getError();
						//$app->enqueueMessage($error instanceof Exception ? $error->getMessage() : $error, 'error');
						$app->enqueueMessage(JText::_('FLEXI_CAPTCHA_FAILED') .' '. JText::_('FLEXI_MUST_REFILL_SOME_FIELDS'), 'error');

						// Set the POSTed form data into the session, so that they get reloaded
						$app->setUserState($form->option.'.edit.item.data', $data);      // Save the jform data in the session.
						$app->setUserState($form->option.'.edit.item.custom', $custom);  // Save the custom fields data in the session.
						$app->setUserState($form->option.'.edit.item.jfdata', $jfdata);  // Save the falang translations into the session
						$app->setUserState($form->option.'.edit.item.unique_tmp_itemid', $unique_tmp_itemid);  // Save temporary unique item id into the session

						// Captcha error, reload edit form using referer URL
						$this->setRedirect($this->refererURL);

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

		// Validate Form data (record properties and parameters specified in XML file)
		$validated_data = $model->validate($form, $data);


		/**
		 * Perform validation / manipulation of the already validated data,
		 * run this even if validation failed, in case we want to handle this case too
		 */
		$extraChecks = $this->_afterModelValidation($validated_data, $data, $model);


		/**
		 * Redirect on validation errors or on other checks failing
		 */
		if (!$validated_data || !$extraChecks)
		{
			// Check for validation errors
			if (!$validated_data)
			{
				// Get the validation messages and push up to three validation messages out to the user
				$errors	= $form->getErrors();

				for ($i = 0, $n = count($errors); $i < $n && $i < 3; $i++)
				{
					$app->enqueueMessage($errors[$i] instanceof Exception ? $errors[$i]->getMessage() : $errors[$i], 'error');
				}
			}

			// Check for errors in after-validation handler
			if (!$extraChecks)
			{
				$app->enqueueMessage($model->getError() ?: JText::_('FLEXI_ERROR_SAVING_' . $this->_NAME), 'error');
			}

			// Set the POSTed form data into the session, so that they get reloaded
			$app->setUserState($form->option . '.edit.' . $form->context . '.data', $data);      // Save the jform data in the session
			$app->setUserState($form->option . '.edit.' . $form->context . '.custom', $custom);  // Save the custom fields data in the session
			$app->setUserState($form->option . '.edit.' . $form->context . '.jfdata', $jfdata);  // Save the falang translations into the session
			$app->setUserState($form->option . '.edit.' . $form->context . '.unique_tmp_itemid', $unique_tmp_itemid);  // Save temporary unique item id into the session

			// Validation error, reload edit form using referer URL
			$this->setRedirect($this->refererURL);

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

		// Validate jfdata using same JForm
		$validated_jf = array();

		foreach ($jfdata as $lang_index => $lang_data)
		{
			foreach ($lang_data as $i => $v)
			{
				$validated_jf[$lang_index][$i] = flexicontent_html::dataFilter($v, ($i != 'text' ? 4000 : 0), 2, 0);
			}
		}

		// Some values need to be assigned after validation
		$validated_data['custom']  = & $custom;          // Assign array of custom field values, they are in the 'custom' form array instead of jform (validation will follow at each field)
		$validated_data['jfdata']  = & $validated_jf;    // Assign array of Joomfish field values, they are in the 'jfdata' form array instead of jform (validated above)

		/**
		 * Assign template parameters of the select ilayout as an sub-array (the DB model will handle the merging of parameters)
		 * Usually this will be set in backend, but usually not in frontend, if frontend template editing is not shown
		 */
		$ilayout = isset($data['attribs']['ilayout']) ? $data['attribs']['ilayout'] : null;

		// Give UNVALIDATED data for the case of LAYOUTS to the MODEL. Model will load the
		// XML file of the layout into a JForm object and do validation before merging them
		if ($ilayout && !empty($data['layouts'][$ilayout]))
		{
			$validated_data['attribs']['layouts'] = $data['layouts'];
		}

		// USEFULL FOR DEBUGING (do not remove commented code)
		// $diff_arr = array_diff_assoc ( $data, $validated_data);
		// echo "<pre>"; print_r($diff_arr); jexit();


		/**
		 * PERFORM ACCESS CHECKS, NOTE: we need to check access again, despite having
		 * checked them on edit form load, because user may have tampered with the form ...
		 */

		$itemAccess = $model->getItemAccess();
		$canAdd  = $itemAccess->get('access-create');  // Includes check of creating in at least one category
		$canEdit = $itemAccess->get('access-edit');    // includes privileges edit and edit-own

		$type_id = (int) $validated_data['type_id'];

		// Existing item with Type not being ALTERED, content type can be maintained regardless of privilege
		if (!$isnew && $model->get('type_id') == $type_id)
		{
			$canCreateType = true;
		}

		// New item or existing item with Type is being ALTERED, check privilege to create items of this type
		else
		{
			$canCreateType = $model->canCreateType(array($type_id), true, $types);
		}


		/**
		 * Calculate user's CREATE / EDIT privileges on current content item
		 */

		$hasCoupon = false;  // Normally used in frontend only

		if (!$isnew)
		{
			if (!$canEdit)
			{
				// No edit privilege, check if item is editable till logoff
				if ($session->has('rendered_uneditable', 'flexicontent'))
				{
					$rendered_uneditable = $session->get('rendered_uneditable', array(), 'flexicontent');
					$canEdit = isset($rendered_uneditable[$model->get('id')]) && $rendered_uneditable[$model->get('id')];
					$hasCoupon = isset($rendered_uneditable[$model->get('id')]) && $rendered_uneditable[$model->get('id')] == 2;  // Editable via coupon
				}
			}
		}

		// Special CASEs of overriding CREATE ACL in FrontEnd via menu item
		elseif ($isSite)
		{
			// Allow creating via submit menu OVERRIDE
			if ($allowunauthorize)
			{
				$canAdd = true;
				$canCreateType = true;
			}

			// If without create privelege and category override is enabled then only check type and do not check category ACL
			elseif (!$canAdd)
			{
				$canAdd = $overridecatperms && $canCreateType;
			}
		}

		// New item: check if user can create in at least one category
		if ($isnew && !$canAdd)
		{
			$app->enqueueMessage(JText::_('FLEXI_NO_ACCESS_CREATE'), 'error');
			$app->setHeader('status', 403, true);
			$this->setRedirect($this->returnURL);

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

		// Existing item: Check if user can edit current item
		if (!$isnew && !$canEdit)
		{
			$app->enqueueMessage(JText::_('FLEXI_NO_ACCESS_EDIT'), 'error');
			$app->setHeader('status', 403, true);
			$this->setRedirect($this->returnURL);

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

		if (!$canCreateType)
		{
			$msg = isset($types[$type_id])
				? JText::sprintf('FLEXI_NO_ACCESS_CREATE_CONTENT_OF_TYPE', JText::_($types[$type_id]->name))
				: ' Content Type ' . $type_id . ' was not found OR is not published';

			$app->enqueueMessage($msg, 'error');
			$app->setHeader('status', 403, true);
			$this->setRedirect($this->returnURL);

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

		// Get "BEFORE SAVE" categories for information mail
		$before_cats = array();

		if (!$isnew)
		{
			$query 	= 'SELECT DISTINCT c.id, c.title FROM #__categories AS c'
				. ' JOIN #__flexicontent_cats_item_relations AS rel ON rel.catid = c.id'
				. ' WHERE rel.itemid = ' . (int) $model->get('id');
			$before_cats = $db->setQuery($query)->loadObjectList('id');
			$before_maincat = $model->get('catid');
		}


		/**
		 * Try to store the form data into the item
		 */

		// If saving fails, do any needed cleanup, and then redirect back to record form
		if (!$model->store($validated_data))
		{
			if (empty($model->abort_redirect_url))
			{
				// Set the POSTed form data into the session, so that they get reloaded
				$app->setUserState($form->option . '.edit.' . $form->context . '.data', $data);      // Save the jform data in the session
				$app->setUserState($form->option . '.edit.' . $form->context . '.custom', $custom);  // Save the custom fields data in the session
				$app->setUserState($form->option . '.edit.' . $form->context . '.jfdata', $jfdata);  // Save the falang translations into the session
				$app->setUserState($form->option . '.edit.' . $form->context . '.unique_tmp_itemid', $unique_tmp_itemid);  // Save temporary unique item id into the session
			}

			// Set error message and the redirect URL (back to the record form)
			$app->setHeader('status', '500 Internal Server Error', true);
			$app->enqueueMessage($model->getError() ?: JText::_('FLEXI_ERROR_SAVING_' . $this->_NAME), '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(!empty($model->abort_redirect_url) ? $model->abort_redirect_url: $this->returnURL);
			}

			// 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();
		$validated_data['id'] = $isnew ? (int) $model->get('id') : $validated_data['id'];

		// Get items marked as newly submitted
		$newly_submitted = $session->get('newly_submitted', array(), 'flexicontent');

		if ($isnew)
		{
			// Mark item as newly submitted, to allow to a proper "THANKS" message after final save & close operation (since user may have clicked add instead of add & close)
			$newly_submitted[$model->get('id')] = 1;
			$session->set('newly_submitted', $newly_submitted, 'flexicontent');
		}

		$newly_submitted_item = @ $newly_submitted[$model->get('id')];


		/**
		 * Get newly saved -latest- version (store task gets latest) of the item, and also calculate publish privelege
		 */
		$item = $model->getItem($validated_data['id'], $check_view_access = false, $no_cache = true, $force_version = -1);
		$canPublish = $model->canEditState($item) || $hasCoupon;


		/**
		 * Use session to detect multiple item saves to avoid sending notification EMAIL multiple times
		 */
		$is_first_save = true;

		if ($session->has('saved_fcitems', 'flexicontent'))
		{
			$saved_fcitems = $session->get('saved_fcitems', array(), 'flexicontent');
			$is_first_save = $isnew ? true : !isset($saved_fcitems[$model->get('id')]);
		}

		// Add item to saved items of the corresponding session array
		$saved_fcitems[$model->get('id')] = $timestamp = time();  // Current time as seconds since Unix epoc;
		$session->set('saved_fcitems', $saved_fcitems, 'flexicontent');


		/**
		 * Get categories added / removed from the item
		 */
		$query 	= 'SELECT DISTINCT c.id, c.title FROM #__categories AS c'
			. ' JOIN #__flexicontent_cats_item_relations AS rel ON rel.catid = c.id'
			. ' WHERE rel.itemid = ' . (int) $model->get('id');
		$after_cats = $db->setQuery($query)->loadObjectList('id');

		if (!$isnew)
		{
			$cats_added_ids = array_diff(array_keys($after_cats), array_keys($before_cats));

			foreach ($cats_added_ids as $cats_added_id)
			{
				$cats_added_titles[] = $after_cats[$cats_added_id]->title;
			}

			$cats_removed_ids = array_diff(array_keys($before_cats), array_keys($after_cats));

			foreach ($cats_removed_ids as $cats_removed_id)
			{
				$cats_removed_titles[] = $before_cats[$cats_removed_id]->title;
			}

			$cats_altered = count($cats_added_ids) + count($cats_removed_ids);
			$after_maincat = $model->get('catid');
		}

		/**
		 * Track category changes and nullify return URL
		 */
		if ($isSite)
		{
			$cats_changed = $session->get('cats_changed', array(), 'flexicontent');

			if (!empty($cats_altered))
			{
				$cats_changed[$this->record_name][$model->get('id')] = -1;
				$session->set('cats_changed', $cats_changed, 'flexicontent');
			}
		}


		/**
		 * We need to get emails to notify, from Global/item's Content Type parameters -AND- from item's categories parameters
		 */
		$notify_emails = array();

		if ($is_first_save || $cats_altered || $params->get('nf_enable_debug', 0))
		{
			// Get needed flags regarding the saved items
			$approve_version = 2;
			$pending_approval_state = -3;
			$draft_state = -4;

			$current_version = FLEXIUtilities::getCurrentVersions($item->id, true); // Get current item version
			$last_version    = FLEXIUtilities::getLastVersions($item->id, true);    // Get last version (=latest one saved, highest version id),

			// $validated_data variables vstate & state may have been (a) tampered in the form, and/or (b) altered by save procedure so better not use them
			$needs_version_reviewal     = !$isnew && ($last_version > $current_version) && !$canPublish && !$AutoApproveChanges;
			$needs_publication_approval = $isnew && ($item->state == $pending_approval_state) && !$canPublish;

			$draft_from_non_publisher = $item->state == $draft_state && !$canPublish;

			if ($draft_from_non_publisher)
			{
				// Suppress notifications for draft-state items (new or existing ones), for these each author will publication approval manually via a button
				$nConf = false;
			}
			else
			{
				// Get notifications configuration and select appropriate emails for current saving case
				$nConf = $model->getNotificationsConf($params);  // echo "<pre>"; print_r($nConf); "</pre>";
			}

			if ($nConf)
			{
				$states_notify_new = $params->get('states_notify_new', array(1, 0, 2, -3, -4, -5));

				if (empty($states_notify_new))
				{
					$states_notify_new = array();
				}

				elseif (! is_array($states_notify_new))
				{
					$states_notify_new = !FLEXI_J16GE ? array($states_notify_new) : explode("|", $states_notify_new);
				}

				$states_notify_existing = $params->get('states_notify_existing', array(1, 0, 2, -3, -4, -5));

				if (empty($states_notify_existing))
				{
					$states_notify_existing = array();
				}

				elseif (! is_array($states_notify_existing))
				{
					$states_notify_existing = !FLEXI_J16GE ? array($states_notify_existing) : explode("|", $states_notify_existing);
				}

				$n_state_ok = in_array($item->state, $states_notify_new);
				$e_state_ok = in_array($item->state, $states_notify_existing);

				if ($needs_publication_approval)
				{
					$notify_emails = $nConf->emails->notify_new_pending;
				}
				elseif ($isnew && $n_state_ok)
				{
					$notify_emails = $nConf->emails->notify_new;
				}
				elseif ($isnew)
				{
					$notify_emails = array();
				}
				elseif ($needs_version_reviewal)
				{
					$notify_emails = $nConf->emails->notify_existing_reviewal;
				}
				elseif (!$isnew && $e_state_ok)
				{
					$notify_emails = $nConf->emails->notify_existing;
				}
				elseif (!$isnew)
				{
					$notify_emails = array();
				}

				if ($needs_publication_approval)
				{
					$notify_text = $params->get('text_notify_new_pending');
				}
				elseif ($isnew)
				{
					$notify_text = $params->get('text_notify_new');
				}
				elseif ($needs_version_reviewal)
				{
					$notify_text = $params->get('text_notify_existing_reviewal');
				}
				elseif (!$isnew)
				{
					$notify_text = $params->get('text_notify_existing');
				}

				// print_r($notify_emails); jexit();
			}
		}


		/**
		 * If there are emails to notify for current saving case, then send the notifications emails, but
		 */

		if (!empty($notify_emails))
		{
			$notify_vars = new stdClass;
			$notify_vars->needs_version_reviewal     = $needs_version_reviewal;
			$notify_vars->needs_publication_approval = $needs_publication_approval;
			$notify_vars->isnew         = $isnew;
			$notify_vars->notify_emails = $notify_emails;
			$notify_vars->notify_text   = $notify_text;
			$notify_vars->before_cats   = $before_cats;
			$notify_vars->after_cats    = $after_cats;
			$notify_vars->original_item = $record;

			$model->sendNotificationEmails($notify_vars, $params, $manual_approval_request = 0);
		}


		/**
		 * Recalculate EDIT PRIVILEGE of new item. Reason for needing to do this is because we can have create permission in a category
		 * and thus being able to set this category as item's main category, but then have no edit/editown permission for this category
		 */

		// This will clear JAcesse cache by calling: Access::clearStatics(), but it will also clear caches inside relevant inside JUser
		$user->clearAccessRights();

		// Now we can recalculate
		$asset   = 'com_content.article.' . $model->get('id');
		$canEdit = $user->authorise('core.edit', $asset) || ($user->authorise('core.edit.own', $asset) && $isOwner);


		/**
		 * Check if user can not edit item further (due to changed main category, without edit/editown permission)
		 */

		if (!$canEdit)
		{
			// APPLY TASK: Temporarily set item to be editable till closing it and not through all session
			// (we will/should clear this flag when item is closed, since we have another flag to indicate new items
			if (in_array($this->task, array('apply', 'apply_type')))
			{
				$rendered_uneditable = $session->get('rendered_uneditable', array(), 'flexicontent');
				$rendered_uneditable[$model->get('id')] = -1;
				$session->set('rendered_uneditable', $rendered_uneditable, 'flexicontent');
				$canEdit = 1;
			}

			// NEW ITEM: Do not use editable till logoff behaviour
			// ALSO: Clear editable FLAG set in the case that 'apply' button was used during new item creation
			elseif ($newly_submitted_item)
			{
				if (!$params->get('items_session_editable', 1))
				{
					$rendered_uneditable = $session->get('rendered_uneditable', array(), 'flexicontent');

					if (isset($rendered_uneditable[$model->get('id')]))
					{
						unset($rendered_uneditable[$model->get('id')]);
						$session->set('rendered_uneditable', $rendered_uneditable, 'flexicontent');
					}
				}
			}

			// EXISTING ITEM: (if enabled) Use the editable till logoff behaviour
			else
			{
				if ($params->get('items_session_editable', 1))
				{
					// Set notice for existing item being editable till logoff
					$app->enqueueMessage(JText::_('FLEXI_CANNOT_EDIT_AFTER_LOGOFF'), 'notice');

					// Allow item to be editable till logoff
					$rendered_uneditable = $session->get('rendered_uneditable', array(), 'flexicontent');
					$rendered_uneditable[$model->get('id')]  = 1;
					$session->set('rendered_uneditable', $rendered_uneditable, 'flexicontent');
					$canEdit = 1;
				}
			}

			// Set notice about saving an item that cannot be changed further
			if (!$canEdit)
			{
				$app->enqueueMessage(JText::_('FLEXI_CANNOT_MAKE_FURTHER_CHANGES_TO_CONTENT'), 'notice');
			}
		}


		/**
		 * Check for new Content Item is being closed, and clear some flags
		 */

		if (!in_array($this->task, array('apply', 'apply_type')) && $newly_submitted_item)
		{
			// Clear item from being marked as newly submitted
			unset($newly_submitted[$model->get('id')]);
			$session->set('newly_submitted', $newly_submitted, 'flexicontent');

			// The 'apply' task may set 'editable till logoff' FLAG ...
			// CLEAR IT, since NEW content this is meant to be used temporarily
			if (!$params->get('items_session_editable', 1))
			{
				$rendered_uneditable = $session->get('rendered_uneditable', array(), 'flexicontent');

				if (isset($rendered_uneditable[$model->get('id')]))
				{
					unset($rendered_uneditable[$model->get('id')]);
					$session->set('rendered_uneditable', $rendered_uneditable, 'flexicontent');
				}
			}
		}


		/**
		 * Check if Content Item is being closed, and clear some flags, like cats_changed
		 */
		if ($isSite)
		{
			if (!in_array($this->task, array('apply', 'apply_type')))
			{
				$cats_changed = $session->get('cats_changed', array(), 'flexicontent');
				//echo '<pre>'; print_r($cats_changed); exit;

				if (!empty($cats_changed[$this->record_name][$model->get('id')]))
				{
					unset($cats_changed[$this->record_name][$model->get('id')]);
					$session->set('cats_changed', $cats_changed, 'flexicontent');

					$this->returnURL = null;
				}
			}
		}


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

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

		switch ($this->task)
		{
			// REDIRECT CASE FOR APPLY / SAVE AS COPY: Save and reload the edit form
			case 'apply':
			case 'apply_type':
				if ($app->isClient('administrator'))
				{
					$link = 'index.php?option=com_flexicontent&' . $ctrl_task . 'edit&view=' . $this->record_name . '&id=' . (int) $model->get('id') . ($tmpl ? '&tmpl=' . $tmpl : '');
				}
				else
				{
					// Create the URL, maintain current menu item if this was given
					$Itemid = $this->input->get('Itemid', 0, 'int');
					$item_url = JRoute::_(FlexicontentHelperRoute::getItemRoute($item->slug, $item->categoryslug, $Itemid));

					// Set task to 'edit', and pass original referer back to avoid making the form itself the referer, but also check that it is safe enough
					$link = $item_url
						. ( strstr($item_url, '?') ? '&' : '?' ) . 'task=edit'
						. '&return='.base64_encode($this->returnURL ?: $item_url)
						. ($tmpl ? '&tmpl=' . $tmpl : '');
				}
				break;

			// REDIRECT CASE FOR SAVE and NEW: Save and load new record form
			case 'save2new':
				if ($app->isClient('administrator'))
				{
					$link = 'index.php?option=com_flexicontent&view=' . $this->record_name
						. '&typeid=' . $model->get('type_id')
						. '&filter_cats=' . $model->get('catid')
						. ($tmpl ? '&tmpl=' . $tmpl : '');
				}
				else
				{
					// Create the URL, maintain current menu item if this was given
					$Itemid = $this->input->get('Itemid', 0, 'int');
					$item_url = 'index.php?option=com_flexicontent&view=item&task=add'
						. '&typeid=' . $model->get('type_id')
						. '&maincat=' . $model->get('catid')
						. '&Itemid=' . $Itemid
						. '&return='.base64_encode($this->returnURL ?: $item_url)
						. ($tmpl ? '&tmpl=' . $tmpl : '');

					// Set task to 'edit', and pass original referer back to avoid making the form itself the referer, but also check that it is safe enough
					$link = JRoute::_($item_url, false);
				}
				break;

			// REDIRECT CASES FOR SAVING
			default:

				// REDIRECT CASE: Save and preview the latest version
				if ($this->task === 'save_a_preview')
				{
					// Do not use SLUG !!! since we maybe previewing a non-current version !!
					$item_url = FlexicontentHelperRoute::getItemRoute($model->get('id') . ':' . $model->get('alias'), $model->get('catid'), 0, $item);
					$link = JRoute::_($item_url . ($tmpl ? '&tmpl=' . $tmpl : '') . '&preview=1', false);
				}

				elseif ($app->isClient('administrator'))
				{
					$link = $this->returnURL;
				}

				// REDIRECT CASE: Return to a custom page after creating a new item (e.g. a thanks page)
				elseif ($newly_submitted_item && $submit_redirect_url_fe)
				{
					$link = $submit_redirect_url_fe;
				}

				// REDIRECT CASE: Return to the form 's original referer after item saving
				else
				{
					$msg = $newly_submitted_item
						? JText::_('FLEXI_THANKS_SUBMISSION')
						: JText::_('FLEXI_ITEM_SAVED');

					$item_url = FlexicontentHelperRoute::getItemRoute($item->slug, $item->categoryslug, 0, $item);
					$link = $this->returnURL ?: JRoute::_($item_url . ($tmpl ? '&tmpl=' . $tmpl : ''), false);
				}
				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());
		}
	}


	/**
	 * 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();
		$isAdmin  = $app->isClient('administrator');

		$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', $app->isClient('administrator') ? 'default' : 'form', 'string');
		$view = $this->getView($viewName, $viewType, '', array('base_path' => $this->basePath, 'layout' => $viewLayout));

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

		// Indicate to model that current view IS item form
		$model->isForm = true;

		// Indicate to model to merge menu parameters if menu matches
		$model->mergeMenuParams = true;

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

		// Try to load by unique ID or NAME
		else
		{
			// Force model to load versioned data (URL specified version or latest version (last saved))
			$version = $isAdmin
				? $this->input->get('version', 0, 'int')   // Load specific item version (non-zero), 0 version: is unversioned data, -1 version: is latest version (=default for edit form)
				: 0;
			$record  = $model->getItem(null, $check_view_access = false, $no_cache = true, $force_version = ($version != 0 ? $version : -1));  // -1 version means latest
		}

		// Set error message for models that do not throw exception
		if (!$record)
		{
			$app->setHeader('status', '404', true);
			$app->enqueueMessage(JText::_('FLEXI_OPERATION_FAILED') . ' : ' . $model->getError(), 'error');

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

			return;
		}

		$model->isForm = true;
		$isnew = !$model->getId();

		// 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;


		/**
		 * ACL checks only for BACKEND,
		 * since for FRONTEND we allow ACL override via menu
		 * and this checks for ACL and ACL override via menu
		 * are currently inside the frontent VIEW code
		 * Also FRONTEND VIEW will do the record CHECKOUT
		 */
		if ($isAdmin)
		{
			// Calculate access
			$canAdd  = $model->getItemAccess()->get('access-create');
			$canEdit = $model->getItemAccess()->get('access-edit');

			if (!$canEdit)
			{
				// No edit privilege, check if item is editable till logoff
				if ($session->has('rendered_uneditable', 'flexicontent'))
				{
					$rendered_uneditable = $session->get('rendered_uneditable', array(), 'flexicontent');
					$canEdit = isset($rendered_uneditable[$model->get('id')]) && $rendered_uneditable[$model->get('id')];
				}
			}

			// New item: check if user can create in at least one category
			if ($isnew)
			{
				// A. Check create privilege
				if (!$canAdd)
				{
					$app->setHeader('status', '403 Forbidden', true);
					$this->setRedirect($this->returnURL, JText::_('FLEXI_NO_ACCESS_CREATE'), 'error');

					$model->enqueueMessages($_exclude = array('showAfterLoad' => 1));

					return;
				}

				// Get User Group / Author parameters
				$db = JFactory::getDbo();
				$authorparams = flexicontent_db::getUserConfig($user->id);
				$max_auth_limit = intval($authorparams->get('max_auth_limit', 0));  // Maximum number of content items the user can create

				// B. Check if max authored content limit reached
				if ($max_auth_limit)
				{
					$db->setQuery('SELECT COUNT(id) FROM #__content WHERE created_by = ' . $user->id);
					$authored_count = $db->loadResult();

					if ($authored_count >= $max_auth_limit)
					{
						$app->setHeader('status', '403 Forbidden', true);
						$this->setRedirect($this->returnURL, JText::sprintf('FLEXI_ALERTNOTAUTH_CREATE_MORE', $max_auth_limit), 'warning');

						$model->enqueueMessages($_exclude = array('showAfterLoad' => 1));

						return;
					}
				}

				// C. Check if Content Type can be created by current user
				$typeid = $this->input->get('typeid', 0, 'int');
				$canCreateType = $typeid
					? $model->canCreateType(array($typeid), true, $types)  // Can create given Content Type
					: $model->canCreateType();  // Can create at least one Content Type

				if (!$canCreateType)
				{
					// Check if Content Type exists
					if ($typeid && !isset($types[$typeid]))
					{
						$status = '400 Bad Request';
						$msg = 'Type ID: '.$typeid.' not found';
					}
					else
					{
						$status    = '403 Forbidden';
						$type_name = isset($types[$typeid])
							? '"' . JText::_($types[$typeid]->name) . '"'
							: JText::_('FLEXI_ANY');
						$msg = JText::sprintf('FLEXI_NO_ACCESS_CREATE_CONTENT_OF_TYPE', $type_name);
					}

					$app->setHeader('status', $status, true);
					$this->setRedirect($this->returnURL, $msg, 'error');

					$model->enqueueMessages($_exclude = array('showAfterLoad' => 1));

					return;
				}
			}

			// Existing item: Check if user can edit current item
			else
			{
				if (!$canEdit)
				{
					$app->setHeader('status', '403 Forbidden', true);
					$this->setRedirect($this->returnURL, JText::_('FLEXI_NO_ACCESS_EDIT'), 'error');

					$model->enqueueMessages($_exclude = array('showAfterLoad' => 1));

					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);
					}

					// Do not add messages meant only if form load succeeds
					$model->enqueueMessages($_exclude = array('showAfterLoad' => 1));

					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);
					}

					// Do not add messages meant only if form load succeeds
					$model->enqueueMessages($_exclude = array('showAfterLoad' => 1));

					return;
				}
			}
		}


		// We succeeded, enqueue all minor model messages / notices
		$model->enqueueMessages();

		// 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');

		parent::_cleanCache();

		$cache_site = FLEXIUtilities::getCache($group = '', $client = 0);
		$cache_site->clean('com_flexicontent_items');
		$cache_site->clean('com_flexicontent_filters');

		$cache_admin = FLEXIUtilities::getCache($group = '', $client = 1);
		$cache_admin->clean('com_flexicontent_items');
		$cache_admin->clean('com_flexicontent_filters');

		// Also clean this as it contains Joomla frontend view cache of the component)
		$cache_site->clean('com_flexicontent');
	}


	/**
	 * Method for extra form validation after JForm validation is executed
	 *
	 * @param   array     $validated_data  The already jform-validated data of the record
	 * @param   object    $model            The Model object of current controller instance
	 * @param   array     $data            The original posted data of the record
	 *
	 * @return  boolean   true on success, false on failure
	 *
	 * @since 3.3
	 */
	protected function _afterModelValidation(& $validated_data, & $data, $model)
	{
		$this->input->get('task', '', 'cmd') !== __FUNCTION__ or die(__FUNCTION__ . ' : direct call not allowed');

		return true;
	}


	/**
	 * Method for doing some record type specific work before calling model store
	 *
	 * @return  boolean   true on success, false on failure
	 *
	 * @since 3.3
	 */
	protected function _beforeModelStore(& $validated_data, & $data, $model)
	{
		$this->input->get('task', '', 'cmd') !== __FUNCTION__ or die(__FUNCTION__ . ' : direct call not allowed');

		return true;
	}


	/**
	 * Logic to order up/down a record
	 *
	 * @return void
	 *
	 * @since 3.3
	 */
	public function reorder($dir = null)
	{
		// Check for request forgeries
		JSession::checkToken('request') or die(JText::_('JINVALID_TOKEN'));

		$app   = JFactory::getApplication();
		$model = $this->getModel($this->record_name_pl);
		$user  = JFactory::getUser();

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

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

		// Get record id and ordering group
		$cid         = $this->input->get('cid', array(0), 'array');
		$filter_cats = $this->input->get('filter_cats', array(0), 'array');

		$cid = ArrayHelper::toInteger($cid);
		$filter_cats = ArrayHelper::toInteger($filter_cats);

		// Make sure direction is set
		$dir = $dir ?: ($this->task === 'orderup' ? -1 : 1);

		if (!$model->move($dir, reset($filter_cats)))
		{
			$app->setHeader('status', '500 Internal Server Error', true);
			$app->enqueueMessage(JText::_('FLEXI_ERROR_SAVING_ORDER') . ': ' . $model->getError(), 'error');
			$app->redirect($this->returnURL);
		}

		// Note we no longer set the somewhat redundant message: JText::_('FLEXI_NEW_ORDERING_SAVED')
		$this->setRedirect($this->returnURL);
	}


	/**
	 * Logic to orderup a record, wrapper for reorder method
	 *
	 * @return void
	 *
	 * @since 3.3
	 */
	public function orderup()
	{
		$this->reorder($dir = -1);
	}


	/**
	 * Logic to orderdown a record, wrapper for reorder method
	 *
	 * @return void
	 *
	 * @since 3.3
	 */
	public function orderdown()
	{
		$this->reorder($dir = 1);
	}


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

		$app   = JFactory::getApplication();
		$model = $this->getModel($this->record_name_pl);
		$user  = JFactory::getUser();

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

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

		// Get record ids, new orderings and the ordering group
		$cid         = $this->input->get('cid', array(0), 'array');
		$order       = $this->input->get('order', array(0), 'array');
		$filter_cats = $this->input->get('filter_cats', array(0), 'array');

		$cid = ArrayHelper::toInteger($cid);
		$order = ArrayHelper::toInteger($order);
		$filter_cats = ArrayHelper::toInteger($filter_cats);

		if (!$model->saveorder($cid, $order, reset($filter_cats)))
		{
			$app->setHeader('status', 500);
			$app->enqueueMessage(JText::_('FLEXI_ERROR_SAVING_ORDER') . ': ' . $model->getError(), 'error');
			$app->redirect($this->returnURL);
		}

		// Note we no longer set the somewhat redundant message: JText::_('FLEXI_NEW_ORDERING_SAVED')
		$this->setRedirect($this->returnURL);
	}


	/**
	 * Logic to handle batch actions on records: copy / move / update
	 *
	 * @return void
	 *
	 * @since 1.5
	 */
	public function batchprocess()
	{
		// Check for request forgeries
		JSession::checkToken('request') or die(JText::_('JINVALID_TOKEN'));

		$app   = JFactory::getApplication();
		$db    = JFactory::getDbo();
		$model = $this->getModel($this->record_name_pl);
		$user  = JFactory::getUser();

		$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', true);
			$app->enqueueMessage(JText::_('FLEXI_NO_ITEMS_SELECTED'), 'error');
			$this->setRedirect($this->returnURL);

			return;
		}

		/**
		 * Language CASE 1: Either Copy/moving/translating multiple items to a single language, use 'language' REQUEST variable
		 */
		$lang    = $this->input->get('language', '_not_posted_', 'string');

		/**
		 * Language CASE 2: Translation task that translates single item to multiple languages. Get target languages
		 */
		$lang_arr = $this->input->get('languages', array(), 'array');
		$lang_arr = array_flip($lang_arr);
		$lang_tmp = array();

		$site_languages = FLEXIUtilities::getlanguageslist();
		foreach($site_languages as $site_lang)
		{
			if (isset($lang_arr[$site_lang->code]))
			{
				$lang_tmp[] = $site_lang->code;
			}
		}

		$lang_arr = $lang_tmp;

		// Calculate access of copyitems task
		$canCopy = $user->authorise('flexicontent.copyitems', 'com_flexicontent');

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

			return false;
		}

		$method   = $this->input->get('method', 1, 'int');
		$keeepcats = $this->input->get('keeepcats', 1, 'int');
		$keeptags = $this->input->get('keeptags', 1, 'int');
		$prefix   = $this->input->get('prefix', 1, 'string');
		$suffix   = $this->input->get('suffix', 1, 'string');
		$copynr   = $this->input->get('copynr', 1, 'int');
		$maincat  = $this->input->get('maincat', '', 'int');
		$seccats  = $this->input->get('seccats', array(), 'array');
		$keepseccats = $this->input->get('keepseccats', 0, 'int');

		$state   = $this->input->get('state', '', 'string');
		$state   = strlen($state) ? (int) $state : null;

		$type_id = $this->input->get('type_id', '', 'int');

		$access  = $this->input->get('access', '', 'string');
		$access  = strlen($access) ? (int) $access : null;

		// Set $seccats to --null-- to indicate that we will maintain secondary categories
		$seccats = $keepseccats ? null : $seccats;

		// Access check
		$copytask_allow_uneditable = JComponentHelper::getParams('com_flexicontent')->get('copytask_allow_uneditable', 1);

		if (!$copytask_allow_uneditable || $method == 2)  // If method is 2 (move) we will deny moving uneditable items
		{
			// Remove uneditable items
			$auth_cid = array();
			$non_auth_cid = array();

			// Get record owner and other record data
			$q = $this->_getRecordsQuery($cid, array('id', 'created_by', 'catid'));
			$itemdata = $db->setQuery($q)->loadObjectList('id');

			// Check authorization for edit operation
			foreach ($cid as $id)
			{
				$isOwner = $itemdata[$id]->created_by == $user->id;
				$asset = 'com_content.article.' . $id;
				$canEdit = $user->authorise('core.edit', $asset) || ($user->authorise('core.edit.own', $asset) && $isOwner);

				if ($canEdit)
				{
					$auth_cid[] = $id;
				}
				else
				{
					$non_auth_cid[] = $id;
				}
			}

			// echo "<pre>"; echo "authorized:\n"; print_r($auth_cid); echo "\n\nNOT authorized:\n"; print_r($non_auth_cid); echo "</pre>"; exit;
		}
		else
		{
			$auth_cid = & $cid;
			$non_auth_cid = array();
		}

		// Set warning for uneditable items
		if (count($non_auth_cid))
		{
			$msg_noauth = JText::_('FLEXI_CANNOT_COPY_ASSETS') . ' ' . JText::_('FLEXI_REASON_NO_EDIT_PERMISSION')
				. '<br>' . JText::_('FLEXI_ROWS_SKIPPED') . ' : '. implode(',', $non_auth_cid);
			$app->enqueueMessage($msg_noauth, 'warning');

			if (!count($auth_cid))  // Cancel task if no items can be copied
			{
				$this->setRedirect($this->returnURL);

				return false;
			}
		}

		// Set only authenticated item ids for the copyitems() method
		$auth_cid = $cid;
		$clean_cache_flag = false;


		/**
		 * Execute batch
		 */
		switch ($method)
		{
			// Copy CASE
			case 1:
				if ($model->copyitems($auth_cid, $keeptags, $prefix, $suffix, $copynr, $lang, $state))
				{
					$msg = JText::sprintf('FLEXI_ITEMS_COPY_SUCCESS', count($auth_cid));
					$clean_cache_flag = true;
				}
				else
				{
					$app->setHeader('status', 500);
					$app->enqueueMessage(JText::_('FLEXI_ERROR_COPY_ITEMS') . " " . $model->getError(), 'error');
					$msg = '';
				}
				break;

			// Update CASE (optionally moving)
			case 2:
				$msg = JText::sprintf('FLEXI_ITEMS_MOVE_SUCCESS', count($auth_cid));

				foreach ($auth_cid as $itemid)
				{
					if (!$model->moveitem($itemid, $maincat, $seccats, $lang, $state, $type_id, $access))
					{
						$app->setHeader('status', 500);
						$app->enqueueMessage(JText::_('FLEXI_ERROR_MOVE_ITEMS') . " " . $model->getError(), 'error');
						$msg = '';
					}
				}

				$clean_cache_flag = true;
				break;

			// Translate
			case 99:
				// CHECK CASE OF TRANSLATING SINGLE ITEM TO MULTIPLE LANGUAGES but zero languages have been given and terminate with error (we are in modal window)
				if (!$lang_arr && $lang === '_not_posted_')
				{
					jexit('<div class="alert alert-warning">Nothing to do, please select target languages</div>');
				}

			// Copy and update CASE (optionally moving) 
			case 3:
				$languages = $lang_arr ?: array($lang);
				$total_cnt = $model->copyitems($auth_cid, $keeptags, $prefix, $suffix, $copynr, $languages, $state, $method, $maincat, $seccats, $type_id, $access);
				if ($total_cnt)
				{
					$msg = JText::sprintf('FLEXI_ITEMS_COPYMOVE_SUCCESS', $total_cnt);   //count($auth_cid)
					$clean_cache_flag = true;
				}
				else
				{
					$app->setHeader('status', 500);
					$app->enqueueMessage(JText::_('FLEXI_ERROR_COPYMOVE_ITEMS') . " " . $model->getError(), 'error');
					$msg = '';
				}
				break;

			default:
				die('Unknown batch mode: ' . $method);
				break;
		}

		// CHECK CASE OF TRANSLATING SINGLE ITEM TO MULTIPLE LANGUAGES and terminate with success message, because this is inside a modal window
		if ($lang_arr)
		{
			$msg = JText::sprintf('FLEXI_N_ITEMS_CREATED', $total_cnt);
			jexit('<div class="alert alert-info">' . $msg . '</div>');
		}

		$link = 'index.php?option=com_flexicontent&view=items';

		// CLEAN THE CACHE so that our changes appear realtime
		if ($clean_cache_flag)
		{
			$this->_cleanCache();
		}

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


	/**
	 * 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
	 *
	 * @return void
	 *
	 * @since 3.3
	 */
	public function publish()
	{
		parent::publish();
	}


	/**
	 * Logic to unpublish records
	 *
	 * @return void
	 *
	 * @since 3.3
	 */
	public function unpublish()
	{
		parent::unpublish();
	}


	/**
	 * Method to toggle the featured setting of a list of records
	 *
	 * @return  void
	 *
	 * @since   3.2
	 */
	public function featured()
	{
		// Check for request forgeries
		JSession::checkToken() or jexit(JText::_('JINVALID_TOKEN'));

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

		$cid    = $this->input->get('cid', array(), 'array');
		$values = array('featured' => 1, 'unfeatured' => 0);
		$value  = ArrayHelper::getValue($values, $this->task, 0, 'int');

		// Access checks.
		foreach ($cid as $i => $id)
		{
			if (!$user->authorise('core.edit.state', 'com_content.article.' . (int) $id))
			{
				// Prune items that you can't change.
				unset($cid[$i]);
				$app->enqueueMessage(JText::_('FLEXI_ITEM') . ': ' . $id . ' - ' . JText::_('JLIB_APPLICATION_ERROR_EDITSTATE_NOT_PERMITTED'), 'warning');
			}
		}

		if (empty($cid))
		{
			$app->setHeader('status', '500', true);
			$app->enqueueMessage(JText::_('FLEXI_NO_ITEMS_SELECTED'), 'error');
			$this->setRedirect($this->returnURL);

			return;
		}

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

		// Update featured flag (model will also handle cache cleaning)
		if (!$record_model->featured($cid, $value))
		{
			$app->enqueueMessage($record_model->getError(), 'error');
			$app->redirect($this->returnURL);
		}

		$message = $value == 1
			? JText::plural('COM_CONTENT_N_ITEMS_FEATURED', count($cid))
			: JText::plural('COM_CONTENT_N_ITEMS_UNFEATURED', count($cid));
		$this->setRedirect($this->returnURL, $message);
	}


	/**
	 * 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 = null)
	{
		return parent::changestate($state);
	}


	/**
	 * Logic to submit item to approval
	 *
	 * @access public
	 * @return void
	 *
	 * @since 1.5
	 */
	function approval()
	{
		$app    = JFactory::getApplication();
		$isSite = $app->isClient('site');
		$tokarr = $app->isClient('site') ? 'request' : 'post';

		// Check for request forgeries, use REQUEST in the case of FRONTEND (which may uses GET urls)
		JSession::checkToken($tokarr) or jexit(JText::_('JINVALID_TOKEN'));

		// FRONTEND: get item ID via 'id' URL variable, instead of 'cid' (BACKEND)
		$cid_name = $isSite ? 'id' : 'cid';
		$cid = $this->input->get($cid_name, array(), 'array');
		$cid = ArrayHelper::toInteger($cid);

		if (!count($cid))
		{
			$app->setHeader('status', '500 Internal Server Error', true);
			$app->enqueueMessage(JText::_('FLEXI_APPROVAL_SELECT_ITEM_SUBMIT'), 'warning');
			$app->redirect($this->returnURL);
		}

		// Approve item(s) (model will also handle cache cleaning)
		$record_model = $this->getModel($this->record_name);
		$msg = $record_model->approval($cid);

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


	/**
	 * Logic to delete records
	 *
	 * @return void
	 *
	 * @since 3.3
	 */
	public function remove()
	{
		$app    = JFactory::getApplication();
		$isSite = $app->isClient('site');
		$tokarr = $app->isClient('site') ? 'request' : 'post';

		// Check for request forgeries, use REQUEST (frontend uses GET urls)
		JSession::checkToken($tokarr) or jexit(JText::_('JINVALID_TOKEN'));

		// Frontend may use GET request, before calling parent task method, set token to POST too
		if ($isSite && !JSession::checkToken('post'))
		{
			$this->input->post->set(JSession::getFormToken(), '1');
		}

		// FRONTEND: Get extra variables
		if ($isSite)
		{
			// These are needed in case we are in item view and we need to redirect to a different URL after deletion (to deleted item's category)
			$catid = $this->input->getInt('cid');
			$isitemview = $this->input->getInt('isitemview');

			// Get item ID from 'id' URL variable and set it back to 'cid' URL variable
			$cid = $this->input->get('id', array(), 'array');
			$cid = ArrayHelper::toInteger($cid);
			$this->input->set('cid', $cid);
		}

		$result = parent::remove();

		// FRONTEND: Check if we cannot redirect back to the deleted item
		if ($isSite && $result && reset($cid) === $this->input->getInt('id') && $isitemview)
		{
			$non_sef_link = FlexicontentHelperRoute::getCategoryRoute($catid, $Itemid = 0, $urlvars = array());
			$category_link = JRoute::_($non_sef_link);
			$this->setRedirect($category_link);
		}
	}


	/**
	 * logic for restore an old version
	 *
	 * @return void
	 *
	 * @since 1.5
	 */
	function restore()
	{
		// Check for request forgeries
		JSession::checkToken('request') or die(JText::_('JINVALID_TOKEN'));

		$id			= $this->input->getInt('id', 0);
		$version	= $this->input->getInt('version', 0);
		$record_model = $this->getModel($this->record_name);

		// First checkin the open item
		$item = JTable::getInstance($this->records_jtable, '');
		$item->load($id);
		$item->checkin();

		if ($version)
		{
			$msg = JText::sprintf('FLEXI_VERSION_RESTORED', $version);
			$record_model->restore($version, $id);
		}
		else
		{
			$msg = JText::_('FLEXI_NOTHING_TO_RESTORE');
		}

		$ctrlTask = 'task=items.edit';
		$this->setRedirect('index.php?option=com_flexicontent&' . $ctrlTask . '&cid[]=' . $id, $msg);
	}


	/**
	 * Logic to set the access level of the records
	 *
	 * @return void
	 *
	 * @since 3.3
	 */
	public function access()
	{
		parent::access();
	}


	/**
	 * Method to fetch the tags edit field for the edit form, this is currently NOT USED
	 *
	 * @since 1.5
	 */
	public function gettags()
	{
		// Check for request forgeries
		JSession::checkToken() or die(JText::_('JINVALID_TOKEN'));

		$id    = $this->input->get('id', 0, 'int');
		$record_model = $this->getModel($this->record_name);
		$tags  = $record_model->gettags();
		$user  = JFactory::getUser();

		// Get tag ids if non-new item
		$used = $id ? $record_model->getUsedtagsIds($id) : null;
		$used = is_array($used) ? $used : array();

		$permission = FlexicontentHelperPerm::getPerm();
		$CanCreateTags = $permission->CanCreateTags;
		$CanUseTags    = $permission->CanUseTags;

		$CanUseTags = $CanUseTags ? '' : ' disabled="disabled"';
		$n = count($tags);
		$html = '';

		// Create list of current item's already assigned tags
		if ($n)
		{
			$html .= '<div class="fc_tagbox" id="fc_tagbox">';
			$html .= '<ul id="ultagbox">';

			for ($i = 0, $n; $i < $n; $i++)
			{
				$tag = $tags[$i];

				if (!in_array($tag->id, $used))
				{
					continue; // Tag not assigned to item
				}

				if ($CanUseTags && in_array($tag->id, $used))
				{
					$html .= '
					<li class="tagitem">
						<span>' . $tag->name . '</span>
						<input type="hidden" name="jform[tag][]" value="' . $tag->tid . '" />
						<a href="javascript:;" class="deletetag" onclick="javascript:deleteTag(this);" title="' . JText::_('FLEXI_DELETE_TAG') . '"></a>
					</li>';
				}
				else
				{
					$html .= '
					<li class="tagitem plain">
						<span>' . $tag->name . '</span>
						<input type="hidden" name="jform[tag][]" value="' . $tag->tid . '" />
					</li>';
				}
			}

			$html .= '</ul>';
			$html .= '</div>';
			$html .= '<div class="fcclear"></div>';
		}

		if ($CanCreateTags)
		{
			$html .= '
			<div class="fc_addtag">
				<label for="addtags">' . JText::_('FLEXI_ADD_TAG') . '</label>
				<input type="text" id="tagname" class="inputbox" size="30" />
				<input type="button" class="fc_button" value="' . JText::_('FLEXI_ADD') . '" onclick="addtag()" />
			</div>';
		}

		echo $html;
	}


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

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

		$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', true);
			$app->enqueueMessage(JText::_('FLEXI_NO_ITEMS_SELECTED'), 'error');
			$this->setRedirect($this->returnURL);

			return;
		}

		// Calculate access of copyitems task
		$canCopy = $user->authorise('flexicontent.copyitems', 'com_flexicontent');

		// Check access
		if (!$canCopy)
		{
			$app->setHeader('status', 403);
			$app->enqueueMessage(JText::_('FLEXI_ALERTNOTAUTH_TASK'), 'error');
			$app->redirect($this->returnURL);
		}

		// Access check
		$copytask_allow_uneditable = JComponentHelper::getParams('com_flexicontent')->get('copytask_allow_uneditable', 1);

		if (!$copytask_allow_uneditable)
		{
			// Remove uneditable items
			$auth_cid = array();
			$non_auth_cid = array();

			// Get record owner and other record data
			$q = $this->_getRecordsQuery($cid, array('id', 'created_by', 'catid'));
			$itemdata = $db->setQuery($q)->loadObjectList('id');

			// Check authorization for edit operation
			foreach ($cid as $id)
			{
				$isOwner = $itemdata[$id]->created_by == $user->id;
				$asset = 'com_content.article.' . $id;
				$canEdit = $user->authorise('core.edit', $asset) || ($user->authorise('core.edit.own', $asset) && $isOwner);

				if ($canEdit)
				{
					$auth_cid[] = $id;
				}
				else
				{
					$non_auth_cid[] = $id;
				}
			}

			// echo "<pre>"; echo "authorized:\n"; print_r($auth_cid); echo "\n\nNOT authorized:\n"; print_r($non_auth_cid); echo "</pre>"; exit;
		}
		else
		{
			$auth_cid = & $cid;
			$non_auth_cid = array();
		}

		// Set warning for uneditable items
		if (count($non_auth_cid))
		{
			$msg_noauth = JText::_('FLEXI_CANNOT_COPY_ASSETS') . ' ' . JText::_('FLEXI_REASON_NO_EDIT_PERMISSION')
				. '<br>' . JText::_('FLEXI_ROWS_SKIPPED') . ' : '. implode(',', $non_auth_cid);
			$app->enqueueMessage($msg_noauth, 'warning');

			if (!count($auth_cid))  // Cancel task if no items can be copied
			{
				$app->redirect($this->returnURL);
			}
		}

		// Set only authenticated item ids, to be used by the parent display method ...
		$cid = $this->input->set('cid', $auth_cid);

		// Display the form of the task
		parent::display();
	}
}