primo commit

This commit is contained in:
2024-12-17 17:34:10 +01:00
commit e650f8df99
16435 changed files with 2451012 additions and 0 deletions

View File

@ -0,0 +1,373 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_workflow
*
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
* @since 4.0.0
*/
namespace Joomla\Component\Workflow\Administrator\Model;
use Joomla\CMS\Factory;
use Joomla\CMS\Form\Form;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Log\Log;
use Joomla\CMS\MVC\Model\AdminModel;
use Joomla\String\StringHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Model class for stage
*
* @since 4.0.0
*/
class StageModel extends AdminModel
{
/**
* Auto-populate the model state.
*
* Note. Calling getState in this method will result in recursion.
*
* @return void
*
* @since 4.0.0
*/
public function populateState()
{
parent::populateState();
$app = Factory::getApplication();
$context = $this->option . '.' . $this->name;
$extension = $app->getUserStateFromRequest($context . '.filter.extension', 'extension', null, 'cmd');
$this->setState('filter.extension', $extension);
}
/**
* Method to change the title
*
* @param integer $categoryId The id of the category.
* @param string $alias The alias.
* @param string $title The title.
*
* @return array Contains the modified title and alias.
*
* @since 4.0.0
*/
protected function generateNewTitle($categoryId, $alias, $title)
{
// Alter the title & alias
$table = $this->getTable();
while ($table->load(['title' => $title])) {
$title = StringHelper::increment($title);
}
return [$title, $alias];
}
/**
* Method to save the form data.
*
* @param array $data The form data.
*
* @return boolean True on success.
*
* @since 4.0.0
*/
public function save($data)
{
$table = $this->getTable();
$context = $this->option . '.' . $this->name;
$app = Factory::getApplication();
$user = $app->getIdentity();
$input = $app->getInput();
$workflowID = $app->getUserStateFromRequest($context . '.filter.workflow_id', 'workflow_id', 0, 'int');
if (empty($data['workflow_id'])) {
$data['workflow_id'] = $workflowID;
}
$workflow = $this->getTable('Workflow');
$workflow->load($data['workflow_id']);
$parts = explode('.', $workflow->extension);
if (isset($data['rules']) && !$user->authorise('core.admin', $parts[0])) {
unset($data['rules']);
}
// Make sure we use the correct extension when editing an existing workflow
$key = $table->getKeyName();
$pk = (isset($data[$key])) ? $data[$key] : (int) $this->getState($this->getName() . '.id');
if ($pk > 0) {
$table->load($pk);
if ((int) $table->workflow_id) {
$data['workflow_id'] = (int) $table->workflow_id;
}
}
if ($input->get('task') == 'save2copy') {
$origTable = clone $this->getTable();
// Alter the title for save as copy
if ($origTable->load(['title' => $data['title']])) {
list($title) = $this->generateNewTitle(0, '', $data['title']);
$data['title'] = $title;
}
$data['published'] = 0;
$data['default'] = 0;
}
return parent::save($data);
}
/**
* Method to test whether a record can be deleted.
*
* @param object $record A record object.
*
* @return boolean True if allowed to delete the record. Defaults to the permission for the component.
*
* @since 4.0.0
*/
protected function canDelete($record)
{
$table = $this->getTable('Workflow', 'Administrator');
$table->load($record->workflow_id);
if (empty($record->id) || $record->published != -2) {
return false;
}
$app = Factory::getApplication();
$extension = $app->getUserStateFromRequest('com_workflow.stage.filter.extension', 'extension', null, 'cmd');
$parts = explode('.', $extension);
$component = reset($parts);
if (!$this->getCurrentUser()->authorise('core.delete', $component . '.state.' . (int) $record->id) || $record->default) {
$this->setError(Text::_('JLIB_APPLICATION_ERROR_DELETE_NOT_PERMITTED'));
return false;
}
return true;
}
/**
* Method to test whether a record can have its state changed.
*
* @param object $record A record object.
*
* @return boolean True if allowed to change the state of the record. Defaults to the permission set in the component.
*
* @since 4.0.0
*/
protected function canEditState($record)
{
$user = $this->getCurrentUser();
$app = Factory::getApplication();
$context = $this->option . '.' . $this->name;
$extension = $app->getUserStateFromRequest($context . '.filter.extension', 'extension', null, 'cmd');
if (!property_exists($record, 'workflow_id')) {
$workflowID = $app->getUserStateFromRequest($context . '.filter.workflow_id', 'workflow_id', 0, 'int');
$record->workflow_id = $workflowID;
}
// Check for existing workflow.
if (!empty($record->id)) {
return $user->authorise('core.edit.state', $extension . '.state.' . (int) $record->id);
}
// Default to component settings if workflow isn't known.
return $user->authorise('core.edit.state', $extension);
}
/**
* Abstract method for getting the form from the model.
*
* @param array $data Data for the form.
* @param boolean $loadData True if the form is to load its own data (default case), false if not.
*
* @return Form|boolean A Form object on success, false on failure
*
* @since 4.0.0
*/
public function getForm($data = [], $loadData = true)
{
// Get the form.
$form = $this->loadForm(
'com_workflow.state',
'stage',
[
'control' => 'jform',
'load_data' => $loadData,
]
);
if (empty($form)) {
return false;
}
$id = $data['id'] ?? $form->getValue('id');
$item = $this->getItem($id);
$canEditState = $this->canEditState((object) $item);
// Modify the form based on access controls.
if (!$canEditState || !empty($item->default)) {
if (!$canEditState) {
$form->setFieldAttribute('published', 'disabled', 'true');
$form->setFieldAttribute('published', 'required', 'false');
$form->setFieldAttribute('published', 'filter', 'unset');
}
$form->setFieldAttribute('default', 'disabled', 'true');
$form->setFieldAttribute('default', 'required', 'false');
$form->setFieldAttribute('default', 'filter', 'unset');
}
return $form;
}
/**
* Method to get the data that should be injected in the form.
*
* @return mixed The data for the form.
*
* @since 4.0.0
*/
protected function loadFormData()
{
// Check the session for previously entered form data.
$data = Factory::getApplication()->getUserState(
'com_workflow.edit.state.data',
[]
);
if (empty($data)) {
$data = $this->getItem();
}
return $data;
}
/**
* Method to change the home state of one or more items.
*
* @param array $pk A list of the primary keys to change.
* @param integer $value The value of the home state.
*
* @return boolean True on success.
*
* @since 4.0.0
*/
public function setDefault($pk, $value = 1)
{
$table = $this->getTable();
if ($table->load($pk)) {
if (!$table->published) {
$this->setError(Text::_('COM_WORKFLOW_ITEM_MUST_PUBLISHED'));
return false;
}
}
if (empty($table->id) || !$this->canEditState($table)) {
Log::add(Text::_('JLIB_APPLICATION_ERROR_EDITSTATE_NOT_PERMITTED'), Log::WARNING, 'jerror');
return false;
}
if ($value) {
// Verify that the home page for this language is unique per client id
if ($table->load(['default' => '1', 'workflow_id' => $table->workflow_id])) {
$table->default = 0;
$table->store();
}
}
if ($table->load($pk)) {
$table->default = $value;
$table->store();
}
// Clean the cache
$this->cleanCache();
return true;
}
/**
* Method to change the published state of one or more records.
*
* @param array &$pks A list of the primary keys to change.
* @param integer $value The value of the published state.
*
* @return boolean True on success.
*
* @since 4.0.0
*/
public function publish(&$pks, $value = 1)
{
$table = $this->getTable();
$pks = (array) $pks;
$app = Factory::getApplication();
$extension = $app->getUserStateFromRequest('com_workflow.state.filter.extension', 'extension', null, 'cmd');
// Default item existence checks.
if ($value != 1) {
foreach ($pks as $i => $pk) {
if ($table->load($pk) && $table->default) {
// Prune items that you can't change.
$app->enqueueMessage(Text::_('COM_WORKFLOW_MSG_DISABLE_DEFAULT'), 'error');
unset($pks[$i]);
}
}
}
return parent::publish($pks, $value);
}
/**
* Method to preprocess the form.
*
* @param Form $form A Form object.
* @param mixed $data The data expected for the form.
* @param string $group The name of the plugin group to import (defaults to "content").
*
* @return void
*
* @since 4.0.0
*/
protected function preprocessForm(Form $form, $data, $group = 'content')
{
$extension = Factory::getApplication()->getInput()->get('extension');
$parts = explode('.', $extension);
$extension = array_shift($parts);
// Set the access control rules field component value.
$form->setFieldAttribute('rules', 'component', $extension);
parent::preprocessForm($form, $data, $group);
}
}

