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