Your IP : 216.73.217.20


Current Path : /home/cbequiperp/www/administrator/components/com_flexicontent/controllers/base/
Upload File :
Current File : /home/cbequiperp/www/administrator/components/com_flexicontent/controllers/base/baseadmin.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('FlexicontentController', JPATH_BASE . DS . 'components' . DS . 'com_flexicontent' . DS . 'controller.php');
require_once('traitbase.php');

/**
 * FLEXIcontent BaseAdmin Controller
 *
 * @since 3.3
 */
class FlexicontentControllerBaseAdmin extends FlexicontentController
{
	use FCControllerTraitBase;

	static $record_limit = 20000;

	var $records_dbtbl = 'flexicontent_records';
	var $records_jtable = 'flexicontent_records';

	var $record_name = 'record';
	var $record_name_pl = 'records';

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

	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('add',          'edit');
		$this->registerTask('apply',        'save');
		$this->registerTask('apply_ajax',   'save');
		$this->registerTask('save2new',     'save');
		$this->registerTask('save2copy',    'save');

		// These will be usable only if (plural) records model has 'canDelRelated' Flag
		$this->registerTask('remove_cascade',   'remove');
		$this->registerTask('remove_relations', 'remove');

		$this->registerTask('exportxml', 'export');
		$this->registerTask('exportsql', 'export');
		$this->registerTask('exportcsv', 'export');


		/**
		 * OVERRIDE parent controler registerTask() mappings
		 * Wrap them to 'publish' (which is a Wrapper to our 'changestate' method)
		 */
		$this->registerTask('publish',    'publish');
		$this->registerTask('unpublish', 'unpublish');
		$this->registerTask('archive', 'archive');
		$this->registerTask('trash', 'trash');
		$this->registerTask('report', 'report');

		$this->option = $this->input->get('option', '', 'cmd');
		$this->task   = $this->input->get('task', '', 'cmd');
		$this->view   = $this->input->get('view', $this->record_name, '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();

		// Can manage ACL
		$this->canManage = false;

		/**
		 * Common messages, the derived controller may override these
		 */

		// Error messages
		$this->err_locked_recs_changestate = 'FLEXI_ROW_STATE_NOT_MODIFIED_DUE_ASSOCIATED_DATA';
		$this->err_locked_recs_delete      = 'FLEXI_ROWS_NOT_DELETED_DUE_ASSOCIATED_DATA';
		$this->err_noauth_recs_changestate = 'FLEXI_ROW_STATE_NOT_MODIFIED_DUE_NO_ACCESS';
		$this->err_noauth_recs_delete      = 'FLEXI_ROWS_NOT_DELETED_DUE_NO_ACCESS_OR_NOT_IN_TRASH';

		// Warning messages
		$this->warn_locked_recs_skipped     = 'FLEXI_SKIPPED_N_ROWS_WITH_ASSOCIATIONS';
		$this->warn_locked_recs_skipped_del = 'FLEXI_SKIPPED_N_ROWS_WITH_ASSOCIATIONS';
		$this->warn_noauth_recs_skipped     = 'FLEXI_SKIPPED_N_ROWS_UNAUTHORISED';
		$this->warn_noauth_recs_skipped_del = 'FLEXI_SKIPPED_N_ROWS_DUE_TO_NO_ACL_OR_NOT_IN_TRASH';

		// Messages about related data
		$this->msg_relations_deleted        = 'FLEXI_ASSIGNMENTS_DELETED';
	}


