1<?php
2namespace TYPO3\CMS\Workspaces\Service;
3
4/*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17use TYPO3\CMS\Backend\Backend\Avatar\Avatar;
18use TYPO3\CMS\Backend\History\RecordHistory;
19use TYPO3\CMS\Backend\Utility\BackendUtility;
20use TYPO3\CMS\Core\Localization\LanguageService;
21use TYPO3\CMS\Core\SingletonInterface;
22use TYPO3\CMS\Core\Utility\DiffUtility;
23use TYPO3\CMS\Core\Utility\GeneralUtility;
24
25/**
26 * Service for history
27 */
28class HistoryService implements SingletonInterface
29{
30    /**
31     * @var array
32     */
33    protected $backendUserNames;
34
35    /**
36     * @var array
37     */
38    protected $historyEntries = [];
39
40    /**
41     * @var DiffUtility
42     */
43    protected $differencesObject;
44
45    /**
46     * Creates this object.
47     */
48    public function __construct()
49    {
50        $this->backendUserNames = BackendUtility::getUserNames();
51    }
52
53    /**
54     * Gets the editing history of a record.
55     *
56     * @param string $table Name of the table
57     * @param int $id Uid of the record
58     * @return array Record history entries
59     */
60    public function getHistory($table, $id)
61    {
62        $history = [];
63        $i = 0;
64        foreach ((array)$this->getHistoryEntries($table, $id) as $entry) {
65            if ($i++ > 20) {
66                break;
67            }
68            $history[] = $this->getHistoryEntry($entry);
69        }
70        return $history;
71    }
72
73    /**
74     * Gets the human readable representation of one
75     * record history entry.
76     *
77     * @param array $entry Record history entry
78     * @return array
79     * @see getHistory
80     */
81    protected function getHistoryEntry(array $entry)
82    {
83        if (!empty($entry['action'])) {
84            $differences = $entry['action'];
85        } else {
86            $differences = $this->getDifferences($entry);
87        }
88
89        $avatar = GeneralUtility::makeInstance(Avatar::class);
90        $beUserRecord = BackendUtility::getRecord('be_users', $entry['userid']);
91
92        return [
93            'datetime' => htmlspecialchars(BackendUtility::datetime($entry['tstamp'])),
94            'user' => htmlspecialchars($this->getUserName($entry['userid'])),
95            'user_avatar' => $avatar->render($beUserRecord),
96            'differences' => $differences
97        ];
98    }
99
100    /**
101     * Gets the differences between two record versions out
102     * of one record history entry.
103     *
104     * @param array $entry Record history entry
105     * @return array
106     */
107    protected function getDifferences(array $entry)
108    {
109        $differences = [];
110        $tableName = $entry['tablename'];
111        if (is_array($entry['newRecord'])) {
112            $fields = array_keys($entry['newRecord']);
113            foreach ($fields as $field) {
114                if (!empty($GLOBALS['TCA'][$tableName]['columns'][$field]['config']['type']) && $GLOBALS['TCA'][$tableName]['columns'][$field]['config']['type'] !== 'passthrough') {
115                    // Create diff-result:
116                    $fieldDifferences = $this->getDifferencesObject()->makeDiffDisplay(
117                        BackendUtility::getProcessedValue($tableName, $field, $entry['oldRecord'][$field], 0, true),
118                        BackendUtility::getProcessedValue($tableName, $field, $entry['newRecord'][$field], 0, true)
119                    );
120                    if (!empty($fieldDifferences)) {
121                        $differences[] = [
122                            'label' => $this->getLanguageService()->sL((string)BackendUtility::getItemLabel($tableName, $field)),
123                            'html' => nl2br(trim($fieldDifferences)),
124                        ];
125                    }
126                }
127            }
128        }
129        return $differences;
130    }
131
132    /**
133     * Gets the username of a backend user.
134     *
135     * @param string $user
136     * @return string
137     */
138    protected function getUserName($user)
139    {
140        $userName = 'unknown';
141        if (!empty($this->backendUserNames[$user]['username'])) {
142            $userName = $this->backendUserNames[$user]['username'];
143        }
144        return $userName;
145    }
146
147    /**
148     * Gets an instance of the record history of a record.
149     *
150     * @param string $table Name of the table
151     * @param int $id Uid of the record
152     * @return array
153     */
154    protected function getHistoryEntries($table, $id)
155    {
156        if (!isset($this->historyEntries[$table][$id])) {
157            $historyObject = GeneralUtility::makeInstance(RecordHistory::class);
158            $this->historyEntries[$table][$id] = $historyObject->getHistoryDataForRecord($table, $id);
159        }
160        return $this->historyEntries[$table][$id];
161    }
162
163    /**
164     * Gets an instance of the record differences utility.
165     *
166     * @return DiffUtility
167     */
168    protected function getDifferencesObject()
169    {
170        if (!isset($this->differencesObject)) {
171            $this->differencesObject = GeneralUtility::makeInstance(DiffUtility::class);
172            $this->differencesObject->stripTags = false;
173        }
174        return $this->differencesObject;
175    }
176
177    /**
178     * @return LanguageService
179     */
180    protected function getLanguageService()
181    {
182        return $GLOBALS['LANG'];
183    }
184}
185