View File

@ -0,0 +1,201 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_workflow
*
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
* @since 4.0.0
*/
namespace Joomla\Component\Workflow\Administrator\Model;
use Joomla\CMS\Factory;
use Joomla\CMS\MVC\Model\ListModel;
use Joomla\Database\ParameterType;
use Joomla\Database\QueryInterface;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Model class for stages
*
* @since 4.0.0
*/
class StagesModel extends ListModel
{
/**
* Constructor.
*
* @param array $config An optional associative array of configuration settings.
*
* @see JController
* @since 4.0.0
*/
public function __construct($config = [])
{
if (empty($config['filter_fields'])) {
$config['filter_fields'] = [
'id', 's.id',
'title', 's.title',
'ordering','s.ordering',
'published', 's.published',
];
}
parent::__construct($config);
}
/**
* Method to auto-populate the model state.
*
* This method should only be called once per instantiation and is designed
* to be called on the first call to the getState() method unless the model
* configuration flag to ignore the request is set.
*
* Note. Calling getState in this method will result in recursion.
*
* @param string $ordering An optional ordering field.
* @param string $direction An optional direction (asc|desc).
*
* @return void
*
* @since 4.0.0
*/
protected function populateState($ordering = 's.ordering', $direction = 'ASC')
{
$app = Factory::getApplication();
$workflowID = $app->getUserStateFromRequest($this->context . '.filter.workflow_id', 'workflow_id', 1, 'int');
$extension = $app->getUserStateFromRequest($this->context . '.filter.extension', 'extension', null, 'cmd');
if ($workflowID) {
$table = $this->getTable('Workflow', 'Administrator');
if ($table->load($workflowID)) {
$this->setState('active_workflow', $table->title);
}
}
$this->setState('filter.workflow_id', $workflowID);
$this->setState('filter.extension', $extension);
parent::populateState($ordering, $direction);
}
/**
* A protected method to get a set of ordering conditions.
*
* @param object $table A record object.
*
* @return array An array of conditions to add to ordering queries.
*
* @since 4.0.0
*/
protected function getReorderConditions($table)
{
return [
$this->getDatabase()->quoteName('workflow_id') . ' = ' . (int) $table->workflow_id,
];
}
/**
* Method to get a table object, load it if necessary.
*
* @param string $type The table name. Optional.
* @param string $prefix The class prefix. Optional.
* @param array $config Configuration array for model. Optional.
*
* @return \Joomla\CMS\Table\Table A Table object
*
* @since 4.0.0
*/
public function getTable($type = 'Stage', $prefix = 'Administrator', $config = [])
{
return parent::getTable($type, $prefix, $config);
}
/**
* Method to get the data that should be injected in the form.
*
* @return QueryInterface The query to database.
*
* @since 4.0.0
*/
public function getListQuery()
{
$db = $this->getDatabase();
$query = $db->getQuery(true);
$query
->select(
[
$db->quoteName('s.id'),
$db->quoteName('s.title'),
$db->quoteName('s.ordering'),
$db->quoteName('s.default'),
$db->quoteName('s.published'),
$db->quoteName('s.checked_out'),
$db->quoteName('s.checked_out_time'),
$db->quoteName('s.description'),
$db->quoteName('uc.name', 'editor'),
]
)
->from($db->quoteName('#__workflow_stages', 's'))
->join('LEFT', $db->quoteName('#__users', 'uc'), $db->quoteName('uc.id') . ' = ' . $db->quoteName('s.checked_out'));
// Filter by extension
if ($workflowID = (int) $this->getState('filter.workflow_id')) {
$query->where($db->quoteName('s.workflow_id') . ' = :id')
->bind(':id', $workflowID, ParameterType::INTEGER);
}
$status = (string) $this->getState('filter.published');
// Filter by publish state
if (is_numeric($status)) {
$status = (int) $status;
$query->where($db->quoteName('s.published') . ' = :status')
->bind(':status', $status, ParameterType::INTEGER);
} elseif ($status === '') {
$query->where($db->quoteName('s.published') . ' IN (0, 1)');
}
// Filter by search in title
$search = $this->getState('filter.search');
if (!empty($search)) {
$search = '%' . str_replace(' ', '%', trim($search)) . '%';
$query->where('(' . $db->quoteName('s.title') . ' LIKE :search1 OR ' . $db->quoteName('s.description') . ' LIKE :search2)')
->bind([':search1', ':search2'], $search);
}
// Add the list ordering clause.
$query->order($db->escape($this->getState('list.ordering', 's.ordering')) . ' ' . $db->escape($this->getState('list.direction', 'ASC')));
return $query;
}
/**
* Returns a workflow object
*
* @return object The workflow
*
* @since 4.0.0
*/
public function getWorkflow()
{
$table = $this->getTable('Workflow', 'Administrator');
$workflowId = (int) $this->getState('filter.workflow_id');
if ($workflowId > 0) {
$table->load($workflowId);
}
return (object) $table->getProperties();
}
}

