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

This commit is contained in:
Nicolas Lœuillet 2014-07-11 16:03:59 +02:00
parent 6400371ff9
commit 3602405ec0
20 changed files with 930 additions and 771 deletions

View file

@ -1,14 +1,19 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE Copyright (c) 2013-2014 Nicolas Lœuillet
Version 2, December 2004
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 The above copyright notice and this permission notice shall be included in all
copies of this license document, and changing it is allowed as long copies or substantial portions of the Software.
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 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.

View file

@ -1,7 +1,6 @@
wallabag is based on : wallabag is based on :
* PHP Readability https://bitbucket.org/fivefilters/php-readability * PHP Readability https://bitbucket.org/fivefilters/php-readability
* Full Text RSS http://code.fivefilters.org/full-text-rss/src * 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 * logo by Maylis Agniel https://github.com/wallabag/logo
* icons http://icomoon.io * icons http://icomoon.io
* PHP Simple HTML DOM Parser (for Pocket import) http://simplehtmldom.sourceforge.net/ * PHP Simple HTML DOM Parser (for Pocket import) http://simplehtmldom.sourceforge.net/
@ -10,6 +9,6 @@ wallabag is based on :
* Flash messages https://github.com/plasticbrain/PHP-Flash-Messages * Flash messages https://github.com/plasticbrain/PHP-Flash-Messages
* Pagination https://github.com/daveismyname/pagination * 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 Contributors : https://github.com/wallabag/wallabag/graphs/contributors

View file

