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