	/*
	 * Terminate
	 * - CASE 1: either do no redirection / no termination, only returning exit data (controller task was executed by custom code)
	 * - CASE 2: or setting HTTP header and doing a JSON response with data or error message
	 * - CASE 3: or setting HTTP header and enqueuing an error/success message and doing a redirect
	 */
	function terminate($exitData = null, & $exitMessages = null, $data = null)
	{
		$this->input->get('task', '', 'cmd') !== __FUNCTION__ or die(__FUNCTION__ . ' : direct call not allowed');

		// CASE 1: interactive mode
		if ($this->runMode === 'interactive')
		{
			// Return messages to the caller
			$exitMessages = array();

			foreach	($this->exitMessages as $msg)
			{
				$exitMessages[] = array(key($msg) => JText::_(reset($msg)));
			}

			return $exitData;
		}

		// Standalone modes, set HTTP headers, also get value of 'status' header
		$app  = JFactory::getApplication();
		$user = JFactory::getUser();

		$httpStatus = $this->exitSuccess ? '200 OK' : '400 Bad Request';

		foreach	($this->exitHttpHead as $header)
		{
			if (key($header) == 'status')
			{
				$httpStatus = key($header);
			}

			$app->setHeader(key($header), reset($header), true);
		}

		// CASE 2: standalone mode with JSON response:  Set HTTP headers, and create a jsonrpc response
		if ($this->format === 'json')
		{
			if (!empty($this->exitLogTexts))
			{
				$log_filename = 'filemanager_upload_' . ($user->id) . '.php';
				jimport('joomla.log.log');
				JLog::addLogger(
					array(
						'text_file' => $log_filename,  // Sets the target log file
					'text_entry_format' => '{DATETIME} {PRIORITY} {MESSAGE}'  // Sets the format of each line
					),
					JLog::ALL,  // Sets messages of all log levels to be sent to the file
					array('com_flexicontent.filemanager')  // category of logged messages
				);

				foreach	($this->exitLogTexts as $msg)
				{
					JLog::add(reset($msg), key($msg), 'com_flexicontent.filemanager');
				}
			}

			$msg_text_all = array();

			foreach	($this->exitMessages as $msg)
			{
				$msg_text_all[] = JText::_(reset($msg));
			}

			$msg_text_all = implode(' <br/> ', $msg_text_all);

			if ($this->exitSuccess)
			{
				jexit('{"jsonrpc" : "2.0", "result" : ' . json_encode($msg_text_all) . ', "data" : ' . json_encode($data) . '}');
			}
			else
			{
				jexit('{"jsonrpc" : "2.0", "error" : {"code": ' . $httpStatus . ', "message": ' . json_encode($msg_text_all) . '}, "data" : ' . json_encode($data) . '}');
			}
		}

		// CASE 3: standalone mode with HTML response:  Set HTTP headers, enqueue messages and optionally redirect
		foreach	($this->exitMessages as $msg)
		{
			$app->enqueueMessage(JText::_(reset($msg)), key($msg));
		}

		// Redirect or return
		if ($this->returnURL)
		{
			$app->redirect($this->returnURL . ($httpStatus == '403 Forbidden' ? '' : '&' . JSession::getFormToken() . '=1'));
		}
		else
		{
			return ! $this->exitSuccess ? false : null;
		}
	}


	/**
	 * 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();

		$ctrl_task = 'task=' . $this->record_name_pl . '.';
		$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

		// Validate ID and set is new flag
		$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 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');

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


		/**
		 * 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();

		// 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 any 'form' 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

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

		// Extra custom step before model store
		if ($this->_beforeModelStore($validated_data, $data, $model) === false)
		{
			$app->enqueueMessage($this->getError(), 'error');
			$app->setHeader('status', 500, true);

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

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


		/**
		 * 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))
		{
			// 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

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

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

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


		/**
		 * 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':
				if ($app->isClient('administrator') || in_array($this->record_name, array('review')))
				{
					$link = 'index.php?option=com_flexicontent&' . $ctrl_task . 'edit&view=' . $this->record_name
						. '&id=' . (int) $model->get('id') . ($tmpl ? '&tmpl=' . $tmpl : '');
				}
				// REDIRECT CASE: Return to the form 's original referer after item saving
				else
				{
					$link = $this->returnURL;
				}
				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 . ($tmpl ? '&tmpl=' . $tmpl : '');
				}

				// REDIRECT CASE: Return to the form 's original referer after item saving
				else
				{
					$link = $this->returnURL;
				}
				break;

			// REDIRECT CASES FOR SAVING
			default:
				if ($app->isClient('administrator'))
				{
					$link = $this->returnURL;
				}

				// REDIRECT CASE: Return to the form 's original referer after item saving
				else
				{
					$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()
	{
		// Check for request forgeries
		JSession::checkToken('request') or die(JText::_('JINVALID_TOKEN'));

		$redirect_url = $this->returnURL;
		flexicontent_db::checkin($this->records_jtable, $redirect_url, $this);
	}


	/**
	 * Cancel the edit, check in the record and return to the records manager
	 *
	 * @return bool
	 *
	 * @since 3.3
	 */
	public function cancel()
	{
		// Check for request forgeries
		JSession::checkToken('request') or die(JText::_('JINVALID_TOKEN'));

		// Set record ID (JForm) into the request array variable cid[] (expected by the 'checkin' task)
		$raw_data = $this->input->get('jform', array(), 'array');
		$cid = $raw_data['id'] ? (int) $raw_data['id'] : $this->input->getInt('id', 0);
		$this->input->set('cid', $cid);

		// Check in the record (if possible) and redirect (typically) to records manager
		$this->checkin();

		// Set redirect URL
		$this->setRedirect($this->returnURL);

		return true;
	}


