From 11a029b90cd7f8a43e808995fcde99158ed1e64a Mon Sep 17 00:00:00 2001 From: Benjamin Trenkle Date: Sat, 18 Apr 2020 05:26:52 +0200 Subject: [PATCH] Remove workflow from com_content (#56) * Remove more conditions Load transition field if component uses WorkflowModelTrait * Revert publish method to Joomla! 3 version and add workflow plugin group * Fix spelling error * Execute transition if selected in the item Add before/after item save for workflow --- .../components/com_content/forms/article.xml | 17 +- .../com_content/src/Model/ArticleModel.php | 395 +----------------- .../src/View/Articles/HtmlView.php | 5 - administrator/language/en-GB/com_content.ini | 2 +- libraries/src/Form/Field/TransitionField.php | 10 - .../src/MVC/Controller/AdminController.php | 5 +- libraries/src/MVC/Model/AdminModel.php | 3 +- .../src/MVC/Model/WorkflowBehaviorTrait.php | 366 +++++++++++++++- .../src/MVC/Model/WorkflowModelInterface.php | 60 ++- libraries/src/Workflow/Workflow.php | 13 +- 10 files changed, 453 insertions(+), 423 deletions(-) diff --git a/administrator/components/com_content/forms/article.xml b/administrator/components/com_content/forms/article.xml index 91092af9a3007..f3bc14773de4a 100644 --- a/administrator/components/com_content/forms/article.xml +++ b/administrator/components/com_content/forms/article.xml @@ -67,11 +67,18 @@ /> + name="state" + type="list" + label="JSTATUS" + class="custom-select-color-state" + size="1" + default="1" + > + + + + + setUpWorkflow('com_content.article'); @@ -126,11 +125,9 @@ protected function cleanupPostBatchCopy(TableInterface $table, $newId, $oldId) } // Copy workflow association - $workflow = new Workflow(['extension' => 'com_content']); - - $assoc = $workflow->getAssociation((int) $oldId); + $assoc = $this->workflow->getAssociation((int) $oldId); - $workflow->createAssociation((int) $newId, (int) $assoc->stage_id); + $this->workflow->createAssociation((int) $newId, (int) $assoc->stage_id); // Register FieldsHelper \JLoader::register('FieldsHelper', JPATH_ADMINISTRATOR . '/components/com_fields/helpers/fields.php'); @@ -154,49 +151,6 @@ protected function cleanupPostBatchCopy(TableInterface $table, $newId, $oldId) Factory::getApplication()->triggerEvent('onContentAfterSave', array('com_content.article', &$this->table, false, $fieldsData)); } - /** - * Batch change workflow stage or current. - * - * @param integer $value The workflow stage ID. - * @param array $pks An array of row IDs. - * @param array $contexts An array of item contexts. - * - * @return mixed An array of new IDs on success, boolean false on failure. - * - * @since 4.0.0 - */ - protected function batchWorkflowStage(int $value, array $pks, array $contexts) - { - $user = Factory::getUser(); - - if (!$user->authorise('core.admin', 'com_content')) - { - $this->setError(Text::_('JLIB_APPLICATION_ERROR_BATCH_CANNOT_EXECUTE_TRANSITION')); - } - - // Get workflow stage information - $stage = new StageTable($this->_db); - - if (empty($value) || !$stage->load($value)) - { - Factory::getApplication()->enqueueMessage(Text::sprintf('JGLOBAL_BATCH_WORKFLOW_STAGE_ROW_NOT_FOUND'), 'error'); - - return false; - } - - if (empty($pks)) - { - Factory::getApplication()->enqueueMessage(Text::sprintf('JGLOBAL_BATCH_WORKFLOW_STAGE_ROW_NOT_FOUND'), 'error'); - - return false; - } - - $workflow = new Workflow(['extension' => 'com_content']); - - // Update content state value and workflow associations - return $workflow->updateAssociations($pks, $value); - } - /** * Batch move categories to a new category. * @@ -392,114 +346,9 @@ protected function prepareTable($table) */ public function publish(&$pks, $value = 1) { - $input = Factory::getApplication()->input; - - $user = Factory::getUser(); - $table = $this->getTable(); - $pks = (array) $pks; - $value = (int) $value; - - $itrans = $input->get('publish_transitions', [], 'array'); - - // Include the plugins for the change of state event. - PluginHelper::importPlugin($this->events_map['change_state']); - - $db = $this->getDbo(); - $query = $db->getQuery(true); - - $query->select( - [ - $db->quoteName('wt.id'), - $db->quoteName('wa.item_id'), - ] - ) - ->from( - [ - $db->quoteName('#__workflow_transitions', 'wt'), - $db->quoteName('#__workflow_stages', 'ws'), - $db->quoteName('#__workflow_stages', 'ws2'), - $db->quoteName('#__workflow_associations', 'wa'), - ] - ) - ->where( - [ - $db->quoteName('wt.to_stage_id') . ' = ' . $db->quoteName('ws.id'), - $db->quoteName('wa.stage_id') . ' = ' . $db->quoteName('ws2.id'), - $db->quoteName('wt.workflow_id') . ' = ' . $db->quoteName('ws.workflow_id'), - $db->quoteName('wt.workflow_id') . ' = ' . $db->quoteName('ws2.workflow_id'), - $db->quoteName('wt.to_stage_id') . ' != ' . $db->quoteName('wa.stage_id'), - $db->quoteName('wa.extension') . ' = ' . $db->quote('com_content'), - $db->quoteName('ws.condition') . ' = :condition', - ] - ) - ->extendWhere( - 'AND', - [ - $db->quoteName('wt.from_stage_id') . ' = -1', - $db->quoteName('wt.from_stage_id') . ' = ' . $db->quoteName('wa.stage_id'), - ], - 'OR' - ) - ->whereIn($db->quoteName('wa.item_id'), $pks) - ->bind(':condition', $value, ParameterType::INTEGER); - - $transitions = $db->setQuery($query)->loadObjectList(); - - $items = []; - - foreach ($transitions as $transition) - { - if ($user->authorise('core.execute.transition', 'com_content.transition.' . $transition->id)) - { - if (!isset($itrans[$transition->item_id]) || $itrans[$transition->item_id] == $transition->id) - { - $items[$transition->item_id] = (int) $transition->id; - } - } - } + $this->importWorkflowPlugins(); - // Access checks. - foreach ($pks as $i => $pk) - { - $table->reset(); - - if ($table->load($pk)) - { - if (!isset($items[$pk])) - { - // Prune items that you can't change. - unset($pks[$i]); - - Log::add(Text::_('JLIB_APPLICATION_ERROR_EDITSTATE_NOT_PERMITTED'), Log::WARNING, 'jerror'); - - return false; - } - - // If the table is checked out by another user, drop it and report to the user trying to change its state. - if ($table->hasField('checked_out') && $table->checked_out && ($table->checked_out != $user->id)) - { - Log::add(Text::_('JLIB_APPLICATION_ERROR_CHECKIN_USER_MISMATCH'), Log::WARNING, 'jerror'); - - // Prune items that you can't change. - unset($pks[$i]); - - return false; - } - } - } - - foreach ($pks as $i => $pk) - { - if (!$this->runTransition($pk, $items[$pk])) - { - return false; - } - } - - // Clear the component's cache - $this->cleanCache(); - - return true; + return parent::publish($pks, $value); } /** @@ -630,18 +479,6 @@ public function getForm($data = array(), $loadData = true) $form->setFieldAttribute('catid', 'filter', 'unset'); } } - - $table = $this->getTable(); - - if ($table->load(array('id' => $id))) - { - $workflow = new Workflow(['extension' => 'com_content']); - - // Transition field - $assoc = $workflow->getAssociation($table->id); - - $form->setFieldAttribute('transition', 'workflow_stage', (int) $assoc->stage_id); - } } else { @@ -677,10 +514,6 @@ public function getForm($data = array(), $loadData = true) $form->setFieldAttribute('catid', 'refresh-enabled', true); $form->setFieldAttribute('catid', 'refresh-cat-id', $assignedCatids); $form->setFieldAttribute('catid', 'refresh-section', 'article'); - - $workflow = $this->getWorkflowByCategory($assignedCatids); - - $form->setFieldAttribute('transition', 'workflow_stage', (int) $workflow->stage_id); } } @@ -842,6 +675,8 @@ public function save($data) $data['images'] = (string) $registry; } + $this->workflowBeforeSave(); + // Create new category, if needed. $createCategory = true; @@ -926,26 +761,6 @@ public function save($data) } } - $stageId = 0; - - // Set status depending on category - if (empty($data['id'])) - { - $workflow = $this->getWorkflowByCategory($data['catid']); - - if (empty($workflow->id)) - { - $this->setError(Text::_('COM_CONTENT_WORKFLOW_NOT_FOUND')); - - return false; - } - - $stageId = (int) $workflow->stage_id; - - // B/C state - $data['state'] = (int) $workflow->condition; - } - // Automatic handling of alias for empty fields if (in_array($input->get('task'), array('apply', 'save', 'save2new')) && (!isset($data['id']) || (int) $data['id'] == 0)) { @@ -977,8 +792,6 @@ public function save($data) } } - $workflow = new Workflow(['extension' => 'com_content']); - if (parent::save($data)) { if (isset($data['featured'])) @@ -991,47 +804,7 @@ public function save($data) ); } - // Let's check if we have workflow association (perhaps something went wrong before) - if (empty($stageId)) - { - $assoc = $workflow->getAssociation((int) $this->getState($this->getName() . '.id')); - - // If not, reset the state and let's create the associations - if (empty($assoc->item_id)) - { - $table = $this->getTable(); - - $table->load((int) $this->getState($this->getName() . '.id')); - - $workflow = $this->getWorkflowByCategory((int) $table->catid); - - if (empty($workflow->id)) - { - $this->setError(Text::_('COM_CONTENT_WORKFLOW_NOT_FOUND')); - - return false; - } - - $stageId = (int) $workflow->stage_id; - - // B/C state - $table->state = $workflow->condition; - - $table->store(); - } - } - - // If we have a new state, create the workflow association - if (!empty($stageId)) - { - $workflow->createAssociation((int) $this->getState($this->getName() . '.id'), (int) $stageId); - } - - // Run the transition and update the workflow association - if (!empty($data['transition'])) - { - $this->runTransition((int) $this->getState($this->getName() . '.id'), (int) $data['transition']); - } + $this->workflowAfterSave($data); return true; } @@ -1232,7 +1005,7 @@ protected function preprocessForm(Form $form, $data, $group = 'content') } } - $this->preprocessFormWorkflow($form, $data); + $this->workflowPreprocessForm($form, $data); parent::preprocessForm($form, $data, $group); } @@ -1305,9 +1078,7 @@ public function delete(&$pks) $db->setQuery($query); $db->execute(); - $workflow = new Workflow(['extension' => 'com_content']); - - $workflow->deleteAssociation($pks); + $this->workflow->deleteAssociation($pks); } return $return; @@ -1320,148 +1091,4 @@ public function delete(&$pks) * * @return integer|boolean If found, the workflow ID, otherwise false */ - protected function getWorkflowByCategory(int $catId) - { - $db = $this->getDbo(); - - // Search categories and parents (if requested) for a workflow - $category = new Category($db); - - $categories = array_reverse($category->getPath($catId)); - - $workflow_id = 0; - - foreach ($categories as $cat) - { - $cat->params = new Registry($cat->params); - - $workflow_id = $cat->params->get('workflow_id'); - - if ($workflow_id == 'inherit') - { - $workflow_id = 0; - - continue; - } - elseif ($workflow_id == 'use_default') - { - $workflow_id = 0; - - break; - } - elseif ($workflow_id > 0) - { - break; - } - } - - // Check if the workflow exists - if ($workflow_id = (int) $workflow_id) - { - $query = $db->getQuery(true); - - $query->select( - [ - $db->quoteName('w.id'), - $db->quoteName('ws.id', 'stage_id'), - ] - ) - ->from( - [ - $db->quoteName('#__workflow_stages', 'ws'), - $db->quoteName('#__workflows', 'w'), - ] - ) - ->where( - [ - $db->quoteName('ws.workflow_id') . ' = ' . $db->quoteName('w.id'), - $db->quoteName('ws.default') . ' = 1', - $db->quoteName('w.published') . ' = 1', - $db->quoteName('ws.published') . ' = 1', - $db->quoteName('w.id') . ' = :workflowId', - ] - ) - ->bind(':workflowId', $workflow_id, ParameterType::INTEGER); - - $workflow = $db->setQuery($query)->loadObject(); - - if (!empty($workflow->id)) - { - return $workflow; - } - } - - // Use default workflow - $query = $db->getQuery(true); - - $query->select( - [ - $db->quoteName('w.id'), - $db->quoteName('ws.id', 'stage_id'), - ] - ) - ->from( - [ - $db->quoteName('#__workflow_stages', 'ws'), - $db->quoteName('#__workflows', 'w'), - ] - ) - ->where( - [ - $db->quoteName('ws.default') . ' = 1', - $db->quoteName('ws.workflow_id') . ' = ' . $db->quoteName('w.id'), - $db->quoteName('w.published') . ' = 1', - $db->quoteName('ws.published') . ' = 1', - $db->quoteName('w.default') . ' = 1', - ] - ); - - $workflow = $db->setQuery($query)->loadObject(); - - // Last check if we have a workflow ID - if (!empty($workflow->id)) - { - return $workflow; - } - - return false; - } - - /** - * Runs transition for item. - * - * @param integer $pk Id of article - * @param integer $transition_id Id of transition - * - * @return boolean - * - * @since 4.0.0 - */ - public function runTransition(int $pk, int $transition_id): bool - { - $workflow = new Workflow(['extension' => 'com_content']); - - $runTransaction = $workflow->executeTransition([$pk], $transition_id); - - if (!$runTransaction) - { - $this->setError(Text::_('COM_CONTENT_ERROR_UPDATE_STAGE')); - - return false; - } - - // B/C state change trigger for UCM - /* @TODO Move to transition - * $context = $this->option . '.' . $this->name; - - // Include the plugins for the change of stage event. - PluginHelper::importPlugin($this->events_map['change_state']); - - // Trigger the change stage event. - //Factory::getApplication()->triggerEvent($this->event_change_state, [$context, [$pk]]); - * - */ - - return true; - } } diff --git a/administrator/components/com_content/src/View/Articles/HtmlView.php b/administrator/components/com_content/src/View/Articles/HtmlView.php index fdae82fb336fa..e61ae552e3270 100644 --- a/administrator/components/com_content/src/View/Articles/HtmlView.php +++ b/administrator/components/com_content/src/View/Articles/HtmlView.php @@ -116,9 +116,6 @@ public function display($tpl = null) $this->filterForm->setFieldAttribute('category_id', 'language', '*,' . $forcedLanguage, 'filter'); } } - - /* - @TODO Move to the plugin /* @TODO Move to the plugin @@ -153,8 +150,6 @@ public function display($tpl = null) } $this->document->addScriptOptions('articles.transitions', $transitions); - - */ */ diff --git a/administrator/language/en-GB/com_content.ini b/administrator/language/en-GB/com_content.ini index efba71e6a34c1..7489424f6a1e1 100644 --- a/administrator/language/en-GB/com_content.ini +++ b/administrator/language/en-GB/com_content.ini @@ -168,7 +168,7 @@ COM_CONTENT_SUBMENU_CATEGORIES="Categories" COM_CONTENT_SUBMENU_FEATURED="Featured Articles" COM_CONTENT_SUBMENU_WORKFLOWS="Workflows" COM_CONTENT_TIP_ASSOCIATION="Associated articles" -COM_CONTENT_TRANSITION="Status" +COM_CONTENT_TRANSITION="Transition" COM_CONTENT_TRASHED="Trashed" COM_CONTENT_UNFEATURED="Unfeatured Article" COM_CONTENT_UNPUBLISHED="Unpublished" diff --git a/libraries/src/Form/Field/TransitionField.php b/libraries/src/Form/Field/TransitionField.php index f478e54af9de2..72a7442d4031c 100644 --- a/libraries/src/Form/Field/TransitionField.php +++ b/libraries/src/Form/Field/TransitionField.php @@ -112,7 +112,6 @@ protected function getOptions() [ $db->quoteName('t.id', 'value'), $db->quoteName('t.title', 'text'), - $db->quoteName('s.condition'), ] ) ->from( @@ -155,15 +154,6 @@ function ($item) use ($user, $extension) $items = ArrayHelper::sortObjects($items, 'value', 1, true, true); Factory::getLanguage()->load('com_workflow', JPATH_ADMINISTRATOR); - - $workflow = new Workflow(['extension' => $this->extension]); - - foreach ($items as $item) - { - $conditionName = $workflow->getConditionName((int) $item->condition); - - $item->text .= ' [' . Text::_($conditionName) . ']'; - } } // Get workflow stage title diff --git a/libraries/src/MVC/Controller/AdminController.php b/libraries/src/MVC/Controller/AdminController.php index ffa9a3d57224e..a094b1a576ff6 100644 --- a/libraries/src/MVC/Controller/AdminController.php +++ b/libraries/src/MVC/Controller/AdminController.php @@ -86,9 +86,6 @@ public function __construct($config = array(), MVCFactoryInterface $factory = nu $this->registerTask('orderup', 'reorder'); $this->registerTask('orderdown', 'reorder'); - // Transition - $this->registerTask('runTransition', 'runTransition'); - // Guess the option as com_NameOfController. if (empty($this->option)) { @@ -447,7 +444,7 @@ public function runTransition() // Get the model $model = $this->getModel(); - $return = $model->runTransition($pk, $transitionId); + $return = $model->executeTransition($pk, $transitionId); $redirect = Route::_('index.php?option=' . $this->option . '&view=' . $this->view_list . $this->getRedirectToListAppend(), false); diff --git a/libraries/src/MVC/Model/AdminModel.php b/libraries/src/MVC/Model/AdminModel.php index 8434153fb1216..d07be3a254832 100644 --- a/libraries/src/MVC/Model/AdminModel.php +++ b/libraries/src/MVC/Model/AdminModel.php @@ -117,8 +117,7 @@ abstract class AdminModel extends FormModel protected $batch_commands = array( 'assetgroup_id' => 'batchAccess', 'language_id' => 'batchLanguage', - 'tag' => 'batchTag', - 'workflowstage_id' => 'batchWorkflowStage', + 'tag' => 'batchTag' ); /** diff --git a/libraries/src/MVC/Model/WorkflowBehaviorTrait.php b/libraries/src/MVC/Model/WorkflowBehaviorTrait.php index 98e7ab4b47b6d..c26c0d5ae6d73 100644 --- a/libraries/src/MVC/Model/WorkflowBehaviorTrait.php +++ b/libraries/src/MVC/Model/WorkflowBehaviorTrait.php @@ -10,18 +10,21 @@ \defined('JPATH_PLATFORM') or die; +use Joomla\CMS\Component\ComponentHelper; use Joomla\CMS\Factory; use Joomla\CMS\Form\Form; use Joomla\CMS\Plugin\PluginHelper; use Joomla\CMS\Workflow\Workflow; +use Joomla\CMS\Table\Category; +use Joomla\Registry\Registry; /** * Trait which supports state behavior * * @since 4.0.0 */ -trait WorkflowBehaviorTrait { - +trait WorkflowBehaviorTrait +{ /** * The for the component. * @@ -30,14 +33,57 @@ trait WorkflowBehaviorTrait { */ protected $extension = null; + protected $section = ''; + + protected $workflowEnabled = false; + + /** + * The workflow object + * + * @var Workflow + * @since 4.0.0 + */ + protected $workflow; + /** * Set Up the workflow * * @param string $extension The option and section separated by . */ - public function setUpWorkflow($extension) { + public function setUpWorkflow($extension) + { + $parts = explode('.', $extension); + + $this->extension = array_shift($parts); + + if (count($parts)) + { + $this->section = array_shift($parts); + } + + $this->workflow = new Workflow(['extension' => $this->extension]); + + $params = ComponentHelper::getParams($this->extension); + + $this->workflowEnabled = $params->get('workflows_enable', 1); - $this->extension = $extension; + $this->enableWorkflowBatch(); + } + + /** + * Add the workflow batch to the command list. Can be overwritten bei the child class + * + * @return void + * + * @since 4.0.0 + */ + protected function enableWorkflowBatch() + { + // Enable batch + if ($this->workflowEnabled && property_exists($this, 'batch_commands')) + { + $this->batch_commands['workflowstage_id'] = 'batchWorkflowStage'; + } } /** @@ -48,16 +94,318 @@ public function setUpWorkflow($extension) { * * @return void * - * @throws \Exception if there is an error in the form event. * @since 4.0.0 * @see FormField */ - public function preprocessFormWorkflow(Form $form, $data) { + public function workflowPreprocessForm(Form $form, $data) + { + $this->addTransitionField($form, $data); + + if (!$this->workflowEnabled) + { + return; + } + + // Import the workflow plugin group to allow form manipulation. + $this->importWorkflowPlugins(); + } + + /** + * Preparation of workflow data/plugins + * + * @return void + * + * @since 4.0.0 + */ + public function workflowBeforeSave() + { + if (!$this->workflowEnabled) + { + return; + } + + $this->importWorkflowPlugins(); + } + + /** + * Executing of relevant workflow methods + * + * @return void + * + * @since 4.0.0 + */ + public function workflowAfterSave($data) + { + // Regardless if workflow is active or not, we have to set the default stage + // So we can work with the workflow, when the user activates it later + $id = $this->getState($this->getName() . '.id'); + $isNew = $this->getState($this->getName() . '.new'); + + // We save the first stage + if ($isNew) + { + $form = $this->getForm(); + + $stage_id = $this->getStageForNewItem($form, $data); + + $this->workflow->createAssociation($id, $stage_id); + } + + if (!$this->workflowEnabled) + { + return; + } + + // Execute transition + if (!empty($data['transition'])) + { + $this->executeTransition($id, $data['transition']); + } + } + + /** + * Runs transition for item. + * + * @param integer $pk Id of article + * @param integer $transition_id Id of transition + * + * @return boolean + * + * @since 4.0.0 + */ + public function executeTransition(int $pk, int $transition_id): bool + { + $result = $this->workflow->executeTransition([$pk], $transition_id); + + if (!$result) + { + $this->setError(Text::_('COM_CONTENT_ERROR_UPDATE_STAGE')); + + return false; + } - // Import the appropriate plugin group. + return true; + } + + /** + * Import the Workflow plugins. + * + * @param Form $form A Form object. + * @param mixed $data The data expected for the form. + * + * @return void + */ + protected function importWorkflowPlugins() + { PluginHelper::importPlugin('workflow'); } + /** + * Adds a transition field to the form. Can be overwritten by the child class if not needed + * + * @param Form $form A Form object. + * @param mixed $data The data expected for the form. + * + * @return void + * @since 4.0.0 + */ + protected function addTransitionField(Form $form, $data) + { + $extension = $this->extension . ($this->section ? '.' . $this->section : ''); + + $field = new \SimpleXMLElement(''); + + $field->addAttribute('name', 'transition'); + $field->addAttribute('type', $this->workflowEnabled ? 'transition' : 'hidden'); + $field->addAttribute('label', 'COM_CONTENT_TRANSITION'); + $field->addAttribute('extension', $extension); + + $form->setField($field); + + $table = $this->getTable(); + + $key = $table->getKeyName(); + + $id = isset($data->$key) ? $data->$key : $form->getValue($key); + + if ($id) + { + // Transition field + $assoc = $this->workflow->getAssociation($id); + + $form->setFieldAttribute('transition', 'workflow_stage', (int) $assoc->stage_id); + } + else + { + $stage_id = $this->getStageForNewItem($form, $data); + + if (!empty($stage_id)) + { + $form->setFieldAttribute('transition', 'workflow_stage', (int) $stage_id); + } + } + } + + /** + * Try to load a workflow object for newly created items + * which does not have a workflow assinged yet. If the category is not the + * carrier, overwrite it on your model and deliver your own carrier. + * + * @param Form $form A Form object. + * @param mixed $data The data expected for the form. + * + * @return boolean|object A object containing workflow information or false + * @since 4.0.0 + */ + protected function getStageForNewItem(Form $form, $data) + { + $table = $this->getTable(); + + $hasKey = $table->hasField('catid'); + + if (!$hasKey) + { + return false; + } + + $catKey = $table->getColumnAlias('catid'); + + $field = $form->getField($catKey); + + if (!$field) + { + return false; + } + + $catId = isset(((object) $data)->$catKey) ? ((object) $data)->$catKey : $form->getValue($catKey); + + // Try to get the category from the html code of the field + if (empty($catId)) + { + $catId = $field->getAttribute('default', null); + + // Choose the first category available + $xml = new \DOMDocument; + libxml_use_internal_errors(true); + $xml->loadHTML($field->__get('input')); + libxml_clear_errors(); + libxml_use_internal_errors(false); + $options = $xml->getElementsByTagName('option'); + + if (!$catId && $firstChoice = $options->item(0)) + { + $catId = $firstChoice->getAttribute('value'); + } + } + + if (empty($catId)) + { + return false; + } + + $db = Factory::getContainer()->get('DatabaseDriver'); + + // Let's check if a workflow ID is assigned to a category + $category = new Category($db); + + $categories = array_reverse($category->getPath($catId)); + + $workflow_id = 0; + + foreach ($categories as $cat) + { + $cat->params = new Registry($cat->params); + + $workflow_id = $cat->params->get('workflow_id'); + + if ($workflow_id == 'inherit') + { + $workflow_id = 0; + + continue; + } + elseif ($workflow_id == 'use_default') + { + $workflow_id = 0; + + break; + } + elseif ($workflow_id > 0) + { + break; + } + } + + // Check if the workflow exists + if ($workflow_id = (int) $workflow_id) + { + $query = $db->getQuery(true); + + $query->select( + [ + $db->quoteName('ws.id') + ] + ) + ->from( + [ + $db->quoteName('#__workflow_stages', 'ws'), + $db->quoteName('#__workflows', 'w'), + ] + ) + ->where( + [ + $db->quoteName('ws.workflow_id') . ' = ' . $db->quoteName('w.id'), + $db->quoteName('ws.default') . ' = 1', + $db->quoteName('w.published') . ' = 1', + $db->quoteName('ws.published') . ' = 1', + $db->quoteName('w.id') . ' = :workflowId', + ] + ) + ->bind(':workflowId', $workflow_id, ParameterType::INTEGER); + + $stage_id = (int) $db->setQuery($query)->loadResult(); + + if (!empty($stage_id)) + { + return $stage_id; + } + } + + // Use default workflow + $query = $db->getQuery(true); + + $query->select( + [ + $db->quoteName('ws.id') + ] + ) + ->from( + [ + $db->quoteName('#__workflow_stages', 'ws'), + $db->quoteName('#__workflows', 'w'), + ] + ) + ->where( + [ + $db->quoteName('ws.default') . ' = 1', + $db->quoteName('ws.workflow_id') . ' = ' . $db->quoteName('w.id'), + $db->quoteName('w.published') . ' = 1', + $db->quoteName('ws.published') . ' = 1', + $db->quoteName('w.default') . ' = 1', + ] + ); + + $stage_id = (int) $db->setQuery($query)->loadResult(); + + // Last check if we have a workflow ID + if (!empty($stage_id)) + { + return $stage_id; + } + + return false; + } + /** * Batch change workflow stage or current. * @@ -97,10 +445,8 @@ public function batchWorkflowStage(int $value, array $pks, array $contexts) { return false; } - $workflow = new Workflow(['extension' => $this->option]); - // Update workflow associations - return $workflow->updateAssociations($pks, $value); + return $this->workflow->updateAssociations($pks, $value); } } diff --git a/libraries/src/MVC/Model/WorkflowModelInterface.php b/libraries/src/MVC/Model/WorkflowModelInterface.php index cba3aef197647..a2bb76279c972 100644 --- a/libraries/src/MVC/Model/WorkflowModelInterface.php +++ b/libraries/src/MVC/Model/WorkflowModelInterface.php @@ -31,6 +31,64 @@ interface WorkflowModelInterface * @since 4.0.0 * @throws \Exception if there is an error in the form event. */ - public function preProcessFormWorkflow(Form $form, $data); + public function workflowPreprocessForm(Form $form, $data); + + /** + * Preparation of workflow data/plugins + * + * @return void + * + * @since 4.0.0 + */ + public function workflowBeforeSave(); + + /** + * Executing of relevant workflow methods + * + * @return void + * + * @since 4.0.0 + */ + public function workflowAfterSave($data); + + /** + * Method to get state variables. + * + * @param string $property Optional parameter name + * @param mixed $default Optional default value + * + * @return mixed The property where specified, the state object where omitted + * + * @since 4.0.0 + */ + public function getState($property = null, $default = null); + + /** + * Method to get the model name + * + * The model name. By default parsed using the classname or it can be set + * by passing a $config['name'] in the class constructor + * + * @return string The name of the model + * + * @since 4.0.0 + * @throws \Exception + */ + public function getName(); + + + /** + * Method to get a table object, load it if necessary. + * + * @param string $name The table name. Optional. + * @param string $prefix The class prefix. Optional. + * @param array $options Configuration array for model. Optional. + * + * @return Table A Table object + * + * @since 3.0 + * @throws \Exception + */ + public function getTable($name = '', $prefix = '', $options = array()); } diff --git a/libraries/src/Workflow/Workflow.php b/libraries/src/Workflow/Workflow.php index ddeb536bad673..5d4e7cc6dac1a 100644 --- a/libraries/src/Workflow/Workflow.php +++ b/libraries/src/Workflow/Workflow.php @@ -196,11 +196,22 @@ public function executeTransition(array $pks, int $transition_id): bool } } + $app = Factory::getApplication(); + + $app->triggerEvent( + 'onWorkflowBeforeTransition', + [ + 'pks' => $pks, + 'extension' => $this->extension, + 'user' => $app->getIdentity(), + 'transition' => $transition, + ] + ); + $success = $this->updateAssociations($pks, (int) $transition->to_stage_id); if ($success) { - $app = Factory::getApplication(); $app->triggerEvent( 'onWorkflowAfterTransition', [