]>
git.immae.eu Git - github/wallabag/wallabag.git/blob - inc/poche/Poche.class.php
3 * poche, a read it later open source system
6 * @author Nicolas Lœuillet <support@inthepoche.com>
8 * @license http://www.wtfpl.net/ see COPYING file
19 function __construct ()
22 if (! $this- > checkBeforeInstall ()) {
25 $this- > store
= new Database ();
27 $this- > messages
= new Messages ();
30 if (! $this- > store
-> isInstalled ())
37 * all checks before installation.
40 private function checkBeforeInstall ()
45 if (! is_writable ( CACHE
)) {
46 Tools
:: logm ( 'you don \' t have write access on cache directory' );
47 die ( 'You don \' t have write access on cache directory.' );
49 else if ( file_exists ( './install/update.php' ) && ! DEBUG_POCHE
) {
50 $msg = '<h1>setup</h1><p><strong>It \' s your first time here?</strong> Please copy /install/poche.sqlite in db folder. Then, delete install folder.<br /><strong>If you have already installed poche</strong>, an update is needed <a href="install/update.php">by clicking here</a>.</p>' ;
53 else if ( file_exists ( './install' ) && ! DEBUG_POCHE
) {
54 $msg = '<h1>setup</h1><p><strong>If you want to update your poche</strong>, you just have to delete /install folder. <br /><strong>To install your poche with sqlite</strong>, copy /install/poche.sqlite in /db and delete the folder /install. you have to delete the /install folder before using poche.</p>' ;
57 else if ( STORAGE
== 'sqlite' && ! is_writable ( STORAGE_SQLITE
)) {
58 Tools
:: logm ( 'you don \' t have write access on sqlite file' );
59 $msg = '<h1>error</h1><p>You don \' t have write access on sqlite file.</p>' ;
64 echo $this- > tpl
-> render ( 'error.twig' , array (
72 private function initTpl ()
75 $loader = new Twig_Loader_Filesystem ( TPL
);
77 $twig_params = array ();
80 $twig_params = array ( 'cache' => CACHE
);
82 $this- > tpl
= new Twig_Environment ( $loader , $twig_params );
83 $this- > tpl
-> addExtension ( new Twig_Extensions_Extension_I18n ());
84 # filter to display domain name of an url
85 $filter = new Twig_SimpleFilter ( 'getDomain' , 'Tools::getDomain' );
86 $this- > tpl
-> addFilter ( $filter );
88 # filter for reading time
89 $filter = new Twig_SimpleFilter ( 'getReadingTime' , 'Tools::getReadingTime' );
90 $this- > tpl
-> addFilter ( $filter );
93 private function init ()
98 if ( isset ( $_SESSION [ 'poche_user' ]) && $_SESSION [ 'poche_user' ] != array ()) {
99 $this- > user
= $_SESSION [ 'poche_user' ];
102 # fake user, just for install & login screens
103 $this- > user
= new User ();
104 $this- > user
-> setConfig ( $this- > getDefaultConfig ());
108 $language = $this- > user
-> getConfigValue ( 'language' );
109 putenv ( 'LC_ALL=' . $language );
110 setlocale ( LC_ALL
, $language );
111 bindtextdomain ( $language , LOCALE
);
112 textdomain ( $language );
115 $this- > pagination
= new Paginator ( $this- > user
-> getConfigValue ( 'pager' ), 'p' );
118 private function install ()
120 Tools
:: logm ( 'poche still not installed' );
121 echo $this- > tpl
-> render ( 'install.twig' , array (
122 'token' => Session
:: getToken ()
124 if ( isset ( $_GET [ 'install' ])) {
125 if (( $_POST [ 'password' ] == $_POST [ 'password_repeat' ])
126 && $_POST [ 'password' ] != "" && $_POST [ 'login' ] != "" ) {
127 # let's rock, install poche baby !
128 if ( $this- > store
-> install ( $_POST [ 'login' ], Tools
:: encodeString ( $_POST [ 'password' ] . $_POST [ 'login' ])))
131 Tools
:: logm ( 'poche is now installed' );
136 Tools
:: logm ( 'error during installation' );
143 public function getDefaultConfig ()
146 'pager' => PAGINATION
,
152 * Call action (mark as fav, archive, delete, etc.)
154 public function action ( $action , Url
$url , $id = 0 , $import = FALSE )
159 $content = $url- > extract ();
161 if ( $this- > store
-> add ( $url- > getUrl (), $content [ 'title' ], $content [ 'body' ], $this- > user
-> getId ())) {
162 Tools
:: logm ( 'add link ' . $url- > getUrl ());
164 if ( STORAGE
== 'postgres' ) {
165 $sequence = 'entries_id_seq' ;
167 $last_id = $this- > store
-> getLastId ( $sequence );
168 if ( DOWNLOAD_PICTURES
) {
169 $content = filtre_picture ( $content [ 'body' ], $url- > getUrl (), $last_id );
170 Tools
:: logm ( 'updating content article' );
171 $this- > store
-> updateContent ( $last_id , $content , $this- > user
-> getId ());
174 $this- > messages
-> add ( 's' , _ ( 'the link has been added successfully' ));
179 $this- > messages
-> add ( 'e' , _ ( 'error during insertion : the link wasn \' t added' ));
180 Tools
:: logm ( 'error during insertion : the link wasn \' t added ' . $url- > getUrl ());
189 $msg = 'delete link #' . $id ;
190 if ( $this- > store
-> deleteById ( $id , $this- > user
-> getId ())) {
191 if ( DOWNLOAD_PICTURES
) {
192 remove_directory ( ABS_PATH
. $id );
194 $this- > messages
-> add ( 's' , _ ( 'the link has been deleted successfully' ));
197 $this- > messages
-> add ( 'e' , _ ( 'the link wasn \' t deleted' ));
198 $msg = 'error : can \' t delete link #' . $id ;
201 Tools
:: redirect ( '?' );
204 $this- > store
-> favoriteById ( $id , $this- > user
-> getId ());
205 Tools
:: logm ( 'mark as favorite link #' . $id );
210 case 'toggle_archive' :
211 $this- > store
-> archiveById ( $id , $this- > user
-> getId ());
212 Tools
:: logm ( 'archive link #' . $id );
222 function displayView ( $view , $id = 0 )
229 $dev = $this- > getPocheVersion ( 'dev' );
230 $prod = $this- > getPocheVersion ( 'prod' );
231 $compare_dev = version_compare ( POCHE_VERSION
, $dev );
232 $compare_prod = version_compare ( POCHE_VERSION
, $prod );
236 'compare_dev' => $compare_dev ,
237 'compare_prod' => $compare_prod ,
239 Tools
:: logm ( 'config view' );
242 $entry = $this- > store
-> retrieveOneById ( $id , $this- > user
-> getId ());
243 if ( $entry != NULL ) {
244 Tools
:: logm ( 'view link #' . $id );
245 $content = $entry [ 'content' ];
246 if ( function_exists ( 'tidy_parse_string' )) {
247 $tidy = tidy_parse_string ( $content , array ( 'indent' => true , 'show-body-only' => true ), 'UTF8' );
248 $tidy- > cleanRepair ();
249 $content = $tidy- > value
;
252 $flattr = new FlattrItem ();
253 $flattr- > checkitem ( $entry [ 'url' ]);
257 'content' => $content ,
263 Tools
:: logm ( 'error in view call : entry is null' );
266 default : // home, favorites and archive views
267 $entries = $this- > store
-> getEntriesByView ( $view , $this- > user
-> getId ());
272 if ( count ( $entries ) > 0 ) {
273 $this- > pagination
-> set_total ( count ( $entries ));
274 $page_links = $this- > pagination
-> page_links ( '?view=' . $view . '&sort=' . $_SESSION [ 'sort' ] . '&' );
275 $datas = $this- > store
-> getEntriesByView ( $view , $this- > user
-> getId (), $this- > pagination
-> get_limit ());
276 $tpl_vars [ 'entries' ] = $datas ;
277 $tpl_vars [ 'page_links' ] = $page_links ;
279 Tools
:: logm ( 'display ' . $view . ' view' );
287 * update the password of the current user.
288 * if MODE_DEMO is TRUE, the password can't be updated.
289 * @todo add the return value
290 * @todo set the new password in function header like this updatePassword($newPassword)
293 public function updatePassword ()
296 $this- > messages
-> add ( 'i' , _ ( 'in demo mode, you can \' t update your password' ));
297 Tools
:: logm ( 'in demo mode, you can \' t do this' );
298 Tools
:: redirect ( '?view=config' );
301 if ( isset ( $_POST [ 'password' ]) && isset ( $_POST [ 'password_repeat' ])) {
302 if ( $_POST [ 'password' ] == $_POST [ 'password_repeat' ] && $_POST [ 'password' ] != "" ) {
303 $this- > messages
-> add ( 's' , _ ( 'your password has been updated' ));
304 $this- > store
-> updatePassword ( $this- > user
-> getId (), Tools
:: encodeString ( $_POST [ 'password' ] . $this- > user
-> getUsername ()));
306 Tools
:: logm ( 'password updated' );
310 $this- > messages
-> add ( 'e' , _ ( 'the two fields have to be filled & the password must be the same in the two fields' ));
311 Tools
:: redirect ( '?view=config' );
318 * checks if login & password are correct and save the user in session.
319 * it redirects the user to the $referer link
320 * @param string $referer the url to redirect after login
321 * @todo add the return value
324 public function login ( $referer )
326 if (! empty ( $_POST [ 'login' ]) && ! empty ( $_POST [ 'password' ])) {
327 $user = $this- > store
-> login ( $_POST [ 'login' ], Tools
:: encodeString ( $_POST [ 'password' ] . $_POST [ 'login' ]));
328 if ( $user != array ()) {
329 # Save login into Session
330 Session
:: login ( $user [ 'username' ], $user [ 'password' ], $_POST [ 'login' ], Tools
:: encodeString ( $_POST [ 'password' ] . $_POST [ 'login' ]), array ( 'poche_user' => new User ( $user )));
332 $this- > messages
-> add ( 's' , _ ( 'welcome to your poche' ));
333 if (! empty ( $_POST [ 'longlastingsession' ])) {
334 $_SESSION [ 'longlastingsession' ] = 31536000 ;
335 $_SESSION [ 'expires_on' ] = time () +
$_SESSION [ 'longlastingsession' ];
336 session_set_cookie_params ( $_SESSION [ 'longlastingsession' ]);
338 session_set_cookie_params ( 0 );
340 session_regenerate_id ( true );
341 Tools
:: logm ( 'login successful' );
342 Tools
:: redirect ( $referer );
344 $this- > messages
-> add ( 'e' , _ ( 'login failed: bad login or password' ));
345 Tools
:: logm ( 'login failed' );
348 $this- > messages
-> add ( 'e' , _ ( 'login failed: you have to fill all fields' ));
349 Tools
:: logm ( 'login failed' );
355 * log out the poche user. It cleans the session.
356 * @todo add the return value
359 public function logout ()
361 $this- > user
= array ();
363 $this- > messages
-> add ( 's' , _ ( 'see you soon!' ));
364 Tools
:: logm ( 'logout' );
369 * import from Instapaper. poche needs a ./instapaper-export.html file
370 * @todo add the return value
371 * @param string $targetFile the file used for importing
374 private function importFromInstapaper ( $targetFile )
376 # TODO gestion des articles favs
377 $html = new simple_html_dom ();
378 $html- > load_file ( $targetFile );
379 Tools
:: logm ( 'starting import from instapaper' );
383 foreach ( $html- > find ( 'ol' ) as $ul )
385 foreach ( $ul- > find ( 'li' ) as $li )
388 $url = new Url ( base64_encode ( $a [ 0 ]-> href
));
389 $this- > action ( 'add' , $url , 0 , TRUE );
392 if ( STORAGE
== 'postgres' ) {
393 $sequence = 'entries_id_seq' ;
395 $last_id = $this- > store
-> getLastId ( $sequence );
396 $this- > action ( 'toggle_archive' , $url , $last_id , TRUE );
400 # the second <ol> is for read links
403 $this- > messages
-> add ( 's' , _ ( 'import from instapaper completed' ));
404 Tools
:: logm ( 'import from instapaper completed' );
409 * import from Pocket. poche needs a ./ril_export.html file
410 * @todo add the return value
411 * @param string $targetFile the file used for importing
414 private function importFromPocket ( $targetFile )
416 # TODO gestion des articles favs
417 $html = new simple_html_dom ();
418 $html- > load_file ( $targetFile );
419 Tools
:: logm ( 'starting import from pocket' );
423 foreach ( $html- > find ( 'ul' ) as $ul )
425 foreach ( $ul- > find ( 'li' ) as $li )
428 $url = new Url ( base64_encode ( $a [ 0 ]-> href
));
429 $this- > action ( 'add' , $url , 0 , TRUE );
432 if ( STORAGE
== 'postgres' ) {
433 $sequence = 'entries_id_seq' ;
435 $last_id = $this- > store
-> getLastId ( $sequence );
436 $this- > action ( 'toggle_archive' , $url , $last_id , TRUE );
440 # the second <ul> is for read links
443 $this- > messages
-> add ( 's' , _ ( 'import from pocket completed' ));
444 Tools
:: logm ( 'import from pocket completed' );
449 * import from Readability. poche needs a ./readability file
450 * @todo add the return value
451 * @param string $targetFile the file used for importing
454 private function importFromReadability ( $targetFile )
456 # TODO gestion des articles lus / favs
457 $str_data = file_get_contents ( $targetFile );
458 $data = json_decode ( $str_data , true );
459 Tools
:: logm ( 'starting import from Readability' );
461 foreach ( $data as $key => $value ) {
465 foreach ( $value as $attr => $attr_value ) {
466 if ( $attr == 'article__url' ) {
467 $url = new Url ( base64_encode ( $attr_value ));
470 if ( STORAGE
== 'postgres' ) {
471 $sequence = 'entries_id_seq' ;
473 if ( $attr_value == 'true' ) {
474 if ( $attr == 'favorite' ) {
477 if ( $attr == 'archive' ) {
483 if (! is_null ( $url ) && $url- > isCorrect ()) {
484 $this- > action ( 'add' , $url , 0 , TRUE );
487 $last_id = $this- > store
-> getLastId ( $sequence );
488 $this- > action ( 'toggle_fav' , $url , $last_id , TRUE );
491 $last_id = $this- > store
-> getLastId ( $sequence );
492 $this- > action ( 'toggle_archive' , $url , $last_id , TRUE );
496 $this- > messages
-> add ( 's' , _ ( 'import from Readability completed. ' . $count . ' new links.' ));
497 Tools
:: logm ( 'import from Readability completed' );
502 * import datas into your poche
503 * @param string $from name of the service to import : pocket, instapaper or readability
504 * @todo add the return value
507 public function import ( $from )
510 'pocket' => 'importFromPocket' ,
511 'readability' => 'importFromReadability' ,
512 'instapaper' => 'importFromInstapaper'
515 if (! isset ( $providers [ $from ])) {
516 $this- > messages
-> add ( 'e' , _ ( 'Unknown import provider.' ));
520 $targetDefinition = 'IMPORT_' . strtoupper ( $from ) . '_FILE' ;
521 $targetFile = constant ( $targetDefinition );
523 if (! defined ( $targetDefinition )) {
524 $this- > messages
-> add ( 'e' , _ ( 'Incomplete inc/poche/define.inc.php file, please define "' . $targetDefinition . '".' ));
528 if (! file_exists ( $targetFile )) {
529 $this- > messages
-> add ( 'e' , _ ( 'Could not find required "' . $targetFile . '" import file.' ));
533 $this- > $providers [ $from ]( $targetFile );
537 * export poche entries in json
538 * @return json all poche entries
540 public function export ()
542 $entries = $this- > store
-> retrieveAll ( $this- > user
-> getId ());
543 echo $this- > tpl
-> render ( 'export.twig' , array (
544 'export' => Tools
:: renderJson ( $entries ),
546 Tools
:: logm ( 'export view' );
550 * Checks online the latest version of poche and cache it
551 * @param string $which 'prod' or 'dev'
552 * @return string latest $which version
554 private function getPocheVersion ( $which = 'prod' )
556 $cache_file = CACHE
. '/' . $which ;
558 # checks if the cached version file exists
559 if ( file_exists ( $cache_file ) && ( filemtime ( $cache_file ) > ( time () - 86400 ))) {
560 $version = file_get_contents ( $cache_file );
562 $version = file_get_contents ( 'http://static.inthepoche.com/versions/' . $which );
563 file_put_contents ( $cache_file , $version , LOCK_EX
);
569 /* class for Flattr querying. Should be put in a separate file
570 * Or maybe just create an array instead of a complete class... My mistake. :-°
575 public $flattrItemURL ;
578 public function checkitem ( $urltoflattr ){
579 $this- > cacheflattrfile ( $urltoflattr );
580 $flattrResponse = file_get_contents ( "cache/flattr/" . base64_encode ( $urltoflattr ). ".cache" );
581 if ( $flattrResponse != FALSE ){
582 $result = json_decode ( $flattrResponse );
583 if ( isset ( $result- > message
)){
584 if ( $result- > message
== "flattrable" ){
585 $this- > status
= "flattrable" ;
588 elseif ( $result- > link
) {
589 $this- > status
= "flattred" ;
590 $this- > flattrItemURL
= $result- > link
;
591 $this- > numflattrs
= $result- > flattrs
;
594 $this- > status
= "not flattrable" ;
599 $this- > status
= "FLATTR_ERR_CONNECTION" ;
603 private function cacheflattrfile ( $urltoflattr ){
604 if (! is_dir ( 'cache/flattr' )){
605 mkdir ( './cache/flattr' , 0777 );
607 // if a cache flattr file for this url already exists and it's been less than one day than it have been updated, see in /cache
608 if ((! file_exists ( "cache/flattr/" . base64_encode ( $urltoflattr ). ".cache" )) || ( time () - filemtime ( "cache/flattr/" . base64_encode ( $urltoflattr ). ".cache" ) > 86400 ))
610 $askForFlattr = Tools
:: getFile ( "https://api.flattr.com/rest/v2/things/lookup/?url=" . $urltoflattr );
611 $flattrCacheFile = fopen ( "cache/flattr/" . base64_encode ( $urltoflattr ). ".cache" , 'w+' );
612 fwrite ( $flattrCacheFile , $askForFlattr );
613 fclose ( $flattrCacheFile );