	/**
	 * 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)
	{
		// Check for request forgeries
		JSession::checkToken('request') or die(JText::_('JINVALID_TOKEN'));

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

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

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

			return;
		}

		$state_aliases = array(
			'PE' => -3,
			'OQ' => -4,
			'IP' => -5,
			'P'  =>  1,
			'U'  =>  0,
			'A'  =>  2,
			'T'  => -2
		);

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

		$state = isset($state_aliases[$state])
			? $state_aliases[$state]
			: (is_numeric($state) ? (int) $state : null);

		// Check for valid state
		if ($state === null || !isset($record_model->supported_conditions[$state]))
		{
			$app->enqueueMessage(JText::_('Invalid State') . ': ' . $state, 'error');
			$app->redirect($this->returnURL);
		}

		// Calculate access
		$cid_noauth = array();
		$cid_locked = array();

		$model->canDoAction($cid, $cid_noauth, $cid_locked, $state);
		$cid = array_diff($cid, $cid_noauth, $cid_locked);

		$is_authorised = count($cid);

		// Check access
		if (!$is_authorised)
		{
			count($cid_locked)
				? $app->enqueueMessage(JText::sprintf($this->err_locked_recs_changestate, JText::_('FLEXI_' . $this->_NAME . 'S')), 'error')
				: $app->enqueueMessage(JText::sprintf($this->err_noauth_recs_changestate, JText::_('FLEXI_' . $this->_NAME . 'S')), 'error');
			$app->setHeader('status', 403, true);
			$this->setRedirect($this->returnURL);

			return;
		}

		count($cid_locked)
			? $app->enqueueMessage(JText::sprintf($this->warn_locked_recs_skipped, count($cid_locked), JText::_('FLEXI_' . $this->_NAME . 'S'))
				. ' <br> ' . JText::_('FLEXI_ROWS_SKIPPED') . ' : '
				. implode(',', $cid_locked), 'warning')
			: false;
		count($cid_noauth)
			? $app->enqueueMessage(JText::sprintf($this->warn_noauth_recs_skipped, count($cid_noauth), JText::_('FLEXI_' . $this->_NAME . 'S'))
				. ' <br> ' . JText::_('FLEXI_ROWS_SKIPPED') . ' : '
				. implode(',', $cid_locked), 'warning')
			: false;

		// Do not modify records that already in target state
		$record_table = $model->getTable();
		$cid = array_keys($model->getItemsByConditions(
			array(
				'select' => array($db->quoteName($record_table->getKeyName()) . ' AS id'),
				'where'  => array(
					$db->quoteName($record_table->getKeyName()) . ' IN (' . implode(',', ArrayHelper::toInteger($cid)) . ')',
					$model->state_col . ' <> '. (int) $state,
				),
			), false
		));

		if (count($cid))
		{
			// Some record types need to be changed atomically to allow plugin triggering ... and other custom code execution
			$atomic_change_record_types = array('item');

			/**
			 * Change state of the record(s), note cache will be cleaned in subsequent step
			 */
			if (in_array($this->record_name, $atomic_change_record_types))
			{
				$count = 0 ;

				foreach ($cid as $item_id)
				{
					$result = $record_model->setitemstate($item_id, $state, $_cleanCache = false);

					// Check for errors during state changing
					if ($result === false)
					{
						$app->enqueueMessage(JText::_('FLEXI_OPERATION_FAILED') . ' : ' . $record_model->getError(), 'warning');
					}
					else
					{
						$count++;
					}
				}

				$result = true;
			}
			else
			{
				$result = $model->changestate($cid, $state);

				// Check for errors during state changing
				if ($result === false)
				{
					$app->enqueueMessage(JText::_('FLEXI_OPERATION_FAILED') . ' : ' . $model->getError(), 'error');
					$app->setHeader('status', '500', true);
					$this->setRedirect($this->returnURL);
				}

				$count = (int) $result;
			}

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

