aboutsummaryrefslogtreecommitdiffhomepage
path: root/inc/poche/Poche.class.php
diff options
context:
space:
mode:
Diffstat (limited to 'inc/poche/Poche.class.php')
-rwxr-xr-xinc/poche/Poche.class.php947
1 files changed, 515 insertions, 432 deletions
diff --git a/inc/poche/Poche.class.php b/inc/poche/Poche.class.php
index 8a9de488..09a9f5ff 100755
--- a/inc/poche/Poche.class.php
+++ b/inc/poche/Poche.class.php
@@ -18,7 +18,7 @@ class Poche
18 public $tpl; 18 public $tpl;
19 public $messages; 19 public $messages;
20 public $pagination; 20 public $pagination;
21 21
22 private $currentTheme = ''; 22 private $currentTheme = '';
23 private $currentLanguage = ''; 23 private $currentLanguage = '';
24 private $notInstalledMessage = array(); 24 private $notInstalledMessage = array();
@@ -32,20 +32,21 @@ class Poche
32 'fr_FR.utf8' => 'Français', 32 'fr_FR.utf8' => 'Français',
33 'it_IT.utf8' => 'Italiano', 33 'it_IT.utf8' => 'Italiano',
34 'pl_PL.utf8' => 'Polski', 34 'pl_PL.utf8' => 'Polski',
35 'pt_BR.utf8' => 'Português (Brasil)',
35 'ru_RU.utf8' => 'Pусский', 36 'ru_RU.utf8' => 'Pусский',
36 'sl_SI.utf8' => 'Slovenščina', 37 'sl_SI.utf8' => 'Slovenščina',
37 'uk_UA.utf8' => 'Українськй', 38 'uk_UA.utf8' => 'Українськ',
38 ); 39 );
39 public function __construct() 40 public function __construct()
40 { 41 {
41 if ($this->configFileIsAvailable()) { 42 if ($this->configFileIsAvailable()) {
42 $this->init(); 43 $this->init();
43 } 44 }
44 45
45 if ($this->themeIsInstalled()) { 46 if ($this->themeIsInstalled()) {
46 $this->initTpl(); 47 $this->initTpl();
47 } 48 }
48 49
49 if ($this->systemIsInstalled()) { 50 if ($this->systemIsInstalled()) {
50 $this->store = new Database(); 51 $this->store = new Database();
51 $this->messages = new Messages(); 52 $this->messages = new Messages();
@@ -56,12 +57,10 @@ class Poche
56 $this->store->checkTags(); 57 $this->store->checkTags();
57 } 58 }
58 } 59 }
59 60
60 private function init() 61 private function init()
61 { 62 {
62 Tools::initPhp(); 63 Tools::initPhp();
63 Session::$sessionName = 'poche';
64 Session::init();
65 64
66 if (isset($_SESSION['poche_user']) && $_SESSION['poche_user'] != array()) { 65 if (isset($_SESSION['poche_user']) && $_SESSION['poche_user'] != array()) {
67 $this->user = $_SESSION['poche_user']; 66 $this->user = $_SESSION['poche_user'];
@@ -73,43 +72,43 @@ class Poche
73 72
74 # l10n 73 # l10n
75 $language = $this->user->getConfigValue('language'); 74 $language = $this->user->getConfigValue('language');
76 putenv('LC_ALL=' . $language); 75 @putenv('LC_ALL=' . $language);
77 setlocale(LC_ALL, $language); 76 setlocale(LC_ALL, $language);
78 bindtextdomain($language, LOCALE); 77 bindtextdomain($language, LOCALE);
79 textdomain($language); 78 textdomain($language);
80 79
81 # Pagination 80 # Pagination
82 $this->pagination = new Paginator($this->user->getConfigValue('pager'), 'p'); 81 $this->pagination = new Paginator($this->user->getConfigValue('pager'), 'p');
83 82
84 # Set up theme 83 # Set up theme
85 $themeDirectory = $this->user->getConfigValue('theme'); 84 $themeDirectory = $this->user->getConfigValue('theme');
86 85
87 if ($themeDirectory === false) { 86 if ($themeDirectory === false) {
88 $themeDirectory = DEFAULT_THEME; 87 $themeDirectory = DEFAULT_THEME;
89 } 88 }
90 89
91 $this->currentTheme = $themeDirectory; 90 $this->currentTheme = $themeDirectory;
92 91
93 # Set up language 92 # Set up language
94 $languageDirectory = $this->user->getConfigValue('language'); 93 $languageDirectory = $this->user->getConfigValue('language');
95 94
96 if ($languageDirectory === false) { 95 if ($languageDirectory === false) {
97 $languageDirectory = DEFAULT_THEME; 96 $languageDirectory = DEFAULT_THEME;
98 } 97 }
99 98
100 $this->currentLanguage = $languageDirectory; 99 $this->currentLanguage = $languageDirectory;
101 } 100 }
102 101
103 public function configFileIsAvailable() { 102 public function configFileIsAvailable() {
104 if (! self::$configFileAvailable) { 103 if (! self::$configFileAvailable) {
105 $this->notInstalledMessage[] = 'You have to rename inc/poche/config.inc.php.new to inc/poche/config.inc.php.'; 104 $this->notInstalledMessage[] = 'You have to copy (don\'t just rename!) inc/poche/config.inc.default.php to inc/poche/config.inc.php.';
106 105
107 return false; 106 return false;
108 } 107 }
109 108
110 return true; 109 return true;
111 } 110 }
112 111
113 public function themeIsInstalled() { 112 public function themeIsInstalled() {
114 $passTheme = TRUE; 113 $passTheme = TRUE;
115 # Twig is an absolute requirement for Poche to function. Abort immediately if the Composer installer hasn't been run yet 114 # Twig is an absolute requirement for Poche to function. Abort immediately if the Composer installer hasn't been run yet
@@ -124,27 +123,27 @@ class Poche
124 self::$canRenderTemplates = false; 123 self::$canRenderTemplates = false;
125 124
126 $passTheme = FALSE; 125 $passTheme = FALSE;
127 } 126 }
128 127
129 # Check if the selected theme and its requirements are present 128 # Check if the selected theme and its requirements are present
130 $theme = $this->getTheme(); 129 $theme = $this->getTheme();
131 130
132 if ($theme != '' && ! is_dir(THEME . '/' . $theme)) { 131 if ($theme != '' && ! is_dir(THEME . '/' . $theme)) {
133 $this->notInstalledMessage[] = 'The currently selected theme (' . $theme . ') does not seem to be properly installed (Missing directory: ' . THEME . '/' . $theme . ')'; 132 $this->notInstalledMessage[] = 'The currently selected theme (' . $theme . ') does not seem to be properly installed (Missing directory: ' . THEME . '/' . $theme . ')';
134 133
135 self::$canRenderTemplates = false; 134 self::$canRenderTemplates = false;
136 135
137 $passTheme = FALSE; 136 $passTheme = FALSE;
138 } 137 }
139 138
140 $themeInfo = $this->getThemeInfo($theme); 139 $themeInfo = $this->getThemeInfo($theme);
141 if (isset($themeInfo['requirements']) && is_array($themeInfo['requirements'])) { 140 if (isset($themeInfo['requirements']) && is_array($themeInfo['requirements'])) {
142 foreach ($themeInfo['requirements'] as $requiredTheme) { 141 foreach ($themeInfo['requirements'] as $requiredTheme) {
143 if (! is_dir(THEME . '/' . $requiredTheme)) { 142 if (! is_dir(THEME . '/' . $requiredTheme)) {
144 $this->notInstalledMessage[] = 'The required "' . $requiredTheme . '" theme is missing for the current theme (' . $theme . ')'; 143 $this->notInstalledMessage[] = 'The required "' . $requiredTheme . '" theme is missing for the current theme (' . $theme . ')';
145 144
146 self::$canRenderTemplates = false; 145 self::$canRenderTemplates = false;
147 146
148 $passTheme = FALSE; 147 $passTheme = FALSE;
149 } 148 }
150 } 149 }
@@ -154,21 +153,21 @@ class Poche
154 return FALSE; 153 return FALSE;
155 } 154 }
156 155
157 156
158 return true; 157 return true;
159 } 158 }
160 159
161 /** 160 /**
162 * all checks before installation. 161 * all checks before installation.
163 * @todo move HTML to template 162 * @todo move HTML to template
164 * @return boolean 163 * @return boolean
165 */ 164 */
166 public function systemIsInstalled() 165 public function systemIsInstalled()
167 { 166 {
168 $msg = TRUE; 167 $msg = TRUE;
169 168
170 $configSalt = defined('SALT') ? constant('SALT') : ''; 169 $configSalt = defined('SALT') ? constant('SALT') : '';
171 170
172 if (empty($configSalt)) { 171 if (empty($configSalt)) {
173 $this->notInstalledMessage[] = 'You have not yet filled in the SALT value in the config.inc.php file.'; 172 $this->notInstalledMessage[] = 'You have not yet filled in the SALT value in the config.inc.php file.';
174 $msg = FALSE; 173 $msg = FALSE;
@@ -194,7 +193,7 @@ class Poche
194 193
195 return true; 194 return true;
196 } 195 }
197 196
198 public function getNotInstalledMessage() { 197 public function getNotInstalledMessage() {
199 return $this->notInstalledMessage; 198 return $this->notInstalledMessage;
200 } 199 }
@@ -203,7 +202,7 @@ class Poche
203 { 202 {
204 $loaderChain = new Twig_Loader_Chain(); 203 $loaderChain = new Twig_Loader_Chain();
205 $theme = $this->getTheme(); 204 $theme = $this->getTheme();
206 205
207 # add the current theme as first to the loader chain so Twig will look there first for overridden template files 206 # add the current theme as first to the loader chain so Twig will look there first for overridden template files
208 try { 207 try {
209 $loaderChain->addLoader(new Twig_Loader_Filesystem(THEME . '/' . $theme)); 208 $loaderChain->addLoader(new Twig_Loader_Filesystem(THEME . '/' . $theme));
@@ -211,7 +210,7 @@ class Poche
211 # @todo isInstalled() should catch this, inject Twig later 210 # @todo isInstalled() should catch this, inject Twig later
212 die('The currently selected theme (' . $theme . ') does not seem to be properly installed (' . THEME . '/' . $theme .' is missing)'); 211 die('The currently selected theme (' . $theme . ') does not seem to be properly installed (' . THEME . '/' . $theme .' is missing)');
213 } 212 }
214 213
215 # add all required themes to the loader chain 214 # add all required themes to the loader chain
216 $themeInfo = $this->getThemeInfo($theme); 215 $themeInfo = $this->getThemeInfo($theme);
217 if (isset($themeInfo['requirements']) && is_array($themeInfo['requirements'])) { 216 if (isset($themeInfo['requirements']) && is_array($themeInfo['requirements'])) {
@@ -224,16 +223,16 @@ class Poche
224 } 223 }
225 } 224 }
226 } 225 }
227 226
228 if (DEBUG_POCHE) { 227 if (DEBUG_POCHE) {
229 $twigParams = array(); 228 $twigParams = array();
230 } else { 229 } else {
231 $twigParams = array('cache' => CACHE); 230 $twigParams = array('cache' => CACHE);
232 } 231 }
233 232
234 $this->tpl = new Twig_Environment($loaderChain, $twigParams); 233 $this->tpl = new Twig_Environment($loaderChain, $twigParams);
235 $this->tpl->addExtension(new Twig_Extensions_Extension_I18n()); 234 $this->tpl->addExtension(new Twig_Extensions_Extension_I18n());
236 235
237 # filter to display domain name of an url 236 # filter to display domain name of an url
238 $filter = new Twig_SimpleFilter('getDomain', 'Tools::getDomain'); 237 $filter = new Twig_SimpleFilter('getDomain', 'Tools::getDomain');
239 $this->tpl->addFilter($filter); 238 $this->tpl->addFilter($filter);
@@ -243,6 +242,58 @@ class Poche
243 $this->tpl->addFilter($filter); 242 $this->tpl->addFilter($filter);
244 } 243 }
245 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 }
296
246 private function install() 297 private function install()
247 { 298 {
248 Tools::logm('poche still not installed'); 299 Tools::logm('poche still not installed');
@@ -252,7 +303,7 @@ class Poche
252 'poche_url' => Tools::getPocheUrl() 303 'poche_url' => Tools::getPocheUrl()
253 )); 304 ));
254 if (isset($_GET['install'])) { 305 if (isset($_GET['install'])) {
255 if (($_POST['password'] == $_POST['password_repeat']) 306 if (($_POST['password'] == $_POST['password_repeat'])
256 && $_POST['password'] != "" && $_POST['login'] != "") { 307 && $_POST['password'] != "" && $_POST['login'] != "") {
257 # let's rock, install poche baby ! 308 # let's rock, install poche baby !
258 if ($this->store->install($_POST['login'], Tools::encodeString($_POST['password'] . $_POST['login']))) 309 if ($this->store->install($_POST['login'], Tools::encodeString($_POST['password'] . $_POST['login'])))
@@ -269,7 +320,7 @@ class Poche
269 } 320 }
270 exit(); 321 exit();
271 } 322 }
272 323
273 public function getTheme() { 324 public function getTheme() {
274 return $this->currentTheme; 325 return $this->currentTheme;
275 } 326 }
@@ -294,7 +345,7 @@ class Poche
294 if (is_file($themeIniFile) && is_readable($themeIniFile)) { 345 if (is_file($themeIniFile) && is_readable($themeIniFile)) {
295 $themeInfo = parse_ini_file($themeIniFile); 346 $themeInfo = parse_ini_file($themeIniFile);
296 } 347 }
297 348
298 if ($themeInfo === false) { 349 if ($themeInfo === false) {
299 $themeInfo = array(); 350 $themeInfo = array();
300 } 351 }
@@ -305,7 +356,7 @@ class Poche
305 356
306 return $themeInfo; 357 return $themeInfo;
307 } 358 }
308 359
309 public function getInstalledThemes() { 360 public function getInstalledThemes() {
310 $handle = opendir(THEME); 361 $handle = opendir(THEME);
311 $themes = array(); 362 $themes = array();
@@ -332,28 +383,28 @@ class Poche
332 public function getInstalledLanguages() { 383 public function getInstalledLanguages() {
333 $handle = opendir(LOCALE); 384 $handle = opendir(LOCALE);
334 $languages = array(); 385 $languages = array();
335 386
336 while (($language = readdir($handle)) !== false) { 387 while (($language = readdir($handle)) !== false) {
337 # Languages are stored in a directory, so all directory names are languages 388 # Languages are stored in a directory, so all directory names are languages
338 # @todo move language installation data to database 389 # @todo move language installation data to database
339 if (! is_dir(LOCALE . '/' . $language) || in_array($language, array('..', '.'))) { 390 if (! is_dir(LOCALE . '/' . $language) || in_array($language, array('..', '.', 'tools'))) {
340 continue; 391 continue;
341 } 392 }
342 393
343 $current = false; 394 $current = false;
344 395
345 if ($language === $this->getLanguage()) { 396 if ($language === $this->getLanguage()) {
346 $current = true; 397 $current = true;
347 } 398 }
348 399
349 $languages[] = array('name' => $this->language_names[$language], 'value' => $language, 'current' => $current); 400 $languages[] = array('name' => (isset($this->language_names[$language]) ? $this->language_names[$language] : $language), 'value' => $language, 'current' => $current);
350 } 401 }
351 402
352 return $languages; 403 return $languages;
353 } 404 }
354 405
355 public function getDefaultConfig() 406 public function getDefaultConfig()
356 { 407 {
357 return array( 408 return array(
358 'pager' => PAGINATION, 409 'pager' => PAGINATION,
359 'language' => LANG, 410 'language' => LANG,
@@ -361,60 +412,6 @@ class Poche
361 ); 412 );
362 } 413 }
363 414
364 protected function getPageContent(Url $url)
365 {
366 // Saving and clearing context
367 $REAL = array();
368 foreach( $GLOBALS as $key => $value ) {
369 if( $key != "GLOBALS" && $key != "_SESSION" ) {
370 $GLOBALS[$key] = array();
371 $REAL[$key] = $value;
372 }
373 }
374 // Saving and clearing session
375 $REAL_SESSION = array();
376 foreach( $_SESSION as $key => $value ) {
377 $REAL_SESSION[$key] = $value;
378 unset($_SESSION[$key]);
379 }
380
381 // Running code in different context
382 $scope = function() {
383 extract( func_get_arg(1) );
384 $_GET = $_REQUEST = array(
385 "url" => $url->getUrl(),
386 "max" => 5,
387 "links" => "preserve",
388 "exc" => "",
389 "format" => "json",
390 "submit" => "Create Feed"
391 );
392 ob_start();
393 require func_get_arg(0);
394 $json = ob_get_flush();
395 return $json;
396 };
397 $json = $scope( "inc/3rdparty/makefulltextfeed.php", array("url" => $url) );
398
399 // Clearing and restoring context
400 foreach( $GLOBALS as $key => $value ) {
401 if( $key != "GLOBALS" && $key != "_SESSION" ) {
402 unset($GLOBALS[$key]);
403 }
404 }
405 foreach( $REAL as $key => $value ) {
406 $GLOBALS[$key] = $value;
407 }
408 // Clearing and restoring session
409 foreach( $_SESSION as $key => $value ) {
410 unset($_SESSION[$key]);
411 }
412 foreach( $REAL_SESSION as $key => $value ) {
413 $_SESSION[$key] = $value;
414 }
415 return json_decode($json, true);
416 }
417
418 /** 415 /**
419 * Call action (mark as fav, archive, delete, etc.) 416 * Call action (mark as fav, archive, delete, etc.)
420 */ 417 */
@@ -423,28 +420,22 @@ class Poche
423 switch ($action) 420 switch ($action)
424 { 421 {
425 case 'add': 422 case 'add':
426 $content = $this->getPageContent($url); 423 $content = Tools::getPageContent($url);
427 $title = ($content['rss']['channel']['item']['title'] != '') ? $content['rss']['channel']['item']['title'] : _('Untitled'); 424 $title = ($content['rss']['channel']['item']['title'] != '') ? $content['rss']['channel']['item']['title'] : _('Untitled');
428 $body = $content['rss']['channel']['item']['description']; 425 $body = $content['rss']['channel']['item']['description'];
429 426
430 // clean content from prevent xss attack 427 // clean content from prevent xss attack
431 $config = HTMLPurifier_Config::createDefault(); 428 $purifier = $this->getPurifier();
432 $purifier = new HTMLPurifier($config);
433 $title = $purifier->purify($title); 429 $title = $purifier->purify($title);
434 $body = $purifier->purify($body); 430 $body = $purifier->purify($body);
435 431
436 //search for possible duplicate if not in import mode 432 //search for possible duplicate
437 if (!$import) { 433 $duplicate = NULL;
438 $duplicate = $this->store->retrieveOneByURL($url->getUrl(), $this->user->getId()); 434 $duplicate = $this->store->retrieveOneByURL($url->getUrl(), $this->user->getId());
439 }
440 435
441 if ($this->store->add($url->getUrl(), $title, $body, $this->user->getId())) { 436 $last_id = $this->store->add($url->getUrl(), $title, $body, $this->user->getId());
437 if ( $last_id ) {
442 Tools::logm('add link ' . $url->getUrl()); 438 Tools::logm('add link ' . $url->getUrl());
443 $sequence = '';
444 if (STORAGE == 'postgres') {
445 $sequence = 'entries_id_seq';
446 }
447 $last_id = $this->store->getLastId($sequence);
448 if (DOWNLOAD_PICTURES) { 439 if (DOWNLOAD_PICTURES) {
449 $content = filtre_picture($body, $url->getUrl(), $last_id); 440 $content = filtre_picture($body, $url->getUrl(), $last_id);
450 Tools::logm('updating content article'); 441 Tools::logm('updating content article');
@@ -464,23 +455,17 @@ class Poche
464 } 455 }
465 } 456 }
466 457
467 if (!$import) { 458 $this->messages->add('s', _('the link has been added successfully'));
468 $this->messages->add('s', _('the link has been added successfully'));
469 }
470 } 459 }
471 else { 460 else {
472 if (!$import) { 461 $this->messages->add('e', _('error during insertion : the link wasn\'t added'));
473 $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());
474 Tools::logm('error during insertion : the link wasn\'t added ' . $url->getUrl());
475 }
476 } 463 }
477 464
478 if (!$import) { 465 if ($autoclose == TRUE) {
479 if ($autoclose == TRUE) { 466 Tools::redirect('?view=home');
480 Tools::redirect('?view=home'); 467 } else {
481 } else { 468 Tools::redirect('?view=home&closewin=true');
482 Tools::redirect('?view=home&closewin=true');
483 }
484 } 469 }
485 break; 470 break;
486 case 'delete': 471 case 'delete':
@@ -501,62 +486,81 @@ class Poche
501 case 'toggle_fav' : 486 case 'toggle_fav' :
502 $this->store->favoriteById($id, $this->user->getId()); 487 $this->store->favoriteById($id, $this->user->getId());
503 Tools::logm('mark as favorite link #' . $id); 488 Tools::logm('mark as favorite link #' . $id);
504 if (!$import) { 489 if ( Tools::isAjaxRequest() ) {
505 Tools::redirect(); 490 echo 1;
491 exit;
492 }
493 else {
494 Tools::redirect();
506 } 495 }
507 break; 496 break;
508 case 'toggle_archive' : 497 case 'toggle_archive' :
509 $this->store->archiveById($id, $this->user->getId()); 498 $this->store->archiveById($id, $this->user->getId());
510 Tools::logm('archive link #' . $id); 499 Tools::logm('archive link #' . $id);
511 if (!$import) { 500 if ( Tools::isAjaxRequest() ) {
512 Tools::redirect(); 501 echo 1;
502 exit;
503 }
504 else {
505 Tools::redirect();
513 } 506 }
514 break; 507 break;
515 case 'archive_all' : 508 case 'archive_all' :
516 $this->store->archiveAll($this->user->getId()); 509 $this->store->archiveAll($this->user->getId());
517 Tools::logm('archive all links'); 510 Tools::logm('archive all links');
518 if (!$import) { 511 Tools::redirect();
519 Tools::redirect();
520 }
521 break; 512 break;
522 case 'add_tag' : 513 case 'add_tag' :
523 if($import){ 514 if (isset($_GET['search'])) {
524 $entry_id = $id; 515 //when we want to apply a tag to a search
525 $tags = explode(',', $tags); 516 $tags = array($_GET['search']);
526 } 517 $allentry_ids = $this->store->search($tags[0], $this->user->getId());
527 else{ 518 $entry_ids = array();
519 foreach ($allentry_ids as $eachentry) {
520 $entry_ids[] = $eachentry[0];
521 }
522 } else { //add a tag to a single article
528 $tags = explode(',', $_POST['value']); 523 $tags = explode(',', $_POST['value']);
529 $entry_id = $_POST['entry_id']; 524 $entry_ids = array($_POST['entry_id']);
530 } 525 }
531 $entry = $this->store->retrieveOneById($entry_id, $this->user->getId()); 526 foreach($entry_ids as $entry_id) {
532 if (!$entry) { 527 $entry = $this->store->retrieveOneById($entry_id, $this->user->getId());
533 $this->messages->add('e', _('Article not found!')); 528 if (!$entry) {
534 Tools::logm('error : article not found'); 529 $this->messages->add('e', _('Article not found!'));
535 Tools::redirect(); 530 Tools::logm('error : article not found');
536 } 531 Tools::redirect();
537 foreach($tags as $key => $tag_value) {
538 $value = trim($tag_value);
539 $tag = $this->store->retrieveTagByValue($value);
540
541 if (is_null($tag)) {
542 # we create the tag
543 $tag = $this->store->createTag($value);
544 $sequence = '';
545 if (STORAGE == 'postgres') {
546 $sequence = 'tags_id_seq';
547 }
548 $tag_id = $this->store->getLastId($sequence);
549 } 532 }
550 else { 533 //get all already set tags to preven duplicates
551 $tag_id = $tag['id']; 534 $already_set_tags = array();
535 $entry_tags = $this->store->retrieveTagsByEntry($entry_id);
536 foreach ($entry_tags as $tag) {
537 $already_set_tags[] = $tag['value'];
538 }
539 foreach($tags as $key => $tag_value) {
540 $value = trim($tag_value);
541 if ($value && !in_array($value, $already_set_tags)) {
542 $tag = $this->store->retrieveTagByValue($value);
543 if (is_null($tag)) {
544 # we create the tag
545 $tag = $this->store->createTag($value);
546 $sequence = '';
547 if (STORAGE == 'postgres') {
548 $sequence = 'tags_id_seq';
549 }
550 $tag_id = $this->store->getLastId($sequence);
551 }
552 else {
553 $tag_id = $tag['id'];
554 }
555
556 # we assign the tag to the article
557 $this->store->setTagToEntry($tag_id, $entry_id);
558 }
552 } 559 }
553
554 # we assign the tag to the article
555 $this->store->setTagToEntry($tag_id, $entry_id);
556 }
557 if(!$import) {
558 Tools::redirect();
559 } 560 }
561 $this->messages->add('s', _('The tag has been applied successfully'));
562 Tools::logm('The tag has been applied successfully');
563 Tools::redirect();
560 break; 564 break;
561 case 'remove_tag' : 565 case 'remove_tag' :
562 $tag_id = $_GET['tag_id']; 566 $tag_id = $_GET['tag_id'];
@@ -567,6 +571,11 @@ class Poche
567 Tools::redirect(); 571 Tools::redirect();
568 } 572 }
569 $this->store->removeTagForEntry($id, $tag_id); 573 $this->store->removeTagForEntry($id, $tag_id);
574 Tools::logm('tag entry deleted');
575 if ($this->store->cleanUnusedTag($tag_id)) {
576 Tools::logm('tag deleted');
577 }
578 $this->messages->add('s', _('The tag has been successfully deleted'));
570 Tools::redirect(); 579 Tools::redirect();
571 break; 580 break;
572 default: 581 default:
@@ -581,24 +590,32 @@ class Poche
581 switch ($view) 590 switch ($view)
582 { 591 {
583 case 'config': 592 case 'config':
584 $dev = trim($this->getPocheVersion('dev')); 593 $dev_infos = $this->getPocheVersion('dev');
585 $prod = trim($this->getPocheVersion('prod')); 594 $dev = trim($dev_infos[0]);
595 $check_time_dev = date('d-M-Y H:i', $dev_infos[1]);
596 $prod_infos = $this->getPocheVersion('prod');
597 $prod = trim($prod_infos[0]);
598 $check_time_prod = date('d-M-Y H:i', $prod_infos[1]);
586 $compare_dev = version_compare(POCHE, $dev); 599 $compare_dev = version_compare(POCHE, $dev);
587 $compare_prod = version_compare(POCHE, $prod); 600 $compare_prod = version_compare(POCHE, $prod);
588 $themes = $this->getInstalledThemes(); 601 $themes = $this->getInstalledThemes();
589 $languages = $this->getInstalledLanguages(); 602 $languages = $this->getInstalledLanguages();
590 $token = $this->user->getConfigValue('token'); 603 $token = $this->user->getConfigValue('token');
591 $http_auth = (isset($_SERVER['PHP_AUTH_USER']) || isset($_SERVER['REMOTE_USER'])) ? true : false; 604 $http_auth = (isset($_SERVER['PHP_AUTH_USER']) || isset($_SERVER['REMOTE_USER'])) ? true : false;
605 $only_user = ($this->store->listUsers() > 1) ? false : true;
592 $tpl_vars = array( 606 $tpl_vars = array(
593 'themes' => $themes, 607 'themes' => $themes,
594 'languages' => $languages, 608 'languages' => $languages,
595 'dev' => $dev, 609 'dev' => $dev,
596 'prod' => $prod, 610 'prod' => $prod,
611 'check_time_dev' => $check_time_dev,
612 'check_time_prod' => $check_time_prod,
597 'compare_dev' => $compare_dev, 613 'compare_dev' => $compare_dev,
598 'compare_prod' => $compare_prod, 614 'compare_prod' => $compare_prod,
599 'token' => $token, 615 'token' => $token,
600 'user_id' => $this->user->getId(), 616 'user_id' => $this->user->getId(),
601 'http_auth' => $http_auth, 617 'http_auth' => $http_auth,
618 'only_user' => $only_user
602 ); 619 );
603 Tools::logm('config view'); 620 Tools::logm('config view');
604 break; 621 break;
@@ -619,13 +636,36 @@ class Poche
619 break; 636 break;
620 case 'tags': 637 case 'tags':
621 $token = $this->user->getConfigValue('token'); 638 $token = $this->user->getConfigValue('token');
622 $tags = $this->store->retrieveAllTags($this->user->getId()); 639 //if term is set - search tags for this term
640 $term = Tools::checkVar('term');
641 $tags = $this->store->retrieveAllTags($this->user->getId(), $term);
642 if (Tools::isAjaxRequest()) {
643 $result = array();
644 foreach ($tags as $tag) {
645 $result[] = $tag['value'];
646 }
647 echo json_encode($result);
648 exit;
649 }
623 $tpl_vars = array( 650 $tpl_vars = array(
624 'token' => $token, 651 'token' => $token,
625 'user_id' => $this->user->getId(), 652 'user_id' => $this->user->getId(),
626 'tags' => $tags, 653 'tags' => $tags,
627 ); 654 );
628 break; 655 break;
656 case 'search':
657 if (isset($_GET['search'])) {
658 $search = filter_var($_GET['search'], FILTER_SANITIZE_STRING);
659 $tpl_vars['entries'] = $this->store->search($search, $this->user->getId());
660 $count = count($tpl_vars['entries']);
661 $this->pagination->set_total($count);
662 $page_links = str_replace(array('previous', 'next'), array(_('previous'), _('next')),
663 $this->pagination->page_links('?view=' . $view . '?search=' . $search . '&sort=' . $_SESSION['sort'] . '&' ));
664 $tpl_vars['page_links'] = $page_links;
665 $tpl_vars['nb_results'] = $count;
666 $tpl_vars['search_term'] = $search;
667 }
668 break;
629 case 'view': 669 case 'view':
630 $entry = $this->store->retrieveOneById($id, $this->user->getId()); 670 $entry = $this->store->retrieveOneById($id, $this->user->getId());
631 if ($entry != NULL) { 671 if ($entry != NULL) {
@@ -660,8 +700,9 @@ class Poche
660 'entries' => '', 700 'entries' => '',
661 'page_links' => '', 701 'page_links' => '',
662 'nb_results' => '', 702 'nb_results' => '',
703 'listmode' => (isset($_COOKIE['listmode']) ? true : false),
663 ); 704 );
664 705
665 //if id is given - we retrive entries by tag: id is tag id 706 //if id is given - we retrive entries by tag: id is tag id
666 if ($id) { 707 if ($id) {
667 $tpl_vars['tag'] = $this->store->retrieveTag($id, $this->user->getId()); 708 $tpl_vars['tag'] = $this->store->retrieveTag($id, $this->user->getId());
@@ -686,8 +727,8 @@ class Poche
686 } 727 }
687 728
688 /** 729 /**
689 * update the password of the current user. 730 * update the password of the current user.
690 * if MODE_DEMO is TRUE, the password can't be updated. 731 * if MODE_DEMO is TRUE, the password can't be updated.
691 * @todo add the return value 732 * @todo add the return value
692 * @todo set the new password in function header like this updatePassword($newPassword) 733 * @todo set the new password in function header like this updatePassword($newPassword)
693 * @return boolean 734 * @return boolean
@@ -715,42 +756,44 @@ class Poche
715 } 756 }
716 } 757 }
717 } 758 }
718 759
719 public function updateTheme() 760 public function updateTheme()
720 { 761 {
721 # no data 762 # no data
722 if (empty($_POST['theme'])) { 763 if (empty($_POST['theme'])) {
723 } 764 }
724 765
725 # we are not going to change it to the current theme... 766 # we are not going to change it to the current theme...
726 if ($_POST['theme'] == $this->getTheme()) { 767 if ($_POST['theme'] == $this->getTheme()) {
727 $this->messages->add('w', _('still using the "' . $this->getTheme() . '" theme!')); 768 $this->messages->add('w', _('still using the "' . $this->getTheme() . '" theme!'));
728 Tools::redirect('?view=config'); 769 Tools::redirect('?view=config');
729 } 770 }
730 771
731 $themes = $this->getInstalledThemes(); 772 $themes = $this->getInstalledThemes();
732 $actualTheme = false; 773 $actualTheme = false;
733 774
734 foreach (array_keys($themes) as $theme) { 775 foreach (array_keys($themes) as $theme) {
735 if ($theme == $_POST['theme']) { 776 if ($theme == $_POST['theme']) {
736 $actualTheme = true; 777 $actualTheme = true;
737 break; 778 break;
738 } 779 }
739 } 780 }
740 781
741 if (! $actualTheme) { 782 if (! $actualTheme) {
742 $this->messages->add('e', _('that theme does not seem to be installed')); 783 $this->messages->add('e', _('that theme does not seem to be installed'));
743 Tools::redirect('?view=config'); 784 Tools::redirect('?view=config');
744 } 785 }
745 786
746 $this->store->updateUserConfig($this->user->getId(), 'theme', $_POST['theme']); 787 $this->store->updateUserConfig($this->user->getId(), 'theme', $_POST['theme']);
747 $this->messages->add('s', _('you have changed your theme preferences')); 788 $this->messages->add('s', _('you have changed your theme preferences'));
748 789
749 $currentConfig = $_SESSION['poche_user']->config; 790 $currentConfig = $_SESSION['poche_user']->config;
750 $currentConfig['theme'] = $_POST['theme']; 791 $currentConfig['theme'] = $_POST['theme'];
751 792
752 $_SESSION['poche_user']->setConfig($currentConfig); 793 $_SESSION['poche_user']->setConfig($currentConfig);
753 794
795 $this->emptyCache();
796
754 Tools::redirect('?view=config'); 797 Tools::redirect('?view=config');
755 } 798 }
756 799
@@ -759,39 +802,40 @@ class Poche
759 # no data 802 # no data
760 if (empty($_POST['language'])) { 803 if (empty($_POST['language'])) {
761 } 804 }
762 805
763 # we are not going to change it to the current language... 806 # we are not going to change it to the current language...
764 if ($_POST['language'] == $this->getLanguage()) { 807 if ($_POST['language'] == $this->getLanguage()) {
765 $this->messages->add('w', _('still using the "' . $this->getLanguage() . '" language!')); 808 $this->messages->add('w', _('still using the "' . $this->getLanguage() . '" language!'));
766 Tools::redirect('?view=config'); 809 Tools::redirect('?view=config');
767 } 810 }
768 811
769 $languages = $this->getInstalledLanguages(); 812 $languages = $this->getInstalledLanguages();
770 $actualLanguage = false; 813 $actualLanguage = false;
771 814
772 foreach ($languages as $language) { 815 foreach ($languages as $language) {
773 if ($language['value'] == $_POST['language']) { 816 if ($language['value'] == $_POST['language']) {
774 $actualLanguage = true; 817 $actualLanguage = true;
775 break; 818 break;
776 } 819 }
777 } 820 }
778 821
779 if (! $actualLanguage) { 822 if (! $actualLanguage) {
780 $this->messages->add('e', _('that language does not seem to be installed')); 823 $this->messages->add('e', _('that language does not seem to be installed'));
781 Tools::redirect('?view=config'); 824 Tools::redirect('?view=config');
782 } 825 }
783 826
784 $this->store->updateUserConfig($this->user->getId(), 'language', $_POST['language']); 827 $this->store->updateUserConfig($this->user->getId(), 'language', $_POST['language']);
785 $this->messages->add('s', _('you have changed your language preferences')); 828 $this->messages->add('s', _('you have changed your language preferences'));
786 829
787 $currentConfig = $_SESSION['poche_user']->config; 830 $currentConfig = $_SESSION['poche_user']->config;
788 $currentConfig['language'] = $_POST['language']; 831 $currentConfig['language'] = $_POST['language'];
789 832
790 $_SESSION['poche_user']->setConfig($currentConfig); 833 $_SESSION['poche_user']->setConfig($currentConfig);
791 834
835 $this->emptyCache();
836
792 Tools::redirect('?view=config'); 837 Tools::redirect('?view=config');
793 } 838 }
794
795 /** 839 /**
796 * get credentials from differents sources 840 * get credentials from differents sources
797 * it redirects the user to the $referer link 841 * it redirects the user to the $referer link
@@ -846,7 +890,7 @@ class Poche
846 /** 890 /**
847 * log out the poche user. It cleans the session. 891 * log out the poche user. It cleans the session.
848 * @todo add the return value 892 * @todo add the return value
849 * @return boolean 893 * @return boolean
850 */ 894 */
851 public function logout() 895 public function logout()
852 { 896 {
@@ -857,238 +901,137 @@ class Poche
857 } 901 }
858 902
859 /** 903 /**
860 * import from Instapaper. poche needs a ./instapaper-export.html file 904 * import datas into your poche
861 * @todo add the return value
862 * @param string $targetFile the file used for importing
863 * @return boolean 905 * @return boolean
864 */ 906 */
865 private function importFromInstapaper($targetFile) 907 public function import() {
866 { 908
867 # TODO gestion des articles favs 909 if ( isset($_FILES['file']) ) {
868 $html = new simple_html_dom(); 910 Tools::logm('Import stated: parsing file');
869 $html->load_file($targetFile); 911
870 Tools::logm('starting import from instapaper'); 912 // assume, that file is in json format
871 913 $str_data = file_get_contents($_FILES['file']['tmp_name']);
872 $read = 0; 914 $data = json_decode($str_data, true);
873 $errors = array(); 915
874 foreach($html->find('ol') as $ul) 916 if ( $data === null ) {
875 { 917 //not json - assume html
876 foreach($ul->find('li') as $li) 918 $html = new simple_html_dom();
877 { 919 $html->load_file($_FILES['file']['tmp_name']);
878 $a = $li->find('a'); 920 $data = array();
879 $url = new Url(base64_encode($a[0]->href)); 921 $read = 0;
880 $this->action('add', $url, 0, TRUE); 922 foreach (array('ol','ul') as $list) {
881 if ($read == '1') { 923 foreach ($html->find($list) as $ul) {
882 $sequence = ''; 924 foreach ($ul->find('li') as $li) {
883 if (STORAGE == 'postgres') { 925 $tmpEntry = array();
884 $sequence = 'entries_id_seq'; 926 $a = $li->find('a');
885 } 927 $tmpEntry['url'] = $a[0]->href;
886 $last_id = $this->store->getLastId($sequence); 928 $tmpEntry['tags'] = $a[0]->tags;
887 $this->action('toggle_archive', $url, $last_id, TRUE); 929 $tmpEntry['is_read'] = $read;
888 } 930 if ($tmpEntry['url']) {
931 $data[] = $tmpEntry;
932 }
933 }
934 # the second <ol/ul> is for read links
935 $read = ((sizeof($data) && $read)?0:1);
889 } 936 }
890 937 }
891 # the second <ol> is for read links
892 $read = 1;
893 } 938 }
894 $this->messages->add('s', _('import from instapaper completed'));
895 Tools::logm('import from instapaper completed');
896 Tools::redirect();
897 }
898 939
899 /** 940 //for readability structure
900 * import from Pocket. poche needs a ./ril_export.html file 941 foreach ($data as $record) {
901 * @todo add the return value 942 if (is_array($record)) {
902 * @param string $targetFile the file used for importing 943 $data[] = $record;
903 * @return boolean 944 foreach ($record as $record2) {
904 */ 945 if (is_array($record2)) {
905 private function importFromPocket($targetFile) 946 $data[] = $record2;
906 { 947 }
907 # TODO gestion des articles favs 948 }
908 $html = new simple_html_dom(); 949 }
909 $html->load_file($targetFile); 950 }
910 Tools::logm('starting import from pocket'); 951
911 952 $urlsInserted = array(); //urls of articles inserted
912 $read = 0; 953 foreach ($data as $record) {
913 $errors = array(); 954 $url = trim( isset($record['article__url']) ? $record['article__url'] : (isset($record['url']) ? $record['url'] : '') );
914 foreach($html->find('ul') as $ul) 955 if ( $url and !in_array($url, $urlsInserted) ) {
915 { 956 $title = (isset($record['title']) ? $record['title'] : _('Untitled - Import - ').'</a> <a href="./?import">'._('click to finish import').'</a><a>');
916 foreach($ul->find('li') as $li) 957 $body = (isset($record['content']) ? $record['content'] : '');
917 { 958 $isRead = (isset($record['is_read']) ? intval($record['is_read']) : (isset($record['archive'])?intval($record['archive']):0));
918 $a = $li->find('a'); 959 $isFavorite = (isset($record['is_fav']) ? intval($record['is_fav']) : (isset($record['favorite'])?intval($record['favorite']):0) );
919 $url = new Url(base64_encode($a[0]->href)); 960 //insert new record
920 $this->action('add', $url, 0, TRUE); 961 $id = $this->store->add($url, $title, $body, $this->user->getId(), $isFavorite, $isRead);
921 $sequence = ''; 962 if ( $id ) {
922 if (STORAGE == 'postgres') { 963 $urlsInserted[] = $url; //add
923 $sequence = 'entries_id_seq'; 964
924 } 965 if ( isset($record['tags']) && trim($record['tags']) ) {
925 $last_id = $this->store->getLastId($sequence); 966 //@TODO: set tags
926 if ($read == '1') { 967
927 $this->action('toggle_archive', $url, $last_id, TRUE); 968 }
928 }
929 $tags = $a[0]->tags;
930 if(!empty($tags)) {
931 $this->action('add_tag',$url,$last_id,true,false,$tags);
932 }
933 } 969 }
934 970 }
935 # the second <ul> is for read links
936 $read = 1;
937 } 971 }
938 $this->messages->add('s', _('import from pocket completed'));
939 Tools::logm('import from pocket completed');
940 Tools::redirect();
941 }
942
943 /**
944 * import from Readability. poche needs a ./readability file
945 * @todo add the return value
946 * @param string $targetFile the file used for importing
947 * @return boolean
948 */
949 private function importFromReadability($targetFile)
950 {
951 # TODO gestion des articles lus / favs
952 $str_data = file_get_contents($targetFile);
953 $data = json_decode($str_data,true);
954 Tools::logm('starting import from Readability');
955 $count = 0;
956 foreach ($data as $key => $value) {
957 $url = NULL;
958 $favorite = FALSE;
959 $archive = FALSE;
960 foreach ($value as $item) {
961 foreach ($item as $attr => $value) {
962 if ($attr == 'article__url') {
963 $url = new Url(base64_encode($value));
964 }
965 $sequence = '';
966 if (STORAGE == 'postgres') {
967 $sequence = 'entries_id_seq';
968 }
969 if ($value == 'true') {
970 if ($attr == 'favorite') {
971 $favorite = TRUE;
972 }
973 if ($attr == 'archive') {
974 $archive = TRUE;
975 }
976 }
977 }
978 972
979 # we can add the url 973 $i = sizeof($urlsInserted);
980 if (!is_null($url) && $url->isCorrect()) { 974 if ( $i > 0 ) {
981 $this->action('add', $url, 0, TRUE); 975 $this->messages->add('s', _('Articles inserted: ').$i._('. Please note, that some may be marked as "read".'));
982 $count++;
983 if ($favorite) {
984 $last_id = $this->store->getLastId($sequence);
985 $this->action('toggle_fav', $url, $last_id, TRUE);
986 }
987 if ($archive) {
988 $last_id = $this->store->getLastId($sequence);
989 $this->action('toggle_archive', $url, $last_id, TRUE);
990 }
991 }
992 }
993 } 976 }
994 $this->messages->add('s', _('import from Readability completed. ' . $count . ' new links.')); 977 Tools::logm('Import of articles finished: '.$i.' articles added (w/o content if not provided).');
995 Tools::logm('import from Readability completed'); 978 }
979 //file parsing finished here
980
981 //now download article contents if any
982
983 //check if we need to download any content
984 $recordsDownloadRequired = $this->store->retrieveUnfetchedEntriesCount($this->user->getId());
985 if ( $recordsDownloadRequired == 0 ) {
986 //nothing to download
987 $this->messages->add('s', _('Import finished.'));
988 Tools::logm('Import finished completely');
996 Tools::redirect(); 989 Tools::redirect();
997 } 990 }
991 else {
992 //if just inserted - don't download anything, download will start in next reload
993 if ( !isset($_FILES['file']) ) {
994 //download next batch
995 Tools::logm('Fetching next batch of articles...');
996 $items = $this->store->retrieveUnfetchedEntries($this->user->getId(), IMPORT_LIMIT);
998 997
999 /** 998 $purifier = $this->getPurifier();
1000 * import from Poche exported file
1001 * @param string $targetFile the file used for importing
1002 * @return boolean
1003 */
1004 private function importFromPoche($targetFile)
1005 {
1006 $str_data = file_get_contents($targetFile);
1007 $data = json_decode($str_data,true);
1008 Tools::logm('starting import from Poche');
1009 999
1000 foreach ($items as $item) {
1001 $url = new Url(base64_encode($item['url']));
1002 Tools::logm('Fetching article '.$item['id']);
1003 $content = Tools::getPageContent($url);
1010 1004
1011 $sequence = ''; 1005 $title = (($content['rss']['channel']['item']['title'] != '') ? $content['rss']['channel']['item']['title'] : _('Untitled'));
1012 if (STORAGE == 'postgres') { 1006 $body = (($content['rss']['channel']['item']['description'] != '') ? $content['rss']['channel']['item']['description'] : _('Undefined'));
1013 $sequence = 'entries_id_seq';
1014 }
1015 1007
1016 $count = 0; 1008 //clean content to prevent xss attack
1017 foreach ($data as $value) { 1009 $title = $purifier->purify($title);
1018 1010 $body = $purifier->purify($body);
1019 $url = new Url(base64_encode($value['url'])); 1011
1020 $favorite = ($value['is_fav'] == -1); 1012 $this->store->updateContentAndTitle($item['id'], $title, $body, $this->user->getId());
1021 $archive = ($value['is_read'] == -1); 1013 Tools::logm('Article '.$item['id'].' updated.');
1022 1014 }
1023 # we can add the url
1024 if (!is_null($url) && $url->isCorrect()) {
1025
1026 $this->action('add', $url, 0, TRUE);
1027
1028 $count++;
1029 if ($favorite) {
1030 $last_id = $this->store->getLastId($sequence);
1031 $this->action('toggle_fav', $url, $last_id, TRUE);
1032 }
1033 if ($archive) {
1034 $last_id = $this->store->getLastId($sequence);
1035 $this->action('toggle_archive', $url, $last_id, TRUE);
1036 }
1037 }
1038
1039 }
1040 $this->messages->add('s', _('import from Poche completed. ' . $count . ' new links.'));
1041 Tools::logm('import from Poche completed');
1042 Tools::redirect();
1043 }
1044 1015
1045 /**
1046 * import datas into your poche
1047 * @param string $from name of the service to import : pocket, instapaper or readability
1048 * @todo add the return value
1049 * @return boolean
1050 */
1051 public function import($from)
1052 {
1053 $providers = array(
1054 'pocket' => 'importFromPocket',
1055 'readability' => 'importFromReadability',
1056 'instapaper' => 'importFromInstapaper',
1057 'poche' => 'importFromPoche',
1058 );
1059
1060 if (! isset($providers[$from])) {
1061 $this->messages->add('e', _('Unknown import provider.'));
1062 Tools::redirect();
1063 }
1064
1065 $targetDefinition = 'IMPORT_' . strtoupper($from) . '_FILE';
1066 $targetFile = constant($targetDefinition);
1067
1068 if (! defined($targetDefinition)) {
1069 $this->messages->add('e', _('Incomplete inc/poche/define.inc.php file, please define "' . $targetDefinition . '".'));
1070 Tools::redirect();
1071 }
1072
1073 if (! file_exists($targetFile)) {
1074 $this->messages->add('e', _('Could not find required "' . $targetFile . '" import file.'));
1075 Tools::redirect();
1076 } 1016 }
1077 1017 }
1078 $this->$providers[$from]($targetFile); 1018
1019 return array('includeImport'=>true, 'import'=>array('recordsDownloadRequired'=>$recordsDownloadRequired, 'recordsUnderDownload'=> IMPORT_LIMIT, 'delay'=> IMPORT_DELAY * 1000) );
1079 } 1020 }
1080 1021
1081 /** 1022 /**
1082 * export poche entries in json 1023 * export poche entries in json
1083 * @return json all poche entries 1024 * @return json all poche entries
1084 */ 1025 */
1085 public function export() 1026 public function export() {
1086 { 1027 $filename = "wallabag-export-".$this->user->getId()."-".date("Y-m-d").".json";
1087 $entries = $this->store->retrieveAll($this->user->getId()); 1028 header('Content-Disposition: attachment; filename='.$filename);
1088 echo $this->tpl->render('export.twig', array( 1029
1089 'export' => Tools::renderJson($entries), 1030 $entries = $this->store->retrieveAll($this->user->getId());
1090 )); 1031 echo $this->tpl->render('export.twig', array(
1091 Tools::logm('export view'); 1032 'export' => Tools::renderJson($entries),
1033 ));
1034 Tools::logm('export view');
1092 } 1035 }
1093 1036
1094 /** 1037 /**
@@ -1096,35 +1039,42 @@ class Poche
1096 * @param string $which 'prod' or 'dev' 1039 * @param string $which 'prod' or 'dev'
1097 * @return string latest $which version 1040 * @return string latest $which version
1098 */ 1041 */
1099 private function getPocheVersion($which = 'prod') 1042 private function getPocheVersion($which = 'prod') {
1100 { 1043 $cache_file = CACHE . '/' . $which;
1101 $cache_file = CACHE . '/' . $which; 1044 $check_time = time();
1102 1045
1103 # checks if the cached version file exists 1046 # checks if the cached version file exists
1104 if (file_exists($cache_file) && (filemtime($cache_file) > (time() - 86400 ))) { 1047 if (file_exists($cache_file) && (filemtime($cache_file) > (time() - 86400 ))) {
1105 $version = file_get_contents($cache_file); 1048 $version = file_get_contents($cache_file);
1106 } else { 1049 $check_time = filemtime($cache_file);
1107 $version = file_get_contents('http://static.wallabag.org/versions/' . $which); 1050 } else {
1108 file_put_contents($cache_file, $version, LOCK_EX); 1051 $version = file_get_contents('http://static.wallabag.org/versions/' . $which);
1109 } 1052 file_put_contents($cache_file, $version, LOCK_EX);
1110 return $version; 1053 }
1054 return array($version, $check_time);
1111 } 1055 }
1112 1056
1113 public function generateToken() 1057 public function generateToken()
1114 { 1058 {
1115 if (ini_get('open_basedir') === '') { 1059 if (ini_get('open_basedir') === '') {
1116 $token = substr(base64_encode(file_get_contents('/dev/urandom', false, null, 0, 20)), 0, 15); 1060 if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
1117 } 1061 echo 'This is a server using Windows!';
1118 else { 1062 // alternative to /dev/urandom for Windows
1119 $token = substr(base64_encode(uniqid(mt_rand(), true)), 0, 20); 1063 $token = substr(base64_encode(uniqid(mt_rand(), true)), 0, 20);
1120 } 1064 } else {
1121 1065 $token = substr(base64_encode(file_get_contents('/dev/urandom', false, null, 0, 20)), 0, 15);
1122 $token = str_replace('+', '', $token); 1066 }
1123 $this->store->updateUserConfig($this->user->getId(), 'token', $token); 1067 }
1124 $currentConfig = $_SESSION['poche_user']->config; 1068 else {
1125 $currentConfig['token'] = $token; 1069 $token = substr(base64_encode(uniqid(mt_rand(), true)), 0, 20);
1126 $_SESSION['poche_user']->setConfig($currentConfig); 1070 }
1127 Tools::redirect(); 1071
1072 $token = str_replace('+', '', $token);
1073 $this->store->updateUserConfig($this->user->getId(), 'token', $token);
1074 $currentConfig = $_SESSION['poche_user']->config;
1075 $currentConfig['token'] = $token;
1076 $_SESSION['poche_user']->setConfig($currentConfig);
1077 Tools::redirect();
1128 } 1078 }
1129 1079
1130 public function generateFeeds($token, $user_id, $tag_id, $type = 'home') 1080 public function generateFeeds($token, $user_id, $tag_id, $type = 'home')
@@ -1132,8 +1082,11 @@ class Poche
1132 $allowed_types = array('home', 'fav', 'archive', 'tag'); 1082 $allowed_types = array('home', 'fav', 'archive', 'tag');
1133 $config = $this->store->getConfigUser($user_id); 1083 $config = $this->store->getConfigUser($user_id);
1134 1084
1135 if (!in_array($type, $allowed_types) || 1085 if ($config == null) {
1136 $token != $config['token']) { 1086 die(sprintf(_('User with this id (%d) does not exist.'), $user_id));
1087 }
1088
1089 if (!in_array($type, $allowed_types) || $token != $config['token']) {
1137 die(_('Uh, there is a problem while generating feeds.')); 1090 die(_('Uh, there is a problem while generating feeds.'));
1138 } 1091 }
1139 // Check the token 1092 // Check the token
@@ -1141,8 +1094,9 @@ class Poche
1141 $feed = new FeedWriter(RSS2); 1094 $feed = new FeedWriter(RSS2);
1142 $feed->setTitle('wallabag — ' . $type . ' feed'); 1095 $feed->setTitle('wallabag — ' . $type . ' feed');
1143 $feed->setLink(Tools::getPocheUrl()); 1096 $feed->setLink(Tools::getPocheUrl());
1144 $feed->setChannelElement('updated', date(DATE_RSS , time())); 1097 $feed->setChannelElement('pubDate', date(DATE_RSS , time()));
1145 $feed->setChannelElement('author', 'wallabag'); 1098 $feed->setChannelElement('generator', 'wallabag');
1099 $feed->setDescription('wallabag ' . $type . ' elements');
1146 1100
1147 if ($type == 'tag') { 1101 if ($type == 'tag') {
1148 $entries = $this->store->retrieveEntriesByTag($tag_id, $user_id); 1102 $entries = $this->store->retrieveEntriesByTag($tag_id, $user_id);
@@ -1155,7 +1109,8 @@ class Poche
1155 foreach ($entries as $entry) { 1109 foreach ($entries as $entry) {
1156 $newItem = $feed->createNewItem(); 1110 $newItem = $feed->createNewItem();
1157 $newItem->setTitle($entry['title']); 1111 $newItem->setTitle($entry['title']);
1158 $newItem->setLink(Tools::getPocheUrl() . '?view=view&amp;id=' . $entry['id']); 1112 $newItem->setSource(Tools::getPocheUrl() . '?view=view&amp;id=' . $entry['id']);
1113 $newItem->setLink($entry['url']);
1159 $newItem->setDate(time()); 1114 $newItem->setDate(time());
1160 $newItem->setDescription($entry['content']); 1115 $newItem->setDescription($entry['content']);
1161 $feed->addItem($newItem); 1116 $feed->addItem($newItem);
@@ -1181,4 +1136,132 @@ class Poche
1181 $this->messages->add('s', _('Cache deleted.')); 1136 $this->messages->add('s', _('Cache deleted.'));
1182 Tools::redirect(); 1137 Tools::redirect();
1183 } 1138 }
1139
1140 /**
1141 * return new purifier object with actual config
1142 */
1143 protected function getPurifier() {
1144 $config = HTMLPurifier_Config::createDefault();
1145 $config->set('Cache.SerializerPath', CACHE);
1146 $config->set('HTML.SafeIframe', true);
1147
1148 //allow YouTube, Vimeo and dailymotion videos
1149 $config->set('URI.SafeIframeRegexp', '%^(https?:)?//(www\.youtube(?:-nocookie)?\.com/embed/|player\.vimeo\.com/video/|www\.dailymotion\.com/embed/video/)%');
1150
1151 return new HTMLPurifier($config);
1152 }
1153
1154 /**
1155 * handle epub
1156 */
1157 public function createEpub() {
1158
1159 switch ($_GET['method']) {
1160 case 'id':
1161 $entryID = filter_var($_GET['id'],FILTER_SANITIZE_NUMBER_INT);
1162 $entry = $this->store->retrieveOneById($entryID, $this->user->getId());
1163 $entries = array($entry);
1164 $bookTitle = $entry['title'];
1165 $bookFileName = substr($bookTitle, 0, 200);
1166 break;
1167 case 'all':
1168 $entries = $this->store->retrieveAll($this->user->getId());
1169 $bookTitle = sprintf(_('All my articles on '), date(_('d.m.y'))); #translatable because each country has it's own date format system
1170 $bookFileName = _('Allarticles') . date(_('dmY'));
1171 break;
1172 case 'tag':
1173 $tag = filter_var($_GET['tag'],FILTER_SANITIZE_STRING);
1174 $tags_id = $this->store->retrieveAllTags($this->user->getId(),$tag);
1175 $tag_id = $tags_id[0]["id"]; // we take the first result, which is supposed to match perfectly. There must be a workaround.
1176 $entries = $this->store->retrieveEntriesByTag($tag_id,$this->user->getId());
1177 $bookTitle = sprintf(_('Articles tagged %s'),$tag);
1178 $bookFileName = substr(sprintf(_('Tag %s'),$tag), 0, 200);
1179 break;
1180 case 'category':
1181 $category = filter_var($_GET['category'],FILTER_SANITIZE_STRING);
1182 $entries = $this->store->getEntriesByView($category,$this->user->getId());
1183 $bookTitle = sprintf(_('All articles in category %s'), $category);
1184 $bookFileName = substr(sprintf(_('Category %s'),$category), 0, 200);
1185 break;
1186 case 'search':
1187 $search = filter_var($_GET['search'],FILTER_SANITIZE_STRING);
1188 $entries = $this->store->search($search,$this->user->getId());
1189 $bookTitle = sprintf(_('All articles for search %s'), $search);
1190 $bookFileName = substr(sprintf(_('Search %s'), $search), 0, 200);
1191 break;
1192 case 'default':
1193 die(_('Uh, there is a problem while generating epub.'));
1194
1195 }
1196
1197 $content_start =
1198 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1199 . "<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n"
1200 . "<head>"
1201 . "<meta http-equiv=\"Default-Style\" content=\"text/html; charset=utf-8\" />\n"
1202 . "<title>wallabag articles book</title>\n"
1203 . "</head>\n"
1204 . "<body>\n";
1205
1206 $bookEnd = "</body>\n</html>\n";
1207
1208 $log = new Logger("wallabag", TRUE);
1209 $fileDir = CACHE;
1210
1211 $book = new EPub(EPub::BOOK_VERSION_EPUB3, DEBUG_POCHE);
1212 $log->logLine("new EPub()");
1213 $log->logLine("EPub class version: " . EPub::VERSION);
1214 $log->logLine("EPub Req. Zip version: " . EPub::REQ_ZIP_VERSION);
1215 $log->logLine("Zip version: " . Zip::VERSION);
1216 $log->logLine("getCurrentServerURL: " . $book->getCurrentServerURL());
1217 $log->logLine("getCurrentPageURL..: " . $book->getCurrentPageURL());
1218
1219 $book->setTitle(_('wallabag\'s articles'));
1220 $book->setIdentifier("http://$_SERVER[HTTP_HOST]", EPub::IDENTIFIER_URI); // Could also be the ISBN number, prefered for published books, or a UUID.
1221 //$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.
1222 $book->setDescription(_("Some articles saved on my wallabag"));
1223 $book->setAuthor("wallabag","wallabag");
1224 $book->setPublisher("wallabag","wallabag"); // I hope this is a non existant address :)
1225 $book->setDate(time()); // Strictly not needed as the book date defaults to time().
1226 //$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.
1227 $book->setSourceURL("http://$_SERVER[HTTP_HOST]");
1228
1229 $book->addDublinCoreMetadata(DublinCore::CONTRIBUTOR, "PHP");
1230 $book->addDublinCoreMetadata(DublinCore::CONTRIBUTOR, "wallabag");
1231
1232 $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";
1233
1234 $log->logLine("Add Cover");
1235
1236 $fullTitle = "<h1> " . $bookTitle . "</h1>\n";
1237
1238 $book->setCoverImage("Cover.png", file_get_contents("themes/baggy/img/apple-touch-icon-152.png"), "image/png", $fullTitle);
1239
1240 $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;
1241
1242 //$book->addChapter("Table of Contents", "TOC.xhtml", NULL, false, EPub::EXTERNAL_REF_IGNORE);
1243 $book->addChapter("Notices", "Cover2.html", $cover);
1244
1245 $book->buildTOC();
1246
1247 foreach ($entries as $entry) { //set tags as subjects
1248 $tags = $this->store->retrieveTagsByEntry($entry['id']);
1249 foreach ($tags as $tag) {
1250 $book->setSubject($tag['value']);
1251 }
1252
1253 $log->logLine("Set up parameters");
1254
1255 $chapter = $content_start . $entry['content'] . $bookEnd;
1256 $book->addChapter($entry['title'], htmlspecialchars($entry['title']) . ".html", $chapter, true, EPub::EXTERNAL_REF_ADD);
1257 $log->logLine("Added chapter " . $entry['title']);
1258 }
1259
1260 if (DEBUG_POCHE) {
1261 $epuplog = $book->getLog();
1262 $book->addChapter("Log", "Log.html", $content_start . $log->getLog() . "\n</pre>" . $bookEnd); // log generation
1263 }
1264 $book->finalize();
1265 $zipData = $book->sendBook($bookFileName);
1266 }
1184} 1267}