diff options
Diffstat (limited to 'inc')
-rwxr-xr-x | inc/poche/Database.class.php | 601 | ||||
-rw-r--r-- | inc/poche/FlattrItem.class.php | 57 | ||||
-rw-r--r-- | inc/poche/Language.class.php | 114 | ||||
-rwxr-xr-x | inc/poche/Poche.class.php | 859 | ||||
-rwxr-xr-x | inc/poche/Routing.class.php | 161 | ||||
-rw-r--r-- | inc/poche/Template.class.php | 235 | ||||
-rwxr-xr-x | inc/poche/Tools.class.php | 420 | ||||
-rw-r--r-- | inc/poche/Url.class.php | 31 | ||||
-rw-r--r-- | inc/poche/User.class.php | 57 | ||||
-rw-r--r-- | inc/poche/WallabagEBooks.class.php | 245 | ||||
-rwxr-xr-x | inc/poche/config.inc.default.php | 78 | ||||
-rwxr-xr-x | inc/poche/global.inc.php | 43 | ||||
-rw-r--r-- | inc/poche/pochePictures.php | 168 |
13 files changed, 0 insertions, 3069 deletions
diff --git a/inc/poche/Database.class.php b/inc/poche/Database.class.php deleted file mode 100755 index f6ba4708..00000000 --- a/inc/poche/Database.class.php +++ /dev/null | |||
@@ -1,601 +0,0 @@ | |||
1 | <?php | ||
2 | /** | ||
3 | * wallabag, self hostable application allowing you to not miss any content anymore | ||
4 | * | ||
5 | * @category wallabag | ||
6 | * @author Nicolas Lœuillet <nicolas@loeuillet.org> | ||
7 | * @copyright 2013 | ||
8 | * @license http://opensource.org/licenses/MIT see COPYING file | ||
9 | */ | ||
10 | |||
11 | class Database { | ||
12 | |||
13 | var $handle; | ||
14 | private $order = array ( | ||
15 | 'ia' => 'ORDER BY entries.id', | ||
16 | 'id' => 'ORDER BY entries.id DESC', | ||
17 | 'ta' => 'ORDER BY lower(entries.title)', | ||
18 | 'td' => 'ORDER BY lower(entries.title) DESC', | ||
19 | 'default' => 'ORDER BY entries.id' | ||
20 | ); | ||
21 | |||
22 | function __construct() | ||
23 | { | ||
24 | switch (STORAGE) { | ||
25 | case 'sqlite': | ||
26 | // Check if /db is writeable | ||
27 | if ( !is_writable(STORAGE_SQLITE) || !is_writable(dirname(STORAGE_SQLITE))) { | ||
28 | die('An error occured: "db" directory must be writeable for your web server user!'); | ||
29 | } | ||
30 | $db_path = 'sqlite:' . STORAGE_SQLITE; | ||
31 | $this->handle = new PDO($db_path); | ||
32 | break; | ||
33 | case 'mysql': | ||
34 | $db_path = 'mysql:host=' . STORAGE_SERVER . ';dbname=' . STORAGE_DB . ';charset=utf8mb4'; | ||
35 | $this->handle = new PDO($db_path, STORAGE_USER, STORAGE_PASSWORD, array( | ||
36 | PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4', | ||
37 | )); | ||
38 | break; | ||
39 | case 'postgres': | ||
40 | $db_path = 'pgsql:host=' . STORAGE_SERVER . ';dbname=' . STORAGE_DB; | ||
41 | $this->handle = new PDO($db_path, STORAGE_USER, STORAGE_PASSWORD); | ||
42 | break; | ||
43 | default: | ||
44 | die(STORAGE . ' is not a recognised database system !'); | ||
45 | } | ||
46 | |||
47 | $this->handle->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); | ||
48 | $this->_checkTags(); | ||
49 | Tools::logm('storage type ' . STORAGE); | ||
50 | } | ||
51 | |||
52 | private function getHandle() | ||
53 | { | ||
54 | return $this->handle; | ||
55 | } | ||
56 | |||
57 | private function _checkTags() | ||
58 | { | ||
59 | |||
60 | if (STORAGE == 'sqlite') { | ||
61 | $sql = ' | ||
62 | CREATE TABLE IF NOT EXISTS tags ( | ||
63 | id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE, | ||
64 | value TEXT | ||
65 | )'; | ||
66 | } | ||
67 | elseif(STORAGE == 'mysql') { | ||
68 | $sql = ' | ||
69 | CREATE TABLE IF NOT EXISTS `tags` ( | ||
70 | `id` int(11) NOT NULL AUTO_INCREMENT, | ||
71 | `value` varchar(255) NOT NULL, | ||
72 | PRIMARY KEY (`id`) | ||
73 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; | ||
74 | '; | ||
75 | } | ||
76 | else { | ||
77 | $sql = ' | ||
78 | CREATE TABLE IF NOT EXISTS tags ( | ||
79 | id bigserial primary key, | ||
80 | value varchar(255) NOT NULL | ||
81 | ); | ||
82 | '; | ||
83 | } | ||
84 | |||
85 | $query = $this->executeQuery($sql, array()); | ||
86 | |||
87 | if (STORAGE == 'sqlite') { | ||
88 | $sql = ' | ||
89 | CREATE TABLE IF NOT EXISTS tags_entries ( | ||
90 | id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE, | ||
91 | entry_id INTEGER, | ||
92 | tag_id INTEGER, | ||
93 | FOREIGN KEY(entry_id) REFERENCES entries(id) ON DELETE CASCADE, | ||
94 | FOREIGN KEY(tag_id) REFERENCES tags(id) ON DELETE CASCADE | ||
95 | )'; | ||
96 | } | ||
97 | elseif(STORAGE == 'mysql') { | ||
98 | $sql = ' | ||
99 | CREATE TABLE IF NOT EXISTS `tags_entries` ( | ||
100 | `id` int(11) NOT NULL AUTO_INCREMENT, | ||
101 | `entry_id` int(11) NOT NULL, | ||
102 | `tag_id` int(11) NOT NULL, | ||
103 | FOREIGN KEY(entry_id) REFERENCES entries(id) ON DELETE CASCADE, | ||
104 | FOREIGN KEY(tag_id) REFERENCES tags(id) ON DELETE CASCADE, | ||
105 | PRIMARY KEY (`id`) | ||
106 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; | ||
107 | '; | ||
108 | } | ||
109 | else { | ||
110 | $sql = ' | ||
111 | CREATE TABLE IF NOT EXISTS tags_entries ( | ||
112 | id bigserial primary key, | ||
113 | entry_id integer NOT NULL, | ||
114 | tag_id integer NOT NULL | ||
115 | ) | ||
116 | '; | ||
117 | } | ||
118 | |||
119 | $query = $this->executeQuery($sql, array()); | ||
120 | } | ||
121 | |||
122 | public function install($login, $password, $email = '') | ||
123 | { | ||
124 | $sql = 'INSERT INTO users ( username, password, name, email) VALUES (?, ?, ?, ?)'; | ||
125 | $params = array($login, $password, $login, $email); | ||
126 | $query = $this->executeQuery($sql, $params); | ||
127 | |||
128 | $sequence = ''; | ||
129 | if (STORAGE == 'postgres') { | ||
130 | $sequence = 'users_id_seq'; | ||
131 | } | ||
132 | |||
133 | $id_user = intval($this->getLastId($sequence)); | ||
134 | |||
135 | $sql = 'INSERT INTO users_config ( user_id, name, value ) VALUES (?, ?, ?)'; | ||
136 | $params = array($id_user, 'pager', PAGINATION); | ||
137 | $query = $this->executeQuery($sql, $params); | ||
138 | |||
139 | $sql = 'INSERT INTO users_config ( user_id, name, value ) VALUES (?, ?, ?)'; | ||
140 | $params = array($id_user, 'language', LANG); | ||
141 | $query = $this->executeQuery($sql, $params); | ||
142 | |||
143 | $sql = 'INSERT INTO users_config ( user_id, name, value ) VALUES (?, ?, ?)'; | ||
144 | $params = array($id_user, 'theme', DEFAULT_THEME); | ||
145 | $query = $this->executeQuery($sql, $params); | ||
146 | |||
147 | return TRUE; | ||
148 | } | ||
149 | |||
150 | public function getConfigUser($id) | ||
151 | { | ||
152 | $sql = "SELECT * FROM users_config WHERE user_id = ?"; | ||
153 | $query = $this->executeQuery($sql, array($id)); | ||
154 | $result = $query->fetchAll(); | ||
155 | $user_config = array(); | ||
156 | |||
157 | foreach ($result as $key => $value) { | ||
158 | $user_config[$value['name']] = $value['value']; | ||
159 | } | ||
160 | |||
161 | return $user_config; | ||
162 | } | ||
163 | |||
164 | public function userExists($username) | ||
165 | { | ||
166 | $sql = "SELECT * FROM users WHERE username=?"; | ||
167 | $query = $this->executeQuery($sql, array($username)); | ||
168 | $login = $query->fetchAll(); | ||
169 | if (isset($login[0])) { | ||
170 | return true; | ||
171 | } else { | ||
172 | return false; | ||
173 | } | ||
174 | } | ||
175 | |||
176 | public function login($username, $password, $isauthenticated = FALSE) | ||
177 | { | ||
178 | if ($isauthenticated) { | ||
179 | $sql = "SELECT * FROM users WHERE username=?"; | ||
180 | $query = $this->executeQuery($sql, array($username)); | ||
181 | } else { | ||
182 | $sql = "SELECT * FROM users WHERE username=? AND password=?"; | ||
183 | $query = $this->executeQuery($sql, array($username, $password)); | ||
184 | } | ||
185 | $login = $query->fetchAll(); | ||
186 | |||
187 | $user = array(); | ||
188 | if (isset($login[0])) { | ||
189 | $user['id'] = $login[0]['id']; | ||
190 | $user['username'] = $login[0]['username']; | ||
191 | $user['password'] = $login[0]['password']; | ||
192 | $user['name'] = $login[0]['name']; | ||
193 | $user['email'] = $login[0]['email']; | ||
194 | $user['config'] = $this->getConfigUser($login[0]['id']); | ||
195 | } | ||
196 | |||
197 | return $user; | ||
198 | } | ||
199 | |||
200 | public function updatePassword($userId, $password) | ||
201 | { | ||
202 | $sql_update = "UPDATE users SET password=? WHERE id=?"; | ||
203 | $params_update = array($password, $userId); | ||
204 | $query = $this->executeQuery($sql_update, $params_update); | ||
205 | } | ||
206 | |||
207 | public function updateUserConfig($userId, $key, $value) | ||
208 | { | ||
209 | $config = $this->getConfigUser($userId); | ||
210 | |||
211 | if (! isset($config[$key])) { | ||
212 | $sql = "INSERT INTO users_config (value, user_id, name) VALUES (?, ?, ?)"; | ||
213 | } | ||
214 | else { | ||
215 | $sql = "UPDATE users_config SET value=? WHERE user_id=? AND name=?"; | ||
216 | } | ||
217 | |||
218 | $params = array($value, $userId, $key); | ||
219 | $query = $this->executeQuery($sql, $params); | ||
220 | } | ||
221 | |||
222 | private function executeQuery($sql, $params) | ||
223 | { | ||
224 | try | ||
225 | { | ||
226 | $query = $this->getHandle()->prepare($sql); | ||
227 | $query->execute($params); | ||
228 | return $query; | ||
229 | } | ||
230 | catch (Exception $e) | ||
231 | { | ||
232 | Tools::logm('execute query error : '.$e->getMessage()); | ||
233 | return FALSE; | ||
234 | } | ||
235 | } | ||
236 | |||
237 | public function listUsers($username = NULL) | ||
238 | { | ||
239 | $sql = 'SELECT count(*) FROM users'.( $username ? ' WHERE username=?' : ''); | ||
240 | $query = $this->executeQuery($sql, ( $username ? array($username) : array())); | ||
241 | list($count) = $query->fetch(); | ||
242 | return $count; | ||
243 | } | ||
244 | |||
245 | public function getUserPassword($userID) | ||
246 | { | ||
247 | $sql = "SELECT * FROM users WHERE id=?"; | ||
248 | $query = $this->executeQuery($sql, array($userID)); | ||
249 | $password = $query->fetchAll(); | ||
250 | return isset($password[0]['password']) ? $password[0]['password'] : null; | ||
251 | } | ||
252 | |||
253 | public function deleteUserConfig($userID) | ||
254 | { | ||
255 | $sql_action = 'DELETE from users_config WHERE user_id=?'; | ||
256 | $params_action = array($userID); | ||
257 | $query = $this->executeQuery($sql_action, $params_action); | ||
258 | return $query; | ||
259 | } | ||
260 | |||
261 | public function deleteTagsEntriesAndEntries($userID) | ||
262 | { | ||
263 | $entries = $this->retrieveAll($userID); | ||
264 | foreach($entries as $entryid) { | ||
265 | $tags = $this->retrieveTagsByEntry($entryid); | ||
266 | foreach($tags as $tag) { | ||
267 | $this->removeTagForEntry($entryid,$tags); | ||
268 | } | ||
269 | $this->deleteById($entryid,$userID); | ||
270 | } | ||
271 | } | ||
272 | |||
273 | public function deleteUser($userID) | ||
274 | { | ||
275 | $sql_action = 'DELETE from users WHERE id=?'; | ||
276 | $params_action = array($userID); | ||
277 | $query = $this->executeQuery($sql_action, $params_action); | ||
278 | } | ||
279 | |||
280 | public function updateContentAndTitle($id, $title, $body, $user_id) | ||
281 | { | ||
282 | $sql_action = 'UPDATE entries SET content = ?, title = ? WHERE id=? AND user_id=?'; | ||
283 | $params_action = array($body, $title, $id, $user_id); | ||
284 | $query = $this->executeQuery($sql_action, $params_action); | ||
285 | return $query; | ||
286 | } | ||
287 | |||
288 | public function retrieveUnfetchedEntries($user_id, $limit) | ||
289 | { | ||
290 | |||
291 | $sql_limit = "LIMIT 0,".$limit; | ||
292 | if (STORAGE == 'postgres') { | ||
293 | $sql_limit = "LIMIT ".$limit." OFFSET 0"; | ||
294 | } | ||
295 | |||
296 | $sql = "SELECT * FROM entries WHERE (content = '' OR content IS NULL) AND title LIKE 'Untitled - Import%' AND user_id=? ORDER BY id " . $sql_limit; | ||
297 | $query = $this->executeQuery($sql, array($user_id)); | ||
298 | $entries = $query->fetchAll(); | ||
299 | |||
300 | return $entries; | ||
301 | } | ||
302 | |||
303 | public function retrieveUnfetchedEntriesCount($user_id) | ||
304 | { | ||
305 | $sql = "SELECT count(*) FROM entries WHERE (content = '' OR content IS NULL) AND title LIKE 'Untitled - Import%' AND user_id=?"; | ||
306 | $query = $this->executeQuery($sql, array($user_id)); | ||
307 | list($count) = $query->fetch(); | ||
308 | |||
309 | return $count; | ||
310 | } | ||
311 | |||
312 | public function retrieveAll($user_id) | ||
313 | { | ||
314 | $sql = "SELECT * FROM entries WHERE user_id=? ORDER BY id"; | ||
315 | $query = $this->executeQuery($sql, array($user_id)); | ||
316 | $entries = $query->fetchAll(); | ||
317 | |||
318 | return $entries; | ||
319 | } | ||
320 | |||
321 | public function retrieveOneById($id, $user_id) | ||
322 | { | ||
323 | $entry = NULL; | ||
324 | $sql = "SELECT * FROM entries WHERE id=? AND user_id=?"; | ||
325 | $params = array(intval($id), $user_id); | ||
326 | $query = $this->executeQuery($sql, $params); | ||
327 | $entry = $query->fetchAll(); | ||
328 | |||
329 | return isset($entry[0]) ? $entry[0] : null; | ||
330 | } | ||
331 | |||
332 | public function retrieveOneByURL($url, $user_id) | ||
333 | { | ||
334 | $entry = NULL; | ||
335 | $sql = "SELECT * FROM entries WHERE url=? AND user_id=?"; | ||
336 | $params = array($url, $user_id); | ||
337 | $query = $this->executeQuery($sql, $params); | ||
338 | $entry = $query->fetchAll(); | ||
339 | |||
340 | return isset($entry[0]) ? $entry[0] : null; | ||
341 | } | ||
342 | |||
343 | public function reassignTags($old_entry_id, $new_entry_id) | ||
344 | { | ||
345 | $sql = "UPDATE tags_entries SET entry_id=? WHERE entry_id=?"; | ||
346 | $params = array($new_entry_id, $old_entry_id); | ||
347 | $query = $this->executeQuery($sql, $params); | ||
348 | } | ||
349 | |||
350 | public function getEntriesByView($view, $user_id, $limit = '', $tag_id = 0) | ||
351 | { | ||
352 | switch ($view) { | ||
353 | case 'archive': | ||
354 | $sql = "SELECT * FROM entries WHERE user_id=? AND is_read=? "; | ||
355 | $params = array($user_id, 1); | ||
356 | break; | ||
357 | case 'fav' : | ||
358 | $sql = "SELECT * FROM entries WHERE user_id=? AND is_fav=? "; | ||
359 | $params = array($user_id, 1); | ||
360 | break; | ||
361 | case 'tag' : | ||
362 | $sql = "SELECT entries.* FROM entries | ||
363 | LEFT JOIN tags_entries ON tags_entries.entry_id=entries.id | ||
364 | WHERE entries.user_id=? AND tags_entries.tag_id = ? "; | ||
365 | $params = array($user_id, $tag_id); | ||
366 | break; | ||
367 | default: | ||
368 | $sql = "SELECT * FROM entries WHERE user_id=? AND is_read=? "; | ||
369 | $params = array($user_id, 0); | ||
370 | break; | ||
371 | } | ||
372 | |||
373 | $sql .= $this->getEntriesOrder().' ' . $limit; | ||
374 | |||
375 | $query = $this->executeQuery($sql, $params); | ||
376 | $entries = $query->fetchAll(); | ||
377 | |||
378 | return $entries; | ||
379 | } | ||
380 | |||
381 | public function getEntriesByViewCount($view, $user_id, $tag_id = 0) | ||
382 | { | ||
383 | switch ($view) { | ||
384 | case 'archive': | ||
385 | $sql = "SELECT count(*) FROM entries WHERE user_id=? AND is_read=? "; | ||
386 | $params = array($user_id, 1); | ||
387 | break; | ||
388 | case 'fav' : | ||
389 | $sql = "SELECT count(*) FROM entries WHERE user_id=? AND is_fav=? "; | ||
390 | $params = array($user_id, 1); | ||
391 | break; | ||
392 | case 'tag' : | ||
393 | $sql = "SELECT count(*) FROM entries | ||
394 | LEFT JOIN tags_entries ON tags_entries.entry_id=entries.id | ||
395 | WHERE entries.user_id=? AND tags_entries.tag_id = ? "; | ||
396 | $params = array($user_id, $tag_id); | ||
397 | break; | ||
398 | default: | ||
399 | $sql = "SELECT count(*) FROM entries WHERE user_id=? AND is_read=? "; | ||
400 | $params = array($user_id, 0); | ||
401 | break; | ||
402 | } | ||
403 | |||
404 | $query = $this->executeQuery($sql, $params); | ||
405 | list($count) = $query->fetch(); | ||
406 | |||
407 | return $count; | ||
408 | } | ||
409 | |||
410 | public function updateContent($id, $content, $user_id) | ||
411 | { | ||
412 | $sql_action = 'UPDATE entries SET content = ? WHERE id=? AND user_id=?'; | ||
413 | $params_action = array($content, $id, $user_id); | ||
414 | $query = $this->executeQuery($sql_action, $params_action); | ||
415 | return $query; | ||
416 | } | ||
417 | |||
418 | /** | ||
419 | * | ||
420 | * @param string $url | ||
421 | * @param string $title | ||
422 | * @param string $content | ||
423 | * @param integer $user_id | ||
424 | * @return integer $id of inserted record | ||
425 | */ | ||
426 | public function add($url, $title, $content, $user_id, $isFavorite=0, $isRead=0) | ||
427 | { | ||
428 | $sql_action = 'INSERT INTO entries ( url, title, content, user_id, is_fav, is_read ) VALUES (?, ?, ?, ?, ?, ?)'; | ||
429 | $params_action = array($url, $title, $content, $user_id, $isFavorite, $isRead); | ||
430 | |||
431 | if ( !$this->executeQuery($sql_action, $params_action) ) { | ||
432 | $id = null; | ||
433 | } | ||
434 | else { | ||
435 | $id = intval($this->getLastId( (STORAGE == 'postgres') ? 'entries_id_seq' : '') ); | ||
436 | } | ||
437 | return $id; | ||
438 | } | ||
439 | |||
440 | public function deleteById($id, $user_id) | ||
441 | { | ||
442 | $sql_action = "DELETE FROM entries WHERE id=? AND user_id=?"; | ||
443 | $params_action = array($id, $user_id); | ||
444 | $query = $this->executeQuery($sql_action, $params_action); | ||
445 | return $query; | ||
446 | } | ||
447 | |||
448 | public function favoriteById($id, $user_id) | ||
449 | { | ||
450 | $sql_action = "UPDATE entries SET is_fav=NOT is_fav WHERE id=? AND user_id=?"; | ||
451 | $params_action = array($id, $user_id); | ||
452 | $query = $this->executeQuery($sql_action, $params_action); | ||
453 | } | ||
454 | |||
455 | public function archiveById($id, $user_id) | ||
456 | { | ||
457 | $sql_action = "UPDATE entries SET is_read=NOT is_read WHERE id=? AND user_id=?"; | ||
458 | $params_action = array($id, $user_id); | ||
459 | $query = $this->executeQuery($sql_action, $params_action); | ||
460 | } | ||
461 | |||
462 | public function archiveAll($user_id) | ||
463 | { | ||
464 | $sql_action = "UPDATE entries SET is_read=? WHERE user_id=? AND is_read=?"; | ||
465 | $params_action = array($user_id, 1, 0); | ||
466 | $query = $this->executeQuery($sql_action, $params_action); | ||
467 | } | ||
468 | |||
469 | public function getLastId($column = '') | ||
470 | { | ||
471 | return $this->getHandle()->lastInsertId($column); | ||
472 | } | ||
473 | |||
474 | public function search($term, $user_id, $limit = '') | ||
475 | { | ||
476 | $search = '%'.$term.'%'; | ||
477 | $sql_action = "SELECT * FROM entries WHERE user_id=? AND (content LIKE ? OR title LIKE ? OR url LIKE ?) "; //searches in content, title and URL | ||
478 | $sql_action .= $this->getEntriesOrder().' ' . $limit; | ||
479 | $params_action = array($user_id, $search, $search, $search); | ||
480 | $query = $this->executeQuery($sql_action, $params_action); | ||
481 | return $query->fetchAll(); | ||
482 | } | ||
483 | |||
484 | public function retrieveAllTags($user_id, $term = NULL) | ||
485 | { | ||
486 | $sql = "SELECT DISTINCT tags.*, count(entries.id) AS entriescount FROM tags | ||
487 | LEFT JOIN tags_entries ON tags_entries.tag_id=tags.id | ||
488 | LEFT JOIN entries ON tags_entries.entry_id=entries.id | ||
489 | WHERE entries.user_id=? | ||
490 | ". (($term) ? "AND lower(tags.value) LIKE ?" : '') ." | ||
491 | GROUP BY tags.id, tags.value | ||
492 | ORDER BY tags.value"; | ||
493 | $query = $this->executeQuery($sql, (($term)? array($user_id, strtolower('%'.$term.'%')) : array($user_id) )); | ||
494 | $tags = $query->fetchAll(); | ||
495 | |||
496 | return $tags; | ||
497 | } | ||
498 | |||
499 | public function retrieveTag($id, $user_id) | ||
500 | { | ||
501 | $tag = NULL; | ||
502 | $sql = "SELECT DISTINCT tags.* FROM tags | ||
503 | LEFT JOIN tags_entries ON tags_entries.tag_id=tags.id | ||
504 | LEFT JOIN entries ON tags_entries.entry_id=entries.id | ||
505 | WHERE tags.id=? AND entries.user_id=?"; | ||
506 | $params = array(intval($id), $user_id); | ||
507 | $query = $this->executeQuery($sql, $params); | ||
508 | $tag = $query->fetchAll(); | ||
509 | |||
510 | return isset($tag[0]) ? $tag[0] : NULL; | ||
511 | } | ||
512 | |||
513 | public function retrieveEntriesByTag($tag_id, $user_id) | ||
514 | { | ||
515 | $sql = | ||
516 | "SELECT entries.* FROM entries | ||
517 | LEFT JOIN tags_entries ON tags_entries.entry_id=entries.id | ||
518 | WHERE tags_entries.tag_id = ? AND entries.user_id=? ORDER by entries.id DESC"; | ||
519 | $query = $this->executeQuery($sql, array($tag_id, $user_id)); | ||
520 | $entries = $query->fetchAll(); | ||
521 | |||
522 | return $entries; | ||
523 | } | ||
524 | |||
525 | public function retrieveTagsByEntry($entry_id) | ||
526 | { | ||
527 | $sql = | ||
528 | "SELECT tags.* FROM tags | ||
529 | LEFT JOIN tags_entries ON tags_entries.tag_id=tags.id | ||
530 | WHERE tags_entries.entry_id = ?"; | ||
531 | $query = $this->executeQuery($sql, array($entry_id)); | ||
532 | $tags = $query->fetchAll(); | ||
533 | |||
534 | return $tags; | ||
535 | } | ||
536 | |||
537 | public function removeTagForEntry($entry_id, $tag_id) | ||
538 | { | ||
539 | $sql_action = "DELETE FROM tags_entries WHERE tag_id=? AND entry_id=?"; | ||
540 | $params_action = array($tag_id, $entry_id); | ||
541 | $query = $this->executeQuery($sql_action, $params_action); | ||
542 | return $query; | ||
543 | } | ||
544 | |||
545 | public function cleanUnusedTag($tag_id) | ||
546 | { | ||
547 | $sql_action = "SELECT tags.* FROM tags JOIN tags_entries ON tags_entries.tag_id=tags.id WHERE tags.id=?"; | ||
548 | $query = $this->executeQuery($sql_action,array($tag_id)); | ||
549 | $tagstokeep = $query->fetchAll(); | ||
550 | $sql_action = "SELECT tags.* FROM tags LEFT JOIN tags_entries ON tags_entries.tag_id=tags.id WHERE tags.id=?"; | ||
551 | $query = $this->executeQuery($sql_action,array($tag_id)); | ||
552 | $alltags = $query->fetchAll(); | ||
553 | |||
554 | foreach ($alltags as $tag) { | ||
555 | if ($tag && !in_array($tag,$tagstokeep)) { | ||
556 | $sql_action = "DELETE FROM tags WHERE id=?"; | ||
557 | $params_action = array($tag[0]); | ||
558 | $this->executeQuery($sql_action, $params_action); | ||
559 | return true; | ||
560 | } | ||
561 | } | ||
562 | |||
563 | } | ||
564 | |||
565 | public function retrieveTagByValue($value) | ||
566 | { | ||
567 | $tag = NULL; | ||
568 | $sql = "SELECT * FROM tags WHERE value=?"; | ||
569 | $params = array($value); | ||
570 | $query = $this->executeQuery($sql, $params); | ||
571 | $tag = $query->fetchAll(); | ||
572 | |||
573 | return isset($tag[0]) ? $tag[0] : null; | ||
574 | } | ||
575 | |||
576 | public function createTag($value) | ||
577 | { | ||
578 | $sql_action = 'INSERT INTO tags ( value ) VALUES (?)'; | ||
579 | $params_action = array($value); | ||
580 | $query = $this->executeQuery($sql_action, $params_action); | ||
581 | return $query; | ||
582 | } | ||
583 | |||
584 | public function setTagToEntry($tag_id, $entry_id) | ||
585 | { | ||
586 | $sql_action = 'INSERT INTO tags_entries ( tag_id, entry_id ) VALUES (?, ?)'; | ||
587 | $params_action = array($tag_id, $entry_id); | ||
588 | $query = $this->executeQuery($sql_action, $params_action); | ||
589 | return $query; | ||
590 | } | ||
591 | |||
592 | private function getEntriesOrder() | ||
593 | { | ||
594 | if (isset($_SESSION['sort']) and array_key_exists($_SESSION['sort'], $this->order)) { | ||
595 | return $this->order[$_SESSION['sort']]; | ||
596 | } | ||
597 | else { | ||
598 | return $this->order['default']; | ||
599 | } | ||
600 | } | ||
601 | } | ||
diff --git a/inc/poche/FlattrItem.class.php b/inc/poche/FlattrItem.class.php deleted file mode 100644 index ef8c62f7..00000000 --- a/inc/poche/FlattrItem.class.php +++ /dev/null | |||
@@ -1,57 +0,0 @@ | |||
1 | <?php | ||
2 | /** | ||
3 | * wallabag, self hostable application allowing you to not miss any content anymore | ||
4 | * | ||
5 | * @category wallabag | ||
6 | * @author Nicolas Lœuillet <nicolas@loeuillet.org> | ||
7 | * @copyright 2013 | ||
8 | * @license http://opensource.org/licenses/MIT see COPYING file | ||
9 | */ | ||
10 | |||
11 | class FlattrItem | ||
12 | { | ||
13 | public $status; | ||
14 | public $urlToFlattr; | ||
15 | public $flattrItemURL; | ||
16 | public $numFlattrs; | ||
17 | |||
18 | public function checkItem($urlToFlattr, $id) | ||
19 | { | ||
20 | $this->_cacheFlattrFile($urlToFlattr, $id); | ||
21 | $flattrResponse = file_get_contents(CACHE . "/flattr/".$id.".cache"); | ||
22 | if($flattrResponse != FALSE) { | ||
23 | $result = json_decode($flattrResponse); | ||
24 | if (isset($result->message)) { | ||
25 | if ($result->message == "flattrable") { | ||
26 | $this->status = FLATTRABLE; | ||
27 | } | ||
28 | } | ||
29 | elseif (is_object($result) && $result->link) { | ||
30 | $this->status = FLATTRED; | ||
31 | $this->flattrItemURL = $result->link; | ||
32 | $this->numFlattrs = $result->flattrs; | ||
33 | } | ||
34 | else { | ||
35 | $this->status = NOT_FLATTRABLE; | ||
36 | } | ||
37 | } | ||
38 | else { | ||
39 | $this->status = "FLATTR_ERR_CONNECTION"; | ||
40 | } | ||
41 | } | ||
42 | |||
43 | private function _cacheFlattrFile($urlToFlattr, $id) | ||
44 | { | ||
45 | if (!is_dir(CACHE . '/flattr')) { | ||
46 | mkdir(CACHE . '/flattr', 0777); | ||
47 | } | ||
48 | |||
49 | // if a cache flattr file for this url already exists and it's been less than one day than it have been updated, see in /cache | ||
50 | if ((!file_exists(CACHE . "/flattr/".$id.".cache")) || (time() - filemtime(CACHE . "/flattr/".$id.".cache") > 86400)) { | ||
51 | $askForFlattr = Tools::getFile(FLATTR_API . $urlToFlattr); | ||
52 | $flattrCacheFile = fopen(CACHE . "/flattr/".$id.".cache", 'w+'); | ||
53 | fwrite($flattrCacheFile, $askForFlattr); | ||
54 | fclose($flattrCacheFile); | ||
55 | } | ||
56 | } | ||
57 | } | ||
diff --git a/inc/poche/Language.class.php b/inc/poche/Language.class.php deleted file mode 100644 index 420f2fb9..00000000 --- a/inc/poche/Language.class.php +++ /dev/null | |||
@@ -1,114 +0,0 @@ | |||
1 | <?php | ||
2 | /** | ||
3 | * wallabag, self hostable application allowing you to not miss any content anymore | ||
4 | * | ||
5 | * @category wallabag | ||
6 | * @author Nicolas Lœuillet <nicolas@loeuillet.org> | ||
7 | * @copyright 2013 | ||
8 | * @license http://opensource.org/licenses/MIT see COPYING file | ||
9 | */ | ||
10 | |||
11 | class Language | ||
12 | { | ||
13 | protected $wallabag; | ||
14 | |||
15 | private $currentLanguage; | ||
16 | |||
17 | private $languageNames = array( | ||
18 | 'cs_CZ.utf8' => 'čeština', | ||
19 | 'de_DE.utf8' => 'German', | ||
20 | 'en_EN.utf8' => 'English', | ||
21 | 'en_US.utf8' => 'English (US)', | ||
22 | 'es_ES.utf8' => 'Español', | ||
23 | 'fa_IR.utf8' => 'فارسی', | ||
24 | 'fr_FR.utf8' => 'Français', | ||
25 | 'it_IT.utf8' => 'Italiano', | ||
26 | 'pl_PL.utf8' => 'Polski', | ||
27 | 'pt_BR.utf8' => 'Português (Brasil)', | ||
28 | 'ru_RU.utf8' => 'Pусский', | ||
29 | 'sl_SI.utf8' => 'Slovenščina', | ||
30 | 'uk_UA.utf8' => 'Українська', | ||
31 | ); | ||
32 | |||
33 | public function __construct(Poche $wallabag) | ||
34 | { | ||
35 | $this->wallabag = $wallabag; | ||
36 | $pocheUser = Session::getParam('poche_user'); | ||
37 | $language = (is_null($pocheUser) ? LANG : $pocheUser->getConfigValue('language')); | ||
38 | |||
39 | @putenv('LC_ALL=' . $language); | ||
40 | setlocale(LC_ALL, $language); | ||
41 | bindtextdomain($language, LOCALE); | ||
42 | textdomain($language); | ||
43 | |||
44 | $this->currentLanguage = $language; | ||
45 | } | ||
46 | |||
47 | public function getLanguage() { | ||
48 | return $this->currentLanguage; | ||
49 | } | ||
50 | |||
51 | public function getInstalledLanguages() { | ||
52 | $handle = opendir(LOCALE); | ||
53 | $languages = array(); | ||
54 | |||
55 | while (($language = readdir($handle)) !== false) { | ||
56 | # Languages are stored in a directory, so all directory names are languages | ||
57 | # @todo move language installation data to database | ||
58 | if (! is_dir(LOCALE . '/' . $language) || in_array($language, array('..', '.', 'tools'))) { | ||
59 | continue; | ||
60 | } | ||
61 | |||
62 | $current = false; | ||
63 | |||
64 | if ($language === $this->getLanguage()) { | ||
65 | $current = true; | ||
66 | } | ||
67 | |||
68 | $languages[] = array('name' => (isset($this->languageNames[$language]) ? $this->languageNames[$language] : $language), 'value' => $language, 'current' => $current); | ||
69 | } | ||
70 | |||
71 | return $languages; | ||
72 | } | ||
73 | |||
74 | |||
75 | /** | ||
76 | * Update language for current user | ||
77 | * | ||
78 | * @param $newLanguage | ||
79 | */ | ||
80 | public function updateLanguage($newLanguage) | ||
81 | { | ||
82 | # we are not going to change it to the current language | ||
83 | if ($newLanguage == $this->getLanguage()) { | ||
84 | $this->wallabag->messages->add('w', _('still using the "' . $this->getLanguage() . '" language!')); | ||
85 | Tools::redirect('?view=config'); | ||
86 | } | ||
87 | |||
88 | $languages = $this->getInstalledLanguages(); | ||
89 | $actualLanguage = false; | ||
90 | |||
91 | foreach ($languages as $language) { | ||
92 | if ($language['value'] == $newLanguage) { | ||
93 | $actualLanguage = true; | ||
94 | break; | ||
95 | } | ||
96 | } | ||
97 | |||
98 | if (!$actualLanguage) { | ||
99 | $this->wallabag->messages->add('e', _('that language does not seem to be installed')); | ||
100 | Tools::redirect('?view=config'); | ||
101 | } | ||
102 | |||
103 | $this->wallabag->store->updateUserConfig($this->wallabag->user->getId(), 'language', $newLanguage); | ||
104 | $this->wallabag->messages->add('s', _('you have changed your language preferences')); | ||
105 | |||
106 | $currentConfig = $_SESSION['poche_user']->config; | ||
107 | $currentConfig['language'] = $newLanguage; | ||
108 | |||
109 | $_SESSION['poche_user']->setConfig($currentConfig); | ||
110 | |||
111 | Tools::emptyCache(); | ||
112 | Tools::redirect('?view=config'); | ||
113 | } | ||
114 | } | ||
diff --git a/inc/poche/Poche.class.php b/inc/poche/Poche.class.php deleted file mode 100755 index 8b0d3a19..00000000 --- a/inc/poche/Poche.class.php +++ /dev/null | |||
@@ -1,859 +0,0 @@ | |||
1 | <?php | ||
2 | /** | ||
3 | * wallabag, self hostable application allowing you to not miss any content anymore | ||
4 | * | ||
5 | * @category wallabag | ||
6 | * @author Nicolas Lœuillet <nicolas@loeuillet.org> | ||
7 | * @copyright 2013 | ||
8 | * @license http://opensource.org/licenses/MIT see COPYING file | ||
9 | */ | ||
10 | |||
11 | class Poche | ||
12 | { | ||
13 | /** | ||
14 | * @var User | ||
15 | */ | ||
16 | public $user; | ||
17 | /** | ||
18 | * @var Database | ||
19 | */ | ||
20 | public $store; | ||
21 | /** | ||
22 | * @var Template | ||
23 | */ | ||
24 | public $tpl; | ||
25 | /** | ||
26 | * @var Language | ||
27 | */ | ||
28 | public $language; | ||
29 | /** | ||
30 | * @var Routing | ||
31 | */ | ||
32 | public $routing; | ||
33 | /** | ||
34 | * @var Messages | ||
35 | */ | ||
36 | public $messages; | ||
37 | /** | ||
38 | * @var Paginator | ||
39 | */ | ||
40 | public $pagination; | ||
41 | |||
42 | public function __construct() | ||
43 | { | ||
44 | $this->init(); | ||
45 | } | ||
46 | |||
47 | private function init() | ||
48 | { | ||
49 | Tools::initPhp(); | ||
50 | |||
51 | $pocheUser = Session::getParam('poche_user'); | ||
52 | |||
53 | if ($pocheUser && $pocheUser != array()) { | ||
54 | $this->user = $pocheUser; | ||
55 | } else { | ||
56 | // fake user, just for install & login screens | ||
57 | $this->user = new User(); | ||
58 | $this->user->setConfig($this->getDefaultConfig()); | ||
59 | } | ||
60 | |||
61 | $this->pagination = new Paginator($this->user->getConfigValue('pager'), 'p'); | ||
62 | $this->language = new Language($this); | ||
63 | $this->tpl = new Template($this); | ||
64 | $this->store = new Database(); | ||
65 | $this->messages = new Messages(); | ||
66 | $this->routing = new Routing($this); | ||
67 | } | ||
68 | |||
69 | public function run() | ||
70 | { | ||
71 | $this->routing->run(); | ||
72 | } | ||
73 | |||
74 | /** | ||
75 | * Creates a new user | ||
76 | */ | ||
77 | public function createNewUser($username, $password, $email = "") | ||
78 | { | ||
79 | if (!empty($username) && !empty($password)){ | ||
80 | $newUsername = filter_var($username, FILTER_SANITIZE_STRING); | ||
81 | $email = filter_var($email, FILTER_SANITIZE_STRING); | ||
82 | if (!$this->store->userExists($newUsername)){ | ||
83 | if ($this->store->install($newUsername, Tools::encodeString($password . $newUsername), $email)) { | ||
84 | Tools::logm('The new user ' . $newUsername . ' has been installed'); | ||
85 | $this->messages->add('s', sprintf(_('The new user %s has been installed. Do you want to <a href="?logout">logout ?</a>'), $newUsername)); | ||
86 | Tools::redirect(); | ||
87 | } | ||
88 | else { | ||
89 | Tools::logm('error during adding new user'); | ||
90 | Tools::redirect(); | ||
91 | } | ||
92 | } | ||
93 | else { | ||
94 | $this->messages->add('e', sprintf(_('Error : An user with the name %s already exists !'), $newUsername)); | ||
95 | Tools::logm('An user with the name ' . $newUsername . ' already exists !'); | ||
96 | Tools::redirect(); | ||
97 | } | ||
98 | } | ||
99 | } | ||
100 | |||
101 | /** | ||
102 | * Delete an existing user | ||
103 | */ | ||
104 | public function deleteUser($password) | ||
105 | { | ||
106 | if ($this->store->listUsers() > 1) { | ||
107 | if (Tools::encodeString($password . $this->user->getUsername()) == $this->store->getUserPassword($this->user->getId())) { | ||
108 | $username = $this->user->getUsername(); | ||
109 | $this->store->deleteUserConfig($this->user->getId()); | ||
110 | Tools::logm('The configuration for user '. $username .' has been deleted !'); | ||
111 | $this->store->deleteTagsEntriesAndEntries($this->user->getId()); | ||
112 | Tools::logm('The entries for user '. $username .' has been deleted !'); | ||
113 | $this->store->deleteUser($this->user->getId()); | ||
114 | Tools::logm('User '. $username .' has been completely deleted !'); | ||
115 | Session::logout(); | ||
116 | Tools::logm('logout'); | ||
117 | Tools::redirect(); | ||
118 | $this->messages->add('s', sprintf(_('User %s has been successfully deleted !'), $username)); | ||
119 | } | ||
120 | else { | ||
121 | Tools::logm('Bad password !'); | ||
122 | $this->messages->add('e', _('Error : The password is wrong !')); | ||
123 | } | ||
124 | } | ||
125 | else { | ||
126 | Tools::logm('Only user !'); | ||
127 | $this->messages->add('e', _('Error : You are the only user, you cannot delete your account !')); | ||
128 | } | ||
129 | } | ||
130 | |||
131 | public function getDefaultConfig() | ||
132 | { | ||
133 | return array( | ||
134 | 'pager' => PAGINATION, | ||
135 | 'language' => LANG, | ||
136 | 'theme' => DEFAULT_THEME | ||
137 | ); | ||
138 | } | ||
139 | |||
140 | /** | ||
141 | * Call action (mark as fav, archive, delete, etc.) | ||
142 | */ | ||
143 | public function action($action, Url $url, $id = 0, $import = FALSE, $autoclose = FALSE, $tags = null) | ||
144 | { | ||
145 | switch ($action) | ||
146 | { | ||
147 | case 'add': | ||
148 | $content = Tools::getPageContent($url); | ||
149 | $title = ($content['rss']['channel']['item']['title'] != '') ? $content['rss']['channel']['item']['title'] : _('Untitled'); | ||
150 | $body = $content['rss']['channel']['item']['description']; | ||
151 | |||
152 | // clean content from prevent xss attack | ||
153 | $purifier = $this->_getPurifier(); | ||
154 | $title = $purifier->purify($title); | ||
155 | $body = $purifier->purify($body); | ||
156 | |||
157 | //search for possible duplicate | ||
158 | $duplicate = NULL; | ||
159 | $duplicate = $this->store->retrieveOneByURL($url->getUrl(), $this->user->getId()); | ||
160 | |||
161 | $last_id = $this->store->add($url->getUrl(), $title, $body, $this->user->getId()); | ||
162 | if ( $last_id ) { | ||
163 | Tools::logm('add link ' . $url->getUrl()); | ||
164 | if (DOWNLOAD_PICTURES) { | ||
165 | $content = Picture::filterPicture($body, $url->getUrl(), $last_id); | ||
166 | Tools::logm('updating content article'); | ||
167 | $this->store->updateContent($last_id, $content, $this->user->getId()); | ||
168 | } | ||
169 | |||
170 | if ($duplicate != NULL) { | ||
171 | // duplicate exists, so, older entry needs to be deleted (as new entry should go to the top of list), BUT favorite mark and tags should be preserved | ||
172 | Tools::logm('link ' . $url->getUrl() . ' is a duplicate'); | ||
173 | // 1) - preserve tags and favorite, then drop old entry | ||
174 | $this->store->reassignTags($duplicate['id'], $last_id); | ||
175 | if ($duplicate['is_fav']) { | ||
176 | $this->store->favoriteById($last_id, $this->user->getId()); | ||
177 | } | ||
178 | if ($this->store->deleteById($duplicate['id'], $this->user->getId())) { | ||
179 | Tools::logm('previous link ' . $url->getUrl() .' entry deleted'); | ||
180 | } | ||
181 | } | ||
182 | |||
183 | // if there are tags, add them to the new article | ||
184 | if (isset($_GET['tags'])) { | ||
185 | $_POST['value'] = $_GET['tags']; | ||
186 | $_POST['entry_id'] = $last_id; | ||
187 | $this->action('add_tag', $url); | ||
188 | } | ||
189 | |||
190 | $this->messages->add('s', _('the link has been added successfully')); | ||
191 | } | ||
192 | else { | ||
193 | $this->messages->add('e', _('error during insertion : the link wasn\'t added')); | ||
194 | Tools::logm('error during insertion : the link wasn\'t added ' . $url->getUrl()); | ||
195 | } | ||
196 | |||
197 | if ($autoclose == TRUE) { | ||
198 | Tools::redirect('?view=home'); | ||
199 | } else { | ||
200 | Tools::redirect('?view=home&closewin=true'); | ||
201 | } | ||
202 | break; | ||
203 | case 'delete': | ||
204 | if (isset($_GET['search'])) { | ||
205 | //when we want to apply a delete to a search | ||
206 | $tags = array($_GET['search']); | ||
207 | $allentry_ids = $this->store->search($tags[0], $this->user->getId()); | ||
208 | $entry_ids = array(); | ||
209 | foreach ($allentry_ids as $eachentry) { | ||
210 | $entry_ids[] = $eachentry[0]; | ||
211 | } | ||
212 | } else { // delete a single article | ||
213 | $entry_ids = array($id); | ||
214 | } | ||
215 | foreach($entry_ids as $id) { | ||
216 | $msg = 'delete link #' . $id; | ||
217 | if ($this->store->deleteById($id, $this->user->getId())) { | ||
218 | if (DOWNLOAD_PICTURES) { | ||
219 | Picture::removeDirectory(ABS_PATH . $id); | ||
220 | } | ||
221 | $this->messages->add('s', _('the link has been deleted successfully')); | ||
222 | } | ||
223 | else { | ||
224 | $this->messages->add('e', _('the link wasn\'t deleted')); | ||
225 | $msg = 'error : can\'t delete link #' . $id; | ||
226 | } | ||
227 | Tools::logm($msg); | ||
228 | } | ||
229 | Tools::redirect('?'); | ||
230 | break; | ||
231 | case 'toggle_fav' : | ||
232 | $this->store->favoriteById($id, $this->user->getId()); | ||
233 | Tools::logm('mark as favorite link #' . $id); | ||
234 | if ( Tools::isAjaxRequest() ) { | ||
235 | echo 1; | ||
236 | exit; | ||
237 | } | ||
238 | else { | ||
239 | Tools::redirect(); | ||
240 | } | ||
241 | break; | ||
242 | case 'toggle_archive' : | ||
243 | if (isset($_GET['tag_id'])) { | ||
244 | //when we want to archive a whole tag | ||
245 | $tag_id = $_GET['tag_id']; | ||
246 | $allentry_ids = $this->store->retrieveEntriesByTag($tag_id, $this->user->getId()); | ||
247 | $entry_ids = array(); | ||
248 | foreach ($allentry_ids as $eachentry) { | ||
249 | $entry_ids[] = $eachentry[0]; | ||
250 | } | ||
251 | } else { //archive a single article | ||
252 | $entry_ids = array($id); | ||
253 | } | ||
254 | foreach($entry_ids as $id) { | ||
255 | $this->store->archiveById($id, $this->user->getId()); | ||
256 | Tools::logm('archive link #' . $id); | ||
257 | } | ||
258 | if ( Tools::isAjaxRequest() ) { | ||
259 | echo 1; | ||
260 | exit; | ||
261 | } | ||
262 | else { | ||
263 | Tools::redirect(); | ||
264 | } | ||
265 | break; | ||
266 | case 'archive_all' : | ||
267 | $this->store->archiveAll($this->user->getId()); | ||
268 | Tools::logm('archive all links'); | ||
269 | Tools::redirect(); | ||
270 | break; | ||
271 | case 'add_tag' : | ||
272 | if (isset($_GET['search'])) { | ||
273 | //when we want to apply a tag to a search | ||
274 | $tags = array($_GET['search']); | ||
275 | $allentry_ids = $this->store->search($tags[0], $this->user->getId()); | ||
276 | $entry_ids = array(); | ||
277 | foreach ($allentry_ids as $eachentry) { | ||
278 | $entry_ids[] = $eachentry[0]; | ||
279 | } | ||
280 | } else { //add a tag to a single article | ||
281 | $tags = explode(',', $_POST['value']); | ||
282 | $entry_ids = array($_POST['entry_id']); | ||
283 | } | ||
284 | foreach($entry_ids as $entry_id) { | ||
285 | $entry = $this->store->retrieveOneById($entry_id, $this->user->getId()); | ||
286 | if (!$entry) { | ||
287 | $this->messages->add('e', _('Article not found!')); | ||
288 | Tools::logm('error : article not found'); | ||
289 | Tools::redirect(); | ||
290 | } | ||
291 | //get all already set tags to preven duplicates | ||
292 | $already_set_tags = array(); | ||
293 | $entry_tags = $this->store->retrieveTagsByEntry($entry_id); | ||
294 | foreach ($entry_tags as $tag) { | ||
295 | $already_set_tags[] = $tag['value']; | ||
296 | } | ||
297 | foreach($tags as $key => $tag_value) { | ||
298 | $value = trim($tag_value); | ||
299 | if ($value && !in_array($value, $already_set_tags)) { | ||
300 | $tag = $this->store->retrieveTagByValue($value); | ||
301 | if (is_null($tag)) { | ||
302 | # we create the tag | ||
303 | $tag = $this->store->createTag($value); | ||
304 | $sequence = ''; | ||
305 | if (STORAGE == 'postgres') { | ||
306 | $sequence = 'tags_id_seq'; | ||
307 | } | ||
308 | $tag_id = $this->store->getLastId($sequence); | ||
309 | } | ||
310 | else { | ||
311 | $tag_id = $tag['id']; | ||
312 | } | ||
313 | |||
314 | # we assign the tag to the article | ||
315 | $this->store->setTagToEntry($tag_id, $entry_id); | ||
316 | } | ||
317 | } | ||
318 | } | ||
319 | $this->messages->add('s', _('The tag has been applied successfully')); | ||
320 | Tools::logm('The tag has been applied successfully'); | ||
321 | Tools::redirect(); | ||
322 | break; | ||
323 | case 'remove_tag' : | ||
324 | $tag_id = $_GET['tag_id']; | ||
325 | $entry = $this->store->retrieveOneById($id, $this->user->getId()); | ||
326 | if (!$entry) { | ||
327 | $this->messages->add('e', _('Article not found!')); | ||
328 | Tools::logm('error : article not found'); | ||
329 | Tools::redirect(); | ||
330 | } | ||
331 | $this->store->removeTagForEntry($id, $tag_id); | ||
332 | Tools::logm('tag entry deleted'); | ||
333 | if ($this->store->cleanUnusedTag($tag_id)) { | ||
334 | Tools::logm('tag deleted'); | ||
335 | } | ||
336 | $this->messages->add('s', _('The tag has been successfully deleted')); | ||
337 | Tools::redirect(); | ||
338 | break; | ||
339 | default: | ||
340 | break; | ||
341 | } | ||
342 | } | ||
343 | |||
344 | function displayView($view, $id = 0) | ||
345 | { | ||
346 | $tpl_vars = array(); | ||
347 | |||
348 | switch ($view) | ||
349 | { | ||
350 | case 'about': | ||
351 | break; | ||
352 | case 'config': | ||
353 | $dev_infos = $this->_getPocheVersion('dev'); | ||
354 | $dev = trim($dev_infos[0]); | ||
355 | $check_time_dev = date('d-M-Y H:i', $dev_infos[1]); | ||
356 | $prod_infos = $this->_getPocheVersion('prod'); | ||
357 | $prod = trim($prod_infos[0]); | ||
358 | $check_time_prod = date('d-M-Y H:i', $prod_infos[1]); | ||
359 | $compare_dev = version_compare(POCHE, $dev); | ||
360 | $compare_prod = version_compare(POCHE, $prod); | ||
361 | $themes = $this->tpl->getInstalledThemes(); | ||
362 | $languages = $this->language->getInstalledLanguages(); | ||
363 | $token = $this->user->getConfigValue('token'); | ||
364 | $http_auth = (isset($_SERVER['PHP_AUTH_USER']) || isset($_SERVER['REMOTE_USER'])) ? true : false; | ||
365 | $only_user = ($this->store->listUsers() > 1) ? false : true; | ||
366 | $tpl_vars = array( | ||
367 | 'themes' => $themes, | ||
368 | 'languages' => $languages, | ||
369 | 'dev' => $dev, | ||
370 | 'prod' => $prod, | ||
371 | 'check_time_dev' => $check_time_dev, | ||
372 | 'check_time_prod' => $check_time_prod, | ||
373 | 'compare_dev' => $compare_dev, | ||
374 | 'compare_prod' => $compare_prod, | ||
375 | 'token' => $token, | ||
376 | 'user_id' => $this->user->getId(), | ||
377 | 'http_auth' => $http_auth, | ||
378 | 'only_user' => $only_user | ||
379 | ); | ||
380 | Tools::logm('config view'); | ||
381 | break; | ||
382 | case 'edit-tags': | ||
383 | # tags | ||
384 | $entry = $this->store->retrieveOneById($id, $this->user->getId()); | ||
385 | if (!$entry) { | ||
386 | $this->messages->add('e', _('Article not found!')); | ||
387 | Tools::logm('error : article not found'); | ||
388 | Tools::redirect(); | ||
389 | } | ||
390 | $tags = $this->store->retrieveTagsByEntry($id); | ||
391 | $tpl_vars = array( | ||
392 | 'entry_id' => $id, | ||
393 | 'tags' => $tags, | ||
394 | 'entry' => $entry, | ||
395 | ); | ||
396 | break; | ||
397 | case 'tags': | ||
398 | $token = $this->user->getConfigValue('token'); | ||
399 | //if term is set - search tags for this term | ||
400 | $term = Tools::checkVar('term'); | ||
401 | $tags = $this->store->retrieveAllTags($this->user->getId(), $term); | ||
402 | if (Tools::isAjaxRequest()) { | ||
403 | $result = array(); | ||
404 | foreach ($tags as $tag) { | ||
405 | $result[] = $tag['value']; | ||
406 | } | ||
407 | echo json_encode($result); | ||
408 | exit; | ||
409 | } | ||
410 | $tpl_vars = array( | ||
411 | 'token' => $token, | ||
412 | 'user_id' => $this->user->getId(), | ||
413 | 'tags' => $tags, | ||
414 | ); | ||
415 | break; | ||
416 | case 'search': | ||
417 | if (isset($_GET['search'])) { | ||
418 | $search = filter_var($_GET['search'], FILTER_SANITIZE_STRING); | ||
419 | $tpl_vars['entries'] = $this->store->search($search, $this->user->getId()); | ||
420 | $count = count($tpl_vars['entries']); | ||
421 | $this->pagination->set_total($count); | ||
422 | $page_links = str_replace(array('previous', 'next'), array(_('previous'), _('next')), | ||
423 | $this->pagination->page_links('?view=' . $view . '?search=' . $search . '&sort=' . $_SESSION['sort'] . '&' )); | ||
424 | $tpl_vars['page_links'] = $page_links; | ||
425 | $tpl_vars['nb_results'] = $count; | ||
426 | $tpl_vars['searchterm'] = $search; | ||
427 | } | ||
428 | break; | ||
429 | case 'view': | ||
430 | $entry = $this->store->retrieveOneById($id, $this->user->getId()); | ||
431 | if ($entry != NULL) { | ||
432 | Tools::logm('view link #' . $id); | ||
433 | $content = $entry['content']; | ||
434 | if (function_exists('tidy_parse_string')) { | ||
435 | $tidy = tidy_parse_string($content, array('indent'=>true, 'show-body-only' => true), 'UTF8'); | ||
436 | $tidy->cleanRepair(); | ||
437 | $content = $tidy->value; | ||
438 | } | ||
439 | |||
440 | # flattr checking | ||
441 | $flattr = NULL; | ||
442 | if (FLATTR) { | ||
443 | $flattr = new FlattrItem(); | ||
444 | $flattr->checkItem($entry['url'], $entry['id']); | ||
445 | } | ||
446 | |||
447 | # tags | ||
448 | $tags = $this->store->retrieveTagsByEntry($entry['id']); | ||
449 | |||
450 | $tpl_vars = array( | ||
451 | 'entry' => $entry, | ||
452 | 'content' => $content, | ||
453 | 'flattr' => $flattr, | ||
454 | 'tags' => $tags | ||
455 | ); | ||
456 | } | ||
457 | else { | ||
458 | Tools::logm('error in view call : entry is null'); | ||
459 | } | ||
460 | break; | ||
461 | default: # home, favorites, archive and tag views | ||
462 | $tpl_vars = array( | ||
463 | 'entries' => '', | ||
464 | 'page_links' => '', | ||
465 | 'nb_results' => '', | ||
466 | 'listmode' => (isset($_COOKIE['listmode']) ? true : false), | ||
467 | ); | ||
468 | |||
469 | //if id is given - we retrieve entries by tag: id is tag id | ||
470 | if ($id) { | ||
471 | $tpl_vars['tag'] = $this->store->retrieveTag($id, $this->user->getId()); | ||
472 | $tpl_vars['id'] = intval($id); | ||
473 | } | ||
474 | |||
475 | $count = $this->store->getEntriesByViewCount($view, $this->user->getId(), $id); | ||
476 | |||
477 | if ($count > 0) { | ||
478 | $this->pagination->set_total($count); | ||
479 | $page_links = str_replace(array('previous', 'next'), array(_('previous'), _('next')), | ||
480 | $this->pagination->page_links('?view=' . $view . '&sort=' . $_SESSION['sort'] . (($id)?'&id='.$id:'') . '&' )); | ||
481 | $tpl_vars['entries'] = $this->store->getEntriesByView($view, $this->user->getId(), $this->pagination->get_limit(), $id); | ||
482 | $tpl_vars['page_links'] = $page_links; | ||
483 | $tpl_vars['nb_results'] = $count; | ||
484 | } | ||
485 | Tools::logm('display ' . $view . ' view'); | ||
486 | break; | ||
487 | } | ||
488 | |||
489 | return $tpl_vars; | ||
490 | } | ||
491 | |||
492 | /** | ||
493 | * update the password of the current user. | ||
494 | * if MODE_DEMO is TRUE, the password can't be updated. | ||
495 | * @todo add the return value | ||
496 | * @todo set the new password in function header like this updatePassword($newPassword) | ||
497 | * @return boolean | ||
498 | */ | ||
499 | public function updatePassword($password, $confirmPassword) | ||
500 | { | ||
501 | if (MODE_DEMO) { | ||
502 | $this->messages->add('i', _('in demo mode, you can\'t update your password')); | ||
503 | Tools::logm('in demo mode, you can\'t do this'); | ||
504 | Tools::redirect('?view=config'); | ||
505 | } | ||
506 | else { | ||
507 | if (isset($password) && isset($confirmPassword)) { | ||
508 | if ($password == $confirmPassword && !empty($password)) { | ||
509 | $this->messages->add('s', _('your password has been updated')); | ||
510 | $this->store->updatePassword($this->user->getId(), Tools::encodeString($password . $this->user->getUsername())); | ||
511 | Session::logout(); | ||
512 | Tools::logm('password updated'); | ||
513 | Tools::redirect(); | ||
514 | } | ||
515 | else { | ||
516 | $this->messages->add('e', _('the two fields have to be filled & the password must be the same in the two fields')); | ||
517 | Tools::redirect('?view=config'); | ||
518 | } | ||
519 | } | ||
520 | } | ||
521 | } | ||
522 | |||
523 | /** | ||
524 | * Get credentials from differents sources | ||
525 | * It redirects the user to the $referer link | ||
526 | * | ||
527 | * @return array | ||
528 | */ | ||
529 | private function credentials() | ||
530 | { | ||
531 | if (isset($_SERVER['PHP_AUTH_USER'])) { | ||
532 | return array($_SERVER['PHP_AUTH_USER'], 'php_auth', true); | ||
533 | } | ||
534 | if (!empty($_POST['login']) && !empty($_POST['password'])) { | ||
535 | return array($_POST['login'], $_POST['password'], false); | ||
536 | } | ||
537 | if (isset($_SERVER['REMOTE_USER'])) { | ||
538 | return array($_SERVER['REMOTE_USER'], 'http_auth', true); | ||
539 | } | ||
540 | |||
541 | return array(false, false, false); | ||
542 | } | ||
543 | |||
544 | /** | ||
545 | * checks if login & password are correct and save the user in session. | ||
546 | * it redirects the user to the $referer link | ||
547 | * @param string $referer the url to redirect after login | ||
548 | * @todo add the return value | ||
549 | * @return boolean | ||
550 | */ | ||
551 | public function login($referer) | ||
552 | { | ||
553 | list($login,$password,$isauthenticated)=$this->credentials(); | ||
554 | if($login === false || $password === false) { | ||
555 | $this->messages->add('e', _('login failed: you have to fill all fields')); | ||
556 | Tools::logm('login failed'); | ||
557 | Tools::redirect(); | ||
558 | } | ||
559 | if (!empty($login) && !empty($password)) { | ||
560 | $user = $this->store->login($login, Tools::encodeString($password . $login), $isauthenticated); | ||
561 | if ($user != array()) { | ||
562 | # Save login into Session | ||
563 | $longlastingsession = isset($_POST['longlastingsession']); | ||
564 | $passwordTest = ($isauthenticated) ? $user['password'] : Tools::encodeString($password . $login); | ||
565 | Session::login($user['username'], $user['password'], $login, $passwordTest, $longlastingsession, array('poche_user' => new User($user))); | ||
566 | |||
567 | # reload l10n | ||
568 | $language = $user['config']['language']; | ||
569 | @putenv('LC_ALL=' . $language); | ||
570 | setlocale(LC_ALL, $language); | ||
571 | bindtextdomain($language, LOCALE); | ||
572 | textdomain($language); | ||
573 | |||
574 | $this->messages->add('s', _('welcome to your wallabag')); | ||
575 | Tools::logm('login successful'); | ||
576 | Tools::redirect($referer); | ||
577 | } | ||
578 | $this->messages->add('e', _('login failed: bad login or password')); | ||
579 | // log login failure in web server log to allow fail2ban usage | ||
580 | error_log('user '.$login.' authentication failure'); | ||
581 | Tools::logm('login failed'); | ||
582 | Tools::redirect(); | ||
583 | } | ||
584 | } | ||
585 | |||
586 | /** | ||
587 | * log out the poche user. It cleans the session. | ||
588 | * @todo add the return value | ||
589 | * @return boolean | ||
590 | */ | ||
591 | public function logout() | ||
592 | { | ||
593 | $this->user = array(); | ||
594 | Session::logout(); | ||
595 | Tools::logm('logout'); | ||
596 | Tools::redirect(); | ||
597 | } | ||
598 | |||
599 | /** | ||
600 | * import datas into your wallabag | ||
601 | * @return boolean | ||
602 | */ | ||
603 | |||
604 | public function import() { | ||
605 | |||
606 | if ( isset($_FILES['file']) && $_FILES['file']['tmp_name'] ) { | ||
607 | Tools::logm('Import stated: parsing file'); | ||
608 | |||
609 | // assume, that file is in json format | ||
610 | $str_data = file_get_contents($_FILES['file']['tmp_name']); | ||
611 | $data = json_decode($str_data, true); | ||
612 | |||
613 | if ( $data === null ) { | ||
614 | //not json - assume html | ||
615 | $html = new simple_html_dom(); | ||
616 | $html->load_file($_FILES['file']['tmp_name']); | ||
617 | $data = array(); | ||
618 | $read = 0; | ||
619 | foreach (array('ol','ul') as $list) { | ||
620 | foreach ($html->find($list) as $ul) { | ||
621 | foreach ($ul->find('li') as $li) { | ||
622 | $tmpEntry = array(); | ||
623 | $a = $li->find('a'); | ||
624 | $tmpEntry['url'] = $a[0]->href; | ||
625 | $tmpEntry['tags'] = $a[0]->tags; | ||
626 | $tmpEntry['is_read'] = $read; | ||
627 | if ($tmpEntry['url']) { | ||
628 | $data[] = $tmpEntry; | ||
629 | } | ||
630 | } | ||
631 | # the second <ol/ul> is for read links | ||
632 | $read = ((sizeof($data) && $read)?0:1); | ||
633 | } | ||
634 | } | ||
635 | } | ||
636 | |||
637 | // for readability structure | ||
638 | |||
639 | foreach($data as $record) { | ||
640 | if (is_array($record)) { | ||
641 | $data[] = $record; | ||
642 | foreach($record as $record2) { | ||
643 | if (is_array($record2)) { | ||
644 | $data[] = $record2; | ||
645 | } | ||
646 | } | ||
647 | } | ||
648 | } | ||
649 | |||
650 | $urlsInserted = array(); //urls of articles inserted | ||
651 | foreach($data as $record) { | ||
652 | $url = trim(isset($record['article__url']) ? $record['article__url'] : (isset($record['url']) ? $record['url'] : '')); | ||
653 | if ($url and !in_array($url, $urlsInserted)) { | ||
654 | $title = (isset($record['title']) ? $record['title'] : _('Untitled - Import - ') . '</a> <a href="./?import">' . _('click to finish import') . '</a><a>'); | ||
655 | $body = (isset($record['content']) ? $record['content'] : ''); | ||
656 | $isRead = (isset($record['is_read']) ? intval($record['is_read']) : (isset($record['archive']) ? intval($record['archive']) : 0)); | ||
657 | $isFavorite = (isset($record['is_fav']) ? intval($record['is_fav']) : (isset($record['favorite']) ? intval($record['favorite']) : 0)); | ||
658 | |||
659 | // insert new record | ||
660 | |||
661 | $id = $this->store->add($url, $title, $body, $this->user->getId() , $isFavorite, $isRead); | ||
662 | if ($id) { | ||
663 | $urlsInserted[] = $url; //add | ||
664 | if (isset($record['tags']) && trim($record['tags'])) { | ||
665 | |||
666 | // @TODO: set tags | ||
667 | |||
668 | } | ||
669 | } | ||
670 | } | ||
671 | } | ||
672 | |||
673 | $i = sizeof($urlsInserted); | ||
674 | if ($i > 0) { | ||
675 | $this->messages->add('s', _('Articles inserted: ') . $i . _('. Please note, that some may be marked as "read".')); | ||
676 | } | ||
677 | |||
678 | Tools::logm('Import of articles finished: '.$i.' articles added (w/o content if not provided).'); | ||
679 | } | ||
680 | else { | ||
681 | $this->messages->add('s', _('Did you forget to select a file?')); | ||
682 | } | ||
683 | // file parsing finished here | ||
684 | // now download article contents if any | ||
685 | // check if we need to download any content | ||
686 | |||
687 | $recordsDownloadRequired = $this->store->retrieveUnfetchedEntriesCount($this->user->getId()); | ||
688 | |||
689 | if ($recordsDownloadRequired == 0) { | ||
690 | |||
691 | // nothing to download | ||
692 | |||
693 | $this->messages->add('s', _('Import finished.')); | ||
694 | Tools::logm('Import finished completely'); | ||
695 | Tools::redirect(); | ||
696 | } | ||
697 | else { | ||
698 | |||
699 | // if just inserted - don't download anything, download will start in next reload | ||
700 | |||
701 | if (!isset($_FILES['file'])) { | ||
702 | |||
703 | // download next batch | ||
704 | |||
705 | Tools::logm('Fetching next batch of articles...'); | ||
706 | $items = $this->store->retrieveUnfetchedEntries($this->user->getId() , IMPORT_LIMIT); | ||
707 | $purifier = $this->_getPurifier(); | ||
708 | foreach($items as $item) { | ||
709 | $url = new Url(base64_encode($item['url'])); | ||
710 | Tools::logm('Fetching article ' . $item['id']); | ||
711 | $content = Tools::getPageContent($url); | ||
712 | $title = (($content['rss']['channel']['item']['title'] != '') ? $content['rss']['channel']['item']['title'] : _('Untitled')); | ||
713 | $body = (($content['rss']['channel']['item']['description'] != '') ? $content['rss']['channel']['item']['description'] : _('Undefined')); | ||
714 | |||
715 | // clean content to prevent xss attack | ||
716 | |||
717 | $title = $purifier->purify($title); | ||
718 | $body = $purifier->purify($body); | ||
719 | $this->store->updateContentAndTitle($item['id'], $title, $body, $this->user->getId()); | ||
720 | Tools::logm('Article ' . $item['id'] . ' updated.'); | ||
721 | } | ||
722 | } | ||
723 | } | ||
724 | |||
725 | return array( | ||
726 | 'includeImport' => true, | ||
727 | 'import' => array( | ||
728 | 'recordsDownloadRequired' => $recordsDownloadRequired, | ||
729 | 'recordsUnderDownload' => IMPORT_LIMIT, | ||
730 | 'delay' => IMPORT_DELAY * 1000 | ||
731 | ) | ||
732 | ); | ||
733 | } | ||
734 | |||
735 | /** | ||
736 | * export poche entries in json | ||
737 | * @return json all poche entries | ||
738 | */ | ||
739 | public function export() | ||
740 | { | ||
741 | $filename = "wallabag-export-".$this->user->getId()."-".date("Y-m-d").".json"; | ||
742 | header('Content-Disposition: attachment; filename='.$filename); | ||
743 | |||
744 | $entries = $this->store->retrieveAll($this->user->getId()); | ||
745 | echo $this->tpl->render('export.twig', array( | ||
746 | 'export' => Tools::renderJson($entries), | ||
747 | )); | ||
748 | Tools::logm('export view'); | ||
749 | } | ||
750 | |||
751 | /** | ||
752 | * Checks online the latest version of poche and cache it | ||
753 | * @param string $which 'prod' or 'dev' | ||
754 | * @return string latest $which version | ||
755 | */ | ||
756 | private function _getPocheVersion($which = 'prod') { | ||
757 | $cache_file = CACHE . '/' . $which; | ||
758 | $check_time = time(); | ||
759 | |||
760 | # checks if the cached version file exists | ||
761 | if (file_exists($cache_file) && (filemtime($cache_file) > (time() - 86400 ))) { | ||
762 | $version = file_get_contents($cache_file); | ||
763 | $check_time = filemtime($cache_file); | ||
764 | } else { | ||
765 | $version = file_get_contents('http://static.wallabag.org/versions/' . $which); | ||
766 | file_put_contents($cache_file, $version, LOCK_EX); | ||
767 | } | ||
768 | return array($version, $check_time); | ||
769 | } | ||
770 | |||
771 | /** | ||
772 | * Update token for current user | ||
773 | */ | ||
774 | public function updateToken() | ||
775 | { | ||
776 | $token = Tools::generateToken(); | ||
777 | $this->store->updateUserConfig($this->user->getId(), 'token', $token); | ||
778 | $currentConfig = $_SESSION['poche_user']->config; | ||
779 | $currentConfig['token'] = $token; | ||
780 | $_SESSION['poche_user']->setConfig($currentConfig); | ||
781 | Tools::redirect(); | ||
782 | } | ||
783 | |||
784 | /** | ||
785 | * Generate RSS feeds for current user | ||
786 | * | ||
787 | * @param $token | ||
788 | * @param $user_id | ||
789 | * @param $tag_id if $type is 'tag', the id of the tag to generate feed for | ||
790 | * @param string $type the type of feed to generate | ||
791 | * @param int $limit the maximum number of items (0 means all) | ||
792 | */ | ||
793 | public function generateFeeds($token, $user_id, $tag_id, $type = 'home', $limit = 0) | ||
794 | { | ||
795 | $allowed_types = array('home', 'fav', 'archive', 'tag'); | ||
796 | $config = $this->store->getConfigUser($user_id); | ||
797 | |||
798 | if ($config == null) { | ||
799 | die(sprintf(_('User with this id (%d) does not exist.'), $user_id)); | ||
800 | } | ||
801 | |||
802 | if (!in_array($type, $allowed_types) || !isset($config['token']) || $token != $config['token']) { | ||
803 | die(_('Uh, there is a problem while generating feed. Wrong token used?')); | ||
804 | } | ||
805 | |||
806 | $feed = new FeedWriter(RSS2); | ||
807 | $feed->setTitle('wallabag — ' . $type . ' feed'); | ||
808 | $feed->setLink(Tools::getPocheUrl()); | ||
809 | $feed->setChannelElement('pubDate', date(DATE_RSS , time())); | ||
810 | $feed->setChannelElement('generator', 'wallabag'); | ||
811 | $feed->setDescription('wallabag ' . $type . ' elements'); | ||
812 | |||
813 | if ($type == 'tag') { | ||
814 | $entries = $this->store->retrieveEntriesByTag($tag_id, $user_id); | ||
815 | } | ||
816 | else { | ||
817 | $entries = $this->store->getEntriesByView($type, $user_id); | ||
818 | } | ||
819 | |||
820 | // if $limit is set to zero, use all entries | ||
821 | if (0 == $limit) { | ||
822 | $limit = count($entries); | ||
823 | } | ||
824 | if (count($entries) > 0) { | ||
825 | for ($i = 0; $i < min(count($entries), $limit); $i++) { | ||
826 | $entry = $entries[$i]; | ||
827 | $newItem = $feed->createNewItem(); | ||
828 | $newItem->setTitle($entry['title']); | ||
829 | $newItem->setSource(Tools::getPocheUrl() . '?view=view&id=' . $entry['id']); | ||
830 | $newItem->setLink($entry['url']); | ||
831 | $newItem->setDate(time()); | ||
832 | $newItem->setDescription($entry['content']); | ||
833 | $feed->addItem($newItem); | ||
834 | } | ||
835 | } | ||
836 | |||
837 | $feed->genarateFeed(); | ||
838 | exit; | ||
839 | } | ||
840 | |||
841 | |||
842 | |||
843 | /** | ||
844 | * Returns new purifier object with actual config | ||
845 | */ | ||
846 | private function _getPurifier() | ||
847 | { | ||
848 | $config = HTMLPurifier_Config::createDefault(); | ||
849 | $config->set('Cache.SerializerPath', CACHE); | ||
850 | $config->set('HTML.SafeIframe', true); | ||
851 | |||
852 | //allow YouTube, Vimeo and dailymotion videos | ||
853 | $config->set('URI.SafeIframeRegexp', '%^(https?:)?//(www\.youtube(?:-nocookie)?\.com/embed/|player\.vimeo\.com/video/|www\.dailymotion\.com/embed/video/)%'); | ||
854 | |||
855 | return new HTMLPurifier($config); | ||
856 | } | ||
857 | |||
858 | |||
859 | } | ||
diff --git a/inc/poche/Routing.class.php b/inc/poche/Routing.class.php deleted file mode 100755 index be06a433..00000000 --- a/inc/poche/Routing.class.php +++ /dev/null | |||
@@ -1,161 +0,0 @@ | |||
1 | <?php | ||
2 | /** | ||
3 | * wallabag, self hostable application allowing you to not miss any content anymore | ||
4 | * | ||
5 | * @category wallabag | ||
6 | * @author Nicolas Lœuillet <nicolas@loeuillet.org> | ||
7 | * @copyright 2013 | ||
8 | * @license http://opensource.org/licenses/MIT see COPYING file | ||
9 | */ | ||
10 | |||
11 | class Routing | ||
12 | { | ||
13 | protected $wallabag; | ||
14 | protected $referer; | ||
15 | protected $view; | ||
16 | protected $action; | ||
17 | protected $id; | ||
18 | protected $url; | ||
19 | protected $file; | ||
20 | protected $defaultVars = array(); | ||
21 | protected $vars = array(); | ||
22 | |||
23 | public function __construct(Poche $wallabag) | ||
24 | { | ||
25 | $this->wallabag = $wallabag; | ||
26 | $this->_init(); | ||
27 | } | ||
28 | |||
29 | private function _init() | ||
30 | { | ||
31 | # Parse GET & REFERER vars | ||
32 | $this->referer = empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER']; | ||
33 | $this->view = Tools::checkVar('view', 'home'); | ||
34 | $this->action = Tools::checkVar('action'); | ||
35 | $this->id = Tools::checkVar('id'); | ||
36 | $_SESSION['sort'] = Tools::checkVar('sort', 'id'); | ||
37 | $this->url = new Url((isset ($_GET['url'])) ? $_GET['url'] : ''); | ||
38 | } | ||
39 | |||
40 | public function run() | ||
41 | { | ||
42 | # vars to _always_ send to templates | ||
43 | $this->defaultVars = array( | ||
44 | 'referer' => $this->referer, | ||
45 | 'view' => $this->view, | ||
46 | 'poche_url' => Tools::getPocheUrl(), | ||
47 | 'title' => _('wallabag, a read it later open source system'), | ||
48 | 'token' => \Session::getToken(), | ||
49 | 'theme' => $this->wallabag->tpl->getTheme() | ||
50 | ); | ||
51 | |||
52 | $this->_launchAction(); | ||
53 | $this->_defineTplInformation(); | ||
54 | |||
55 | # because messages can be added in $poche->action(), we have to add this entry now (we can add it before) | ||
56 | $this->vars = array_merge($this->vars, array('messages' => $this->wallabag->messages->display('all', FALSE))); | ||
57 | |||
58 | $this->_render($this->file, $this->vars); | ||
59 | } | ||
60 | |||
61 | private function _defineTplInformation() | ||
62 | { | ||
63 | $tplFile = array(); | ||
64 | $tplVars = array(); | ||
65 | |||
66 | if (\Session::isLogged()) { | ||
67 | $this->wallabag->action($this->action, $this->url, $this->id); | ||
68 | $tplFile = Tools::getTplFile($this->view); | ||
69 | $tplVars = array_merge($this->vars, $this->wallabag->displayView($this->view, $this->id)); | ||
70 | } elseif(isset($_SERVER['PHP_AUTH_USER'])) { | ||
71 | if($this->wallabag->store->userExists($_SERVER['PHP_AUTH_USER'])) { | ||
72 | $this->wallabag->login($this->referer); | ||
73 | } else { | ||
74 | $this->wallabag->messages->add('e', _('login failed: user doesn\'t exist')); | ||
75 | Tools::logm('user doesn\'t exist'); | ||
76 | $tplFile = Tools::getTplFile('login'); | ||
77 | $tplVars['http_auth'] = 1; | ||
78 | } | ||
79 | } elseif(isset($_SERVER['REMOTE_USER'])) { | ||
80 | if($this->wallabag->store->userExists($_SERVER['REMOTE_USER'])) { | ||
81 | $this->wallabag->login($this->referer); | ||
82 | } else { | ||
83 | $this->wallabag->messages->add('e', _('login failed: user doesn\'t exist')); | ||
84 | Tools::logm('user doesn\'t exist'); | ||
85 | $tplFile = Tools::getTplFile('login'); | ||
86 | $tplVars['http_auth'] = 1; | ||
87 | } | ||
88 | } else { | ||
89 | $tplFile = Tools::getTplFile('login'); | ||
90 | $tplVars['http_auth'] = 0; | ||
91 | \Session::logout(); | ||
92 | } | ||
93 | |||
94 | $this->file = $tplFile; | ||
95 | $this->vars = array_merge($this->defaultVars, $tplVars); | ||
96 | } | ||
97 | |||
98 | private function _launchAction() | ||
99 | { | ||
100 | if (isset($_GET['login'])) { | ||
101 | // hello to you | ||
102 | $this->wallabag->login($this->referer); | ||
103 | } elseif (isset($_GET['feed']) && isset($_GET['user_id'])) { | ||
104 | $tag_id = (isset($_GET['tag_id']) ? intval($_GET['tag_id']) : 0); | ||
105 | $limit = (isset($_GET['limit']) ? intval($_GET['limit']) : 0); | ||
106 | $this->wallabag->generateFeeds($_GET['token'], filter_var($_GET['user_id'],FILTER_SANITIZE_NUMBER_INT), $tag_id, $_GET['type'], $limit); | ||
107 | } | ||
108 | |||
109 | //allowed ONLY to logged in user | ||
110 | if (\Session::isLogged() === true) | ||
111 | { | ||
112 | if (isset($_GET['logout'])) { | ||
113 | // see you soon ! | ||
114 | $this->wallabag->logout(); | ||
115 | } elseif (isset($_GET['config'])) { | ||
116 | // update password | ||
117 | $this->wallabag->updatePassword($_POST['password'], $_POST['password_repeat']); | ||
118 | } elseif (isset($_GET['newuser'])) { | ||
119 | $this->wallabag->createNewUser($_POST['newusername'], $_POST['password4newuser']); | ||
120 | } elseif (isset($_GET['deluser'])) { | ||
121 | $this->wallabag->deleteUser($_POST['password4deletinguser']); | ||
122 | } elseif (isset($_GET['epub'])) { | ||
123 | $epub = new WallabagEpub($this->wallabag, $_GET['method'], $_GET['value']); | ||
124 | $epub->prepareData(); | ||
125 | $epub->produceEpub(); | ||
126 | } elseif (isset($_GET['mobi'])) { | ||
127 | $mobi = new WallabagMobi($this->wallabag, $_GET['method'], $_GET['value']); | ||
128 | $mobi->prepareData(); | ||
129 | $mobi->produceMobi(); | ||
130 | } elseif (isset($_GET['pdf'])) { | ||
131 | $pdf = new WallabagPDF($this->wallabag, $_GET['method'], $_GET['value']); | ||
132 | $pdf->prepareData(); | ||
133 | $pdf->producePDF(); | ||
134 | } elseif (isset($_GET['import'])) { | ||
135 | $import = $this->wallabag->import(); | ||
136 | $tplVars = array_merge($this->vars, $import); | ||
137 | } elseif (isset($_GET['empty-cache'])) { | ||
138 | Tools::emptyCache(); | ||
139 | } elseif (isset($_GET['export'])) { | ||
140 | $this->wallabag->export(); | ||
141 | } elseif (isset($_GET['updatetheme'])) { | ||
142 | $this->wallabag->tpl->updateTheme($_POST['theme']); | ||
143 | } elseif (isset($_GET['updatelanguage'])) { | ||
144 | $this->wallabag->language->updateLanguage($_POST['language']); | ||
145 | } elseif (isset($_GET['uploadfile'])) { | ||
146 | $this->wallabag->uploadFile(); | ||
147 | } elseif (isset($_GET['feed']) && isset($_GET['action']) && $_GET['action'] == 'generate') { | ||
148 | $this->wallabag->updateToken(); | ||
149 | } | ||
150 | elseif (isset($_GET['plainurl']) && !empty($_GET['plainurl'])) { | ||
151 | $plainUrl = new Url(base64_encode($_GET['plainurl'])); | ||
152 | $this->wallabag->action('add', $plainUrl); | ||
153 | } | ||
154 | } | ||
155 | } | ||
156 | |||
157 | public function _render($file, $vars) | ||
158 | { | ||
159 | echo $this->wallabag->tpl->render($file, $vars); | ||
160 | } | ||
161 | } | ||
diff --git a/inc/poche/Template.class.php b/inc/poche/Template.class.php deleted file mode 100644 index 4d0bfdbb..00000000 --- a/inc/poche/Template.class.php +++ /dev/null | |||
@@ -1,235 +0,0 @@ | |||
1 | <?php | ||
2 | /** | ||
3 | * wallabag, self hostable application allowing you to not miss any content anymore | ||
4 | * | ||
5 | * @category wallabag | ||
6 | * @author Nicolas Lœuillet <nicolas@loeuillet.org> | ||
7 | * @copyright 2013 | ||
8 | * @license http://opensource.org/licenses/MIT see COPYING file | ||
9 | */ | ||
10 | |||
11 | class Template extends Twig_Environment | ||
12 | { | ||
13 | protected $wallabag; | ||
14 | |||
15 | private $canRenderTemplates = TRUE; | ||
16 | private $currentTheme = ''; | ||
17 | |||
18 | public function __construct(Poche $wallabag) | ||
19 | { | ||
20 | $this->wallabag = $wallabag; | ||
21 | |||
22 | // Set up theme | ||
23 | $pocheUser = Session::getParam('poche_user'); | ||
24 | |||
25 | $themeDirectory = (is_null($pocheUser) ? DEFAULT_THEME : $pocheUser->getConfigValue('theme')); | ||
26 | |||
27 | if ($themeDirectory === false || !is_dir(THEME . '/' . $themeDirectory)) { | ||
28 | $themeDirectory = DEFAULT_THEME; | ||
29 | } | ||
30 | |||
31 | $this->currentTheme = $themeDirectory; | ||
32 | |||
33 | if ($this->_themeIsInstalled() === array()) { | ||
34 | $this->_init(); | ||
35 | } | ||
36 | } | ||
37 | |||
38 | /** | ||
39 | * Returns true if selected theme is installed | ||
40 | * | ||
41 | * @return bool | ||
42 | */ | ||
43 | private function _themeIsInstalled() | ||
44 | { | ||
45 | $errors = array(); | ||
46 | |||
47 | // Twig is an absolute requirement for wallabag to function. | ||
48 | // Abort immediately if the Composer installer hasn't been run yet | ||
49 | if (!$this->canRenderTemplates) { | ||
50 | $errors[] = 'Twig does not seem to be installed. Please initialize the Composer installation to automatically fetch dependencies. You can also download <a href="http://wllbg.org/vendor">vendor.zip</a> and extract it in your wallabag folder.'; | ||
51 | } | ||
52 | |||
53 | // Check if the selected theme and its requirements are present | ||
54 | $theme = $this->getTheme(); | ||
55 | if ($theme != '' && !is_dir(THEME . '/' . $theme)) { | ||
56 | $errors[] = 'The currently selected theme (' . $theme . ') does not seem to be properly installed (Missing directory: ' . THEME . '/' . $theme . ')'; | ||
57 | $this->canRenderTemplates = FALSE; | ||
58 | } | ||
59 | |||
60 | $themeInfo = $this->getThemeInfo($theme); | ||
61 | if (isset($themeInfo['requirements']) && is_array($themeInfo['requirements'])) { | ||
62 | foreach ($themeInfo['requirements'] as $requiredTheme) { | ||
63 | if (! is_dir(THEME . '/' . $requiredTheme)) { | ||
64 | $errors[] = 'The required "' . $requiredTheme . '" theme is missing for the current theme (' . $theme . ')'; | ||
65 | $this->canRenderTemplates = FALSE; | ||
66 | } | ||
67 | } | ||
68 | } | ||
69 | |||
70 | $currentErrors = (is_null(Session::getParam('errors'))? array() : Session::getParam('errors')); | ||
71 | Session::setParam('errors', array_merge($errors, $currentErrors)); | ||
72 | |||
73 | return $errors; | ||
74 | } | ||
75 | |||
76 | /** | ||
77 | * Initialization for templates | ||
78 | */ | ||
79 | private function _init() | ||
80 | { | ||
81 | $loaderChain = new Twig_Loader_Chain(); | ||
82 | $theme = $this->getTheme(); | ||
83 | |||
84 | // add the current theme as first to the loader chain | ||
85 | // so Twig will look there first for overridden template files | ||
86 | try { | ||
87 | $loaderChain->addLoader(new Twig_Loader_Filesystem(THEME . '/' . $theme)); | ||
88 | } catch (Twig_Error_Loader $e) { | ||
89 | # @todo isInstalled() should catch this, inject Twig later | ||
90 | die('The currently selected theme (' . $theme . ') does not seem to be properly installed (' . THEME . '/' . $theme .' is missing)'); | ||
91 | } | ||
92 | |||
93 | // add all required themes to the loader chain | ||
94 | $themeInfo = $this->getThemeInfo($theme); | ||
95 | if (isset($themeInfo['requirements']) && is_array($themeInfo['requirements'])) { | ||
96 | foreach ($themeInfo['requirements'] as $requiredTheme) { | ||
97 | try { | ||
98 | $loaderChain->addLoader(new Twig_Loader_Filesystem(THEME . '/' . $requiredTheme)); | ||
99 | } catch (Twig_Error_Loader $e) { | ||
100 | # @todo isInstalled() should catch this, inject Twig later | ||
101 | die('The required "' . $requiredTheme . '" theme is missing for the current theme (' . $theme . ')'); | ||
102 | } | ||
103 | } | ||
104 | } | ||
105 | |||
106 | if (DEBUG_POCHE) { | ||
107 | $twigParams = array(); | ||
108 | } else { | ||
109 | $twigParams = array('cache' => CACHE); | ||
110 | } | ||
111 | |||
112 | parent::__construct($loaderChain, $twigParams); | ||
113 | |||
114 | //$tpl = new Twig_Environment($loaderChain, $twigParams); | ||
115 | $this->addExtension(new Twig_Extensions_Extension_I18n()); | ||
116 | |||
117 | # filter to display domain name of an url | ||
118 | $filter = new Twig_SimpleFilter('getDomain', 'Tools::getDomain'); | ||
119 | $this->addFilter($filter); | ||
120 | |||
121 | # filter for reading time | ||
122 | $filter = new Twig_SimpleFilter('getReadingTime', 'Tools::getReadingTime'); | ||
123 | $this->addFilter($filter); | ||
124 | } | ||
125 | |||
126 | /** | ||
127 | * Returns current theme | ||
128 | * | ||
129 | * @return string | ||
130 | */ | ||
131 | public function getTheme() | ||
132 | { | ||
133 | return $this->currentTheme; | ||
134 | } | ||
135 | |||
136 | /** | ||
137 | * Provides theme information by parsing theme.ini file if present in the theme's root directory. | ||
138 | * In all cases, the following data will be returned: | ||
139 | * - name: theme's name, or key if the theme is unnamed, | ||
140 | * - current: boolean informing if the theme is the current user theme. | ||
141 | * | ||
142 | * @param string $theme Theme key (directory name) | ||
143 | * @return array|boolean Theme information, or false if the theme doesn't exist. | ||
144 | */ | ||
145 | public function getThemeInfo($theme) | ||
146 | { | ||
147 | if (!is_dir(THEME . '/' . $theme)) { | ||
148 | return false; | ||
149 | } | ||
150 | |||
151 | $themeIniFile = THEME . '/' . $theme . '/theme.ini'; | ||
152 | $themeInfo = array(); | ||
153 | |||
154 | if (is_file($themeIniFile) && is_readable($themeIniFile)) { | ||
155 | $themeInfo = parse_ini_file($themeIniFile); | ||
156 | } | ||
157 | |||
158 | if ($themeInfo === false) { | ||
159 | $themeInfo = array(); | ||
160 | } | ||
161 | |||
162 | if (!isset($themeInfo['name'])) { | ||
163 | $themeInfo['name'] = $theme; | ||
164 | } | ||
165 | |||
166 | $themeInfo['current'] = ($theme === $this->getTheme()); | ||
167 | |||
168 | return $themeInfo; | ||
169 | } | ||
170 | |||
171 | /** | ||
172 | * Returns an array with installed themes | ||
173 | * | ||
174 | * @return array | ||
175 | */ | ||
176 | public function getInstalledThemes() | ||
177 | { | ||
178 | $handle = opendir(THEME); | ||
179 | $themes = array(); | ||
180 | |||
181 | while (($theme = readdir($handle)) !== false) { | ||
182 | # Themes are stored in a directory, so all directory names are themes | ||
183 | # @todo move theme installation data to database | ||
184 | if (!is_dir(THEME . '/' . $theme) || in_array($theme, array('.', '..', '_global'))) { | ||
185 | continue; | ||
186 | } | ||
187 | |||
188 | $themes[$theme] = $this->getThemeInfo($theme); | ||
189 | } | ||
190 | |||
191 | ksort($themes); | ||
192 | |||
193 | return $themes; | ||
194 | } | ||
195 | |||
196 | /** | ||
197 | * Update theme for the current user | ||
198 | * | ||
199 | * @param $newTheme | ||
200 | */ | ||
201 | public function updateTheme($newTheme) | ||
202 | { | ||
203 | # we are not going to change it to the current theme... | ||
204 | if ($newTheme == $this->getTheme()) { | ||
205 | $this->wallabag->messages->add('w', _('still using the "' . $this->getTheme() . '" theme!')); | ||
206 | Tools::redirect('?view=config'); | ||
207 | } | ||
208 | |||
209 | $themes = $this->getInstalledThemes(); | ||
210 | $actualTheme = false; | ||
211 | |||
212 | foreach (array_keys($themes) as $theme) { | ||
213 | if ($theme == $newTheme) { | ||
214 | $actualTheme = true; | ||
215 | break; | ||
216 | } | ||
217 | } | ||
218 | |||
219 | if (!$actualTheme) { | ||
220 | $this->wallabag->messages->add('e', _('that theme does not seem to be installed')); | ||
221 | Tools::redirect('?view=config'); | ||
222 | } | ||
223 | |||
224 | $this->wallabag->store->updateUserConfig($this->wallabag->user->getId(), 'theme', $newTheme); | ||
225 | $this->wallabag->messages->add('s', _('you have changed your theme preferences')); | ||
226 | |||
227 | $currentConfig = $_SESSION['poche_user']->config; | ||
228 | $currentConfig['theme'] = $newTheme; | ||
229 | |||
230 | $_SESSION['poche_user']->setConfig($currentConfig); | ||
231 | |||
232 | Tools::emptyCache(); | ||
233 | Tools::redirect('?view=config'); | ||
234 | } | ||
235 | } | ||
diff --git a/inc/poche/Tools.class.php b/inc/poche/Tools.class.php deleted file mode 100755 index d625fc40..00000000 --- a/inc/poche/Tools.class.php +++ /dev/null | |||
@@ -1,420 +0,0 @@ | |||
1 | <?php | ||
2 | /** | ||
3 | * wallabag, self hostable application allowing you to not miss any content anymore | ||
4 | * | ||
5 | * @category wallabag | ||
6 | * @author Nicolas Lœuillet <nicolas@loeuillet.org> | ||
7 | * @copyright 2013 | ||
8 | * @license http://opensource.org/licenses/MIT see COPYING file | ||
9 | */ | ||
10 | |||
11 | final class Tools | ||
12 | { | ||
13 | /** | ||
14 | * Initialize PHP environment | ||
15 | */ | ||
16 | public static function initPhp() | ||
17 | { | ||
18 | define('START_TIME', microtime(true)); | ||
19 | |||
20 | function stripslashesDeep($value) { | ||
21 | return is_array($value) | ||
22 | ? array_map('stripslashesDeep', $value) | ||
23 | : stripslashes($value); | ||
24 | } | ||
25 | |||
26 | if (get_magic_quotes_gpc()) { | ||
27 | $_POST = array_map('stripslashesDeep', $_POST); | ||
28 | $_GET = array_map('stripslashesDeep', $_GET); | ||
29 | $_COOKIE = array_map('stripslashesDeep', $_COOKIE); | ||
30 | } | ||
31 | |||
32 | ob_start(); | ||
33 | register_shutdown_function('ob_end_flush'); | ||
34 | } | ||
35 | |||
36 | /** | ||
37 | * Get wallabag instance URL | ||
38 | * | ||
39 | * @return string | ||
40 | */ | ||
41 | public static function getPocheUrl() | ||
42 | { | ||
43 | $https = (!empty($_SERVER['HTTPS']) | ||
44 | && (strtolower($_SERVER['HTTPS']) == 'on')) | ||
45 | || (isset($_SERVER["SERVER_PORT"]) | ||
46 | && $_SERVER["SERVER_PORT"] == '443') // HTTPS detection. | ||
47 | || (isset($_SERVER["SERVER_PORT"]) //Custom HTTPS port detection | ||
48 | && $_SERVER["SERVER_PORT"] == SSL_PORT) | ||
49 | || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) | ||
50 | && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https'); | ||
51 | |||
52 | $serverport = (!isset($_SERVER["SERVER_PORT"]) | ||
53 | || $_SERVER["SERVER_PORT"] == '80' | ||
54 | || $_SERVER["SERVER_PORT"] == HTTP_PORT | ||
55 | || ($https && $_SERVER["SERVER_PORT"] == '443') | ||
56 | || ($https && $_SERVER["SERVER_PORT"]==SSL_PORT) //Custom HTTPS port detection | ||
57 | ? '' : ':' . $_SERVER["SERVER_PORT"]); | ||
58 | |||
59 | if (isset($_SERVER["HTTP_X_FORWARDED_PORT"])) { | ||
60 | $serverport = ':' . $_SERVER["HTTP_X_FORWARDED_PORT"]; | ||
61 | } | ||
62 | |||
63 | $scriptname = str_replace('/index.php', '/', $_SERVER["SCRIPT_NAME"]); | ||
64 | |||
65 | if (!isset($_SERVER["HTTP_HOST"])) { | ||
66 | return $scriptname; | ||
67 | } | ||
68 | |||
69 | $host = (isset($_SERVER['HTTP_X_FORWARDED_HOST']) ? $_SERVER['HTTP_X_FORWARDED_HOST'] : (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME'])); | ||
70 | |||
71 | if (strpos($host, ':') !== false) { | ||
72 | $serverport = ''; | ||
73 | } | ||
74 | |||
75 | return 'http' . ($https ? 's' : '') . '://' | ||
76 | . $host . $serverport . $scriptname; | ||
77 | } | ||
78 | |||
79 | /** | ||
80 | * Redirects to a URL | ||
81 | * | ||
82 | * @param string $url | ||
83 | */ | ||
84 | public static function redirect($url = '') | ||
85 | { | ||
86 | if ($url === '') { | ||
87 | $url = (empty($_SERVER['HTTP_REFERER'])?'?':$_SERVER['HTTP_REFERER']); | ||
88 | if (isset($_POST['returnurl'])) { | ||
89 | $url = $_POST['returnurl']; | ||
90 | } | ||
91 | } | ||
92 | |||
93 | # prevent loop | ||
94 | if (empty($url) || parse_url($url, PHP_URL_QUERY) === $_SERVER['QUERY_STRING']) { | ||
95 | $url = Tools::getPocheUrl(); | ||
96 | } | ||
97 | |||
98 | if (substr($url, 0, 1) !== '?') { | ||
99 | $ref = Tools::getPocheUrl(); | ||
100 | if (substr($url, 0, strlen($ref)) !== $ref) { | ||
101 | $url = $ref; | ||
102 | } | ||
103 | } | ||
104 | |||
105 | self::logm('redirect to ' . $url); | ||
106 | header('Location: '.$url); | ||
107 | exit(); | ||
108 | } | ||
109 | |||
110 | /** | ||
111 | * Returns name of the template file to display | ||
112 | * | ||
113 | * @param $view | ||
114 | * @return string | ||
115 | */ | ||
116 | public static function getTplFile($view) | ||
117 | { | ||
118 | $views = array( | ||
119 | 'install', 'import', 'export', 'config', 'tags', | ||
120 | 'edit-tags', 'view', 'login', 'error', 'about' | ||
121 | ); | ||
122 | |||
123 | return (in_array($view, $views) ? $view . '.twig' : 'home.twig'); | ||
124 | } | ||
125 | |||
126 | /** | ||
127 | * Download a file (typically, for downloading pictures on web server) | ||
128 | * | ||
129 | * @param $url | ||
130 | * @return bool|mixed|string | ||
131 | */ | ||
132 | public static function getFile($url) | ||
133 | { | ||
134 | $timeout = 15; | ||
135 | $useragent = "Mozilla/5.0 (Windows NT 5.1; rv:18.0) Gecko/20100101 Firefox/18.0"; | ||
136 | |||
137 | if (in_array ('curl', get_loaded_extensions())) { | ||
138 | # Fetch feed from URL | ||
139 | $curl = curl_init(); | ||
140 | curl_setopt($curl, CURLOPT_URL, $url); | ||
141 | curl_setopt($curl, CURLOPT_TIMEOUT, $timeout); | ||
142 | if (!ini_get('open_basedir') && !ini_get('safe_mode')) { | ||
143 | curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); | ||
144 | } | ||
145 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); | ||
146 | curl_setopt($curl, CURLOPT_HEADER, false); | ||
147 | |||
148 | # for ssl, do not verified certificate | ||
149 | curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE); | ||
150 | curl_setopt($curl, CURLOPT_AUTOREFERER, TRUE ); | ||
151 | |||
152 | # FeedBurner requires a proper USER-AGENT... | ||
153 | curl_setopt($curl, CURL_HTTP_VERSION_1_1, true); | ||
154 | curl_setopt($curl, CURLOPT_ENCODING, "gzip, deflate"); | ||
155 | curl_setopt($curl, CURLOPT_USERAGENT, $useragent); | ||
156 | |||
157 | $data = curl_exec($curl); | ||
158 | $httpcode = curl_getinfo($curl, CURLINFO_HTTP_CODE); | ||
159 | $httpcodeOK = isset($httpcode) and ($httpcode == 200 or $httpcode == 301); | ||
160 | curl_close($curl); | ||
161 | } else { | ||
162 | # create http context and add timeout and user-agent | ||
163 | $context = stream_context_create( | ||
164 | array( | ||
165 | 'http' => array( | ||
166 | 'timeout' => $timeout, | ||
167 | 'header' => "User-Agent: " . $useragent, | ||
168 | 'follow_location' => true | ||
169 | ), | ||
170 | 'ssl' => array( | ||
171 | 'verify_peer' => false, | ||
172 | 'allow_self_signed' => true | ||
173 | ) | ||
174 | ) | ||
175 | ); | ||
176 | |||
177 | # only download page lesser than 4MB | ||
178 | $data = @file_get_contents($url, false, $context, -1, 4000000); | ||
179 | |||
180 | if (isset($http_response_header) and isset($http_response_header[0])) { | ||
181 | $httpcodeOK = isset($http_response_header) and isset($http_response_header[0]) and ((strpos($http_response_header[0], '200 OK') !== FALSE) or (strpos($http_response_header[0], '301 Moved Permanently') !== FALSE)); | ||
182 | } | ||
183 | } | ||
184 | |||
185 | # if response is not empty and response is OK | ||
186 | if (isset($data) and isset($httpcodeOK) and $httpcodeOK) { | ||
187 | |||
188 | # take charset of page and get it | ||
189 | preg_match('#<meta .*charset=.*>#Usi', $data, $meta); | ||
190 | |||
191 | # if meta tag is found | ||
192 | if (!empty($meta[0])) { | ||
193 | preg_match('#charset="?(.*)"#si', $meta[0], $encoding); | ||
194 | # if charset is found set it otherwise, set it to utf-8 | ||
195 | $html_charset = (!empty($encoding[1])) ? strtolower($encoding[1]) : 'utf-8'; | ||
196 | if (empty($encoding[1])) $encoding[1] = 'utf-8'; | ||
197 | } else { | ||
198 | $html_charset = 'utf-8'; | ||
199 | $encoding[1] = ''; | ||
200 | } | ||
201 | |||
202 | # replace charset of url to charset of page | ||
203 | $data = str_replace('charset=' . $encoding[1], 'charset=' . $html_charset, $data); | ||
204 | |||
205 | return $data; | ||
206 | } | ||
207 | else { | ||
208 | return FALSE; | ||
209 | } | ||
210 | } | ||
211 | |||
212 | /** | ||
213 | * Headers for JSON export | ||
214 | * | ||
215 | * @param $data | ||
216 | */ | ||
217 | public static function renderJson($data) | ||
218 | { | ||
219 | header('Cache-Control: no-cache, must-revalidate'); | ||
220 | header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); | ||
221 | header('Content-type: application/json; charset=UTF-8'); | ||
222 | echo json_encode($data); | ||
223 | exit(); | ||
224 | } | ||
225 | |||
226 | /** | ||
227 | * Create new line in log file | ||
228 | * | ||
229 | * @param $message | ||
230 | */ | ||
231 | public static function logm($message) | ||
232 | { | ||
233 | if (DEBUG_POCHE && php_sapi_name() != 'cli') { | ||
234 | $t = strval(date('Y/m/d_H:i:s')) . ' - ' . $_SERVER["REMOTE_ADDR"] . ' - ' . strval($message) . "\n"; | ||
235 | file_put_contents(CACHE . '/log.txt', $t, FILE_APPEND); | ||
236 | error_log('DEBUG POCHE : ' . $message); | ||
237 | } | ||
238 | } | ||
239 | |||
240 | /** | ||
241 | * Encode a URL by using a salt | ||
242 | * | ||
243 | * @param $string | ||
244 | * @return string | ||
245 | */ | ||
246 | public static function encodeString($string) | ||
247 | { | ||
248 | return sha1($string . SALT); | ||
249 | } | ||
250 | |||
251 | /** | ||
252 | * Cleans a variable | ||
253 | * | ||
254 | * @param $var | ||
255 | * @param string $default | ||
256 | * @return string | ||
257 | */ | ||
258 | public static function checkVar($var, $default = '') | ||
259 | { | ||
260 | return ((isset($_REQUEST["$var"])) ? htmlentities($_REQUEST["$var"]) : $default); | ||
261 | } | ||
262 | |||
263 | /** | ||
264 | * Returns the domain name for a URL | ||
265 | * | ||
266 | * @param $url | ||
267 | * @return string | ||
268 | */ | ||
269 | public static function getDomain($url) | ||
270 | { | ||
271 | return parse_url($url, PHP_URL_HOST); | ||
272 | } | ||
273 | |||
274 | /** | ||
275 | * For a given text, we calculate reading time for an article | ||
276 | * | ||
277 | * @param $text | ||
278 | * @return float | ||
279 | */ | ||
280 | public static function getReadingTime($text) | ||
281 | { | ||
282 | return floor(str_word_count(strip_tags($text)) / 200); | ||
283 | } | ||
284 | |||
285 | /** | ||
286 | * Returns the correct header for a status code | ||
287 | * | ||
288 | * @param $status_code | ||
289 | */ | ||
290 | private static function _status($status_code) | ||
291 | { | ||
292 | if (strpos(php_sapi_name(), 'apache') !== false) { | ||
293 | |||
294 | header('HTTP/1.0 '.$status_code); | ||
295 | } | ||
296 | else { | ||
297 | |||
298 | header('Status: '.$status_code); | ||
299 | } | ||
300 | } | ||
301 | |||
302 | /** | ||
303 | * Get the content for a given URL (by a call to FullTextFeed) | ||
304 | * | ||
305 | * @param Url $url | ||
306 | * @return mixed | ||
307 | */ | ||
308 | public static function getPageContent(Url $url) | ||
309 | { | ||
310 | // Saving and clearing context | ||
311 | $REAL = array(); | ||
312 | foreach( $GLOBALS as $key => $value ) { | ||
313 | if( $key != 'GLOBALS' && $key != '_SESSION' && $key != 'HTTP_SESSION_VARS' ) { | ||
314 | $GLOBALS[$key] = array(); | ||
315 | $REAL[$key] = $value; | ||
316 | } | ||
317 | } | ||
318 | // Saving and clearing session | ||
319 | if (isset($_SESSION)) { | ||
320 | $REAL_SESSION = array(); | ||
321 | foreach( $_SESSION as $key => $value ) { | ||
322 | $REAL_SESSION[$key] = $value; | ||
323 | unset($_SESSION[$key]); | ||
324 | } | ||
325 | } | ||
326 | |||
327 | // Running code in different context | ||
328 | $scope = function() { | ||
329 | extract( func_get_arg(1) ); | ||
330 | $_GET = $_REQUEST = array( | ||
331 | "url" => $url->getUrl(), | ||
332 | "max" => 5, | ||
333 | "links" => "preserve", | ||
334 | "exc" => "", | ||
335 | "format" => "json", | ||
336 | "submit" => "Create Feed" | ||
337 | ); | ||
338 | ob_start(); | ||
339 | require func_get_arg(0); | ||
340 | $json = ob_get_contents(); | ||
341 | ob_end_clean(); | ||
342 | return $json; | ||
343 | }; | ||
344 | |||
345 | // Silence $scope function to avoid | ||
346 | // issues with FTRSS when error_reporting is to high | ||
347 | // FTRSS generates PHP warnings which break output | ||
348 | $json = @$scope("vendor/wallabag/Fivefilters_Libraries/makefulltextfeed.php", array("url" => $url)); | ||
349 | |||
350 | // Clearing and restoring context | ||
351 | foreach ($GLOBALS as $key => $value) { | ||
352 | if($key != "GLOBALS" && $key != "_SESSION" ) { | ||
353 | unset($GLOBALS[$key]); | ||
354 | } | ||
355 | } | ||
356 | foreach ($REAL as $key => $value) { | ||
357 | $GLOBALS[$key] = $value; | ||
358 | } | ||
359 | |||
360 | // Clearing and restoring session | ||
361 | if (isset($REAL_SESSION)) { | ||
362 | foreach($_SESSION as $key => $value) { | ||
363 | unset($_SESSION[$key]); | ||
364 | } | ||
365 | |||
366 | foreach($REAL_SESSION as $key => $value) { | ||
367 | $_SESSION[$key] = $value; | ||
368 | } | ||
369 | } | ||
370 | |||
371 | return json_decode($json, true); | ||
372 | } | ||
373 | |||
374 | /** | ||
375 | * Returns whether we handle an AJAX (XMLHttpRequest) request. | ||
376 | * | ||
377 | * @return boolean whether we handle an AJAX (XMLHttpRequest) request. | ||
378 | */ | ||
379 | public static function isAjaxRequest() | ||
380 | { | ||
381 | return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH']==='XMLHttpRequest'; | ||
382 | } | ||
383 | |||
384 | /* | ||
385 | * Empty cache folder | ||
386 | */ | ||
387 | public static function emptyCache() | ||
388 | { | ||
389 | $files = new RecursiveIteratorIterator( | ||
390 | new RecursiveDirectoryIterator(CACHE, RecursiveDirectoryIterator::SKIP_DOTS), | ||
391 | RecursiveIteratorIterator::CHILD_FIRST | ||
392 | ); | ||
393 | |||
394 | foreach ($files as $fileInfo) { | ||
395 | $todo = ($fileInfo->isDir() ? 'rmdir' : 'unlink'); | ||
396 | $todo($fileInfo->getRealPath()); | ||
397 | } | ||
398 | |||
399 | Tools::logm('empty cache'); | ||
400 | Tools::redirect(); | ||
401 | } | ||
402 | |||
403 | public static function generateToken() | ||
404 | { | ||
405 | if (ini_get('open_basedir') === '') { | ||
406 | if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { | ||
407 | // alternative to /dev/urandom for Windows | ||
408 | $token = substr(base64_encode(uniqid(mt_rand(), true)), 0, 20); | ||
409 | } else { | ||
410 | $token = substr(base64_encode(file_get_contents('/dev/urandom', false, null, 0, 20)), 0, 15); | ||
411 | } | ||
412 | } | ||
413 | else { | ||
414 | $token = substr(base64_encode(uniqid(mt_rand(), true)), 0, 20); | ||
415 | } | ||
416 | |||
417 | return str_replace('+', '', $token); | ||
418 | } | ||
419 | |||
420 | } | ||
diff --git a/inc/poche/Url.class.php b/inc/poche/Url.class.php deleted file mode 100644 index d9172b7d..00000000 --- a/inc/poche/Url.class.php +++ /dev/null | |||
@@ -1,31 +0,0 @@ | |||
1 | <?php | ||
2 | /** | ||
3 | * wallabag, self hostable application allowing you to not miss any content anymore | ||
4 | * | ||
5 | * @category wallabag | ||
6 | * @author Nicolas Lœuillet <nicolas@loeuillet.org> | ||
7 | * @copyright 2013 | ||
8 | * @license http://opensource.org/licenses/MIT see COPYING file | ||
9 | */ | ||
10 | |||
11 | class Url | ||
12 | { | ||
13 | public $url; | ||
14 | |||
15 | function __construct($url) | ||
16 | { | ||
17 | $this->url = base64_decode($url); | ||
18 | } | ||
19 | |||
20 | public function getUrl() { | ||
21 | return $this->url; | ||
22 | } | ||
23 | |||
24 | public function setUrl($url) { | ||
25 | $this->url = $url; | ||
26 | } | ||
27 | |||
28 | public function isCorrect() { | ||
29 | return filter_var($this->url, FILTER_VALIDATE_URL) !== FALSE; | ||
30 | } | ||
31 | } \ No newline at end of file | ||
diff --git a/inc/poche/User.class.php b/inc/poche/User.class.php deleted file mode 100644 index eaadd3e5..00000000 --- a/inc/poche/User.class.php +++ /dev/null | |||
@@ -1,57 +0,0 @@ | |||
1 | <?php | ||
2 | /** | ||
3 | * wallabag, self hostable application allowing you to not miss any content anymore | ||
4 | * | ||
5 | * @category wallabag | ||
6 | * @author Nicolas Lœuillet <nicolas@loeuillet.org> | ||
7 | * @copyright 2013 | ||
8 | * @license http://opensource.org/licenses/MIT see COPYING file | ||
9 | */ | ||
10 | |||
11 | class User | ||
12 | { | ||
13 | public $id; | ||
14 | public $username; | ||
15 | public $name; | ||
16 | public $password; | ||
17 | public $email; | ||
18 | public $config; | ||
19 | |||
20 | function __construct($user = array()) | ||
21 | { | ||
22 | if ($user != array()) { | ||
23 | $this->id = $user['id']; | ||
24 | $this->username = $user['username']; | ||
25 | $this->name = $user['name']; | ||
26 | $this->password = $user['password']; | ||
27 | $this->email = $user['email']; | ||
28 | $this->config = $user['config']; | ||
29 | } | ||
30 | } | ||
31 | |||
32 | public function getId() | ||
33 | { | ||
34 | return $this->id; | ||
35 | } | ||
36 | |||
37 | public function getUsername() | ||
38 | { | ||
39 | return $this->username; | ||
40 | } | ||
41 | |||
42 | public function setConfig($config) | ||
43 | { | ||
44 | $this->config = $config; | ||
45 | } | ||
46 | |||
47 | /** | ||
48 | * Returns configuration entry for a user | ||
49 | * | ||
50 | * @param $name | ||
51 | * @return bool | ||
52 | */ | ||
53 | public function getConfigValue($name) | ||
54 | { | ||
55 | return (isset($this->config[$name])) ? $this->config[$name] : FALSE; | ||
56 | } | ||
57 | } \ No newline at end of file | ||
diff --git a/inc/poche/WallabagEBooks.class.php b/inc/poche/WallabagEBooks.class.php deleted file mode 100644 index a9c62af9..00000000 --- a/inc/poche/WallabagEBooks.class.php +++ /dev/null | |||
@@ -1,245 +0,0 @@ | |||
1 | <?php | ||
2 | /** | ||
3 | * wallabag, self hostable application allowing you to not miss any content anymore | ||
4 | * | ||
5 | * @category wallabag | ||
6 | * @author Nicolas Lœuillet <nicolas@loeuillet.org> | ||
7 | * @copyright 2013 | ||
8 | * @license http://opensource.org/licenses/MIT see COPYING file | ||
9 | */ | ||
10 | |||
11 | class WallabagEBooks | ||
12 | { | ||
13 | protected $wallabag; | ||
14 | protected $method; | ||
15 | protected $value; | ||
16 | protected $entries; | ||
17 | protected $bookTitle; | ||
18 | protected $bookFileName; | ||
19 | protected $author = 'wallabag'; | ||
20 | |||
21 | public function __construct(Poche $wallabag, $method, $value) | ||
22 | { | ||
23 | $this->wallabag = $wallabag; | ||
24 | $this->method = $method; | ||
25 | $this->value = $value; | ||
26 | } | ||
27 | |||
28 | public function prepareData() | ||
29 | { | ||
30 | switch ($this->method) { | ||
31 | case 'id': | ||
32 | $entryID = filter_var($this->value, FILTER_SANITIZE_NUMBER_INT); | ||
33 | $entry = $this->wallabag->store->retrieveOneById($entryID, $this->wallabag->user->getId()); | ||
34 | $this->entries = array($entry); | ||
35 | $this->bookTitle = $entry['title']; | ||
36 | $this->bookFileName = substr($this->bookTitle, 0, 200); | ||
37 | $this->author = preg_replace('#^w{3}.#', '', Tools::getdomain($entry["url"])); # if only one article, set author to domain name (we strip the eventual www part) | ||
38 | Tools::logm('Producing ebook from article ' . $this->bookTitle); | ||
39 | break; | ||
40 | case 'all': | ||
41 | $this->entries = $this->wallabag->store->retrieveAll($this->wallabag->user->getId()); | ||
42 | $this->bookTitle = sprintf(_('All my articles on %s'), date(_('d.m.y'))); #translatable because each country has it's own date format system | ||
43 | $this->bookFileName = _('Allarticles') . date(_('dmY')); | ||
44 | Tools::logm('Producing ebook from all articles'); | ||
45 | break; | ||
46 | case 'tag': | ||
47 | $tag = filter_var($this->value, FILTER_SANITIZE_STRING); | ||
48 | $tags_id = $this->wallabag->store->retrieveAllTags($this->wallabag->user->getId(), $tag); | ||
49 | $tag_id = $tags_id[0]["id"]; // we take the first result, which is supposed to match perfectly. There must be a workaround. | ||
50 | $this->entries = $this->wallabag->store->retrieveEntriesByTag($tag_id, $this->wallabag->user->getId()); | ||
51 | $this->bookTitle = sprintf(_('Articles tagged %s'), $tag); | ||
52 | $this->bookFileName = substr(sprintf(_('Tag %s'), $tag), 0, 200); | ||
53 | Tools::logm('Producing ebook from tag ' . $tag); | ||
54 | break; | ||
55 | case 'category': | ||
56 | $category = filter_var($this->value, FILTER_SANITIZE_STRING); | ||
57 | $this->entries = $this->wallabag->store->getEntriesByView($category, $this->wallabag->user->getId()); | ||
58 | $this->bookTitle = sprintf(_('Articles in category %s'), $category); | ||
59 | $this->bookFileName = substr(sprintf(_('Category %s'), $category), 0, 200); | ||
60 | Tools::logm('Producing ebook from category ' . $category); | ||
61 | break; | ||
62 | case 'search': | ||
63 | $search = filter_var($this->value, FILTER_SANITIZE_STRING); | ||
64 | Tools::logm($search); | ||
65 | $this->entries = $this->wallabag->store->search($search, $this->wallabag->user->getId()); | ||
66 | $this->bookTitle = sprintf(_('Articles for search %s'), $search); | ||
67 | $this->bookFileName = substr(sprintf(_('Search %s'), $search), 0, 200); | ||
68 | Tools::logm('Producing ebook from search ' . $search); | ||
69 | break; | ||
70 | case 'default': | ||
71 | die(_('Uh, there is a problem while generating eBook.')); | ||
72 | } | ||
73 | } | ||
74 | } | ||
75 | |||
76 | class WallabagEpub extends WallabagEBooks | ||
77 | { | ||
78 | /** | ||
79 | * handle ePub | ||
80 | */ | ||
81 | public function produceEpub() | ||
82 | { | ||
83 | Tools::logm('Starting to produce ePub 3 file'); | ||
84 | $content_start = | ||
85 | "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" | ||
86 | . "<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n" | ||
87 | . "<head>" | ||
88 | . "<meta http-equiv=\"Default-Style\" content=\"text/html; charset=utf-8\" />\n" | ||
89 | . "<title>" . _("wallabag articles book") . "</title>\n" | ||
90 | . "</head>\n" | ||
91 | . "<body>\n"; | ||
92 | |||
93 | $bookEnd = "</body>\n</html>\n"; | ||
94 | |||
95 | $log = new Logger("wallabag", TRUE); | ||
96 | $fileDir = CACHE; | ||
97 | |||
98 | $book = new EPub(EPub::BOOK_VERSION_EPUB3, DEBUG_POCHE); | ||
99 | $log->logLine("new EPub()"); | ||
100 | $log->logLine("EPub class version: " . EPub::VERSION); | ||
101 | $log->logLine("EPub Req. Zip version: " . EPub::REQ_ZIP_VERSION); | ||
102 | $log->logLine("Zip version: " . Zip::VERSION); | ||
103 | $log->logLine("getCurrentServerURL: " . $book->getCurrentServerURL()); | ||
104 | $log->logLine("getCurrentPageURL..: " . $book->getCurrentPageURL()); | ||
105 | |||
106 | Tools::logm('Filling metadata for ePub...'); | ||
107 | |||
108 | $book->setTitle($this->bookTitle); | ||
109 | $book->setIdentifier("http://$_SERVER[HTTP_HOST]", EPub::IDENTIFIER_URI); // Could also be the ISBN number, prefered for published books, or a UUID. | ||
110 | //$book->setLanguage("en"); // Not needed, but included for the example, Language is mandatory, but EPub defaults to "en". Use RFC3066 Language codes, such as "en", "da", "fr" etc. | ||
111 | $book->setDescription(_("Some articles saved on my wallabag")); | ||
112 | $book->setAuthor($this->author,$this->author); | ||
113 | $book->setPublisher("wallabag", "wallabag"); // I hope this is a non existant address :) | ||
114 | $book->setDate(time()); // Strictly not needed as the book date defaults to time(). | ||
115 | //$book->setRights("Copyright and licence information specific for the book."); // As this is generated, this _could_ contain the name or licence information of the user who purchased the book, if needed. If this is used that way, the identifier must also be made unique for the book. | ||
116 | $book->setSourceURL("http://$_SERVER[HTTP_HOST]"); | ||
117 | |||
118 | $book->addDublinCoreMetadata(DublinCore::CONTRIBUTOR, "PHP"); | ||
119 | $book->addDublinCoreMetadata(DublinCore::CONTRIBUTOR, "wallabag"); | ||
120 | |||
121 | $cssData = "body {\n margin-left: .5em;\n margin-right: .5em;\n text-align: justify;\n}\n\np {\n font-family: serif;\n font-size: 10pt;\n text-align: justify;\n text-indent: 1em;\n margin-top: 0px;\n margin-bottom: 1ex;\n}\n\nh1, h2 {\n font-family: sans-serif;\n font-style: italic;\n text-align: center;\n background-color: #6b879c;\n color: white;\n width: 100%;\n}\n\nh1 {\n margin-bottom: 2px;\n}\n\nh2 {\n margin-top: -2px;\n margin-bottom: 2px;\n}\n"; | ||
122 | |||
123 | $log->logLine("Add Cover"); | ||
124 | |||
125 | $fullTitle = "<h1> " . $this->bookTitle . "</h1>\n"; | ||
126 | |||
127 | $book->setCoverImage("Cover.png", file_get_contents("themes/_global/img/appicon/apple-touch-icon-152.png"), "image/png", $fullTitle); | ||
128 | |||
129 | $cover = $content_start . '<div style="text-align:center;"><p>' . _('Produced by wallabag with PHPePub') . '</p><p>'. _('Please open <a href="https://github.com/wallabag/wallabag/issues" >an issue</a> if you have trouble with the display of this E-Book on your device.') . '</p></div>' . $bookEnd; | ||
130 | |||
131 | //$book->addChapter("Table of Contents", "TOC.xhtml", NULL, false, EPub::EXTERNAL_REF_IGNORE); | ||
132 | $book->addChapter("Notices", "Cover2.html", $cover); | ||
133 | |||
134 | $book->buildTOC(); | ||
135 | |||
136 | Tools::logm('Adding actual content...'); | ||
137 | |||
138 | foreach ($this->entries as $entry) { //set tags as subjects | ||
139 | $tags = $this->wallabag->store->retrieveTagsByEntry($entry['id']); | ||
140 | foreach ($tags as $tag) { | ||
141 | $book->setSubject($tag['value']); | ||
142 | } | ||
143 | |||
144 | $log->logLine("Set up parameters"); | ||
145 | |||
146 | $chapter = $content_start . $entry['content'] . $bookEnd; | ||
147 | $book->addChapter($entry['title'], htmlspecialchars($entry['title']) . ".html", $chapter, true, EPub::EXTERNAL_REF_ADD); | ||
148 | $log->logLine("Added chapter " . $entry['title']); | ||
149 | } | ||
150 | |||
151 | if (DEBUG_POCHE) { | ||
152 | $book->addChapter("Log", "Log.html", $content_start . $log->getLog() . "\n</pre>" . $bookEnd); // log generation | ||
153 | Tools::logm('Production log available in produced file'); | ||
154 | } | ||
155 | $book->finalize(); | ||
156 | $zipData = $book->sendBook($this->bookFileName); | ||
157 | Tools::logm('Ebook produced'); | ||
158 | } | ||
159 | } | ||
160 | |||
161 | class WallabagMobi extends WallabagEBooks | ||
162 | { | ||
163 | /** | ||
164 | * MOBI Class | ||
165 | * @author Sander Kromwijk | ||
166 | */ | ||
167 | |||
168 | public function produceMobi() | ||
169 | { | ||
170 | |||
171 | Tools::logm('Starting to produce Mobi file'); | ||
172 | $mobi = new MOBI(); | ||
173 | $content = new MOBIFile(); | ||
174 | |||
175 | $messages = new Messages(); // for later | ||
176 | |||
177 | Tools::logm('Filling metadata for Mobi...'); | ||
178 | |||
179 | $content->set("title", $this->bookTitle); | ||
180 | $content->set("author", $this->author); | ||
181 | $content->set("subject", $this->bookTitle); | ||
182 | |||
183 | # introduction | ||
184 | $content->appendParagraph('<div style="text-align:center;" ><p>' . _('Produced by wallabag with PHPMobi') . '</p><p>'. _('Please open <a href="https://github.com/wallabag/wallabag/issues" >an issue</a> if you have trouble with the display of this E-Book on your device.') . '</p></div>'); | ||
185 | $content->appendImage(imagecreatefrompng("themes/_global/img/appicon/apple-touch-icon-152.png")); | ||
186 | $content->appendPageBreak(); | ||
187 | |||
188 | Tools::logm('Adding actual content...'); | ||
189 | |||
190 | foreach ($this->entries as $item) { | ||
191 | $content->appendChapterTitle($item['title']); | ||
192 | $content->appendParagraph($item['content']); | ||
193 | $content->appendPageBreak(); | ||
194 | } | ||
195 | $mobi->setContentProvider($content); | ||
196 | |||
197 | // we offer file to download | ||
198 | $mobi->download($this->bookFileName.'.mobi'); | ||
199 | Tools::logm('Mobi file produced'); | ||
200 | } | ||
201 | } | ||
202 | |||
203 | class WallabagPDF extends WallabagEbooks | ||
204 | { | ||
205 | public function producePDF() | ||
206 | { | ||
207 | |||
208 | Tools::logm('Starting to produce PDF file'); | ||
209 | |||
210 | $pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false); | ||
211 | |||
212 | Tools::logm('Filling metadata for PDF...'); | ||
213 | $pdf->SetCreator(PDF_CREATOR); | ||
214 | $pdf->SetAuthor(''); | ||
215 | $pdf->SetTitle($this->bookTitle); | ||
216 | $pdf->SetSubject($this->bookTitle); | ||
217 | |||
218 | Tools::logm('Adding introduction...'); | ||
219 | $pdf->AddPage(); | ||
220 | $intro = '<h1>' . $this->bookTitle . '</h1><div style="text-align:center;" > | ||
221 | <p>' . _('Produced by wallabag with tcpdf') . '</p> | ||
222 | <p>'. _('Please open <a href="https://github.com/wallabag/wallabag/issues" >an issue</a> if you have trouble with the display of this E-Book on your device.') . '</p> | ||
223 | <img src="themes/_global/img/appicon/apple-touch-icon-152.png" /></div>'; | ||
224 | |||
225 | |||
226 | $pdf->writeHTMLCell(0, 0, '', '', $intro, 0, 1, 0, true, '', true); | ||
227 | |||
228 | $i = 1; | ||
229 | Tools::logm('Adding actual content...'); | ||
230 | foreach ($this->entries as $item) { | ||
231 | $pdf->AddPage(); | ||
232 | $html = '<h1>' . $item['title'] . '</h1>'; | ||
233 | $html .= $item['content']; | ||
234 | $pdf->writeHTMLCell(0, 0, '', '', $html, 0, 1, 0, true, '', true); | ||
235 | $i = $i+1; | ||
236 | } | ||
237 | |||
238 | // set image scale factor | ||
239 | $pdf->setImageScale(PDF_IMAGE_SCALE_RATIO); | ||
240 | |||
241 | |||
242 | $pdf->Output(CACHE . '/' . $this->bookFileName . '.pdf', 'FD'); | ||
243 | |||
244 | } | ||
245 | } | ||
diff --git a/inc/poche/config.inc.default.php b/inc/poche/config.inc.default.php deleted file mode 100755 index a159e713..00000000 --- a/inc/poche/config.inc.default.php +++ /dev/null | |||
@@ -1,78 +0,0 @@ | |||
1 | <?php | ||
2 | /** | ||
3 | * wallabag, self hostable application allowing you to not miss any content anymore | ||
4 | * | ||
5 | * @category wallabag | ||
6 | * @author Nicolas Lœuillet <nicolas@loeuillet.org> | ||
7 | * @copyright 2013 | ||
8 | * @license http://opensource.org/licenses/MIT see COPYING file | ||
9 | */ | ||
10 | |||
11 | @define ('SALT', ''); # put a strong string here | ||
12 | @define ('LANG', 'en_EN.utf8'); | ||
13 | |||
14 | @define ('STORAGE', 'sqlite'); # postgres, mysql or sqlite | ||
15 | |||
16 | @define ('STORAGE_SQLITE', ROOT . '/db/poche.sqlite'); # if you are using sqlite, where the database file is located | ||
17 | |||
18 | # only for postgres & mysql | ||
19 | @define ('STORAGE_SERVER', 'localhost'); | ||
20 | @define ('STORAGE_DB', 'poche'); | ||
21 | @define ('STORAGE_USER', 'poche'); | ||
22 | @define ('STORAGE_PASSWORD', 'poche'); | ||
23 | |||
24 | ################################################################################# | ||
25 | # Do not trespass unless you know what you are doing | ||
26 | ################################################################################# | ||
27 | // Change this if http is running on nonstandard port - i.e is behind cache proxy | ||
28 | @define ('HTTP_PORT', 80); | ||
29 | |||
30 | // Change this if not using the standart port for SSL - i.e you server is behind sslh | ||
31 | @define ('SSL_PORT', 443); | ||
32 | |||
33 | @define ('MODE_DEMO', FALSE); | ||
34 | @define ('DEBUG_POCHE', FALSE); | ||
35 | |||
36 | //default level of error reporting in application. Developers should override it in their config.inc.php: set to E_ALL. | ||
37 | @define ('ERROR_REPORTING', E_ALL & ~E_NOTICE); | ||
38 | |||
39 | @define ('DOWNLOAD_PICTURES', FALSE); # This can slow down the process of adding articles | ||
40 | @define ('REGENERATE_PICTURES_QUALITY', 75); | ||
41 | @define ('CONVERT_LINKS_FOOTNOTES', FALSE); | ||
42 | @define ('REVERT_FORCED_PARAGRAPH_ELEMENTS', FALSE); | ||
43 | @define ('SHARE_TWITTER', TRUE); | ||
44 | @define ('SHARE_MAIL', TRUE); | ||
45 | @define ('SHARE_SHAARLI', FALSE); | ||
46 | @define ('SHAARLI_URL', 'http://myshaarliurl.com'); | ||
47 | @define ('SHARE_DIASPORA', FALSE); | ||
48 | @define ('DIASPORA_URL', 'http://diasporapod.com'); # Don't add a / at the end | ||
49 | @define ('FLATTR', TRUE); | ||
50 | @define ('FLATTR_API', 'https://api.flattr.com/rest/v2/things/lookup/?url='); | ||
51 | @define ('NOT_FLATTRABLE', '0'); | ||
52 | @define ('FLATTRABLE', '1'); | ||
53 | @define ('FLATTRED', '2'); | ||
54 | @define ('CARROT', FALSE); | ||
55 | |||
56 | // ebook | ||
57 | @define ('EPUB', TRUE); | ||
58 | @define ('MOBI', FALSE); | ||
59 | @define ('PDF', FALSE); | ||
60 | |||
61 | // display or not print link in article view | ||
62 | @define ('SHOW_PRINTLINK', '1'); | ||
63 | // display or not percent of read in article view. Affects only default theme. | ||
64 | @define ('SHOW_READPERCENT', '1'); | ||
65 | @define ('ABS_PATH', 'assets/'); | ||
66 | |||
67 | @define ('DEFAULT_THEME', 'baggy'); | ||
68 | |||
69 | @define ('THEME', ROOT . '/themes'); | ||
70 | @define ('LOCALE', ROOT . '/locale'); | ||
71 | @define ('CACHE', ROOT . '/cache'); | ||
72 | |||
73 | @define ('PAGINATION', '12'); | ||
74 | |||
75 | //limit for download of articles during import | ||
76 | @define ('IMPORT_LIMIT', 5); | ||
77 | //delay between downloads (in sec) | ||
78 | @define ('IMPORT_DELAY', 5); | ||
diff --git a/inc/poche/global.inc.php b/inc/poche/global.inc.php deleted file mode 100755 index ff7ebf64..00000000 --- a/inc/poche/global.inc.php +++ /dev/null | |||
@@ -1,43 +0,0 @@ | |||
1 | <?php | ||
2 | /** | ||
3 | * wallabag, self hostable application allowing you to not miss any content anymore | ||
4 | * | ||
5 | * @category wallabag | ||
6 | * @author Nicolas Lœuillet <nicolas@loeuillet.org> | ||
7 | * @copyright 2013 | ||
8 | * @license http://opensource.org/licenses/MIT see COPYING file | ||
9 | */ | ||
10 | |||
11 | # the poche system root directory (/inc) | ||
12 | define('INCLUDES', dirname(__FILE__) . '/..'); | ||
13 | |||
14 | # the poche root directory | ||
15 | define('ROOT', INCLUDES . '/..'); | ||
16 | |||
17 | require_once ROOT . '/vendor/autoload.php'; | ||
18 | require_once INCLUDES . '/poche/Tools.class.php'; | ||
19 | require_once INCLUDES . '/poche/User.class.php'; | ||
20 | require_once INCLUDES . '/poche/Url.class.php'; | ||
21 | require_once INCLUDES . '/poche/Template.class.php'; | ||
22 | require_once INCLUDES . '/poche/Language.class.php'; | ||
23 | require_once INCLUDES . '/poche/Routing.class.php'; | ||
24 | require_once INCLUDES . '/poche/WallabagEBooks.class.php'; | ||
25 | require_once INCLUDES . '/poche/Poche.class.php'; | ||
26 | require_once INCLUDES . '/poche/Database.class.php'; | ||
27 | require_once INCLUDES . '/poche/FlattrItem.class.php'; | ||
28 | |||
29 | # system configuration; database credentials et caetera | ||
30 | require_once INCLUDES . '/poche/config.inc.php'; | ||
31 | require_once INCLUDES . '/poche/config.inc.default.php'; | ||
32 | |||
33 | if (DOWNLOAD_PICTURES) { | ||
34 | require_once INCLUDES . '/poche/pochePictures.php'; | ||
35 | } | ||
36 | |||
37 | if (!ini_get('date.timezone') || !@date_default_timezone_set(ini_get('date.timezone'))) { | ||
38 | date_default_timezone_set('UTC'); | ||
39 | } | ||
40 | |||
41 | if (defined('ERROR_REPORTING')) { | ||
42 | error_reporting(ERROR_REPORTING); | ||
43 | } \ No newline at end of file | ||
diff --git a/inc/poche/pochePictures.php b/inc/poche/pochePictures.php deleted file mode 100644 index 52394c70..00000000 --- a/inc/poche/pochePictures.php +++ /dev/null | |||
@@ -1,168 +0,0 @@ | |||
1 | <?php | ||
2 | /** | ||
3 | * wallabag, self hostable application allowing you to not miss any content anymore | ||
4 | * | ||
5 | * @category wallabag | ||
6 | * @author Nicolas Lœuillet <nicolas@loeuillet.org> | ||
7 | * @copyright 2013 | ||
8 | * @license http://opensource.org/licenses/MIT see COPYING file | ||
9 | */ | ||
10 | |||
11 | |||
12 | final class Picture | ||
13 | { | ||
14 | /** | ||
15 | * Changing pictures URL in article content | ||
16 | */ | ||
17 | public static function filterPicture($content, $url, $id) | ||
18 | { | ||
19 | $matches = array(); | ||
20 | $processing_pictures = array(); // list of processing image to avoid processing the same pictures twice | ||
21 | preg_match_all('#<\s*(img)[^>]+src="([^"]*)"[^>]*>#Si', $content, $matches, PREG_SET_ORDER); | ||
22 | foreach($matches as $i => $link) { | ||
23 | $link[1] = trim($link[1]); | ||
24 | if (!preg_match('#^(([a-z]+://)|(\#))#', $link[1])) { | ||
25 | $absolute_path = self::_getAbsoluteLink($link[2], $url); | ||
26 | $filename = basename(parse_url($absolute_path, PHP_URL_PATH)); | ||
27 | $directory = self::_createAssetsDirectory($id); | ||
28 | $fullpath = $directory . '/' . $filename; | ||
29 | |||
30 | if (in_array($absolute_path, $processing_pictures) === true) { | ||
31 | // replace picture's URL only if processing is OK : already processing -> go to next picture | ||
32 | continue; | ||
33 | } | ||
34 | |||
35 | if (self::_downloadPictures($absolute_path, $fullpath) === true) { | ||
36 | $content = str_replace($matches[$i][2], Tools::getPocheUrl() . $fullpath, $content); | ||
37 | } | ||
38 | |||
39 | $processing_pictures[] = $absolute_path; | ||
40 | } | ||
41 | } | ||
42 | |||
43 | return $content; | ||
44 | } | ||
45 | |||
46 | /** | ||
47 | * Get absolute URL | ||
48 | */ | ||
49 | private static function _getAbsoluteLink($relativeLink, $url) | ||
50 | { | ||
51 | /* return if already absolute URL */ | ||
52 | if (parse_url($relativeLink, PHP_URL_SCHEME) != '') return $relativeLink; | ||
53 | |||
54 | /* queries and anchors */ | ||
55 | if ($relativeLink[0]=='#' || $relativeLink[0]=='?') return $url . $relativeLink; | ||
56 | |||
57 | /* parse base URL and convert to local variables: | ||
58 | $scheme, $host, $path */ | ||
59 | extract(parse_url($url)); | ||
60 | |||
61 | /* remove non-directory element from path */ | ||
62 | $path = preg_replace('#/[^/]*$#', '', $path); | ||
63 | |||
64 | /* destroy path if relative url points to root */ | ||
65 | if ($relativeLink[0] == '/') $path = ''; | ||
66 | |||
67 | /* dirty absolute URL */ | ||
68 | $abs = $host . $path . '/' . $relativeLink; | ||
69 | |||
70 | /* replace '//' or '/./' or '/foo/../' with '/' */ | ||
71 | $re = array('#(/\.?/)#', '#/(?!\.\.)[^/]+/\.\./#'); | ||
72 | for($n=1; $n>0; $abs=preg_replace($re, '/', $abs, -1, $n)) {} | ||
73 | |||
74 | /* absolute URL is ready! */ | ||
75 | return $scheme.'://'.$abs; | ||
76 | } | ||
77 | |||
78 | /** | ||
79 | * Downloading pictures | ||
80 | * | ||
81 | * @return bool true if the download and processing is OK, false else | ||
82 | */ | ||
83 | private static function _downloadPictures($absolute_path, $fullpath) | ||
84 | { | ||
85 | $rawdata = Tools::getFile($absolute_path); | ||
86 | $fullpath = urldecode($fullpath); | ||
87 | |||
88 | if(file_exists($fullpath)) { | ||
89 | unlink($fullpath); | ||
90 | } | ||
91 | |||
92 | // check extension | ||
93 | $file_ext = strrchr($fullpath, '.'); | ||
94 | $whitelist = array(".jpg",".jpeg",".gif",".png"); | ||
95 | if (!(in_array($file_ext, $whitelist))) { | ||
96 | Tools::logm('processed image with not allowed extension. Skipping ' . $fullpath); | ||
97 | return false; | ||
98 | } | ||
99 | |||
100 | // check headers | ||
101 | $imageinfo = getimagesize($absolute_path); | ||
102 | if ($imageinfo['mime'] != 'image/gif' && $imageinfo['mime'] != 'image/jpeg'&& $imageinfo['mime'] != 'image/jpg'&& $imageinfo['mime'] != 'image/png') { | ||
103 | Tools::logm('processed image with bad header. Skipping ' . $fullpath); | ||
104 | return false; | ||
105 | } | ||
106 | |||
107 | // regenerate image | ||
108 | $im = imagecreatefromstring($rawdata); | ||
109 | if ($im === false) { | ||
110 | Tools::logm('error while regenerating image ' . $fullpath); | ||
111 | return false; | ||
112 | } | ||
113 | |||
114 | switch ($imageinfo['mime']) { | ||
115 | case 'image/gif': | ||
116 | $result = imagegif($im, $fullpath); | ||
117 | break; | ||
118 | case 'image/jpeg': | ||
119 | case 'image/jpg': | ||
120 | $result = imagejpeg($im, $fullpath, REGENERATE_PICTURES_QUALITY); | ||
121 | break; | ||
122 | case 'image/png': | ||
123 | $result = imagepng($im, $fullpath, ceil(REGENERATE_PICTURES_QUALITY / 100 * 9)); | ||
124 | break; | ||
125 | } | ||
126 | imagedestroy($im); | ||
127 | |||
128 | return $result; | ||
129 | } | ||
130 | |||
131 | /** | ||
132 | * Create a directory for an article | ||
133 | * | ||
134 | * @param $id ID of the article | ||
135 | * @return string | ||
136 | */ | ||
137 | private static function _createAssetsDirectory($id) | ||
138 | { | ||
139 | $assets_path = ABS_PATH; | ||
140 | if (!is_dir($assets_path)) { | ||
141 | mkdir($assets_path, 0715); | ||
142 | } | ||
143 | |||
144 | $article_directory = $assets_path . $id; | ||
145 | if (!is_dir($article_directory)) { | ||
146 | mkdir($article_directory, 0715); | ||
147 | } | ||
148 | |||
149 | return $article_directory; | ||
150 | } | ||
151 | |||
152 | /** | ||
153 | * Remove the directory | ||
154 | * | ||
155 | * @param $directory | ||
156 | * @return bool | ||
157 | */ | ||
158 | public static function removeDirectory($directory) | ||
159 | { | ||
160 | if (is_dir($directory)) { | ||
161 | $files = array_diff(scandir($directory), array('.','..')); | ||
162 | foreach ($files as $file) { | ||
163 | (is_dir("$directory/$file")) ? self::removeDirectory("$directory/$file") : unlink("$directory/$file"); | ||
164 | } | ||
165 | return rmdir($directory); | ||
166 | } | ||
167 | } | ||
168 | } \ No newline at end of file | ||