Skip to content
Snippets Groups Projects
Commit 75e9d0e0 authored by Oliver Hader's avatar Oliver Hader Committed by Oliver Hader
Browse files

[TASK] Missing visual representation of sys_file_reference

File references are currently only represented by the accordant
record uid which should at least be a filename. In a workspace
environment the changed file references shall be visualized
as thumbnails - either being removed or inserted.

Resolves: #60011
Releases: master
Change-Id: I6d22619c264ff0e5411a47b2d566ec2c9b7c2607
Reviewed-on: https://review.typo3.org/31270


Reviewed-by: default avatarAlexander Opitz <opitz.alexander@googlemail.com>
Tested-by: default avatarAlexander Opitz <opitz.alexander@googlemail.com>
Reviewed-by: default avatarOliver Hader <oliver.hader@typo3.org>
Tested-by: default avatarOliver Hader <oliver.hader@typo3.org>
parent 6b0282e5
Branches
Tags
No related merge requests found
......@@ -19,6 +19,7 @@ use TYPO3\CMS\Core\Imaging\Icon;
use TYPO3\CMS\Core\Imaging\IconFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Core\Resource\FileReference;
/**
* ExtDirect server
......@@ -35,10 +36,15 @@ class ExtDirectServer extends AbstractHandler
*/
protected $stagesService;
/**
* @var \cogpowered\FineDiff\Diff
*/
protected $differenceHandler;
/**
* Checks integrity of elements before peforming actions on them.
*
* @param stdClass $parameters
* @param \stdClass $parameters
* @return array
*/
public function checkIntegrity(\stdClass $parameters)
......@@ -134,10 +140,61 @@ class ExtDirectServer extends AbstractHandler
}
}
foreach ($fieldsOfRecords as $fieldName) {
if (empty($GLOBALS['TCA'][$parameter->table]['columns'][$fieldName]['config'])) {
continue;
}
// Get the field's label. If not available, use the field name
$fieldTitle = $GLOBALS['LANG']->sL(BackendUtility::getItemLabel($parameter->table, $fieldName));
if (empty($fieldTitle)) {
$fieldTitle = $fieldName;
}
// Gets the TCA configuration for the current field
$configuration = $GLOBALS['TCA'][$parameter->table]['columns'][$fieldName]['config'];
// check for exclude fields
if ($GLOBALS['BE_USER']->isAdmin() || $GLOBALS['TCA'][$parameter->table]['columns'][$fieldName]['exclude'] == 0 || GeneralUtility::inList($GLOBALS['BE_USER']->groupData['non_exclude_fields'], $parameter->table . ':' . $fieldName)) {
// call diff class only if there is a difference
if ((string)$liveRecord[$fieldName] !== (string)$versionRecord[$fieldName]) {
if ($configuration['type'] === 'inline' && $configuration['foreign_table'] === 'sys_file_reference') {
$useThumbnails = false;
if (!empty($configuration['foreign_selector_fieldTcaOverride']['config']['appearance']['elementBrowserAllowed']) && !empty($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'])) {
$fileExtensions = GeneralUtility::trimExplode(',', $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], true);
$allowedExtensions = GeneralUtility::trimExplode(',', $configuration['foreign_selector_fieldTcaOverride']['config']['appearance']['elementBrowserAllowed'], true);
$differentExtensions = array_diff($allowedExtensions, $fileExtensions);
$useThumbnails = empty($differentExtensions);
}
$liveFileReferences = BackendUtility::resolveFileReferences(
$parameter->table,
$fieldName,
$liveRecord,
0
);
$versionFileReferences = BackendUtility::resolveFileReferences(
$parameter->table,
$fieldName,
$versionRecord,
$this->getCurrentWorkspace()
);
$fileReferenceDifferences = $this->prepareFileReferenceDifferences(
$liveFileReferences,
$versionFileReferences,
$useThumbnails
);
if ($fileReferenceDifferences === null) {
continue;
}
$diffReturnArray[] = array(
'field' => $fieldName,
'label' => $fieldTitle,
'content' => $fileReferenceDifferences['differences']
);
$liveReturnArray[] = array(
'field' => $fieldName,
'label' => $fieldTitle,
'content' => $fileReferenceDifferences['live']
);
} elseif ((string)$liveRecord[$fieldName] !== (string)$versionRecord[$fieldName]) {
// Select the human readable values before diff
$liveRecord[$fieldName] = BackendUtility::getProcessedValue(
$parameter->table,
......@@ -157,12 +214,8 @@ class ExtDirectServer extends AbstractHandler
false,
$versionRecord['uid']
);
// Get the field's label. If not available, use the field name
$fieldTitle = $GLOBALS['LANG']->sL(BackendUtility::getItemLabel($parameter->table, $fieldName));
if (empty($fieldTitle)) {
$fieldTitle = $fieldName;
}
if ($GLOBALS['TCA'][$parameter->table]['columns'][$fieldName]['config']['type'] == 'group' && $GLOBALS['TCA'][$parameter->table]['columns'][$fieldName]['config']['internal_type'] == 'file') {
if ($configuration['type'] == 'group' && $configuration['internal_type'] == 'file') {
$versionThumb = BackendUtility::thumbCode($versionRecord, $parameter->table, $fieldName, '');
$liveThumb = BackendUtility::thumbCode($liveRecord, $parameter->table, $fieldName, '');
$diffReturnArray[] = array(
......@@ -194,8 +247,10 @@ class ExtDirectServer extends AbstractHandler
// (this may be used by custom or dynamically-defined fields)
if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['workspaces']['modifyDifferenceArray'])) {
foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['workspaces']['modifyDifferenceArray'] as $className) {
$hookObject =& GeneralUtility::getUserObj($className);
$hookObject->modifyDifferenceArray($parameter, $diffReturnArray, $liveReturnArray, $diffUtility);
$hookObject = GeneralUtility::getUserObj($className);
if (method_exists($hookObject, 'modifyDifferenceArray')) {
$hookObject->modifyDifferenceArray($parameter, $diffReturnArray, $liveReturnArray, $diffUtility);
}
}
}
$commentsForRecord = $this->getCommentsForRecord($parameter->uid, $parameter->table);
......@@ -217,6 +272,74 @@ class ExtDirectServer extends AbstractHandler
);
}
/**
* Prepares difference view for file references.
*
* @param FileReference[] $liveFileReferences
* @param FileReference[] $versionFileReferences
* @param bool|false $useThumbnails
* @return array|null
*/
protected function prepareFileReferenceDifferences(array $liveFileReferences, array $versionFileReferences, $useThumbnails = false)
{
$randomValue = uniqid('file');
$liveValues = array();
$versionValues = array();
$candidates = array();
$substitutes = array();
// Process live references
foreach ($liveFileReferences as $identifier => $liveFileReference) {
$identifierWithRandomValue = $randomValue . '__' . $liveFileReference->getUid() . '__' . $randomValue;
$candidates[$identifierWithRandomValue] = $liveFileReference;
$liveValues[] = $identifierWithRandomValue;
}
// Process version references
foreach ($versionFileReferences as $identifier => $versionFileReference) {
$identifierWithRandomValue = $randomValue . '__' . $versionFileReference->getUid() . '__' . $randomValue;
$candidates[$identifierWithRandomValue] = $versionFileReference;
$versionValues[] = $identifierWithRandomValue;
}
// Combine values and surround by spaces
// (to reduce the chunks Diff will find)
$liveInformation = ' ' . implode(' ', $liveValues) . ' ';
$versionInformation = ' ' . implode(' ', $versionValues) . ' ';
// Return if information has not changed
if ($liveInformation === $versionInformation) {
return null;
}
/**
* @var string $identifierWithRandomValue
* @var FileReference $fileReference
*/
foreach ($candidates as $identifierWithRandomValue => $fileReference) {
if ($useThumbnails) {
$thumbnailFile = $fileReference->getOriginalFile()->process(
\TYPO3\CMS\Core\Resource\ProcessedFile::CONTEXT_IMAGEPREVIEW,
array('width' => 40, 'height' => 40)
);
$thumbnailMarkup = '<img src="' . $thumbnailFile->getPublicUrl(true) . '" />';
$substitutes[$identifierWithRandomValue] = $thumbnailMarkup;
} else {
$substitutes[$identifierWithRandomValue] = $fileReference->getPublicUrl();
}
}
$differences = $this->getDifferenceHandler()->render($liveInformation, $versionInformation);
$liveInformation = str_replace(array_keys($substitutes), array_values($substitutes), trim($liveInformation));
$differences = str_replace(array_keys($substitutes), array_values($substitutes), trim($differences));
return array(
'live' => $liveInformation,
'differences' => $differences
);
}
/**
* Gets an array with all sys_log entries and their comments for the given record uid and table
*
......@@ -307,6 +430,20 @@ class ExtDirectServer extends AbstractHandler
return $this->stagesService;
}
/**
* Gets the difference handler, parsing differences based on sentences.
*
* @return \cogpowered\FineDiff\Diff
*/
protected function getDifferenceHandler()
{
if (!isset($this->differenceHandler)) {
$granularity = new \cogpowered\FineDiff\Granularity\Word();
$this->differenceHandler = new \cogpowered\FineDiff\Diff($granularity);
}
return $this->differenceHandler;
}
/**
* @return \TYPO3\CMS\Extbase\Object\ObjectManager
*/
......
......@@ -132,6 +132,16 @@ table.t3-workspaces-foldout-contentDiff td {
table.t3-workspaces-foldout-contentDiff .diff-r {
text-decoration: line-through;
}
.t3-workspaces-foldout-contentDiff .content ins > img {
padding: 1px;
margin-right: 2px;
border: 2px solid green;
}
.t3-workspaces-foldout-contentDiff .content del > img {
padding: 1px;
margin-right: 2px;
border: 2px solid red;
}
div.t3-workspaces-foldoutWrapper td.char_select_profile_stats {
padding-right: 10px;
}
......
<?php
namespace TYPO3\CMS\Workspaces\Tests\Unit\ExtDirect;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use Prophecy\Argument;
use Prophecy\Prophecy\ObjectProphecy;
use TYPO3\CMS\Core\Resource\File;
use TYPO3\CMS\Core\Resource\FileReference;
use TYPO3\CMS\Core\Resource\ProcessedFile;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* ExtDirectServer test
*/
class ExtDirectServerTest extends \TYPO3\CMS\Core\Tests\UnitTestCase
{
/**
* @var \TYPO3\CMS\Workspaces\ExtDirect\ExtDirectServer
*/
protected $subject;
/**
* @var FileReference[]|ObjectProphecy[]
*/
protected $fileReferenceProphecies;
/**
* Set up
*/
protected function setUp()
{
parent::setUp();
$this->subject = $this->getAccessibleMock(\TYPO3\CMS\Workspaces\ExtDirect\ExtDirectServer::class, array('__none'));
}
/**
* Tear down.
*/
protected function tearDown() {
parent::tearDown();
unset($this->subject);
unset($this->fileReferenceProphecies);
}
/**
* @return array
*/
public function prepareFileReferenceDifferencesAreCorrectDataProvider() {
return array(
// without thumbnails
'unchanged wo/thumbnails' => array('1,2,3,4', '1,2,3,4', false, null),
'front addition wo/thumbnails' => array('1,2,3,4', '99,1,2,3,4', false, array(
'live' => '/img/1.png /img/2.png /img/3.png /img/4.png',
'differences' => '<ins>/img/99.png </ins>/img/1.png /img/2.png /img/3.png /img/4.png',
)),
'end addition wo/thumbnails' => array('1,2,3,4', '1,2,3,4,99', false, array(
'live' => '/img/1.png /img/2.png /img/3.png /img/4.png',
'differences' => '/img/1.png /img/2.png /img/3.png /img/4.png <ins>/img/99.png </ins>',
)),
'reorder wo/thumbnails' => array('1,2,3,4', '1,3,2,4', false, array(
'live' => '/img/1.png /img/2.png /img/3.png /img/4.png',
'differences' => '/img/1.png <ins>/img/3.png </ins>/img/2.png <del>/img/3.png </del>/img/4.png',
)),
'move to end wo/thumbnails' => array('1,2,3,4', '2,3,4,1', false, array(
'live' => '/img/1.png /img/2.png /img/3.png /img/4.png',
'differences' => '<del>/img/1.png </del>/img/2.png /img/3.png /img/4.png <ins>/img/1.png </ins>',
)),
'move to front wo/thumbnails' => array('1,2,3,4', '4,1,2,3', false, array(
'live' => '/img/1.png /img/2.png /img/3.png /img/4.png',
'differences' => '<ins>/img/4.png </ins>/img/1.png /img/2.png /img/3.png <del>/img/4.png </del>',
)),
'keep last wo/thumbnails' => array('1,2,3,4', '4', false, array(
'live' => '/img/1.png /img/2.png /img/3.png /img/4.png',
'differences' => '<del>/img/1.png /img/2.png /img/3.png </del>/img/4.png',
)),
// with thumbnails
'unchanged w/thumbnails' => array('1,2,3,4', '1,2,3,4', true, null),
'front addition w/thumbnails' => array('1,2,3,4', '99,1,2,3,4', true, array(
'live' => '<img src="/tmb/1.png" /> <img src="/tmb/2.png" /> <img src="/tmb/3.png" /> <img src="/tmb/4.png" />',
'differences' => '<ins><img src="/tmb/99.png" /> </ins><img src="/tmb/1.png" /> <img src="/tmb/2.png" /> <img src="/tmb/3.png" /> <img src="/tmb/4.png" />',
)),
'end addition w/thumbnails' => array('1,2,3,4', '1,2,3,4,99', true, array(
'live' => '<img src="/tmb/1.png" /> <img src="/tmb/2.png" /> <img src="/tmb/3.png" /> <img src="/tmb/4.png" />',
'differences' => '<img src="/tmb/1.png" /> <img src="/tmb/2.png" /> <img src="/tmb/3.png" /> <img src="/tmb/4.png" /> <ins><img src="/tmb/99.png" /> </ins>',
)),
'reorder w/thumbnails' => array('1,2,3,4', '1,3,2,4', true, array(
'live' => '<img src="/tmb/1.png" /> <img src="/tmb/2.png" /> <img src="/tmb/3.png" /> <img src="/tmb/4.png" />',
'differences' => '<img src="/tmb/1.png" /> <ins><img src="/tmb/3.png" /> </ins><img src="/tmb/2.png" /> <del><img src="/tmb/3.png" /> </del><img src="/tmb/4.png" />',
)),
'move to end w/thumbnails' => array('1,2,3,4', '2,3,4,1', true, array(
'live' => '<img src="/tmb/1.png" /> <img src="/tmb/2.png" /> <img src="/tmb/3.png" /> <img src="/tmb/4.png" />',
'differences' => '<del><img src="/tmb/1.png" /> </del><img src="/tmb/2.png" /> <img src="/tmb/3.png" /> <img src="/tmb/4.png" /> <ins><img src="/tmb/1.png" /> </ins>',
)),
'move to front w/thumbnails' => array('1,2,3,4', '4,1,2,3', true, array(
'live' => '<img src="/tmb/1.png" /> <img src="/tmb/2.png" /> <img src="/tmb/3.png" /> <img src="/tmb/4.png" />',
'differences' => '<ins><img src="/tmb/4.png" /> </ins><img src="/tmb/1.png" /> <img src="/tmb/2.png" /> <img src="/tmb/3.png" /> <del><img src="/tmb/4.png" /> </del>',
)),
'keep last w/thumbnails' => array('1,2,3,4', '4', true, array(
'live' => '<img src="/tmb/1.png" /> <img src="/tmb/2.png" /> <img src="/tmb/3.png" /> <img src="/tmb/4.png" />',
'differences' => '<del><img src="/tmb/1.png" /> <img src="/tmb/2.png" /> <img src="/tmb/3.png" /> </del><img src="/tmb/4.png" />',
)),
);
}
/**
* @param string $fileFileReferenceList
* @param string $versionFileReferenceList
* @param $useThumbnails
* @param array|null $expected
* @dataProvider prepareFileReferenceDifferencesAreCorrectDataProvider
* @test
*/
public function prepareFileReferenceDifferencesAreCorrect($fileFileReferenceList, $versionFileReferenceList, $useThumbnails, array $expected = null) {
$liveFileReferences = $this->getFileReferenceProphecies($fileFileReferenceList);
$versionFileReferences = $this->getFileReferenceProphecies($versionFileReferenceList);
$result = $this->subject->_call(
'prepareFileReferenceDifferences',
$liveFileReferences,
$versionFileReferences,
$useThumbnails
);
$this->assertSame($expected, $result);
}
/**
* @param string $idList List of ids
* @return FileReference[]|ObjectProphecy[]
*/
protected function getFileReferenceProphecies($idList) {
$fileReferenceProphecies = array();
$ids = GeneralUtility::trimExplode(',', $idList, true);
foreach ($ids as $id) {
$fileReferenceProphecies[$id] = $this->getFileReferenceProphecy($id);
}
return $fileReferenceProphecies;
}
/**
* @param int $id
* @return ObjectProphecy|FileReference
*/
protected function getFileReferenceProphecy($id) {
if (isset($this->fileReferenceProphecies[$id])) {
return $this->fileReferenceProphecies[$id];
}
$processedFileProphecy = $this->prophesize(ProcessedFile::class);
$processedFileProphecy->getPublicUrl(Argument::cetera())->willReturn('/tmb/' . $id . '.png');
$fileProphecy = $this->prophesize(File::class);
$fileProphecy->process(Argument::cetera())->willReturn($processedFileProphecy->reveal());
$fileReferenceProphecy = $this->prophesize(FileReference::class);
$fileReferenceProphecy->getUid()->willReturn($id);
$fileReferenceProphecy->getOriginalFile()->willReturn($fileProphecy->reveal());
$fileReferenceProphecy->getPublicUrl(Argument::cetera())->willReturn('/img/' . $id . '.png');
$this->fileReferenceProphecies[$id] = $fileReferenceProphecy->reveal();
return $this->fileReferenceProphecies[$id];
}
}
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment