diff options
Diffstat (limited to 'application')
-rw-r--r-- | application/Config.php | 27 | ||||
-rw-r--r-- | application/LinkDB.php | 17 | ||||
-rw-r--r-- | application/LinkFilter.php | 31 | ||||
-rw-r--r-- | application/Updater.php | 243 |
4 files changed, 276 insertions, 42 deletions
diff --git a/application/Config.php b/application/Config.php index 9af5a535..05a59452 100644 --- a/application/Config.php +++ b/application/Config.php | |||
@@ -174,33 +174,6 @@ function load_plugin_parameter_values($plugins, $config) | |||
174 | } | 174 | } |
175 | 175 | ||
176 | /** | 176 | /** |
177 | * Milestone 0.9 - shaarli/Shaarli#41: options.php is not supported anymore. | ||
178 | * ==> if user is loggedIn, merge its content with config.php, then delete options.php. | ||
179 | * | ||
180 | * @param array $config contains all configuration fields. | ||
181 | * @param bool $isLoggedIn true if user is logged in. | ||
182 | * | ||
183 | * @return void | ||
184 | */ | ||
185 | function mergeDeprecatedConfig($config, $isLoggedIn) | ||
186 | { | ||
187 | $config_file = $config['config']['CONFIG_FILE']; | ||
188 | |||
189 | if (is_file($config['config']['DATADIR'].'/options.php') && $isLoggedIn) { | ||
190 | include $config['config']['DATADIR'].'/options.php'; | ||
191 | |||
192 | // Load GLOBALS into config | ||
193 | foreach ($GLOBALS as $key => $value) { | ||
194 | $config[$key] = $value; | ||
195 | } | ||
196 | $config['config']['CONFIG_FILE'] = $config_file; | ||
197 | writeConfig($config, $isLoggedIn); | ||
198 | |||
199 | unlink($config['config']['DATADIR'].'/options.php'); | ||
200 | } | ||
201 | } | ||
202 | |||
203 | /** | ||
204 | * Exception used if a mandatory field is missing in given configuration. | 177 | * Exception used if a mandatory field is missing in given configuration. |
205 | */ | 178 | */ |
206 | class MissingFieldConfigException extends Exception | 179 | class MissingFieldConfigException extends Exception |
diff --git a/application/LinkDB.php b/application/LinkDB.php index 19ca6435..9f4d3e3c 100644 --- a/application/LinkDB.php +++ b/application/LinkDB.php | |||
@@ -260,15 +260,19 @@ You use the community supported version of the original Shaarli project, by Seba | |||
260 | } | 260 | } |
261 | } | 261 | } |
262 | 262 | ||
263 | // Keep the list of the mapping URLs-->linkdate up-to-date. | ||
264 | $this->_urls = array(); | 263 | $this->_urls = array(); |
265 | foreach ($this->_links as $link) { | 264 | foreach ($this->_links as &$link) { |
265 | // Keep the list of the mapping URLs-->linkdate up-to-date. | ||
266 | $this->_urls[$link['url']] = $link['linkdate']; | 266 | $this->_urls[$link['url']] = $link['linkdate']; |
267 | } | ||
268 | 267 | ||
269 | // Escape links data | 268 | // Sanitize data fields. |
270 | foreach($this->_links as &$link) { | ||
271 | sanitizeLink($link); | 269 | sanitizeLink($link); |
270 | |||
271 | // Remove private tags if the user is not logged in. | ||
272 | if (! $this->_loggedIn) { | ||
273 | $link['tags'] = preg_replace('/(^| )\.[^($| )]+/', '', $link['tags']); | ||
274 | } | ||
275 | |||
272 | // Do not use the redirector for internal links (Shaarli note URL starting with a '?'). | 276 | // Do not use the redirector for internal links (Shaarli note URL starting with a '?'). |
273 | if (!empty($this->_redirector) && !startsWith($link['url'], '?')) { | 277 | if (!empty($this->_redirector) && !startsWith($link['url'], '?')) { |
274 | $link['real_url'] = $this->_redirector . urlencode($link['url']); | 278 | $link['real_url'] = $this->_redirector . urlencode($link['url']); |
@@ -343,7 +347,7 @@ You use the community supported version of the original Shaarli project, by Seba | |||
343 | * | 347 | * |
344 | * @return array filtered links | 348 | * @return array filtered links |
345 | */ | 349 | */ |
346 | public function filter($type, $request, $casesensitive = false, $privateonly = false) | 350 | public function filter($type = '', $request = '', $casesensitive = false, $privateonly = false) |
347 | { | 351 | { |
348 | $linkFilter = new LinkFilter($this->_links); | 352 | $linkFilter = new LinkFilter($this->_links); |
349 | $requestFilter = is_array($request) ? implode(' ', $request) : $request; | 353 | $requestFilter = is_array($request) ? implode(' ', $request) : $request; |
@@ -381,6 +385,7 @@ You use the community supported version of the original Shaarli project, by Seba | |||
381 | } | 385 | } |
382 | $linkDays = array_keys($linkDays); | 386 | $linkDays = array_keys($linkDays); |
383 | sort($linkDays); | 387 | sort($linkDays); |
388 | |||
384 | return $linkDays; | 389 | return $linkDays; |
385 | } | 390 | } |
386 | } | 391 | } |
diff --git a/application/LinkFilter.php b/application/LinkFilter.php index b2e6530f..ceb47d16 100644 --- a/application/LinkFilter.php +++ b/application/LinkFilter.php | |||
@@ -209,19 +209,33 @@ class LinkFilter | |||
209 | */ | 209 | */ |
210 | public function filterTags($tags, $casesensitive = false, $privateonly = false) | 210 | public function filterTags($tags, $casesensitive = false, $privateonly = false) |
211 | { | 211 | { |
212 | $searchtags = $this->tagsStrToArray($tags, $casesensitive); | 212 | $searchtags = self::tagsStrToArray($tags, $casesensitive); |
213 | $filtered = array(); | 213 | $filtered = array(); |
214 | if (empty($searchtags)) { | ||
215 | return $filtered; | ||
216 | } | ||
214 | 217 | ||
215 | foreach ($this->links as $l) { | 218 | foreach ($this->links as $link) { |
216 | // ignore non private links when 'privatonly' is on. | 219 | // ignore non private links when 'privatonly' is on. |
217 | if (! $l['private'] && $privateonly === true) { | 220 | if (! $link['private'] && $privateonly === true) { |
218 | continue; | 221 | continue; |
219 | } | 222 | } |
220 | 223 | ||
221 | $linktags = $this->tagsStrToArray($l['tags'], $casesensitive); | 224 | $linktags = self::tagsStrToArray($link['tags'], $casesensitive); |
222 | 225 | ||
223 | if (count(array_intersect($linktags, $searchtags)) == count($searchtags)) { | 226 | $found = true; |
224 | $filtered[$l['linkdate']] = $l; | 227 | for ($i = 0 ; $i < count($searchtags) && $found; $i++) { |
228 | // Exclusive search, quit if tag found. | ||
229 | // Or, tag not found in the link, quit. | ||
230 | if (($searchtags[$i][0] == '-' && in_array(substr($searchtags[$i], 1), $linktags)) | ||
231 | || ($searchtags[$i][0] != '-') && ! in_array($searchtags[$i], $linktags) | ||
232 | ) { | ||
233 | $found = false; | ||
234 | } | ||
235 | } | ||
236 | |||
237 | if ($found) { | ||
238 | $filtered[$link['linkdate']] = $link; | ||
225 | } | 239 | } |
226 | } | 240 | } |
227 | krsort($filtered); | 241 | krsort($filtered); |
@@ -260,19 +274,18 @@ class LinkFilter | |||
260 | * Convert a list of tags (str) to an array. Also | 274 | * Convert a list of tags (str) to an array. Also |
261 | * - handle case sensitivity. | 275 | * - handle case sensitivity. |
262 | * - accepts spaces commas as separator. | 276 | * - accepts spaces commas as separator. |
263 | * - remove private tags for loggedout users. | ||
264 | * | 277 | * |
265 | * @param string $tags string containing a list of tags. | 278 | * @param string $tags string containing a list of tags. |
266 | * @param bool $casesensitive will convert everything to lowercase if false. | 279 | * @param bool $casesensitive will convert everything to lowercase if false. |
267 | * | 280 | * |
268 | * @return array filtered tags string. | 281 | * @return array filtered tags string. |
269 | */ | 282 | */ |
270 | public function tagsStrToArray($tags, $casesensitive) | 283 | public static function tagsStrToArray($tags, $casesensitive) |
271 | { | 284 | { |
272 | // We use UTF-8 conversion to handle various graphemes (i.e. cyrillic, or greek) | 285 | // We use UTF-8 conversion to handle various graphemes (i.e. cyrillic, or greek) |
273 | $tagsOut = $casesensitive ? $tags : mb_convert_case($tags, MB_CASE_LOWER, 'UTF-8'); | 286 | $tagsOut = $casesensitive ? $tags : mb_convert_case($tags, MB_CASE_LOWER, 'UTF-8'); |
274 | $tagsOut = str_replace(',', ' ', $tagsOut); | 287 | $tagsOut = str_replace(',', ' ', $tagsOut); |
275 | 288 | ||
276 | return explode(' ', trim($tagsOut)); | 289 | return array_filter(explode(' ', trim($tagsOut)), 'strlen'); |
277 | } | 290 | } |
278 | } | 291 | } |
diff --git a/application/Updater.php b/application/Updater.php new file mode 100644 index 00000000..773a1ffa --- /dev/null +++ b/application/Updater.php | |||
@@ -0,0 +1,243 @@ | |||
1 | <?php | ||
2 | |||
3 | /** | ||
4 | * Class Updater. | ||
5 | * Used to update stuff when a new Shaarli's version is reached. | ||
6 | * Update methods are ran only once, and the stored in a JSON file. | ||
7 | */ | ||
8 | class Updater | ||
9 | { | ||
10 | /** | ||
11 | * @var array Updates which are already done. | ||
12 | */ | ||
13 | protected $doneUpdates; | ||
14 | |||
15 | /** | ||
16 | * @var array Shaarli's configuration array. | ||
17 | */ | ||
18 | protected $config; | ||
19 | |||
20 | /** | ||
21 | * @var LinkDB instance. | ||
22 | */ | ||
23 | protected $linkDB; | ||
24 | |||
25 | /** | ||
26 | * @var bool True if the user is logged in, false otherwise. | ||
27 | */ | ||
28 | protected $isLoggedIn; | ||
29 | |||
30 | /** | ||
31 | * @var ReflectionMethod[] List of current class methods. | ||
32 | */ | ||
33 | protected $methods; | ||
34 | |||
35 | /** | ||
36 | * Object constructor. | ||
37 | * | ||
38 | * @param array $doneUpdates Updates which are already done. | ||
39 | * @param array $config Shaarli's configuration array. | ||
40 | * @param LinkDB $linkDB LinkDB instance. | ||
41 | * @param boolean $isLoggedIn True if the user is logged in. | ||
42 | */ | ||
43 | public function __construct($doneUpdates, $config, $linkDB, $isLoggedIn) | ||
44 | { | ||
45 | $this->doneUpdates = $doneUpdates; | ||
46 | $this->config = $config; | ||
47 | $this->linkDB = $linkDB; | ||
48 | $this->isLoggedIn = $isLoggedIn; | ||
49 | |||
50 | // Retrieve all update methods. | ||
51 | $class = new ReflectionClass($this); | ||
52 | $this->methods = $class->getMethods(); | ||
53 | } | ||
54 | |||
55 | /** | ||
56 | * Run all new updates. | ||
57 | * Update methods have to start with 'updateMethod' and return true (on success). | ||
58 | * | ||
59 | * @return array An array containing ran updates. | ||
60 | * | ||
61 | * @throws UpdaterException If something went wrong. | ||
62 | */ | ||
63 | public function update() | ||
64 | { | ||
65 | $updatesRan = array(); | ||
66 | |||
67 | // If the user isn't logged in, exit without updating. | ||
68 | if ($this->isLoggedIn !== true) { | ||
69 | return $updatesRan; | ||
70 | } | ||
71 | |||
72 | if ($this->methods == null) { | ||
73 | throw new UpdaterException('Couldn\'t retrieve Updater class methods.'); | ||
74 | } | ||
75 | |||
76 | foreach ($this->methods as $method) { | ||
77 | // Not an update method or already done, pass. | ||
78 | if (! startsWith($method->getName(), 'updateMethod') | ||
79 | || in_array($method->getName(), $this->doneUpdates) | ||
80 | ) { | ||
81 | continue; | ||
82 | } | ||
83 | |||
84 | try { | ||
85 | $method->setAccessible(true); | ||
86 | $res = $method->invoke($this); | ||
87 | // Update method must return true to be considered processed. | ||
88 | if ($res === true) { | ||
89 | $updatesRan[] = $method->getName(); | ||
90 | } | ||
91 | } catch (Exception $e) { | ||
92 | throw new UpdaterException($method, $e); | ||
93 | } | ||
94 | } | ||
95 | |||
96 | $this->doneUpdates = array_merge($this->doneUpdates, $updatesRan); | ||
97 | |||
98 | return $updatesRan; | ||
99 | } | ||
100 | |||
101 | /** | ||
102 | * @return array Updates methods already processed. | ||
103 | */ | ||
104 | public function getDoneUpdates() | ||
105 | { | ||
106 | return $this->doneUpdates; | ||
107 | } | ||
108 | |||
109 | /** | ||
110 | * Move deprecated options.php to config.php. | ||
111 | * | ||
112 | * Milestone 0.9 (old versioning) - shaarli/Shaarli#41: | ||
113 | * options.php is not supported anymore. | ||
114 | */ | ||
115 | public function updateMethodMergeDeprecatedConfigFile() | ||
116 | { | ||
117 | $config_file = $this->config['config']['CONFIG_FILE']; | ||
118 | |||
119 | if (is_file($this->config['config']['DATADIR'].'/options.php')) { | ||
120 | include $this->config['config']['DATADIR'].'/options.php'; | ||
121 | |||
122 | // Load GLOBALS into config | ||
123 | foreach ($GLOBALS as $key => $value) { | ||
124 | $this->config[$key] = $value; | ||
125 | } | ||
126 | $this->config['config']['CONFIG_FILE'] = $config_file; | ||
127 | writeConfig($this->config, $this->isLoggedIn); | ||
128 | |||
129 | unlink($this->config['config']['DATADIR'].'/options.php'); | ||
130 | } | ||
131 | |||
132 | return true; | ||
133 | } | ||
134 | |||
135 | /** | ||
136 | * Rename tags starting with a '-' to work with tag exclusion search. | ||
137 | */ | ||
138 | public function updateMethodRenameDashTags() | ||
139 | { | ||
140 | $linklist = $this->linkDB->filter(); | ||
141 | foreach ($linklist as $link) { | ||
142 | $link['tags'] = preg_replace('/(^| )\-/', '$1', $link['tags']); | ||
143 | $link['tags'] = implode(' ', array_unique(LinkFilter::tagsStrToArray($link['tags'], true))); | ||
144 | $this->linkDB[$link['linkdate']] = $link; | ||
145 | } | ||
146 | $this->linkDB->savedb($this->config['config']['PAGECACHE']); | ||
147 | return true; | ||
148 | } | ||
149 | } | ||
150 | |||
151 | /** | ||
152 | * Class UpdaterException. | ||
153 | */ | ||
154 | class UpdaterException extends Exception | ||
155 | { | ||
156 | /** | ||
157 | * @var string Method where the error occurred. | ||
158 | */ | ||
159 | protected $method; | ||
160 | |||
161 | /** | ||
162 | * @var Exception The parent exception. | ||
163 | */ | ||
164 | protected $previous; | ||
165 | |||
166 | /** | ||
167 | * Constructor. | ||
168 | * | ||
169 | * @param string $message Force the error message if set. | ||
170 | * @param string $method Method where the error occurred. | ||
171 | * @param Exception|bool $previous Parent exception. | ||
172 | */ | ||
173 | public function __construct($message = '', $method = '', $previous = false) | ||
174 | { | ||
175 | $this->method = $method; | ||
176 | $this->previous = $previous; | ||
177 | $this->message = $this->buildMessage($message); | ||
178 | } | ||
179 | |||
180 | /** | ||
181 | * Build the exception error message. | ||
182 | * | ||
183 | * @param string $message Optional given error message. | ||
184 | * | ||
185 | * @return string The built error message. | ||
186 | */ | ||
187 | private function buildMessage($message) | ||
188 | { | ||
189 | $out = ''; | ||
190 | if (! empty($message)) { | ||
191 | $out .= $message . PHP_EOL; | ||
192 | } | ||
193 | |||
194 | if (! empty($this->method)) { | ||
195 | $out .= 'An error occurred while running the update '. $this->method . PHP_EOL; | ||
196 | } | ||
197 | |||
198 | if (! empty($this->previous)) { | ||
199 | $out .= ' '. $this->previous->getMessage(); | ||
200 | } | ||
201 | |||
202 | return $out; | ||
203 | } | ||
204 | } | ||
205 | |||
206 | |||
207 | /** | ||
208 | * Read the updates file, and return already done updates. | ||
209 | * | ||
210 | * @param string $updatesFilepath Updates file path. | ||
211 | * | ||
212 | * @return array Already done update methods. | ||
213 | */ | ||
214 | function read_updates_file($updatesFilepath) | ||
215 | { | ||
216 | if (! empty($updatesFilepath) && is_file($updatesFilepath)) { | ||
217 | $content = file_get_contents($updatesFilepath); | ||
218 | if (! empty($content)) { | ||
219 | return explode(';', $content); | ||
220 | } | ||
221 | } | ||
222 | return array(); | ||
223 | } | ||
224 | |||
225 | /** | ||
226 | * Write updates file. | ||
227 | * | ||
228 | * @param string $updatesFilepath Updates file path. | ||
229 | * @param array $updates Updates array to write. | ||
230 | * | ||
231 | * @throws Exception Couldn't write version number. | ||
232 | */ | ||
233 | function write_updates_file($updatesFilepath, $updates) | ||
234 | { | ||
235 | if (empty($updatesFilepath)) { | ||
236 | throw new Exception('Updates file path is not set, can\'t write updates.'); | ||
237 | } | ||
238 | |||
239 | $res = file_put_contents($updatesFilepath, implode(';', $updates)); | ||
240 | if ($res === false) { | ||
241 | throw new Exception('Unable to write updates in '. $updatesFilepath . '.'); | ||
242 | } | ||
243 | } | ||