Spade
Mini Shell
<?php
/**
* @package OSL
* @subpackage Controller
*
* @copyright Copyright (C) 2016 Ossolution Team, Inc. All rights
reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace OSL\Model;
use Exception;
use Joomla\Registry\Registry, Joomla\String\StringHelper, JFactory,
JPluginHelper, JLanguageMultilang, JTable, JApplicationHelper;
use OSL\Container\Container,
OSL\Input\Input;
/**
* Admin model class. It will handle tasks such as add, update, delete,
publish, unpublish...items
*
* @package OSF
* @subpackage Model
* @since 1.0
*/
class AdminModel extends Model
{
/**
* Context, used to get user session data and also trigger plugin
*
* @var string
*/
protected $context;
/**
* The prefix to use with controller messages.
*
* @var string
*/
protected $languagePrefix = null;
/**
* This model trigger events or not. By default, set it to No to improve
performance
*
* @var boolean
*/
protected $triggerEvents = false;
/**
* The event to trigger after deleting the data.
*
* @var string
*/
protected $eventAfterDelete = null;
/**
* The event to trigger after saving the data.
*
* @var string
*/
protected $eventAfterSave = null;
/**
* The event to trigger before deleting the data.
*
* @var string
*/
protected $eventBeforeDelete = null;
/**
* The event to trigger before saving the data.
*
* @var string
*/
protected $eventBeforeSave = null;
/**
* The event to trigger after changing the published state of the data.
*
* @var string
*/
protected $eventChangeState = null;
/**
* Name of plugin group which will be loaded to process the triggered
event.
* Default is component name
*
* @var string
*/
protected $pluginGroup = null;
/**
* Data for the item
*
* @var object
*/
protected $data = null;
/**
* Constructor.
*
* @param mixed $config An object store model configuration
*
* @see OSFModel
*/
public function __construct(Container $container, $config = [])
{
parent::__construct($container, $config);
/**@var Registry $config * */
$this->context = $this->container->option . '.' .
$this->name;
//Insert the default model states for admin
$this->state->insert('id', 'int',
0)->insert('cid', 'array', []);
if ($this->triggerEvents)
{
$name = ucfirst($this->name);
if (isset($config['plugin_group']))
{
$this->pluginGroup = $config['plugin_group'];
}
elseif (empty($this->pluginGroup))
{
//Plugin group should default to component name
$this->pluginGroup = substr($this->container->option, 4);
}
//Initialize the events
if (isset($config['event_after_delete']))
{
$this->eventAfterDelete = $config['event_after_delete'];
}
elseif (empty($this->eventAfterDelete))
{
$this->eventAfterDelete = 'on' . $name .
'AfterDelete';
}
if (isset($config['event_after_save']))
{
$this->eventAfterSave = $config['event_after_save'];
}
elseif (empty($this->eventAfterSave))
{
$this->eventAfterSave = 'on' . $name .
'AfterSave';
}
if (isset($config['event_before_delete']))
{
$this->eventBeforeDelete =
$config['event_before_delete'];
}
elseif (empty($this->eventBeforeDelete))
{
$this->eventBeforeDelete = 'on' . $name .
'BeforeDelete';
}
if (isset($config['event_before_save']))
{
$this->eventBeforeSave = $config['event_before_save'];
}
elseif (empty($this->eventBeforeSave))
{
$this->eventBeforeSave = 'on' . $name .
'BeforeSave';
}
if (isset($config['event_change_state']))
{
$this->eventChangeState = $config['event_change_state'];
}
elseif (empty($this->eventChangeState))
{
$this->eventChangeState = 'on' . $name .
'ChangeState';
}
}
if (empty($this->languagePrefix))
{
$this->languagePrefix = $this->container->languagePrefix;
}
}
/**
* Method to get the record data
*
* @return object
*/
public function getData()
{
if (empty($this->data))
{
if (count($this->state->cid))
{
$this->state->id = (int) $this->state->cid[0];
}
if ($this->state->id)
{
$this->loadData();
}
else
{
$this->initData();
}
}
return $this->data;
}
/**
* Method to store a record
*
* @param Input $input
* @param array $ignore
*
* @return bool
* @throws Exception
*/
public function store($input, $ignore = [])
{
if ($this->triggerEvents)
{
JPluginHelper::importPlugin($this->pluginGroup);
}
$row = $this->getTable();
$isNew = true;
$id = $input->getInt('id', 0);
if ($id)
{
$isNew = false;
$row->load($id);
}
// Pre-process the input data
$this->beforeStore($row, $input, $isNew);
if ($this->container->user->authorise('core.admin',
'com_pmform'))
{
$data = $input->getData(\OSL\Input\Input::INPUT_ALLOWRAW);
}
else
{
$data = $input->getData();
}
$row->bind($data, $ignore);
$this->prepareTable($row, $input->get('task'));
$row->check();
if ($this->triggerEvents)
{
$this->container->app->triggerEvent($this->eventBeforeSave,
[$row, $data, $isNew]);
}
$row->store();
if ($this->triggerEvents)
{
$this->container->app->triggerEvent($this->eventAfterSave,
[$row, $data, $isNew]);
}
$input->set('id', $row->id);
// Post process after the record stored into database
$this->afterStore($row, $input, $isNew);
}
/**
* Method to delete one or more records.
*
* @param array $cid
*
* @throws Exception
*/
public function delete($cid = [])
{
if (count($cid))
{
if ($this->triggerEvents)
{
JPluginHelper::importPlugin($this->pluginGroup);
}
// Before delete
$this->beforeDelete($cid);
$row = $this->getTable();
foreach ($cid as $id)
{
if ($row->load($id))
{
if ($this->triggerEvents)
{
$result =
$this->container->app->triggerEvent($this->eventBeforeDelete,
[$this->context, $row]);
if (in_array(false, $result, true))
{
throw new Exception($row->getError());
}
}
if (!$row->delete())
{
throw new Exception($row->getError());
}
if ($this->triggerEvents)
{
$this->container->app->triggerEvent($this->eventAfterDelete,
[$this->context, $row]);
}
}
else
{
throw new Exception($row->getError());
}
}
// Post process after records has been deleted from main table
$this->afterDelete($cid);
$this->cleanCache();
}
}
/**
* 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.
*
* @throws Exception
*/
public function publish($pks, $value = 1)
{
$row = $this->getTable();
$pks = (array) $pks;
$this->beforePublish($pks, $value);
// Attempt to change the state of the records.
if (!$row->publish($pks, $value,
$this->container->user->get('id')))
{
throw new Exception($row->getError());
}
$this->afterPublish($pks, $value);
if ($this->triggerEvents)
{
// Trigger the eventChangeState event.
JPluginHelper::importPlugin($this->pluginGroup);
$this->container->app->triggerEvent($this->eventChangeState,
[$this->context, $pks, $value]);
}
// Clear the component's cache
$this->cleanCache();
}
/**
* Saves the manually set order of records.
*
* @param array $pks An array of primary key ids.
*
* @param array $order An array contain ordering value of item
corresponding with $pks array
*
* @return mixed
*
* @throws Exception
*/
public function saveorder($pks = null, $order = null)
{
$row = $this->getTable();
$conditions = [];
// Update ordering values
foreach ($pks as $i => $pk)
{
$row->load((int) $pk);
if ($row->ordering != $order[$i])
{
$row->ordering = $order[$i];
$row->store();
// Remember to reorder within position and client_id
$condition = $this->getReorderConditions($row);
$found = false;
foreach ($conditions as $cond)
{
if ($cond[1] == $condition)
{
$found = true;
break;
}
}
if (!$found)
{
$conditions[] = [$row->id, $condition];
}
}
}
// Execute reorder for each category.
foreach ($conditions as $cond)
{
$row->load($cond[0]);
$row->reorder($cond[1]);
}
// Clear the component's cache
$this->cleanCache();
return true;
}
/**
* Load the record from database
*
*/
protected function loadData()
{
$db = $this->getDbo();
$query = $db->getQuery(true);
$query->select('*')
->from($this->table)
->where('id = ' . (int) $this->state->id);
$db->setQuery($query);
$this->data = $db->loadObject();
}
/**
* Init the record dara object
*/
protected function initData()
{
$this->data = $this->getTable();
if (property_exists($this->data, 'published'))
{
$this->data->published = 1;
}
}
/**
* Method to change the title & alias, usually used on save2copy
method
*
* @param $row the object being saved
*
* @param string $alias The alias.
*
* @param string $title The title.
*
* @return array Contains the modified title and alias.
*/
protected function generateNewTitle($row, $alias, $title)
{
$db = $this->getDbo();
$query = $db->getQuery(true);
$query->select('COUNT(*)')->from($this->table);
$conditions = $this->getReorderConditions($row);
while (true)
{
$query->where('alias=' . $db->quote($alias));
if (count($conditions))
{
$query->where($conditions);
}
$db->setQuery($query);
$found = (int) $db->loadResult();
if ($found)
{
$title = StringHelper::increment($title);
$alias = StringHelper::increment($alias, 'dash');
$query->clear('where');
}
else
{
break;
}
}
return [$title, $alias];
}
/**
* A protected method to get a set of ordering conditions.
*
* @param JTable $row A JTable object.
*
* @return array An array of conditions to add to ordering queries.
*
*/
protected function getReorderConditions($row)
{
$conditions = [];
if (property_exists($row, 'catid'))
{
$conditions[] = 'catid = ' . (int) $row->catid;
}
return $conditions;
}
/**
* Prepare and sanitise the table data prior to saving.
*
* @param JTable $row A reference to a JTable object.
*
* @return void
*
*/
protected function prepareTable($row, $task)
{
$user = $this->container->user;
if (property_exists($row, 'title'))
{
$titleField = 'title';
}
elseif (property_exists($row, 'name'))
{
$titleField = 'name';
}
if (($task == 'save2copy') && $titleField)
{
if (property_exists($row, 'alias'))
{
//Need to generate new title and alias
list ($title, $alias) = $this->generateNewTitle($row,
$row->alias, $row->{$titleField});
$row->{$titleField} = $title;
$row->alias = $alias;
}
else
{
$row->{$titleField} =
StringHelper::increment($row->{$titleField});
}
}
if (property_exists($row, 'title'))
{
$row->title = htmlspecialchars_decode($row->title, ENT_QUOTES);
}
if (property_exists($row, 'name'))
{
$row->name = htmlspecialchars_decode($row->name, ENT_QUOTES);
}
if (property_exists($row, 'alias'))
{
if (empty($row->alias))
{
$row->alias = $row->{$titleField};
}
$row->alias = JApplicationHelper::stringURLSafe($row->alias);
// Handle alias for extra languages
if (JLanguageMultilang::isEnabled())
{
// Build alias alias for other languages
$languages = \OSL\Utils\Helper::getLanguages();
if (count($languages))
{
foreach ($languages as $language)
{
$sef = $language->sef;
if (!$row->{'alias_' . $sef})
{
$row->{'alias_' . $sef} =
JApplicationHelper::stringURLSafe($row->{$titleField . '_' .
$sef});
}
else
{
$row->{'alias_' . $sef} =
JApplicationHelper::stringURLSafe($row->{'alias_' . $sef});
}
}
}
}
}
if (empty($row->id))
{
// Set ordering to the last item if not set
if (property_exists($row, 'ordering') &&
empty($row->ordering))
{
$db = $this->getDbo();
$query = $db->getQuery(true)
->select('MAX(ordering)')
->from($db->quoteName($this->table));
$conditions = $this->getReorderConditions($row);
if (count($conditions))
{
$query->where($conditions);
}
$db->setQuery($query);
$max = $db->loadResult();
$row->ordering = $max + 1;
}
if (property_exists($row, 'created_date') &&
!$row->created_date)
{
$row->created_date = JFactory::getDate()->toSql();
}
if (property_exists($row, 'created_by') &&
!$row->created_by)
{
$row->created_by = $user->get('id');
}
}
if (property_exists($row, 'modified_date') &&
!$row->modified_date)
{
$row->modified_date = JFactory::getDate()->toSql();
}
if (property_exists($row, 'modified_by') &&
!$row->modified_by)
{
$row->modified_by = $user->get('id');
}
if (property_exists($row, 'params') &&
is_array($row->params))
{
$row->params = json_encode($row->params);
}
}
/**
* Give a chance for child class to pre-process the data
*
* @param $row
* @param $input
* @param $isNew bool
*/
protected function beforeStore($row, $input, $isNew)
{
}
/**
* Give a chance for child class to post-process the data
*
* @param $row
* @param $input
* @param $isNew bool
*/
protected function afterStore($row, $input, $isNew)
{
}
/**
* Give a chance for child class tp pre-process the delete. For example,
delete the relation records
*
* @param array $cid Ids of deleted record
*/
protected function beforeDelete($cid)
{
}
/**
* Give a chance for child class tp post-process the delete. For example,
delete the relation records
*
* @param array $cid Ids of deleted record
*/
protected function afterDelete($cid)
{
}
/**
* Give a chance for child class to pre-process the publish.
*
* @param array $cid
* @param int $state
*/
protected function beforePublish($cid, $state)
{
}
/**
* Give a chance for child class to post-process the publish.
*
* @param array $cid
* @param int $state
*/
protected function afterPublish($cid, $state)
{
}
}