Spade

Mini Shell

Directory:~$ /home/lmsyaran/public_html/joomla5/administrator/components/com_fabrik/models/
Upload File

[Home] [System Details] [Kill Me]
Current File:~$ /home/lmsyaran/public_html/joomla5/administrator/components/com_fabrik/models/list.php

<?php
/**
 * Fabrik Admin List Model
 *
 * @package     Joomla.Administrator
 * @subpackage  Fabrik
 * @copyright   Copyright (C) 2005-2020  Media A-Team, Inc. - All rights
reserved.
 * @license     GNU/GPL http://www.gnu.org/copyleft/gpl.html
 * @since       1.6
 */

// No direct access
defined('_JEXEC') or die('Restricted access');

use Joomla\CMS\Table\Table;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\Version;
use Joomla\CMS\Form\Form;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\String\StringHelper;
use Joomla\Registry\Registry;
use Joomla\Utilities\ArrayHelper;
use Joomla\CMS\Factory;

require_once 'fabmodeladmin.php';


/**
 * Fabrik Admin List Model
 *
 * @package     Joomla.Administrator
 * @subpackage  Fabrik
 * @since       3.0
 */
class FabrikAdminModelList extends FabModelAdmin
{
	/**
	 * The prefix to use with controller messages.
	 *
	 * @var  string
	 */
	protected $text_prefix = 'COM_FABRIK_LIST';

	/**
	 * Front end form model
	 *
	 * @var object model
	 */
	protected $formModel = null;

	/**
	 * Front end list model
	 *
	 * @var object
	 */
	protected $feListModel = null;

	/**
	 * Currently loaded list row
	 *
	 * @var array
	 */
	protected $tables = array();

	/**
	 * Plugin type
	 *
	 * @var string
	 * @deprecated ?
	 */
	protected $pluginType = 'List';

	/**
	 * Database fields
	 *
	 * @var array
	 */
	protected $dbFields = null;

	/**
	 * Returns a reference to the a Table object, always creating it.
	 *
	 * @param   string $type   The table type to instantiate
	 * @param   string $prefix A prefix for the table class name. Optional.
	 * @param   array  $config Configuration array for model. Optional.
	 *
	 * @return  FabTableList    List table
	 *
	 * @since    1.6
	 * This is causing issue in J!4
	 */
	public function getTable($type = 'List', $prefix =
'FabrikTable', $config = array())
	{
		$sig = $type . $prefix . implode('.', $config);

		if (!array_key_exists($sig, $this->tables))
		{
			$config['dbo']      = FabrikWorker::getDbo(true);
			$this->tables[$sig] = FabTable::getInstance($type, $prefix, $config);
		}

		return $this->tables[$sig];
	}

	/**
	 * Method to get the record form.
	 *
	 * @param   array $data     Data for the form.
	 * @param   bool  $loadData True if the form is to load its own data
(default case), false if not.
	 *
	 * @return  mixed    A Form object on success, false on failure
	 *
	 * @since    1.6
	 */
	public function getForm($data = array(), $loadData = true)
	{
		// Get the form.
		$form = $this->loadForm('com_fabrik.list', 'list',
array('control' => 'jform', 'load_data'
=> $loadData));

		if (empty($form))
		{
			return false;
		}

		$form->model = $this;

		return $form;
	}

	/**
	 * Method to get the confirm list delete form.
	 *
	 * @param   array $data     Data for the form.
	 * @param   bool  $loadData True if the form is to load its own data
(default case), false if not.
	 *
	 * @return  mixed  A Form object on success, false on failure
	 *
	 * @since    1.6
	 */
	public function getConfirmDeleteForm($data = array(), $loadData = true)
	{
		// Get the form.
		$form = $this->loadForm('com_fabrik.confirmdelete',
'confirmlistdelete', array('control' =>
'jform', 'load_data' => $loadData));

		if (empty($form))
		{
			return false;
		}

		return $form;
	}

	/**
	 * Method to get the select content type form.
	 *
	 * @param   array $data     Data for the form.
	 * @param   bool  $loadData True if the form is to load its own data
(default case), false if not.
	 *
	 * @return  Form|bool  A Form object on success, false on failure
	 *
	 * @since    3.3.5
	 */
	public function getContentTypeForm($data = array(), $loadData = true)
	{
		$contentTypeModel =
Factory::getApplication()->bootComponent('com_fabrik')->getMVCFactory()->createModel('ContentTypeImport',
'FabrikAdminModel', array('listModel' => $this));
		return $contentTypeModel->getForm($data, $loadData);
	}

	/**
	 * Method to get the data that should be injected in the form.
	 *
	 * @return  mixed    The data for the form.
	 *
	 * @since    1.6
	 */
	protected function loadFormData()
	{
		// Check the session for previously entered form data.
		$data =
$this->app->getUserState('com_fabrik.edit.list.data',
array());

		if (empty($data))
		{
			$data = $this->getItem();
		}

		return $data;
	}

	/**
	 * Method to change the published state of one or more records.
	 *
	 * @param   array &$pks  A list of the primary keys to change.
	 * @param   int   $value The value of the published state.
	 *
	 * @return  boolean    True on success.
	 *
	 * @since    1.6
	 */
	public function publish(&$pks, $value = 1)
	{
		// Initialise variables.
		$table      = $this->getTable();
		$pks        = (array) $pks;

		// Include the content plugins for the change of state event.
		PluginHelper::importPlugin('content');

		// Access checks.
		foreach ($pks as $i => $pk)
		{
			if ($table->load($pk))
			{
				if (!$this->canEditState($table))
				{
					// Prune items that you can't change.
					unset($pks[$i]);
					\Joomla\CMS\Factory::getApplication()->enqueueMessage(Text::_('JLIB_APPLICATION_ERROR_EDIT_STATE_NOT_PERMITTED'),
'warning');
				}
			}
		}

		// Attempt to change the state of the records.
		if (!$table->publish($pks, $value,
$this->user->get('id')))
		{
			$this->setError($table->getError());

			return false;
		}

		$context = $this->option . '.' . $this->name;

		// Trigger the onContentChangeState event.
		$result =
Factory::getApplication()->triggerEvent($this->event_change_state,
array($context, $pks, $value));

		if (in_array(false, $result, true))
		{
			$this->setError($table->getError());

			return false;
		}

		return true;
	}

	/**
	 * Build and/or dropdown list
	 *
	 * @param   bool   $addSlashes to reutrn data
	 * @param   string $name       input name
	 *
	 * @return string dropdown
	 */
	protected function getFilterJoinDd($addSlashes = true, $name =
'join')
	{
		$aConditions   = array();
		$aConditions[] = HTMLHelper::_('select.option',
'AND');
		$aConditions[] = HTMLHelper::_('select.option',
'OR');
//		$attribs       = 'class="inputbox input-small"
size="1"';
		$attribs       = 'class="form-select"';// makes no
difference
		$dd            = str_replace("\n", "",
HTMLHelper::_('select.genericlist', $aConditions, $name,
$attribs, 'value', 'text', ''));

		if ($addSlashes)
		{
			$dd = addslashes($dd);
		}

		return $dd;
	}

	/**
	 * Build prefilter dropdown
	 *
	 * @param   bool   $addSlashes add slashes to reutrn data
	 * @param   string $name       name of the drop down
	 * @param   int    $mode       states what values get put into drop down
	 *
	 * @return string dropdown
	 */
	protected function getFilterConditionDd($addSlashes = true, $name =
'conditions', $mode = 1)
	{
		$aConditions = array();

		switch ($mode)
		{
			case 1: /* used for search filter */
				$aConditions[] = HTMLHelper::_('select.option',
'<>', 'NOT EQUALS');
				$aConditions[] = HTMLHelper::_('select.option',
'=', 'EQUALS');
				$aConditions[] = HTMLHelper::_('select.option',
'like', 'BEGINS WITH');
				$aConditions[] = HTMLHelper::_('select.option',
'like', 'CONTAINS');
				$aConditions[] = HTMLHelper::_('select.option',
'like', 'ENDS WITH');
				$aConditions[] = HTMLHelper::_('select.option',
'>', 'GREATER THAN');
				$aConditions[] = HTMLHelper::_('select.option',
'>=', 'GREATER THAN OR EQUALS');
				$aConditions[] = HTMLHelper::_('select.option',
'<', 'LESS THAN');
				$aConditions[] = HTMLHelper::_('select.option',
'<=', 'LESS THAN OR EQUALS');
				break;
			case 2: /* used for prefilter */
				$aConditions[] = HTMLHelper::_('select.option',
'equals', 'EQUALS');
				$aConditions[] = HTMLHelper::_('select.option',
'notequals', 'NOT EQUAL TO');
				$aConditions[] = HTMLHelper::_('select.option',
'begins', 'BEGINS WITH');
				$aConditions[] = HTMLHelper::_('select.option',
'contains', 'CONTAINS');
				$aConditions[] = HTMLHelper::_('select.option',
'ends', 'ENDS WITH');
				$aConditions[] = HTMLHelper::_('select.option',
'>', 'GREATER THAN');
				$aConditions[] = HTMLHelper::_('select.option',
'>=', 'GREATER THAN OR EQUALS');
				$aConditions[] = HTMLHelper::_('select.option',
'<', 'LESS THAN');
				$aConditions[] = HTMLHelper::_('select.option', 'IS
NULL', 'IS NULL');
				$aConditions[] = HTMLHelper::_('select.option',
'<=', 'LESS THAN OR EQUALS');
				$aConditions[] = HTMLHelper::_('select.option',
'in', 'IN');
				$aConditions[] = HTMLHelper::_('select.option',
'not_in', 'NOT IN');
				$aConditions[] = HTMLHelper::_('select.option',
'exists', 'EXISTS');
				$aConditions[] = HTMLHelper::_('select.option',
'thisyear', Text::_('COM_FABRIK_THIS_YEAR'));
				$aConditions[] = HTMLHelper::_('select.option',
'earlierthisyear',
Text::_('COM_FABRIK_EARLIER_THIS_YEAR'));
				$aConditions[] = HTMLHelper::_('select.option',
'laterthisyear',
Text::_('COM_FABRIK_LATER_THIS_YEAR'));

				$aConditions[] = HTMLHelper::_('select.option',
'yesterday', Text::_('COM_FABRIK_YESTERDAY'));
				$aConditions[] = HTMLHelper::_('select.option',
'today', Text::_('COM_FABRIK_TODAY'));
				$aConditions[] = HTMLHelper::_('select.option',
'tomorrow', Text::_('COM_FABRIK_TOMORROW'));
				$aConditions[] = HTMLHelper::_('select.option',
'thismonth', Text::_('COM_FABRIK_THIS_MONTH'));
				$aConditions[] = HTMLHelper::_('select.option',
'lastmonth', Text::_('COM_FABRIK_LAST_MONTH'));
				$aConditions[] = HTMLHelper::_('select.option',
'nextmonth', Text::_('COM_FABRIK_NEXT_MONTH'));
				$aConditions[] = HTMLHelper::_('select.option',
'nextweek1', Text::_('COM_FABRIK_NEXT_WEEK1'));
				$aConditions[] = HTMLHelper::_('select.option',
'birthday', Text::_('COM_FABRIK_BIRTHDAY_TODAY'));

				break;
		}

//		$dd = str_replace("\n", "",
HTMLHelper::_('select.genericlist', $aConditions, $name,
'class="inputbox input-medium"  size="1" ',
'value', 'text', ''));
		$dd = str_replace("\n", "",
HTMLHelper::_('select.genericlist', $aConditions, $name,
'class="form-select-sm" ', 'value',
'text', ''));

		if ($addSlashes)
		{
			$dd = addslashes($dd);
		}

		return $dd;
	}