@ -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) More informations on our website: [wallabag.org](http://wallabag.org)
## License ## 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 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, terms of the MIT License. See the COPYING file for more details.
as published by Sam Hocevar. See the COPYING file for more details.

View file

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

View file

@ -1,25 +1,32 @@
<?php <?php
/* /**
* Class for Flattr querying * wallabag, self hostable application allowing you to not miss any content anymore
*/ *
class FlattrItem { * @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 $status;
public $urltoflattr; public $urltoflattr;
public $flattrItemURL; public $flattrItemURL;
public $numflattrs; public $numflattrs;
public function checkItem($urltoflattr,$id) { public function checkItem($urltoflattr, $id)
$this->cacheflattrfile($urltoflattr, $id); {
$this->_cacheflattrfile($urltoflattr, $id);
$flattrResponse = file_get_contents(CACHE . "/flattr/".$id.".cache"); $flattrResponse = file_get_contents(CACHE . "/flattr/".$id.".cache");
if($flattrResponse != FALSE) { if($flattrResponse != FALSE) {
$result = json_decode($flattrResponse); $result = json_decode($flattrResponse);
if (isset($result->message)){ if (isset($result->message)) {
if ($result->message == "flattrable") { if ($result->message == "flattrable") {
$this->status = FLATTRABLE; $this->status = FLATTRABLE;
} }
} }
elseif (is_object($result) && $result->link) { elseif (is_object($result) && $result->link) {
$this->status = FLATTRED; $this->status = FLATTRED;
$this->flattrItemURL = $result->link; $this->flattrItemURL = $result->link;
$this->numflattrs = $result->flattrs; $this->numflattrs = $result->flattrs;
@ -33,7 +40,8 @@ class FlattrItem {
} }
} }
private function cacheflattrfile($urltoflattr, $id) { private function _cacheflattrfile($urltoflattr, $id)
{
if (!is_dir(CACHE . '/flattr')) { if (!is_dir(CACHE . '/flattr')) {
mkdir(CACHE . '/flattr', 0777); mkdir(CACHE . '/flattr', 0777);
} }

View file

@ -309,4 +309,38 @@ class Session
return true; // User is not banned. 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;
}
} }

View file

@ -5,7 +5,7 @@
* @category wallabag * @category wallabag
* @author Nicolas Lœuillet <nicolas@loeuillet.org> * @author Nicolas Lœuillet <nicolas@loeuillet.org>
* @copyright 2013 * @copyright 2013
* @license http://www.wtfpl.net/ see COPYING file * @license http://opensource.org/licenses/MIT see COPYING file
*/ */
class Database { class Database {
@ -38,6 +38,7 @@ class Database {
} }
$this->handle->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $this->handle->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->_checkTags();
Tools::logm('storage type ' . STORAGE); Tools::logm('storage type ' . STORAGE);
} }
@ -45,21 +46,7 @@ class Database {
return $this->handle; return $this->handle;
} }
public function isInstalled() { private function _checkTags() {
$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() {
if (STORAGE == 'sqlite') { if (STORAGE == 'sqlite') {
$sql = ' $sql = '

View file

@ -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');
}
}

View file

@ -5,244 +5,77 @@
* @category wallabag * @category wallabag
* @author Nicolas Lœuillet <nicolas@loeuillet.org> * @author Nicolas Lœuillet <nicolas@loeuillet.org>
* @copyright 2013 * @copyright 2013
* @license http://www.wtfpl.net/ see COPYING file * @license http://opensource.org/licenses/MIT see COPYING file
*/ */
class Poche class Poche
{ {
public static $canRenderTemplates = true; /**
public static $configFileAvailable = true; * @var User
*/
public $user; public $user;
/**
* @var Database
*/
public $store; public $store;
/**
* @var Template
*/
public $tpl; public $tpl;
/**
* @var Language
*/
public $language;
/**
* @var Routing
*/
public $routing;
/**
* @var Messages
*/
public $messages; public $messages;
/**
* @var Paginator
*/
public $pagination; 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() public function __construct()
{ {
if ($this->configFileIsAvailable()) { $this->init();
$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();
}
} }
private function init() private function init()
{ {
Tools::initPhp(); Tools::initPhp();
if (isset($_SESSION['poche_user']) && $_SESSION['poche_user'] != array()) { $pocheUser = Session::getParam('poche_user');
$this->user = $_SESSION['poche_user'];
if ($pocheUser && $pocheUser != array()) {
$this->user = $pocheUser;
} else { } else {
# fake user, just for install & login screens // fake user, just for install & login screens
$this->user = new User(); $this->user = new User();
$this->user->setConfig($this->getDefaultConfig()); $this->user->setConfig($this->getDefaultConfig());
} }
# l10n $this->pagination = new Paginator($this->user->getConfigValue('pager'), 'p');
$language = $this->user->getConfigValue('language'); $this->language = new Language($this);
@putenv('LC_ALL=' . $language); $this->tpl = new Template($this);
setlocale(LC_ALL, $language); $this->store = new Database();
bindtextdomain($language, LOCALE); $this->messages = new Messages();
textdomain($language); $this->routing = new Routing($this);
# 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() { public function run()
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.'; $this->routing->run();
return false;
}
return true;
}
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;
} }
/** /**
* all checks before installation. * Creates a new user
* @todo move HTML to template
* @return boolean
*/ */
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 (isset($_GET['newuser'])){
if ($_POST['newusername'] != "" && $_POST['password4newuser'] != ""){ if ($_POST['newusername'] != "" && $_POST['password4newuser'] != ""){
$newusername = filter_var($_POST['newusername'], FILTER_SANITIZE_STRING); $newusername = filter_var($_POST['newusername'], FILTER_SANITIZE_STRING);
@ -266,7 +99,11 @@ class Poche
} }
} }
public function deleteUser(){ /**
* Delete an existing user
*/
public function deleteUser()
{
if (isset($_GET['deluser'])){ if (isset($_GET['deluser'])){
if ($this->store->listUsers() > 1) { if ($this->store->listUsers() > 1) {
if (Tools::encodeString($_POST['password4deletinguser'].$this->user->getUsername()) == $this->store->getUserPassword($this->user->getId())) { if (Tools::encodeString($_POST['password4deletinguser'].$this->user->getUsername()) == $this->store->getUserPassword($this->user->getId())) {
@ -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() public function getDefaultConfig()
{ {
return array( return array(
@ -437,7 +165,7 @@ class Poche
if ( $last_id ) { if ( $last_id ) {
Tools::logm('add link ' . $url->getUrl()); Tools::logm('add link ' . $url->getUrl());
if (DOWNLOAD_PICTURES) { if (DOWNLOAD_PICTURES) {
$content = filtre_picture($body, $url->getUrl(), $last_id); $content = Picture::filterPicture($body, $url->getUrl(), $last_id);
Tools::logm('updating content article'); Tools::logm('updating content article');
$this->store->updateContent($last_id, $content, $this->user->getId()); $this->store->updateContent($last_id, $content, $this->user->getId());
} }
@ -472,7 +200,7 @@ class Poche
$msg = 'delete link #' . $id; $msg = 'delete link #' . $id;
if ($this->store->deleteById($id, $this->user->getId())) { if ($this->store->deleteById($id, $this->user->getId())) {
if (DOWNLOAD_PICTURES) { if (DOWNLOAD_PICTURES) {
remove_directory(ABS_PATH . $id); Picture::removeDirectory(ABS_PATH . $id);
} }
$this->messages->add('s', _('the link has been deleted successfully')); $this->messages->add('s', _('the link has been deleted successfully'));
} }
@ -598,8 +326,8 @@ class Poche
$check_time_prod = date('d-M-Y H:i', $prod_infos[1]); $check_time_prod = date('d-M-Y H:i', $prod_infos[1]);
$compare_dev = version_compare(POCHE, $dev); $compare_dev = version_compare(POCHE, $dev);
$compare_prod = version_compare(POCHE, $prod); $compare_prod = version_compare(POCHE, $prod);
$themes = $this->getInstalledThemes(); $themes = $this->tpl->getInstalledThemes();
$languages = $this->getInstalledLanguages(); $languages = $this->language->getInstalledLanguages();
$token = $this->user->getConfigValue('token'); $token = $this->user->getConfigValue('token');
$http_auth = (isset($_SERVER['PHP_AUTH_USER']) || isset($_SERVER['REMOTE_USER'])) ? true : false; $http_auth = (isset($_SERVER['PHP_AUTH_USER']) || isset($_SERVER['REMOTE_USER'])) ? true : false;
$only_user = ($this->store->listUsers() > 1) ? false : true; $only_user = ($this->store->listUsers() > 1) ? false : true;
@ -703,7 +431,7 @@ class Poche
'listmode' => (isset($_COOKIE['listmode']) ? true : false), '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) { if ($id) {
$tpl_vars['tag'] = $this->store->retrieveTag($id, $this->user->getId()); $tpl_vars['tag'] = $this->store->retrieveTag($id, $this->user->getId());
$tpl_vars['id'] = intval($id); $tpl_vars['id'] = intval($id);
@ -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 * get credentials from differents sources
* it redirects the user to the $referer link * it redirects the user to the $referer link

149
inc/poche/Routing.class.php Normal file
View file

@ -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);
}
}

View file

@ -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');
}
}

View file

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

View file

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

View file

@ -5,7 +5,7 @@
* @category wallabag * @category wallabag
* @author Nicolas Lœuillet <nicolas@loeuillet.org> * @author Nicolas Lœuillet <nicolas@loeuillet.org>
* @copyright 2013 * @copyright 2013
* @license http://www.wtfpl.net/ see COPYING file * @license http://opensource.org/licenses/MIT see COPYING file
*/ */
class User class User
@ -44,7 +44,14 @@ class User
$this->config = $config; $this->config = $config;
} }
public function getConfigValue($name) { /**
* Returns configuration entry for a user
*
* @param $name
* @return bool
*/
public function getConfigValue($name)
{
return (isset($this->config[$name])) ? $this->config[$name] : FALSE; return (isset($this->config[$name])) ? $this->config[$name] : FALSE;
} }
} }

View file

@ -5,7 +5,7 @@
* @category wallabag * @category wallabag
* @author Nicolas Lœuillet <nicolas@loeuillet.org> * @author Nicolas Lœuillet <nicolas@loeuillet.org>
* @copyright 2013 * @copyright 2013
* @license http://www.wtfpl.net/ see COPYING file * @license http://opensource.org/licenses/MIT see COPYING file
*/ */
@define ('SALT', ''); # put a strong string here @define ('SALT', ''); # put a strong string here

View file

@ -5,7 +5,7 @@
* @category wallabag * @category wallabag
* @author Nicolas Lœuillet <nicolas@loeuillet.org> * @author Nicolas Lœuillet <nicolas@loeuillet.org>
* @copyright 2013 * @copyright 2013
* @license http://www.wtfpl.net/ see COPYING file * @license http://opensource.org/licenses/MIT see COPYING file
*/ */
# the poche system root directory (/inc) # the poche system root directory (/inc)
@ -18,6 +18,10 @@ require_once INCLUDES . '/poche/Tools.class.php';
require_once INCLUDES . '/poche/User.class.php'; require_once INCLUDES . '/poche/User.class.php';
require_once INCLUDES . '/poche/Url.class.php'; require_once INCLUDES . '/poche/Url.class.php';
require_once INCLUDES . '/3rdparty/class.messages.php'; require_once INCLUDES . '/3rdparty/class.messages.php';
require_once ROOT . '/vendor/autoload.php';
require_once INCLUDES . '/poche/Template.class.php';
require_once INCLUDES . '/poche/Language.class.php';
require_once INCLUDES . '/poche/Routing.class.php';
require_once INCLUDES . '/poche/Poche.class.php'; require_once INCLUDES . '/poche/Poche.class.php';
require_once INCLUDES . '/poche/Database.class.php'; require_once INCLUDES . '/poche/Database.class.php';
@ -36,22 +40,11 @@ require_once INCLUDES . '/3rdparty/libraries/PHPePub/Logger.php';
require_once INCLUDES . '/3rdparty/libraries/PHPePub/EPub.php'; require_once INCLUDES . '/3rdparty/libraries/PHPePub/EPub.php';
require_once INCLUDES . '/3rdparty/libraries/PHPePub/EPubChapterSplitter.php'; require_once INCLUDES . '/3rdparty/libraries/PHPePub/EPubChapterSplitter.php';
# Composer its autoloader for automatically loading Twig
if (! file_exists(ROOT . '/vendor/autoload.php')) {
Poche::$canRenderTemplates = false;
} else {
require_once ROOT . '/vendor/autoload.php';
}
# system configuration; database credentials et caetera # system configuration; database credentials et caetera
if (! file_exists(INCLUDES . '/poche/config.inc.php')) { require_once INCLUDES . '/poche/config.inc.php';
Poche::$configFileAvailable = false; require_once INCLUDES . '/poche/config.inc.default.php';
} else {
require_once INCLUDES . '/poche/config.inc.php';
require_once INCLUDES . '/poche/config.inc.default.php';
}
if (Poche::$configFileAvailable && DOWNLOAD_PICTURES) { if (DOWNLOAD_PICTURES) {
require_once INCLUDES . '/poche/pochePictures.php'; require_once INCLUDES . '/poche/pochePictures.php';
} }

View file

@ -5,154 +5,169 @@
* @category wallabag * @category wallabag
* @author Nicolas Lœuillet <nicolas@loeuillet.org> * @author Nicolas Lœuillet <nicolas@loeuillet.org>
* @copyright 2013 * @copyright 2013
* @license http://www.wtfpl.net/ see COPYING file * @license http://opensource.org/licenses/MIT see COPYING file
*/ */
/**
* On modifie les URLS des images dans le corps de l'article final class Picture
*/
function filtre_picture($content, $url, $id)
{ {
$matches = array(); private function __construct()
$processing_pictures = array(); // list of processing image to avoid processing the same pictures twice {
preg_match_all('#<\s*(img)[^>]+src="([^"]*)"[^>]*>#Si', $content, $matches, PREG_SET_ORDER);
foreach($matches as $i => $link) { }
$link[1] = trim($link[1]);
if (!preg_match('#^(([a-z]+://)|(\#))#', $link[1])) { /**
$absolute_path = get_absolute_link($link[2],$url); * Changing pictures URL in article content
$filename = basename(parse_url($absolute_path, PHP_URL_PATH)); */
$directory = create_assets_directory($id); public static function filterPicture($content, $url, $id)
$fullpath = $directory . '/' . $filename; {
$matches = array();
if (in_array($absolute_path, $processing_pictures) === true) { $processing_pictures = array(); // list of processing image to avoid processing the same pictures twice
// replace picture's URL only if processing is OK : already processing -> go to next picture preg_match_all('#<\s*(img)[^>]+src="([^"]*)"[^>]*>#Si', $content, $matches, PREG_SET_ORDER);
continue; foreach($matches as $i => $link) {
$link[1] = trim($link[1]);
if (!preg_match('#^(([a-z]+://)|(\#))#', $link[1])) {
$absolute_path = self::_getAbsoluteLink($link[2], $url);
$filename = basename(parse_url($absolute_path, PHP_URL_PATH));
$directory = self::_createAssetsDirectory($id);
$fullpath = $directory . '/' . $filename;
if (in_array($absolute_path, $processing_pictures) === true) {
// replace picture's URL only if processing is OK : already processing -> go to next picture
continue;
}
if (self::_downloadPictures($absolute_path, $fullpath) === true) {
$content = str_replace($matches[$i][2], $fullpath, $content);
}
$processing_pictures[] = $absolute_path;
} }
if (download_pictures($absolute_path, $fullpath) === true) {
$content = str_replace($matches[$i][2], $fullpath, $content);
}
$processing_pictures[] = $absolute_path;
} }
return $content;
} }
return $content; /**
} * Get absolute URL
*/
private static function _getAbsoluteLink($relativeLink, $url)
{
/* return if already absolute URL */
if (parse_url($relativeLink, PHP_URL_SCHEME) != '') return $relativeLink;
/** /* queries and anchors */
* Retourne le lien absolu if ($relativeLink[0]=='#' || $relativeLink[0]=='?') return $url . $relativeLink;
*/
function get_absolute_link($relative_link, $url) {
/* return if already absolute URL */
if (parse_url($relative_link, PHP_URL_SCHEME) != '') return $relative_link;
/* queries and anchors */ /* parse base URL and convert to local variables:
if ($relative_link[0]=='#' || $relative_link[0]=='?') return $url . $relative_link; $scheme, $host, $path */
extract(parse_url($url));
/* parse base URL and convert to local variables: /* remove non-directory element from path */
$scheme, $host, $path */ $path = preg_replace('#/[^/]*$#', '', $path);
extract(parse_url($url));
/* remove non-directory element from path */ /* destroy path if relative url points to root */
$path = preg_replace('#/[^/]*$#', '', $path); if ($relativeLink[0] == '/') $path = '';
/* destroy path if relative url points to root */ /* dirty absolute URL */
if ($relative_link[0] == '/') $path = ''; $abs = $host . $path . '/' . $relativeLink;
/* dirty absolute URL */ /* replace '//' or '/./' or '/foo/../' with '/' */
$abs = $host . $path . '/' . $relative_link; $re = array('#(/\.?/)#', '#/(?!\.\.)[^/]+/\.\./#');
for($n=1; $n>0; $abs=preg_replace($re, '/', $abs, -1, $n)) {}
/* replace '//' or '/./' or '/foo/../' with '/' */ /* absolute URL is ready! */
$re = array('#(/\.?/)#', '#/(?!\.\.)[^/]+/\.\./#'); return $scheme.'://'.$abs;
for($n=1; $n>0; $abs=preg_replace($re, '/', $abs, -1, $n)) {}
/* absolute URL is ready! */
return $scheme.'://'.$abs;
}
/**
* Téléchargement des images
*
* @return bool true if the download and processing is OK, false else
*/
function download_pictures($absolute_path, $fullpath)
{
$rawdata = Tools::getFile($absolute_path);
$fullpath = urldecode($fullpath);
if(file_exists($fullpath)) {
unlink($fullpath);
}
// check extension
$file_ext = strrchr($fullpath, '.');
$whitelist = array(".jpg",".jpeg",".gif",".png");
if (!(in_array($file_ext, $whitelist))) {
Tools::logm('processed image with not allowed extension. Skipping ' . $fullpath);
return false;
}
// check headers
$imageinfo = getimagesize($absolute_path);
if ($imageinfo['mime'] != 'image/gif' && $imageinfo['mime'] != 'image/jpeg'&& $imageinfo['mime'] != 'image/jpg'&& $imageinfo['mime'] != 'image/png') {
Tools::logm('processed image with bad header. Skipping ' . $fullpath);
return false;
}
// regenerate image
$im = imagecreatefromstring($rawdata);
if ($im === false) {
Tools::logm('error while regenerating image ' . $fullpath);
return false;
}
switch ($imageinfo['mime']) {
case 'image/gif':
$result = imagegif($im, $fullpath);
break;
case 'image/jpeg':
case 'image/jpg':
$result = imagejpeg($im, $fullpath, REGENERATE_PICTURES_QUALITY);
break;
case 'image/png':
$result = imagepng($im, $fullpath, ceil(REGENERATE_PICTURES_QUALITY / 100 * 9));
break;
}
imagedestroy($im);
return $result;
}
/**
* Crée un répertoire de médias pour l'article
*/
function create_assets_directory($id)
{
$assets_path = ABS_PATH;
if(!is_dir($assets_path)) {
mkdir($assets_path, 0715);
} }
$article_directory = $assets_path . $id; /**
if(!is_dir($article_directory)) { * Downloading pictures
mkdir($article_directory, 0715); *
} * @return bool true if the download and processing is OK, false else
*/
private static function _downloadPictures($absolute_path, $fullpath)
{
$rawdata = Tools::getFile($absolute_path);
$fullpath = urldecode($fullpath);
return $article_directory; if(file_exists($fullpath)) {
} unlink($fullpath);
/**
* Suppression du répertoire d'images
*/
function remove_directory($directory)
{
if(is_dir($directory)) {
$files = array_diff(scandir($directory), array('.','..'));
foreach ($files as $file) {
(is_dir("$directory/$file")) ? remove_directory("$directory/$file") : unlink("$directory/$file");
} }
return rmdir($directory);
// check extension
$file_ext = strrchr($fullpath, '.');
$whitelist = array(".jpg",".jpeg",".gif",".png");
if (!(in_array($file_ext, $whitelist))) {
Tools::logm('processed image with not allowed extension. Skipping ' . $fullpath);
return false;
}
// check headers
$imageinfo = getimagesize($absolute_path);
if ($imageinfo['mime'] != 'image/gif' && $imageinfo['mime'] != 'image/jpeg'&& $imageinfo['mime'] != 'image/jpg'&& $imageinfo['mime'] != 'image/png') {
Tools::logm('processed image with bad header. Skipping ' . $fullpath);
return false;
}
// regenerate image
$im = imagecreatefromstring($rawdata);
if ($im === false) {
Tools::logm('error while regenerating image ' . $fullpath);
return false;
}
switch ($imageinfo['mime']) {
case 'image/gif':
$result = imagegif($im, $fullpath);
break;
case 'image/jpeg':
case 'image/jpg':
$result = imagejpeg($im, $fullpath, REGENERATE_PICTURES_QUALITY);
break;
case 'image/png':
$result = imagepng($im, $fullpath, ceil(REGENERATE_PICTURES_QUALITY / 100 * 9));
break;
}
imagedestroy($im);
return $result;
} }
}
/**
* Create a directory for an article
*
* @param $id ID of the article
* @return string
*/
private static function _createAssetsDirectory($id)
{
$assets_path = ABS_PATH;
if (!is_dir($assets_path)) {
mkdir($assets_path, 0715);
}
$article_directory = $assets_path . $id;
if (!is_dir($article_directory)) {
mkdir($article_directory, 0715);
}
return $article_directory;
}
/**
* Remove the directory
*
* @param $directory
* @return bool
*/
public static function removeDirectory($directory)
{
if (is_dir($directory)) {
$files = array_diff(scandir($directory), array('.','..'));
foreach ($files as $file) {
(is_dir("$directory/$file")) ? self::removeDirectory("$directory/$file") : unlink("$directory/$file");
}
return rmdir($directory);
}
}
}

132
index.php
View file

@ -5,139 +5,21 @@
* @category wallabag * @category wallabag
* @author Nicolas Lœuillet <nicolas@loeuillet.org> * @author Nicolas Lœuillet <nicolas@loeuillet.org>
* @copyright 2013 * @copyright 2013
* @license http://www.wtfpl.net/ see COPYING file * @license http://opensource.org/licenses/MIT see COPYING file
*/ */
define ('POCHE', '1.7.1'); define ('POCHE', '1.8.0');
require 'check_setup.php'; require 'check_setup.php';
require_once 'inc/poche/global.inc.php'; require_once 'inc/poche/global.inc.php';
# Set error reporting level
if (defined('ERROR_REPORTING')) { if (defined('ERROR_REPORTING')) {
error_reporting(ERROR_REPORTING); error_reporting(ERROR_REPORTING);
} }
# Start session // Start session
Session::$sessionName = 'poche'; Session::$sessionName = 'wallabag';
Session::init(); Session::init();
# Start Poche // Let's rock !
$poche = new Poche(); $wallabag = new Poche();
$notInstalledMessage = $poche -> getNotInstalledMessage(); $wallabag->run();
# Parse GET & REFERER vars
$referer = empty($_SERVER['HTTP_REFERER']) ? '' : $_SERVER['HTTP_REFERER'];
$view = Tools::checkVar('view', 'home');
$action = Tools::checkVar('action');
$id = Tools::checkVar('id');
$_SESSION['sort'] = Tools::checkVar('sort', 'id');
$url = new Url((isset ($_GET['url'])) ? $_GET['url'] : '');
# vars to _always_ send to templates
$tpl_vars = array(
'referer' => $referer,
'view' => $view,
'poche_url' => Tools::getPocheUrl(),
'title' => _('wallabag, a read it later open source system'),
'token' => Session::getToken(),
'theme' => $poche->getTheme()
);
if (! empty($notInstalledMessage)) {
if (! Poche::$canRenderTemplates || ! Poche::$configFileAvailable) {
# We cannot use Twig to display the error message
echo '<h1>Errors</h1><ol>';
foreach ($notInstalledMessage as $message) {
echo '<li>' . $message . '</li>';
}
echo '</ol>';
die();
} else {
# Twig is installed, put the error message in the template
$tpl_file = Tools::getTplFile('error');
$tpl_vars = array_merge($tpl_vars, array('msg' => $poche->getNotInstalledMessage()));
echo $poche->tpl->render($tpl_file, $tpl_vars);
exit;
}
}
# poche actions
if (isset($_GET['login'])) {
# hello you
$poche->login($referer);
} elseif (isset($_GET['logout'])) {
# see you soon !
$poche->logout();
} elseif (isset($_GET['config'])) {
# Update password
$poche->updatePassword();
} elseif (isset($_GET['newuser'])) {
$poche->createNewUser();
} elseif (isset($_GET['deluser'])) {
$poche->deleteUser();
} elseif (isset($_GET['epub'])) {
$poche->createEpub();
} elseif (isset($_GET['import'])) {
$import = $poche->import();
$tpl_vars = array_merge($tpl_vars, $import);
} elseif (isset($_GET['download'])) {
Tools::download_db();
} elseif (isset($_GET['empty-cache'])) {
$poche->emptyCache();
} elseif (isset($_GET['export'])) {
$poche->export();
} elseif (isset($_GET['updatetheme'])) {
$poche->updateTheme();
} elseif (isset($_GET['updatelanguage'])) {
$poche->updateLanguage();
} elseif (isset($_GET['uploadfile'])) {
$poche->uploadFile();
} elseif (isset($_GET['feed'])) {
if (isset($_GET['action']) && $_GET['action'] == 'generate') {
$poche->generateToken();
}
else {
$tag_id = (isset($_GET['tag_id']) ? intval($_GET['tag_id']) : 0);
$poche->generateFeeds($_GET['token'], filter_var($_GET['user_id'],FILTER_SANITIZE_NUMBER_INT), $tag_id, $_GET['type']);
}
}
elseif (isset($_GET['plainurl']) && !empty($_GET['plainurl'])) {
$plain_url = new Url(base64_encode($_GET['plainurl']));
$poche->action('add', $plain_url);
}
if (Session::isLogged()) {
$poche->action($action, $url, $id);
$tpl_file = Tools::getTplFile($view);
$tpl_vars = array_merge($tpl_vars, $poche->displayView($view, $id));
} elseif(isset($_SERVER['PHP_AUTH_USER'])) {
if($poche->store->userExists($_SERVER['PHP_AUTH_USER'])) {
$poche->login($referer);
} else {
$poche->messages->add('e', _('login failed: user doesn\'t exist'));
Tools::logm('user doesn\'t exist');
$tpl_file = Tools::getTplFile('login');
$tpl_vars['http_auth'] = 1;
}
} elseif(isset($_SERVER['REMOTE_USER'])) {
if($poche->store->userExists($_SERVER['REMOTE_USER'])) {
$poche->login($referer);
} else {
$poche->messages->add('e', _('login failed: user doesn\'t exist'));
Tools::logm('user doesn\'t exist');
$tpl_file = Tools::getTplFile('login');
$tpl_vars['http_auth'] = 1;
}
} else {
$tpl_file = Tools::getTplFile('login');
$tpl_vars['http_auth'] = 0;
Session::logout();
}
# because messages can be added in $poche->action(), we have to add this entry now (we can add it before)
$messages = $poche->messages->display('all', FALSE);
$tpl_vars = array_merge($tpl_vars, array('messages' => $messages));
# display poche
echo $poche->tpl->render($tpl_file, $tpl_vars);

View file

@ -1,4 +1,13 @@
<?php <?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
*/
$errors = array(); $errors = array();
$successes = array(); $successes = array();

View file

@ -1,4 +1,13 @@
<?php <?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
*/
$app_name = 'wallabag'; $app_name = 'wallabag';
$php_ok = (function_exists('version_compare') && version_compare(phpversion(), '5.3.3', '>=')); $php_ok = (function_exists('version_compare') && version_compare(phpversion(), '5.3.3', '>='));