aboutsummaryrefslogtreecommitdiffhomepage
path: root/inc
diff options
context:
space:
mode:
authorNicolas Lœuillet <nicolas@loeuillet.org>2015-01-20 07:40:39 +0100
committerNicolas Lœuillet <nicolas@loeuillet.org>2015-01-20 07:40:39 +0100
commit6ad93dff69d7c2beb2196e73f641e6484fccbeb7 (patch)
tree12430b858be2a41029fc11d1afa1fa2a120b96f9 /inc
parentc78c1a3f08815aab99752026ccdf1dcf63cf43c1 (diff)
downloadwallabag-6ad93dff69d7c2beb2196e73f641e6484fccbeb7.tar.gz
wallabag-6ad93dff69d7c2beb2196e73f641e6484fccbeb7.tar.zst
wallabag-6ad93dff69d7c2beb2196e73f641e6484fccbeb7.zip
new folders
Diffstat (limited to 'inc')
-rwxr-xr-xinc/poche/Database.class.php601
-rw-r--r--inc/poche/FlattrItem.class.php57
-rw-r--r--inc/poche/Language.class.php114
-rwxr-xr-xinc/poche/Poche.class.php859
-rwxr-xr-xinc/poche/Routing.class.php161
-rw-r--r--inc/poche/Template.class.php235
-rwxr-xr-xinc/poche/Tools.class.php420
-rw-r--r--inc/poche/Url.class.php31
-rw-r--r--inc/poche/User.class.php57
-rw-r--r--inc/poche/WallabagEBooks.class.php245
-rwxr-xr-xinc/poche/config.inc.default.php78
-rwxr-xr-xinc/poche/global.inc.php43
-rw-r--r--inc/poche/pochePictures.php168
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
11class 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
11class 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
11class 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
11class 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&amp;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
11class 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
11class 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
11final 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
11class 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
11class 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
11class 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
76class 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
161class 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
203class 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)
12define('INCLUDES', dirname(__FILE__) . '/..');
13
14# the poche root directory
15define('ROOT', INCLUDES . '/..');
16
17require_once ROOT . '/vendor/autoload.php';
18require_once INCLUDES . '/poche/Tools.class.php';
19require_once INCLUDES . '/poche/User.class.php';
20require_once INCLUDES . '/poche/Url.class.php';
21require_once INCLUDES . '/poche/Template.class.php';
22require_once INCLUDES . '/poche/Language.class.php';
23require_once INCLUDES . '/poche/Routing.class.php';
24require_once INCLUDES . '/poche/WallabagEBooks.class.php';
25require_once INCLUDES . '/poche/Poche.class.php';
26require_once INCLUDES . '/poche/Database.class.php';
27require_once INCLUDES . '/poche/FlattrItem.class.php';
28
29# system configuration; database credentials et caetera
30require_once INCLUDES . '/poche/config.inc.php';
31require_once INCLUDES . '/poche/config.inc.default.php';
32
33if (DOWNLOAD_PICTURES) {
34 require_once INCLUDES . '/poche/pochePictures.php';
35}
36
37if (!ini_get('date.timezone') || !@date_default_timezone_set(ini_get('date.timezone'))) {
38 date_default_timezone_set('UTC');
39}
40
41if (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
12final 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