diff options
Diffstat (limited to 'application/History.php')
-rw-r--r-- | application/History.php | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/application/History.php b/application/History.php new file mode 100644 index 00000000..c06067df --- /dev/null +++ b/application/History.php | |||
@@ -0,0 +1,183 @@ | |||
1 | <?php | ||
2 | |||
3 | /** | ||
4 | * Class History | ||
5 | * | ||
6 | * Handle the history file tracing events in Shaarli. | ||
7 | * The history is stored as JSON in a file set by 'resource.history' setting. | ||
8 | * | ||
9 | * Available data: | ||
10 | * - event: event key | ||
11 | * - datetime: event date, in ISO8601 format. | ||
12 | * - id: event item identifier (currently only link IDs). | ||
13 | * | ||
14 | * Available event keys: | ||
15 | * - CREATED: new link | ||
16 | * - UPDATED: link updated | ||
17 | * - DELETED: link deleted | ||
18 | * - SETTINGS: the settings have been updated through the UI. | ||
19 | * | ||
20 | * Note: new events are put at the beginning of the file and history array. | ||
21 | */ | ||
22 | class History | ||
23 | { | ||
24 | /** | ||
25 | * @var string Action key: a new link has been created. | ||
26 | */ | ||
27 | const CREATED = 'CREATED'; | ||
28 | |||
29 | /** | ||
30 | * @var string Action key: a link has been updated. | ||
31 | */ | ||
32 | const UPDATED = 'UPDATED'; | ||
33 | |||
34 | /** | ||
35 | * @var string Action key: a link has been deleted. | ||
36 | */ | ||
37 | const DELETED = 'DELETED'; | ||
38 | |||
39 | /** | ||
40 | * @var string Action key: settings have been updated. | ||
41 | */ | ||
42 | const SETTINGS = 'SETTINGS'; | ||
43 | |||
44 | /** | ||
45 | * @var string History file path. | ||
46 | */ | ||
47 | protected $historyFilePath; | ||
48 | |||
49 | /** | ||
50 | * @var array History data. | ||
51 | */ | ||
52 | protected $history; | ||
53 | |||
54 | /** | ||
55 | * @var int History retention time in seconds (1 month). | ||
56 | */ | ||
57 | protected $retentionTime = 2678400; | ||
58 | |||
59 | /** | ||
60 | * History constructor. | ||
61 | * | ||
62 | * @param string $historyFilePath History file path. | ||
63 | * @param int $retentionTime History content rentention time in seconds. | ||
64 | * | ||
65 | * @throws Exception if something goes wrong. | ||
66 | */ | ||
67 | public function __construct($historyFilePath, $retentionTime = null) | ||
68 | { | ||
69 | $this->historyFilePath = $historyFilePath; | ||
70 | if ($retentionTime !== null) { | ||
71 | $this->retentionTime = $retentionTime; | ||
72 | } | ||
73 | $this->check(); | ||
74 | $this->read(); | ||
75 | } | ||
76 | |||
77 | /** | ||
78 | * Add Event: new link. | ||
79 | * | ||
80 | * @param array $link Link data. | ||
81 | */ | ||
82 | public function addLink($link) | ||
83 | { | ||
84 | $this->addEvent(self::CREATED, $link['id']); | ||
85 | } | ||
86 | |||
87 | /** | ||
88 | * Add Event: update existing link. | ||
89 | * | ||
90 | * @param array $link Link data. | ||
91 | */ | ||
92 | public function updateLink($link) | ||
93 | { | ||
94 | $this->addEvent(self::UPDATED, $link['id']); | ||
95 | } | ||
96 | |||
97 | /** | ||
98 | * Add Event: delete existing link. | ||
99 | * | ||
100 | * @param array $link Link data. | ||
101 | */ | ||
102 | public function deleteLink($link) | ||
103 | { | ||
104 | $this->addEvent(self::DELETED, $link['id']); | ||
105 | } | ||
106 | |||
107 | /** | ||
108 | * Add Event: settings updated. | ||
109 | */ | ||
110 | public function updateSettings() | ||
111 | { | ||
112 | $this->addEvent(self::SETTINGS); | ||
113 | } | ||
114 | |||
115 | /** | ||
116 | * Save a new event and write it in the history file. | ||
117 | * | ||
118 | * @param string $status Event key, should be defined as constant. | ||
119 | * @param mixed $id Event item identifier (e.g. link ID). | ||
120 | */ | ||
121 | protected function addEvent($status, $id = null) | ||
122 | { | ||
123 | $item = [ | ||
124 | 'event' => $status, | ||
125 | 'datetime' => (new DateTime())->format(DateTime::ATOM), | ||
126 | 'id' => $id !== null ? $id : '', | ||
127 | ]; | ||
128 | $this->history = array_merge([$item], $this->history); | ||
129 | $this->write(); | ||
130 | } | ||
131 | |||
132 | /** | ||
133 | * Check that the history file is writable. | ||
134 | * Create the file if it doesn't exist. | ||
135 | * | ||
136 | * @throws Exception if it isn't writable. | ||
137 | */ | ||
138 | protected function check() | ||
139 | { | ||
140 | if (! is_file($this->historyFilePath)) { | ||
141 | FileUtils::writeFlatDB($this->historyFilePath, []); | ||
142 | } | ||
143 | |||
144 | if (! is_writable($this->historyFilePath)) { | ||
145 | throw new Exception('History file isn\'t readable or writable'); | ||
146 | } | ||
147 | } | ||
148 | |||
149 | /** | ||
150 | * Read JSON history file. | ||
151 | */ | ||
152 | protected function read() | ||
153 | { | ||
154 | $this->history = FileUtils::readFlatDB($this->historyFilePath, []); | ||
155 | if ($this->history === false) { | ||
156 | throw new Exception('Could not parse history file'); | ||
157 | } | ||
158 | } | ||
159 | |||
160 | /** | ||
161 | * Write JSON history file and delete old entries. | ||
162 | */ | ||
163 | protected function write() | ||
164 | { | ||
165 | $comparaison = new DateTime('-'. $this->retentionTime . ' seconds'); | ||
166 | foreach ($this->history as $key => $value) { | ||
167 | if (DateTime::createFromFormat(DateTime::ATOM, $value['datetime']) < $comparaison) { | ||
168 | unset($this->history[$key]); | ||
169 | } | ||
170 | } | ||
171 | FileUtils::writeFlatDB($this->historyFilePath, array_values($this->history)); | ||
172 | } | ||
173 | |||
174 | /** | ||
175 | * Get the History. | ||
176 | * | ||
177 | * @return array | ||
178 | */ | ||
179 | public function getHistory() | ||
180 | { | ||
181 | return $this->history; | ||
182 | } | ||
183 | } | ||