View File

@ -0,0 +1,336 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_workflow
*
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
* @since 4.0.0
*/
namespace Joomla\Component\Workflow\Administrator\Model;
use Joomla\CMS\Factory;
use Joomla\CMS\Form\Form;
use Joomla\CMS\MVC\Model\AdminModel;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\Registry\Registry;
use Joomla\String\StringHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Model class for transition
*
* @since 4.0.0
*/
class TransitionModel extends AdminModel
{
/**
* Auto-populate the model state.
*
* Note. Calling getState in this method will result in recursion.
*
* @return void
*
* @since 4.0.0
*/
public function populateState()
{
parent::populateState();
$app = Factory::getApplication();
$context = $this->option . '.' . $this->name;
$extension = $app->getUserStateFromRequest($context . '.filter.extension', 'extension', null, 'cmd');
$this->setState('filter.extension', $extension);
}
/**
* Method to test whether a record can be deleted.
*
* @param object $record A record object.
*
* @return boolean True if allowed to delete the record. Defaults to the permission for the component.
*
* @since 4.0.0
*/
protected function canDelete($record)
{
if (empty($record->id) || $record->published != -2) {
return false;
}
$app = Factory::getApplication();
$extension = $app->getUserStateFromRequest('com_workflow.transition.filter.extension', 'extension', null, 'cmd');
return $this->getCurrentUser()->authorise('core.delete', $extension . '.transition.' . (int) $record->id);
}
/**
* Method to test whether a record can have its state changed.
*
* @param object $record A record object.
*
* @return boolean True if allowed to change the state of the record. Defaults to the permission set in the component.
*
* @since 4.0.0
*/
protected function canEditState($record)
{
$user = $this->getCurrentUser();
$app = Factory::getApplication();
$context = $this->option . '.' . $this->name;
$extension = $app->getUserStateFromRequest($context . '.filter.extension', 'extension', null, 'cmd');
if (!property_exists($record, 'workflow_id')) {
$workflowID = $app->getUserStateFromRequest($context . '.filter.workflow_id', 'workflow_id', 0, 'int');
$record->workflow_id = $workflowID;
}
// Check for existing workflow.
if (!empty($record->id)) {
return $user->authorise('core.edit.state', $extension . '.transition.' . (int) $record->id);
}
// Default to component settings if workflow isn't known.
return $user->authorise('core.edit.state', $extension);
}
/**
* Method to get a single record.
*
* @param integer $pk The id of the primary key.
*
* @return \Joomla\CMS\Object\CMSObject|boolean Object on success, false on failure.
*
* @since 4.0.0
*/
public function getItem($pk = null)
{
$item = parent::getItem($pk);
if (property_exists($item, 'options')) {
$registry = new Registry($item->options);
$item->options = $registry->toArray();
}
return $item;
}
/**
* Method to save the form data.
*
* @param array $data The form data.
*
* @return boolean True on success.
*
* @since 4.0.0
*/
public function save($data)
{
$table = $this->getTable();
$context = $this->option . '.' . $this->name;
$app = Factory::getApplication();
$user = $app->getIdentity();
$input = $app->getInput();
$workflowID = $app->getUserStateFromRequest($context . '.filter.workflow_id', 'workflow_id', 0, 'int');
if (empty($data['workflow_id'])) {
$data['workflow_id'] = $workflowID;
}
$workflow = $this->getTable('Workflow');
$workflow->load($data['workflow_id']);
$parts = explode('.', $workflow->extension);
if (isset($data['rules']) && !$user->authorise('core.admin', $parts[0])) {
unset($data['rules']);
}
// Make sure we use the correct workflow_id when editing an existing transition
$key = $table->getKeyName();
$pk = (isset($data[$key])) ? $data[$key] : (int) $this->getState($this->getName() . '.id');
if ($pk > 0) {
$table->load($pk);
if ((int) $table->workflow_id) {
$data['workflow_id'] = (int) $table->workflow_id;
}
}
if ($input->get('task') == 'save2copy') {
$origTable = clone $this->getTable();
// Alter the title for save as copy
if ($origTable->load(['title' => $data['title']])) {
list($title) = $this->generateNewTitle(0, '', $data['title']);
$data['title'] = $title;
}
$data['published'] = 0;
}
return parent::save($data);
}
/**
* Method to change the title
*
* @param integer $categoryId The id of the category.
* @param string $alias The alias.
* @param string $title The title.
*
* @return array Contains the modified title and alias.
*
* @since 4.0.0
*/
protected function generateNewTitle($categoryId, $alias, $title)
{
// Alter the title & alias
$table = $this->getTable();
while ($table->load(['title' => $title])) {
$title = StringHelper::increment($title);
}
return [$title, $alias];
}
/**
* Abstract method for getting the form from the model.
*
* @param array $data Data for the form.
* @param boolean $loadData True if the form is to load its own data (default case), false if not.
*
* @return \Joomla\CMS\Form\Form|boolean A Form object on success, false on failure
*
* @since 4.0.0
*/
public function getForm($data = [], $loadData = true)
{
// Get the form.
$form = $this->loadForm(
'com_workflow.transition',
'transition',
[
'control' => 'jform',
'load_data' => $loadData,
]
);
if (empty($form)) {
return false;
}
$id = $data['id'] ?? $form->getValue('id');
$item = $this->getItem($id);
$canEditState = $this->canEditState((object) $item);
// Modify the form based on access controls.
if (!$canEditState) {
$form->setFieldAttribute('published', 'disabled', 'true');
$form->setFieldAttribute('published', 'required', 'false');
$form->setFieldAttribute('published', 'filter', 'unset');
}
if (!empty($item->workflow_id)) {
$data['workflow_id'] = (int) $item->workflow_id;
}
if (empty($data['workflow_id'])) {
$context = $this->option . '.' . $this->name;
$data['workflow_id'] = (int) Factory::getApplication()->getUserStateFromRequest(
$context . '.filter.workflow_id',
'workflow_id',
0,
'int'
);
}
$where = $this->getDatabase()->quoteName('workflow_id') . ' = ' . (int) $data['workflow_id'];
$where .= ' AND ' . $this->getDatabase()->quoteName('published') . ' = 1';
$form->setFieldAttribute('from_stage_id', 'sql_where', $where);
$form->setFieldAttribute('to_stage_id', 'sql_where', $where);
return $form;
}
/**
* Method to get the data that should be injected in the form.
*
* @return mixed The data for the form.
*
* @since 4.0.0
*/
protected function loadFormData()
{
// Check the session for previously entered form data.
$data = Factory::getApplication()->getUserState(
'com_workflow.edit.transition.data',
[]
);
if (empty($data)) {
$data = $this->getItem();
}
return $data;
}
public function getWorkflow()
{
$app = Factory::getApplication();
$context = $this->option . '.' . $this->name;
$workflow_id = (int) $app->getUserStateFromRequest($context . '.filter.workflow_id', 'workflow_id', 0, 'int');
$workflow = $this->getTable('Workflow');
$workflow->load($workflow_id);
return (object) $workflow->getProperties();
}
/**
* Trigger the form preparation for the workflow group
*
* @param Form $form A Form object.
* @param mixed $data The data expected for the form.
* @param string $group The name of the plugin group to import (defaults to "content").
*
* @return void
*
* @see FormField
* @since 4.0.0
* @throws \Exception if there is an error in the form event.
*/
protected function preprocessForm(Form $form, $data, $group = 'content')
{
$extension = Factory::getApplication()->getInput()->get('extension');
$parts = explode('.', $extension);
$extension = array_shift($parts);
// Set the access control rules field component value.
$form->setFieldAttribute('rules', 'component', $extension);
// Import the appropriate plugin group.
PluginHelper::importPlugin('workflow');
parent::preprocessForm($form, $data, $group);
}
}

