aboutsummaryrefslogtreecommitdiffhomepage
path: root/application
diff options
context:
space:
mode:
authorArthurHoaro <arthur@hoa.ro>2017-01-16 12:31:08 +0100
committerArthurHoaro <arthur@hoa.ro>2017-03-21 20:29:20 +0100
commit4306b184c4471825f916d895b047ed03fdf58985 (patch)
treeae4ffd760d74e58bf469f743076aecf838f634f2 /application
parentb2306b0c783365e3f8110ae25bc93f2630b8b2c8 (diff)
downloadShaarli-4306b184c4471825f916d895b047ed03fdf58985.tar.gz
Shaarli-4306b184c4471825f916d895b047ed03fdf58985.tar.zst
Shaarli-4306b184c4471825f916d895b047ed03fdf58985.zip
History mechanism
Use case: rest API service * saved by default in data/history * same format as datastore.php * traced events: * save/edit/delete link * change settings or plugins settings * rename tag
Diffstat (limited to 'application')
-rw-r--r--application/History.php183
-rw-r--r--application/NetscapeBookmarkUtils.php5
-rw-r--r--application/config/ConfigManager.php1
3 files changed, 188 insertions, 1 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 */
22class 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}
diff --git a/application/NetscapeBookmarkUtils.php b/application/NetscapeBookmarkUtils.php
index ab346f81..bbfde138 100644
--- a/application/NetscapeBookmarkUtils.php
+++ b/application/NetscapeBookmarkUtils.php
@@ -95,10 +95,11 @@ class NetscapeBookmarkUtils
95 * @param array $files Server $_FILES parameters 95 * @param array $files Server $_FILES parameters
96 * @param LinkDB $linkDb Loaded LinkDB instance 96 * @param LinkDB $linkDb Loaded LinkDB instance
97 * @param ConfigManager $conf instance 97 * @param ConfigManager $conf instance
98 * @param History $history History instance
98 * 99 *
99 * @return string Summary of the bookmark import status 100 * @return string Summary of the bookmark import status
100 */ 101 */
101 public static function import($post, $files, $linkDb, $conf) 102 public static function import($post, $files, $linkDb, $conf, $history)
102 { 103 {
103 $filename = $files['filetoupload']['name']; 104 $filename = $files['filetoupload']['name'];
104 $filesize = $files['filetoupload']['size']; 105 $filesize = $files['filetoupload']['size'];
@@ -182,6 +183,7 @@ class NetscapeBookmarkUtils
182 $linkDb[$existingLink['id']] = $newLink; 183 $linkDb[$existingLink['id']] = $newLink;
183 $importCount++; 184 $importCount++;
184 $overwriteCount++; 185 $overwriteCount++;
186 $history->updateLink($newLink);
185 continue; 187 continue;
186 } 188 }
187 189
@@ -193,6 +195,7 @@ class NetscapeBookmarkUtils
193 $newLink['shorturl'] = link_small_hash($newLink['created'], $newLink['id']); 195 $newLink['shorturl'] = link_small_hash($newLink['created'], $newLink['id']);
194 $linkDb[$newLink['id']] = $newLink; 196 $linkDb[$newLink['id']] = $newLink;
195 $importCount++; 197 $importCount++;
198 $history->addLink($newLink);
196 } 199 }
197 200
198 $linkDb->save($conf->get('resource.page_cache')); 201 $linkDb->save($conf->get('resource.page_cache'));
diff --git a/application/config/ConfigManager.php b/application/config/ConfigManager.php
index 7bfbfc72..86a917fb 100644
--- a/application/config/ConfigManager.php
+++ b/application/config/ConfigManager.php
@@ -301,6 +301,7 @@ class ConfigManager
301 $this->setEmpty('resource.updates', 'data/updates.txt'); 301 $this->setEmpty('resource.updates', 'data/updates.txt');
302 $this->setEmpty('resource.log', 'data/log.txt'); 302 $this->setEmpty('resource.log', 'data/log.txt');
303 $this->setEmpty('resource.update_check', 'data/lastupdatecheck.txt'); 303 $this->setEmpty('resource.update_check', 'data/lastupdatecheck.txt');
304 $this->setEmpty('resource.history', 'data/history.php');
304 $this->setEmpty('resource.raintpl_tpl', 'tpl/'); 305 $this->setEmpty('resource.raintpl_tpl', 'tpl/');
305 $this->setEmpty('resource.theme', 'default'); 306 $this->setEmpty('resource.theme', 'default');
306 $this->setEmpty('resource.raintpl_tmp', 'tmp/'); 307 $this->setEmpty('resource.raintpl_tmp', 'tmp/');