Browse Source

WHAT. A. BIG. REFACTOR. + new license (we moved to MIT one)

footnotes
Nicolas Lœuillet 9 years ago
parent
commit
3602405ec0
  1. 27
      COPYING.md
  2. 3
      CREDITS.md
  3. 5
      README.md
  4. 14
      check_setup.php
  5. 26
      inc/3rdparty/FlattrItem.class.php
  6. 34
      inc/3rdparty/Session.class.php
  7. 19
      inc/poche/Database.class.php
  8. 114
      inc/poche/Language.class.php
  9. 453
      inc/poche/Poche.class.php
  10. 149
      inc/poche/Routing.class.php
  11. 236
      inc/poche/Template.class.php
  12. 156
      inc/poche/Tools.class.php
  13. 2
      inc/poche/Url.class.php
  14. 11
      inc/poche/User.class.php
  15. 2
      inc/poche/config.inc.default.php
  16. 23
      inc/poche/global.inc.php
  17. 267
      inc/poche/pochePictures.php
  18. 132
      index.php
  19. 9
      install/index.php
  20. 9
      wallabag_compatibility_test.php

27
COPYING.md

@ -1,14 +1,19 @@ @@ -1,14 +1,19 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (c) 2013-2014 Nicolas Lœuillet
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

3
CREDITS.md

@ -1,7 +1,6 @@ @@ -1,7 +1,6 @@
wallabag is based on :
* PHP Readability https://bitbucket.org/fivefilters/php-readability
* Full Text RSS http://code.fivefilters.org/full-text-rss/src
* Encoding https://github.com/neitanod/forceutf8
* logo by Maylis Agniel https://github.com/wallabag/logo
* icons http://icomoon.io
* PHP Simple HTML DOM Parser (for Pocket import) http://simplehtmldom.sourceforge.net/
@ -10,6 +9,6 @@ wallabag is based on : @@ -10,6 +9,6 @@ wallabag is based on :
* Flash messages https://github.com/plasticbrain/PHP-Flash-Messages
* Pagination https://github.com/daveismyname/pagination
wallabag is developed by Nicolas Lœuillet under the Do What the Fuck You Want to Public License
wallabag is mainly developed by Nicolas Lœuillet under the MIT License
Contributors : https://github.com/wallabag/wallabag/graphs/contributors

5
README.md