View File

@ -0,0 +1,248 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_workflow
*
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
* @since 4.0.0
*/
namespace Joomla\Component\Workflow\Administrator\Model;
use Joomla\CMS\Factory;
use Joomla\CMS\MVC\Model\ListModel;
use Joomla\Database\ParameterType;
use Joomla\Database\QueryInterface;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Model class for transitions
*
* @since 4.0.0
*/
class TransitionsModel extends ListModel
{
/**
* Constructor.
*
* @param array $config An optional associative array of configuration settings.
*
* @see JController
* @since 4.0.0
*/
public function __construct($config = [])
{
if (empty($config['filter_fields'])) {
$config['filter_fields'] = [
'id', 't.id',
'published', 't.published',
'ordering', 't.ordering',
'title', 't.title',
'from_stage', 't.from_stage_id',
'to_stage', 't.to_stage_id',
];
}
parent::__construct($config);
}
/**
* Method to auto-populate the model state.
*
* This method should only be called once per instantiation and is designed
* to be called on the first call to the getState() method unless the model
* configuration flag to ignore the request is set.
*
* Note. Calling getState in this method will result in recursion.
*
* @param string $ordering An optional ordering field.
* @param string $direction An optional direction (asc|desc).
*
* @return void
*
* @since 4.0.0
*/
protected function populateState($ordering = 't.ordering', $direction = 'ASC')
{
$app = Factory::getApplication();
$workflowID = $app->getUserStateFromRequest($this->context . '.filter.workflow_id', 'workflow_id', 1, 'int');
$extension = $app->getUserStateFromRequest($this->context . '.filter.extension', 'extension', null, 'cmd');
if ($workflowID) {
$table = $this->getTable('Workflow', 'Administrator');
if ($table->load($workflowID)) {
$this->setState('active_workflow', $table->title);
}
}
$this->setState('filter.workflow_id', $workflowID);
$this->setState('filter.extension', $extension);
parent::populateState($ordering, $direction);
}
/**
* Method to get a table object, load it if necessary.
*
* @param string $type The table name. Optional.
* @param string $prefix The class prefix. Optional.
* @param array $config Configuration array for model. Optional.
*
* @return \Joomla\CMS\Table\Table A Table object
*
* @since 4.0.0
*/
public function getTable($type = 'Transition', $prefix = 'Administrator', $config = [])
{
return parent::getTable($type, $prefix, $config);
}
/**
* A protected method to get a set of ordering conditions.
*
* @param object $table A record object.
*
* @return array An array of conditions to add to ordering queries.
*
* @since 4.0.0
*/
protected function getReorderConditions($table)
{
return [
$this->getDatabase()->quoteName('workflow_id') . ' = ' . (int) $table->workflow_id,
];
}
/**
* Method to get the data that should be injected in the form.
*
* @return QueryInterface The query to database.
*
* @since 4.0.0
*/
public function getListQuery()
{
$db = $this->getDatabase();
$query = $db->getQuery(true);
$query
->select(
[
$db->quoteName('t.id'),
$db->quoteName('t.title'),
$db->quoteName('t.from_stage_id'),
$db->quoteName('t.to_stage_id'),
$db->quoteName('t.published'),
$db->quoteName('t.checked_out'),
$db->quoteName('t.checked_out_time'),
$db->quoteName('t.ordering'),
$db->quoteName('t.description'),
$db->quoteName('f_stage.title', 'from_stage'),
$db->quoteName('t_stage.title', 'to_stage'),
$db->quoteName('uc.name', 'editor'),
]
)
->from($db->quoteName('#__workflow_transitions', 't'))
->join('LEFT', $db->quoteName('#__workflow_stages', 'f_stage'), $db->quoteName('f_stage.id') . ' = ' . $db->quoteName('t.from_stage_id'))
->join('LEFT', $db->quoteName('#__workflow_stages', 't_stage'), $db->quoteName('t_stage.id') . ' = ' . $db->quoteName('t.to_stage_id'))
->join('LEFT', $db->quoteName('#__users', 'uc'), $db->quoteName('uc.id') . ' = ' . $db->quoteName('t.checked_out'));
// Filter by extension
if ($workflowID = (int) $this->getState('filter.workflow_id')) {
$query->where($db->quoteName('t.workflow_id') . ' = :id')
->bind(':id', $workflowID, ParameterType::INTEGER);
}
$status = (string) $this->getState('filter.published');
// Filter by status
if (is_numeric($status)) {
$status = (int) $status;
$query->where($db->quoteName('t.published') . ' = :status')
->bind(':status', $status, ParameterType::INTEGER);
} elseif ($status === '') {
$query->where($db->quoteName('t.published') . ' IN (0, 1)');
}
// Filter by column from_stage_id
if ($fromStage = (int) $this->getState('filter.from_stage')) {
$query->where($db->quoteName('from_stage_id') . ' = :fromStage')
->bind(':fromStage', $fromStage, ParameterType::INTEGER);
}
// Filter by column to_stage_id
if ($toStage = (int) $this->getState('filter.to_stage')) {
$query->where($db->quoteName('to_stage_id') . ' = :toStage')
->bind(':toStage', $toStage, ParameterType::INTEGER);
}
// Filter by search in title
$search = $this->getState('filter.search');
if (!empty($search)) {
$search = '%' . str_replace(' ', '%', trim($search)) . '%';
$query->where('(' . $db->quoteName('t.title') . ' LIKE :search1 OR ' . $db->quoteName('t.description') . ' LIKE :search2)')
->bind([':search1', ':search2'], $search);
}
// Add the list ordering clause.
$orderCol = $this->state->get('list.ordering', 't.id');
$orderDirn = strtoupper($this->state->get('list.direction', 'ASC'));
$query->order($db->escape($orderCol) . ' ' . ($orderDirn === 'DESC' ? 'DESC' : 'ASC'));
return $query;
}
/**
* Get the filter form
*
* @param array $data data
* @param boolean $loadData load current data
*
* @return \Joomla\CMS\Form\Form|boolean The Form object or false on error
*
* @since 4.0.0
*/
public function getFilterForm($data = [], $loadData = true)
{
$form = parent::getFilterForm($data, $loadData);
$id = (int) $this->getState('filter.workflow_id');
if ($form) {
$where = $this->getDatabase()->quoteName('workflow_id') . ' = ' . $id . ' AND ' . $this->getDatabase()->quoteName('published') . ' = 1';
$form->setFieldAttribute('from_stage', 'sql_where', $where, 'filter');
$form->setFieldAttribute('to_stage', 'sql_where', $where, 'filter');
}
return $form;
}
/**
* Returns a workflow object
*
* @return object The workflow
*
* @since 4.0.0
*/
public function getWorkflow()
{
$table = $this->getTable('Workflow', 'Administrator');
$workflowId = (int) $this->getState('filter.workflow_id');
if ($workflowId > 0) {
$table->load($workflowId);
}
return (object) $table->getProperties();
}
}

