aboutsummaryrefslogtreecommitdiffhomepage
path: root/inc/poche/Poche.class.php
diff options
context:
space:
mode:
Diffstat (limited to 'inc/poche/Poche.class.php')
-rw-r--r--inc/poche/Poche.class.php214
1 files changed, 150 insertions, 64 deletions
diff --git a/inc/poche/Poche.class.php b/inc/poche/Poche.class.php
index 76a73be2..4df90067 100644
--- a/inc/poche/Poche.class.php
+++ b/inc/poche/Poche.class.php
@@ -1,9 +1,9 @@
1<?php 1<?php
2/** 2/**
3 * poche, a read it later open source system 3 * wallabag, self hostable application allowing you to not miss any content anymore
4 * 4 *
5 * @category poche 5 * @category wallabag
6 * @author Nicolas LÅ“uillet <support@inthepoche.com> 6 * @author Nicolas LÅ“uillet <nicolas@loeuillet.org>
7 * @copyright 2013 7 * @copyright 2013
8 * @license http://www.wtfpl.net/ see COPYING file 8 * @license http://www.wtfpl.net/ see COPYING file
9 */ 9 */
@@ -22,15 +22,6 @@ class Poche
22 private $currentTheme = ''; 22 private $currentTheme = '';
23 private $currentLanguage = ''; 23 private $currentLanguage = '';
24 private $notInstalledMessage = array(); 24 private $notInstalledMessage = array();
25
26 # @todo make this dynamic (actually install themes and save them in the database including author information et cetera)
27 private $installedThemes = array(
28 'default' => array('requires' => array()),
29 'dark' => array('requires' => array('default')),
30 'dmagenta' => array('requires' => array('default')),
31 'solarized' => array('requires' => array('default')),
32 'solarized-dark' => array('requires' => array('default'))
33 );
34 25
35 public function __construct() 26 public function __construct()
36 { 27 {
@@ -110,7 +101,7 @@ class Poche
110 $passTheme = TRUE; 101 $passTheme = TRUE;
111 # Twig is an absolute requirement for Poche to function. Abort immediately if the Composer installer hasn't been run yet 102 # Twig is an absolute requirement for Poche to function. Abort immediately if the Composer installer hasn't been run yet
112 if (! self::$canRenderTemplates) { 103 if (! self::$canRenderTemplates) {
113 $this->notInstalledMessage[] = 'Twig does not seem to be installed. Please initialize the Composer installation to automatically fetch dependencies. Have a look at <a href="http://doc.inthepoche.com/doku.php?id=users:begin:install">the documentation.</a>'; 104 $this->notInstalledMessage[] = 'Twig does not seem to be installed. Please initialize the Composer installation to automatically fetch dependencies. Have a look at <a href="http://doc.wallabag.org/doku.php?id=users:begin:install">the documentation.</a>';
114 $passTheme = FALSE; 105 $passTheme = FALSE;
115 } 106 }
116 107
@@ -123,21 +114,26 @@ class Poche
123 } 114 }
124 115
125 # Check if the selected theme and its requirements are present 116 # Check if the selected theme and its requirements are present
126 if ($this->getTheme() != '' && ! is_dir(THEME . '/' . $this->getTheme())) { 117 $theme = $this->getTheme();
127 $this->notInstalledMessage[] = 'The currently selected theme (' . $this->getTheme() . ') does not seem to be properly installed (Missing directory: ' . THEME . '/' . $this->getTheme() . ')'; 118
119 if ($theme != '' && ! is_dir(THEME . '/' . $theme)) {
120 $this->notInstalledMessage[] = 'The currently selected theme (' . $theme . ') does not seem to be properly installed (Missing directory: ' . THEME . '/' . $theme . ')';
128 121
129 self::$canRenderTemplates = false; 122 self::$canRenderTemplates = false;
130 123
131 $passTheme = FALSE; 124 $passTheme = FALSE;
132 } 125 }
133 126
134 foreach ($this->installedThemes[$this->getTheme()]['requires'] as $requiredTheme) { 127 $themeInfo = $this->getThemeInfo($theme);
135 if (! is_dir(THEME . '/' . $requiredTheme)) { 128 if (isset($themeInfo['requirements']) && is_array($themeInfo['requirements'])) {
136 $this->notInstalledMessage[] = 'The required "' . $requiredTheme . '" theme is missing for the current theme (' . $this->getTheme() . ')'; 129 foreach ($themeInfo['requirements'] as $requiredTheme) {
130 if (! is_dir(THEME . '/' . $requiredTheme)) {
131 $this->notInstalledMessage[] = 'The required "' . $requiredTheme . '" theme is missing for the current theme (' . $theme . ')';
137 132
138 self::$canRenderTemplates = false; 133 self::$canRenderTemplates = false;
139 134
140 $passTheme = FALSE; 135 $passTheme = FALSE;
136 }
141 } 137 }
142 } 138 }
143 139
@@ -193,32 +189,36 @@ class Poche
193 private function initTpl() 189 private function initTpl()
194 { 190 {
195 $loaderChain = new Twig_Loader_Chain(); 191 $loaderChain = new Twig_Loader_Chain();
192 $theme = $this->getTheme();
196 193
197 # add the current theme as first to the loader chain so Twig will look there first for overridden template files 194 # add the current theme as first to the loader chain so Twig will look there first for overridden template files
198 try { 195 try {
199 $loaderChain->addLoader(new Twig_Loader_Filesystem(THEME . '/' . $this->getTheme())); 196 $loaderChain->addLoader(new Twig_Loader_Filesystem(THEME . '/' . $theme));
200 } catch (Twig_Error_Loader $e) { 197 } catch (Twig_Error_Loader $e) {
201 # @todo isInstalled() should catch this, inject Twig later 198 # @todo isInstalled() should catch this, inject Twig later
202 die('The currently selected theme (' . $this->getTheme() . ') does not seem to be properly installed (' . THEME . '/' . $this->getTheme() .' is missing)'); 199 die('The currently selected theme (' . $theme . ') does not seem to be properly installed (' . THEME . '/' . $theme .' is missing)');
203 } 200 }
204 201
205 # add all required themes to the loader chain 202 # add all required themes to the loader chain
206 foreach ($this->installedThemes[$this->getTheme()]['requires'] as $requiredTheme) { 203 $themeInfo = $this->getThemeInfo($theme);
207 try { 204 if (isset($themeInfo['requirements']) && is_array($themeInfo['requirements'])) {
208 $loaderChain->addLoader(new Twig_Loader_Filesystem(THEME . '/' . DEFAULT_THEME)); 205 foreach ($themeInfo['requirements'] as $requiredTheme) {
209 } catch (Twig_Error_Loader $e) { 206 try {
210 # @todo isInstalled() should catch this, inject Twig later 207 $loaderChain->addLoader(new Twig_Loader_Filesystem(THEME . '/' . $requiredTheme));
211 die('The required "' . $requiredTheme . '" theme is missing for the current theme (' . $this->getTheme() . ')'); 208 } catch (Twig_Error_Loader $e) {
209 # @todo isInstalled() should catch this, inject Twig later
210 die('The required "' . $requiredTheme . '" theme is missing for the current theme (' . $theme . ')');
211 }
212 } 212 }
213 } 213 }
214 214
215 if (DEBUG_POCHE) { 215 if (DEBUG_POCHE) {
216 $twig_params = array(); 216 $twigParams = array();
217 } else { 217 } else {
218 $twig_params = array('cache' => CACHE); 218 $twigParams = array('cache' => CACHE);
219 } 219 }
220 220
221 $this->tpl = new Twig_Environment($loaderChain, $twig_params); 221 $this->tpl = new Twig_Environment($loaderChain, $twigParams);
222 $this->tpl->addExtension(new Twig_Extensions_Extension_I18n()); 222 $this->tpl->addExtension(new Twig_Extensions_Extension_I18n());
223 223
224 # filter to display domain name of an url 224 # filter to display domain name of an url
@@ -234,7 +234,7 @@ class Poche
234 $this->tpl->addFilter($filter); 234 $this->tpl->addFilter($filter);
235 } 235 }
236 236
237 private function install() 237 private function install()
238 { 238 {
239 Tools::logm('poche still not installed'); 239 Tools::logm('poche still not installed');
240 echo $this->tpl->render('install.twig', array( 240 echo $this->tpl->render('install.twig', array(
@@ -265,34 +265,59 @@ class Poche
265 return $this->currentTheme; 265 return $this->currentTheme;
266 } 266 }
267 267
268 public function getLanguage() { 268 /**
269 return $this->currentLanguage; 269 * Provides theme information by parsing theme.ini file if present in the theme's root directory.
270 * In all cases, the following data will be returned:
271 * - name: theme's name, or key if the theme is unnamed,
272 * - current: boolean informing if the theme is the current user theme.
273 *
274 * @param string $theme Theme key (directory name)
275 * @return array|boolean Theme information, or false if the theme doesn't exist.
276 */
277 public function getThemeInfo($theme) {
278 if (!is_dir(THEME . '/' . $theme)) {
279 return false;
280 }
281
282 $themeIniFile = THEME . '/' . $theme . '/theme.ini';
283 $themeInfo = array();
284
285 if (is_file($themeIniFile) && is_readable($themeIniFile)) {
286 $themeInfo = parse_ini_file($themeIniFile);
287 }
288
289 if ($themeInfo === false) {
290 $themeInfo = array();
291 }
292 if (!isset($themeInfo['name'])) {
293 $themeInfo['name'] = $theme;
294 }
295 $themeInfo['current'] = ($theme === $this->getTheme());
296
297 return $themeInfo;
270 } 298 }
271 299
272 public function getInstalledThemes() { 300 public function getInstalledThemes() {
273 $handle = opendir(THEME); 301 $handle = opendir(THEME);
274 $themes = array(); 302 $themes = array();
275 303
276 while (($theme = readdir($handle)) !== false) { 304 while (($theme = readdir($handle)) !== false) {
277 # Themes are stored in a directory, so all directory names are themes 305 # Themes are stored in a directory, so all directory names are themes
278 # @todo move theme installation data to database 306 # @todo move theme installation data to database
279 if (! is_dir(THEME . '/' . $theme) || in_array($theme, array('..', '.'))) { 307 if (!is_dir(THEME . '/' . $theme) || in_array($theme, array('.', '..'))) {
280 continue; 308 continue;
281 } 309 }
282 310
283 $current = false; 311 $themes[$theme] = $this->getThemeInfo($theme);
284
285 if ($theme === $this->getTheme()) {
286 $current = true;
287 }
288
289 $themes[] = array('name' => $theme, 'current' => $current);
290 } 312 }
291 313
292 sort($themes);
293 return $themes; 314 return $themes;
294 } 315 }
295 316
317 public function getLanguage() {
318 return $this->currentLanguage;
319 }
320
296 public function getInstalledLanguages() { 321 public function getInstalledLanguages() {
297 $handle = opendir(LOCALE); 322 $handle = opendir(LOCALE);
298 $languages = array(); 323 $languages = array();
@@ -325,6 +350,22 @@ class Poche
325 ); 350 );
326 } 351 }
327 352
353 protected function getPageContent(Url $url)
354 {
355 $options = array('http' => array('user_agent' => 'poche'));
356 if (isset($_SERVER['AUTH_TYPE']) && "basic" === strtolower($_SERVER['AUTH_TYPE'])) {
357 $options['http']['header'] = sprintf(
358 "Authorization: Basic %s",
359 base64_encode(
360 sprintf('%s:%s', $_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'])
361 )
362 );
363 }
364 $context = stream_context_create($options);
365 $json = file_get_contents(Tools::getPocheUrl() . '/inc/3rdparty/makefulltextfeed.php?url='.urlencode($url->getUrl()).'&max=5&links=preserve&exc=&format=json&submit=Create+Feed', false, $context);
366 return json_decode($json, true);
367 }
368
328 /** 369 /**
329 * Call action (mark as fav, archive, delete, etc.) 370 * Call action (mark as fav, archive, delete, etc.)
330 */ 371 */
@@ -333,11 +374,8 @@ class Poche
333 switch ($action) 374 switch ($action)
334 { 375 {
335 case 'add': 376 case 'add':
336 $options = array('http' => array('user_agent' => 'poche')); 377 $content = $this->getPageContent($url);
337 $context = stream_context_create($options); 378 $title = ($content['rss']['channel']['item']['title'] != '') ? $content['rss']['channel']['item']['title'] : _('Untitled');
338 $json = file_get_contents(Tools::getPocheUrl() . '/inc/3rdparty/makefulltextfeed.php?url='.urlencode($url->getUrl()).'&max=5&links=preserve&exc=&format=json&submit=Create+Feed', false, $context);
339 $content = json_decode($json, true);
340 $title = $content['rss']['channel']['item']['title'];
341 $body = $content['rss']['channel']['item']['description']; 379 $body = $content['rss']['channel']['item']['description'];
342 380
343 if ($this->store->add($url->getUrl(), $title, $body, $this->user->getId())) { 381 if ($this->store->add($url->getUrl(), $title, $body, $this->user->getId())) {
@@ -586,8 +624,8 @@ class Poche
586 $themes = $this->getInstalledThemes(); 624 $themes = $this->getInstalledThemes();
587 $actualTheme = false; 625 $actualTheme = false;
588 626
589 foreach ($themes as $theme) { 627 foreach (array_keys($themes) as $theme) {
590 if ($theme['name'] == $_POST['theme']) { 628 if ($theme == $_POST['theme']) {
591 $actualTheme = true; 629 $actualTheme = true;
592 break; 630 break;
593 } 631 }
@@ -654,17 +692,17 @@ class Poche
654 */ 692 */
655 private function credentials() { 693 private function credentials() {
656 if(isset($_SERVER['PHP_AUTH_USER'])) { 694 if(isset($_SERVER['PHP_AUTH_USER'])) {
657 return array($_SERVER['PHP_AUTH_USER'],'php_auth'); 695 return array($_SERVER['PHP_AUTH_USER'],'php_auth',true);
658 } 696 }
659 if(!empty($_POST['login']) && !empty($_POST['password'])) { 697 if(!empty($_POST['login']) && !empty($_POST['password'])) {
660 return array($_POST['login'],$_POST['password']); 698 return array($_POST['login'],$_POST['password'],false);
661 } 699 }
662 if(isset($_SERVER['REMOTE_USER'])) { 700 if(isset($_SERVER['REMOTE_USER'])) {
663 return array($_SERVER['REMOTE_USER'],'http_auth'); 701 return array($_SERVER['REMOTE_USER'],'http_auth',true);
664 } 702 }
665 703
666 return array(false,false); 704 return array(false,false,false);
667 } 705 }
668 706
669 /** 707 /**
670 * checks if login & password are correct and save the user in session. 708 * checks if login & password are correct and save the user in session.
@@ -675,18 +713,19 @@ class Poche
675 */ 713 */
676 public function login($referer) 714 public function login($referer)
677 { 715 {
678 list($login,$password)=$this->credentials(); 716 list($login,$password,$isauthenticated)=$this->credentials();
679 if($login === false || $password === false) { 717 if($login === false || $password === false) {
680 $this->messages->add('e', _('login failed: you have to fill all fields')); 718 $this->messages->add('e', _('login failed: you have to fill all fields'));
681 Tools::logm('login failed'); 719 Tools::logm('login failed');
682 Tools::redirect(); 720 Tools::redirect();
683 } 721 }
684 if (!empty($login) && !empty($password)) { 722 if (!empty($login) && !empty($password)) {
685 $user = $this->store->login($login, Tools::encodeString($password . $login)); 723 $user = $this->store->login($login, Tools::encodeString($password . $login), $isauthenticated);
686 if ($user != array()) { 724 if ($user != array()) {
687 # Save login into Session 725 # Save login into Session
688 $longlastingsession = isset($_POST['longlastingsession']); 726 $longlastingsession = isset($_POST['longlastingsession']);
689 Session::login($user['username'], $user['password'], $login, Tools::encodeString($password . $login), $longlastingsession, array('poche_user' => new User($user))); 727 $passwordTest = ($isauthenticated) ? $user['password'] : Tools::encodeString($password . $login);
728 Session::login($user['username'], $user['password'], $login, $passwordTest, $longlastingsession, array('poche_user' => new User($user)));
690 $this->messages->add('s', _('welcome to your poche')); 729 $this->messages->add('s', _('welcome to your poche'));
691 Tools::logm('login successful'); 730 Tools::logm('login successful');
692 Tools::redirect($referer); 731 Tools::redirect($referer);
@@ -848,6 +887,52 @@ class Poche
848 } 887 }
849 888
850 /** 889 /**
890 * import from Poche exported file
891 * @param string $targetFile the file used for importing
892 * @return boolean
893 */
894 private function importFromPoche($targetFile)
895 {
896 $str_data = file_get_contents($targetFile);
897 $data = json_decode($str_data,true);
898 Tools::logm('starting import from Poche');
899
900
901 $sequence = '';
902 if (STORAGE == 'postgres') {
903 $sequence = 'entries_id_seq';
904 }
905
906 $count = 0;
907 foreach ($data as $value) {
908
909 $url = new Url(base64_encode($value['url']));
910 $favorite = ($value['is_fav'] == -1);
911 $archive = ($value['is_read'] == -1);
912
913 # we can add the url
914 if (!is_null($url) && $url->isCorrect()) {
915
916 $this->action('add', $url, 0, TRUE);
917
918 $count++;
919 if ($favorite) {
920 $last_id = $this->store->getLastId($sequence);
921 $this->action('toggle_fav', $url, $last_id, TRUE);
922 }
923 if ($archive) {
924 $last_id = $this->store->getLastId($sequence);
925 $this->action('toggle_archive', $url, $last_id, TRUE);
926 }
927 }
928
929 }
930 $this->messages->add('s', _('import from Poche completed. ' . $count . ' new links.'));
931 Tools::logm('import from Poche completed');
932 Tools::redirect();
933 }
934
935 /**
851 * import datas into your poche 936 * import datas into your poche
852 * @param string $from name of the service to import : pocket, instapaper or readability 937 * @param string $from name of the service to import : pocket, instapaper or readability
853 * @todo add the return value 938 * @todo add the return value
@@ -858,7 +943,8 @@ class Poche
858 $providers = array( 943 $providers = array(
859 'pocket' => 'importFromPocket', 944 'pocket' => 'importFromPocket',
860 'readability' => 'importFromReadability', 945 'readability' => 'importFromReadability',
861 'instapaper' => 'importFromInstapaper' 946 'instapaper' => 'importFromInstapaper',
947 'poche' => 'importFromPoche',
862 ); 948 );
863 949
864 if (! isset($providers[$from])) { 950 if (! isset($providers[$from])) {
@@ -908,7 +994,7 @@ class Poche
908 if (file_exists($cache_file) && (filemtime($cache_file) > (time() - 86400 ))) { 994 if (file_exists($cache_file) && (filemtime($cache_file) > (time() - 86400 ))) {
909 $version = file_get_contents($cache_file); 995 $version = file_get_contents($cache_file);
910 } else { 996 } else {
911 $version = file_get_contents('http://static.inthepoche.com/versions/' . $which); 997 $version = file_get_contents('http://static.wallabag.org/versions/' . $which);
912 file_put_contents($cache_file, $version, LOCK_EX); 998 file_put_contents($cache_file, $version, LOCK_EX);
913 } 999 }
914 return $version; 1000 return $version;