]> git.immae.eu Git - github/wallabag/wallabag.git/blame - inc/poche/Poche.class.php
new fields in database, reading time / date and domain name are stored
[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
NL
7 * @copyright 2013
8 * @license http://www.wtfpl.net/ see COPYING file
9 */
10
11class Poche
12{
00dbaf90
NL
13 public static $canRenderTemplates = true;
14 public static $configFileAvailable = true;
15
7ce7ec4c 16 public $user;
eb1af592
NL
17 public $store;
18 public $tpl;
55821e04 19 public $messages;
6a361945 20 public $pagination;
182faf26 21
00dbaf90 22 private $currentTheme = '';
5011388f 23 private $currentLanguage = '';
9d3b88b3 24 private $notInstalledMessage = array();
eb1af592 25
c9bd17a1
NL
26 private $language_names = array(
27 'cs_CZ.utf8' => 'čeština',
28 'de_DE.utf8' => 'German',
29 'en_EN.utf8' => 'English',
30 'es_ES.utf8' => 'Español',
31 'fa_IR.utf8' => 'فارسی',
32 'fr_FR.utf8' => 'Français',
33 'it_IT.utf8' => 'Italiano',
34 'pl_PL.utf8' => 'Polski',
5805ac45 35 'pt_BR.utf8' => 'Português (Brasil)',
c9bd17a1
NL
36 'ru_RU.utf8' => 'Pусский',
37 'sl_SI.utf8' => 'Slovenščina',
cbcae403 38 'uk_UA.utf8' => 'Українська',
c9bd17a1 39 );
00dbaf90 40 public function __construct()
eb1af592 41 {
9d3b88b3
NL
42 if ($this->configFileIsAvailable()) {
43 $this->init();
00dbaf90 44 }
182faf26 45
9d3b88b3
NL
46 if ($this->themeIsInstalled()) {
47 $this->initTpl();
00dbaf90 48 }
182faf26 49
9d3b88b3
NL
50 if ($this->systemIsInstalled()) {
51 $this->store = new Database();
52 $this->messages = new Messages();
53 # installation
54 if (! $this->store->isInstalled()) {
55 $this->install();
56 }
5cfafc61 57 $this->store->checkTags();
eb1af592 58 }
eb1af592 59 }
182faf26
MR
60
61 private function init()
00dbaf90
NL
62 {
63 Tools::initPhp();
eb1af592 64
00dbaf90
NL
65 if (isset($_SESSION['poche_user']) && $_SESSION['poche_user'] != array()) {
66 $this->user = $_SESSION['poche_user'];
67 } else {
68 # fake user, just for install & login screens
69 $this->user = new User();
70 $this->user->setConfig($this->getDefaultConfig());
71 }
72
73 # l10n
74 $language = $this->user->getConfigValue('language');
b6413975 75 @putenv('LC_ALL=' . $language);
00dbaf90 76 setlocale(LC_ALL, $language);
182faf26
MR
77 bindtextdomain($language, LOCALE);
78 textdomain($language);
00dbaf90
NL
79
80 # Pagination
81 $this->pagination = new Paginator($this->user->getConfigValue('pager'), 'p');
182faf26 82
00dbaf90
NL
83 # Set up theme
84 $themeDirectory = $this->user->getConfigValue('theme');
182faf26 85
00dbaf90
NL
86 if ($themeDirectory === false) {
87 $themeDirectory = DEFAULT_THEME;
88 }
182faf26 89
00dbaf90 90 $this->currentTheme = $themeDirectory;
5011388f
NL
91
92 # Set up language
93 $languageDirectory = $this->user->getConfigValue('language');
182faf26 94
5011388f
NL
95 if ($languageDirectory === false) {
96 $languageDirectory = DEFAULT_THEME;
97 }
182faf26 98
5011388f 99 $this->currentLanguage = $languageDirectory;
00dbaf90
NL
100 }
101
102 public function configFileIsAvailable() {
103 if (! self::$configFileAvailable) {
43c7b978 104 $this->notInstalledMessage[] = 'You have to copy (don\'t just rename!) inc/poche/config.inc.default.php to inc/poche/config.inc.php.';
00dbaf90
NL
105
106 return false;
107 }
108
109 return true;
110 }
182faf26 111
00dbaf90 112 public function themeIsInstalled() {
9d3b88b3 113 $passTheme = TRUE;
00dbaf90
NL
114 # Twig is an absolute requirement for Poche to function. Abort immediately if the Composer installer hasn't been run yet
115 if (! self::$canRenderTemplates) {
41265e07 116 $this->notInstalledMessage[] = '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.';
9d3b88b3 117 $passTheme = FALSE;
00dbaf90 118 }
7f17a38d
NL
119
120 if (! is_writable(CACHE)) {
9d3b88b3 121 $this->notInstalledMessage[] = 'You don\'t have write access on cache directory.';
7f17a38d
NL
122
123 self::$canRenderTemplates = false;
124
9d3b88b3 125 $passTheme = FALSE;
182faf26
MR
126 }
127
00dbaf90 128 # Check if the selected theme and its requirements are present
f4fbfaa7
NL
129 $theme = $this->getTheme();
130
131 if ($theme != '' && ! is_dir(THEME . '/' . $theme)) {
132 $this->notInstalledMessage[] = 'The currently selected theme (' . $theme . ') does not seem to be properly installed (Missing directory: ' . THEME . '/' . $theme . ')';
182faf26 133
00dbaf90 134 self::$canRenderTemplates = false;
182faf26 135
9d3b88b3 136 $passTheme = FALSE;
00dbaf90 137 }
182faf26 138
f4fbfaa7
NL
139 $themeInfo = $this->getThemeInfo($theme);
140 if (isset($themeInfo['requirements']) && is_array($themeInfo['requirements'])) {
141 foreach ($themeInfo['requirements'] as $requiredTheme) {
142 if (! is_dir(THEME . '/' . $requiredTheme)) {
143 $this->notInstalledMessage[] = 'The required "' . $requiredTheme . '" theme is missing for the current theme (' . $theme . ')';
182faf26 144
f4fbfaa7 145 self::$canRenderTemplates = false;
182faf26 146
f4fbfaa7
NL
147 $passTheme = FALSE;
148 }
00dbaf90
NL
149 }
150 }
9d3b88b3
NL
151
152 if (!$passTheme) {
153 return FALSE;
154 }
155
182faf26 156
00dbaf90
NL
157 return true;
158 }
182faf26 159
4a291288
NL
160 /**
161 * all checks before installation.
00dbaf90 162 * @todo move HTML to template
182faf26 163 * @return boolean
4a291288 164 */
00dbaf90 165 public function systemIsInstalled()
eb1af592 166 {
9d3b88b3 167 $msg = TRUE;
182faf26 168
00dbaf90 169 $configSalt = defined('SALT') ? constant('SALT') : '';
182faf26 170
00dbaf90 171 if (empty($configSalt)) {
9d3b88b3
NL
172 $this->notInstalledMessage[] = 'You have not yet filled in the SALT value in the config.inc.php file.';
173 $msg = FALSE;
174 }
175 if (STORAGE == 'sqlite' && ! file_exists(STORAGE_SQLITE)) {
00dbaf90 176 Tools::logm('sqlite file doesn\'t exist');
9d3b88b3
NL
177 $this->notInstalledMessage[] = 'sqlite file doesn\'t exist, you can find it in install folder. Copy it in /db folder.';
178 $msg = FALSE;
179 }
180 if (is_dir(ROOT . '/install') && ! DEBUG_POCHE) {
181 $this->notInstalledMessage[] = 'you have to delete the /install folder before using poche.';
182 $msg = FALSE;
183 }
184 if (STORAGE == 'sqlite' && ! is_writable(STORAGE_SQLITE)) {
bb5a7d9e 185 Tools::logm('you don\'t have write access on sqlite file');
9d3b88b3
NL
186 $this->notInstalledMessage[] = 'You don\'t have write access on sqlite file.';
187 $msg = FALSE;
bb5a7d9e 188 }
00dbaf90 189
9d3b88b3 190 if (! $msg) {
00dbaf90 191 return false;
8d3275be 192 }
7ce7ec4c 193
00dbaf90
NL
194 return true;
195 }
182faf26 196
00dbaf90
NL
197 public function getNotInstalledMessage() {
198 return $this->notInstalledMessage;
4a291288 199 }
eb1af592 200
4a291288
NL
201 private function initTpl()
202 {
00dbaf90 203 $loaderChain = new Twig_Loader_Chain();
f4fbfaa7 204 $theme = $this->getTheme();
182faf26 205
00dbaf90
NL
206 # add the current theme as first to the loader chain so Twig will look there first for overridden template files
207 try {
f4fbfaa7 208 $loaderChain->addLoader(new Twig_Loader_Filesystem(THEME . '/' . $theme));
00dbaf90
NL
209 } catch (Twig_Error_Loader $e) {
210 # @todo isInstalled() should catch this, inject Twig later
f4fbfaa7 211 die('The currently selected theme (' . $theme . ') does not seem to be properly installed (' . THEME . '/' . $theme .' is missing)');
00dbaf90 212 }
182faf26 213
00dbaf90 214 # add all required themes to the loader chain
f4fbfaa7
NL
215 $themeInfo = $this->getThemeInfo($theme);
216 if (isset($themeInfo['requirements']) && is_array($themeInfo['requirements'])) {
217 foreach ($themeInfo['requirements'] as $requiredTheme) {
218 try {
219 $loaderChain->addLoader(new Twig_Loader_Filesystem(THEME . '/' . $requiredTheme));
220 } catch (Twig_Error_Loader $e) {
221 # @todo isInstalled() should catch this, inject Twig later
222 die('The required "' . $requiredTheme . '" theme is missing for the current theme (' . $theme . ')');
223 }
00dbaf90
NL
224 }
225 }
182faf26 226
bc1ee852 227 if (DEBUG_POCHE) {
f4fbfaa7 228 $twigParams = array();
00dbaf90 229 } else {
f4fbfaa7 230 $twigParams = array('cache' => CACHE);
bc1ee852 231 }
182faf26 232
f4fbfaa7 233 $this->tpl = new Twig_Environment($loaderChain, $twigParams);
eb1af592 234 $this->tpl->addExtension(new Twig_Extensions_Extension_I18n());
eb1af592 235 }
b6413975 236
4d99bae8 237 public function createNewUser() {
238 if (isset($_GET['newuser'])){
239 if ($_POST['newusername'] != "" && $_POST['password4newuser'] != ""){
240 $newusername = filter_var($_POST['newusername'], FILTER_SANITIZE_STRING);
b6413975 241 if (!$this->store->userExists($newusername)){
4d99bae8 242 if ($this->store->install($newusername, Tools::encodeString($_POST['password4newuser'] . $newusername))) {
243 Tools::logm('The new user '.$newusername.' has been installed');
244 $this->messages->add('s', sprintf(_('The new user %s has been installed. Do you want to <a href="?logout">logout ?</a>'),$newusername));
245 Tools::redirect();
246 }
247 else {
248 Tools::logm('error during adding new user');
249 Tools::redirect();
250 }
251 }
252 else {
253 $this->messages->add('e', sprintf(_('Error : An user with the name %s already exists !'),$newusername));
254 Tools::logm('An user with the name '.$newusername.' already exists !');
255 Tools::redirect();
256 }
257 }
258 }
259 }
b6413975 260
4d99bae8 261 public function deleteUser(){
262 if (isset($_GET['deluser'])){
263 if ($this->store->listUsers() > 1) {
264 if (Tools::encodeString($_POST['password4deletinguser'].$this->user->getUsername()) == $this->store->getUserPassword($this->user->getId())) {
265 $username = $this->user->getUsername();
266 $this->store->deleteUserConfig($this->user->getId());
267 Tools::logm('The configuration for user '. $username .' has been deleted !');
268 $this->store->deleteTagsEntriesAndEntries($this->user->getId());
269 Tools::logm('The entries for user '. $username .' has been deleted !');
270 $this->store->deleteUser($this->user->getId());
271 Tools::logm('User '. $username .' has been completely deleted !');
272 Session::logout();
273 Tools::logm('logout');
274 Tools::redirect();
275 $this->messages->add('s', sprintf(_('User %s has been successfully deleted !'),$newusername));
276 }
277 else {
278 Tools::logm('Bad password !');
279 $this->messages->add('e', _('Error : The password is wrong !'));
280 }
281 }
282 else {
283 Tools::logm('Only user !');
284 $this->messages->add('e', _('Error : You are the only user, you cannot delete your account !'));
285 }
286 }
287 }
eb1af592 288
f4fbfaa7 289 private function install()
eb1af592
NL
290 {
291 Tools::logm('poche still not installed');
292 echo $this->tpl->render('install.twig', array(
00dbaf90
NL
293 'token' => Session::getToken(),
294 'theme' => $this->getTheme(),
295 'poche_url' => Tools::getPocheUrl()
eb1af592
NL
296 ));
297 if (isset($_GET['install'])) {
182faf26 298 if (($_POST['password'] == $_POST['password_repeat'])
eb1af592
NL
299 && $_POST['password'] != "" && $_POST['login'] != "") {
300 # let's rock, install poche baby !
bb5a7d9e
NL
301 if ($this->store->install($_POST['login'], Tools::encodeString($_POST['password'] . $_POST['login'])))
302 {
303 Session::logout();
304 Tools::logm('poche is now installed');
305 Tools::redirect();
306 }
6a361945
NL
307 }
308 else {
309 Tools::logm('error during installation');
eb1af592
NL
310 Tools::redirect();
311 }
312 }
313 exit();
314 }
182faf26 315
00dbaf90
NL
316 public function getTheme() {
317 return $this->currentTheme;
318 }
5011388f 319
f4fbfaa7
NL
320 /**
321 * Provides theme information by parsing theme.ini file if present in the theme's root directory.
322 * In all cases, the following data will be returned:
323 * - name: theme's name, or key if the theme is unnamed,
324 * - current: boolean informing if the theme is the current user theme.
325 *
326 * @param string $theme Theme key (directory name)
327 * @return array|boolean Theme information, or false if the theme doesn't exist.
328 */
329 public function getThemeInfo($theme) {
330 if (!is_dir(THEME . '/' . $theme)) {
331 return false;
332 }
333
334 $themeIniFile = THEME . '/' . $theme . '/theme.ini';
335 $themeInfo = array();
336
337 if (is_file($themeIniFile) && is_readable($themeIniFile)) {
338 $themeInfo = parse_ini_file($themeIniFile);
339 }
182faf26 340
f4fbfaa7
NL
341 if ($themeInfo === false) {
342 $themeInfo = array();
343 }
344 if (!isset($themeInfo['name'])) {
345 $themeInfo['name'] = $theme;
346 }
347 $themeInfo['current'] = ($theme === $this->getTheme());
348
349 return $themeInfo;
5011388f 350 }
182faf26 351
00dbaf90
NL
352 public function getInstalledThemes() {
353 $handle = opendir(THEME);
354 $themes = array();
f4fbfaa7 355
00dbaf90
NL
356 while (($theme = readdir($handle)) !== false) {
357 # Themes are stored in a directory, so all directory names are themes
358 # @todo move theme installation data to database
f4fbfaa7 359 if (!is_dir(THEME . '/' . $theme) || in_array($theme, array('.', '..'))) {
00dbaf90
NL
360 continue;
361 }
f4fbfaa7
NL
362
363 $themes[$theme] = $this->getThemeInfo($theme);
00dbaf90 364 }
f4fbfaa7 365
3ade95a3
NL
366 ksort($themes);
367
00dbaf90
NL
368 return $themes;
369 }
eb1af592 370
f4fbfaa7
NL
371 public function getLanguage() {
372 return $this->currentLanguage;
373 }
374
5011388f
NL
375 public function getInstalledLanguages() {
376 $handle = opendir(LOCALE);
377 $languages = array();
182faf26 378
5011388f
NL
379 while (($language = readdir($handle)) !== false) {
380 # Languages are stored in a directory, so all directory names are languages
381 # @todo move language installation data to database
cbcae403 382 if (! is_dir(LOCALE . '/' . $language) || in_array($language, array('..', '.', 'tools'))) {
5011388f
NL
383 continue;
384 }
182faf26 385
5011388f 386 $current = false;
182faf26 387
5011388f
NL
388 if ($language === $this->getLanguage()) {
389 $current = true;
390 }
182faf26 391
cbcae403 392 $languages[] = array('name' => (isset($this->language_names[$language]) ? $this->language_names[$language] : $language), 'value' => $language, 'current' => $current);
5011388f 393 }
182faf26 394
5011388f
NL
395 return $languages;
396 }
397
8d3275be 398 public function getDefaultConfig()
182faf26 399 {
8d3275be
NL
400 return array(
401 'pager' => PAGINATION,
402 'language' => LANG,
00dbaf90
NL
403 'theme' => DEFAULT_THEME
404 );
8d3275be
NL
405 }
406
eb1af592
NL
407 /**
408 * Call action (mark as fav, archive, delete, etc.)
409 */
926acd7b 410 public function action($action, Url $url, $id = 0, $import = FALSE, $autoclose = FALSE, $tags = null)
eb1af592
NL
411 {
412 switch ($action)
413 {
414 case 'add':
a297fb1e
MR
415 $content = Tools::getPageContent($url);
416 $title = ($content['rss']['channel']['item']['title'] != '') ? $content['rss']['channel']['item']['title'] : _('Untitled');
417 $body = $content['rss']['channel']['item']['description'];
418
419 // clean content from prevent xss attack
0f859c6f 420 $purifier = $this->getPurifier();
a297fb1e
MR
421 $title = $purifier->purify($title);
422 $body = $purifier->purify($body);
1570a653 423
182faf26 424 //search for possible duplicate
8d7cd2cc 425 $duplicate = NULL;
a297fb1e 426 $duplicate = $this->store->retrieveOneByURL($url->getUrl(), $this->user->getId());
488fc63b 427
182faf26 428 $last_id = $this->store->add($url->getUrl(), $title, $body, $this->user->getId());
a297fb1e 429 if ( $last_id ) {
ec397236 430 Tools::logm('add link ' . $url->getUrl());
ec397236 431 if (DOWNLOAD_PICTURES) {
42c80841 432 $content = filtre_picture($body, $url->getUrl(), $last_id);
ec397236
NL
433 Tools::logm('updating content article');
434 $this->store->updateContent($last_id, $content, $this->user->getId());
435 }
488fc63b
MR
436
437 if ($duplicate != NULL) {
438 // 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
439 Tools::logm('link ' . $url->getUrl() . ' is a duplicate');
440 // 1) - preserve tags and favorite, then drop old entry
441 $this->store->reassignTags($duplicate['id'], $last_id);
442 if ($duplicate['is_fav']) {
443 $this->store->favoriteById($last_id, $this->user->getId());
444 }
445 if ($this->store->deleteById($duplicate['id'], $this->user->getId())) {
446 Tools::logm('previous link ' . $url->getUrl() .' entry deleted');
447 }
448 }
449
182faf26 450 $this->messages->add('s', _('the link has been added successfully'));
eb1af592
NL
451 }
452 else {
a297fb1e
MR
453 $this->messages->add('e', _('error during insertion : the link wasn\'t added'));
454 Tools::logm('error during insertion : the link wasn\'t added ' . $url->getUrl());
b916bcfc 455 }
ec397236 456
a297fb1e
MR
457 if ($autoclose == TRUE) {
458 Tools::redirect('?view=home');
459 } else {
460 Tools::redirect('?view=home&closewin=true');
eb1af592
NL
461 }
462 break;
463 case 'delete':
bc1ee852 464 $msg = 'delete link #' . $id;
8d3275be 465 if ($this->store->deleteById($id, $this->user->getId())) {
eb1af592
NL
466 if (DOWNLOAD_PICTURES) {
467 remove_directory(ABS_PATH . $id);
468 }
6a361945 469 $this->messages->add('s', _('the link has been deleted successfully'));
eb1af592
NL
470 }
471 else {
6a361945 472 $this->messages->add('e', _('the link wasn\'t deleted'));
bc1ee852 473 $msg = 'error : can\'t delete link #' . $id;
eb1af592 474 }
bc1ee852 475 Tools::logm($msg);
985ce3ec 476 Tools::redirect('?');
eb1af592
NL
477 break;
478 case 'toggle_fav' :
8d3275be 479 $this->store->favoriteById($id, $this->user->getId());
eb1af592 480 Tools::logm('mark as favorite link #' . $id);
c2cf7075
MR
481 if ( Tools::isAjaxRequest() ) {
482 echo 1;
483 exit;
484 }
485 else {
486 Tools::redirect();
487 }
eb1af592
NL
488 break;
489 case 'toggle_archive' :
8d3275be 490 $this->store->archiveById($id, $this->user->getId());
eb1af592 491 Tools::logm('archive link #' . $id);
c2cf7075
MR
492 if ( Tools::isAjaxRequest() ) {
493 echo 1;
494 exit;
495 }
496 else {
497 Tools::redirect();
498 }
eb1af592 499 break;
f14807de
NL
500 case 'archive_all' :
501 $this->store->archiveAll($this->user->getId());
502 Tools::logm('archive all links');
a297fb1e 503 Tools::redirect();
f14807de 504 break;
c432fa16 505 case 'add_tag' :
decc23aa 506 if (isset($_GET['search'])) {
507 //when we want to apply a tag to a search
decc23aa 508 $tags = array($_GET['search']);
509 $allentry_ids = $this->store->search($tags[0], $this->user->getId());
510 $entry_ids = array();
511 foreach ($allentry_ids as $eachentry) {
512 $entry_ids[] = $eachentry[0];
513 }
514 } else { //add a tag to a single article
515 $tags = explode(',', $_POST['value']);
516 $entry_ids = array($_POST['entry_id']);
fb26cc93 517 }
decc23aa 518 foreach($entry_ids as $entry_id) {
519 $entry = $this->store->retrieveOneById($entry_id, $this->user->getId());
520 if (!$entry) {
521 $this->messages->add('e', _('Article not found!'));
522 Tools::logm('error : article not found');
523 Tools::redirect();
524 }
525 //get all already set tags to preven duplicates
526 $already_set_tags = array();
527 $entry_tags = $this->store->retrieveTagsByEntry($entry_id);
528 foreach ($entry_tags as $tag) {
529 $already_set_tags[] = $tag['value'];
530 }
531 foreach($tags as $key => $tag_value) {
532 $value = trim($tag_value);
533 if ($value && !in_array($value, $already_set_tags)) {
534 $tag = $this->store->retrieveTagByValue($value);
535 if (is_null($tag)) {
536 # we create the tag
537 $tag = $this->store->createTag($value);
538 $sequence = '';
539 if (STORAGE == 'postgres') {
540 $sequence = 'tags_id_seq';
541 }
542 $tag_id = $this->store->getLastId($sequence);
fb26cc93 543 }
decc23aa 544 else {
545 $tag_id = $tag['id'];
546 }
547
548 # we assign the tag to the article
549 $this->store->setTagToEntry($tag_id, $entry_id);
550 }
c432fa16 551 }
c432fa16 552 }
9c743ab9 553 $this->messages->add('s', _('The tag has been applied successfully'));
24696800 554 Tools::logm('The tag has been applied successfully');
a297fb1e 555 Tools::redirect();
c432fa16
NL
556 break;
557 case 'remove_tag' :
558 $tag_id = $_GET['tag_id'];
b89d5a2b
NL
559 $entry = $this->store->retrieveOneById($id, $this->user->getId());
560 if (!$entry) {
561 $this->messages->add('e', _('Article not found!'));
562 Tools::logm('error : article not found');
563 Tools::redirect();
564 }
c432fa16 565 $this->store->removeTagForEntry($id, $tag_id);
9c743ab9 566 Tools::logm('tag entry deleted');
24696800 567 if ($this->store->cleanUnusedTag($tag_id)) {
568 Tools::logm('tag deleted');
569 }
9c743ab9 570 $this->messages->add('s', _('The tag has been successfully deleted'));
c432fa16
NL
571 Tools::redirect();
572 break;
eb1af592
NL
573 default:
574 break;
575 }
576 }
577
578 function displayView($view, $id = 0)
579 {
580 $tpl_vars = array();
581
582 switch ($view)
583 {
eb1af592 584 case 'config':
11c680f9
NL
585 $dev_infos = $this->getPocheVersion('dev');
586 $dev = trim($dev_infos[0]);
587 $check_time_dev = date('d-M-Y H:i', $dev_infos[1]);
588 $prod_infos = $this->getPocheVersion('prod');
589 $prod = trim($prod_infos[0]);
590 $check_time_prod = date('d-M-Y H:i', $prod_infos[1]);
031df528
NL
591 $compare_dev = version_compare(POCHE, $dev);
592 $compare_prod = version_compare(POCHE, $prod);
00dbaf90 593 $themes = $this->getInstalledThemes();
5011388f 594 $languages = $this->getInstalledLanguages();
72c20a52 595 $token = $this->user->getConfigValue('token');
1810c13b 596 $http_auth = (isset($_SERVER['PHP_AUTH_USER']) || isset($_SERVER['REMOTE_USER'])) ? true : false;
4d99bae8 597 $only_user = ($this->store->listUsers() > 1) ? false : true;
32520785 598 $tpl_vars = array(
00dbaf90 599 'themes' => $themes,
5011388f 600 'languages' => $languages,
32520785
NL
601 'dev' => $dev,
602 'prod' => $prod,
11c680f9
NL
603 'check_time_dev' => $check_time_dev,
604 'check_time_prod' => $check_time_prod,
32520785
NL
605 'compare_dev' => $compare_dev,
606 'compare_prod' => $compare_prod,
72c20a52
NL
607 'token' => $token,
608 'user_id' => $this->user->getId(),
df6afaf0 609 'http_auth' => $http_auth,
4d99bae8 610 'only_user' => $only_user
32520785 611 );
eb1af592
NL
612 Tools::logm('config view');
613 break;
6cab59c3
NL
614 case 'edit-tags':
615 # tags
b89d5a2b
NL
616 $entry = $this->store->retrieveOneById($id, $this->user->getId());
617 if (!$entry) {
618 $this->messages->add('e', _('Article not found!'));
619 Tools::logm('error : article not found');
620 Tools::redirect();
621 }
6cab59c3
NL
622 $tags = $this->store->retrieveTagsByEntry($id);
623 $tpl_vars = array(
c432fa16 624 'entry_id' => $id,
6cab59c3 625 'tags' => $tags,
032e0ca1 626 'entry' => $entry,
4886ed6d
NL
627 );
628 break;
2e2ebe5e 629 case 'tags':
f778e472 630 $token = $this->user->getConfigValue('token');
fb26cc93
MR
631 //if term is set - search tags for this term
632 $term = Tools::checkVar('term');
633 $tags = $this->store->retrieveAllTags($this->user->getId(), $term);
634 if (Tools::isAjaxRequest()) {
635 $result = array();
636 foreach ($tags as $tag) {
637 $result[] = $tag['value'];
638 }
639 echo json_encode($result);
640 exit;
641 }
2e2ebe5e 642 $tpl_vars = array(
f778e472
NL
643 'token' => $token,
644 'user_id' => $this->user->getId(),
2e2ebe5e
NL
645 'tags' => $tags,
646 );
647 break;
a4585f7e
MR
648 case 'search':
649 if (isset($_GET['search'])) {
650 $search = filter_var($_GET['search'], FILTER_SANITIZE_STRING);
651 $tpl_vars['entries'] = $this->store->search($search, $this->user->getId());
652 $count = count($tpl_vars['entries']);
653 $this->pagination->set_total($count);
654 $page_links = str_replace(array('previous', 'next'), array(_('previous'), _('next')),
655 $this->pagination->page_links('?view=' . $view . '?search=' . $search . '&sort=' . $_SESSION['sort'] . '&' ));
656 $tpl_vars['page_links'] = $page_links;
657 $tpl_vars['nb_results'] = $count;
658 $tpl_vars['search_term'] = $search;
659 }
660 break;
eb1af592 661 case 'view':
8d3275be 662 $entry = $this->store->retrieveOneById($id, $this->user->getId());
eb1af592
NL
663 if ($entry != NULL) {
664 Tools::logm('view link #' . $id);
665 $content = $entry['content'];
666 if (function_exists('tidy_parse_string')) {
667 $tidy = tidy_parse_string($content, array('indent'=>true, 'show-body-only' => true), 'UTF8');
668 $tidy->cleanRepair();
669 $content = $tidy->value;
3408ed48 670 }
a3223127 671
3408ed48
NL
672 # flattr checking
673 $flattr = new FlattrItem();
7b171c73
NL
674 $flattr->checkItem($entry['url'], $entry['id']);
675
676 # tags
677 $tags = $this->store->retrieveTagsByEntry($entry['id']);
a3223127 678
3408ed48 679 $tpl_vars = array(
7b171c73
NL
680 'entry' => $entry,
681 'content' => $content,
682 'flattr' => $flattr,
683 'tags' => $tags
3408ed48 684 );
eb1af592
NL
685 }
686 else {
d8d1542e 687 Tools::logm('error in view call : entry is null');
eb1af592
NL
688 }
689 break;
032e0ca1 690 default: # home, favorites, archive and tag views
eb1af592 691 $tpl_vars = array(
3eb04903
N
692 'entries' => '',
693 'page_links' => '',
7f9f5281 694 'nb_results' => '',
6065553c 695 'listmode' => (isset($_COOKIE['listmode']) ? true : false),
eb1af592 696 );
182faf26 697
032e0ca1
MR
698 //if id is given - we retrive entries by tag: id is tag id
699 if ($id) {
700 $tpl_vars['tag'] = $this->store->retrieveTag($id, $this->user->getId());
701 $tpl_vars['id'] = intval($id);
702 }
703
704 $count = $this->store->getEntriesByViewCount($view, $this->user->getId(), $id);
705
706 if ($count > 0) {
707 $this->pagination->set_total($count);
c515ffec 708 $page_links = str_replace(array('previous', 'next'), array(_('previous'), _('next')),
032e0ca1
MR
709 $this->pagination->page_links('?view=' . $view . '&sort=' . $_SESSION['sort'] . (($id)?'&id='.$id:'') . '&' ));
710 $tpl_vars['entries'] = $this->store->getEntriesByView($view, $this->user->getId(), $this->pagination->get_limit(), $id);
3eb04903 711 $tpl_vars['page_links'] = $page_links;
032e0ca1 712 $tpl_vars['nb_results'] = $count;
3eb04903 713 }
6a361945 714 Tools::logm('display ' . $view . ' view');
eb1af592
NL
715 break;
716 }
717
718 return $tpl_vars;
719 }
c765c367 720
07ee09f4 721 /**
182faf26
MR
722 * update the password of the current user.
723 * if MODE_DEMO is TRUE, the password can't be updated.
07ee09f4
NL
724 * @todo add the return value
725 * @todo set the new password in function header like this updatePassword($newPassword)
726 * @return boolean
727 */
c765c367
NL
728 public function updatePassword()
729 {
55821e04 730 if (MODE_DEMO) {
8d3275be 731 $this->messages->add('i', _('in demo mode, you can\'t update your password'));
55821e04 732 Tools::logm('in demo mode, you can\'t do this');
6a361945 733 Tools::redirect('?view=config');
55821e04
NL
734 }
735 else {
736 if (isset($_POST['password']) && isset($_POST['password_repeat'])) {
737 if ($_POST['password'] == $_POST['password_repeat'] && $_POST['password'] != "") {
8d3275be
NL
738 $this->messages->add('s', _('your password has been updated'));
739 $this->store->updatePassword($this->user->getId(), Tools::encodeString($_POST['password'] . $this->user->getUsername()));
c765c367 740 Session::logout();
8d3275be 741 Tools::logm('password updated');
c765c367
NL
742 Tools::redirect();
743 }
744 else {
8d3275be 745 $this->messages->add('e', _('the two fields have to be filled & the password must be the same in the two fields'));
6a361945 746 Tools::redirect('?view=config');
c765c367
NL
747 }
748 }
749 }
750 }
182faf26 751
00dbaf90
NL
752 public function updateTheme()
753 {
754 # no data
755 if (empty($_POST['theme'])) {
756 }
182faf26 757
00dbaf90
NL
758 # we are not going to change it to the current theme...
759 if ($_POST['theme'] == $this->getTheme()) {
760 $this->messages->add('w', _('still using the "' . $this->getTheme() . '" theme!'));
761 Tools::redirect('?view=config');
762 }
182faf26 763
00dbaf90
NL
764 $themes = $this->getInstalledThemes();
765 $actualTheme = false;
182faf26 766
f4fbfaa7
NL
767 foreach (array_keys($themes) as $theme) {
768 if ($theme == $_POST['theme']) {
00dbaf90
NL
769 $actualTheme = true;
770 break;
771 }
772 }
182faf26 773
00dbaf90
NL
774 if (! $actualTheme) {
775 $this->messages->add('e', _('that theme does not seem to be installed'));
776 Tools::redirect('?view=config');
777 }
182faf26 778
00dbaf90
NL
779 $this->store->updateUserConfig($this->user->getId(), 'theme', $_POST['theme']);
780 $this->messages->add('s', _('you have changed your theme preferences'));
182faf26 781
00dbaf90
NL
782 $currentConfig = $_SESSION['poche_user']->config;
783 $currentConfig['theme'] = $_POST['theme'];
182faf26 784
00dbaf90 785 $_SESSION['poche_user']->setConfig($currentConfig);
56532c4e
NL
786
787 $this->emptyCache();
182faf26 788
00dbaf90
NL
789 Tools::redirect('?view=config');
790 }
c765c367 791
5011388f
NL
792 public function updateLanguage()
793 {
794 # no data
795 if (empty($_POST['language'])) {
796 }
182faf26 797
5011388f
NL
798 # we are not going to change it to the current language...
799 if ($_POST['language'] == $this->getLanguage()) {
800 $this->messages->add('w', _('still using the "' . $this->getLanguage() . '" language!'));
801 Tools::redirect('?view=config');
802 }
182faf26 803
5011388f
NL
804 $languages = $this->getInstalledLanguages();
805 $actualLanguage = false;
182faf26 806
5011388f 807 foreach ($languages as $language) {
c9bd17a1 808 if ($language['value'] == $_POST['language']) {
5011388f
NL
809 $actualLanguage = true;
810 break;
811 }
812 }
182faf26 813
5011388f
NL
814 if (! $actualLanguage) {
815 $this->messages->add('e', _('that language does not seem to be installed'));
816 Tools::redirect('?view=config');
817 }
182faf26 818
5011388f
NL
819 $this->store->updateUserConfig($this->user->getId(), 'language', $_POST['language']);
820 $this->messages->add('s', _('you have changed your language preferences'));
182faf26 821
5011388f
NL
822 $currentConfig = $_SESSION['poche_user']->config;
823 $currentConfig['language'] = $_POST['language'];
182faf26 824
5011388f 825 $_SESSION['poche_user']->setConfig($currentConfig);
e145f767
NL
826
827 $this->emptyCache();
182faf26 828
5011388f 829 Tools::redirect('?view=config');
182faf26 830 }
df6afaf0
DS
831 /**
832 * get credentials from differents sources
833 * it redirects the user to the $referer link
834 * @return array
835 */
1810c13b
NL
836 private function credentials() {
837 if(isset($_SERVER['PHP_AUTH_USER'])) {
6af66b11 838 return array($_SERVER['PHP_AUTH_USER'],'php_auth',true);
1810c13b
NL
839 }
840 if(!empty($_POST['login']) && !empty($_POST['password'])) {
6af66b11 841 return array($_POST['login'],$_POST['password'],false);
1810c13b
NL
842 }
843 if(isset($_SERVER['REMOTE_USER'])) {
6af66b11 844 return array($_SERVER['REMOTE_USER'],'http_auth',true);
1810c13b 845 }
5cfafc61 846
6af66b11
MR
847 return array(false,false,false);
848 }
df6afaf0 849
07ee09f4
NL
850 /**
851 * checks if login & password are correct and save the user in session.
852 * it redirects the user to the $referer link
853 * @param string $referer the url to redirect after login
854 * @todo add the return value
855 * @return boolean
856 */
c765c367
NL
857 public function login($referer)
858 {
6af66b11 859 list($login,$password,$isauthenticated)=$this->credentials();
df6afaf0
DS
860 if($login === false || $password === false) {
861 $this->messages->add('e', _('login failed: you have to fill all fields'));
862 Tools::logm('login failed');
863 Tools::redirect();
864 }
865 if (!empty($login) && !empty($password)) {
6af66b11 866 $user = $this->store->login($login, Tools::encodeString($password . $login), $isauthenticated);
7ce7ec4c
NL
867 if ($user != array()) {
868 # Save login into Session
6af66b11
MR
869 $longlastingsession = isset($_POST['longlastingsession']);
870 $passwordTest = ($isauthenticated) ? $user['password'] : Tools::encodeString($password . $login);
871 Session::login($user['username'], $user['password'], $login, $passwordTest, $longlastingsession, array('poche_user' => new User($user)));
26929c08 872 $this->messages->add('s', _('welcome to your wallabag'));
8d3275be 873 Tools::logm('login successful');
c765c367
NL
874 Tools::redirect($referer);
875 }
8d3275be 876 $this->messages->add('e', _('login failed: bad login or password'));
c765c367
NL
877 Tools::logm('login failed');
878 Tools::redirect();
c765c367
NL
879 }
880 }
881
07ee09f4
NL
882 /**
883 * log out the poche user. It cleans the session.
884 * @todo add the return value
182faf26 885 * @return boolean
07ee09f4 886 */
c765c367
NL
887 public function logout()
888 {
7ce7ec4c 889 $this->user = array();
c765c367 890 Session::logout();
b916bcfc 891 Tools::logm('logout');
c765c367
NL
892 Tools::redirect();
893 }
894
07ee09f4
NL
895 /**
896 * import datas into your poche
182faf26 897 * @return boolean
07ee09f4 898 */
182faf26
MR
899 public function import() {
900
901 if ( isset($_FILES['file']) ) {
5ce39784
MR
902 Tools::logm('Import stated: parsing file');
903
182faf26
MR
904 // assume, that file is in json format
905 $str_data = file_get_contents($_FILES['file']['tmp_name']);
906 $data = json_decode($str_data, true);
907
908 if ( $data === null ) {
909 //not json - assume html
910 $html = new simple_html_dom();
911 $html->load_file($_FILES['file']['tmp_name']);
912 $data = array();
913 $read = 0;
914 foreach (array('ol','ul') as $list) {
915 foreach ($html->find($list) as $ul) {
86da3988
MR
916 foreach ($ul->find('li') as $li) {
917 $tmpEntry = array();
a8ef1f3f
MR
918 $a = $li->find('a');
919 $tmpEntry['url'] = $a[0]->href;
920 $tmpEntry['tags'] = $a[0]->tags;
921 $tmpEntry['is_read'] = $read;
922 if ($tmpEntry['url']) {
923 $data[] = $tmpEntry;
924 }
86da3988
MR
925 }
926 # the second <ol/ul> is for read links
927 $read = ((sizeof($data) && $read)?0:1);
182faf26
MR
928 }
929 }
63c35580 930 }
182faf26 931
a297fb1e
MR
932 //for readability structure
933 foreach ($data as $record) {
934 if (is_array($record)) {
935 $data[] = $record;
936 foreach ($record as $record2) {
937 if (is_array($record2)) {
86da3988 938 $data[] = $record2;
a297fb1e
MR
939 }
940 }
941 }
942 }
943
86da3988 944 $urlsInserted = array(); //urls of articles inserted
182faf26 945 foreach ($data as $record) {
a297fb1e 946 $url = trim( isset($record['article__url']) ? $record['article__url'] : (isset($record['url']) ? $record['url'] : '') );
86da3988 947 if ( $url and !in_array($url, $urlsInserted) ) {
182faf26
MR
948 $title = (isset($record['title']) ? $record['title'] : _('Untitled - Import - ').'</a> <a href="./?import">'._('click to finish import').'</a><a>');
949 $body = (isset($record['content']) ? $record['content'] : '');
a297fb1e
MR
950 $isRead = (isset($record['is_read']) ? intval($record['is_read']) : (isset($record['archive'])?intval($record['archive']):0));
951 $isFavorite = (isset($record['is_fav']) ? intval($record['is_fav']) : (isset($record['favorite'])?intval($record['favorite']):0) );
182faf26
MR
952 //insert new record
953 $id = $this->store->add($url, $title, $body, $this->user->getId(), $isFavorite, $isRead);
954 if ( $id ) {
86da3988
MR
955 $urlsInserted[] = $url; //add
956
182faf26 957 if ( isset($record['tags']) && trim($record['tags']) ) {
86da3988 958 //@TODO: set tags
182faf26
MR
959
960 }
961 }
962 }
963 }
964
86da3988 965 $i = sizeof($urlsInserted);
182faf26
MR
966 if ( $i > 0 ) {
967 $this->messages->add('s', _('Articles inserted: ').$i._('. Please note, that some may be marked as "read".'));
968 }
5ce39784 969 Tools::logm('Import of articles finished: '.$i.' articles added (w/o content if not provided).');
182faf26
MR
970 }
971 //file parsing finished here
972
973 //now download article contents if any
974
975 //check if we need to download any content
976 $recordsDownloadRequired = $this->store->retrieveUnfetchedEntriesCount($this->user->getId());
977 if ( $recordsDownloadRequired == 0 ) {
978 //nothing to download
979 $this->messages->add('s', _('Import finished.'));
5ce39784 980 Tools::logm('Import finished completely');
182faf26
MR
981 Tools::redirect();
982 }
983 else {
984 //if just inserted - don't download anything, download will start in next reload
985 if ( !isset($_FILES['file']) ) {
986 //download next batch
5ce39784 987 Tools::logm('Fetching next batch of articles...');
182faf26
MR
988 $items = $this->store->retrieveUnfetchedEntries($this->user->getId(), IMPORT_LIMIT);
989
0f859c6f 990 $purifier = $this->getPurifier();
182faf26
MR
991
992 foreach ($items as $item) {
86da3988 993 $url = new Url(base64_encode($item['url']));
5ce39784 994 Tools::logm('Fetching article '.$item['id']);
86da3988 995 $content = Tools::getPageContent($url);
182faf26 996
86da3988
MR
997 $title = (($content['rss']['channel']['item']['title'] != '') ? $content['rss']['channel']['item']['title'] : _('Untitled'));
998 $body = (($content['rss']['channel']['item']['description'] != '') ? $content['rss']['channel']['item']['description'] : _('Undefined'));
182faf26 999
86da3988
MR
1000 //clean content to prevent xss attack
1001 $title = $purifier->purify($title);
1002 $body = $purifier->purify($body);
182faf26 1003
86da3988 1004 $this->store->updateContentAndTitle($item['id'], $title, $body, $this->user->getId());
5ce39784 1005 Tools::logm('Article '.$item['id'].' updated.');
182faf26
MR
1006 }
1007
63c35580 1008 }
182faf26
MR
1009 }
1010
1011 return array('includeImport'=>true, 'import'=>array('recordsDownloadRequired'=>$recordsDownloadRequired, 'recordsUnderDownload'=> IMPORT_LIMIT, 'delay'=> IMPORT_DELAY * 1000) );
63c35580 1012 }
c765c367 1013
07ee09f4
NL
1014 /**
1015 * export poche entries in json
1016 * @return json all poche entries
1017 */
a8ef1f3f
MR
1018 public function export() {
1019 $filename = "wallabag-export-".$this->user->getId()."-".date("Y-m-d").".json";
1020 header('Content-Disposition: attachment; filename='.$filename);
1021
1022 $entries = $this->store->retrieveAll($this->user->getId());
1023 echo $this->tpl->render('export.twig', array(
1024 'export' => Tools::renderJson($entries),
1025 ));
1026 Tools::logm('export view');
c765c367 1027 }
32520785 1028
07ee09f4 1029 /**
a3436d4c 1030 * Checks online the latest version of poche and cache it
07ee09f4
NL
1031 * @param string $which 'prod' or 'dev'
1032 * @return string latest $which version
1033 */
a8ef1f3f
MR
1034 private function getPocheVersion($which = 'prod') {
1035 $cache_file = CACHE . '/' . $which;
1036 $check_time = time();
1037
1038 # checks if the cached version file exists
1039 if (file_exists($cache_file) && (filemtime($cache_file) > (time() - 86400 ))) {
1040 $version = file_get_contents($cache_file);
1041 $check_time = filemtime($cache_file);
1042 } else {
1043 $version = file_get_contents('http://static.wallabag.org/versions/' . $which);
1044 file_put_contents($cache_file, $version, LOCK_EX);
1045 }
1046 return array($version, $check_time);
32520785 1047 }
72c20a52
NL
1048
1049 public function generateToken()
1050 {
a8ef1f3f
MR
1051 if (ini_get('open_basedir') === '') {
1052 if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
1053 echo 'This is a server using Windows!';
1054 // alternative to /dev/urandom for Windows
1055 $token = substr(base64_encode(uniqid(mt_rand(), true)), 0, 20);
1056 } else {
1057 $token = substr(base64_encode(file_get_contents('/dev/urandom', false, null, 0, 20)), 0, 15);
72c20a52 1058 }
a8ef1f3f
MR
1059 }
1060 else {
1061 $token = substr(base64_encode(uniqid(mt_rand(), true)), 0, 20);
1062 }
72c20a52 1063
a8ef1f3f
MR
1064 $token = str_replace('+', '', $token);
1065 $this->store->updateUserConfig($this->user->getId(), 'token', $token);
1066 $currentConfig = $_SESSION['poche_user']->config;
1067 $currentConfig['token'] = $token;
1068 $_SESSION['poche_user']->setConfig($currentConfig);
1069 Tools::redirect();
72c20a52
NL
1070 }
1071
f778e472 1072 public function generateFeeds($token, $user_id, $tag_id, $type = 'home')
72c20a52 1073 {
f778e472 1074 $allowed_types = array('home', 'fav', 'archive', 'tag');
72c20a52
NL
1075 $config = $this->store->getConfigUser($user_id);
1076
17b2afef 1077 if ($config == null) {
30bd2735 1078 die(sprintf(_('User with this id (%d) does not exist.'), $user_id));
17b2afef
NL
1079 }
1080
cbc75bef 1081 if (!in_array($type, $allowed_types) || $token != $config['token']) {
72c20a52
NL
1082 die(_('Uh, there is a problem while generating feeds.'));
1083 }
1084 // Check the token
1085
9e7c840b 1086 $feed = new FeedWriter(RSS2);
2e4440c3 1087 $feed->setTitle('wallabag — ' . $type . ' feed');
72c20a52 1088 $feed->setLink(Tools::getPocheUrl());
223268c2
NL
1089 $feed->setChannelElement('pubDate', date(DATE_RSS , time()));
1090 $feed->setChannelElement('generator', 'wallabag');
1091 $feed->setDescription('wallabag ' . $type . ' elements');
72c20a52 1092
f778e472 1093 if ($type == 'tag') {
b89d5a2b 1094 $entries = $this->store->retrieveEntriesByTag($tag_id, $user_id);
f778e472
NL
1095 }
1096 else {
1097 $entries = $this->store->getEntriesByView($type, $user_id);
1098 }
1099
72c20a52
NL
1100 if (count($entries) > 0) {
1101 foreach ($entries as $entry) {
1102 $newItem = $feed->createNewItem();
0b57c682 1103 $newItem->setTitle($entry['title']);
f86784c2 1104 $newItem->setSource(Tools::getPocheUrl() . '?view=view&amp;id=' . $entry['id']);
ed02e38e 1105 $newItem->setLink($entry['url']);
72c20a52
NL
1106 $newItem->setDate(time());
1107 $newItem->setDescription($entry['content']);
1108 $feed->addItem($newItem);
1109 }
1110 }
1111
1112 $feed->genarateFeed();
1113 exit;
1114 }
6285e57c
NL
1115
1116 public function emptyCache() {
1117 $files = new RecursiveIteratorIterator(
1118 new RecursiveDirectoryIterator(CACHE, RecursiveDirectoryIterator::SKIP_DOTS),
1119 RecursiveIteratorIterator::CHILD_FIRST
1120 );
1121
1122 foreach ($files as $fileinfo) {
1123 $todo = ($fileinfo->isDir() ? 'rmdir' : 'unlink');
1124 $todo($fileinfo->getRealPath());
1125 }
1126
1127 Tools::logm('empty cache');
1128 $this->messages->add('s', _('Cache deleted.'));
1129 Tools::redirect();
1130 }
0f859c6f
MR
1131
1132 /**
1133 * return new purifier object with actual config
1134 */
1135 protected function getPurifier() {
ec15d0a7 1136 $config = HTMLPurifier_Config::createDefault();
1137 $config->set('Cache.SerializerPath', CACHE);
1138 $config->set('HTML.SafeIframe', true);
35d4e275 1139
0b9bb8cb
MR
1140 //allow YouTube, Vimeo and dailymotion videos
1141 $config->set('URI.SafeIframeRegexp', '%^(https?:)?//(www\.youtube(?:-nocookie)?\.com/embed/|player\.vimeo\.com/video/|www\.dailymotion\.com/embed/video/)%');
ec15d0a7 1142
0f859c6f
MR
1143 return new HTMLPurifier($config);
1144 }
cbc75bef 1145
87090d8a 1146 /**
1147 * handle epub
1148 */
1149 public function createEpub() {
cbc75bef 1150
7ec445b0 1151 switch ($_GET['method']) {
1152 case 'id':
87090d8a 1153 $entryID = filter_var($_GET['id'],FILTER_SANITIZE_NUMBER_INT);
1154 $entry = $this->store->retrieveOneById($entryID, $this->user->getId());
1155 $entries = array($entry);
f2b6b4e2 1156 $bookTitle = $entry['title'];
f3f0b113 1157 $bookFileName = substr($bookTitle, 0, 200);
7ec445b0 1158 break;
1159 case 'all':
1160 $entries = $this->store->retrieveAll($this->user->getId());
f3f0b113 1161 $bookTitle = sprintf(_('All my articles on '), date(_('d.m.y'))); #translatable because each country has it's own date format system
1162 $bookFileName = _('Allarticles') . date(_('dmY'));
7ec445b0 1163 break;
1164 case 'tag':
1165 $tag = filter_var($_GET['tag'],FILTER_SANITIZE_STRING);
1166 $tags_id = $this->store->retrieveAllTags($this->user->getId(),$tag);
1167 $tag_id = $tags_id[0]["id"]; // we take the first result, which is supposed to match perfectly. There must be a workaround.
1168 $entries = $this->store->retrieveEntriesByTag($tag_id,$this->user->getId());
f3f0b113 1169 $bookTitle = sprintf(_('Articles tagged %s'),$tag);
1170 $bookFileName = substr(sprintf(_('Tag %s'),$tag), 0, 200);
7ec445b0 1171 break;
1172 case 'category':
1173 $category = filter_var($_GET['category'],FILTER_SANITIZE_STRING);
1174 $entries = $this->store->getEntriesByView($category,$this->user->getId());
f3f0b113 1175 $bookTitle = sprintf(_('All articles in category %s'), $category);
1176 $bookFileName = substr(sprintf(_('Category %s'),$category), 0, 200);
7ec445b0 1177 break;
1178 case 'search':
1179 $search = filter_var($_GET['search'],FILTER_SANITIZE_STRING);
1180 $entries = $this->store->search($search,$this->user->getId());
f3f0b113 1181 $bookTitle = sprintf(_('All articles for search %s'), $search);
1182 $bookFileName = substr(sprintf(_('Search %s'), $search), 0, 200);
7ec445b0 1183 break;
1184 case 'default':
1185 die(_('Uh, there is a problem while generating epub.'));
cbc75bef 1186
87090d8a 1187 }
7ec445b0 1188
87090d8a 1189 $content_start =
f2b6b4e2 1190 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1191 . "<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n"
1192 . "<head>"
1193 . "<meta http-equiv=\"Default-Style\" content=\"text/html; charset=utf-8\" />\n"
1194 . "<title>wallabag articles book</title>\n"
87090d8a 1195 . "</head>\n"
1196 . "<body>\n";
1197
1198 $bookEnd = "</body>\n</html>\n";
cbc75bef 1199
7ec445b0 1200 $log = new Logger("wallabag", TRUE);
87090d8a 1201 $fileDir = CACHE;
87090d8a 1202
ec15d0a7 1203 $book = new EPub(EPub::BOOK_VERSION_EPUB3, DEBUG_POCHE);
87090d8a 1204 $log->logLine("new EPub()");
1205 $log->logLine("EPub class version: " . EPub::VERSION);
1206 $log->logLine("EPub Req. Zip version: " . EPub::REQ_ZIP_VERSION);
1207 $log->logLine("Zip version: " . Zip::VERSION);
1208 $log->logLine("getCurrentServerURL: " . $book->getCurrentServerURL());
1209 $log->logLine("getCurrentPageURL..: " . $book->getCurrentPageURL());
cbc75bef 1210
34acb02c 1211 $book->setTitle(_('wallabag\'s articles'));
87090d8a 1212 $book->setIdentifier("http://$_SERVER[HTTP_HOST]", EPub::IDENTIFIER_URI); // Could also be the ISBN number, prefered for published books, or a UUID.
1213 //$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 1214 $book->setDescription(_("Some articles saved on my wallabag"));
87090d8a 1215 $book->setAuthor("wallabag","wallabag");
1216 $book->setPublisher("wallabag","wallabag"); // I hope this is a non existant address :)
1217 $book->setDate(time()); // Strictly not needed as the book date defaults to time().
1218 //$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.
1219 $book->setSourceURL("http://$_SERVER[HTTP_HOST]");
cbc75bef 1220
87090d8a 1221 $book->addDublinCoreMetadata(DublinCore::CONTRIBUTOR, "PHP");
4877836b 1222 $book->addDublinCoreMetadata(DublinCore::CONTRIBUTOR, "wallabag");
cbc75bef 1223
87090d8a 1224 $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 1225
4877836b 1226 $log->logLine("Add Cover");
cbc75bef 1227
f2b6b4e2 1228 $fullTitle = "<h1> " . $bookTitle . "</h1>\n";
cbc75bef 1229
f2b6b4e2 1230 $book->setCoverImage("Cover.png", file_get_contents("themes/baggy/img/apple-touch-icon-152.png"), "image/png", $fullTitle);
cbc75bef 1231
e212e6b1 1232 $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 1233
f2b6b4e2 1234 //$book->addChapter("Table of Contents", "TOC.xhtml", NULL, false, EPub::EXTERNAL_REF_IGNORE);
1235 $book->addChapter("Notices", "Cover2.html", $cover);
cbc75bef 1236
f2b6b4e2 1237 $book->buildTOC();
cbc75bef 1238
e212e6b1 1239 foreach ($entries as $entry) { //set tags as subjects
87090d8a 1240 $tags = $this->store->retrieveTagsByEntry($entry['id']);
1241 foreach ($tags as $tag) {
f2b6b4e2 1242 $book->setSubject($tag['value']);
87090d8a 1243 }
cbc75bef 1244
87090d8a 1245 $log->logLine("Set up parameters");
cbc75bef 1246
87090d8a 1247 $chapter = $content_start . $entry['content'] . $bookEnd;
7ec445b0 1248 $book->addChapter($entry['title'], htmlspecialchars($entry['title']) . ".html", $chapter, true, EPub::EXTERNAL_REF_ADD);
4877836b 1249 $log->logLine("Added chapter " . $entry['title']);
f2b6b4e2 1250 }
87090d8a 1251
cbc75bef 1252 if (DEBUG_POCHE) {
e212e6b1 1253 $epuplog = $book->getLog();
1254 $book->addChapter("Log", "Log.html", $content_start . $log->getLog() . "\n</pre>" . $bookEnd); // log generation
87090d8a 1255 }
1256 $book->finalize();
f3f0b113 1257 $zipData = $book->sendBook($bookFileName);
87090d8a 1258 }
df6afaf0 1259}