	/**
	 * Get connection model
	 *
	 * @return  object  connect model
	 */
	protected function getCnn()
	{
		$item      = $this->getItem();
		$connModel =
Factory::getApplication()->bootComponent('com_fabrik')->getMVCFactory()->createModel('Connection',
'FabrikFEModel');
		$connModel->setId($item->connection_id);
		$connModel->getConnection($item->connection_id);

		return $connModel;
	}

	/**
	 * Create the js that manages the edit list view page
	 *
	 * @return  string  js
	 */
	public function getJs()
	{
		$connModel = $this->getCnn();
		$item      = $this->getItem();
		Text::script('COM_FABRIK_OPTIONS');
		Text::script('COM_FABRIK_JOIN');
		Text::script('COM_FABRIK_FIELD');
		Text::script('COM_FABRIK_CONDITION');
		Text::script('COM_FABRIK_VALUE');
		Text::script('COM_FABRIK_EVAL');
		Text::script('COM_FABRIK_APPLY_FILTER_TO');
		Text::script('COM_FABRIK_DELETE');
		Text::script('JYES');
		Text::script('JNO');
		Text::script('COM_FABRIK_QUERY');
		Text::script('COM_FABRIK_NO_QUOTES');
		Text::script('COM_FABRIK_TEXT');
		Text::script('COM_FABRIK_TYPE');
		Text::script('COM_FABRIK_PLEASE_SELECT');
		Text::script('COM_FABRIK_GROUPED');
		Text::script('COM_FABRIK_TO');
		Text::script('COM_FABRIK_FROM');
		Text::script('COM_FABRIK_JOIN_TYPE');
		Text::script('COM_FABRIK_FROM_COLUMN');
		Text::script('COM_FABRIK_TO_COLUMN');
		Text::script('COM_FABRIK_REPEAT_GROUP_BUTTON_LABEL');
		Text::script('COM_FABRIK_PUBLISHED');

		$joinTypeOpts      = array();
		$joinTypeOpts[]    = array('inner', Text::_('INNER
JOIN'));
		$joinTypeOpts[]    = array('left', Text::_('LEFT
JOIN'));
		$joinTypeOpts[]    = array('right', Text::_('RIGHT
JOIN'));
		$activeTableOpts[] = '';
		$activeTableOpts[] = $item->get('db_table_name');

		$joins = $this->getJoins();

		for ($i = 0; $i < count($joins); $i++)
		{
			$j                 = $joins[$i];
			$activeTableOpts[] = $j->table_join;
			$activeTableOpts[] = $j->join_from_table;
		}

		$activeTableOpts       = array_unique($activeTableOpts);
		$activeTableOpts       = array_values($activeTableOpts);
		$opts                  = new stdClass;
		$opts->joinOpts        = $joinTypeOpts;
		$opts->tableOpts       = $connModel->getThisTables(true);
		$opts->activetableOpts = $activeTableOpts;
		$opts->j3              = true;
		$opts                  = json_encode($opts);

		$filterOpts               = new stdClass;
		$filterOpts->filterJoinDd = $this->getFilterJoinDd(false,
'jform[params][filter-join][]');
		$filterOpts->filterCondDd = $this->getFilterConditionDd(false,
'jform[params][filter-conditions][]', 2);
		$filterOpts->filterAccess = HTMLHelper::_('access.level',
'jform[params][filter-access][]', $item->access,
'class="form-select-sm"', false);
		$filterOpts->filterAccess = str_replace(array("\n",
"\r"), '', $filterOpts->filterAccess);
		$filterOpts->j3           = true;
		$filterOpts               = json_encode($filterOpts);

		$formModel    = $this->getFormModel();
//		$attribs      = 'class="inputbox input-medium"
size="1"';
		$attribs      = 'class="form-select-sm"';
		$filterfields =
$formModel->getElementList('jform[params][filter-fields][]',
'', false, false, true, 'name', $attribs);
		$filterfields = addslashes(str_replace(array("\n",
"\r"), '', $filterfields));

		$plugins = json_encode($this->getPlugins());

		$js   = array();
		$js[] = "window.addEvent('domready', function () {";
		$js[] = "Fabrik.controller = new PluginManager($plugins, " .
(int) $this->getItem()->id . ", 'list');";

		$js[] = "oAdminTable = new ListForm($opts);";
		$js[] = "oAdminTable.watchJoins();";

		for ($i = 0; $i < count($joins); $i++)
		{
			$joinGroupParams = json_decode($joins[$i]->params);
			$j               = $joins[$i];
			$joinFormFields  = json_encode($j->joinFormFields);
			$joinToFields    = json_encode($j->joinToFields);
			$repeat          = $joinGroupParams->repeat_group_button == 1 ? 1 :
0;
			$js[]            =
"	oAdminTable.addJoin('{$j->group_id}','{$j->id}','{$j->join_type}','{$j->table_join}',"
				.
"'{$j->table_key}','{$j->table_join_key}','{$j->join_from_table}',
$joinFormFields, $joinToFields, $repeat);";
		}

		$js[]         = "oAdminFilters = new
adminFilters('filterContainer', '$filterfields',
$filterOpts);";
		$form         = $this->getForm();
		$afilterJoins = $form->getValue('params.filter-join');

		// Force to arrays as single prefilters imported from 2.x will be stored
as string values
		$filterFields      = (array)
$form->getValue('params.filter-fields');
		$afilterConditions = (array)
$form->getValue('params.filter-conditions');
		$afilterEval       = (array)
$form->getValue('params.filter-eval');
		$afilterValues     = (array)
$form->getValue('params.filter-value');
		$afilterAccess     = (array)
$form->getValue('params.filter-access');
		$aGrouped          = (array)
$form->getValue('params.filter-grouped');

		for ($i = 0; $i < count($filterFields); $i++)
		{
			$selJoin = FArrayHelper::getValue($afilterJoins, $i, 'and');

			// 2.0 upgraded sites had quoted filter names
			$selFilter    = str_replace('`', '',
$filterFields[$i]);
			$grouped      = FArrayHelper::getValue($aGrouped, $i, 0);
			$selCondition = $afilterConditions[$i];
			$filerEval    = (int) FArrayHelper::getValue($afilterEval, $i,
'1');

			if ($selCondition == '&gt;')
			{
				$selCondition = '>';
			}

			if ($selCondition == '&lt;')
			{
				$selCondition = '<';
			}

			$selValue  = FArrayHelper::getValue($afilterValues, $i, '');
			$selAccess = $afilterAccess[$i];

			// Allow for multi-line js variables ?
			$selValue = htmlspecialchars_decode($selValue, ENT_QUOTES);
			$selValue = json_encode($selValue);

			// No longer check for empty $selFilter as EXISTS pre-filter condition
doesn't require element to be selected
			$js[] = "\toAdminFilters.addFilterOption('$selJoin',
'$selFilter', '$selCondition', $selValue,
'$selAccess', $filerEval, '$grouped');\n";
		}

		$js[] = "});";

		return implode("\n", $js);
	}

	/**
	 * Get the list's join objects
	 *
	 * @return  array
	 */
	protected function getJoins()
	{
		$item = $this->getItem();

		if ((int) $item->id === 0)
		{
			return array();
		}

		$db    = FabrikWorker::getDbo(true);
		$query = $db->getQuery(true);
		$query->select('*, j.id AS id, j.params as
jparams')->from('#__fabrik_joins AS j')
			->join('INNER', '#__fabrik_groups AS g ON g.id =
j.group_id')->where('j.list_id = ' . (int) $item->id);
		$db->setQuery($query);
		$joins    = $db->loadObjectList();
		$fabrikDb = $this->getFEModel()->getDb();
		$c        = count($joins);

		for ($i = 0; $i < $c; $i++)
		{
			$join    =& $joins[$i];
			$jparams = $join->jparams == '' ? new stdClass :
json_decode($join->jparams);

			if (isset($jparams->type) && ($jparams->type ==
'element' || $jparams->type == 'repeatElement'))
			{
				unset($joins[$i]);
				continue;
			}

			if (empty($join->join_from_table) || empty($join->table_join))
			{
				unset($joins[$i]);
				continue;
			}

			$fields               =
$fabrikDb->getTableColumns($join->join_from_table);
			$join->joinFormFields = array_keys($fields);
			$fields               =
$fabrikDb->getTableColumns($join->table_join);
			$join->joinToFields   = array_keys($fields);
		}

		// $$$ re-index the array in case we zapped anything
		return array_values($joins);
	}

	/**
	 * Load up a front end form model - used in saving the list
	 *
	 * @return  FabrikFEModelForm  front end form model
	 */
	public function getFormModel()
	{
		if (is_null($this->formModel))
		{
			$config          = array();
			$config['dbo']   = FabrikWorker::getDbo(true);
			$this->formModel =
Factory::getApplication()->bootComponent('com_fabrik')->getMVCFactory()->createModel('Form',
'FabrikFEModel', $config);

			$this->formModel->setDbo($config['dbo']);

			/**
			 * $$$ rob commenting out as this loads up an empty form when saving a
new list
			 * $item = $this->getItem();
			 *
$this->formModel->setId($this->getState('list.form_id',
$item->id));
			 * $this->formModel->getForm();
			 */

			/**
			 * $$$ hugh - we need the setId(), otherwise Bad Things <tm>
happen when the ID isn't set
			 * in the form model.  Like index creation borks, because
getPublishedGroups() thinks form ID is 0.
			 */
			$item = $this->getItem();
			$this->formModel->setId($this->getState('list.form_id',
$item->id));

			/**
			 * $$$ rob isnt this wrong as normally the front end form models list
model is the fe list model?
			 * $this->formModel->setListModel($this);
			 */
		}

		return $this->formModel;
	}

	/**
	 * Set the form model
	 *
	 * @param   object $model form model
	 *
	 * @return  void
	 */
	public function setFormModel($model)
	{
		$this->formModel = $model;
	}

	/**
	 * Load up the front end list model so we can use some of its methods
	 *
	 * @return  object  front end list model
	 */
	public function getFEModel()
	{
		if (is_null($this->feListModel))
		{
			$this->feListModel =
Factory::getApplication()->bootComponent('com_fabrik')->getMVCFactory()->createModel('List',
'FabrikFEModel');
			$this->feListModel->setState('list.id',
$this->getState('list.id'));
		}

		return $this->feListModel;
	}

	/**
	 * Validate the form
	 *
	 * @param   Form  $form  The form to validate against.
	 * @param   array  $data  The data to validate.
	 * @param   string $group The name of the field group to validate.
	 *
	 * @return mixed  false or data
	 */
	public function validate($form, $data, $group = null)
	{
		$params = $data['params'];
		$data   = parent::validate($form, $data, $group);

		if (!$data)
		{
			return false;
		}

		if (empty($data['_database_name']) &&
FArrayHelper::getValue($data, 'db_table_name') == '')
		{
			$this->setError(Text::_('COM_FABRIK_SELECT_DB_OR_ENTER_NAME'));

			return false;
		}

		// Hack - must be able to add the plugin xml fields file to $form to
include in validation but cant see how at the moment
		$data['params'] = $params;

		return $data;
	}

	/**
	 * Save the form
	 *
	 * @param   array $data the jForm part of the request data
	 *
	 * @return  bool
	 */
	public function save($data)
	{
		$this->populateState();
		$input = $this->app->input;
		$jForm = $input->get('jform', array(), 'array');
		$date  = Factory::getDate();
		$row   = $this->getTable();
		$id = FArrayHelper::getValue($data, 'id');
		$row->load($id);

		$params = new Registry($row->get('params'));

		$row->bind($data);
		$isView = $this->setIsView($params);
		$data['params']['isview'] = (string) $isView;


		$this->setState('list.id', $id);
		$this->setState('list.form_id',
$row->get('form_id'));
		$feModel = $this->getFEModel();

		/** @var $contentTypeModel FabrikAdminModelContentTypeImport */
		$contentTypeModel =
Factory::getApplication()->bootComponent('com_fabrik')->getMVCFactory()->createModel('ContentTypeImport',
'FabrikAdminModel', array('listModel' => $this));
		$contentType      = ArrayHelper::getValue($jForm,
'contenttype', '');

		if ($contentType !== '')
		{
			$contentTypeModel->check($contentType);
		}

		// Get original collation
		$db            = $feModel->getDb();
		$origCollation = $this->getOriginalCollation($params, $db,
FArrayHelper::getValue($data, 'db_table_name', ''));
		$row->bind($data);

		$row->set('order_by',
json_encode($input->get('order_by', array(),
'array')));
		$row->set('order_dir',
json_encode($input->get('order_dir', array(),
'array')));

		$row->check();

		$isNew = true;

		if ($row->id != 0)
		{
			$this->collation($feModel, $origCollation, $row);
			if (FabrikWorker::isNullDate($row->get('created'))) {
				$row->set('created', $date->toSql());
			}
		}

		if ($id == 0)
		{
			if ($row->get('created', '') == '')
			{
				$row->set('created', $date->toSql());
				$row->set('created_by',
$this->user->get('id'));
				$row->set('created_by_alias',
$this->user->get('username'));
			}

			$isNew         = false;
			$existingTable = ArrayHelper::getValue($data, 'db_table_name',
'');
			$newTable      = $existingTable === '' ?
trim(FArrayHelper::getValue($data, '_database_name')) :
'';

			// Mysql will force db table names to lower case even if you set the db
name to upper case - so use clean()
			$newTable = FabrikString::clean($newTable);

			// can't have table names ending in _
			$newTable = rtrim($newTable, '_');

			// Check the entered database table doesn't already exist
			if ($newTable != '' &&
$this->databaseTableExists($newTable))
			{
				throw new
RuntimeException(Text::_('COM_FABRIK_DATABASE_TABLE_ALREADY_EXISTS'));
			}

			if (!$this->canCreateDbTable())
			{
				throw new
RuntimeException(Text::_('COM_FABRIK_INSUFFICIENT_RIGHTS_TO_CREATE_TABLE'));
			}

            $row->set('modified', $date->toSql());
            $row->set('modified_by',
$this->user->get('id'));

			// Save the row now
			$row->store();
			// Create fabrik form
			$this->createLinkedForm();
			$row->set('form_id',
$this->getState('list.form_id'));
			$groupData          = FabrikWorker::formDefaults('group');
			$groupData['name']  = $row->label;
			$groupData['label'] = $row->label;

			$params = new Registry($row->get('params'));
			$this->setIsView($params);

			if ($params->get('isview', '') === '1')
			{
				$this->app->enqueueMessage(Text::_('COM_FABRIK_LIST_VIEW_SET_ALTER_NO'));
				$params->set('alter_existing_db_cols', '0');
			}

			$row->params = $params->toString();

			if ($newTable == '')
			{
				// Create fabrik group
				$input->set('_createGroup', 1);
				$groupId = $this->createLinkedGroup($groupData, false);

				// New fabrik list but existing db table
				$this->createLinkedElements($groupId);
			}
			else
			{
				$row->set('db_table_name', $newTable);
				$row->set('auto_inc', 1);

				$dbOpts            = array();
				$params            = new Registry($row->get('params'));
				$dbOpts['COLLATE'] = $params->get('collation',
'');
				$fields            =
$contentTypeModel->import($row->get('db_table_name'),
$contentType, $groupData);
				$res               = $this->createDBTable($newTable, $fields,
$dbOpts);

				if (is_array($res))
				{
					$row->set('db_primary_key', $newTable . '.' .
$res[0]);
				}
			}
		}

		// Set the publish date
		if (FabrikWorker::isNullDate($row->get('publish_up'))) {
			if ($row->get('published') == 1) {
				$row->set('publish_up', Factory::getDate()->toSql());
			} else {
				$row->set('publish_up', null);
			}
		}

		if (FabrikWorker::isNullDate($row->get('publish_down'))) {
			$row->set('publish_down', null);
		}

		$pk = FArrayHelper::getValue($data, 'db_primary_key');

		if ($pk == '')
		{
			$pk    =
$feModel->getPrimaryKeyAndExtra($row->get('db_table_name'));
			$key   = $pk[0]['colname'];
			$extra = $pk[0]['extra'];

			// Store without qns as that's db specific
			$row->set('db_primary_key',
$row->get('db_primary_key', '') == '' ?
$row->get('db_table_name') . '.' . $key
				: $row->get('db_primary_key'));
			$row->set('auto_inc', StringHelper::stristr($extra,
'auto_increment') ? 1 : 0);
			
			//trob: make strict happy 
			$row->set('created_by',(int)$row->get('created_by')
);
			$row->set('checked_out', 0);
			$row->set('hits', 0);
			$row->set('private', 0);
			$row->set('filter_action', 'onchange');
			$row->set('group_by', '');
		}

		$row->store();
		$this->updateJoins($data);

		// Needed to ensure pk field is not quoted
		$feModel->setTable($row);

		if (!$feModel->isView())
		{
			$this->updatePrimaryKey($row->get('db_primary_key'),
$row->get('auto_inc'));
		}

		// Make an array of elements and a presumed index size, map is then used
in creating indexes
		$this->createIndexes($params, $row);
		$pkName = $row->getKeyName();

		if (isset($row->$pkName))
		{
			$this->setState($this->getName() . '.id',
$row->get($pkName));
		}

		/**
		 * $$$ hugh - I don't know what this state gets used for, but $iNew
is
		 * currently ending up the wrong way round.  New tables it's false,
		 * existing tables it's true.
		 */
		$this->setState($this->getName() . '.new', $isNew);
		parent::cleanCache('com_fabrik');

		if ($id == 0)
		{
			$contentTypeModel->finalise($row);
		}

		return true;
	}

	/**
	 * Tests if the table is in fact a view
	 *
	 * @return  bool	true if table is a view
	 */
	public function setIsView($params)
	{
		$isView = $params->get('isview', null);

		if (!is_null($isView) && (int) $isView >= 0)
		{
			return $isView;
		}

		$feModel = $this->getFEModel();

		$db = FabrikWorker::getDbo();
		$table = $this->getTable();
		$cn = $feModel->getConnection();
		$c = $cn->getConnection();
		$dbName = $c->database;

		if ($table->db_table_name == '')
		{
			return;
		}

		// @todo JQueryBuilder this?
		$sql = " SELECT table_name, table_type, engine FROM
INFORMATION_SCHEMA.tables " . "WHERE table_name = " .
$db->q($table->db_table_name)
			. " AND table_type = 'view' AND table_schema = " .
$db->q($dbName);
		$db->setQuery($sql);
		$row = $db->loadObjectList();
		$isView = empty($row) ? "0" : "1";
		$feModel->setIsView($isView);

		// Store and save param for following tests
		$params->set('isview', $isView);

		return $isView;
	}

	/**
	 * Make an array of elements and a presumed index size, map is then used
in creating indexes
	 *
	 * @param   Registry $params
	 * @param   Table   $row
	 *
	 * @return  void
	 */
	protected function createIndexes($params, $row)
	{
		$map         = array();
		$feModel     = $this->getFormModel();
		$feListModel = $this->getFEModel();
		$groups      = $feModel->getGroupsHiarachy();

		foreach ($groups as $groupModel)
		{
			$elementModels = $groupModel->getMyElements();

			foreach ($elementModels as $element)
			{
				// Int and DATETIME elements cant have a index size attribute

				try
				{
					$colType = $element->getFieldDescription();
				}
				catch (exception $e)
				{
					// some corner case, like an unpublished join to a non existent
database, so make something up
					$map[$element->getFullName(false, false)] = '';
					$map[$element->getElement()->get('id')]   =
'';
					continue;
				}

				if (StringHelper::stristr($colType, 'int'))
				{
					$size = '';
				}
				elseif (StringHelper::stristr($colType, 'datetime'))
				{
					$size = '';
				}
				else
				{
					$size = '10';
				}

				$map[$element->getFullName(false, false)] = $size;
				$map[$element->getElement()->get('id')]   = $size;
			}
		}
		// Update indexes (added array_key_exists check as these may be during
after CSV import)
		if (!empty($orderBys) &&
array_key_exists($row->get('order_by'), $map))
		{
			foreach ($orderBys as $orderBy)
			{
				if (array_key_exists($orderBy, $map))
				{
					$feListModel->addIndex($orderBy, 'tableorder',
'INDEX', $map[$orderBy]);
				}
			}
		}
		if ($row->get('group_by') !== '' &&
array_key_exists($row->get('group_by'), $map))
		{
			$feListModel->addIndex($row->get('group_by'),
'groupby', 'INDEX',
$map[$row->get('group_by')]);
		}

		if (trim($params->get('group_by_order')) !== '')
		{
			$feListModel->addIndex($params->get('group_by_order'),
'groupbyorder', 'INDEX',
$map[$params->get('group_by_order')]);
		}

		$filterFields = (array) $params->get('filter-fields',
array());

		foreach ($filterFields as $field)
		{
			if (array_key_exists($field, $map))
			{
				$feListModel->addIndex($field, 'prefilter',
'INDEX', $map[$field]);
			}
		}
	}

	/**
	 * Get the the collation for a given table
	 *
	 * @param   Registry        $params
	 * @param   JDatabaseDriver $db
	 * @param   string          $tableName
	 *
	 * @return string
	 */
	protected function getOriginalCollation($params, $db, $tableName)
	{
		if (!empty($tableName))
		{
			$db->setQuery('SHOW TABLE STATUS LIKE ' .
$db->q($tableName));
			$info          = $db->loadObject();
			$origCollation = is_object($info) ? $info->Collation :
$params->get('collation', 'none');
		}
		else
		{
			$origCollation = $params->get('collation',
'none');
		}

		return $origCollation;
	}

	/**
	 * Alter the db table's collation
	 *
	 * @param   FabrikFEModelList $feModel       Front end list model
	 * @param   string            $origCollation Original collection name
	 * @param   Table            $row           New collation
	 *
	 * @since   3.0.7
	 *
	 * @return boolean
	 */
	protected function collation($feModel, $origCollation, $row)
	{
		// Don't attempt to alter new table, or a view, or if we
shouldn't alter the table
		if ($row->get('id') == 0 || $feModel->isView() ||
!$feModel->canAlterFields())
		{
			return false;
		}

		$params       = new Registry($row->get('params'));
		$newCollation = $params->get('collation');

		if ($newCollation !== $origCollation)
		{
			$db   = $feModel->getDb();
			$item = $feModel->getTable();
			$db->setQuery('ALTER TABLE ' . $item->db_table_name .
' COLLATE  ' . $newCollation);
			$db->execute();
		}

		return true;
	}

	/**
	 * Check to see if a table exists
	 *
	 * @param   string $tableName name of table (overwrites form_id val to
test)
	 *
	 * @return  bool    false if no table found true if table found
	 */
	public function databaseTableExists($tableName = null)
	{
		if ($tableName === '')
		{
			return false;
		}

		$table = $this->getTable();

		if (is_null($tableName))
		{
			$tableName = $table->db_table_name;
		}

		$fabrikDatabase = $this->getDb();
		$sql            = 'SHOW TABLES LIKE ' .
$fabrikDatabase->quote($tableName);
		$fabrikDatabase->setQuery($sql);
		$total = $fabrikDatabase->loadResult();
//		echo $fabrikDatabase->getError();
// need a different error reporting here

		return ($total == '') ? false : true;
	}

	/**
	 * Deals with ensuring joins are managed correctly when table is saved
	 *
	 * @param   array $data jForm data
	 *
	 * @return  void
	 */
	private function updateJoins($data)
	{
		$db    = FabrikWorker::getDbo(true);
		$query = $db->getQuery(true);

		// If we are creating a new list then don't update any joins - can
result in groups and elements being removed.
		if ((int) $this->getState('list.id') === 0)
		{
			return;
		}
		// $$$ hugh - added "AND element_id = 0" to avoid fallout from
"random join and group deletion" issue from May 2012
		$query->select('*')->from('#__fabrik_joins')->where('list_id
= ' . (int) $this->getState('list.id') . ' AND
element_id = 0');
		$db->setQuery($query);
		$aOldJoins       = $db->loadObjectList();
		$params          = $data['params'];
		$aOldJoinsToKeep = array();
		$joinsToIndex    = array();
		$joinModel       =
Factory::getApplication()->bootComponent('com_fabrik')->getMVCFactory()->createModel('Join',
'FabrikFEModel');
		$joinIds         = FArrayHelper::getValue($params, 'join_id',
array());
		$joinTypes       = FArrayHelper::getValue($params, 'join_type',
array());
		$joinTableFrom   = FArrayHelper::getValue($params,
'join_from_table', array());
		$joinTable       = FArrayHelper::getValue($params,
'table_join', array());
		$tableKey        = FArrayHelper::getValue($params, 'table_key',
array());
		$joinTableKey    = FArrayHelper::getValue($params,
'table_join_key', array());
		$repeats         = FArrayHelper::getValue($params,
'join_repeat', array());
		$jc              = count($joinTypes);

		// Test for repeat elements to ensure their join isn't removed from
here
		foreach ($aOldJoins as $oldJoin)
		{
			if ($oldJoin->params !== '')
			{
				$oldParams = json_decode($oldJoin->params);

				if (isset($oldParams->type) && $oldParams->type ==
'repeatElement')
				{
					$aOldJoinsToKeep[] = $oldJoin->id;
				}
			}
		}

		for ($i = 0; $i < $jc; $i++)
		{
			$existingJoin = false;

			foreach ($aOldJoins as $oOldJoin)
			{
				if ($joinIds[$i] == $oOldJoin->id)
				{
					$existingJoin   = true;
					$joinsToIndex[] = $oOldJoin;
					break;
				}
			}

			if (!$existingJoin)
			{
				$joinsToIndex[] = $this->makeNewJoin($tableKey[$i],
$joinTableKey[$i], $joinTypes[$i], $joinTable[$i], $joinTableFrom[$i],
$repeats[$i][0]);
			}
			else
			{
				/* load in the existing join
				 * if the table_join has changed we need to create a new join
				 * (with its corresponding group and elements)
				 *  and mark the loaded one as to be deleted
				 */
				$joinModel->setId($joinIds[$i]);
				$joinModel->clearJoin();
				$join = $joinModel->getJoin();

				if ($join->table_join != $joinTable[$i])
				{
					$this->makeNewJoin($tableKey[$i], $joinTableKey[$i],
$joinTypes[$i], $joinTable[$i], $joinTableFrom[$i], $repeats[$i][0]);
				}
				else
				{
					// The table_join has stayed the same so we simply update the join
info
					$join->table_key      = str_replace('`', '',
$tableKey[$i]);
					$join->table_join_key = $joinTableKey[$i];
					$join->join_type      = $joinTypes[$i];
					$join->store();

					// Update group
					$group = $this->getTable('Group');
					$group->load($join->group_id);
					$gparams                      = json_decode($group->params);
					$gparams->repeat_group_button = $repeats[$i][0] == 1 ? 1 : 0;
					$group->params                = json_encode($gparams);
					$group->store();
					$aOldJoinsToKeep[] = $joinIds[$i];
				}
			}
		}
		// Remove non existing joins
		if (is_array($aOldJoins))
		{
			foreach ($aOldJoins as $oOldJoin)
			{
				if (!in_array($oOldJoin->id, $aOldJoinsToKeep))
				{
					// Delete join
					$join = $this->getTable('Join');
					$joinModel->setId($oOldJoin->id);
					$joinModel->clearJoin();
					$joinModel->getJoin();
					$joinModel->deleteAll($oOldJoin->group_id);
				}
			}
		}

		// And finally, Esther ... index the join FK's
		foreach ($joinsToIndex as $thisJoin)
		{
			$fields  = $this->getDBFields($thisJoin->table_join,
'Field');
			$fkField = FArrayHelper::getValue($fields, $thisJoin->table_join_key,
false);

			switch ($fkField->BaseType)
			{
				case 'VARCHAR':
					$fkSize = (int) $fkField->BaseLength < 10 ?
$fkField->BaseLength : 10;
					break;
				case 'INT':
				case 'DATETIME':
				default:
					$fkSize = '';
					break;
			}

			$joinField = $thisJoin->table_join . '___' .
$thisJoin->table_join_key;
			$this->getFEModel()->addIndex($joinField, 'join_fk',
'INDEX', $fkSize);
		}
	}

	/**
	 * New join make the group, group elements and formgroup entries for the
join data
	 *
	 * @param   string $tableKey      table key
	 * @param   string $joinTableKey  join to table key
	 * @param   string $joinType      join type
	 * @param   string $joinTable     join to table
	 * @param   string $joinTableFrom join table
	 * @param   bool   $isRepeat      is the group a repeat
	 *
	 * @return  object  $join           returns new join object
	 */
	protected function makeNewJoin($tableKey, $joinTableKey, $joinType,
$joinTable, $joinTableFrom, $isRepeat)
	{
		$groupData          = FabrikWorker::formDefaults('group');
		$groupData['name']  = $this->getTable()->label . '-
[' . $joinTable . ']';
		$groupData['label'] = $joinTable;
		$groupId            = $this->createLinkedGroup($groupData, true,
$isRepeat);

		$join = $this->getTable('Join');
		$join->set('id', null);
		$join->set('list_id',
$this->getState('list.id'));
		$join->set('join_from_table', $joinTableFrom);
		$join->set('table_join', $joinTable);
		$join->set('table_join_key', $joinTableKey);
		$join->set('table_key', str_replace('`',
'', $tableKey));
		$join->set('join_type', $joinType);
		$join->set('group_id', $groupId);
		/**
		 * Create the 'pk' param.  Can't just call front end
setJoinPk() for gory
		 * reasons, so do this by steam.
		 */
		$joinParams = new Registry;
		/**
		 * This is kind of expensive, as getPrimaryKeyAndExtra() method does a
table lookup,
		 * but I don't think we know what the PK of the joined table is any
other
		 * way at this point.
		 */
		$pk =
$this->getFEModel()->getPrimaryKeyAndExtra($join->get('table_join'));

		if ($pk !== false)
		{
			// If it didn't return false, getPrimaryKeyAndExtra will have
created and array with at least one key
			$pk_col = FArrayHelper::getValue($pk[0], 'colname',
'');

			if (!empty($pk_col))
			{
				$db     = FabrikWorker::getDbo(true);
				$pk_col = $join->table_join . '.' . $pk_col;
				$joinParams->set('pk', $db->qn($pk_col));
			}
		}

		$join->set('params', (string) $joinParams);
		$join->store();

		$this->createLinkedElements($groupId, $joinTable);

		return $join;
	}

	/**
	 * When saving a table that links to a database for the first time we
	 * need to create all the elements based on the database table fields and
their
	 * column type
	 *
	 * @param   int    $groupId   group id
	 * @param   string $tableName table name - if not set then use
jform's db_table_name (@since 3.1)
	 *
	 * @return  void
	 */
	private function createLinkedElements($groupId, $tableName = '')
	{
		$db    = FabrikWorker::getDbo(true);
		$input = $this->app->input;

		if ($tableName === '')
		{
			$jForm     = $input->get('jform', array(),
'array');
			$tableName = FArrayHelper::getValue($jForm, 'db_table_name');
		}

		$pluginManager = FabrikWorker::getPluginManager();
		$groupTable    = FabTable::getInstance('Group',
'FabrikTable');
		$groupTable->load($groupId);

		// Here we're importing directly from the database schema
		$query = $db->getQuery(true);
		$query->select('id')->from('#__fabrik_lists')->where('db_table_name
= ' . $db->q($tableName));
		$db->setQuery($query);
		$id = $db->loadResult();

		if ($id)
		{
			// A fabrik table already exists - so we can copy the formatting of its
elements
			/** @var FabrikFEModelList $groupListModel */
			$groupListModel =
Factory::getApplication()->bootComponent('com_fabrik')->getMVCFactory()->createModel('List',
'FabrikFEModel');
			$groupListModel->setId($id);
			$groupListModel->getTable();
			$groups       = $groupListModel->getFormGroupElementData();
			$newElements  = array();
			$elementCount = 0;

			foreach ($groups as $groupModel)
			{
				/**
				 * If we are saving a new table and the previously found tables group
is a join
				 * then don't add its elements to the table as they don't
exist in the database table
				 * we are linking to
				 * $$$ hugh - why the test for task and new table?  When creating
elements for a copy of a table,
				 * surely we NEVER want to include elements which were joined to the
original,
				 * regardless of whether this is a new List?  Bearing in mind that this
routine gets called from
				 * the makeNewJoin() method, when adding a join to an existing list, to
build the "Foo - [bar]" join
				 * group, as well as from save() when creating a new List.
				 *
				 *  if ($groupModel->isJoin() &&
$input->get('task') == 'save' &&
$input->getInt('id') == 0)
				 */
				if ($groupModel->isJoin())
				{
					continue;
				}

				$elementModels = &$groupModel->getMyElements();

				foreach ($elementModels as $elementModel)
				{
					$elementCount++;
					$element                   = $elementModel->getElement();
					$copy                      =
$elementModel->copyRow($element->id, $element->label, $groupId);
					$newElements[$element->id] = $copy->id;
				}
			}

			foreach ($newElements as $origId => $newId)
			{
				$plugin = $pluginManager->getElementPlugin($newId);
				$plugin->finalCopyCheck($newElements);
			}
			// Hmm table with no elements - lets create them from the structure
anyway
			if ($elementCount == 0)
			{
				$this->makeElementsFromFields($groupId, $tableName);
			}
		}
		else
		{
			// No previously found fabrik list
			$this->makeElementsFromFields($groupId, $tableName);
		}
	}

	/**
	 * Take a table name and make elements for all of its fields
	 *
	 * @param   int    $groupId   group id
	 * @param   string $tableName table name
	 *
	 * @return  void
	 */
	protected function makeElementsFromFields($groupId, $tableName)
	{
		$fabrikDb      = $this->getFEModel()->getDb();
		$dispatcher    = Factory::getApplication()->getDispatcher();
		$input         = $this->app->input;
		$elementModel  = new PlgFabrik_Element($dispatcher);
		$pluginManager = FabrikWorker::getPluginManager();
		$fbConfig      = ComponentHelper::getParams('com_fabrik');
		$elementTypes  = $input->get('elementtype', array(),
'array');
		$fields        = $fabrikDb->getTableColumns($tableName, false);
		$createDate    = Factory::getDate()->toSQL();
		$key           =
$this->getFEModel()->getPrimaryKeyAndExtra($tableName);
		$ordering      = 0;
		/**
		 * no existing fabrik table so we take a guess at the most
		 * relevant element types to  create
		 */
		$elementLabels = $input->get('elementlabels', array(),
'array');

		foreach ($fields as $label => $properties)
		{
			$plugin     = 'field';
			$type       = $properties->Type;
			$maxLength  = 255;
			$maxLength2 = 0;

			if (preg_match("/\((.*)\)/i", $type, $matches))
			{
				$maxLength = FArrayHelper::getValue($matches, 1, 255);
				$maxLength = explode(',', $maxLength);

				if (count($maxLength) > 1)
				{
					$maxLength2 = $maxLength[1];
					$maxLength  = $maxLength[0];
				}
				else
				{
					$maxLength  = $maxLength[0];
					$maxLength2 = 0;
				}
			}

			// Get the basic type
			$type    = explode(" ", $type);
			$type    = FArrayHelper::getValue($type, 0, '');
			$type    = preg_replace("/\((.*)\)/i", '', $type);
			$element = FabTable::getInstance('Element',
'FabrikTable');

			if (array_key_exists($ordering, $elementTypes))
			{
				// If importing from a CSV file then we have userselect field
definitions
				$plugin = $elementTypes[$ordering];
			}
			else
			{
				// If the field is the primary key and it's an INT type set the
plugin to be the fabrik internal id
				if ($key[0]['colname'] == $label &&
StringHelper::strtolower(substr($key[0]['type'], 0, 3)) ===
'int')
				{
					$plugin = 'internalid';
				}
				else
				{
					// Otherwise set default type
					switch ($type)
					{
						case "int":
						case "decimal":
						case "tinyint":
						case "smallint":
						case "mediumint":
						case "bigint":
						case "varchar":
						case "time":
							$plugin = 'field';
							break;
						case "text":
						case "tinytext":
						case "mediumtext":
						case "longtext":
							$plugin = 'textarea';
							break;
						case "datetime":
						case "date":
						case "timestamp":
							$plugin = 'jdate';
							break;
						default:
							$plugin = 'field';
							break;
					}
				}
				// Then alter if defined in Fabrik global config
				// Jaanus: but first check if there are any pk field and if yes then
create as internalid
				$defType = StringHelper::strtolower(substr($key[0]['type'],
0, 3));
				$plugin  = ($key[0]['colname'] == $label && $defType
=== 'int') ? 'internalid' : $fbConfig->get($type,
$plugin);
			}

			$element->plugin               = $plugin;
			$element->hidden               = $element->label == 'id'
? '1' : '0';
			$element->group_id             = $groupId;
			$element->name                 = $label;
			$element->created              = $createDate;
			$element->created_by           =
$this->user->get('id');
			$element->created_by_alias     =
$this->user->get('username');
			$element->published            = '1';
			$element->show_in_list_summary = '1';

			switch ($plugin)
			{
				case 'textarea':
					$element->width = '40';
					break;
				case 'date':
					$element->width = '10';
					break;
				default:
					$element->width = '30';
					break;
			}

			if ($element->width > $maxLength)
			{
				$element->width = $maxLength;
			}

			$element->height   = '6';
			$element->ordering = $ordering;
			$p                 = json_decode($elementModel->getDefaultAttribs());

			if (in_array($type, array('int', 'tinyint',
'smallint', 'mediumint', 'bigint'))
&& $plugin == 'field')
			{
				$p->integer_length = $maxLength;
				$p->text_format    = 'integer';
				$p->maxlength      = '255';
				$element->width    = '30';
			}
			elseif ($type == 'decimal' && $plugin ==
'field')
			{
				$p->text_format    = 'decimal';
				$p->decimal_length = $maxLength2;
				$p->integer_length = $maxLength - $maxLength2;
				$p->maxlength      = '255';
				$element->width    = '30';
			}
			else
			{
				$p->maxlength = $maxLength;
			}

			$element->params = json_encode($p);
			$element->label  = FArrayHelper::getValue($elementLabels, $ordering,
str_replace("_", " ", $label));

			//Format Label
			$labelConfig = $fbConfig->get('format_labels',
'0');
			switch ($labelConfig)
			{
				case '1':
					$element->label = strtolower($element->label);
					break;
				case '2':
					$element->label = ucwords($element->label);
					break;
				case '3':
					$element->label = ucfirst($element->label);
					break;
				case '4':
					$element->label = strtoupper($element->label);
					break;
				case '5':
					$element->label = strtoupper(str_replace(" ",
"_", $element->label));
					break;
				case '6':
					$element->label = FArrayHelper::getValue($elementLabels, $ordering,
$label);
					break;
				default:
					break;
			}

			$element->store();
			$elementModel = $pluginManager->getPlugIn($element->plugin,
'element');
			$elementModel->setId($element->id);
			$elementModel->element = $element;

			// Hack for user element
			$details = array('group_id' => $element->group_id);
			$input->set('details', $details);
			$elementModel->onSave(array());
			$ordering++;
		}
	}

	/**
	 * When saving a list that links to a database for the first time we
	 * automatically create a form to allow the update/creation of that tables
	 * records
	 *
	 * @param   int $formId to copy from. If = 0 then create a default form.
If not 0 then copy the form id passed in
	 *
	 * @return  object  form model
	 */
	private function createLinkedForm($formId = 0)
	{
		$this->getFormModel();

		if ($formId == 0)
		{
			/**
			 * $$$ rob required otherwise the Table is loaed with db_table_name as a
property
			 * which then generates an error - not sure why its loaded like that
though?
			 * 18/08/2011 - could be due to the Form table class having it in its
bind method
			 * - (have now overridden form table store() to remove thoes two params)
			 */
			$this->formModel->getForm();
			jimport('joomla.utilities.date');
			$createDate = Factory::getDate();
			$createDate = $createDate->toSql();
			$form       = $this->getTable('Form');
			$item       = $this->getTable('List');

			$defaults = FabrikWorker::formDefaults('form');
			$form->bind($defaults);

			$form->set('label', $item->get('label'));
			$form->set('record_in_database', 1);
			$form->set('created', $createDate);
			$form->set('created_by',
$this->user->get('id'));
			$form->set('checked_out', 0);
			$form->set('created_by_alias',
$this->user->get('username'));
            $form->set('modified', $createDate);
            $form->set('modified_by',
$this->user->get('id'));
			$form->set('error',
Text::_('COM_FABRIK_FORM_ERROR_MSG_TEXT'));
			$form->set('submit_button_label',
Text::_('COM_FABRIK_SAVE'));
			$form->set('published',
$item->get('published'));
			$form->set('form_template', 'bootstrap');
			$form->set('view_only_template', 'bootstrap');

			$form->store();
			$this->setState('list.form_id',
$form->get('id'));
			$this->formModel->setId($form->get('id'));
		}
		else
		{
			$this->setState('list.form_id', $formId);
			$this->formModel->setId($formId);
			$this->formModel->getTable();
			$this->formModel->copy();
		}

		$this->formModel->getForm();

		return $this->formModel;
	}

	/**
	 * Create a group
	 * used when creating a fabrik table from an existing db table
	 *
	 * NEW also creates the form group
	 *
	 * @param   array $data     group data
	 * @param   bool  $isJoin   is the group a join default false
	 * @param   bool  $isRepeat is the group repeating
	 *
	 * @return  int  group id
	 */
	public function createLinkedGroup($data, $isJoin = false, $isRepeat =
false)
	{
		$createDate = Factory::getDate();
		$group      = $this->getTable('Group');
		$group->bind($data);
		$group->set('id', null);
		$group->set('created', $createDate->toSql());
		$group->set('created_by',
$this->user->get('id'));
		$group->set('created_by_alias',
$this->user->get('username'));
		$group->set('modified',
$group->get('created'));
		$group->set('checked_out', 0);
		$group->set('published', ArrayHelper::getValue($data,
'published', 1));
		$opts                          = ArrayHelper::getValue($data,
'params', new stdClass);

		if (is_array($opts))
		{
			$opts = ArrayHelper::toObject($opts);
		}

		$opts->repeat_group_button     = $isRepeat ? 1 : 0;
		$opts->repeat_group_show_first = 1;
		$group->set('params', json_encode($opts));
		$group->set('is_join', ($isJoin == true) ? 1 : 0);
		$group->store();

		// Create form group
		$formId    = $this->getState('list.form_id');
		$formGroup = $this->getTable('FormGroup');

		$formGroup->set('id', null);
		$formGroup->set('form_id', $formId);
		$formGroup->set('group_id', $group->get('id'));
		$formGroup->set('ordering', 999999);
		$formGroup->store();
		$formGroup->reorder(" form_id = '$formId'");

		return $group->id;
	}

	/**
	 * Test if the main J user can create mySQL tables
	 *
	 * @return  bool
	 */
	private function canCreateDbTable()
	{
		return true;
		/**
		 * @todo run create table test once when you install fabrik instead
		 * dont use method below but simply try to create a table and if you cant
give error
		 * if you can remove tmp created table
		 */
		/*$db 		=& FabrikWorker::getDbo();
		$conf =& Factory::getApplication()->getConfig();
		$host 		= $conf->getValue('config.host');
		$user 		= $conf->getValue('config.user');
		$db->setQuery("SELECT Create_priv FROM mysql.user WHERE (Host =
'$host' OR Host = '%') AND user =
'$user'");
		$res = $db->loadResult();
		if ($res == 'N' || is_null($res)) {
		return false;
		} else {
		return true;
		}*/
	}

	/**
	 * Method to copy one or more records.
	 *
	 * @return  boolean    True if successful, false if an error occurs.
	 *
	 * @since    1.6
	 */
	public function copy()
	{
		$db    = FabrikWorker::getDbo(true);
		$input = $this->app->input;
		$pks   = $input->get('cid', array(), 'array');
		$names = $input->get('names', array(), 'array');

		foreach ($pks as $i => $pk)
		{
			$item = $this->getTable();
			$item->load($pk);
			$item->set('id', null);
			$input->set('newFormLabel',
$names[$pk]['formLabel']);
			$input->set('newGroupNames',
$names[$pk]['groupNames']);
			$formModel = $this->createLinkedForm($item->form_id);

			if (!$formModel)
			{
				return;
			}
			// $$$ rob 20/12/2011 - any element id stored in the list needs to get
mapped to the new element ids

			$elementMap = $formModel->newElements;
			$params     = json_decode($item->params);
			$toMaps     = array(array('list_search_elements',
'search_elements'), array('csv_elements',
'show_in_csv'));

			foreach ($toMaps as $toMap)
			{
				$key  = $toMap[0];
				$key2 = $toMap[1];
				$orig = json_decode($params->$key);
				$new  = array();

				foreach ($orig->$key2 as $elementId)
				{
					$new[] = $elementMap[$elementId];
				}

				$c            = new stdClass;
				$c->$key2     = $new;
				$params->$key = json_encode($c);
			}

			$item->set('form_id',
$formModel->getTable()->get('id'));
			$createDate = Factory::getDate();
			$createDate = $createDate->toSql();
			$item->set('label', $names[$pk]['listLabel']);
			$item->set('created', $createDate);
			$item->set('modified', $db->getNullDate());
			$item->set('modified_by',
$this->user->get('id'));
			$item->set('hits', 0);
			$item->set('checked_out', 0);
			$item->set('checked_out_time', $db->getNullDate());
			$item->set('params', json_encode($params));

			if (!$item->store())
			{
				return false;
			}

			$this->setState('list.id', $item->id);

			// Test for seeing if joins correctly stored when coping new table
			$this->copyJoins($pk, $item->id, $formModel->groupidmap);
		}

		return true;
	}

	/**
	 * When copying a table we need to copy its joins as well
	 * note that the group and elements already exists - just the join needs
to be saved
	 *
	 * @param   int   $fromId     table id to copy from
	 * @param   int   $toId       table id to copy to
	 * @param   array $groupIdMap saying which groups got copied to which new
group id (key = old id, value = new id)
	 *
	 * @return null
	 */
	protected function copyJoins($fromId, $toId, $groupIdMap)
	{
		$db    = FabrikWorker::getDbo(true);
		$query = $db->getQuery(true);
		$query->select('*')->from('#__fabrik_joins')->where('list_id
= ' . (int) $fromId);
		$db->setQuery($query);
		$joins   = $db->loadObjectList();
		$feModel = $this->getFEModel();

		foreach ($joins as $join)
		{
			$size = 10;
			$els  = &$feModel->getElements();

			// $$$ FIXME hugh - joined els are missing tablename
			foreach ($els as $el)
			{
				// $$$ FIXME hugh - need to make sure we pick up the element from the
main table,
				// not any similarly named elements from joined tables (like
'id')
				if ($el->getElement()->name == $join->table_key)
				{
					$size = StringHelper::stristr($el->getFieldDescription(),
'int') ? '' : '10';
				}
			}

			$feModel->addIndex($join->table_key, 'join',
'INDEX', $size);
			$joinTable = $this->getTable('Join');
			$joinTable->load($join->id);
			$joinTable->set('id', 0);
			$joinTable->set('group_id',
$groupIdMap[$joinTable->group_id]);
			$joinTable->set('list_id', $toId);
			$joinTable->store();
		}
	}

	/**
	 * Adds a primary key to the database table
	 *
	 * @param   string $fieldName     the column name to make into the primary
key
	 * @param   bool   $autoIncrement is the column an auto incrementing
number
	 * @param   string $type          column type definition (eg varchar(255))
	 *
	 * @return  void
	 */
	protected function updatePrimaryKey($fieldName, $autoIncrement, $type =
'int(11)')
	{
		$feModel = $this->getFEModel();
		$input   = $this->app->input;

		if (!$feModel->canAlterFields())
		{
			return;
		}

		$fabrikDatabase = $feModel->getDb();
		$jForm          = $input->get('jform', array(),
'array');
		$tableName      = ($jForm['db_table_name'] != '') ?
$jForm['db_table_name'] : $jForm['_database_name'];
		$tableName      = preg_replace('#[^0-9a-zA-Z_]#',
'_', $tableName);
		$aPriKey        = $feModel->getPrimaryKeyAndExtra($tableName);

		if (!$aPriKey)
		{
			// No primary key set so we should set it
			$this->addKey($fieldName, $autoIncrement, $type);
		}
		else
		{
			if (count($aPriKey) > 1)
			{
				// $$$ rob multi field pk - ignore for now

				return;
			}

			$aPriKey  = $aPriKey[0];
			$shortKey = FabrikString::shortColName($fieldName);

			// $shortKey = $feModel->_shortKey($fieldName, true); // added true
for second arg so it strips quotes, as was never matching colname with
quotes
			if ($fieldName != $aPriKey['colname'] && $shortKey !=
$aPriKey['colname'])
			{
				// Primary key already exists so we should drop it
				$this->dropKey($aPriKey);
				$this->addKey($fieldName, $autoIncrement, $type);
			}
			else
			{
				// Update the key, it if we need to
				$priInc = $aPriKey['extra'] == 'auto_increment' ?
'1' : '0';

				if ($priInc != $autoIncrement || $type != $aPriKey['type'])
				{
					$this->updateKey($fieldName, $autoIncrement, $type);
				}
			}
		}
	}

	/**
	 * Internal function: add a key to the table
	 *
	 * @param   string $fieldName     primary key column name
	 * @param   bool   $autoIncrement is the column auto incrementing
	 * @param   string $type          the primary keys column type (if
autoincrement true then int(6) is always used as
	 *                                the type)
	 *
	 * @return  mixed  false / JError
	 */
	private function addKey($fieldName, $autoIncrement, $type =
"INT(6)")
	{
		$db        = $this->getFEModel()->getDb();
		$input     = $this->app->input;
		$type      = $autoIncrement != true ? $type : 'INT(6)';
		$jForm     = $input->get('jform', array(),
'array');
		$tableName = ($jForm['db_table_name'] != '') ?
$jForm['db_table_name'] : $jForm['_database_name'];
		$tableName = preg_replace('#[^0-9a-zA-Z_]#', '_',
$tableName);
		$tableName = FabrikString::safeColName($tableName);
		$fieldName = FabrikString::shortColName($fieldName);

		if ($fieldName === '')
		{
			return false;
		}

		$fieldName = $db->qn($fieldName);
		$sql       = 'ALTER TABLE ' . $tableName . ' ADD PRIMARY
KEY (' . $fieldName . ')';

		// Add a primary key
		$db->setQuery($sql);
		$db->execute();

		if ($autoIncrement)
		{
			// Add the autoinc
			$sql = 'ALTER TABLE ' . $tableName . ' CHANGE ' .
$fieldName . ' ' . $fieldName . ' ' . $type . '
NOT NULL AUTO_INCREMENT';
			$db->setQuery($sql);
			$db->execute();
		}

		return true;
	}

	/**
	 * Internal function: drop the table's key
	 *
	 * @param   array $aPriKey existing key data
	 *
	 * @return  bool true if key dropped
	 */
	private function dropKey($aPriKey)
	{
		$db        = $this->getFEModel()->getDb();
		$input     = $this->app->input;
		$jForm     = $input->get('jform', array(),
'array');
		$tableName =
FabrikString::safeColName($jForm['db_table_name']);
		$sql       = 'ALTER TABLE ' . $tableName . ' CHANGE '
. FabrikString::safeColName($aPriKey['colname']) . ' '
			. FabrikString::safeColName($aPriKey['colname']) . '
' . $aPriKey['type'] . ' NOT NULL';

		// Remove the auto-increment
		$db->setQuery($sql);
		$db->execute();

		$sql = 'ALTER TABLE ' . $tableName . ' DROP PRIMARY
KEY';

		// Drop the primary key
		$db->setQuery($sql);
		$db->execute();

		return true;
	}

	/**
	 * Internal function: update an existing key in the table
	 *
	 * @param   string $fieldName     primary key column name
	 * @param   bool   $autoIncrement is the column auto incrementing
	 * @param   string $type          the primary keys column type
	 *
	 * @return  void
	 */
	protected function updateKey($fieldName, $autoIncrement, $type =
"INT(11)")
	{
		$input     = $this->app->input;
		$jForm     = $input->get('jform', array(),
'array');
		$tableName =
FabrikString::safeColName($jForm['db_table_name']);
		$db        = $this->getFEModel()->getDb();

		if (strstr($fieldName, '.'))
		{
			$fieldName = array_pop(explode(".", $fieldName));
		}

		$table = $this->getTable();
		$table->load($this->getState('list.id'));
		$sql = 'ALTER TABLE ' . $tableName . ' CHANGE ' .
FabrikString::safeColName($fieldName) . ' ' .
FabrikString::safeColName($fieldName) . ' '
			. $type . ' NOT NULL';

		// Update primary key
		if ($autoIncrement)
		{
			$sql .= " AUTO_INCREMENT";
		}

		$db->setQuery($sql);
		$db->execute();
	}

	/**
	 * Method to delete one or more records.
	 *
	 * @param   array &$pks An array of record primary keys.
	 *
	 * @return  boolean    True if successful, false if an error occurs.
	 *
	 * @since    1.6
	 */
	public function delete(&$pks)
	{
		// Initialise variables.
		$pks        = (array) $pks;
		$table      = $this->getTable();

		// Include the content plugins for the on delete events.
		PluginHelper::importPlugin('content');

		$input       = $this->app->input;
		$jForm       = $input->get('jform', array(),
'array');
		$deleteDepth = $jForm['recordsDeleteDepth'];
		$drop        = $jForm['dropTablesFromDB'];
		$feModel        = $this->getFEModel();
		$fabrikDatabase = $feModel->getDb();
		$dbConfigPrefix = $this->app->get('dbprefix');

		// Iterate the items to delete each one.
		foreach ($pks as $i => $pk)
		{
			$feModel->setId($pk);

			if ($table->load($pk))
			{
				$feModel->setTable($table);

				if ($drop)
				{
					if (strncasecmp($table->db_table_name, $dbConfigPrefix,
StringHelper::strlen($dbConfigPrefix)) == 0)
					{
						$this->app->enqueueMessage(Text::sprintf('COM_FABRIK_TABLE_NOT_DROPPED_PREFIX',
$table->db_table_name, $dbConfigPrefix), 'notice');
					}
					else
					{
						if (!empty($table->db_table_name))
						{
							$feModel->drop();
							$this->app->enqueueMessage(Text::sprintf('COM_FABRIK_TABLE_DROPPED',
$table->db_table_name));
						}
					}
				}
				else
				{
					$this->app->enqueueMessage(Text::sprintf('COM_FABRIK_TABLE_NOT_DROPPED',
$table->db_table_name));
				}

				if ($this->canDelete($table))
				{
					$context = $this->option . '.' . $this->name;

					// Trigger the onContentBeforeDelete event.
					$result =
Factory::getApplication()->triggerEvent($this->event_before_delete,
array($context, $table));

					if (in_array(false, $result, true))
					{
						$this->setError($table->getError());

						return false;
					}

					if (!$table->delete($pk))
					{
						$this->setError($table->getError());

						return false;
					}

					// Trigger the onContentAfterDelete event.
					Factory::getApplication()->triggerEvent($this->event_after_delete,
array($context, $table));
				}
				else
				{
					// Prune items that you can't change.
					unset($pks[$i]);

					throw new
Exception(Text::_('JLIB_APPLICATION_ERROR_EDIT_STATE_NOT_PERMITTED'),
403);
				}

				switch ($deleteDepth)
				{
					case 0:
					default:
						// List only
						break;
					case 1:
						// List and form
						$form = $this->deleteAssociatedForm($table);
						break;
					case 2:
						// List form and groups
						$form = $this->deleteAssociatedForm($table);
						$this->deleteAssociatedGroups($form, false);
						break;
					case 3:
						// List form groups and elements
						$form = $this->deleteAssociatedForm($table);
						$this->deleteAssociatedGroups($form, true);
						break;
				}
			}
			else
			{
				$this->setError($table->getError());

				return false;
			}
		}

		return true;
	}

	/**
	 * Remove the associated form
	 *
	 * @param   object &$item list item
	 *
	 * @return boolean|form object
	 */
	private function deleteAssociatedForm(&$item)
	{
		$db    = FabrikWorker::getDbo(true);
		$query = $db->getQuery(true);
		$form  = $this->getTable('form');
		$form->load($item->form_id);

		if ((int) $form->id === 0)
		{
			return false;
		}

		$query->delete()->from('#__fabrik_forms')->where('id
= ' . (int) $form->id);
		$db->setQuery($query);
		$db->execute();

		return $form;
	}

	/**
	 * Delete associated fabrik groups
	 *
	 * @param   object &$form          item
	 * @param   bool   $deleteElements delete group items as well
	 *
	 * @return boolean|form id
	 */
	private function deleteAssociatedGroups(&$form, $deleteElements =
false)
	{
		$db    = FabrikWorker::getDbo(true);
		$query = $db->getQuery(true);

		// Get group ids
		if ((int) $form->id === 0)
		{
			return false;
		}

		$query->select('group_id')->from('#__fabrik_formgroup')->where('form_id
= ' . (int) $form->id);
		$db->setQuery($query);
		$groupIds = (array) $db->loadColumn();

		// Delete groups
		$groupModel =
Factory::getApplication()->bootComponent('com_fabrik')->getMVCFactory()->createModel('Group',
'FabrikAdminModel');
		$groupModel->delete($groupIds, $deleteElements);

		return $form;
	}

	/**
	 * Make a database table from  XML definition
	 *
	 * @param   string $key  primary key
	 * @param   string $name table name
	 * @param   string $xml  xml table definition
	 *
	 * @return bool
	 */
	public function dbTableFromXML($key, $name, $xml)
	{
		$row  = $xml[0];
		$data = array();

		// Get which field types to use
		foreach ($row->children() as $child)
		{
			$value = sprintf('%s', $child);
			$type  = $child->attributes()->type;

			if ($type == '')
			{
				$objType = strtotime($value) == false ? 'VARCHAR(255)' :
'DATETIME';

				if (strstr($value, "\n"))
				{
					$objType = 'TEXT';
				}
			}
			else
			{
				switch (StringHelper::strtolower($type))
				{
					case 'integer':
						$objType = 'INT';
						break;
					case 'datetime':
						$objType = 'DATETIME';
						break;
					case 'float':
						$objType = 'DECIMAL(10,2)';
						break;
					default:
						$objType = 'VARCHAR(255)';
						break;
				}
			}

			$data[$child->getName()] = $objType;
		}

		if (empty($data))
		{
			return false;
		}

		$db    = $this->_db;
		$query = 'CREATE TABLE IF NOT EXISTS ' . $db->qn($name) .
' (';

		foreach ($data as $fname => $objType)
		{
			$query .= $db->qn($fname) . " $objType, \n";
		}

		$query .= ' primary key (' . $key . '))';
		$query .= ' ENGINE = MYISAM ';
		$db->setQuery($query);
		$db->execute();

		// Get a list of existing ids
		$query = $db->getQuery(true);
		$query->select($key)->from($name);
		$db->setQuery($query);
		$existingIds = $db->loadColumn();

		// Build the row object to insert/update
		foreach ($xml as $row)
		{
			$o = new stdClass;

			foreach ($row->children() as $child)
			{
				$k     = $child->getName();
				$o->$k = sprintf("%s", $child);
			}

			// Either update or add records
			if (in_array($o->$key, $existingIds))
			{
				$db->updateObject($name, $o, $key);
			}
			else
			{
				$db->insertObject($name, $o, $key);
			}
		}

		return true;
	}

	/**
	 * Load list from form id
	 *
	 * @param   int $formId form id
	 *
	 * @return  Table
	 */
	public function loadFromFormId($formId)
	{
		$item = $this->getTable();

		/**
		 * Not sure why but we need to populate and manually __state_set
		 * Otherwise list.id reverts to the form's id and not the list id
		 */
		$this->populateState();
		$this->__state_set = true;
		$item->load(array('form_id' => $formId));
		$this->table = $item;
		$this->setState('list.id', $item->get('id'));

		return $item;
	}

	/**
	 * Load the database object associated with the list
	 *
	 * @since   3.0b
	 *
	 * @return  object database
	 */
	public function &getDb()
	{
		$listId = $this->getState('list.id');
		$item   = $this->getItem($listId);

		return FabrikWorker::getConnection($item)->getDb();
	}

	/**
	 * Create a table to store the forms' data depending upon what groups
are assigned to the form
	 *
	 * @param   string $dbTableName Taken from the table object linked to the
form
	 * @param   array  $fields      List of default elements to add. (key =
element name, value = plugin
	 * @param   array  $opts        Additional options, e.g. collation
	 *
	 * @return mixed false if fail otherwise array of primary keys
	 */
	public function createDBTable($dbTableName = null, $fields =
array('id' => 'internalid', 'date_time'
=> 'date'),
		$opts = array())
	{
		$db        = FabrikWorker::getDbo(true);
		$fabrikDb  = $this->getDb();
		$formModel = $this->getFormModel();

		if (is_null($dbTableName))
		{
			$dbTableName = $this->getTable()->db_table_name;
		}

		$sql   = 'CREATE TABLE IF NOT EXISTS ' .
$db->qn($dbTableName) . ' (';
		$input = $this->app->input;
		$jForm = $input->get('jform', array(), 'array');

		if ($jForm['id'] == 0 &&
array_key_exists('current_groups', $jForm))
		{
			// Saving a new form
			$groupIds = $jForm['current_groups'];
		}
		else
		{
			$query  = $db->getQuery(true);
			$formId = (int) $this->get('form.id',
$this->getFormModel()->id);
			$query->select('group_id')->from('#__fabrik_formgroup')->where('form_id
= ' . $formId);
			$db->setQuery($query);
			$groupIds = $db->loadColumn();
		}

		$i = 0;

		foreach ($fields as $name => $plugin)
		{
			// $$$ hugh - testing corner case where we are called from form
model's updateDatabase,
			// and the underlying table has been deleted.  So elements already
exist.
			$element = $formModel->getElement($name);

			if ($element === false)
			{
				// Installation demo data sets 2 group ids
				if (is_string($plugin))
				{
					$plugin = array('plugin' => $plugin, 'group_id'
=> $groupIds[0]);
				}

				$plugin['ordering'] = $i;
				$element            = $this->makeElement($name, $plugin);

				if (!$element)
				{
					return false;
				}
			}

			$elementModels[] = clone ($element);
			$i++;
		}

		$arAddedObj = array();
		$keys       = array();
		$lines      = array();

		foreach ($elementModels as $elementModel)
		{
			$element = $elementModel->getElement();

			// Replace all non alphanumeric characters with _
			$objName = FabrikString::dbFieldName($element->name);

			if ($element->get('primary_key') ||
$element->get('plugin') === 'internalid')
			{
				$keys[] = $objName;
			}
			// Any elements that are names the same (eg radio buttons) can not be
entered twice into the database
			if (!in_array($objName, $arAddedObj))
			{
				$arAddedObj[] = $objName;
				$objType      = $elementModel->getFieldDescription();

				if ($objName != '' && !is_null($objType))
				{
					if (StringHelper::stristr($objType, 'not null'))
					{
						$lines[] = $fabrikDb->qn($objName) . ' ' . $objType;
					}
					else
					{
						$lines[] = $fabrikDb->qn($objName) . ' ' . $objType .
' null';
					}
				}
			}
		}

		$sql .= implode(', ', $lines);

		if (!empty($keys))
		{
			$sql .= ', PRIMARY KEY (' . implode(',',
array_map(function($value) use ($db) {return $db->qn($value);}, $keys))
. '))';
		}
		else
		{
			$sql .= ')';
		}

		foreach ($opts as $k => $v)
		{
			if ($v != '')
			{
				$sql .= ' ' . $k . ' ' . $v;
			}
		}

		$sql .= ' ENGINE = MYISAM ';
		$fabrikDb->setQuery($sql);
		$fabrikDb->execute();

		return $keys;
	}

	/**
	 * Create an element
	 *
	 * @param   string $name Element name
	 * @param   array  $data Properties
	 *
	 * @return mixed false if failed, otherwise element plugin
	 */
	public function makeElement($name, $data)
	{
		$pluginManager = FabrikWorker::getPluginManager();
		$element       = $pluginManager->loadPlugIn($data['plugin'],
'element');
		$item          = $element->getDefaultProperties($data);
		$item->id      = null;
		$item->name    = $name;
		$item->label   = str_replace('_', ' ', $name);
		$item->bind($data);
		$item->store();
		return $element;
	}

	/**
	 * Return the default set of attributes when creating a new
	 * fabrik list
	 *
	 * @return string json encoded Params
	 */
	public function getDefaultParams()
	{
		$a                      = array('advanced-filter' => 0,
'show-table-nav' => 1, 'show-table-filters' => 1,
'show-table-add' => 1, 'require-filter' => 0);
		$o                      = (object) $a;
		$o->admin_template      = 'admin';
		$o->detaillink          = 0;
		$o->empty_data_msg      =
Text::_('COM_FABRIK_LIST_NO_DATA_MSG');
		$o->pdf                 = '';
		$o->rss                 = 0;
		$o->feed_title          = '';
		$o->feed_date           = '';
		$o->rsslimit            = 150;
		$o->rsslimitmax         = 2500;
		$o->csv_import_frontend = 3;
		$o->csv_export_frontend = 3;
		$o->csvfullname         = 0;
		$o->access              = 1;
		$o->allow_view_details  = 1;
		$o->allow_edit_details  = 1;
		$o->allow_add           = 1;
		$o->allow_delete        = 1;
		$o->group_by_order      = '';
		$o->group_by_order_dir  = 'ASC';
		$o->prefilter_query     = '';

		return json_encode($o);
	}

	/**
	 * Alter the forms' data collection table when the forms' groups
and/or
	 * elements are altered
	 *
	 * @return void|JError
	 */
	public function ammendTable()
	{
		$db             = FabrikWorker::getDbo(true);
		$input          = $this->app->input;
		$query          = $db->getQuery(true);
		$table          = $this->table;
		$amend          = false;
		$tableName      = $table->db_table_name;
		$fabrikDb       = $this->getDb();
		$columns        = $fabrikDb->getTableColumns($tableName);
		$existingFields = array_keys($columns);
		$existingFields = array_map('strtolower', $existingFields);
		$lastField      = empty($existingFields) ? '' :
$existingFields[count($existingFields) - 1];
		$sql            = 'ALTER TABLE ' . $db->qn($tableName) .
' ';
		$sqlAdd         = array();

		// $$$ hugh - looks like this is now an array in jform
		$jForm    = $input->get('jform', array(),
'array');
		$arGroups = FArrayHelper::getValue($jForm, 'current_groups',
array(), 'array');

		if (empty($arGroups))
		{
			// Get a list of groups used by the form
			$query->select('group_id')->from('#__fabrik_formgroup')->where('form_id
= ' . (int) $this->getFormModel()->getId());
			$db->setQuery($query);
			$groups   = $db->loadObjectList();
			$arGroups = array();

			foreach ($groups as $g)
			{
				$arGroups[] = $g->group_id;
			}
		}

		$arAddedObj = array();

		foreach ($arGroups as $group_id)
		{
			$group = FabTable::getInstance('Group',
'FabrikTable');
			$group->load($group_id);

			if ($group->is_join == '0')
			{
				$query->clear();
				$query->select('*')->from('#__fabrik_elements')->where('group_id
= ' . (int) $group_id);
				$db->setQuery($query);
				$elements = $db->loadObjectList();

				foreach ($elements as $obj)
				{
					$objName = $obj->name;

					/*
					 * Do the check in lowercase (we already strtowlower()'ed
$existingFields up there ^^,
					 * because MySQL field names are case insensitive, so if the element
is called 'foo' and there
					 * is a column called 'Foo', and we try and create
'foo' on the table ... it'll blow up.
					 *
					 * However, leave the $objName unchanged, so if we do create a column
for it, it uses the case
					 * they specific in the element name - it's not up to us to force
their column naming to all lower,
					 * we just need to avoid clashes.
					 *
					 * @TODO We might consider detecting and raising a warning about case
inconsistencies?
					 */

					if (!in_array(strtolower($objName), $existingFields))
					{
						// Make sure that the object is not already in the table
						if (!in_array($objName, $arAddedObj))
						{
							// Any elements that are names the same (eg radio buttons) can not
be entered twice into the database
							$arAddedObj[]    = $objName;
							$pluginClassName = $obj->plugin;
							$plugin          =
$this->pluginManager->getPlugIn($pluginClassName,
'element');

							if (is_object($plugin))
							{
								$plugin->setId($obj->id);
								$objType = $plugin->getFieldDescription();
							}
							else
							{
								$objType = 'VARCHAR(255)';
							}

							if ($objName != '' && !is_null($objType))
							{
								$amend = true;
								$add   = 'ADD COLUMN ' . $db->qn($objName) . '
' . $objType . ' null';

								if ($lastField !== '')
								{
									$add .= ' AFTER ' . $db->qn($lastField);
								}

								$sqlAdd[] = $add;
							}
						}
					}
				}
			}
		}

		if ($amend)
		{
			$sql .= implode(', ', $sqlAdd);
			$fabrikDb->setQuery($sql);

			try
			{
				$fabrikDb->execute();
			} catch (Exception $e)
			{
//				JError::raiseWarning(500, 'amend table: ' .
$e->getMessage());
				\Joomla\CMS\Factory::getApplication()->enqueueMessage('amend
table: ' . $e->getMessage(), 'warning');
			}
		}
	}

	/**
	 * Gets the field names for the given table
	 * $$$ hugh - added this to backend, as I need it in some places where we
have
	 * a backend list model, and until now only existed in the FE model.
	 *
	 * @param   string $tbl Table name
	 * @param   string $key Field to key return array on
	 *
	 * @return  array  table fields
	 */
	public function getDBFields($tbl = null, $key = null)
	{
		if (is_null($tbl))
		{
			$table = $this->getTable();
			$tbl   = $table->db_table_name;
		}

		if ($tbl == '')
		{
			return array();
		}

		$sig = $tbl . $key;
		$tbl = FabrikString::safeColName($tbl);

		if (!isset($this->dbFields[$sig]))
		{
			$db  = $this->getDb();
			$tbl = FabrikString::safeColName($tbl);
			$db->setQuery("DESCRIBE " . $tbl);
			$this->dbFields[$sig] = $db->loadObjectList($key);

			/**
			 * $$$ hugh - added BaseType, which strips (X) from things like INT(6)
OR varchar(32)
			 * Also converts it to UPPER, just to make things a little easier.
			 */
			foreach ($this->dbFields[$sig] as &$row)
			{
				/**
				 * Boil the type down to just the base type, so "INT(11)
UNSIGNED" becomes just "INT"
				 * I'm sure there's other cases than just UNSIGNED I need to
deal with, but for now that's
				 * what I most care about, as this stuff is being written handle being
more specific about
				 * the elements the list PK can be selected from.
				 */
				$row->BaseType = strtoupper(preg_replace('#(\(\d+\))$#',
'', $row->Type));
				$row->BaseType = preg_replace('#(\s+SIGNED|\s+UNSIGNED)#',
'', $row->BaseType);

				/**
				 * Grab the size part ...
				 */
				$matches = array();
				if (preg_match('#\((\d+)\)$#', $row->Type, $matches))
				{
					$row->BaseLength = $matches[1];
				}
				else
				{
					$row->BaseLength = '';
				}
			}
		}

		return $this->dbFields[$sig];
	}
}