Skip to content

Commit 8dbf670

Browse files
authored
[6.0] Fix version history (#46268)
* Fix version history * CS * Fix hardcoded table name * Add field for new installation * Update administrator/components/com_admin/sql/updates/postgresql/6.0.0-2025-10-11.sql * Update administrator/components/com_admin/sql/updates/mysql/6.0.0-2025-10-11.sql Co-authored-by: Richard Fath <[email protected]> * Prevent data lost when restore from version history created prior to Joomla 6 * Update administrator/components/com_admin/sql/updates/postgresql/6.0.0-2025-10-11.sql Co-authored-by: Richard Fath <[email protected]> * Add field for new installation * Make phpstan happy * Update libraries/src/Versioning/VersionableModelTrait.php Co-authored-by: Christian Heel <[email protected]> * Only store item real data into history * Revert "Only store item real data into history" * Avoid random column could not be null when restore from version history * Mark the history record not created by VersionableModelTrait as legacy * Fix warning for subform fields on compare view * Code simplify, thanks Richard * Move merge data logic to trait to make it easier for third party integration if not call parent::save * Use new way for saving category version history * Use new way to store version history for Tags * Prevent Version plugin save history for for Content, Banner, Contact * Use right context for category * Fix current version not detected properly on history view
1 parent ef7b7e0 commit 8dbf670

File tree

18 files changed

+150
-120
lines changed

18 files changed

+150
-120
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
ALTER TABLE `#__history`
2+
ADD COLUMN `is_current` TINYINT NOT NULL DEFAULT 0 /** CAN FAIL **/;
3+
ALTER TABLE `#__history`
4+
ADD COLUMN `is_legacy` TINYINT NOT NULL DEFAULT 0 /** CAN FAIL **/;
5+
UPDATE `#__history` SET `is_legacy` = 1;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
ALTER TABLE "#__history"
2+
ADD COLUMN "is_current" SMALLINT NOT NULL DEFAULT 0 /** CAN FAIL **/;
3+
ALTER TABLE "#__history"
4+
ADD COLUMN "is_legacy" SMALLINT NOT NULL DEFAULT 0 /** CAN FAIL **/;
5+
UPDATE "#__history" SET "is_legacy" = 1;

administrator/components/com_banners/src/Table/BannerTable.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
use Joomla\CMS\Factory;
1616
use Joomla\CMS\Language\Text;
1717
use Joomla\CMS\Table\Table;
18-
use Joomla\CMS\Versioning\VersionableTableInterface;
1918
use Joomla\Database\DatabaseInterface;
2019
use Joomla\Database\ParameterType;
2120
use Joomla\Event\DispatcherInterface;
@@ -31,7 +30,7 @@
3130
*
3231
* @since 1.5
3332
*/
34-
class BannerTable extends Table implements VersionableTableInterface
33+
class BannerTable extends Table
3534
{
3635
/**
3736
* Indicates that columns fully support the NULL value in the database

administrator/components/com_categories/src/Model/CategoryModel.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -710,6 +710,12 @@ public function save($data)
710710

711711
$this->setState($this->getName() . '.id', $table->id);
712712

713+
/**
714+
* Save the version history. We need to call saveHistory method manually because category model does not
715+
* call parent::save()
716+
*/
717+
$this->saveHistory($data, $this->typeAlias);
718+
713719
if (Factory::getApplication()->getInput()->get('task') == 'editAssociations') {
714720
return $this->redirectToAssociations($data);
715721
}

administrator/components/com_contact/src/Table/ContactTable.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
use Joomla\CMS\Tag\TaggableTableTrait;
2121
use Joomla\CMS\User\CurrentUserInterface;
2222
use Joomla\CMS\User\CurrentUserTrait;
23-
use Joomla\CMS\Versioning\VersionableTableInterface;
2423
use Joomla\Database\DatabaseInterface;
2524
use Joomla\Event\DispatcherInterface;
2625
use Joomla\String\StringHelper;
@@ -34,7 +33,7 @@
3433
*
3534
* @since 1.0
3635
*/
37-
class ContactTable extends Table implements VersionableTableInterface, TaggableTableInterface, CurrentUserInterface
36+
class ContactTable extends Table implements TaggableTableInterface, CurrentUserInterface
3837
{
3938
use TaggableTableTrait;
4039
use CurrentUserTrait;

administrator/components/com_contenthistory/src/Model/HistoryModel.php

Lines changed: 15 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
use Joomla\CMS\Access\Exception\NotAllowed;
1414
use Joomla\CMS\Component\ComponentHelper;
1515
use Joomla\CMS\Factory;
16-
use Joomla\CMS\Form\Form;
1716
use Joomla\CMS\Helper\CMSHelper;
1817
use Joomla\CMS\Language\Text;
1918
use Joomla\CMS\Log\Log;
@@ -22,7 +21,6 @@
2221
use Joomla\CMS\Table\ContentHistory;
2322
use Joomla\CMS\Table\ContentType;
2423
use Joomla\CMS\Table\Table;
25-
use Joomla\CMS\Versioning\VersionableModelInterface;
2624
use Joomla\Database\ParameterType;
2725
use Joomla\Database\QueryInterface;
2826

@@ -307,7 +305,6 @@ protected function populateState($ordering = 'h.save_date', $direction = 'DESC')
307305
$itemId = $input->get('item_id', '', 'string');
308306

309307
$this->setState('item_id', $itemId);
310-
$this->setState('sha1_hash', $this->getSha1Hash());
311308

312309
// Load the parameters.
313310
$params = ComponentHelper::getParams('com_contenthistory');
@@ -345,6 +342,7 @@ protected function getListQuery()
345342
$db->quoteName('h.sha1_hash'),
346343
$db->quoteName('h.version_data'),
347344
$db->quoteName('h.keep_forever'),
345+
$db->quoteName('h.is_current'),
348346
]
349347
)
350348
)
@@ -375,47 +373,27 @@ protected function getListQuery()
375373
*
376374
* @since 3.2
377375
*/
378-
protected function getSha1Hash()
376+
public function getSha1Hash()
379377
{
380-
$result = false;
381-
$item_id = Factory::getApplication()->getInput()->getCmd('item_id', '');
382-
383-
[$extension, $type, $id] = explode('.', $item_id);
384-
385-
$app = Factory::getApplication();
386-
387-
$model = $app->bootComponent($extension)->getMVCFactory()->createModel($type, 'Administrator');
388-
389-
if ($model instanceof VersionableModelInterface) {
390-
$path = JPATH_BASE . '/components/' . $extension;
391-
392-
Form::addFormPath($path . '/forms');
393-
Form::addFormPath($path . '/models/forms');
394-
Form::addFieldPath($path . '/models/fields');
395-
Form::addFormPath($path . '/model/form');
396-
Form::addFieldPath($path . '/model/field');
397-
398-
// This is needed to make sure the model has called populateState
399-
$tmp = $model->getState();
400-
401-
// Now we can set the article.id and it is not overwritten later by populateState
402-
$model->setState('article.id', $id);
403-
404-
$item = $model->getItem();
405-
$form = $model->getForm();
378+
/**
379+
* From Joomla 6, we use is_current field to determine the current version, so no need to calculate sha1 hash
380+
* if there is already a current version
381+
*/
406382

407-
$cf = $form->getData()->get('com_fields', null);
383+
$items = $this->getItems();
408384

409-
if (!empty($cf)) {
410-
$item->com_fields = $cf;
385+
foreach ($items as $item) {
386+
if ($item->is_current == 1) {
387+
return $item->sha1_hash;
411388
}
412-
413-
$result = $model->getSha1($item);
414-
415-
return $result;
416389
}
417390

418391
// Legacy code for history concept before 6.0.0, deprecated 6.0.0 will be removed with 8.0.0
392+
$result = false;
393+
$item_id = $this->state->get('item_id', '');
394+
395+
[$extension, $type, $id] = explode('.', $item_id);
396+
419397
Table::addIncludePath(JPATH_ADMINISTRATOR . '/components/' . $extension . '/tables');
420398
$typeTable = $this->getTable('ContentType');
421399
$typeTable->load(['type_alias' => $extension . '.' . $type]);

administrator/components/com_contenthistory/src/View/History/HtmlView.php

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,13 @@ class HtmlView extends BaseHtmlView
5959
*/
6060
protected $toolbar;
6161

62+
/**
63+
* The SHA1 hash of the current version of the item being viewed
64+
*
65+
* @var string
66+
*/
67+
protected $currentVersionHash;
68+
6269
/**
6370
* Method to display the view.
6471
*
@@ -74,9 +81,10 @@ public function display($tpl = null)
7481
$model = $this->getModel();
7582
$model->setUseExceptions(true);
7683

77-
$this->state = $model->getState();
78-
$this->items = $model->getItems();
79-
$this->pagination = $model->getPagination();
84+
$this->state = $model->getState();
85+
$this->items = $model->getItems();
86+
$this->pagination = $model->getPagination();
87+
$this->currentVersionHash = $model->getSha1Hash();
8088

8189
$this->toolbar = $this->addToolbar();
8290

administrator/components/com_contenthistory/tmpl/compare/compare.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@
6868
<td class="original">
6969
<?php if (isset($value1['value'][$key])) : ?>
7070
<?php $currentvalue1 = $value1['value'][$key]; ?>
71-
<?php if (is_array($value1['value'][$key])) : ?>
72-
<?php $currentvalue1 = implode(' | ', $value1['value'][$key]); ?>
71+
<?php if (is_array($currentvalue1)) : ?>
72+
<?php $currentvalue1 = ArrayHelper::isAssociative($currentvalue1) ? json_encode($currentvalue1) : implode(' | ', $currentvalue1); ?>
7373
<?php echo htmlspecialchars($key . ': ' . $currentvalue1, ENT_COMPAT, 'UTF-8'); ?>
7474
<?php else : ?>
7575
<?php echo htmlspecialchars($key . ': ' . $currentvalue1, ENT_COMPAT, 'UTF-8'); ?>
@@ -79,8 +79,8 @@
7979
<td class="changed">
8080
<?php if (isset($value2['value'][$key])) : ?>
8181
<?php $currentvalue2 = $value2['value'][$key]; ?>
82-
<?php if (is_array($value2['value'][$key])) : ?>
83-
<?php $currentvalue2 = implode(' | ', $value2['value'][$key]); ?>
82+
<?php if (is_array($currentvalue2)) : ?>
83+
<?php $currentvalue2 = ArrayHelper::isAssociative($currentvalue2) ? json_encode($currentvalue2) : implode(' | ', $currentvalue2); ?>
8484
<?php echo htmlspecialchars($key . ': ' . $currentvalue2, ENT_COMPAT, 'UTF-8'); ?>
8585
<?php else : ?>
8686
<?php echo htmlspecialchars($key . ': ' . $currentvalue2, ENT_COMPAT, 'UTF-8'); ?>

administrator/components/com_contenthistory/tmpl/history/modal.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
Session::checkToken('get') or die(Text::_('JINVALID_TOKEN'));
2121

22-
$hash = $this->state->get('sha1_hash');
22+
$hash = $this->currentVersionHash;
2323
$formUrl = 'index.php?option=com_contenthistory&view=history&layout=modal&tmpl=component&item_id=' . $this->state->get('item_id') . '&' . Session::getFormToken() . '=1';
2424

2525
Text::script('COM_CONTENTHISTORY_BUTTON_SELECT_ONE_VERSION', true);

administrator/components/com_tags/src/Model/TagModel.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Joomla\CMS\Factory;
1616
use Joomla\CMS\MVC\Model\AdminModel;
1717
use Joomla\CMS\Plugin\PluginHelper;
18+
use Joomla\CMS\Versioning\VersionableModelInterface;
1819
use Joomla\CMS\Versioning\VersionableModelTrait;
1920
use Joomla\Registry\Registry;
2021
use Joomla\String\StringHelper;
@@ -28,7 +29,7 @@
2829
*
2930
* @since 3.1
3031
*/
31-
class TagModel extends AdminModel
32+
class TagModel extends AdminModel implements VersionableModelInterface
3233
{
3334
use VersionableModelTrait;
3435

@@ -307,6 +308,9 @@ public function save($data)
307308
$this->setState($this->getName() . '.id', $table->id);
308309
$this->setState($this->getName() . '.new', $isNew);
309310

311+
// Save version history.
312+
$this->saveHistory($data, $context);
313+
310314
// Clear the cache
311315
$this->cleanCache();
312316

0 commit comments

Comments
 (0)