View File

@ -0,0 +1,398 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_workflow
*
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
* @since 4.0.0
*/
namespace Joomla\Component\Workflow\Administrator\Model;
use Joomla\CMS\Factory;
use Joomla\CMS\Form\Form;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Log\Log;
use Joomla\CMS\MVC\Model\AdminModel;
use Joomla\String\StringHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Model class for workflow
*
* @since 4.0.0
*/
class WorkflowModel extends AdminModel
{
/**
* Auto-populate the model state.
*
* Note. Calling getState in this method will result in recursion.
*
* @return void
*
* @since 4.0.0
*/
public function populateState()
{
parent::populateState();
$app = Factory::getApplication();
$context = $this->option . '.' . $this->name;
$extension = $app->getUserStateFromRequest($context . '.filter.extension', 'extension', null, 'cmd');
$this->setState('filter.extension', $extension);
}
/**
* Method to change the title
*
* @param integer $categoryId The id of the category.
* @param string $alias The alias.
* @param string $title The title.
*
* @return array Contains the modified title and alias.
*
* @since 4.0.0
*/
protected function generateNewTitle($categoryId, $alias, $title)
{
// Alter the title & alias
$table = $this->getTable();
while ($table->load(['title' => $title])) {
$title = StringHelper::increment($title);
}
return [$title, $alias];
}
/**
* Method to save the form data.
*
* @param array $data The form data.
*
* @return boolean True on success.
*
* @since 4.0.0
*/
public function save($data)
{
$table = $this->getTable();
$app = Factory::getApplication();
$user = $app->getIdentity();
$input = $app->getInput();
$context = $this->option . '.' . $this->name;
$extension = $app->getUserStateFromRequest($context . '.filter.extension', 'extension', null, 'cmd');
$data['extension'] = !empty($data['extension']) ? $data['extension'] : $extension;
// Make sure we use the correct extension when editing an existing workflow
$key = $table->getKeyName();
$pk = (isset($data[$key])) ? $data[$key] : (int) $this->getState($this->getName() . '.id');
if ($pk > 0) {
$table->load($pk);
$data['extension'] = $table->extension;
}
if (isset($data['rules']) && !$user->authorise('core.admin', $data['extension'])) {
unset($data['rules']);
}
if ($input->get('task') == 'save2copy') {
$origTable = clone $this->getTable();
// Alter the title for save as copy
if ($origTable->load(['title' => $data['title']])) {
list($title) = $this->generateNewTitle(0, '', $data['title']);
$data['title'] = $title;
}
// Unpublish new copy
$data['published'] = 0;
$data['default'] = 0;
}
$result = parent::save($data);
// Create default stage for new workflow
if ($result && $input->getCmd('task') !== 'save2copy' && $this->getState($this->getName() . '.new')) {
$workflow_id = (int) $this->getState($this->getName() . '.id');
$table = $this->getTable('Stage');
$table->id = 0;
$table->title = 'COM_WORKFLOW_BASIC_STAGE';
$table->description = '';
$table->workflow_id = $workflow_id;
$table->published = 1;
$table->default = 1;
$table->store();
}
return $result;
}
/**
* Abstract method for getting the form from the model.
*
* @param array $data Data for the form.
* @param boolean $loadData True if the form is to load its own data (default case), false if not.
*
* @return \Joomla\CMS\Form\Form|boolean A Form object on success, false on failure
*
* @since 4.0.0
*/
public function getForm($data = [], $loadData = true)
{
// Get the form.
$form = $this->loadForm(
'com_workflow.workflow',
'workflow',
[
'control' => 'jform',
'load_data' => $loadData,
]
);
if (empty($form)) {
return false;
}
$id = $data['id'] ?? $form->getValue('id');
$item = $this->getItem($id);
$canEditState = $this->canEditState((object) $item);
// Modify the form based on access controls.
if (!$canEditState || !empty($item->default)) {
if (!$canEditState) {
$form->setFieldAttribute('published', 'disabled', 'true');
$form->setFieldAttribute('published', 'required', 'false');
$form->setFieldAttribute('published', 'filter', 'unset');
}
$form->setFieldAttribute('default', 'disabled', 'true');
$form->setFieldAttribute('default', 'required', 'false');
$form->setFieldAttribute('default', 'filter', 'unset');
}
$form->setFieldAttribute('created', 'default', Factory::getDate()->format('Y-m-d H:i:s'));
$form->setFieldAttribute('modified', 'default', Factory::getDate()->format('Y-m-d H:i:s'));
return $form;
}
/**
* Method to get the data that should be injected in the form.
*
* @return mixed The data for the form.
*
* @since 4.0.0
*/
protected function loadFormData()
{
// Check the session for previously entered form data.
$data = Factory::getApplication()->getUserState(
'com_workflow.edit.workflow.data',
[]
);
if (empty($data)) {
$data = $this->getItem();
}
return $data;
}
/**
* Method to preprocess the form.
*
* @param Form $form Form object.
* @param mixed $data The data expected for the form.
* @param string $group The name of the plugin group to import (defaults to "content").
*
* @return void
*
* @since 4.0.0
*/
protected function preprocessForm(Form $form, $data, $group = 'content')
{
$extension = Factory::getApplication()->getInput()->get('extension');
$parts = explode('.', $extension);
$extension = array_shift($parts);
// Set the access control rules field component value.
$form->setFieldAttribute('rules', 'component', $extension);
parent::preprocessForm($form, $data, $group);
}
/**
* A protected method to get a set of ordering conditions.
*
* @param object $table A record object.
*
* @return array An array of conditions to add to ordering queries.
*
* @since 4.0.0
*/
protected function getReorderConditions($table)
{
$db = $this->getDatabase();
return [
$db->quoteName('extension') . ' = ' . $db->quote($table->extension),
];
}
/**
* Method to change the default state of one item.
*
* @param array $pk A list of the primary keys to change.
* @param integer $value The value of the home state.
*
* @return boolean True on success.
*
* @since 4.0.0
*/
public function setDefault($pk, $value = 1)
{
$table = $this->getTable();
if ($table->load($pk)) {
if ($table->published !== 1) {
$this->setError(Text::_('COM_WORKFLOW_ITEM_MUST_PUBLISHED'));
return false;
}
}
if (empty($table->id) || !$this->canEditState($table)) {
Log::add(Text::_('JLIB_APPLICATION_ERROR_EDITSTATE_NOT_PERMITTED'), Log::WARNING, 'jerror');
return false;
}
$date = Factory::getDate()->toSql();
if ($value) {
// Unset other default item
if (
$table->load(
[
'default' => '1',
'extension' => $table->extension,
]
)
) {
$table->default = 0;
$table->modified = $date;
$table->store();
}
}
if ($table->load($pk)) {
$table->modified = $date;
$table->default = $value;
$table->store();
}
// Clean the cache
$this->cleanCache();
return true;
}
/**
* Method to test whether a record can be deleted.
*
* @param object $record A record object.
*
* @return boolean True if allowed to delete the record. Defaults to the permission for the component.
*
* @since 4.0.0
*/
protected function canDelete($record)
{
if (empty($record->id) || $record->published != -2) {
return false;
}
return $this->getCurrentUser()->authorise('core.delete', $record->extension . '.workflow.' . (int) $record->id);
}
/**
* Method to test whether a record can have its state changed.
*
* @param object $record A record object.
*
* @return boolean True if allowed to change the state of the record. Defaults to the permission set in the component.
*
* @since 4.0.0
*/
protected function canEditState($record)
{
$user = $this->getCurrentUser();
// Check for existing workflow.
if (!empty($record->id)) {
return $user->authorise('core.edit.state', $record->extension . '.workflow.' . (int) $record->id);
}
// Default to component settings if workflow isn't known.
return $user->authorise('core.edit.state', $record->extension);
}
/**
* Method to change the published state of one or more records.
*
* @param array &$pks A list of the primary keys to change.
* @param integer $value The value of the published state.
*
* @return boolean True on success.
*
* @since 4.0.0
*/
public function publish(&$pks, $value = 1)
{
$table = $this->getTable();
$pks = (array) $pks;
$date = Factory::getDate()->toSql();
// Default workflow item check.
foreach ($pks as $i => $pk) {
if ($table->load($pk) && $value != 1 && $table->default) {
// Prune items that you can't change.
Factory::getApplication()->enqueueMessage(Text::_('COM_WORKFLOW_UNPUBLISH_DEFAULT_ERROR'), 'error');
unset($pks[$i]);
break;
}
}
// Clean the cache.
$this->cleanCache();
// Ensure that previous checks don't empty the array.
if (empty($pks)) {
return true;
}
$table->load($pk);
$table->modified = $date;
$table->store();
return parent::publish($pks, $value);
}
}