			// Check for errors during state changing
			if ($result === false)
			{
				return;
			}
		}

		// Set success message and redirect
		$msg = JText::sprintf('FLEXI_N_RECORDS_CHANGED_TO', $count) . ' ' . JText::_($record_model->supported_conditions[$state]);
		$this->setRedirect($this->returnURL, $msg, 'message');
	}


	/**
	 * Logic to change the state of a tag
	 *
	 * @return void
	 *
	 * @since 3.3
	 */
	public function setitemstate()
	{
		flexicontent_html::setitemstate($this, 'json', $this->record_name);
	}


	/**
	 * 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();

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

		// Check that request action is supported by the model
		if (in_array($this->task, array('remove_cascade', 'remove_relations')) && !$model::canDelRelated)
		{
			$app->enqueueMessage(JText::_('Unsupported task called'), 'error');
			$app->setHeader('status', 500, true);
			$this->setRedirect($this->returnURL);

			return false;
		}

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

			return false;
		}

		// Calculate access, if cascade removal, then pass via 'cid_locked' all records as ignore-assignments records
		$cid_noauth = array();
		$cid_locked = in_array($this->task, array('remove_cascade', 'remove_relations'))
			? $cid
			: array();

		$model->canDoAction($cid, $cid_noauth, $cid_locked, 'core.delete');

		$cid = array_diff($cid, $cid_noauth, $cid_locked);
		$is_authorised = count($cid);

		// Check access
		if (!$is_authorised)
		{
			count($cid_locked)
				? $app->enqueueMessage(JText::sprintf($this->err_locked_recs_delete, JText::_('FLEXI_' . $this->_NAME . 'S')), 'warning')
				: $app->enqueueMessage(JText::sprintf($this->err_noauth_recs_delete, JText::_('FLEXI_' . $this->_NAME . 'S')), 'error');
			$app->setHeader('status', 403, true);
			$this->setRedirect($this->returnURL);

			return false;
		}

		count($cid_locked)
			? $app->enqueueMessage(JText::sprintf($this->warn_locked_recs_skipped_del, count($cid_locked), JText::_('FLEXI_' . $this->_NAME . 'S'))
				. ' <br> ' . JText::_('FLEXI_ROWS_SKIPPED') . ' : '
				. implode(',', $cid_locked), 'warning')
			: false;
		count($cid_noauth)
			? $app->enqueueMessage(JText::sprintf($this->warn_noauth_recs_skipped_del, count($cid_noauth), JText::_('FLEXI_' . $this->_NAME . 'S'))
				. ' <br> ' . JText::_('FLEXI_ROWS_SKIPPED') . ' : '
				. implode(',', $cid_noauth), 'warning')
			: false;

		// Delete the record assignments
		switch ($this->task)
		{
			case 'remove_relations':
				$result = $model->delete_relations($cid);
				break;

			// Delete the record or records and their assignments
			case 'remove':
			case 'remove_cascade':
				$result = $model->delete($cid, $model_s);
				break;
		}

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

		// Check for errors during deletion
		if (!$result)
		{
			$app->enqueueMessage(JText::_('FLEXI_OPERATION_FAILED') . ' : ' . $model->getError(), 'error');
			$app->setHeader('status', '500', true);
			$this->setRedirect($this->returnURL);

			return $result;
		}

		$total = count($cid);
		$msg = $this->task === 'remove_relations'
			? JText::sprintf($this->msg_relations_deleted, $total)
			: $total . ' ' . JText::_(isset($this->msg_records_deleted) ? $this->msg_records_deleted : 'FLEXI_' . $this->_NAME . 'S_DELETED');

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


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

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

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


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

		// Calculate access
		$cid_noauth = array();
		$is_authorised = $this->record_name === 'item'
			? FlexicontentHelperPerm::getPerm()->CanAccLvl
			: $this->canManage;

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

		/**
		 * Find and excluded records that we can not change their state
		 */
		$record_model = $this->getModel($this->record_name);

		foreach ($cid as $i => $_id)
		{
			$record = $record_model->getRecord($_id);

			if (!$record_model->canEditState($record))
			{
				$cid_noauth[] = $_id;
				unset($cid[$i]);
			}
		}

		$is_authorised = count($cid);

		$msg_noauth = JText::_('FLEXI_CANNOT_CHANGE_ACCLEVEL_ASSETS')
			. ': ' . implode(',', $cid_noauth)
			. ',' . JText::_('FLEXI_REASON_NO_PUBLISH_PERMISSION');

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

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

			return;
		}
		elseif (count($cid_noauth))
		{
			$app->enqueueMessage($msg_noauth, 'warning');
		}

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

		// Change access of the record(s)
		$result = $model->saveaccess($cid, $accesses);

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

		// Check for errors during access changing
		if (!$result)
		{
			$msg = JText::_('FLEXI_ERROR_SETTING_ITEM_ACCESS_LEVEL') . ' : ' . $model->getError();
			throw new Exception($msg, 500);
		}

		$msg = count($cid) . ' ' . JText::_('FLEXI_RECORDS_MODIFIED');

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


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

		// Currently no cache cleaned for all cases
	}


	/**
	 * 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 copy the records
	 *
	 * @return void
	 *
	 * @since 3.3
	 */
	public function copy()
	{
		// Check for request forgeries
		JSession::checkToken('request') or die(JText::_('JINVALID_TOKEN'));

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

		$app->enqueueMessage(JText::_('Task ' . __FUNCTION__ . ' not implemented YET'), 'error');
		$app->setHeader('status', 500, true);
		$this->setRedirect($this->returnURL);
	}


	/**
	 * Get return URL via a client request variable, checking if it is safe (otherwise home page will be used)
	 *
	 * @return  string  A validated URL to be used typical as redirect URL when a task completes
	 *
	 * @since 3.3
	 */
	protected function _getReturnUrl()
	{
		$this->input->get('task', '', 'cmd') !== __FUNCTION__ or die(__FUNCTION__ . ' : direct call not allowed');

		$app = JFactory::getApplication();

		// Try 'return' from the GET / POST data (base64 encoded)
		$return = $this->input->get('return', null, 'base64');

		if ($return)
		{
			$return = base64_decode($return);
		}

		else
		{
			// Try 'referer' from the GET / POST data (htmlspecialchars encoded)
			$referer = $this->input->getString('referer', null);

			if ($referer)
			{
				$referer = htmlspecialchars_decode($referer);
			}

			// Try WEB SERVER variable 'HTTP_REFERER'
			else
			{
				$referer = null;
				// Wrong redirection in some cases, since it redirects to the form itself after saving more than once
				/*$referer = !empty($_SERVER['HTTP_REFERER']) && flexicontent_html::is_safe_url($_SERVER['HTTP_REFERER'])
					? $_SERVER['HTTP_REFERER']
					: JUri::base();*/
			}

			$return = $referer;
		}

		// Check return URL if empty or not safe and set a default one
		if (!$return || !flexicontent_html::is_safe_url($return))
		{
			if ($app->isClient('administrator') && ($this->view === $this->record_name || $this->view === $this->record_name_pl))
			{
				$return = 'index.php?option=com_flexicontent&view=' . $this->record_name_pl;
			}
			else
			{
				$return = $app->isClient('administrator') ? null : JUri::base();
			}
		}

		return $return;
	}


	/**
	 * Method to create a query object for getting record data (specific columns) of multiple records
	 *
	 * @param   array     $cid    an array record ids
	 * @param   array     $cid    an array columns names
	 *
	 * @return  object    return a Joomla Database Query object
	 *
	 * @since 3.3.0
	 */
	protected function _getRecordsQuery($cid, $cols)
	{
		$db = JFactory::getDbo();

		$cid = ArrayHelper::toInteger($cid);
		$cols_list = implode(',', array_filter($cols, array($db, 'quoteName')));

		$query = $db->getQuery(true)
			->select($cols_list)
			->from('#__' . $this->records_dbtbl)
			->where('id IN (' . implode(',', $cid) . ')');

		return $query;
	}


	/**
	 * START OF CONTROLLER SPECIFIC METHODS
	 */

}