@ -4,7 +4,6 @@ wallabag is a self hostable application allowing you to not miss any content any @@ -4,7 +4,6 @@ wallabag is a self hostable application allowing you to not miss any content any
More informations on our website: [wallabag.org](http://wallabag.org)
## License
Copyright © 2010-2014 Nicolas Lœuillet <nicolas@loeuillet.org>
Copyright © 2013-2014 Nicolas Lœuillet <nicolas@loeuillet.org>
This work is free. You can redistribute it and/or modify it under the
terms of the Do What The Fuck You Want To Public License, Version 2,
as published by Sam Hocevar. See the COPYING file for more details.
terms of the MIT License. See the COPYING file for more details.

14
check_setup.php

@ -1,5 +1,4 @@ @@ -1,5 +1,4 @@
<?php
// PHP 5.3 minimum
if (version_compare(PHP_VERSION, '5.3.3', '<')) {
die('This software require PHP 5.3.3 minimum');
@ -13,14 +12,11 @@ if (version_compare(PHP_VERSION, '5.4.0', '<')) { @@ -13,14 +12,11 @@ if (version_compare(PHP_VERSION, '5.4.0', '<')) {
}
}
// Check if /cache is writeable
if (! is_writable('cache')) {
die('The directory "cache" must be writeable by your web server user');
}
// Check if /db is writeable
if (! is_writable('db')) {
die('The directory "db" must be writeable by your web server user');
$writableFolders = array('cache', 'db');
foreach ($writableFolders as $folder) {
if (! is_writable($folder)) {
die('The directory "' . $folder . '" must be writeable by your web server user');
}
}
// install folder still present, need to install wallabag

26
inc/3rdparty/FlattrItem.class.php vendored

@ -1,25 +1,32 @@ @@ -1,25 +1,32 @@
<?php
/*
* Class for Flattr querying
*/
class FlattrItem {
/**
* wallabag, self hostable application allowing you to not miss any content anymore
*
* @category wallabag
* @author Nicolas Lœuillet <nicolas@loeuillet.org>
* @copyright 2013
* @license http://opensource.org/licenses/MIT see COPYING file
*/
class FlattrItem
{
public $status;
public $urltoflattr;
public $flattrItemURL;
public $numflattrs;
public function checkItem($urltoflattr,$id) {
$this->cacheflattrfile($urltoflattr, $id);
public function checkItem($urltoflattr, $id)
{
$this->_cacheflattrfile($urltoflattr, $id);
$flattrResponse = file_get_contents(CACHE . "/flattr/".$id.".cache");
if($flattrResponse != FALSE) {
$result = json_decode($flattrResponse);
if (isset($result->message)){
if (isset($result->message)) {
if ($result->message == "flattrable") {
$this->status = FLATTRABLE;
}
}
elseif (is_object($result) && $result->link) {
elseif (is_object($result) && $result->link) {
$this->status = FLATTRED;
$this->flattrItemURL = $result->link;
$this->numflattrs = $result->flattrs;
@ -33,7 +40,8 @@ class FlattrItem { @@ -33,7 +40,8 @@ class FlattrItem {
}
}
private function cacheflattrfile($urltoflattr, $id) {
private function _cacheflattrfile($urltoflattr, $id)
{
if (!is_dir(CACHE . '/flattr')) {
mkdir(CACHE . '/flattr', 0777);
}

34
inc/3rdparty/Session.class.php vendored

@ -309,4 +309,38 @@ class Session @@ -309,4 +309,38 @@ class Session
return true; // User is not banned.
}
/**
* Tells if a param exists in session
*
* @param $name name of the param to test
* @return bool
*/
public static function isInSession($name)
{
return (isset($_SESSION[$name]) ? : FALSE);
}
/**
* Returns param in session
*
* @param $name name of the param to return
* @return mixed param or null
*/
public static function getParam($name)
{
return (self::isInSession($name) ? $_SESSION[$name] : NULL);
}
/**
* Store value in session
*
* @param $name name of the variable to store
* @param $value value to store
*/
public static function setParam($name, $value)
{
$_SESSION[$name] = $value;
}
}

19
inc/poche/Database.class.php

@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
* @category wallabag
* @author Nicolas Lœuillet <nicolas@loeuillet.org>
* @copyright 2013
* @license http://www.wtfpl.net/ see COPYING file
* @license http://opensource.org/licenses/MIT see COPYING file
*/
class Database {
@ -38,6 +38,7 @@ class Database { @@ -38,6 +38,7 @@ class Database {
}
$this->handle->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->_checkTags();
Tools::logm('storage type ' . STORAGE);
}
@ -45,21 +46,7 @@ class Database { @@ -45,21 +46,7 @@ class Database {
return $this->handle;
}
public function isInstalled() {
$sql = "SELECT username FROM users";
$query = $this->executeQuery($sql, array());
if ($query == false) {
die(STORAGE . ' database looks empty. You have to create it (you can find database structure in install folder).');
}
$hasAdmin = count($query->fetchAll());
if ($hasAdmin == 0)
return false;
return true;
}
public function checkTags() {
private function _checkTags() {
if (STORAGE == 'sqlite') {
$sql = '

114
inc/poche/Language.class.php

@ -0,0 +1,114 @@ @@ -0,0 +1,114 @@
<?php
/**
* wallabag, self hostable application allowing you to not miss any content anymore
*
* @category wallabag
* @author Nicolas Lœuillet <nicolas@loeuillet.org>
* @copyright 2013
* @license http://opensource.org/licenses/MIT see COPYING file
*/
class Language
{
protected $wallabag;
private $currentLanguage;
private $languageNames = array(
'cs_CZ.utf8' => 'čeština',
'de_DE.utf8' => 'German',
'en_EN.utf8' => 'English',
'es_ES.utf8' => 'Español',
'fa_IR.utf8' => 'فارسی',
'fr_FR.utf8' => 'Français',
'it_IT.utf8' => 'Italiano',
'pl_PL.utf8' => 'Polski',
'pt_BR.utf8' => 'Português (Brasil)',
'ru_RU.utf8' => 'Pусский',
'sl_SI.utf8' => 'Slovenščina',
'uk_UA.utf8' => 'Українська',
);
public function __construct(Poche $wallabag)
{
$this->wallabag = $wallabag;
$pocheUser = Session::getParam('poche_user');
$language = (is_null($pocheUser) ? LANG : $pocheUser->getConfigValue('language'));
@putenv('LC_ALL=' . $language);
setlocale(LC_ALL, $language);
bindtextdomain($language, LOCALE);
textdomain($language);
$this->currentLanguage = $language;
}
public function getLanguage() {
return $this->currentLanguage;
}
public function getInstalledLanguages() {
$handle = opendir(LOCALE);
$languages = array();
while (($language = readdir($handle)) !== false) {
# Languages are stored in a directory, so all directory names are languages
# @todo move language installation data to database
if (! is_dir(LOCALE . '/' . $language) || in_array($language, array('..', '.', 'tools'))) {
continue;
}
$current = false;
if ($language === $this->getLanguage()) {
$current = true;
}
$languages[] = array('name' => (isset($this->languageNames[$language]) ? $this->languageNames[$language] : $language), 'value' => $language, 'current' => $current);
}
return $languages;
}
/**
* Update language for current user
*
* @param $newLanguage
*/
public function updateLanguage($newLanguage)
{
# we are not going to change it to the current language
if ($newLanguage == $this->getLanguage()) {
$this->wallabag->messages->add('w', _('still using the "' . $this->getLanguage() . '" language!'));
Tools::redirect('?view=config');
}
$languages = $this->getInstalledLanguages();
$actualLanguage = false;
foreach ($languages as $language) {
if ($language['value'] == $newLanguage) {
$actualLanguage = true;
break;
}
}
if (!$actualLanguage) {
$this->wallabag->messages->add('e', _('that language does not seem to be installed'));
Tools::redirect('?view=config');
}
$this->wallabag->store->updateUserConfig($this->wallabag->user->getId(), 'language', $newLanguage);
$this->wallabag->messages->add('s', _('you have changed your language preferences'));
$currentConfig = $_SESSION['poche_user']->config;
$currentConfig['language'] = $newLanguage;
$_SESSION['poche_user']->setConfig($currentConfig);
$this->wallabag->emptyCache();
Tools::redirect('?view=config');
}
}

453
inc/poche/Poche.class.php

@ -5,244 +5,77 @@ @@ -5,244 +5,77 @@
* @category wallabag
* @author Nicolas Lœuillet <nicolas@loeuillet.org>
* @copyright 2013
* @license http://www.wtfpl.net/ see COPYING file
* @license http://opensource.org/licenses/MIT see COPYING file
*/
class Poche
{
public static $canRenderTemplates = true;
public static $configFileAvailable = true;
/**
* @var User
*/
public $user;
/**
* @var Database
*/
public $store;
/**
* @var Template
*/
public $tpl;
/**
* @var Language
*/
public $language;
/**
* @var Routing
*/
public $routing;
/**
* @var Messages
*/
public $messages;
/**
* @var Paginator
*/
public $pagination;
private $currentTheme = '';
private $currentLanguage = '';
private $notInstalledMessage = array();
private $language_names = array(
'cs_CZ.utf8' => 'čeština',
'de_DE.utf8' => 'German',
'en_EN.utf8' => 'English',
'es_ES.utf8' => 'Español',
'fa_IR.utf8' => 'فارسی',
'fr_FR.utf8' => 'Français',
'it_IT.utf8' => 'Italiano',
'pl_PL.utf8' => 'Polski',
'pt_BR.utf8' => 'Português (Brasil)',
'ru_RU.utf8' => 'Pусский',
'sl_SI.utf8' => 'Slovenščina',
'uk_UA.utf8' => 'Українська',
);
public function __construct()
{
if ($this->configFileIsAvailable()) {
$this->init();
}
if ($this->themeIsInstalled()) {
$this->initTpl();
}
if ($this->systemIsInstalled()) {
$this->store = new Database();
$this->messages = new Messages();
# installation
if (! $this->store->isInstalled()) {
$this->install();
}
$this->store->checkTags();
}
$this->init();
}
private function init()
{
Tools::initPhp();
if (isset($_SESSION['poche_user']) && $_SESSION['poche_user'] != array()) {
$this->user = $_SESSION['poche_user'];
$pocheUser = Session::getParam('poche_user');
if ($pocheUser && $pocheUser != array()) {
$this->user = $pocheUser;
} else {
# fake user, just for install & login screens
// fake user, just for install & login screens
$this->user = new User();
$this->user->setConfig($this->getDefaultConfig());
}
# l10n
$language = $this->user->getConfigValue('language');
@putenv('LC_ALL=' . $language);
setlocale(LC_ALL, $language);
bindtextdomain($language, LOCALE);
textdomain($language);
# Pagination
$this->pagination = new Paginator($this->user->getConfigValue('pager'), 'p');
# Set up theme
$themeDirectory = $this->user->getConfigValue('theme');
if ($themeDirectory === false) {
$themeDirectory = DEFAULT_THEME;
}
$this->currentTheme = $themeDirectory;
# Set up language
$languageDirectory = $this->user->getConfigValue('language');
if ($languageDirectory === false) {
$languageDirectory = DEFAULT_THEME;
}
$this->currentLanguage = $languageDirectory;
}
public function configFileIsAvailable() {
if (! self::$configFileAvailable) {
$this->notInstalledMessage[] = 'You have to copy (don\'t just rename!) inc/poche/config.inc.default.php to inc/poche/config.inc.php.';
return false;
}
return true;
$this->pagination = new Paginator($this->user->getConfigValue('pager'), 'p');
$this->language = new Language($this);
$this->tpl = new Template($this);
$this->store = new Database();
$this->messages = new Messages();
$this->routing = new Routing($this);
}
public function themeIsInstalled() {
$passTheme = TRUE;
# Twig is an absolute requirement for Poche to function. Abort immediately if the Composer installer hasn't been run yet
if (! self::$canRenderTemplates) {
$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.';
$passTheme = FALSE;
}
if (! is_writable(CACHE)) {
$this->notInstalledMessage[] = 'You don\'t have write access on cache directory.';
self::$canRenderTemplates = false;
$passTheme = FALSE;
}
# Check if the selected theme and its requirements are present
$theme = $this->getTheme();
if ($theme != '' && ! is_dir(THEME . '/' . $theme)) {
$this->notInstalledMessage[] = 'The currently selected theme (' . $theme . ') does not seem to be properly installed (Missing directory: ' . THEME . '/' . $theme . ')';
self::$canRenderTemplates = false;
$passTheme = FALSE;
}
$themeInfo = $this->getThemeInfo($theme);
if (isset($themeInfo['requirements']) && is_array($themeInfo['requirements'])) {
foreach ($themeInfo['requirements'] as $requiredTheme) {
if (! is_dir(THEME . '/' . $requiredTheme)) {
$this->notInstalledMessage[] = 'The required "' . $requiredTheme . '" theme is missing for the current theme (' . $theme . ')';
self::$canRenderTemplates = false;
$passTheme = FALSE;
}
}
}
if (!$passTheme) {
return FALSE;
}
return true;
public function run()
{
$this->routing->run();
}
/**
* all checks before installation.
* @todo move HTML to template
* @return boolean
* Creates a new user
*/
public function systemIsInstalled()
public function createNewUser()
{
$msg = TRUE;
$configSalt = defined('SALT') ? constant('SALT') : '';
if (empty($configSalt)) {
$this->notInstalledMessage[] = 'You have not yet filled in the SALT value in the config.inc.php file.';
$msg = FALSE;
}
if (STORAGE == 'sqlite' && ! file_exists(STORAGE_SQLITE)) {
Tools::logm('sqlite file doesn\'t exist');
$this->notInstalledMessage[] = 'sqlite file doesn\'t exist, you can find it in install folder. Copy it in /db folder.';
$msg = FALSE;
}
if (is_dir(ROOT . '/install') && ! DEBUG_POCHE) {
$this->notInstalledMessage[] = 'you have to delete the /install folder before using poche.';
$msg = FALSE;
}
if (STORAGE == 'sqlite' && ! is_writable(STORAGE_SQLITE)) {
Tools::logm('you don\'t have write access on sqlite file');
$this->notInstalledMessage[] = 'You don\'t have write access on sqlite file.';
$msg = FALSE;
}
if (! $msg) {
return false;
}
return true;
}
public function getNotInstalledMessage() {
return $this->notInstalledMessage;
}
private function initTpl()
{
$loaderChain = new Twig_Loader_Chain();
$theme = $this->getTheme();
# add the current theme as first to the loader chain so Twig will look there first for overridden template files
try {
$loaderChain->addLoader(new Twig_Loader_Filesystem(THEME . '/' . $theme));
} catch (Twig_Error_Loader $e) {
# @todo isInstalled() should catch this, inject Twig later
die('The currently selected theme (' . $theme . ') does not seem to be properly installed (' . THEME . '/' . $theme .' is missing)');
}
# add all required themes to the loader chain
$themeInfo = $this->getThemeInfo($theme);
if (isset($themeInfo['requirements']) && is_array($themeInfo['requirements'])) {
foreach ($themeInfo['requirements'] as $requiredTheme) {
try {
$loaderChain->addLoader(new Twig_Loader_Filesystem(THEME . '/' . $requiredTheme));
} catch (Twig_Error_Loader $e) {
# @todo isInstalled() should catch this, inject Twig later
die('The required "' . $requiredTheme . '" theme is missing for the current theme (' . $theme . ')');
}
}
}
if (DEBUG_POCHE) {
$twigParams = array();
} else {
$twigParams = array('cache' => CACHE);
}
$this->tpl = new Twig_Environment($loaderChain, $twigParams);
$this->tpl->addExtension(new Twig_Extensions_Extension_I18n());
# filter to display domain name of an url
$filter = new Twig_SimpleFilter('getDomain', 'Tools::getDomain');
$this->tpl->addFilter($filter);
# filter for reading time
$filter = new Twig_SimpleFilter('getReadingTime', 'Tools::getReadingTime');
$this->tpl->addFilter($filter);
}
public function createNewUser() {
if (isset($_GET['newuser'])){
if ($_POST['newusername'] != "" && $_POST['password4newuser'] != ""){
$newusername = filter_var($_POST['newusername'], FILTER_SANITIZE_STRING);
@ -266,7 +99,11 @@ class Poche @@ -266,7 +99,11 @@ class Poche
}
}
public function deleteUser(){
/**
* Delete an existing user
*/
public function deleteUser()
{
if (isset($_GET['deluser'])){
if ($this->store->listUsers() > 1) {
if (Tools::encodeString($_POST['password4deletinguser'].$this->user->getUsername()) == $this->store->getUserPassword($this->user->getId())) {
@ -294,115 +131,6 @@ class Poche @@ -294,115 +131,6 @@ class Poche
}
}
private function install()
{
Tools::logm('poche still not installed');
echo $this->tpl->render('install.twig', array(
'token' => Session::getToken(),
'theme' => $this->getTheme(),
'poche_url' => Tools::getPocheUrl()
));
if (isset($_GET['install'])) {
if (($_POST['password'] == $_POST['password_repeat'])
&& $_POST['password'] != "" && $_POST['login'] != "") {
# let's rock, install poche baby !
if ($this->store->install($_POST['login'], Tools::encodeString($_POST['password'] . $_POST['login'])))
{
Session::logout();
Tools::logm('poche is now installed');
Tools::redirect();
}
}
else {
Tools::logm('error during installation');
Tools::redirect();
}
}
exit();
}
public function getTheme() {
return $this->currentTheme;
}
/**
* Provides theme information by parsing theme.ini file if present in the theme's root directory.
* In all cases, the following data will be returned:
* - name: theme's name, or key if the theme is unnamed,
* - current: boolean informing if the theme is the current user theme.
*
* @param string $theme Theme key (directory name)
* @return array|boolean Theme information, or false if the theme doesn't exist.
*/
public function getThemeInfo($theme) {
if (!is_dir(THEME . '/' . $theme)) {
return false;
}
$themeIniFile = THEME . '/' . $theme . '/theme.ini';
$themeInfo = array();
if (is_file($themeIniFile) && is_readable($themeIniFile)) {
$themeInfo = parse_ini_file($themeIniFile);
}
if ($themeInfo === false) {
$themeInfo = array();
}
if (!isset($themeInfo['name'])) {
$themeInfo['name'] = $theme;
}
$themeInfo['current'] = ($theme === $this->getTheme());
return $themeInfo;
}
public function getInstalledThemes() {
$handle = opendir(THEME);
$themes = array();
while (($theme = readdir($handle)) !== false) {
# Themes are stored in a directory, so all directory names are themes
# @todo move theme installation data to database
if (!is_dir(THEME . '/' . $theme) || in_array($theme, array('.', '..'))) {
continue;
}
$themes[$theme] = $this->getThemeInfo($theme);
}
ksort($themes);
return $themes;
}
public function getLanguage() {
return $this->currentLanguage;
}
public function getInstalledLanguages() {
$handle = opendir(LOCALE);
$languages = array();
while (($language = readdir($handle)) !== false) {
# Languages are stored in a directory, so all directory names are languages
# @todo move language installation data to database
if (! is_dir(LOCALE . '/' . $language) || in_array($language, array('..', '.', 'tools'))) {
continue;
}
$current = false;
if ($language === $this->getLanguage()) {
$current = true;
}
$languages[] = array('name' => (isset($this->language_names[$language]) ? $this->language_names[$language] : $language), 'value' => $language, 'current' => $current);
}
return $languages;
}
public function getDefaultConfig()
{
return array(
@ -437,7 +165,7 @@ class Poche @@ -437,7 +165,7 @@ class Poche
if ( $last_id ) {
Tools::logm('add link ' . $url->getUrl());
if (DOWNLOAD_PICTURES) {
$content = filtre_picture($body, $url->getUrl(), $last_id);
$content = Picture::filterPicture($body, $url->getUrl(), $last_id);
Tools::logm('updating content article');
$this->store->updateContent($last_id, $content, $this->user->getId());
}
@ -472,7 +200,7 @@ class Poche @@ -472,7 +200,7 @@ class Poche
$msg = 'delete link #' . $id;
if ($this->store->deleteById($id, $this->user->getId())) {
if (DOWNLOAD_PICTURES) {
remove_directory(ABS_PATH . $id);
Picture::removeDirectory(ABS_PATH . $id);
}
$this->messages->add('s', _('the link has been deleted successfully'));
}
@ -598,8 +326,8 @@ class Poche @@ -598,8 +326,8 @@ class Poche
$check_time_prod = date('d-M-Y H:i', $prod_infos[1]);
$compare_dev = version_compare(POCHE, $dev);
$compare_prod = version_compare(POCHE, $prod);
$themes = $this->getInstalledThemes();
$languages = $this->getInstalledLanguages();
$themes = $this->tpl->getInstalledThemes();
$languages = $this->language->getInstalledLanguages();
$token = $this->user->getConfigValue('token');
$http_auth = (isset($_SERVER['PHP_AUTH_USER']) || isset($_SERVER['REMOTE_USER'])) ? true : false;
$only_user = ($this->store->listUsers() > 1) ? false : true;
@ -703,7 +431,7 @@ class Poche @@ -703,7 +431,7 @@ class Poche
'listmode' => (isset($_COOKIE['listmode']) ? true : false),
);
//if id is given - we retrive entries by tag: id is tag id
//if id is given - we retrieve entries by tag: id is tag id
if ($id) {
$tpl_vars['tag'] = $this->store->retrieveTag($id, $this->user->getId());
$tpl_vars['id'] = intval($id);
@ -757,85 +485,6 @@ class Poche @@ -757,85 +485,6 @@ class Poche
}
}
public function updateTheme()
{
# no data
if (empty($_POST['theme'])) {
}
# we are not going to change it to the current theme...
if ($_POST['theme'] == $this->getTheme()) {
$this->messages->add('w', _('still using the "' . $this->getTheme() . '" theme!'));
Tools::redirect('?view=config');
}
$themes = $this->getInstalledThemes();
$actualTheme = false;
foreach (array_keys($themes) as $theme) {
if ($theme == $_POST['theme']) {
$actualTheme = true;
break;
}
}
if (! $actualTheme) {
$this->messages->add('e', _('that theme does not seem to be installed'));
Tools::redirect('?view=config');
}
$this->store->updateUserConfig($this->user->getId(), 'theme', $_POST['theme']);
$this->messages->add('s', _('you have changed your theme preferences'));
$currentConfig = $_SESSION['poche_user']->config;
$currentConfig['theme'] = $_POST['theme'];
$_SESSION['poche_user']->setConfig($currentConfig);
$this->emptyCache();
Tools::redirect('?view=config');
}
public function updateLanguage()
{
# no data
if (empty($_POST['language'])) {
}
# we are not going to change it to the current language...
if ($_POST['language'] == $this->getLanguage()) {
$this->messages->add('w', _('still using the "' . $this->getLanguage() . '" language!'));
Tools::redirect('?view=config');
}
$languages = $this->getInstalledLanguages();
$actualLanguage = false;
foreach ($languages as $language) {
if ($language['value'] == $_POST['language']) {
$actualLanguage = true;
break;
}
}
if (! $actualLanguage) {
$this->messages->add('e', _('that language does not seem to be installed'));
Tools::redirect('?view=config');
}
$this->store->updateUserConfig($this->user->getId(), 'language', $_POST['language']);
$this->messages->add('s', _('you have changed your language preferences'));
$currentConfig = $_SESSION['poche_user']->config;
$currentConfig['language'] = $_POST['language'];
$_SESSION['poche_user']->setConfig($currentConfig);
$this->emptyCache();
Tools::redirect('?view=config');
}
/**
* get credentials from differents sources
* it redirects the user to the $referer link

149
inc/poche/Routing.class.php

@ -0,0 +1,149 @@ @@ -0,0 +1,149 @@
<?php
/**
* wallabag, self hostable application allowing you to not miss any content anymore
*
* @category wallabag
* @author Nicolas Lœuillet <nicolas@loeuillet.org>
* @copyright 2013
* @license http://opensource.org/licenses/MIT see COPYING file
*/
class Routing
{
protected $wallabag;
protected $referer;
protected $view;
protected $action;
protected $id;
protected $url;
protected $file;
protected $defaultVars = array();
protected $vars = array();
public function __construct(Poche $wallabag)
{
$this->wallabag = $wallabag;
$this->_init();
}
private function _init()
{
# Parse GET & REFERER vars
$this->referer = empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER'];
$this->view = Tools::checkVar('view', 'home');
$this->action = Tools::checkVar('action');
$this->id = Tools::checkVar('id');
$_SESSION['sort'] = Tools::checkVar('sort', 'id');
$this->url = new Url((isset ($_GET['url'])) ? $_GET['url'] : '');
}
public function run()
{
# vars to _always_ send to templates
$this->defaultVars = array(
'referer' => $this->referer,
'view' => $this->view,
'poche_url' => Tools::getPocheUrl(),
'title' => _('wallabag, a read it later open source system'),
'token' => \Session::getToken(),
'theme' => $this->wallabag->tpl->getTheme()
);
$this->_launchAction();
$this->_defineTplInformation();
# because messages can be added in $poche->action(), we have to add this entry now (we can add it before)
$this->vars = array_merge($this->vars, array('messages' => $this->wallabag->messages->display('all', FALSE)));
$this->_render($this->file, $this->vars);
}
private function _defineTplInformation()
{
$tplFile = array();
$tplVars = array();
if (\Session::isLogged()) {
$this->wallabag->action($this->action, $this->url, $this->id);
$tplFile = Tools::getTplFile($this->view);
$tplVars = array_merge($this->vars, $this->wallabag->displayView($this->view, $this->id));
} elseif(isset($_SERVER['PHP_AUTH_USER'])) {
if($this->wallabag->store->userExists($_SERVER['PHP_AUTH_USER'])) {
$this->wallabag->login($this->referer);
} else {
$this->wallabag->messages->add('e', _('login failed: user doesn\'t exist'));
Tools::logm('user doesn\'t exist');
$tplFile = Tools::getTplFile('login');
$tplVars['http_auth'] = 1;
}
} elseif(isset($_SERVER['REMOTE_USER'])) {
if($this->wallabag->store->userExists($_SERVER['REMOTE_USER'])) {
$this->wallabag->login($this->referer);
} else {
$this->wallabag->messages->add('e', _('login failed: user doesn\'t exist'));
Tools::logm('user doesn\'t exist');
$tplFile = Tools::getTplFile('login');
$tplVars['http_auth'] = 1;
}
} else {
$tplFile = Tools::getTplFile('login');
$tplVars['http_auth'] = 0;
\Session::logout();
}
$this->file = $tplFile;
$this->vars = array_merge($this->defaultVars, $tplVars);
}
private function _launchAction()
{
if (isset($_GET['login'])) {
// hello you
$this->wallabag->login($this->referer);
} elseif (isset($_GET['logout'])) {
// see you soon !
$this->wallabag->logout();
} elseif (isset($_GET['config'])) {
// update password
$this->wallabag->updatePassword();
} elseif (isset($_GET['newuser'])) {
$this->wallabag->createNewUser();
} elseif (isset($_GET['deluser'])) {
$this->wallabag->deleteUser();
} elseif (isset($_GET['epub'])) {
$this->wallabag->createEpub();
} elseif (isset($_GET['import'])) {
$import = $this->wallabag->import();
$tplVars = array_merge($this->vars, $import);
} elseif (isset($_GET['download'])) {
Tools::downloadDb();
} elseif (isset($_GET['empty-cache'])) {
$this->wallabag->emptyCache();
} elseif (isset($_GET['export'])) {
$this->wallabag->export();
} elseif (isset($_GET['updatetheme'])) {
$this->wallabag->tpl->updateTheme($_POST['theme']);
} elseif (isset($_GET['updatelanguage'])) {
$this->wallabag->language->updateLanguage($_POST['language']);
} elseif (isset($_GET['uploadfile'])) {
$this->wallabag->uploadFile();
} elseif (isset($_GET['feed'])) {
if (isset($_GET['action']) && $_GET['action'] == 'generate') {
$this->wallabag->generateToken();
}
else {
$tag_id = (isset($_GET['tag_id']) ? intval($_GET['tag_id']) : 0);
$this->wallabag->generateFeeds($_GET['token'], filter_var($_GET['user_id'],FILTER_SANITIZE_NUMBER_INT), $tag_id, $_GET['type']);
}
}
elseif (isset($_GET['plainurl']) && !empty($_GET['plainurl'])) {
$plainUrl = new Url(base64_encode($_GET['plainurl']));
$this->wallabag->action('add', $plainUrl);
}
}
private function _render($file, $vars)
{
echo $this->wallabag->tpl->render($file, $vars);
}
}

236
inc/poche/Template.class.php

@ -0,0 +1,236 @@ @@ -0,0 +1,236 @@
<?php
/**
* wallabag, self hostable application allowing you to not miss any content anymore
*
* @category wallabag
* @author Nicolas Lœuillet <nicolas@loeuillet.org>
* @copyright 2013
* @license http://opensource.org/licenses/MIT see COPYING file
*/
class Template extends Twig_Environment
{
protected $wallabag;
private $canRenderTemplates = TRUE;
private $currentTheme = '';
public function __construct(Poche $wallabag)
{
$this->wallabag = $wallabag;
// Set up theme
$pocheUser = Session::getParam('poche_user');
$themeDirectory = (is_null($pocheUser) ? DEFAULT_THEME : $pocheUser->getConfigValue('theme'));
if ($themeDirectory === false) {
$themeDirectory = DEFAULT_THEME;
}
$this->currentTheme = $themeDirectory;
if ($this->_themeIsInstalled() === array()) {
$this->_init();
}
}
/**
* Returns true if selected theme is installed
*
* @return bool
*/
private function _themeIsInstalled()
{
$errors = array();
// Twig is an absolute requirement for wallabag to function.
// Abort immediately if the Composer installer hasn't been run yet
if (!$this->canRenderTemplates) {
$errors[] = '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.';
}
// Check if the selected theme and its requirements are present
$theme = $this->getTheme();
if ($theme != '' && !is_dir(THEME . '/' . $theme)) {
$errors[] = 'The currently selected theme (' . $theme . ') does not seem to be properly installed (Missing directory: ' . THEME . '/' . $theme . ')';
$this->canRenderTemplates = FALSE;
}
$themeInfo = $this->getThemeInfo($theme);
if (isset($themeInfo['requirements']) && is_array($themeInfo['requirements'])) {
foreach ($themeInfo['requirements'] as $requiredTheme) {
if (! is_dir(THEME . '/' . $requiredTheme)) {
$errors[] = 'The required "' . $requiredTheme . '" theme is missing for the current theme (' . $theme . ')';
$this->canRenderTemplates = FALSE;
}
}
}
$currentErrors = (is_null(Session::getParam('errors'))? array() : Session::getParam('errors'));
Session::setParam('errors', array_merge($errors, $currentErrors));
return $errors;
}
/**
* Initialization for templates
*/
private function _init()
{
$loaderChain = new Twig_Loader_Chain();
$theme = $this->getTheme();
// add the current theme as first to the loader chain
// so Twig will look there first for overridden template files
try {
$loaderChain->addLoader(new Twig_Loader_Filesystem(THEME . '/' . $theme));
} catch (Twig_Error_Loader $e) {
# @todo isInstalled() should catch this, inject Twig later
die('The currently selected theme (' . $theme . ') does not seem to be properly installed (' . THEME . '/' . $theme .' is missing)');
}
// add all required themes to the loader chain
$themeInfo = $this->getThemeInfo($theme);
if (isset($themeInfo['requirements']) && is_array($themeInfo['requirements'])) {
foreach ($themeInfo['requirements'] as $requiredTheme) {
try {
$loaderChain->addLoader(new Twig_Loader_Filesystem(THEME . '/' . $requiredTheme));
} catch (Twig_Error_Loader $e) {
# @todo isInstalled() should catch this, inject Twig later
die('The required "' . $requiredTheme . '" theme is missing for the current theme (' . $theme . ')');
}
}
}
if (DEBUG_POCHE) {
$twigParams = array();
} else {
$twigParams = array('cache' => CACHE);
}
parent::__construct($loaderChain, $twigParams);
//$tpl = new Twig_Environment($loaderChain, $twigParams);
$this->addExtension(new Twig_Extensions_Extension_I18n());
# filter to display domain name of an url
$filter = new Twig_SimpleFilter('getDomain', 'Tools::getDomain');
$this->addFilter($filter);
# filter for reading time
$filter = new Twig_SimpleFilter('getReadingTime', 'Tools::getReadingTime');
$this->addFilter($filter);
}
/**
* Returns current theme
*
* @return string
*/
public function getTheme()
{
return $this->currentTheme;
}
/**
* Provides theme information by parsing theme.ini file if present in the theme's root directory.
* In all cases, the following data will be returned:
* - name: theme's name, or key if the theme is unnamed,
* - current: boolean informing if the theme is the current user theme.
*
* @param string $theme Theme key (directory name)
* @return array|boolean Theme information, or false if the theme doesn't exist.
*/
public function getThemeInfo($theme)
{
if (!is_dir(THEME . '/' . $theme)) {
return false;
}
$themeIniFile = THEME . '/' . $theme . '/theme.ini';
$themeInfo = array();
if (is_file($themeIniFile) && is_readable($themeIniFile)) {
$themeInfo = parse_ini_file($themeIniFile);
}
if ($themeInfo === false) {
$themeInfo = array();
}
if (!isset($themeInfo['name'])) {
$themeInfo['name'] = $theme;
}
$themeInfo['current'] = ($theme === $this->getTheme());
return $themeInfo;
}
/**
* Returns an array with installed themes
*
* @return array
*/
public function getInstalledThemes()
{
$handle = opendir(THEME);
$themes = array();
while (($theme = readdir($handle)) !== false) {
# Themes are stored in a directory, so all directory names are themes
# @todo move theme installation data to database
if (!is_dir(THEME . '/' . $theme) || in_array($theme, array('.', '..'))) {
continue;
}
$themes[$theme] = $this->getThemeInfo($theme);
}
ksort($themes);
return $themes;
}
/**
* Update theme for the current user
*
* @param $newTheme
*/
public function updateTheme($newTheme)
{
# we are not going to change it to the current theme...
if ($newTheme == $this->getTheme()) {
$this->wallabag->messages->add('w', _('still using the "' . $this->getTheme() . '" theme!'));
Tools::redirect('?view=config');
}
$themes = $this->getInstalledThemes();
$actualTheme = false;
foreach (array_keys($themes) as $theme) {
if ($theme == $newTheme) {
$actualTheme = true;
break;
}
}
if (!$actualTheme) {
$this->wallabag->messages->add('e', _('that theme does not seem to be installed'));
Tools::redirect('?view=config');
}
$this->wallabag->store->updateUserConfig($this->wallabag->user->getId(), 'theme', $newTheme);
$this->wallabag->messages->add('s', _('you have changed your theme preferences'));
$currentConfig = $_SESSION['poche_user']->config;
$currentConfig['theme'] = $newTheme;
$_SESSION['poche_user']->setConfig($currentConfig);
$this->wallabag->emptyCache();
Tools::redirect('?view=config');
}
}

156
inc/poche/Tools.class.php

@ -5,19 +5,23 @@ @@ -5,19 +5,23 @@
* @category wallabag
* @author Nicolas Lœuillet <nicolas@loeuillet.org>
* @copyright 2013
* @license http://www.wtfpl.net/ see COPYING file
* @license http://opensource.org/licenses/MIT see COPYING file
*/
class Tools
final class Tools
{
private function __construct()
{
}
/**
* Initialize PHP environment
*/
public static function initPhp()
{
define('START_TIME', microtime(true));
if (phpversion() < 5) {
die(_('Oops, it seems you don\'t have PHP 5.'));
}
function stripslashesDeep($value) {
return is_array($value)
? array_map('stripslashesDeep', $value)
@ -34,6 +38,11 @@ class Tools @@ -34,6 +38,11 @@ class Tools
register_shutdown_function('ob_end_flush');
}
/**
* Get wallabag instance URL
*
* @return string
*/
public static function getPocheUrl()
{
$https = (!empty($_SERVER['HTTPS'])
@ -67,6 +76,11 @@ class Tools @@ -67,6 +76,11 @@ class Tools
. $host . $serverport . $scriptname;
}
/**
* Redirects to a URL
*
* @param string $url
*/
public static function redirect($url = '')
{
if ($url === '') {
@ -87,11 +101,18 @@ class Tools @@ -87,11 +101,18 @@ class Tools
$url = $ref;
}
}
self::logm('redirect to ' . $url);
header('Location: '.$url);
exit();
}
/**
* Returns name of the template file to display
*
* @param $view
* @return string
*/
public static function getTplFile($view)
{
$views = array(
@ -99,13 +120,15 @@ class Tools @@ -99,13 +120,15 @@ class Tools
'edit-tags', 'view', 'login', 'error'
);
if (in_array($view, $views)) {
return $view . '.twig';
}
return 'home.twig';
return (in_array($view, $views) ? $view . '.twig' : 'home.twig');
}
/**
* Download a file (typically, for downloading pictures on web server)
*
* @param $url
* @return bool|mixed|string
*/
public static function getFile($url)
{
$timeout = 15;
@ -186,6 +209,11 @@ class Tools @@ -186,6 +209,11 @@ class Tools
}
}
/**
* Headers for JSON export
*
* @param $data
*/
public static function renderJson($data)
{
header('Cache-Control: no-cache, must-revalidate');
@ -195,6 +223,11 @@ class Tools @@ -195,6 +223,11 @@ class Tools
exit();
}
/**
* Create new line in log file
*
* @param $message
*/
public static function logm($message)
{
if (DEBUG_POCHE && php_sapi_name() != 'cli') {
@ -204,36 +237,57 @@ class Tools @@ -204,36 +237,57 @@ class Tools
}
}
/**
* Encode a URL by using a salt
*
* @param $string
* @return string
*/
public static function encodeString($string)
{
return sha1($string . SALT);
}
/**
* Cleans a variable
*
* @param $var
* @param string $default
* @return string
*/
public static function checkVar($var, $default = '')
{
return ((isset ($_REQUEST["$var"])) ? htmlentities($_REQUEST["$var"]) : $default);
return ((isset($_REQUEST["$var"])) ? htmlentities($_REQUEST["$var"]) : $default);
}
/**
* Returns the domain name for a URL
*
* @param $url
* @return string
*/
public static function getDomain($url)
{
return parse_url($url, PHP_URL_HOST);
}
public static function getReadingTime($text) {
$word = str_word_count(strip_tags($text));
$minutes = floor($word / 200);
$seconds = floor($word % 200 / (200 / 60));
$time = array('minutes' => $minutes, 'seconds' => $seconds);
return $minutes;
}
public static function getDocLanguage($userlanguage) {
$lang = explode('.', $userlanguage);
return str_replace('_', '-', $lang[0]);
/**
* For a given text, we calculate reading time for an article
*
* @param $text
* @return float
*/
public static function getReadingTime($text)
{
return floor(str_word_count(strip_tags($text)) / 200);
}
public static function status($status_code)
/**
* Returns the correct header for a status code
*
* @param $status_code
*/
private static function _status($status_code)
{
if (strpos(php_sapi_name(), 'apache') !== false) {
@ -245,9 +299,13 @@ class Tools @@ -245,9 +299,13 @@ class Tools
}
}
public static function download_db() {
/**
* Download the sqlite database
*/
public static function downloadDb()
{
header('Content-Disposition: attachment; filename="poche.sqlite.gz"');
self::status(200);
self::_status(200);
header('Content-Transfer-Encoding: binary');
header('Content-Type: application/octet-stream');
@ -256,18 +314,24 @@ class Tools @@ -256,18 +314,24 @@ class Tools
exit;
}
/**
* Get the content for a given URL (by a call to FullTextFeed)
*
* @param Url $url
* @return mixed
*/
public static function getPageContent(Url $url)
{
// Saving and clearing context
$REAL = array();
foreach( $GLOBALS as $key => $value ) {
if( $key != 'GLOBALS' && $key != '_SESSION' && $key != 'HTTP_SESSION_VARS' ) {
$GLOBALS[$key] = array();
$REAL[$key] = $value;
$GLOBALS[$key] = array();
$REAL[$key] = $value;
}
}
// Saving and clearing session
if ( isset($_SESSION) ) {
if (isset($_SESSION)) {
$REAL_SESSION = array();
foreach( $_SESSION as $key => $value ) {
$REAL_SESSION[$key] = $value;
@ -279,12 +343,12 @@ class Tools @@ -279,12 +343,12 @@ class Tools
$scope = function() {
extract( func_get_arg(1) );
$_GET = $_REQUEST = array(
"url" => $url->getUrl(),
"max" => 5,
"links" => "preserve",
"exc" => "",
"format" => "json",
"submit" => "Create Feed"
"url" => $url->getUrl(),
"max" => 5,
"links" => "preserve",
"exc" => "",
"format" => "json",
"submit" => "Create Feed"
);
ob_start();
require func_get_arg(0);
@ -292,23 +356,26 @@ class Tools @@ -292,23 +356,26 @@ class Tools
ob_end_clean();
return $json;
};
$json = $scope( "inc/3rdparty/makefulltextfeed.php", array("url" => $url) );
$json = $scope("inc/3rdparty/makefulltextfeed.php", array("url" => $url));
// Clearing and restoring context
foreach( $GLOBALS as $key => $value ) {
if( $key != "GLOBALS" && $key != "_SESSION" ) {
foreach ($GLOBALS as $key => $value) {
if($key != "GLOBALS" && $key != "_SESSION" ) {
unset($GLOBALS[$key]);
}
}
foreach( $REAL as $key => $value ) {
foreach ($REAL as $key => $value) {
$GLOBALS[$key] = $value;
}
// Clearing and restoring session
if ( isset($REAL_SESSION) ) {
foreach( $_SESSION as $key => $value ) {
if (isset($REAL_SESSION)) {
foreach($_SESSION as $key => $value) {
unset($_SESSION[$key]);
}
foreach( $REAL_SESSION as $key => $value ) {
foreach($REAL_SESSION as $key => $value) {
$_SESSION[$key] = $value;
}
}
@ -318,11 +385,12 @@ class Tools @@ -318,11 +385,12 @@ class Tools
/**
* Returns whether we handle an AJAX (XMLHttpRequest) request.
*
* @return boolean whether we handle an AJAX (XMLHttpRequest) request.
*/
public static function isAjaxRequest()
{
return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH']==='XMLHttpRequest';
return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH']==='XMLHttpRequest';
}
}

2
inc/poche/Url.class.php

<
@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
* @category wallabag
* @author Nicolas Lœuillet <nicolas@loeuillet.org>
* @copyright 2013
* @license http://www.wtfpl.net/ see COPYING file
* @license http://opensource.org/licenses/MIT see COPYING file
*/