* @package WoltLabSuite\Core\Acp\Page
*/
class VersionTrackerListPage extends AbstractPage {
/**
* object id
* @var integer
*/
public $objectID = 0;
/**
* object type name
* @var string
*/
public $objectType = '';
/**
* @var IVersionTrackerProvider
*/
public $objectTypeProcessor;
/**
* @var VersionTrackerEntry[]
*/
public $versions = [];
/**
* left / old version id
* @var integer
*/
public $oldID = 0;
/**
* left / old version
* @var VersionTrackerEntry
*/
public $old;
/**
* right / new version id
* @var integer
*/
public $newID = 0;
/**
* right / new version
* @var VersionTrackerEntry
*/
public $new;
/**
* differences between both versions
* @var Diff[]
*/
public $diffs = [];
/**
* requested object
* @var IVersionTrackerObject
*/
public $object;
/**
* list of available languages for comparison
* @var Language[]
*/
public $languages = [];
/**
* property used for comparison
* @var string
*/
public $property = '';
/**
* @inheritDoc
*/
public function readParameters() {
parent::readParameters();
if (isset($_REQUEST['objectID'])) $this->objectID = intval($_REQUEST['objectID']);
if (isset($_REQUEST['objectType'])) $this->objectType = $_REQUEST['objectType'];
try {
$objectType = VersionTracker::getInstance()->getObjectType($this->objectType);
}
catch (\InvalidArgumentException $e) {
throw new IllegalLinkException();
}
$this->objectTypeProcessor = $objectType->getProcessor();
if (!$this->objectTypeProcessor->canAccess()) {
throw new PermissionDeniedException();
}
$this->activeMenuItem = $this->objectTypeProcessor->getActiveMenuItem();
$this->object = $this->objectTypeProcessor->getObjectByID($this->objectID);
if (!$this->object->getObjectID()) {
throw new IllegalLinkException();
}
$this->versions = VersionTracker::getInstance()->getVersions($this->objectType, $this->objectID);
if (isset($_REQUEST['oldID'])) {
$this->oldID = intval($_REQUEST['oldID']);
$this->old = VersionTracker::getInstance()->getVersion($this->objectType, $this->oldID);
if (!$this->old->versionID) throw new IllegalLinkException();
if (isset($_REQUEST['newID']) && $_REQUEST['newID'] !== 'current') {
$this->newID = intval($_REQUEST['newID']);
$this->new = VersionTracker::getInstance()->getVersion($this->objectType, $this->newID);
if (!$this->new->versionID) throw new IllegalLinkException();
}
}
if (isset($_REQUEST['newID']) && !$this->new) {
$this->new = $this->objectTypeProcessor->getCurrentVersion($this->object);
$this->newID = 'current';
}
if (!empty($_POST)) {
HeaderUtil::redirect(LinkHandler::getInstance()->getLink('VersionTrackerList', [
'objectID' => $this->objectID,
'objectType' => $this->objectType,
'newID' => $this->newID,
'oldID' => $this->oldID
]));
exit;
}
}
/**
* @inheritDoc
*/
public function readData() {
parent::readData();
// valid IDs were given, calculate diff
if ($this->old && $this->new) {
$languageIDs = $this->new->getLanguageIDs();
if (count($languageIDs) > 1 || $languageIDs[0] != 0) {
foreach ($languageIDs as $i => $languageID) {
$language = LanguageFactory::getInstance()->getLanguage($languageID);
if ($language === null) {
unset($languageIDs[$i]);
}
else {
$this->languages[$languageID] = $language;
}
}
$languageIDs = array_unique($languageIDs);
}
$properties = $this->objectTypeProcessor->getTrackedProperties();
foreach ($languageIDs as $languageID) {
$this->diffs[$languageID] = [];
foreach ($properties as $property) {
$a = explode("\n", StringUtil::unifyNewlines($this->old->getPayload($property, $languageID)));
$b = explode("\n", StringUtil::unifyNewlines($this->new->getPayload($property, $languageID)));
if ($a == $b) continue;
$diff = new Diff($a, $b);
$rawDiff = $diff->getRawDiff();
// create word diff for small changes (only one consecutive paragraph modified)
for ($i = 0, $max = count($rawDiff); $i < $max;) {
$previousIsNotRemoved = !isset($rawDiff[$i - 1][0]) || $rawDiff[$i - 1][0] !== Diff::REMOVED;
$currentIsRemoved = $rawDiff[$i][0] === Diff::REMOVED;
$nextIsAdded = isset($rawDiff[$i + 1][0]) && $rawDiff[$i + 1][0] === Diff::ADDED;
$afterNextIsNotAdded = !isset($rawDiff[$i + 2][0]) || $rawDiff[$i + 2][0] !== Diff::ADDED;
if ($previousIsNotRemoved && $currentIsRemoved && $nextIsAdded && $afterNextIsNotAdded) {
$a = preg_split('/(\\W)/u', $rawDiff[$i][1], -1, PREG_SPLIT_DELIM_CAPTURE);
$b = preg_split('/(\\W)/u', $rawDiff[$i + 1][1], -1, PREG_SPLIT_DELIM_CAPTURE);
$diff = new Diff($a, $b);
$rawDiff[$i][1] = '';
$rawDiff[$i + 1][1] = '';
foreach ($diff->getRawDiff() as $entry) {
$entry[1] = StringUtil::encodeHTML($entry[1]);
if ($entry[0] === Diff::SAME) {
$rawDiff[$i][1] .= $entry[1];
$rawDiff[$i + 1][1] .= $entry[1];
}
else {
if ($entry[0] === Diff::REMOVED) {
$rawDiff[$i][1] .= '' . $entry[1] . '';
}
else {
if ($entry[0] === Diff::ADDED) {
$rawDiff[$i + 1][1] .= '' . $entry[1] . '';
}
}
}
}
$i += 2;
}
else {
$rawDiff[$i][1] = StringUtil::encodeHTML($rawDiff[$i][1]);
$i++;
}
}
$this->diffs[$languageID][$property] = $rawDiff;
}
}
// simply template logic by treating diffs with only one language as "no i18n"
if (count($this->diffs) == 1 && !isset($this->diffs[0])) {
$this->diffs = [reset($this->diffs)];
}
}
// set default values
if (!isset($_REQUEST['oldID']) && !isset($_REQUEST['newID'])) {
foreach ($this->versions as $version) {
$this->oldID = $version->versionID;
break;
}
$this->newID = 'current';
}
}
/**
* @inheritDoc
*/
public function assignVariables() {
parent::assignVariables();
WCF::getTPL()->assign([
'oldID' => $this->oldID,
'old' => $this->old,
'newID' => $this->newID,
'new' => $this->new,
'diffs' => $this->diffs,
'objectID' => $this->objectID,
'objectType' => $this->objectType,
'objectTypeProcessor' => $this->objectTypeProcessor,
'object' => $this->object,
'languages' => $this->languages,
'versions' => $this->versions
]);
}
}