]> git.immae.eu Git - github/shaarli/Shaarli.git/blob - application/History.php
Merge pull request #1698 from ArthurHoaro/feature/plugins-search-filter
[github/shaarli/Shaarli.git] / application / History.php
1 <?php
2 namespace Shaarli;
3
4 use DateTime;
5 use Exception;
6
7 /**
8 * Class History
9 *
10 * Handle the history file tracing events in Shaarli.
11 * The history is stored as JSON in a file set by 'resource.history' setting.
12 *
13 * Available data:
14 * - event: event key
15 * - datetime: event date, in ISO8601 format.
16 * - id: event item identifier (currently only link IDs).
17 *
18 * Available event keys:
19 * - CREATED: new link
20 * - UPDATED: link updated
21 * - DELETED: link deleted
22 * - SETTINGS: the settings have been updated through the UI.
23 * - IMPORT: bulk links import
24 *
25 * Note: new events are put at the beginning of the file and history array.
26 */
27 class History
28 {
29 /**
30 * @var string Action key: a new link has been created.
31 */
32 const CREATED = 'CREATED';
33
34 /**
35 * @var string Action key: a link has been updated.
36 */
37 const UPDATED = 'UPDATED';
38
39 /**
40 * @var string Action key: a link has been deleted.
41 */
42 const DELETED = 'DELETED';
43
44 /**
45 * @var string Action key: settings have been updated.
46 */
47 const SETTINGS = 'SETTINGS';
48
49 /**
50 * @var string Action key: a bulk import has been processed.
51 */
52 const IMPORT = 'IMPORT';
53
54 /**
55 * @var string History file path.
56 */
57 protected $historyFilePath;
58
59 /**
60 * @var array History data.
61 */
62 protected $history;
63
64 /**
65 * @var int History retention time in seconds (1 month).
66 */
67 protected $retentionTime = 2678400;
68
69 /**
70 * History constructor.
71 *
72 * @param string $historyFilePath History file path.
73 * @param int $retentionTime History content retention time in seconds.
74 *
75 * @throws Exception if something goes wrong.
76 */
77 public function __construct($historyFilePath, $retentionTime = null)
78 {
79 $this->historyFilePath = $historyFilePath;
80 if ($retentionTime !== null) {
81 $this->retentionTime = $retentionTime;
82 }
83 }
84
85 /**
86 * Initialize: read history file.
87 *
88 * Allow lazy loading (don't read the file if it isn't necessary).
89 */
90 protected function initialize()
91 {
92 $this->check();
93 $this->read();
94 }
95
96 /**
97 * Add Event: new link.
98 *
99 * @param array $link Link data.
100 */
101 public function addLink($link)
102 {
103 $this->addEvent(self::CREATED, $link['id']);
104 }
105
106 /**
107 * Add Event: update existing link.
108 *
109 * @param array $link Link data.
110 */
111 public function updateLink($link)
112 {
113 $this->addEvent(self::UPDATED, $link['id']);
114 }
115
116 /**
117 * Add Event: delete existing link.
118 *
119 * @param array $link Link data.
120 */
121 public function deleteLink($link)
122 {
123 $this->addEvent(self::DELETED, $link['id']);
124 }
125
126 /**
127 * Add Event: settings updated.
128 */
129 public function updateSettings()
130 {
131 $this->addEvent(self::SETTINGS);
132 }
133
134 /**
135 * Add Event: bulk import.
136 *
137 * Note: we don't store links add/update one by one since it can have a huge impact on performances.
138 */
139 public function importLinks()
140 {
141 $this->addEvent(self::IMPORT);
142 }
143
144 /**
145 * Save a new event and write it in the history file.
146 *
147 * @param string $status Event key, should be defined as constant.
148 * @param mixed $id Event item identifier (e.g. link ID).
149 */
150 protected function addEvent($status, $id = null)
151 {
152 if ($this->history === null) {
153 $this->initialize();
154 }
155
156 $item = [
157 'event' => $status,
158 'datetime' => new DateTime(),
159 'id' => $id !== null ? $id : '',
160 ];
161 $this->history = array_merge([$item], $this->history);
162 $this->write();
163 }
164
165 /**
166 * Check that the history file is writable.
167 * Create the file if it doesn't exist.
168 *
169 * @throws Exception if it isn't writable.
170 */
171 protected function check()
172 {
173 if (!is_file($this->historyFilePath)) {
174 FileUtils::writeFlatDB($this->historyFilePath, []);
175 }
176
177 if (!is_writable($this->historyFilePath)) {
178 throw new Exception(t('History file isn\'t readable or writable'));
179 }
180 }
181
182 /**
183 * Read JSON history file.
184 */
185 protected function read()
186 {
187 $this->history = FileUtils::readFlatDB($this->historyFilePath, []);
188 if ($this->history === false) {
189 throw new Exception(t('Could not parse history file'));
190 }
191 }
192
193 /**
194 * Write JSON history file and delete old entries.
195 */
196 protected function write()
197 {
198 $comparaison = new DateTime('-' . $this->retentionTime . ' seconds');
199 foreach ($this->history as $key => $value) {
200 if ($value['datetime'] < $comparaison) {
201 unset($this->history[$key]);
202 }
203 }
204 FileUtils::writeFlatDB($this->historyFilePath, array_values($this->history));
205 }
206
207 /**
208 * Get the History.
209 *
210 * @return array
211 */
212 public function getHistory()
213 {
214 if ($this->history === null) {
215 $this->initialize();
216 }
217
218 return $this->history;
219 }
220 }