View File

@ -0,0 +1,274 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_workflow
*
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
* @since 4.0.0
*/
namespace Joomla\Component\Workflow\Administrator\Model;
use Joomla\CMS\Factory;
use Joomla\CMS\MVC\Model\ListModel;
use Joomla\Database\ParameterType;
use Joomla\Database\QueryInterface;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Model class for workflows
*
* @since 4.0.0
*/
class WorkflowsModel extends ListModel
{
/**
* Constructor.
*
* @param array $config An optional associative array of configuration settings.
*
* @see JController
* @since 4.0.0
*/
public function __construct($config = [])
{
if (empty($config['filter_fields'])) {
$config['filter_fields'] = [
'id', 'w.id',
'title', 'w.title',
'published', 'w.published',
'created_by', 'w.created_by',
'created', 'w.created',
'ordering', 'w.ordering',
'modified', 'w.modified',
'description', 'w.description',
];
}
parent::__construct($config);
}
/**
* Method to auto-populate the model state.
*
* This method should only be called once per instantiation and is designed
* to be called on the first call to the getState() method unless the model
* configuration flag to ignore the request is set.
*
* Note. Calling getState in this method will result in recursion.
*
* @param string $ordering An optional ordering field.
* @param string $direction An optional direction (asc|desc).
*
* @return void
*
* @since 4.0.0
*/
protected function populateState($ordering = 'w.ordering', $direction = 'asc')
{
$app = Factory::getApplication();
$extension = $app->getUserStateFromRequest($this->context . '.filter.extension', 'extension', null, 'cmd');
$this->setState('filter.extension', $extension);
$parts = explode('.', $extension);
// Extract the component name
$this->setState('filter.component', $parts[0]);
// Extract the optional section name
$this->setState('filter.section', (\count($parts) > 1) ? $parts[1] : null);
parent::populateState($ordering, $direction);
}
/**
* Method to get a table object, load it if necessary.
*
* @param string $type The table name. Optional.
* @param string $prefix The class prefix. Optional.
* @param array $config Configuration array for model. Optional.
*
* @return \Joomla\CMS\Table\Table A Table object
*
* @since 4.0.0
*/
public function getTable($type = 'Workflow', $prefix = 'Administrator', $config = [])
{
return parent::getTable($type, $prefix, $config);
}
/**
* Method to get an array of data items.
*
* @return mixed An array of data items on success, false on failure.
*
* @since 4.0.0
*/
public function getItems()
{
$items = parent::getItems();
if ($items) {
$this->countItems($items);
}
return $items;
}
/**
* Get the filter form
*
* @param array $data data
* @param boolean $loadData load current data
*
* @return \Joomla\CMS\Form\Form|bool the Form object or false
*
* @since 4.0.0
*/
public function getFilterForm($data = [], $loadData = true)
{
$form = parent::getFilterForm($data, $loadData);
if ($form) {
$form->setValue('extension', null, $this->getState('filter.extension'));
}
return $form;
}
/**
* Add the number of transitions and states to all workflow items
*
* @param array $items The workflow items
*
* @return void
*
* @since 4.0.0
*/
protected function countItems($items)
{
$db = $this->getDatabase();
$ids = [0];
foreach ($items as $item) {
$ids[] = (int) $item->id;
$item->count_states = 0;
$item->count_transitions = 0;
}
$query = $db->getQuery(true);
$query->select(
[
$db->quoteName('workflow_id'),
'COUNT(*) AS ' . $db->quoteName('count'),
]
)
->from($db->quoteName('#__workflow_stages'))
->whereIn($db->quoteName('workflow_id'), $ids)
->where($db->quoteName('published') . ' >= 0')
->group($db->quoteName('workflow_id'));
$status = $db->setQuery($query)->loadObjectList('workflow_id');
$query = $db->getQuery(true);
$query->select(
[
$db->quoteName('workflow_id'),
'COUNT(*) AS ' . $db->quoteName('count'),
]
)
->from($db->quoteName('#__workflow_transitions'))
->whereIn($db->quoteName('workflow_id'), $ids)
->where($db->quoteName('published') . ' >= 0')
->group($db->quoteName('workflow_id'));
$transitions = $db->setQuery($query)->loadObjectList('workflow_id');
foreach ($items as $item) {
if (isset($status[$item->id])) {
$item->count_states = (int) $status[$item->id]->count;
}
if (isset($transitions[$item->id])) {
$item->count_transitions = (int) $transitions[$item->id]->count;
}
}
}
/**
* Method to get the data that should be injected in the form.
*
* @return QueryInterface The query to database.
*
* @since 4.0.0
*/
public function getListQuery()
{
$db = $this->getDatabase();
$query = $db->getQuery(true);
$query->select(
[
$db->quoteName('w.id'),
$db->quoteName('w.title'),
$db->quoteName('w.created'),
$db->quoteName('w.modified'),
$db->quoteName('w.published'),
$db->quoteName('w.checked_out'),
$db->quoteName('w.checked_out_time'),
$db->quoteName('w.ordering'),
$db->quoteName('w.default'),
$db->quoteName('w.created_by'),
$db->quoteName('w.description'),
$db->quoteName('u.name'),
$db->quoteName('uc.name', 'editor'),
]
)
->from($db->quoteName('#__workflows', 'w'))
->join('LEFT', $db->quoteName('#__users', 'u'), $db->quoteName('u.id') . ' = ' . $db->quoteName('w.created_by'))
->join('LEFT', $db->quoteName('#__users', 'uc'), $db->quoteName('uc.id') . ' = ' . $db->quoteName('w.checked_out'));
// Filter by extension
if ($extension = $this->getState('filter.extension')) {
$query->where($db->quoteName('extension') . ' = :extension')
->bind(':extension', $extension);
}
$status = (string) $this->getState('filter.published');
// Filter by status
if (is_numeric($status)) {
$status = (int) $status;
$query->where($db->quoteName('w.published') . ' = :published')
->bind(':published', $status, ParameterType::INTEGER);
} elseif ($status === '') {
$query->where($db->quoteName('w.published') . ' IN (0, 1)');
}
// Filter by search in title
$search = $this->getState('filter.search');
if (!empty($search)) {
$search = '%' . str_replace(' ', '%', trim($search)) . '%';
$query->where('(' . $db->quoteName('w.title') . ' LIKE :search1 OR ' . $db->quoteName('w.description') . ' LIKE :search2)')
->bind([':search1', ':search2'], $search);
}
// Add the list ordering clause.
$orderCol = $this->state->get('list.ordering', 'w.ordering');
$orderDirn = strtoupper($this->state->get('list.direction', 'ASC'));
$query->order($db->escape($orderCol) . ' ' . ($orderDirn === 'DESC' ? 'DESC' : 'ASC'));
return $query;
}
}