diff --git a/administrator/components/com_contenthistory/tmpl/compare/compare.php b/administrator/components/com_contenthistory/tmpl/compare/compare.php index af7ee42c0ce37..8a899fca4250d 100644 --- a/administrator/components/com_contenthistory/tmpl/compare/compare.php +++ b/administrator/components/com_contenthistory/tmpl/compare/compare.php @@ -41,7 +41,7 @@ $value) : ?> - value != $object2->$name->value) : ?> + value) && isset($object2->$name->value) && $value->value != $object2->$name->value) : ?> value)) : ?> diff --git a/libraries/src/Versioning/Versioning.php b/libraries/src/Versioning/Versioning.php index 5ed8b057ba898..0f770f97fa4ca 100644 --- a/libraries/src/Versioning/Versioning.php +++ b/libraries/src/Versioning/Versioning.php @@ -11,8 +11,11 @@ \defined('JPATH_PLATFORM') or die; use Joomla\CMS\Component\ComponentHelper; +use Joomla\CMS\Event\AbstractEvent; use Joomla\CMS\Factory; +use Joomla\CMS\Plugin\PluginHelper; use Joomla\CMS\Table\Table; +use Joomla\CMS\Workflow\WorkflowServiceInterface; use Joomla\Database\ParameterType; /** @@ -92,12 +95,33 @@ public static function store($typeAlias, $id, $data, $note = '') $historyTable = Table::getInstance('Contenthistory', 'JTable'); $historyTable->item_id = $typeAlias . '.' . $id; + $aliasParts = explode('.', $typeAlias); + // Don't store unless we have a non-zero item id if (!$historyTable->item_id) { return true; } + // We should allow workflow items interact with the versioning + $component = Factory::getApplication()->bootComponent($aliasParts[0]); + + if ($component instanceof WorkflowServiceInterface && $component->isWorkflowActive($typeAlias)) + { + PluginHelper::importPlugin('workflow'); + + // Pre-processing by observers + $event = AbstractEvent::create( + 'onContentVersioningPrepareTable', + [ + 'subject' => $historyTable, + 'extension' => $typeAlias + ] + ); + + Factory::getApplication()->getDispatcher()->dispatch('onContentVersioningPrepareTable', $event); + } + $historyTable->version_data = json_encode($data); $historyTable->version_note = $note; @@ -120,8 +144,6 @@ public static function store($typeAlias, $id, $data, $note = '') $result = $historyTable->store(); // Load history_limit config from extension. - $aliasParts = explode('.', $typeAlias); - $context = $aliasParts[1] ?? ''; $maxVersionsContext = ComponentHelper::getParams($aliasParts[0])->get('history_limit_' . $context, 0); diff --git a/plugins/workflow/featuring/featuring.php b/plugins/workflow/featuring/featuring.php index e228d7dce918d..0a02da672f754 100644 --- a/plugins/workflow/featuring/featuring.php +++ b/plugins/workflow/featuring/featuring.php @@ -11,6 +11,7 @@ use Joomla\CMS\Application\CMSApplicationInterface; use Joomla\CMS\Event\AbstractEvent; +use Joomla\CMS\Event\Table\BeforeStoreEvent; use Joomla\CMS\Event\View\DisplayEvent; use Joomla\CMS\Event\Workflow\WorkflowFunctionalityUsedEvent; use Joomla\CMS\Event\Workflow\WorkflowTransitionEvent; @@ -19,12 +20,14 @@ use Joomla\CMS\Language\Text; use Joomla\CMS\MVC\Model\DatabaseModelInterface; use Joomla\CMS\Plugin\CMSPlugin; +use Joomla\CMS\Table\ContentHistory; use Joomla\CMS\Table\TableInterface; use Joomla\CMS\Workflow\WorkflowPluginTrait; use Joomla\CMS\Workflow\WorkflowServiceInterface; use Joomla\Component\Content\Administrator\Event\Model\FeatureEvent; use Joomla\Event\EventInterface; use Joomla\Event\SubscriberInterface; +use Joomla\Registry\Registry; use Joomla\String\Inflector; /** @@ -70,13 +73,15 @@ class PlgWorkflowFeaturing extends CMSPlugin implements SubscriberInterface public static function getSubscribedEvents(): array { return [ - 'onContentPrepareForm' => 'onContentPrepareForm', - 'onAfterDisplay' => 'onAfterDisplay', - 'onWorkflowBeforeTransition' => 'onWorkflowBeforeTransition', - 'onWorkflowAfterTransition' => 'onWorkflowAfterTransition', - 'onContentBeforeChangeFeatured' => 'onContentBeforeChangeFeatured', - 'onContentBeforeSave' => 'onContentBeforeSave', - 'onWorkflowFunctionalityUsed' => 'onWorkflowFunctionalityUsed', + 'onAfterDisplay' => 'onAfterDisplay', + 'onContentBeforeChangeFeatured' => 'onContentBeforeChangeFeatured', + 'onContentBeforeSave' => 'onContentBeforeSave', + 'onContentPrepareForm' => 'onContentPrepareForm', + 'onContentVersioningPrepareTable' => 'onContentVersioningPrepareTable', + 'onTableBeforeStore' => 'onTableBeforeStore', + 'onWorkflowAfterTransition' => 'onWorkflowAfterTransition', + 'onWorkflowBeforeTransition' => 'onWorkflowBeforeTransition', + 'onWorkflowFunctionalityUsed' => 'onWorkflowFunctionalityUsed' ]; } @@ -421,6 +426,82 @@ public function onContentBeforeSave(EventInterface $event) return true; } + /** + * We remove the featured field from the versioning + * + * @param EventInterface $event + * + * @return boolean + * + * @since 4.0.0 + */ + public function onContentVersioningPrepareTable(EventInterface $event) + { + $subject = $event->getArgument('subject'); + $context = $event->getArgument('extension'); + + if (!$this->isSupported($context)) + { + return true; + } + + $parts = explode('.', $context); + + $component = $this->app->bootComponent($parts[0]); + + $modelName = $component->getModelName($context); + + $model = $component->getMVCFactory()->createModel($modelName, $this->app->getName(), ['ignore_request' => true]); + + $table = $model->getTable(); + + $subject->ignoreChanges[] = $table->getColumnAlias('featured'); + } + + /** + * Pre-processor for $table->store($updateNulls) + * + * @param BeforeStoreEvent $event The event to handle + * + * @return void + * + * @since 4.0.0 + */ + public function onTableBeforeStore(BeforeStoreEvent $event) + { + $subject = $event->getArgument('subject'); + + if (!($subject instanceof ContentHistory)) + { + return; + } + + $parts = explode('.', $subject->item_id); + + $typeAlias = $parts[0] . (isset($parts[1]) ? '.' . $parts[1] : ''); + + if (!$this->isSupported($typeAlias)) + { + return; + } + + $component = $this->app->bootComponent($parts[0]); + + $modelName = $component->getModelName($typeAlias); + + $model = $component->getMVCFactory()->createModel($modelName, $this->app->getName(), ['ignore_request' => true]); + + $table = $model->getTable(); + + $field = $table->getColumnAlias('featured'); + + $versionData = new Registry($subject->version_data); + + $versionData->remove($field); + + $subject->version_data = $versionData->toString(); + } + /** * Check if the current plugin should execute workflow related activities * diff --git a/plugins/workflow/publishing/publishing.php b/plugins/workflow/publishing/publishing.php index 7196f90e2e22d..e1fa787412254 100644 --- a/plugins/workflow/publishing/publishing.php +++ b/plugins/workflow/publishing/publishing.php @@ -10,6 +10,7 @@ defined('_JEXEC') or die; use Joomla\CMS\Application\CMSApplicationInterface; +use Joomla\CMS\Event\Table\BeforeStoreEvent; use Joomla\CMS\Event\View\DisplayEvent; use Joomla\CMS\Event\Workflow\WorkflowFunctionalityUsedEvent; use Joomla\CMS\Event\Workflow\WorkflowTransitionEvent; @@ -18,11 +19,13 @@ use Joomla\CMS\Language\Text; use Joomla\CMS\MVC\Model\DatabaseModelInterface; use Joomla\CMS\Plugin\CMSPlugin; +use Joomla\CMS\Table\ContentHistory; use Joomla\CMS\Table\TableInterface; use Joomla\CMS\Workflow\WorkflowPluginTrait; use Joomla\CMS\Workflow\WorkflowServiceInterface; use Joomla\Event\EventInterface; use Joomla\Event\SubscriberInterface; +use Joomla\Registry\Registry; use Joomla\String\Inflector; /** @@ -68,13 +71,15 @@ class PlgWorkflowPublishing extends CMSPlugin implements SubscriberInterface public static function getSubscribedEvents(): array { return [ - 'onContentPrepareForm' => 'onContentPrepareForm', - 'onAfterDisplay' => 'onAfterDisplay', - 'onWorkflowBeforeTransition' => 'onWorkflowBeforeTransition', - 'onWorkflowAfterTransition' => 'onWorkflowAfterTransition', - 'onContentBeforeChangeState' => 'onContentBeforeChangeState', - 'onContentBeforeSave' => 'onContentBeforeSave', - 'onWorkflowFunctionalityUsed' => 'onWorkflowFunctionalityUsed', + 'onAfterDisplay' => 'onAfterDisplay', + 'onContentBeforeChangeState' => 'onContentBeforeChangeState', + 'onContentBeforeSave' => 'onContentBeforeSave', + 'onContentPrepareForm' => 'onContentPrepareForm', + 'onContentVersioningPrepareTable' => 'onContentVersioningPrepareTable', + 'onTableBeforeStore' => 'onTableBeforeStore', + 'onWorkflowAfterTransition' => 'onWorkflowAfterTransition', + 'onWorkflowBeforeTransition' => 'onWorkflowBeforeTransition', + 'onWorkflowFunctionalityUsed' => 'onWorkflowFunctionalityUsed' ]; } @@ -432,6 +437,82 @@ public function onContentBeforeSave(EventInterface $event) return true; } + /** + * We remove the publishing field from the versioning + * + * @param EventInterface $event + * + * @return boolean + * + * @since 4.0.0 + */ + public function onContentVersioningPrepareTable(EventInterface $event) + { + $subject = $event->getArgument('subject'); + $context = $event->getArgument('extension'); + + if (!$this->isSupported($context)) + { + return true; + } + + $parts = explode('.', $context); + + $component = $this->app->bootComponent($parts[0]); + + $modelName = $component->getModelName($context); + + $model = $component->getMVCFactory()->createModel($modelName, $this->app->getName(), ['ignore_request' => true]); + + $table = $model->getTable(); + + $subject->ignoreChanges[] = $table->getColumnAlias('published'); + } + + /** + * Pre-processor for $table->store($updateNulls) + * + * @param BeforeStoreEvent $event The event to handle + * + * @return void + * + * @since 4.0.0 + */ + public function onTableBeforeStore(BeforeStoreEvent $event) + { + $subject = $event->getArgument('subject'); + + if (!($subject instanceof ContentHistory)) + { + return; + } + + $parts = explode('.', $subject->item_id); + + $typeAlias = $parts[0] . (isset($parts[1]) ? '.' . $parts[1] : ''); + + if (!$this->isSupported($typeAlias)) + { + return; + } + + $component = $this->app->bootComponent($parts[0]); + + $modelName = $component->getModelName($typeAlias); + + $model = $component->getMVCFactory()->createModel($modelName, $this->app->getName(), ['ignore_request' => true]); + + $table = $model->getTable(); + + $field = $table->getColumnAlias('published'); + + $versionData = new Registry($subject->version_data); + + $versionData->remove($field); + + $subject->version_data = $versionData->toString(); + } + /** * Check if the current plugin should execute workflow related activities *