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