]> git.immae.eu Git - github/wallabag/wallabag.git/blame - inc/poche/Poche.class.php
WHAT. A. BIG. REFACTOR. + new license (we moved to MIT one)
[github/wallabag/wallabag.git] / inc / poche / Poche.class.php
CommitLineData
eb1af592
NL
1<?php
2/**
c95b78a8 3 * wallabag, self hostable application allowing you to not miss any content anymore
eb1af592 4 *
c95b78a8
NL
5 * @category wallabag
6 * @author Nicolas Lœuillet <nicolas@loeuillet.org>
eb1af592 7 * @copyright 2013
3602405e 8 * @license http://opensource.org/licenses/MIT see COPYING file
eb1af592
NL
9 */
10
11class Poche
12{
3602405e
NL
13 /**
14 * @var User
15 */
7ce7ec4c 16 public $user;
3602405e
NL
17 /**
18 * @var Database
19 */
eb1af592 20 public $store;
3602405e
NL
21 /**
22 * @var Template
23 */
eb1af592 24 public $tpl;
3602405e
NL
25 /**
26 * @var Language
27 */
28 public $language;
29 /**
30 * @var Routing
31 */
32 public $routing;
33 /**
34 * @var Messages
35 */
55821e04 36 public $messages;
3602405e
NL
37 /**
38 * @var Paginator
39 */
6a361945 40 public $pagination;
182faf26 41
00dbaf90 42 public function __construct()
eb1af592 43 {
3602405e 44 $this->init();
eb1af592 45 }
182faf26
MR
46
47 private function init()
00dbaf90
NL
48 {
49 Tools::initPhp();
eb1af592 50
3602405e
NL
51 $pocheUser = Session::getParam('poche_user');
52
53 if ($pocheUser && $pocheUser != array()) {
54 $this->user = $pocheUser;
00dbaf90 55 } else {
3602405e 56 // fake user, just for install & login screens
00dbaf90
NL
57 $this->user = new User();
58 $this->user->setConfig($this->getDefaultConfig());
59 }
60
3602405e
NL
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);
00dbaf90 67 }
182faf26 68
3602405e
NL
69 public function run()
70 {
71 $this->routing->run();
00dbaf90 72 }
182faf26 73
4a291288 74 /**
3602405e 75 * Creates a new user
4a291288 76 */
3602405e 77 public function createNewUser()
eb1af592 78 {
4d99bae8 79 if (isset($_GET['newuser'])){
80 if ($_POST['newusername'] != "" && $_POST['password4newuser'] != ""){
81 $newusername = filter_var($_POST['newusername'], FILTER_SANITIZE_STRING);
b6413975 82 if (!$this->store->userExists($newusername)){
4d99bae8 83 if ($this->store->install($newusername, Tools::encodeString($_POST['password4newuser'] . $newusername))) {
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 }
b6413975 101
3602405e
NL
102 /**
103 * Delete an existing user
104 */
105 public function deleteUser()
106 {
4d99bae8 107 if (isset($_GET['deluser'])){
108 if ($this->store->listUsers() > 1) {
109 if (Tools::encodeString($_POST['password4deletinguser'].$this->user->getUsername()) == $this->store->getUserPassword($this->user->getId())) {
110 $username = $this->user->getUsername();
111 $this->store->deleteUserConfig($this->user->getId());
112 Tools::logm('The configuration for user '. $username .' has been deleted !');
113 $this->store->deleteTagsEntriesAndEntries($this->user->getId());
114 Tools::logm('The entries for user '. $username .' has been deleted !');
115 $this->store->deleteUser($this->user->getId());
116 Tools::logm('User '. $username .' has been completely deleted !');
117 Session::logout();
118 Tools::logm('logout');
119 Tools::redirect();
120 $this->messages->add('s', sprintf(_('User %s has been successfully deleted !'),$newusername));
121 }
122 else {
123 Tools::logm('Bad password !');
124 $this->messages->add('e', _('Error : The password is wrong !'));
125 }
126 }
127 else {
128 Tools::logm('Only user !');
129 $this->messages->add('e', _('Error : You are the only user, you cannot delete your account !'));
130 }
131 }
132 }
eb1af592 133
8d3275be 134 public function getDefaultConfig()
182faf26 135 {
8d3275be
NL
136 return array(
137 'pager' => PAGINATION,
138 'language' => LANG,
00dbaf90
NL
139 'theme' => DEFAULT_THEME
140 );
8d3275be
NL
141 }
142
eb1af592
NL
143 /**
144 * Call action (mark as fav, archive, delete, etc.)
145 */
926acd7b 146 public function action($action, Url $url, $id = 0, $import = FALSE, $autoclose = FALSE, $tags = null)
eb1af592
NL
147 {
148 switch ($action)
149 {
150 case 'add':
a297fb1e
MR
151 $content = Tools::getPageContent($url);
152 $title = ($content['rss']['channel']['item']['title'] != '') ? $content['rss']['channel']['item']['title'] : _('Untitled');
153 $body = $content['rss']['channel']['item']['description'];
154
155 // clean content from prevent xss attack
0f859c6f 156 $purifier = $this->getPurifier();
a297fb1e
MR
157 $title = $purifier->purify($title);
158 $body = $purifier->purify($body);
1570a653 159
182faf26 160 //search for possible duplicate
8d7cd2cc 161 $duplicate = NULL;
a297fb1e 162 $duplicate = $this->store->retrieveOneByURL($url->getUrl(), $this->user->getId());
488fc63b 163
182faf26 164 $last_id = $this->store->add($url->getUrl(), $title, $body, $this->user->getId());
a297fb1e 165 if ( $last_id ) {
ec397236 166 Tools::logm('add link ' . $url->getUrl());
ec397236 167 if (DOWNLOAD_PICTURES) {
3602405e 168 $content = Picture::filterPicture($body, $url->getUrl(), $last_id);
ec397236
NL
169 Tools::logm('updating content article');
170 $this->store->updateContent($last_id, $content, $this->user->getId());
171 }
488fc63b
MR
172
173 if ($duplicate != NULL) {
174 // 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
175 Tools::logm('link ' . $url->getUrl() . ' is a duplicate');
176 // 1) - preserve tags and favorite, then drop old entry
177 $this->store->reassignTags($duplicate['id'], $last_id);
178 if ($duplicate['is_fav']) {
179 $this->store->favoriteById($last_id, $this->user->getId());
180 }
181 if ($this->store->deleteById($duplicate['id'], $this->user->getId())) {
182 Tools::logm('previous link ' . $url->getUrl() .' entry deleted');
183 }
184 }
185
182faf26 186 $this->messages->add('s', _('the link has been added successfully'));
eb1af592
NL
187 }
188 else {
a297fb1e
MR
189 $this->messages->add('e', _('error during insertion : the link wasn\'t added'));
190 Tools::logm('error during insertion : the link wasn\'t added ' . $url->getUrl());
b916bcfc 191 }
ec397236 192
a297fb1e
MR
193 if ($autoclose == TRUE) {
194 Tools::redirect('?view=home');
195 } else {
196 Tools::redirect('?view=home&closewin=true');
eb1af592
NL
197 }
198 break;
199 case 'delete':
bc1ee852 200 $msg = 'delete link #' . $id;
8d3275be 201 if ($this->store->deleteById($id, $this->user->getId())) {
eb1af592 202 if (DOWNLOAD_PICTURES) {
3602405e 203 Picture::removeDirectory(ABS_PATH . $id);
eb1af592 204 }
6a361945 205 $this->messages->add('s', _('the link has been deleted successfully'));
eb1af592
NL
206 }
207 else {
6a361945 208 $this->messages->add('e', _('the link wasn\'t deleted'));
bc1ee852 209 $msg = 'error : can\'t delete link #' . $id;
eb1af592 210 }
bc1ee852 211 Tools::logm($msg);
985ce3ec 212 Tools::redirect('?');
eb1af592
NL
213 break;
214 case 'toggle_fav' :
8d3275be 215 $this->store->favoriteById($id, $this->user->getId());
eb1af592 216 Tools::logm('mark as favorite link #' . $id);
c2cf7075
MR
217 if ( Tools::isAjaxRequest() ) {
218 echo 1;
219 exit;
220 }
221 else {
222 Tools::redirect();
223 }
eb1af592
NL
224 break;
225 case 'toggle_archive' :
8d3275be 226 $this->store->archiveById($id, $this->user->getId());
eb1af592 227 Tools::logm('archive link #' . $id);
c2cf7075
MR
228 if ( Tools::isAjaxRequest() ) {
229 echo 1;
230 exit;
231 }
232 else {
233 Tools::redirect();
234 }
eb1af592 235 break;
f14807de
NL
236 case 'archive_all' :
237 $this->store->archiveAll($this->user->getId());
238 Tools::logm('archive all links');
a297fb1e 239 Tools::redirect();
f14807de 240 break;
c432fa16 241 case 'add_tag' :
decc23aa 242 if (isset($_GET['search'])) {
243 //when we want to apply a tag to a search
decc23aa 244 $tags = array($_GET['search']);
245 $allentry_ids = $this->store->search($tags[0], $this->user->getId());
246 $entry_ids = array();
247 foreach ($allentry_ids as $eachentry) {
248 $entry_ids[] = $eachentry[0];
249 }
250 } else { //add a tag to a single article
251 $tags = explode(',', $_POST['value']);
252 $entry_ids = array($_POST['entry_id']);
fb26cc93 253 }
decc23aa 254 foreach($entry_ids as $entry_id) {
255 $entry = $this->store->retrieveOneById($entry_id, $this->user->getId());
256 if (!$entry) {
257 $this->messages->add('e', _('Article not found!'));
258 Tools::logm('error : article not found');
259 Tools::redirect();
260 }
261 //get all already set tags to preven duplicates
262 $already_set_tags = array();
263 $entry_tags = $this->store->retrieveTagsByEntry($entry_id);
264 foreach ($entry_tags as $tag) {
265 $already_set_tags[] = $tag['value'];
266 }
267 foreach($tags as $key => $tag_value) {
268 $value = trim($tag_value);
269 if ($value && !in_array($value, $already_set_tags)) {
270 $tag = $this->store->retrieveTagByValue($value);
271 if (is_null($tag)) {
272 # we create the tag
273 $tag = $this->store->createTag($value);
274 $sequence = '';
275 if (STORAGE == 'postgres') {
276 $sequence = 'tags_id_seq';
277 }
278 $tag_id = $this->store->getLastId($sequence);
fb26cc93 279 }
decc23aa 280 else {
281 $tag_id = $tag['id'];
282 }
283
284 # we assign the tag to the article
285 $this->store->setTagToEntry($tag_id, $entry_id);
286 }
c432fa16 287 }
c432fa16 288 }
9c743ab9 289 $this->messages->add('s', _('The tag has been applied successfully'));
24696800 290 Tools::logm('The tag has been applied successfully');
a297fb1e 291 Tools::redirect();
c432fa16
NL
292 break;
293 case 'remove_tag' :
294 $tag_id = $_GET['tag_id'];
b89d5a2b
NL
295 $entry = $this->store->retrieveOneById($id, $this->user->getId());
296 if (!$entry) {
297 $this->messages->add('e', _('Article not found!'));
298 Tools::logm('error : article not found');
299 Tools::redirect();
300 }
c432fa16 301 $this->store->removeTagForEntry($id, $tag_id);
9c743ab9 302 Tools::logm('tag entry deleted');
24696800 303 if ($this->store->cleanUnusedTag($tag_id)) {
304 Tools::logm('tag deleted');
305 }
9c743ab9 306 $this->messages->add('s', _('The tag has been successfully deleted'));
c432fa16
NL
307 Tools::redirect();
308 break;
eb1af592
NL
309 default:
310 break;
311 }
312 }
313
314 function displayView($view, $id = 0)
315 {
316 $tpl_vars = array();
317
318 switch ($view)
319 {
eb1af592 320 case 'config':
11c680f9
NL
321 $dev_infos = $this->getPocheVersion('dev');
322 $dev = trim($dev_infos[0]);
323 $check_time_dev = date('d-M-Y H:i', $dev_infos[1]);
324 $prod_infos = $this->getPocheVersion('prod');
325 $prod = trim($prod_infos[0]);
326 $check_time_prod = date('d-M-Y H:i', $prod_infos[1]);
031df528
NL
327 $compare_dev = version_compare(POCHE, $dev);
328 $compare_prod = version_compare(POCHE, $prod);
3602405e
NL
329 $themes = $this->tpl->getInstalledThemes();
330 $languages = $this->language->getInstalledLanguages();
72c20a52 331 $token = $this->user->getConfigValue('token');
1810c13b 332 $http_auth = (isset($_SERVER['PHP_AUTH_USER']) || isset($_SERVER['REMOTE_USER'])) ? true : false;
4d99bae8 333 $only_user = ($this->store->listUsers() > 1) ? false : true;
32520785 334 $tpl_vars = array(
00dbaf90 335 'themes' => $themes,
5011388f 336 'languages' => $languages,
32520785
NL
337 'dev' => $dev,
338 'prod' => $prod,
11c680f9
NL
339 'check_time_dev' => $check_time_dev,
340 'check_time_prod' => $check_time_prod,
32520785
NL
341 'compare_dev' => $compare_dev,
342 'compare_prod' => $compare_prod,
72c20a52
NL
343 'token' => $token,
344 'user_id' => $this->user->getId(),
df6afaf0 345 'http_auth' => $http_auth,
4d99bae8 346 'only_user' => $only_user
32520785 347 );
eb1af592
NL
348 Tools::logm('config view');
349 break;
6cab59c3
NL
350 case 'edit-tags':
351 # tags
b89d5a2b
NL
352 $entry = $this->store->retrieveOneById($id, $this->user->getId());
353 if (!$entry) {
354 $this->messages->add('e', _('Article not found!'));
355 Tools::logm('error : article not found');
356 Tools::redirect();
357 }
6cab59c3
NL
358 $tags = $this->store->retrieveTagsByEntry($id);
359 $tpl_vars = array(
c432fa16 360 'entry_id' => $id,
6cab59c3 361 'tags' => $tags,
032e0ca1 362 'entry' => $entry,
4886ed6d
NL
363 );
364 break;
2e2ebe5e 365 case 'tags':
f778e472 366 $token = $this->user->getConfigValue('token');
fb26cc93
MR
367 //if term is set - search tags for this term
368 $term = Tools::checkVar('term');
369 $tags = $this->store->retrieveAllTags($this->user->getId(), $term);
370 if (Tools::isAjaxRequest()) {
371 $result = array();
372 foreach ($tags as $tag) {
373 $result[] = $tag['value'];
374 }
375 echo json_encode($result);
376 exit;
377 }
2e2ebe5e 378 $tpl_vars = array(
f778e472
NL
379 'token' => $token,
380 'user_id' => $this->user->getId(),
2e2ebe5e
NL
381 'tags' => $tags,
382 );
383 break;
a4585f7e
MR
384 case 'search':
385 if (isset($_GET['search'])) {
386 $search = filter_var($_GET['search'], FILTER_SANITIZE_STRING);
387 $tpl_vars['entries'] = $this->store->search($search, $this->user->getId());
388 $count = count($tpl_vars['entries']);
389 $this->pagination->set_total($count);
390 $page_links = str_replace(array('previous', 'next'), array(_('previous'), _('next')),
391 $this->pagination->page_links('?view=' . $view . '?search=' . $search . '&sort=' . $_SESSION['sort'] . '&' ));
392 $tpl_vars['page_links'] = $page_links;
393 $tpl_vars['nb_results'] = $count;
394 $tpl_vars['search_term'] = $search;
395 }
396 break;
eb1af592 397 case 'view':
8d3275be 398 $entry = $this->store->retrieveOneById($id, $this->user->getId());
eb1af592
NL
399 if ($entry != NULL) {
400 Tools::logm('view link #' . $id);
401 $content = $entry['content'];
402 if (function_exists('tidy_parse_string')) {
403 $tidy = tidy_parse_string($content, array('indent'=>true, 'show-body-only' => true), 'UTF8');
404 $tidy->cleanRepair();
405 $content = $tidy->value;
3408ed48 406 }
a3223127 407
3408ed48
NL
408 # flattr checking
409 $flattr = new FlattrItem();
7b171c73
NL
410 $flattr->checkItem($entry['url'], $entry['id']);
411
412 # tags
413 $tags = $this->store->retrieveTagsByEntry($entry['id']);
a3223127 414
3408ed48 415 $tpl_vars = array(
7b171c73
NL
416 'entry' => $entry,
417 'content' => $content,
418 'flattr' => $flattr,
419 'tags' => $tags
3408ed48 420 );
eb1af592
NL
421 }
422 else {
d8d1542e 423 Tools::logm('error in view call : entry is null');
eb1af592
NL
424 }
425 break;
032e0ca1 426 default: # home, favorites, archive and tag views
eb1af592 427 $tpl_vars = array(
3eb04903
N
428 'entries' => '',
429 'page_links' => '',
7f9f5281 430 'nb_results' => '',
6065553c 431 'listmode' => (isset($_COOKIE['listmode']) ? true : false),
eb1af592 432 );
182faf26 433
3602405e 434 //if id is given - we retrieve entries by tag: id is tag id
032e0ca1
MR
435 if ($id) {
436 $tpl_vars['tag'] = $this->store->retrieveTag($id, $this->user->getId());
437 $tpl_vars['id'] = intval($id);
438 }
439
440 $count = $this->store->getEntriesByViewCount($view, $this->user->getId(), $id);
441
442 if ($count > 0) {
443 $this->pagination->set_total($count);
c515ffec 444 $page_links = str_replace(array('previous', 'next'), array(_('previous'), _('next')),
032e0ca1
MR
445 $this->pagination->page_links('?view=' . $view . '&sort=' . $_SESSION['sort'] . (($id)?'&id='.$id:'') . '&' ));
446 $tpl_vars['entries'] = $this->store->getEntriesByView($view, $this->user->getId(), $this->pagination->get_limit(), $id);
3eb04903 447 $tpl_vars['page_links'] = $page_links;
032e0ca1 448 $tpl_vars['nb_results'] = $count;
3eb04903 449 }
6a361945 450 Tools::logm('display ' . $view . ' view');
eb1af592
NL
451 break;
452 }
453
454 return $tpl_vars;
455 }
c765c367 456
07ee09f4 457 /**
182faf26
MR
458 * update the password of the current user.
459 * if MODE_DEMO is TRUE, the password can't be updated.
07ee09f4
NL
460 * @todo add the return value
461 * @todo set the new password in function header like this updatePassword($newPassword)
462 * @return boolean
463 */
c765c367
NL
464 public function updatePassword()
465 {
55821e04 466 if (MODE_DEMO) {
8d3275be 467 $this->messages->add('i', _('in demo mode, you can\'t update your password'));
55821e04 468 Tools::logm('in demo mode, you can\'t do this');
6a361945 469 Tools::redirect('?view=config');
55821e04
NL
470 }
471 else {
472 if (isset($_POST['password']) && isset($_POST['password_repeat'])) {
473 if ($_POST['password'] == $_POST['password_repeat'] && $_POST['password'] != "") {
8d3275be
NL
474 $this->messages->add('s', _('your password has been updated'));
475 $this->store->updatePassword($this->user->getId(), Tools::encodeString($_POST['password'] . $this->user->getUsername()));
c765c367 476 Session::logout();
8d3275be 477 Tools::logm('password updated');
c765c367
NL
478 Tools::redirect();
479 }
480 else {
8d3275be 481 $this->messages->add('e', _('the two fields have to be filled & the password must be the same in the two fields'));
6a361945 482 Tools::redirect('?view=config');
c765c367
NL
483 }
484 }
485 }
486 }
182faf26 487
df6afaf0
DS
488 /**
489 * get credentials from differents sources
490 * it redirects the user to the $referer link
491 * @return array
492 */
1810c13b
NL
493 private function credentials() {
494 if(isset($_SERVER['PHP_AUTH_USER'])) {
6af66b11 495 return array($_SERVER['PHP_AUTH_USER'],'php_auth',true);
1810c13b
NL
496 }
497 if(!empty($_POST['login']) && !empty($_POST['password'])) {
6af66b11 498 return array($_POST['login'],$_POST['password'],false);
1810c13b
NL
499 }
500 if(isset($_SERVER['REMOTE_USER'])) {
6af66b11 501 return array($_SERVER['REMOTE_USER'],'http_auth',true);
1810c13b 502 }
5cfafc61 503
6af66b11
MR
504 return array(false,false,false);
505 }
df6afaf0 506
07ee09f4
NL
507 /**
508 * checks if login & password are correct and save the user in session.
509 * it redirects the user to the $referer link
510 * @param string $referer the url to redirect after login
511 * @todo add the return value
512 * @return boolean
513 */
c765c367
NL
514 public function login($referer)
515 {
6af66b11 516 list($login,$password,$isauthenticated)=$this->credentials();
df6afaf0
DS
517 if($login === false || $password === false) {
518 $this->messages->add('e', _('login failed: you have to fill all fields'));
519 Tools::logm('login failed');
520 Tools::redirect();
521 }
522 if (!empty($login) && !empty($password)) {
6af66b11 523 $user = $this->store->login($login, Tools::encodeString($password . $login), $isauthenticated);
7ce7ec4c
NL
524 if ($user != array()) {
525 # Save login into Session
6af66b11
MR
526 $longlastingsession = isset($_POST['longlastingsession']);
527 $passwordTest = ($isauthenticated) ? $user['password'] : Tools::encodeString($password . $login);
528 Session::login($user['username'], $user['password'], $login, $passwordTest, $longlastingsession, array('poche_user' => new User($user)));
26929c08 529 $this->messages->add('s', _('welcome to your wallabag'));
8d3275be 530 Tools::logm('login successful');
c765c367
NL
531 Tools::redirect($referer);
532 }
8d3275be 533 $this->messages->add('e', _('login failed: bad login or password'));
c765c367
NL
534 Tools::logm('login failed');
535 Tools::redirect();
c765c367
NL
536 }
537 }
538
07ee09f4
NL
539 /**
540 * log out the poche user. It cleans the session.
541 * @todo add the return value
182faf26 542 * @return boolean
07ee09f4 543 */
c765c367
NL
544 public function logout()
545 {
7ce7ec4c 546 $this->user = array();
c765c367 547 Session::logout();
b916bcfc 548 Tools::logm('logout');
c765c367
NL
549 Tools::redirect();
550 }
551
07ee09f4
NL
552 /**
553 * import datas into your poche
182faf26 554 * @return boolean
07ee09f4 555 */
182faf26
MR
556 public function import() {
557
558 if ( isset($_FILES['file']) ) {
5ce39784
MR
559 Tools::logm('Import stated: parsing file');
560
182faf26
MR
561 // assume, that file is in json format
562 $str_data = file_get_contents($_FILES['file']['tmp_name']);
563 $data = json_decode($str_data, true);
564
565 if ( $data === null ) {
566 //not json - assume html
567 $html = new simple_html_dom();
568 $html->load_file($_FILES['file']['tmp_name']);
569 $data = array();
570 $read = 0;
571 foreach (array('ol','ul') as $list) {
572 foreach ($html->find($list) as $ul) {
86da3988
MR
573 foreach ($ul->find('li') as $li) {
574 $tmpEntry = array();
a8ef1f3f
MR
575 $a = $li->find('a');
576 $tmpEntry['url'] = $a[0]->href;
577 $tmpEntry['tags'] = $a[0]->tags;
578 $tmpEntry['is_read'] = $read;
579 if ($tmpEntry['url']) {
580 $data[] = $tmpEntry;
581 }
86da3988
MR
582 }
583 # the second <ol/ul> is for read links
584 $read = ((sizeof($data) && $read)?0:1);
182faf26
MR
585 }
586 }
63c35580 587 }
182faf26 588
a297fb1e
MR
589 //for readability structure
590 foreach ($data as $record) {
591 if (is_array($record)) {
592 $data[] = $record;
593 foreach ($record as $record2) {
594 if (is_array($record2)) {
86da3988 595 $data[] = $record2;
a297fb1e
MR
596 }
597 }
598 }
599 }
600
86da3988 601 $urlsInserted = array(); //urls of articles inserted
182faf26 602 foreach ($data as $record) {
a297fb1e 603 $url = trim( isset($record['article__url']) ? $record['article__url'] : (isset($record['url']) ? $record['url'] : '') );
86da3988 604 if ( $url and !in_array($url, $urlsInserted) ) {
182faf26
MR
605 $title = (isset($record['title']) ? $record['title'] : _('Untitled - Import - ').'</a> <a href="./?import">'._('click to finish import').'</a><a>');
606 $body = (isset($record['content']) ? $record['content'] : '');
a297fb1e
MR
607 $isRead = (isset($record['is_read']) ? intval($record['is_read']) : (isset($record['archive'])?intval($record['archive']):0));
608 $isFavorite = (isset($record['is_fav']) ? intval($record['is_fav']) : (isset($record['favorite'])?intval($record['favorite']):0) );
182faf26
MR
609 //insert new record
610 $id = $this->store->add($url, $title, $body, $this->user->getId(), $isFavorite, $isRead);
611 if ( $id ) {
86da3988
MR
612 $urlsInserted[] = $url; //add
613
182faf26 614 if ( isset($record['tags']) && trim($record['tags']) ) {
86da3988 615 //@TODO: set tags
182faf26
MR
616
617 }
618 }
619 }
620 }
621
86da3988 622 $i = sizeof($urlsInserted);
182faf26
MR
623 if ( $i > 0 ) {
624 $this->messages->add('s', _('Articles inserted: ').$i._('. Please note, that some may be marked as "read".'));
625 }
5ce39784 626 Tools::logm('Import of articles finished: '.$i.' articles added (w/o content if not provided).');
182faf26
MR
627 }
628 //file parsing finished here
629
630 //now download article contents if any
631
632 //check if we need to download any content
633 $recordsDownloadRequired = $this->store->retrieveUnfetchedEntriesCount($this->user->getId());
634 if ( $recordsDownloadRequired == 0 ) {
635 //nothing to download
636 $this->messages->add('s', _('Import finished.'));
5ce39784 637 Tools::logm('Import finished completely');
182faf26
MR
638 Tools::redirect();
639 }
640 else {
641 //if just inserted - don't download anything, download will start in next reload
642 if ( !isset($_FILES['file']) ) {
643 //download next batch
5ce39784 644 Tools::logm('Fetching next batch of articles...');
182faf26
MR
645 $items = $this->store->retrieveUnfetchedEntries($this->user->getId(), IMPORT_LIMIT);
646
0f859c6f 647 $purifier = $this->getPurifier();
182faf26
MR
648
649 foreach ($items as $item) {
86da3988 650 $url = new Url(base64_encode($item['url']));
5ce39784 651 Tools::logm('Fetching article '.$item['id']);
86da3988 652 $content = Tools::getPageContent($url);
182faf26 653
86da3988
MR
654 $title = (($content['rss']['channel']['item']['title'] != '') ? $content['rss']['channel']['item']['title'] : _('Untitled'));
655 $body = (($content['rss']['channel']['item']['description'] != '') ? $content['rss']['channel']['item']['description'] : _('Undefined'));
182faf26 656
86da3988
MR
657 //clean content to prevent xss attack
658 $title = $purifier->purify($title);
659 $body = $purifier->purify($body);
182faf26 660
86da3988 661 $this->store->updateContentAndTitle($item['id'], $title, $body, $this->user->getId());
5ce39784 662 Tools::logm('Article '.$item['id'].' updated.');
182faf26
MR
663 }
664
63c35580 665 }
182faf26
MR
666 }
667
668 return array('includeImport'=>true, 'import'=>array('recordsDownloadRequired'=>$recordsDownloadRequired, 'recordsUnderDownload'=> IMPORT_LIMIT, 'delay'=> IMPORT_DELAY * 1000) );
63c35580 669 }
c765c367 670
07ee09f4
NL
671 /**
672 * export poche entries in json
673 * @return json all poche entries
674 */
a8ef1f3f
MR
675 public function export() {
676 $filename = "wallabag-export-".$this->user->getId()."-".date("Y-m-d").".json";
677 header('Content-Disposition: attachment; filename='.$filename);
678
679 $entries = $this->store->retrieveAll($this->user->getId());
680 echo $this->tpl->render('export.twig', array(
681 'export' => Tools::renderJson($entries),
682 ));
683 Tools::logm('export view');
c765c367 684 }
32520785 685
07ee09f4 686 /**
a3436d4c 687 * Checks online the latest version of poche and cache it
07ee09f4
NL
688 * @param string $which 'prod' or 'dev'
689 * @return string latest $which version
690 */
a8ef1f3f
MR
691 private function getPocheVersion($which = 'prod') {
692 $cache_file = CACHE . '/' . $which;
693 $check_time = time();
694
695 # checks if the cached version file exists
696 if (file_exists($cache_file) && (filemtime($cache_file) > (time() - 86400 ))) {
697 $version = file_get_contents($cache_file);
698 $check_time = filemtime($cache_file);
699 } else {
700 $version = file_get_contents('http://static.wallabag.org/versions/' . $which);
701 file_put_contents($cache_file, $version, LOCK_EX);
702 }
703 return array($version, $check_time);
32520785 704 }
72c20a52
NL
705
706 public function generateToken()
707 {
a8ef1f3f
MR
708 if (ini_get('open_basedir') === '') {
709 if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
710 echo 'This is a server using Windows!';
711 // alternative to /dev/urandom for Windows
712 $token = substr(base64_encode(uniqid(mt_rand(), true)), 0, 20);
713 } else {
714 $token = substr(base64_encode(file_get_contents('/dev/urandom', false, null, 0, 20)), 0, 15);
72c20a52 715 }
a8ef1f3f
MR
716 }
717 else {
718 $token = substr(base64_encode(uniqid(mt_rand(), true)), 0, 20);
719 }
72c20a52 720
a8ef1f3f
MR
721 $token = str_replace('+', '', $token);
722 $this->store->updateUserConfig($this->user->getId(), 'token', $token);
723 $currentConfig = $_SESSION['poche_user']->config;
724 $currentConfig['token'] = $token;
725 $_SESSION['poche_user']->setConfig($currentConfig);
726 Tools::redirect();
72c20a52
NL
727 }
728
f778e472 729 public function generateFeeds($token, $user_id, $tag_id, $type = 'home')
72c20a52 730 {
f778e472 731 $allowed_types = array('home', 'fav', 'archive', 'tag');
72c20a52
NL
732 $config = $this->store->getConfigUser($user_id);
733
17b2afef 734 if ($config == null) {
30bd2735 735 die(sprintf(_('User with this id (%d) does not exist.'), $user_id));
17b2afef
NL
736 }
737
cbc75bef 738 if (!in_array($type, $allowed_types) || $token != $config['token']) {
72c20a52
NL
739 die(_('Uh, there is a problem while generating feeds.'));
740 }
741 // Check the token
742
9e7c840b 743 $feed = new FeedWriter(RSS2);
2e4440c3 744 $feed->setTitle('wallabag — ' . $type . ' feed');
72c20a52 745 $feed->setLink(Tools::getPocheUrl());
223268c2
NL
746 $feed->setChannelElement('pubDate', date(DATE_RSS , time()));
747 $feed->setChannelElement('generator', 'wallabag');
748 $feed->setDescription('wallabag ' . $type . ' elements');
72c20a52 749
f778e472 750 if ($type == 'tag') {
b89d5a2b 751 $entries = $this->store->retrieveEntriesByTag($tag_id, $user_id);
f778e472
NL
752 }
753 else {
754 $entries = $this->store->getEntriesByView($type, $user_id);
755 }
756
72c20a52
NL
757 if (count($entries) > 0) {
758 foreach ($entries as $entry) {
759 $newItem = $feed->createNewItem();
0b57c682 760 $newItem->setTitle($entry['title']);
f86784c2 761 $newItem->setSource(Tools::getPocheUrl() . '?view=view&amp;id=' . $entry['id']);
ed02e38e 762 $newItem->setLink($entry['url']);
72c20a52
NL
763 $newItem->setDate(time());
764 $newItem->setDescription($entry['content']);
765 $feed->addItem($newItem);
766 }
767 }
768
769 $feed->genarateFeed();
770 exit;
771 }
6285e57c
NL
772
773 public function emptyCache() {
774 $files = new RecursiveIteratorIterator(
775 new RecursiveDirectoryIterator(CACHE, RecursiveDirectoryIterator::SKIP_DOTS),
776 RecursiveIteratorIterator::CHILD_FIRST
777 );
778
779 foreach ($files as $fileinfo) {
780 $todo = ($fileinfo->isDir() ? 'rmdir' : 'unlink');
781 $todo($fileinfo->getRealPath());
782 }
783
784 Tools::logm('empty cache');
785 $this->messages->add('s', _('Cache deleted.'));
786 Tools::redirect();
787 }
0f859c6f
MR
788
789 /**
790 * return new purifier object with actual config
791 */
792 protected function getPurifier() {
ec15d0a7 793 $config = HTMLPurifier_Config::createDefault();
794 $config->set('Cache.SerializerPath', CACHE);
795 $config->set('HTML.SafeIframe', true);
35d4e275 796
0b9bb8cb
MR
797 //allow YouTube, Vimeo and dailymotion videos
798 $config->set('URI.SafeIframeRegexp', '%^(https?:)?//(www\.youtube(?:-nocookie)?\.com/embed/|player\.vimeo\.com/video/|www\.dailymotion\.com/embed/video/)%');
ec15d0a7 799
0f859c6f
MR
800 return new HTMLPurifier($config);
801 }
cbc75bef 802
87090d8a 803 /**
804 * handle epub
805 */
806 public function createEpub() {
cbc75bef 807
7ec445b0 808 switch ($_GET['method']) {
809 case 'id':
87090d8a 810 $entryID = filter_var($_GET['id'],FILTER_SANITIZE_NUMBER_INT);
811 $entry = $this->store->retrieveOneById($entryID, $this->user->getId());
812 $entries = array($entry);
f2b6b4e2 813 $bookTitle = $entry['title'];
f3f0b113 814 $bookFileName = substr($bookTitle, 0, 200);
7ec445b0 815 break;
816 case 'all':
817 $entries = $this->store->retrieveAll($this->user->getId());
f3f0b113 818 $bookTitle = sprintf(_('All my articles on '), date(_('d.m.y'))); #translatable because each country has it's own date format system
819 $bookFileName = _('Allarticles') . date(_('dmY'));
7ec445b0 820 break;
821 case 'tag':
822 $tag = filter_var($_GET['tag'],FILTER_SANITIZE_STRING);
823 $tags_id = $this->store->retrieveAllTags($this->user->getId(),$tag);
824 $tag_id = $tags_id[0]["id"]; // we take the first result, which is supposed to match perfectly. There must be a workaround.
825 $entries = $this->store->retrieveEntriesByTag($tag_id,$this->user->getId());
f3f0b113 826 $bookTitle = sprintf(_('Articles tagged %s'),$tag);
827 $bookFileName = substr(sprintf(_('Tag %s'),$tag), 0, 200);
7ec445b0 828 break;
829 case 'category':
830 $category = filter_var($_GET['category'],FILTER_SANITIZE_STRING);
831 $entries = $this->store->getEntriesByView($category,$this->user->getId());
f3f0b113 832 $bookTitle = sprintf(_('All articles in category %s'), $category);
833 $bookFileName = substr(sprintf(_('Category %s'),$category), 0, 200);
7ec445b0 834 break;
835 case 'search':
836 $search = filter_var($_GET['search'],FILTER_SANITIZE_STRING);
837 $entries = $this->store->search($search,$this->user->getId());
f3f0b113 838 $bookTitle = sprintf(_('All articles for search %s'), $search);
839 $bookFileName = substr(sprintf(_('Search %s'), $search), 0, 200);
7ec445b0 840 break;
841 case 'default':
842 die(_('Uh, there is a problem while generating epub.'));
cbc75bef 843
87090d8a 844 }
7ec445b0 845
87090d8a 846 $content_start =
f2b6b4e2 847 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
848 . "<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n"
849 . "<head>"
850 . "<meta http-equiv=\"Default-Style\" content=\"text/html; charset=utf-8\" />\n"
851 . "<title>wallabag articles book</title>\n"
87090d8a 852 . "</head>\n"
853 . "<body>\n";
854
855 $bookEnd = "</body>\n</html>\n";
cbc75bef 856
7ec445b0 857 $log = new Logger("wallabag", TRUE);
87090d8a 858 $fileDir = CACHE;
87090d8a 859
ec15d0a7 860 $book = new EPub(EPub::BOOK_VERSION_EPUB3, DEBUG_POCHE);
87090d8a 861 $log->logLine("new EPub()");
862 $log->logLine("EPub class version: " . EPub::VERSION);
863 $log->logLine("EPub Req. Zip version: " . EPub::REQ_ZIP_VERSION);
864 $log->logLine("Zip version: " . Zip::VERSION);
865 $log->logLine("getCurrentServerURL: " . $book->getCurrentServerURL());
866 $log->logLine("getCurrentPageURL..: " . $book->getCurrentPageURL());
cbc75bef 867
34acb02c 868 $book->setTitle(_('wallabag\'s articles'));
87090d8a 869 $book->setIdentifier("http://$_SERVER[HTTP_HOST]", EPub::IDENTIFIER_URI); // Could also be the ISBN number, prefered for published books, or a UUID.
870 //$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.
34acb02c 871 $book->setDescription(_("Some articles saved on my wallabag"));
87090d8a 872 $book->setAuthor("wallabag","wallabag");
873 $book->setPublisher("wallabag","wallabag"); // I hope this is a non existant address :)
874 $book->setDate(time()); // Strictly not needed as the book date defaults to time().
875 //$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.
876 $book->setSourceURL("http://$_SERVER[HTTP_HOST]");
cbc75bef 877
87090d8a 878 $book->addDublinCoreMetadata(DublinCore::CONTRIBUTOR, "PHP");
4877836b 879 $book->addDublinCoreMetadata(DublinCore::CONTRIBUTOR, "wallabag");
cbc75bef 880
87090d8a 881 $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";
cbc75bef 882
4877836b 883 $log->logLine("Add Cover");
cbc75bef 884
f2b6b4e2 885 $fullTitle = "<h1> " . $bookTitle . "</h1>\n";
cbc75bef 886
f2b6b4e2 887 $book->setCoverImage("Cover.png", file_get_contents("themes/baggy/img/apple-touch-icon-152.png"), "image/png", $fullTitle);
cbc75bef 888
e212e6b1 889 $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;
cbc75bef 890
f2b6b4e2 891 //$book->addChapter("Table of Contents", "TOC.xhtml", NULL, false, EPub::EXTERNAL_REF_IGNORE);
892 $book->addChapter("Notices", "Cover2.html", $cover);
cbc75bef 893
f2b6b4e2 894 $book->buildTOC();
cbc75bef 895
e212e6b1 896 foreach ($entries as $entry) { //set tags as subjects
87090d8a 897 $tags = $this->store->retrieveTagsByEntry($entry['id']);
898 foreach ($tags as $tag) {
f2b6b4e2 899 $book->setSubject($tag['value']);
87090d8a 900 }
cbc75bef 901
87090d8a 902 $log->logLine("Set up parameters");
cbc75bef 903
87090d8a 904 $chapter = $content_start . $entry['content'] . $bookEnd;
7ec445b0 905 $book->addChapter($entry['title'], htmlspecialchars($entry['title']) . ".html", $chapter, true, EPub::EXTERNAL_REF_ADD);
4877836b 906 $log->logLine("Added chapter " . $entry['title']);
f2b6b4e2 907 }
87090d8a 908
cbc75bef 909 if (DEBUG_POCHE) {
e212e6b1 910 $epuplog = $book->getLog();
911 $book->addChapter("Log", "Log.html", $content_start . $log->getLog() . "\n</pre>" . $bookEnd); // log generation
87090d8a 912 }
913 $book->finalize();
f3f0b113 914 $zipData = $book->sendBook($bookFileName);
87090d8a 915 }
df6afaf0 916}