]> git.immae.eu Git - github/wallabag/wallabag.git/blame - inc/poche/Poche.class.php
Fixed a bug into PHPePub with special caracters
[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');
75 putenv('LC_ALL=' . $language);
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());
182faf26 235
55821e04
NL
236 # filter to display domain name of an url
237 $filter = new Twig_SimpleFilter('getDomain', 'Tools::getDomain');
238 $this->tpl->addFilter($filter);
eb1af592 239
d9178758
NL
240 # filter for reading time
241 $filter = new Twig_SimpleFilter('getReadingTime', 'Tools::getReadingTime');
242 $this->tpl->addFilter($filter);
eb1af592 243 }
4d99bae8 244
245 public function createNewUser() {
246 if (isset($_GET['newuser'])){
247 if ($_POST['newusername'] != "" && $_POST['password4newuser'] != ""){
248 $newusername = filter_var($_POST['newusername'], FILTER_SANITIZE_STRING);
249 if (!$this->store->userExists($newusername)){
250 if ($this->store->install($newusername, Tools::encodeString($_POST['password4newuser'] . $newusername))) {
251 Tools::logm('The new user '.$newusername.' has been installed');
252 $this->messages->add('s', sprintf(_('The new user %s has been installed. Do you want to <a href="?logout">logout ?</a>'),$newusername));
253 Tools::redirect();
254 }
255 else {
256 Tools::logm('error during adding new user');
257 Tools::redirect();
258 }
259 }
260 else {
261 $this->messages->add('e', sprintf(_('Error : An user with the name %s already exists !'),$newusername));
262 Tools::logm('An user with the name '.$newusername.' already exists !');
263 Tools::redirect();
264 }
265 }
266 }
267 }
268
269 public function deleteUser(){
270 if (isset($_GET['deluser'])){
271 if ($this->store->listUsers() > 1) {
272 if (Tools::encodeString($_POST['password4deletinguser'].$this->user->getUsername()) == $this->store->getUserPassword($this->user->getId())) {
273 $username = $this->user->getUsername();
274 $this->store->deleteUserConfig($this->user->getId());
275 Tools::logm('The configuration for user '. $username .' has been deleted !');
276 $this->store->deleteTagsEntriesAndEntries($this->user->getId());
277 Tools::logm('The entries for user '. $username .' has been deleted !');
278 $this->store->deleteUser($this->user->getId());
279 Tools::logm('User '. $username .' has been completely deleted !');
280 Session::logout();
281 Tools::logm('logout');
282 Tools::redirect();
283 $this->messages->add('s', sprintf(_('User %s has been successfully deleted !'),$newusername));
284 }
285 else {
286 Tools::logm('Bad password !');
287 $this->messages->add('e', _('Error : The password is wrong !'));
288 }
289 }
290 else {
291 Tools::logm('Only user !');
292 $this->messages->add('e', _('Error : You are the only user, you cannot delete your account !'));
293 }
294 }
295 }
eb1af592 296
f4fbfaa7 297 private function install()
eb1af592
NL
298 {
299 Tools::logm('poche still not installed');
300 echo $this->tpl->render('install.twig', array(
00dbaf90
NL
301 'token' => Session::getToken(),
302 'theme' => $this->getTheme(),
303 'poche_url' => Tools::getPocheUrl()
eb1af592
NL
304 ));
305 if (isset($_GET['install'])) {
182faf26 306 if (($_POST['password'] == $_POST['password_repeat'])
eb1af592
NL
307 && $_POST['password'] != "" && $_POST['login'] != "") {
308 # let's rock, install poche baby !
bb5a7d9e
NL
309 if ($this->store->install($_POST['login'], Tools::encodeString($_POST['password'] . $_POST['login'])))
310 {
311 Session::logout();
312 Tools::logm('poche is now installed');
313 Tools::redirect();
314 }
6a361945
NL
315 }
316 else {
317 Tools::logm('error during installation');
eb1af592
NL
318 Tools::redirect();
319 }
320 }
321 exit();
322 }
182faf26 323
00dbaf90
NL
324 public function getTheme() {
325 return $this->currentTheme;
326 }
5011388f 327
f4fbfaa7
NL
328 /**
329 * Provides theme information by parsing theme.ini file if present in the theme's root directory.
330 * In all cases, the following data will be returned:
331 * - name: theme's name, or key if the theme is unnamed,
332 * - current: boolean informing if the theme is the current user theme.
333 *
334 * @param string $theme Theme key (directory name)
335 * @return array|boolean Theme information, or false if the theme doesn't exist.
336 */
337 public function getThemeInfo($theme) {
338 if (!is_dir(THEME . '/' . $theme)) {
339 return false;
340 }
341
342 $themeIniFile = THEME . '/' . $theme . '/theme.ini';
343 $themeInfo = array();
344
345 if (is_file($themeIniFile) && is_readable($themeIniFile)) {
346 $themeInfo = parse_ini_file($themeIniFile);
347 }
182faf26 348
f4fbfaa7
NL
349 if ($themeInfo === false) {
350 $themeInfo = array();
351 }
352 if (!isset($themeInfo['name'])) {
353 $themeInfo['name'] = $theme;
354 }
355 $themeInfo['current'] = ($theme === $this->getTheme());
356
357 return $themeInfo;
5011388f 358 }
182faf26 359
00dbaf90
NL
360 public function getInstalledThemes() {
361 $handle = opendir(THEME);
362 $themes = array();
f4fbfaa7 363
00dbaf90
NL
364 while (($theme = readdir($handle)) !== false) {
365 # Themes are stored in a directory, so all directory names are themes
366 # @todo move theme installation data to database
f4fbfaa7 367 if (!is_dir(THEME . '/' . $theme) || in_array($theme, array('.', '..'))) {
00dbaf90
NL
368 continue;
369 }
f4fbfaa7
NL
370
371 $themes[$theme] = $this->getThemeInfo($theme);
00dbaf90 372 }
f4fbfaa7 373
3ade95a3
NL
374 ksort($themes);
375
00dbaf90
NL
376 return $themes;
377 }
eb1af592 378
f4fbfaa7
NL
379 public function getLanguage() {
380 return $this->currentLanguage;
381 }
382
5011388f
NL
383 public function getInstalledLanguages() {
384 $handle = opendir(LOCALE);
385 $languages = array();
182faf26 386
5011388f
NL
387 while (($language = readdir($handle)) !== false) {
388 # Languages are stored in a directory, so all directory names are languages
389 # @todo move language installation data to database
cbcae403 390 if (! is_dir(LOCALE . '/' . $language) || in_array($language, array('..', '.', 'tools'))) {
5011388f
NL
391 continue;
392 }
182faf26 393
5011388f 394 $current = false;
182faf26 395
5011388f
NL
396 if ($language === $this->getLanguage()) {
397 $current = true;
398 }
182faf26 399
cbcae403 400 $languages[] = array('name' => (isset($this->language_names[$language]) ? $this->language_names[$language] : $language), 'value' => $language, 'current' => $current);
5011388f 401 }
182faf26 402
5011388f
NL
403 return $languages;
404 }
405
8d3275be 406 public function getDefaultConfig()
182faf26 407 {
8d3275be
NL
408 return array(
409 'pager' => PAGINATION,
410 'language' => LANG,
00dbaf90
NL
411 'theme' => DEFAULT_THEME
412 );
8d3275be
NL
413 }
414
eb1af592
NL
415 /**
416 * Call action (mark as fav, archive, delete, etc.)
417 */
926acd7b 418 public function action($action, Url $url, $id = 0, $import = FALSE, $autoclose = FALSE, $tags = null)
eb1af592
NL
419 {
420 switch ($action)
421 {
422 case 'add':
a297fb1e
MR
423 $content = Tools::getPageContent($url);
424 $title = ($content['rss']['channel']['item']['title'] != '') ? $content['rss']['channel']['item']['title'] : _('Untitled');
425 $body = $content['rss']['channel']['item']['description'];
426
427 // clean content from prevent xss attack
0f859c6f 428 $purifier = $this->getPurifier();
a297fb1e
MR
429 $title = $purifier->purify($title);
430 $body = $purifier->purify($body);
1570a653 431
182faf26 432 //search for possible duplicate
8d7cd2cc 433 $duplicate = NULL;
a297fb1e 434 $duplicate = $this->store->retrieveOneByURL($url->getUrl(), $this->user->getId());
488fc63b 435
182faf26 436 $last_id = $this->store->add($url->getUrl(), $title, $body, $this->user->getId());
a297fb1e 437 if ( $last_id ) {
ec397236 438 Tools::logm('add link ' . $url->getUrl());
ec397236 439 if (DOWNLOAD_PICTURES) {
42c80841 440 $content = filtre_picture($body, $url->getUrl(), $last_id);
ec397236
NL
441 Tools::logm('updating content article');
442 $this->store->updateContent($last_id, $content, $this->user->getId());
443 }
488fc63b
MR
444
445 if ($duplicate != NULL) {
446 // 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
447 Tools::logm('link ' . $url->getUrl() . ' is a duplicate');
448 // 1) - preserve tags and favorite, then drop old entry
449 $this->store->reassignTags($duplicate['id'], $last_id);
450 if ($duplicate['is_fav']) {
451 $this->store->favoriteById($last_id, $this->user->getId());
452 }
453 if ($this->store->deleteById($duplicate['id'], $this->user->getId())) {
454 Tools::logm('previous link ' . $url->getUrl() .' entry deleted');
455 }
456 }
457
182faf26 458 $this->messages->add('s', _('the link has been added successfully'));
eb1af592
NL
459 }
460 else {
a297fb1e
MR
461 $this->messages->add('e', _('error during insertion : the link wasn\'t added'));
462 Tools::logm('error during insertion : the link wasn\'t added ' . $url->getUrl());
b916bcfc 463 }
ec397236 464
a297fb1e
MR
465 if ($autoclose == TRUE) {
466 Tools::redirect('?view=home');
467 } else {
468 Tools::redirect('?view=home&closewin=true');
eb1af592
NL
469 }
470 break;
471 case 'delete':
bc1ee852 472 $msg = 'delete link #' . $id;
8d3275be 473 if ($this->store->deleteById($id, $this->user->getId())) {
eb1af592
NL
474 if (DOWNLOAD_PICTURES) {
475 remove_directory(ABS_PATH . $id);
476 }
6a361945 477 $this->messages->add('s', _('the link has been deleted successfully'));
eb1af592
NL
478 }
479 else {
6a361945 480 $this->messages->add('e', _('the link wasn\'t deleted'));
bc1ee852 481 $msg = 'error : can\'t delete link #' . $id;
eb1af592 482 }
bc1ee852 483 Tools::logm($msg);
985ce3ec 484 Tools::redirect('?');
eb1af592
NL
485 break;
486 case 'toggle_fav' :
8d3275be 487 $this->store->favoriteById($id, $this->user->getId());
eb1af592 488 Tools::logm('mark as favorite link #' . $id);
c2cf7075
MR
489 if ( Tools::isAjaxRequest() ) {
490 echo 1;
491 exit;
492 }
493 else {
494 Tools::redirect();
495 }
eb1af592
NL
496 break;
497 case 'toggle_archive' :
8d3275be 498 $this->store->archiveById($id, $this->user->getId());
eb1af592 499 Tools::logm('archive link #' . $id);
c2cf7075
MR
500 if ( Tools::isAjaxRequest() ) {
501 echo 1;
502 exit;
503 }
504 else {
505 Tools::redirect();
506 }
eb1af592 507 break;
f14807de
NL
508 case 'archive_all' :
509 $this->store->archiveAll($this->user->getId());
510 Tools::logm('archive all links');
a297fb1e 511 Tools::redirect();
f14807de 512 break;
c432fa16 513 case 'add_tag' :
a297fb1e
MR
514 $tags = explode(',', $_POST['value']);
515 $entry_id = $_POST['entry_id'];
b89d5a2b
NL
516 $entry = $this->store->retrieveOneById($entry_id, $this->user->getId());
517 if (!$entry) {
518 $this->messages->add('e', _('Article not found!'));
519 Tools::logm('error : article not found');
520 Tools::redirect();
521 }
fb26cc93
MR
522 //get all already set tags to preven duplicates
523 $already_set_tags = array();
524 $entry_tags = $this->store->retrieveTagsByEntry($entry_id);
525 foreach ($entry_tags as $tag) {
526 $already_set_tags[] = $tag['value'];
527 }
c432fa16
NL
528 foreach($tags as $key => $tag_value) {
529 $value = trim($tag_value);
fb26cc93
MR
530 if ($value && !in_array($value, $already_set_tags)) {
531 $tag = $this->store->retrieveTagByValue($value);
532
533 if (is_null($tag)) {
534 # we create the tag
535 $tag = $this->store->createTag($value);
536 $sequence = '';
537 if (STORAGE == 'postgres') {
538 $sequence = 'tags_id_seq';
539 }
540 $tag_id = $this->store->getLastId($sequence);
541 }
542 else {
543 $tag_id = $tag['id'];
544 }
545
546 # we assign the tag to the article
547 $this->store->setTagToEntry($tag_id, $entry_id);
c432fa16 548 }
c432fa16 549 }
a297fb1e 550 Tools::redirect();
c432fa16
NL
551 break;
552 case 'remove_tag' :
553 $tag_id = $_GET['tag_id'];
b89d5a2b
NL
554 $entry = $this->store->retrieveOneById($id, $this->user->getId());
555 if (!$entry) {
556 $this->messages->add('e', _('Article not found!'));
557 Tools::logm('error : article not found');
558 Tools::redirect();
559 }
c432fa16
NL
560 $this->store->removeTagForEntry($id, $tag_id);
561 Tools::redirect();
562 break;
eb1af592
NL
563 default:
564 break;
565 }
566 }
567
568 function displayView($view, $id = 0)
569 {
570 $tpl_vars = array();
571
572 switch ($view)
573 {
eb1af592 574 case 'config':
11c680f9
NL
575 $dev_infos = $this->getPocheVersion('dev');
576 $dev = trim($dev_infos[0]);
577 $check_time_dev = date('d-M-Y H:i', $dev_infos[1]);
578 $prod_infos = $this->getPocheVersion('prod');
579 $prod = trim($prod_infos[0]);
580 $check_time_prod = date('d-M-Y H:i', $prod_infos[1]);
031df528
NL
581 $compare_dev = version_compare(POCHE, $dev);
582 $compare_prod = version_compare(POCHE, $prod);
00dbaf90 583 $themes = $this->getInstalledThemes();
5011388f 584 $languages = $this->getInstalledLanguages();
72c20a52 585 $token = $this->user->getConfigValue('token');
1810c13b 586 $http_auth = (isset($_SERVER['PHP_AUTH_USER']) || isset($_SERVER['REMOTE_USER'])) ? true : false;
4d99bae8 587 $only_user = ($this->store->listUsers() > 1) ? false : true;
32520785 588 $tpl_vars = array(
00dbaf90 589 'themes' => $themes,
5011388f 590 'languages' => $languages,
32520785
NL
591 'dev' => $dev,
592 'prod' => $prod,
11c680f9
NL
593 'check_time_dev' => $check_time_dev,
594 'check_time_prod' => $check_time_prod,
32520785
NL
595 'compare_dev' => $compare_dev,
596 'compare_prod' => $compare_prod,
72c20a52
NL
597 'token' => $token,
598 'user_id' => $this->user->getId(),
df6afaf0 599 'http_auth' => $http_auth,
4d99bae8 600 'only_user' => $only_user
32520785 601 );
eb1af592
NL
602 Tools::logm('config view');
603 break;
6cab59c3
NL
604 case 'edit-tags':
605 # tags
b89d5a2b
NL
606 $entry = $this->store->retrieveOneById($id, $this->user->getId());
607 if (!$entry) {
608 $this->messages->add('e', _('Article not found!'));
609 Tools::logm('error : article not found');
610 Tools::redirect();
611 }
6cab59c3
NL
612 $tags = $this->store->retrieveTagsByEntry($id);
613 $tpl_vars = array(
c432fa16 614 'entry_id' => $id,
6cab59c3 615 'tags' => $tags,
032e0ca1 616 'entry' => $entry,
4886ed6d
NL
617 );
618 break;
2e2ebe5e 619 case 'tags':
f778e472 620 $token = $this->user->getConfigValue('token');
fb26cc93
MR
621 //if term is set - search tags for this term
622 $term = Tools::checkVar('term');
623 $tags = $this->store->retrieveAllTags($this->user->getId(), $term);
624 if (Tools::isAjaxRequest()) {
625 $result = array();
626 foreach ($tags as $tag) {
627 $result[] = $tag['value'];
628 }
629 echo json_encode($result);
630 exit;
631 }
2e2ebe5e 632 $tpl_vars = array(
f778e472
NL
633 'token' => $token,
634 'user_id' => $this->user->getId(),
2e2ebe5e
NL
635 'tags' => $tags,
636 );
637 break;
a4585f7e
MR
638 case 'search':
639 if (isset($_GET['search'])) {
640 $search = filter_var($_GET['search'], FILTER_SANITIZE_STRING);
641 $tpl_vars['entries'] = $this->store->search($search, $this->user->getId());
642 $count = count($tpl_vars['entries']);
643 $this->pagination->set_total($count);
644 $page_links = str_replace(array('previous', 'next'), array(_('previous'), _('next')),
645 $this->pagination->page_links('?view=' . $view . '?search=' . $search . '&sort=' . $_SESSION['sort'] . '&' ));
646 $tpl_vars['page_links'] = $page_links;
647 $tpl_vars['nb_results'] = $count;
648 $tpl_vars['search_term'] = $search;
649 }
650 break;
eb1af592 651 case 'view':
8d3275be 652 $entry = $this->store->retrieveOneById($id, $this->user->getId());
eb1af592
NL
653 if ($entry != NULL) {
654 Tools::logm('view link #' . $id);
655 $content = $entry['content'];
656 if (function_exists('tidy_parse_string')) {
657 $tidy = tidy_parse_string($content, array('indent'=>true, 'show-body-only' => true), 'UTF8');
658 $tidy->cleanRepair();
659 $content = $tidy->value;
3408ed48 660 }
a3223127 661
3408ed48
NL
662 # flattr checking
663 $flattr = new FlattrItem();
7b171c73
NL
664 $flattr->checkItem($entry['url'], $entry['id']);
665
666 # tags
667 $tags = $this->store->retrieveTagsByEntry($entry['id']);
a3223127 668
3408ed48 669 $tpl_vars = array(
7b171c73
NL
670 'entry' => $entry,
671 'content' => $content,
672 'flattr' => $flattr,
673 'tags' => $tags
3408ed48 674 );
eb1af592
NL
675 }
676 else {
d8d1542e 677 Tools::logm('error in view call : entry is null');
eb1af592
NL
678 }
679 break;
032e0ca1 680 default: # home, favorites, archive and tag views
eb1af592 681 $tpl_vars = array(
3eb04903
N
682 'entries' => '',
683 'page_links' => '',
7f9f5281 684 'nb_results' => '',
6065553c 685 'listmode' => (isset($_COOKIE['listmode']) ? true : false),
eb1af592 686 );
182faf26 687
032e0ca1
MR
688 //if id is given - we retrive entries by tag: id is tag id
689 if ($id) {
690 $tpl_vars['tag'] = $this->store->retrieveTag($id, $this->user->getId());
691 $tpl_vars['id'] = intval($id);
692 }
693
694 $count = $this->store->getEntriesByViewCount($view, $this->user->getId(), $id);
695
696 if ($count > 0) {
697 $this->pagination->set_total($count);
c515ffec 698 $page_links = str_replace(array('previous', 'next'), array(_('previous'), _('next')),
032e0ca1
MR
699 $this->pagination->page_links('?view=' . $view . '&sort=' . $_SESSION['sort'] . (($id)?'&id='.$id:'') . '&' ));
700 $tpl_vars['entries'] = $this->store->getEntriesByView($view, $this->user->getId(), $this->pagination->get_limit(), $id);
3eb04903 701 $tpl_vars['page_links'] = $page_links;
032e0ca1 702 $tpl_vars['nb_results'] = $count;
3eb04903 703 }
6a361945 704 Tools::logm('display ' . $view . ' view');
eb1af592
NL
705 break;
706 }
707
708 return $tpl_vars;
709 }
c765c367 710
07ee09f4 711 /**
182faf26
MR
712 * update the password of the current user.
713 * if MODE_DEMO is TRUE, the password can't be updated.
07ee09f4
NL
714 * @todo add the return value
715 * @todo set the new password in function header like this updatePassword($newPassword)
716 * @return boolean
717 */
c765c367
NL
718 public function updatePassword()
719 {
55821e04 720 if (MODE_DEMO) {
8d3275be 721 $this->messages->add('i', _('in demo mode, you can\'t update your password'));
55821e04 722 Tools::logm('in demo mode, you can\'t do this');
6a361945 723 Tools::redirect('?view=config');
55821e04
NL
724 }
725 else {
726 if (isset($_POST['password']) && isset($_POST['password_repeat'])) {
727 if ($_POST['password'] == $_POST['password_repeat'] && $_POST['password'] != "") {
8d3275be
NL
728 $this->messages->add('s', _('your password has been updated'));
729 $this->store->updatePassword($this->user->getId(), Tools::encodeString($_POST['password'] . $this->user->getUsername()));
c765c367 730 Session::logout();
8d3275be 731 Tools::logm('password updated');
c765c367
NL
732 Tools::redirect();
733 }
734 else {
8d3275be 735 $this->messages->add('e', _('the two fields have to be filled & the password must be the same in the two fields'));
6a361945 736 Tools::redirect('?view=config');
c765c367
NL
737 }
738 }
739 }
740 }
182faf26 741
00dbaf90
NL
742 public function updateTheme()
743 {
744 # no data
745 if (empty($_POST['theme'])) {
746 }
182faf26 747
00dbaf90
NL
748 # we are not going to change it to the current theme...
749 if ($_POST['theme'] == $this->getTheme()) {
750 $this->messages->add('w', _('still using the "' . $this->getTheme() . '" theme!'));
751 Tools::redirect('?view=config');
752 }
182faf26 753
00dbaf90
NL
754 $themes = $this->getInstalledThemes();
755 $actualTheme = false;
182faf26 756
f4fbfaa7
NL
757 foreach (array_keys($themes) as $theme) {
758 if ($theme == $_POST['theme']) {
00dbaf90
NL
759 $actualTheme = true;
760 break;
761 }
762 }
182faf26 763
00dbaf90
NL
764 if (! $actualTheme) {
765 $this->messages->add('e', _('that theme does not seem to be installed'));
766 Tools::redirect('?view=config');
767 }
182faf26 768
00dbaf90
NL
769 $this->store->updateUserConfig($this->user->getId(), 'theme', $_POST['theme']);
770 $this->messages->add('s', _('you have changed your theme preferences'));
182faf26 771
00dbaf90
NL
772 $currentConfig = $_SESSION['poche_user']->config;
773 $currentConfig['theme'] = $_POST['theme'];
182faf26 774
00dbaf90 775 $_SESSION['poche_user']->setConfig($currentConfig);
56532c4e
NL
776
777 $this->emptyCache();
182faf26 778
00dbaf90
NL
779 Tools::redirect('?view=config');
780 }
c765c367 781
5011388f
NL
782 public function updateLanguage()
783 {
784 # no data
785 if (empty($_POST['language'])) {
786 }
182faf26 787
5011388f
NL
788 # we are not going to change it to the current language...
789 if ($_POST['language'] == $this->getLanguage()) {
790 $this->messages->add('w', _('still using the "' . $this->getLanguage() . '" language!'));
791 Tools::redirect('?view=config');
792 }
182faf26 793
5011388f
NL
794 $languages = $this->getInstalledLanguages();
795 $actualLanguage = false;
182faf26 796
5011388f 797 foreach ($languages as $language) {
c9bd17a1 798 if ($language['value'] == $_POST['language']) {
5011388f
NL
799 $actualLanguage = true;
800 break;
801 }
802 }
182faf26 803
5011388f
NL
804 if (! $actualLanguage) {
805 $this->messages->add('e', _('that language does not seem to be installed'));
806 Tools::redirect('?view=config');
807 }
182faf26 808
5011388f
NL
809 $this->store->updateUserConfig($this->user->getId(), 'language', $_POST['language']);
810 $this->messages->add('s', _('you have changed your language preferences'));
182faf26 811
5011388f
NL
812 $currentConfig = $_SESSION['poche_user']->config;
813 $currentConfig['language'] = $_POST['language'];
182faf26 814
5011388f 815 $_SESSION['poche_user']->setConfig($currentConfig);
e145f767
NL
816
817 $this->emptyCache();
182faf26 818
5011388f 819 Tools::redirect('?view=config');
182faf26 820 }
df6afaf0
DS
821 /**
822 * get credentials from differents sources
823 * it redirects the user to the $referer link
824 * @return array
825 */
1810c13b
NL
826 private function credentials() {
827 if(isset($_SERVER['PHP_AUTH_USER'])) {
6af66b11 828 return array($_SERVER['PHP_AUTH_USER'],'php_auth',true);
1810c13b
NL
829 }
830 if(!empty($_POST['login']) && !empty($_POST['password'])) {
6af66b11 831 return array($_POST['login'],$_POST['password'],false);
1810c13b
NL
832 }
833 if(isset($_SERVER['REMOTE_USER'])) {
6af66b11 834 return array($_SERVER['REMOTE_USER'],'http_auth',true);
1810c13b 835 }
5cfafc61 836
6af66b11
MR
837 return array(false,false,false);
838 }
df6afaf0 839
07ee09f4
NL
840 /**
841 * checks if login & password are correct and save the user in session.
842 * it redirects the user to the $referer link
843 * @param string $referer the url to redirect after login
844 * @todo add the return value
845 * @return boolean
846 */
c765c367
NL
847 public function login($referer)
848 {
6af66b11 849 list($login,$password,$isauthenticated)=$this->credentials();
df6afaf0
DS
850 if($login === false || $password === false) {
851 $this->messages->add('e', _('login failed: you have to fill all fields'));
852 Tools::logm('login failed');
853 Tools::redirect();
854 }
855 if (!empty($login) && !empty($password)) {
6af66b11 856 $user = $this->store->login($login, Tools::encodeString($password . $login), $isauthenticated);
7ce7ec4c
NL
857 if ($user != array()) {
858 # Save login into Session
6af66b11
MR
859 $longlastingsession = isset($_POST['longlastingsession']);
860 $passwordTest = ($isauthenticated) ? $user['password'] : Tools::encodeString($password . $login);
861 Session::login($user['username'], $user['password'], $login, $passwordTest, $longlastingsession, array('poche_user' => new User($user)));
26929c08 862 $this->messages->add('s', _('welcome to your wallabag'));
8d3275be 863 Tools::logm('login successful');
c765c367
NL
864 Tools::redirect($referer);
865 }
8d3275be 866 $this->messages->add('e', _('login failed: bad login or password'));
c765c367
NL
867 Tools::logm('login failed');
868 Tools::redirect();
c765c367
NL
869 }
870 }
871
07ee09f4
NL
872 /**
873 * log out the poche user. It cleans the session.
874 * @todo add the return value
182faf26 875 * @return boolean
07ee09f4 876 */
c765c367
NL
877 public function logout()
878 {
7ce7ec4c 879 $this->user = array();
c765c367 880 Session::logout();
b916bcfc 881 Tools::logm('logout');
c765c367
NL
882 Tools::redirect();
883 }
884
07ee09f4
NL
885 /**
886 * import datas into your poche
182faf26 887 * @return boolean
07ee09f4 888 */
182faf26
MR
889 public function import() {
890
891 if ( isset($_FILES['file']) ) {
5ce39784
MR
892 Tools::logm('Import stated: parsing file');
893
182faf26
MR
894 // assume, that file is in json format
895 $str_data = file_get_contents($_FILES['file']['tmp_name']);
896 $data = json_decode($str_data, true);
897
898 if ( $data === null ) {
899 //not json - assume html
900 $html = new simple_html_dom();
901 $html->load_file($_FILES['file']['tmp_name']);
902 $data = array();
903 $read = 0;
904 foreach (array('ol','ul') as $list) {
905 foreach ($html->find($list) as $ul) {
86da3988
MR
906 foreach ($ul->find('li') as $li) {
907 $tmpEntry = array();
a8ef1f3f
MR
908 $a = $li->find('a');
909 $tmpEntry['url'] = $a[0]->href;
910 $tmpEntry['tags'] = $a[0]->tags;
911 $tmpEntry['is_read'] = $read;
912 if ($tmpEntry['url']) {
913 $data[] = $tmpEntry;
914 }
86da3988
MR
915 }
916 # the second <ol/ul> is for read links
917 $read = ((sizeof($data) && $read)?0:1);
182faf26
MR
918 }
919 }
63c35580 920 }
182faf26 921
a297fb1e
MR
922 //for readability structure
923 foreach ($data as $record) {
924 if (is_array($record)) {
925 $data[] = $record;
926 foreach ($record as $record2) {
927 if (is_array($record2)) {
86da3988 928 $data[] = $record2;
a297fb1e
MR
929 }
930 }
931 }
932 }
933
86da3988 934 $urlsInserted = array(); //urls of articles inserted
182faf26 935 foreach ($data as $record) {
a297fb1e 936 $url = trim( isset($record['article__url']) ? $record['article__url'] : (isset($record['url']) ? $record['url'] : '') );
86da3988 937 if ( $url and !in_array($url, $urlsInserted) ) {
182faf26
MR
938 $title = (isset($record['title']) ? $record['title'] : _('Untitled - Import - ').'</a> <a href="./?import">'._('click to finish import').'</a><a>');
939 $body = (isset($record['content']) ? $record['content'] : '');
a297fb1e
MR
940 $isRead = (isset($record['is_read']) ? intval($record['is_read']) : (isset($record['archive'])?intval($record['archive']):0));
941 $isFavorite = (isset($record['is_fav']) ? intval($record['is_fav']) : (isset($record['favorite'])?intval($record['favorite']):0) );
182faf26
MR
942 //insert new record
943 $id = $this->store->add($url, $title, $body, $this->user->getId(), $isFavorite, $isRead);
944 if ( $id ) {
86da3988
MR
945 $urlsInserted[] = $url; //add
946
182faf26 947 if ( isset($record['tags']) && trim($record['tags']) ) {
86da3988 948 //@TODO: set tags
182faf26
MR
949
950 }
951 }
952 }
953 }
954
86da3988 955 $i = sizeof($urlsInserted);
182faf26
MR
956 if ( $i > 0 ) {
957 $this->messages->add('s', _('Articles inserted: ').$i._('. Please note, that some may be marked as "read".'));
958 }
5ce39784 959 Tools::logm('Import of articles finished: '.$i.' articles added (w/o content if not provided).');
182faf26
MR
960 }
961 //file parsing finished here
962
963 //now download article contents if any
964
965 //check if we need to download any content
966 $recordsDownloadRequired = $this->store->retrieveUnfetchedEntriesCount($this->user->getId());
967 if ( $recordsDownloadRequired == 0 ) {
968 //nothing to download
969 $this->messages->add('s', _('Import finished.'));
5ce39784 970 Tools::logm('Import finished completely');
182faf26
MR
971 Tools::redirect();
972 }
973 else {
974 //if just inserted - don't download anything, download will start in next reload
975 if ( !isset($_FILES['file']) ) {
976 //download next batch
5ce39784 977 Tools::logm('Fetching next batch of articles...');
182faf26
MR
978 $items = $this->store->retrieveUnfetchedEntries($this->user->getId(), IMPORT_LIMIT);
979
0f859c6f 980 $purifier = $this->getPurifier();
182faf26
MR
981
982 foreach ($items as $item) {
86da3988 983 $url = new Url(base64_encode($item['url']));
5ce39784 984 Tools::logm('Fetching article '.$item['id']);
86da3988 985 $content = Tools::getPageContent($url);
182faf26 986
86da3988
MR
987 $title = (($content['rss']['channel']['item']['title'] != '') ? $content['rss']['channel']['item']['title'] : _('Untitled'));
988 $body = (($content['rss']['channel']['item']['description'] != '') ? $content['rss']['channel']['item']['description'] : _('Undefined'));
182faf26 989
86da3988
MR
990 //clean content to prevent xss attack
991 $title = $purifier->purify($title);
992 $body = $purifier->purify($body);
182faf26 993
86da3988 994 $this->store->updateContentAndTitle($item['id'], $title, $body, $this->user->getId());
5ce39784 995 Tools::logm('Article '.$item['id'].' updated.');
182faf26
MR
996 }
997
63c35580 998 }
182faf26
MR
999 }
1000
1001 return array('includeImport'=>true, 'import'=>array('recordsDownloadRequired'=>$recordsDownloadRequired, 'recordsUnderDownload'=> IMPORT_LIMIT, 'delay'=> IMPORT_DELAY * 1000) );
63c35580 1002 }
c765c367 1003
07ee09f4
NL
1004 /**
1005 * export poche entries in json
1006 * @return json all poche entries
1007 */
a8ef1f3f
MR
1008 public function export() {
1009 $filename = "wallabag-export-".$this->user->getId()."-".date("Y-m-d").".json";
1010 header('Content-Disposition: attachment; filename='.$filename);
1011
1012 $entries = $this->store->retrieveAll($this->user->getId());
1013 echo $this->tpl->render('export.twig', array(
1014 'export' => Tools::renderJson($entries),
1015 ));
1016 Tools::logm('export view');
c765c367 1017 }
32520785 1018
07ee09f4 1019 /**
a3436d4c 1020 * Checks online the latest version of poche and cache it
07ee09f4
NL
1021 * @param string $which 'prod' or 'dev'
1022 * @return string latest $which version
1023 */
a8ef1f3f
MR
1024 private function getPocheVersion($which = 'prod') {
1025 $cache_file = CACHE . '/' . $which;
1026 $check_time = time();
1027
1028 # checks if the cached version file exists
1029 if (file_exists($cache_file) && (filemtime($cache_file) > (time() - 86400 ))) {
1030 $version = file_get_contents($cache_file);
1031 $check_time = filemtime($cache_file);
1032 } else {
1033 $version = file_get_contents('http://static.wallabag.org/versions/' . $which);
1034 file_put_contents($cache_file, $version, LOCK_EX);
1035 }
1036 return array($version, $check_time);
32520785 1037 }
72c20a52
NL
1038
1039 public function generateToken()
1040 {
a8ef1f3f
MR
1041 if (ini_get('open_basedir') === '') {
1042 if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
1043 echo 'This is a server using Windows!';
1044 // alternative to /dev/urandom for Windows
1045 $token = substr(base64_encode(uniqid(mt_rand(), true)), 0, 20);
1046 } else {
1047 $token = substr(base64_encode(file_get_contents('/dev/urandom', false, null, 0, 20)), 0, 15);
72c20a52 1048 }
a8ef1f3f
MR
1049 }
1050 else {
1051 $token = substr(base64_encode(uniqid(mt_rand(), true)), 0, 20);
1052 }
72c20a52 1053
a8ef1f3f
MR
1054 $token = str_replace('+', '', $token);
1055 $this->store->updateUserConfig($this->user->getId(), 'token', $token);
1056 $currentConfig = $_SESSION['poche_user']->config;
1057 $currentConfig['token'] = $token;
1058 $_SESSION['poche_user']->setConfig($currentConfig);
1059 Tools::redirect();
72c20a52
NL
1060 }
1061
f778e472 1062 public function generateFeeds($token, $user_id, $tag_id, $type = 'home')
72c20a52 1063 {
f778e472 1064 $allowed_types = array('home', 'fav', 'archive', 'tag');
72c20a52
NL
1065 $config = $this->store->getConfigUser($user_id);
1066
17b2afef
NL
1067 if ($config == null) {
1068 die(_('User with this id (' . $user_id . ') does not exist.'));
1069 }
1070
72c20a52
NL
1071 if (!in_array($type, $allowed_types) ||
1072 $token != $config['token']) {
1073 die(_('Uh, there is a problem while generating feeds.'));
1074 }
1075 // Check the token
1076
9e7c840b 1077 $feed = new FeedWriter(RSS2);
2e4440c3 1078 $feed->setTitle('wallabag — ' . $type . ' feed');
72c20a52 1079 $feed->setLink(Tools::getPocheUrl());
223268c2
NL
1080 $feed->setChannelElement('pubDate', date(DATE_RSS , time()));
1081 $feed->setChannelElement('generator', 'wallabag');
1082 $feed->setDescription('wallabag ' . $type . ' elements');
72c20a52 1083
f778e472 1084 if ($type == 'tag') {
b89d5a2b 1085 $entries = $this->store->retrieveEntriesByTag($tag_id, $user_id);
f778e472
NL
1086 }
1087 else {
1088 $entries = $this->store->getEntriesByView($type, $user_id);
1089 }
1090
72c20a52
NL
1091 if (count($entries) > 0) {
1092 foreach ($entries as $entry) {
1093 $newItem = $feed->createNewItem();
0b57c682 1094 $newItem->setTitle($entry['title']);
f86784c2 1095 $newItem->setSource(Tools::getPocheUrl() . '?view=view&amp;id=' . $entry['id']);
ed02e38e 1096 $newItem->setLink($entry['url']);
72c20a52
NL
1097 $newItem->setDate(time());
1098 $newItem->setDescription($entry['content']);
1099 $feed->addItem($newItem);
1100 }
1101 }
1102
1103 $feed->genarateFeed();
1104 exit;
1105 }
6285e57c
NL
1106
1107 public function emptyCache() {
1108 $files = new RecursiveIteratorIterator(
1109 new RecursiveDirectoryIterator(CACHE, RecursiveDirectoryIterator::SKIP_DOTS),
1110 RecursiveIteratorIterator::CHILD_FIRST
1111 );
1112
1113 foreach ($files as $fileinfo) {
1114 $todo = ($fileinfo->isDir() ? 'rmdir' : 'unlink');
1115 $todo($fileinfo->getRealPath());
1116 }
1117
1118 Tools::logm('empty cache');
1119 $this->messages->add('s', _('Cache deleted.'));
1120 Tools::redirect();
1121 }
0f859c6f
MR
1122
1123 /**
1124 * return new purifier object with actual config
1125 */
1126 protected function getPurifier() {
1127 $config = HTMLPurifier_Config::createDefault();\r
1128 $config->set('Cache.SerializerPath', CACHE);\r
1129 $config->set('HTML.SafeIframe', true);\r
1130 $config->set('URI.SafeIframeRegexp', '%^(https?:)?//(www\.youtube(?:-nocookie)?\.com/embed/|player\.vimeo\.com/video/)%'); //allow YouTube and Vimeo$purifier = new HTMLPurifier($config);
1131\r
1132 return new HTMLPurifier($config);
1133 }
87090d8a 1134
1135 /**
1136 * handle epub
1137 */
1138 public function createEpub() {
1139
1140 if (isset($_GET['epub']) && isset($_GET['id'])) {
1141 if ($_GET['id'] == "all") { // we put all entries in the file
1142 $entries = $this->store->retrieveAll($this->user->getId());
1143 }
1144 else { // we put only one entry in the file
1145 $entryID = filter_var($_GET['id'],FILTER_SANITIZE_NUMBER_INT);
1146 $entry = $this->store->retrieveOneById($entryID, $this->user->getId());
1147 $entries = array($entry);
1148 }
1149 }
1150 $content_start =
1151 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
1152 . "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\"\n"
1153 . " \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n"
1154 . "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
1155 . "<head>"
1156 . "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n"
1157 . "<link rel=\"stylesheet\" type=\"text/css\" href=\"styles.css\" />\n"
1158 . "<title>Test Book</title>\n"
1159 . "</head>\n"
1160 . "<body>\n";
1161
1162 $bookEnd = "</body>\n</html>\n";
1163
1164 $log = new Logger($entryID, TRUE);
1165 $fileDir = CACHE;
1166
1167
1168 $book = new EPub();
1169 $log->logLine("new EPub()");
1170 $log->logLine("EPub class version: " . EPub::VERSION);
1171 $log->logLine("EPub Req. Zip version: " . EPub::REQ_ZIP_VERSION);
1172 $log->logLine("Zip version: " . Zip::VERSION);
1173 $log->logLine("getCurrentServerURL: " . $book->getCurrentServerURL());
1174 $log->logLine("getCurrentPageURL..: " . $book->getCurrentPageURL());
1175
1176 $book->setTitle("wallabag's articles");
1177 $book->setIdentifier("http://$_SERVER[HTTP_HOST]", EPub::IDENTIFIER_URI); // Could also be the ISBN number, prefered for published books, or a UUID.
1178 //$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.
1179 $book->setDescription("Some articles saved on my wallabag");
1180 $book->setAuthor("wallabag","wallabag");
1181 $book->setPublisher("wallabag","wallabag"); // I hope this is a non existant address :)
1182 $book->setDate(time()); // Strictly not needed as the book date defaults to time().
1183 //$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.
1184 $book->setSourceURL("http://$_SERVER[HTTP_HOST]");
1185
1186 $book->addDublinCoreMetadata(DublinCore::CONTRIBUTOR, "PHP");
1187
1188 $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";
1189 $cover = $content_start . "<h1>My articles on wallabag</h1>\n<h2>As seen on : http://$_SERVER[HTTP_HOST]</h2>\n" . $bookEnd;
1190 $book->addChapter("Notices", "Cover.html", $cover);
1191 $book->buildTOC(NULL, "toc", "Table of Contents", TRUE, TRUE);
1192
1193 foreach ($entries as $entry) {
1194 $tags = $this->store->retrieveTagsByEntry($entry['id']);
1195 foreach ($tags as $tag) {
1196 $book->setSubject($tag);
1197 }
1198
1199 $log->logLine("Set up parameters");
1200
1201
1202
1203 $chapter = $content_start . $entry['content'] . $bookEnd;
1204 $book->addChapter("Chapter " . $entry['id'] . ": " . $entry['title'], htmlspecialchars($entry['title']) . ".html", $chapter, true, EPub::EXTERNAL_REF_ADD);
1205 }
1206
1207
1208 if (true) {
1209 $epuplog = $book->getLog();
1210 $book->addChapter("Log", "Log.html", $content_start . $log->getLog() . "\n</pre>" . $bookEnd); // generation log
1211 // Only used in case we need to debug EPub.php.
1212 //$book->addChapter("ePubLog", "ePubLog.html", $content_start . $epuplog . "\n</pre>" . $bookEnd);
1213 }
1214 $book->finalize();
1215 $zipData = $book->sendBook("wallabag's articles");
1216 }
df6afaf0 1217}