diff options
| author | 2014-09-26 14:36:30 +0200 | |
|---|---|---|
| committer | 2014-09-26 14:36:30 +0200 | |
| commit | 147a3d21bbe78ae66c7134ac0c355472a92e1159 (patch) | |
| tree | 7f8ca64ccc0939c8c9aef145a8fd86cc2e52640b /lib | |
| parent | 7e949d50320317b5c3b5a2da2bdaf324e794b2f7 (diff) | |
| parent | c14162221365077bcaeecde7127806190490dd58 (diff) | |
Merge branch 'dev'
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/Minz/ActionController.php | 6 | ||||
| -rw-r--r-- | lib/Minz/Cache.php | 116 | ||||
| -rw-r--r-- | lib/Minz/Configuration.php | 140 | ||||
| -rw-r--r-- | lib/Minz/Dispatcher.php | 108 | ||||
| -rw-r--r-- | lib/Minz/Error.php | 32 | ||||
| -rw-r--r-- | lib/Minz/FrontController.php | 59 | ||||
| -rw-r--r-- | lib/Minz/Helper.php | 17 | ||||
| -rw-r--r-- | lib/Minz/Log.php | 17 | ||||
| -rw-r--r-- | lib/Minz/ModelPdo.php | 73 | ||||
| -rw-r--r-- | lib/Minz/Request.php | 127 | ||||
| -rw-r--r-- | lib/Minz/Response.php | 60 | ||||
| -rw-r--r-- | lib/Minz/RouteNotFoundException.php | 16 | ||||
| -rw-r--r-- | lib/Minz/Router.php | 209 | ||||
| -rw-r--r-- | lib/Minz/Session.php | 74 | ||||
| -rw-r--r-- | lib/Minz/Translate.php | 34 | ||||
| -rw-r--r-- | lib/Minz/Url.php | 15 | ||||
| -rw-r--r-- | lib/Minz/View.php | 25 | ||||
| -rw-r--r-- | lib/SimplePie/SimplePie.php | 42 | ||||
| -rw-r--r-- | lib/SimplePie/SimplePie/File.php | 12 | ||||
| -rw-r--r-- | lib/SimplePie/SimplePie/Parser.php | 2 | ||||
| -rw-r--r-- | lib/lib_date.php | 131 | ||||
| -rw-r--r-- | lib/lib_opml.php | 277 | ||||
| -rw-r--r-- | lib/lib_rss.php | 57 |
23 files changed, 816 insertions, 833 deletions
diff --git a/lib/Minz/ActionController.php b/lib/Minz/ActionController.php index 409d9611f..b47c54554 100644 --- a/lib/Minz/ActionController.php +++ b/lib/Minz/ActionController.php @@ -8,16 +8,12 @@ * La classe ActionController représente le contrôleur de l'application */ class Minz_ActionController { - protected $router; protected $view; /** * Constructeur - * @param $controller nom du controller - * @param $action nom de l'action à lancer */ - public function __construct ($router) { - $this->router = $router; + public function __construct () { $this->view = new Minz_View (); $this->view->attributeParams (); } diff --git a/lib/Minz/Cache.php b/lib/Minz/Cache.php deleted file mode 100644 index fcb627eb2..000000000 --- a/lib/Minz/Cache.php +++ /dev/null @@ -1,116 +0,0 @@ -<?php -/** - * MINZ - Copyright 2011 Marien Fressinaud - * Sous licence AGPL3 <http://www.gnu.org/licenses/> -*/ - -/** - * La classe Cache permet de gérer facilement les pages en cache - */ -class Minz_Cache { - /** - * $expire timestamp auquel expire le cache de $url - */ - private $expire = 0; - - /** - * $file est le nom du fichier de cache - */ - private $file = ''; - - /** - * $enabled permet de déterminer si le cache est activé - */ - private static $enabled = true; - - /** - * Constructeur - */ - public function __construct () { - $this->_fileName (); - $this->_expire (); - } - - /** - * Setteurs - */ - public function _fileName () { - $file = md5 (Minz_Request::getURI ()); - - $this->file = CACHE_PATH . '/'.$file; - } - - public function _expire () { - if ($this->exist ()) { - $this->expire = filemtime ($this->file) - + Minz_Configuration::delayCache (); - } - } - - /** - * Permet de savoir si le cache est activé - * @return true si activé, false sinon - */ - public static function isEnabled () { - return Minz_Configuration::cacheEnabled () && self::$enabled; - } - - /** - * Active / désactive le cache - */ - public static function switchOn () { - self::$enabled = true; - } - public static function switchOff () { - self::$enabled = false; - } - - /** - * Détermine si le cache de $url a expiré ou non - * @return true si il a expiré, false sinon - */ - public function expired () { - return time () > $this->expire; - } - - /** - * Affiche le contenu du cache - * @print le code html du cache - */ - public function render () { - if ($this->exist ()) { - include ($this->file); - } - } - - /** - * Enregistre $html en cache - * @param $html le html à mettre en cache - */ - public function cache ($html) { - file_put_contents ($this->file, $html); - } - - /** - * Permet de savoir si le cache existe - * @return true si il existe, false sinon - */ - public function exist () { - return file_exists ($this->file); - } - - /** - * Nettoie le cache en supprimant tous les fichiers - */ - public static function clean () { - $files = opendir (CACHE_PATH); - - while ($fic = readdir ($files)) { - if ($fic != '.' && $fic != '..') { - unlink (CACHE_PATH.'/'.$fic); - } - } - - closedir ($files); - } -} diff --git a/lib/Minz/Configuration.php b/lib/Minz/Configuration.php index b3de9e39e..4e9da58b4 100644 --- a/lib/Minz/Configuration.php +++ b/lib/Minz/Configuration.php @@ -30,12 +30,9 @@ class Minz_Configuration { * définition des variables de configuration * $salt une chaîne de caractères aléatoires (obligatoire) * $environment gère le niveau d'affichage pour log et erreurs - * $use_url_rewriting indique si on utilise l'url_rewriting * $base_url le chemin de base pour accéder à l'application * $title le nom de l'application * $language la langue par défaut de l'application - * $cacheEnabled permet de savoir si le cache doit être activé - * $delayCache la limite de cache * $db paramètres pour la base de données (tableau) * - host le serveur de la base * - user nom d'utilisateur @@ -45,15 +42,14 @@ class Minz_Configuration { private static $salt = ''; private static $environment = Minz_Configuration::PRODUCTION; private static $base_url = ''; - private static $use_url_rewriting = false; private static $title = ''; private static $language = 'en'; - private static $cache_enabled = false; - private static $delay_cache = 3600; private static $default_user = ''; private static $allow_anonymous = false; private static $allow_anonymous_refresh = false; private static $auth_type = 'none'; + private static $api_enabled = false; + private static $unsafe_autologin_enabled = false; private static $db = array ( 'type' => 'mysql', @@ -92,21 +88,12 @@ class Minz_Configuration { public static function baseUrl () { return self::$base_url; } - public static function useUrlRewriting () { - return self::$use_url_rewriting; - } public static function title () { return self::$title; } public static function language () { return self::$language; } - public static function cacheEnabled () { - return self::$cache_enabled; - } - public static function delayCache () { - return self::$delay_cache; - } public static function dataBase () { return self::$db; } @@ -131,6 +118,12 @@ class Minz_Configuration { public static function canLogIn() { return self::$auth_type === 'form' || self::$auth_type === 'persona'; } + public static function apiEnabled() { + return self::$api_enabled; + } + public static function unsafeAutologinEnabled() { + return self::$unsafe_autologin_enabled; + } public static function _allowAnonymous($allow = false) { self::$allow_anonymous = ((bool)$allow) && self::canLogIn(); @@ -151,6 +144,13 @@ class Minz_Configuration { self::_allowAnonymous(self::$allow_anonymous); } + public static function _enableApi($value = false) { + self::$api_enabled = (bool)$value; + } + public static function _enableAutologin($value = false) { + self::$unsafe_autologin_enabled = (bool)$value; + } + /** * Initialise les variables de configuration * @exception Minz_FileNotExistException si le CONF_PATH_NAME n'existe pas @@ -171,7 +171,6 @@ class Minz_Configuration { $ini_array = array( 'general' => array( 'environment' => self::environment(true), - 'use_url_rewriting' => self::$use_url_rewriting, 'salt' => self::$salt, 'base_url' => self::$base_url, 'title' => self::$title, @@ -179,6 +178,8 @@ class Minz_Configuration { 'allow_anonymous' => self::$allow_anonymous, 'allow_anonymous_refresh' => self::$allow_anonymous_refresh, 'auth_type' => self::$auth_type, + 'api_enabled' => self::$api_enabled, + 'unsafe_autologin_enabled' => self::$unsafe_autologin_enabled, ), 'db' => self::$db, ); @@ -255,9 +256,6 @@ class Minz_Configuration { if (isset ($general['base_url'])) { self::$base_url = $general['base_url']; } - if (isset ($general['use_url_rewriting'])) { - self::$use_url_rewriting = $general['use_url_rewriting']; - } if (isset ($general['title'])) { self::$title = $general['title']; @@ -265,18 +263,6 @@ class Minz_Configuration { if (isset ($general['language'])) { self::$language = $general['language']; } - if (isset ($general['cache_enabled'])) { - self::$cache_enabled = $general['cache_enabled']; - if (CACHE_PATH === false && self::$cache_enabled) { - throw new FileNotExistException ( - 'CACHE_PATH', - Minz_Exception::ERROR - ); - } - } - if (isset ($general['delay_cache'])) { - self::$delay_cache = inval($general['delay_cache']); - } if (isset ($general['default_user'])) { self::$default_user = $general['default_user']; } @@ -295,45 +281,77 @@ class Minz_Configuration { ($general['allow_anonymous_refresh'] !== 'no') ); } + if (isset ($general['api_enabled'])) { + self::$api_enabled = ( + ((bool)($general['api_enabled'])) && + ($general['api_enabled'] !== 'no') + ); + } + if (isset ($general['unsafe_autologin_enabled'])) { + self::$unsafe_autologin_enabled = ( + ((bool)($general['unsafe_autologin_enabled'])) && + ($general['unsafe_autologin_enabled'] !== 'no') + ); + } // Base de données if (isset ($ini_array['db'])) { $db = $ini_array['db']; - if (empty($db['host'])) { + if (empty($db['type'])) { throw new Minz_BadConfigurationException ( - 'host', + 'type', Minz_Exception::ERROR ); } - if (empty($db['user'])) { - throw new Minz_BadConfigurationException ( - 'user', - Minz_Exception::ERROR - ); - } - if (!isset ($db['password'])) { - throw new Minz_BadConfigurationException ( - 'password', - Minz_Exception::ERROR - ); - } - if (empty($db['base'])) { - throw new Minz_BadConfigurationException ( - 'base', - Minz_Exception::ERROR - ); - } - - if (!empty($db['type'])) { - self::$db['type'] = $db['type']; - } - self::$db['host'] = $db['host']; - self::$db['user'] = $db['user']; - self::$db['password'] = $db['password']; - self::$db['base'] = $db['base']; - if (isset($db['prefix'])) { - self::$db['prefix'] = $db['prefix']; + switch ($db['type']) { + case 'mysql': + if (empty($db['host'])) { + throw new Minz_BadConfigurationException ( + 'host', + Minz_Exception::ERROR + ); + } + if (empty($db['user'])) { + throw new Minz_BadConfigurationException ( + 'user', + Minz_Exception::ERROR + ); + } + if (!isset($db['password'])) { + throw new Minz_BadConfigurationException ( + 'password', + Minz_Exception::ERROR + ); + } + if (empty($db['base'])) { + throw new Minz_BadConfigurationException ( + 'base', + Minz_Exception::ERROR + ); + } + self::$db['host'] = $db['host']; + self::$db['user'] = $db['user']; + self::$db['password'] = $db['password']; + self::$db['base'] = $db['base']; + if (isset($db['prefix'])) { + self::$db['prefix'] = $db['prefix']; + } + break; + case 'sqlite': + self::$db['host'] = ''; + self::$db['user'] = ''; + self::$db['password'] = ''; + self::$db['base'] = ''; + self::$db['prefix'] = ''; + break; + default: + throw new Minz_BadConfigurationException ( + 'type', + Minz_Exception::ERROR + ); + break; } + self::$db['type'] = $db['type']; } } diff --git a/lib/Minz/Dispatcher.php b/lib/Minz/Dispatcher.php index 71dfe8ac6..f62a92911 100644 --- a/lib/Minz/Dispatcher.php +++ b/lib/Minz/Dispatcher.php @@ -14,85 +14,55 @@ class Minz_Dispatcher { /* singleton */ private static $instance = null; + private static $needsReset; - private $router; private $controller; /** * Récupère l'instance du Dispatcher */ - public static function getInstance ($router) { + public static function getInstance () { if (self::$instance === null) { - self::$instance = new Minz_Dispatcher ($router); + self::$instance = new Minz_Dispatcher (); } return self::$instance; } /** - * Constructeur - */ - private function __construct ($router) { - $this->router = $router; - } - - /** * Lance le controller indiqué dans Request * Remplit le body de Response à partir de la Vue * @exception Minz_Exception */ - public function run ($ob = true) { - $cache = new Minz_Cache(); - // Le ob_start est dupliqué : sans ça il y a un bug sous Firefox - // ici on l'appelle avec 'ob_gzhandler', après sans. - // Vraisemblablement la compression fonctionne mais c'est sale - // J'ignore les effets de bord :( - if ($ob) { - ob_start ('ob_gzhandler'); - } + public function run () { + do { + self::$needsReset = false; - if (Minz_Cache::isEnabled () && !$cache->expired ()) { - if ($ob) { - ob_start (); - } - $cache->render (); - if ($ob) { - $text = ob_get_clean(); - } - } else { - $text = ''; //TODO: Clean this code - while (Minz_Request::$reseted) { - Minz_Request::$reseted = false; - - try { - $this->createController ('FreshRSS_' . Minz_Request::controllerName () . '_Controller'); - $this->controller->init (); - $this->controller->firstAction (); + try { + $this->createController ('FreshRSS_' . Minz_Request::controllerName () . '_Controller'); + $this->controller->init (); + $this->controller->firstAction (); + if (!self::$needsReset) { $this->launchAction ( Minz_Request::actionName () . 'Action' ); - $this->controller->lastAction (); - - if (!Minz_Request::$reseted) { - if ($ob) { - ob_start (); - } - $this->controller->view ()->build (); - if ($ob) { - $text = ob_get_clean(); - } - } - } catch (Minz_Exception $e) { - throw $e; } - } + $this->controller->lastAction (); - if (Minz_Cache::isEnabled ()) { - $cache->cache ($text); + if (!self::$needsReset) { + $this->controller->view ()->build (); + } + } catch (Minz_Exception $e) { + throw $e; } - } + } while (self::$needsReset); + } - Minz_Response::setBody ($text); + /** + * Informe le contrôleur qu'il doit recommancer car la requête a été modifiée + */ + public static function reset() { + self::$needsReset = true; } /** @@ -112,7 +82,7 @@ class Minz_Dispatcher { Minz_Exception::ERROR ); } - $this->controller = new $controller_name ($this->router); + $this->controller = new $controller_name (); if (! ($this->controller instanceof Minz_ActionController)) { throw new Minz_ControllerNotActionControllerException ( @@ -129,21 +99,19 @@ class Minz_Dispatcher { * le controller */ private function launchAction ($action_name) { - if (!Minz_Request::$reseted) { - if (!is_callable (array ( - $this->controller, - $action_name - ))) { - throw new Minz_ActionException ( - get_class ($this->controller), - $action_name, - Minz_Exception::ERROR - ); - } - call_user_func (array ( - $this->controller, - $action_name - )); + if (!is_callable (array ( + $this->controller, + $action_name + ))) { + throw new Minz_ActionException ( + get_class ($this->controller), + $action_name, + Minz_Exception::ERROR + ); } + call_user_func (array ( + $this->controller, + $action_name + )); } } diff --git a/lib/Minz/Error.php b/lib/Minz/Error.php index 337ab6c0a..c8222a430 100644 --- a/lib/Minz/Error.php +++ b/lib/Minz/Error.php @@ -23,13 +23,32 @@ class Minz_Error { $logs = self::processLogs ($logs); $error_filename = APP_PATH . '/Controllers/errorController.php'; + switch ($code) { + case 200 : + header('HTTP/1.1 200 OK'); + break; + case 403 : + header('HTTP/1.1 403 Forbidden'); + break; + case 404 : + header('HTTP/1.1 404 Not Found'); + break; + case 500 : + header('HTTP/1.1 500 Internal Server Error'); + break; + case 503 : + header('HTTP/1.1 503 Service Unavailable'); + break; + default : + header('HTTP/1.1 500 Internal Server Error'); + } + if (file_exists ($error_filename)) { $params = array ( 'code' => $code, 'logs' => $logs ); - Minz_Response::setHeader ($code); if ($redirect) { Minz_Request::forward (array ( 'c' => 'error' @@ -41,19 +60,16 @@ class Minz_Error { ), false); } } else { - $text = '<h1>An error occured</h1>'."\n"; + echo '<h1>An error occured</h1>' . "\n"; if (!empty ($logs)) { - $text .= '<ul>'."\n"; + echo '<ul>' . "\n"; foreach ($logs as $log) { - $text .= '<li>' . $log . '</li>'."\n"; + echo '<li>' . $log . '</li>' . "\n"; } - $text .= '</ul>'."\n"; + echo '</ul>' . "\n"; } - Minz_Response::setHeader ($code); - Minz_Response::setBody ($text); - Minz_Response::send (); exit (); } } diff --git a/lib/Minz/FrontController.php b/lib/Minz/FrontController.php index 80eda8877..f13882801 100644 --- a/lib/Minz/FrontController.php +++ b/lib/Minz/FrontController.php @@ -24,13 +24,10 @@ */ class Minz_FrontController { protected $dispatcher; - protected $router; - - private $useOb = true; /** * Constructeur - * Initialise le router et le dispatcher + * Initialise le dispatcher, met à jour la Request */ public function __construct () { if (LOG_PATH === false) { @@ -42,29 +39,50 @@ class Minz_FrontController { Minz_Request::init (); - $this->router = new Minz_Router (); - $this->router->init (); - } catch (Minz_RouteNotFoundException $e) { - Minz_Log::record ($e->getMessage (), Minz_Log::ERROR); - Minz_Error::error ( - 404, - array ('error' => array ($e->getMessage ())) + $url = $this->buildUrl(); + $url['params'] = array_merge ( + $url['params'], + Minz_Request::fetchPOST () ); + Minz_Request::forward ($url); } catch (Minz_Exception $e) { Minz_Log::record ($e->getMessage (), Minz_Log::ERROR); $this->killApp ($e->getMessage ()); } - $this->dispatcher = Minz_Dispatcher::getInstance ($this->router); + $this->dispatcher = Minz_Dispatcher::getInstance(); } /** - * Démarre l'application (lance le dispatcher et renvoie la réponse + * Retourne un tableau représentant l'url passée par la barre d'adresses + * @return tableau représentant l'url + */ + private function buildUrl() { + $url = array (); + + $url['c'] = Minz_Request::fetchGET ( + 'c', + Minz_Request::defaultControllerName () + ); + $url['a'] = Minz_Request::fetchGET ( + 'a', + Minz_Request::defaultActionName () + ); + $url['params'] = Minz_Request::fetchGET (); + + // post-traitement + unset ($url['params']['c']); + unset ($url['params']['a']); + + return $url; + } + + /** + * Démarre l'application (lance le dispatcher et renvoie la réponse) */ public function run () { try { - $this->dispatcher->run ($this->useOb); - Minz_Response::send (); + $this->dispatcher->run(); } catch (Minz_Exception $e) { try { Minz_Log::record ($e->getMessage (), Minz_Log::ERROR); @@ -96,15 +114,4 @@ class Minz_FrontController { } exit ('### Application problem ###<br />'."\n".$txt); } - - public function useOb() { - return $this->useOb; - } - - /** - * Use ob_start('ob_gzhandler') or not. - */ - public function _useOb($ob) { - return $this->useOb = (bool)$ob; - } } diff --git a/lib/Minz/Helper.php b/lib/Minz/Helper.php index b058211d3..f4a547c4e 100644 --- a/lib/Minz/Helper.php +++ b/lib/Minz/Helper.php @@ -12,11 +12,22 @@ class Minz_Helper { * Annule les effets des magic_quotes pour une variable donnée * @param $var variable à traiter (tableau ou simple variable) */ - public static function stripslashes_r ($var) { - if (is_array ($var)){ - return array_map (array ('Helper', 'stripslashes_r'), $var); + public static function stripslashes_r($var) { + if (is_array($var)){ + return array_map(array('Minz_Helper', 'stripslashes_r'), $var); } else { return stripslashes($var); } } + + /** + * Wrapper for htmlspecialchars. + * Force UTf-8 value and can be used on array too. + */ + public static function htmlspecialchars_utf8($var) { + if (is_array($var)) { + return array_map(array('Minz_Helper', 'htmlspecialchars_utf8'), $var); + } + return htmlspecialchars($var, ENT_COMPAT, 'UTF-8'); + } } diff --git a/lib/Minz/Log.php b/lib/Minz/Log.php index e710aad4a..d3eaec2ae 100644 --- a/lib/Minz/Log.php +++ b/lib/Minz/Log.php @@ -80,4 +80,21 @@ class Minz_Log { self::record($msg_get, Minz_Log::DEBUG, $file_name); self::record($msg_post, Minz_Log::DEBUG, $file_name); } + + /** + * Some helpers to Minz_Log::record() method + * Parameters are the same of those of the record() method. + */ + public static function debug($msg, $file_name = null) { + self::record($msg, Minz_Log::DEBUG, $file_name); + } + public static function notice($msg, $file_name = null) { + self::record($msg, Minz_Log::NOTICE, $file_name); + } + public static function warning($msg, $file_name = null) { + self::record($msg, Minz_Log::WARNING, $file_name); + } + public static function error($msg, $file_name = null) { + self::record($msg, Minz_Log::ERROR, $file_name); + } } diff --git a/lib/Minz/ModelPdo.php b/lib/Minz/ModelPdo.php index 831df13a2..b4bfca746 100644 --- a/lib/Minz/ModelPdo.php +++ b/lib/Minz/ModelPdo.php @@ -16,6 +16,7 @@ class Minz_ModelPdo { public static $useSharedBd = true; private static $sharedBd = null; private static $sharedPrefix; + protected static $sharedDbType; /** * $bd variable représentant la base de données @@ -24,46 +25,64 @@ class Minz_ModelPdo { protected $prefix; + public function dbType() { + return self::$sharedDbType; + } + /** * Créé la connexion à la base de données à l'aide des variables * HOST, BASE, USER et PASS définies dans le fichier de configuration */ - public function __construct () { - if (self::$useSharedBd && self::$sharedBd != null) { + public function __construct($currentUser = null) { + if (self::$useSharedBd && self::$sharedBd != null && $currentUser === null) { $this->bd = self::$sharedBd; $this->prefix = self::$sharedPrefix; return; } - $db = Minz_Configuration::dataBase (); - $driver_options = null; + $db = Minz_Configuration::dataBase(); + + if ($currentUser === null) { + $currentUser = Minz_Session::param('currentUser', '_'); + } try { $type = $db['type']; - if($type == 'mysql') { - $string = $type - . ':host=' . $db['host'] + if ($type === 'mysql') { + $string = 'mysql:host=' . $db['host'] . ';dbname=' . $db['base'] . ';charset=utf8'; $driver_options = array( - PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8' + PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8', + ); + $this->prefix = $db['prefix'] . $currentUser . '_'; + } elseif ($type === 'sqlite') { + $string = 'sqlite:' . DATA_PATH . '/' . $currentUser . '.sqlite'; + $driver_options = array( + //PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + ); + $this->prefix = ''; + } else { + throw new Minz_PDOConnectionException( + 'Invalid database type!', + $db['user'], Minz_Exception::ERROR ); - } elseif($type == 'sqlite') { - $string = $type . ':/' . DATA_PATH . $db['base'] . '.sqlite'; //TODO: DEBUG UTF-8 http://www.siteduzero.com/forum/sujet/sqlite-connexion-utf-8-18797 } + self::$sharedDbType = $type; + self::$sharedPrefix = $this->prefix; - $this->bd = new FreshPDO ( + $this->bd = new MinzPDO( $string, $db['user'], $db['password'], $driver_options ); + if ($type === 'sqlite') { + $this->bd->exec('PRAGMA foreign_keys = ON;'); + } self::$sharedBd = $this->bd; - - $this->prefix = $db['prefix'] . Minz_Session::param('currentUser', '_') . '_'; - self::$sharedPrefix = $this->prefix; } catch (Exception $e) { - throw new Minz_PDOConnectionException ( + throw new Minz_PDOConnectionException( $string, $db['user'], Minz_Exception::ERROR ); @@ -80,40 +99,26 @@ class Minz_ModelPdo { $this->bd->rollBack(); } - public function size($all = false) { - $db = Minz_Configuration::dataBase (); - $sql = 'SELECT SUM(data_length + index_length) FROM information_schema.TABLES WHERE table_schema = ?'; - $values = array ($db['base']); - if (!$all) { - $sql .= ' AND table_name LIKE ?'; - $values[] = $this->prefix . '%'; - } - $stm = $this->bd->prepare ($sql); - $stm->execute ($values); - $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0); - return $res[0]; - } - public static function clean() { self::$sharedBd = null; self::$sharedPrefix = ''; } } -class FreshPDO extends PDO { +class MinzPDO extends PDO { private static function check($statement) { if (preg_match('/^(?:UPDATE|INSERT|DELETE)/i', $statement)) { invalidateHttpCache(); } } - public function prepare ($statement, $driver_options = array()) { - FreshPDO::check($statement); + public function prepare($statement, $driver_options = array()) { + MinzPDO::check($statement); return parent::prepare($statement, $driver_options); } - public function exec ($statement) { - FreshPDO::check($statement); + public function exec($statement) { + MinzPDO::check($statement); return parent::exec($statement); } } diff --git a/lib/Minz/Request.php b/lib/Minz/Request.php index 282d47a77..f7a24c026 100644 --- a/lib/Minz/Request.php +++ b/lib/Minz/Request.php @@ -10,68 +10,61 @@ class Minz_Request { private static $controller_name = ''; private static $action_name = ''; - private static $params = array (); + private static $params = array(); private static $default_controller_name = 'index'; private static $default_action_name = 'index'; - public static $reseted = true; - /** * Getteurs */ - public static function controllerName () { + public static function controllerName() { return self::$controller_name; } - public static function actionName () { + public static function actionName() { return self::$action_name; } - public static function params () { + public static function params() { return self::$params; } - static function htmlspecialchars_utf8 ($p) { - return htmlspecialchars($p, ENT_COMPAT, 'UTF-8'); - } - public static function param ($key, $default = false, $specialchars = false) { - if (isset (self::$params[$key])) { + public static function param($key, $default = false, $specialchars = false) { + if (isset(self::$params[$key])) { $p = self::$params[$key]; - if(is_object($p) || $specialchars) { + if (is_object($p) || $specialchars) { return $p; - } elseif(is_array($p)) { - return array_map('self::htmlspecialchars_utf8', $p); } else { - return self::htmlspecialchars_utf8($p); + return Minz_Helper::htmlspecialchars_utf8($p); } } else { return $default; } } - public static function defaultControllerName () { + public static function defaultControllerName() { return self::$default_controller_name; } - public static function defaultActionName () { + public static function defaultActionName() { return self::$default_action_name; } /** * Setteurs */ - public static function _controllerName ($controller_name) { + public static function _controllerName($controller_name) { self::$controller_name = $controller_name; } - public static function _actionName ($action_name) { + public static function _actionName($action_name) { self::$action_name = $action_name; } - public static function _params ($params) { + public static function _params($params) { if (!is_array($params)) { - $params = array ($params); + $params = array($params); } self::$params = $params; } - public static function _param ($key, $value = false) { + public static function _param($key, $value = false) { if ($value === false) { - unset (self::$params[$key]); + unset(self::$params[$key]); } else { self::$params[$key] = $value; } @@ -80,14 +73,14 @@ class Minz_Request { /** * Initialise la Request */ - public static function init () { - self::magicQuotesOff (); + public static function init() { + self::magicQuotesOff(); } /** * Retourn le nom de domaine du site */ - public static function getDomainName () { + public static function getDomainName() { return $_SERVER['HTTP_HOST']; } @@ -95,7 +88,7 @@ class Minz_Request { * Détermine la base de l'url * @return la base de l'url */ - public static function getBaseUrl () { + public static function getBaseUrl() { $defaultBaseUrl = Minz_Configuration::baseUrl(); if (!empty($defaultBaseUrl)) { return $defaultBaseUrl; @@ -110,13 +103,13 @@ class Minz_Request { * Récupère l'URI de la requête * @return l'URI */ - public static function getURI () { - if (isset ($_SERVER['REQUEST_URI'])) { - $base_url = self::getBaseUrl (); + public static function getURI() { + if (isset($_SERVER['REQUEST_URI'])) { + $base_url = self::getBaseUrl(); $uri = $_SERVER['REQUEST_URI']; - $len_base_url = strlen ($base_url); - $real_uri = substr ($uri, $len_base_url); + $len_base_url = strlen($base_url); + $real_uri = substr($uri, $len_base_url); } else { $real_uri = ''; } @@ -130,24 +123,53 @@ class Minz_Request { * @param $redirect si vrai, force la redirection http * > sinon, le dispatcher recharge en interne */ - public static function forward ($url = array (), $redirect = false) { - $url = Minz_Url::checkUrl ($url); + public static function forward($url = array(), $redirect = false) { + if (!is_array($url)) { + header('Location: ' . $url); + exit(); + } + + $url = Minz_Url::checkUrl($url); if ($redirect) { - header ('Location: ' . Minz_Url::display ($url, 'php')); - exit (); + header('Location: ' . Minz_Url::display($url, 'php')); + exit(); } else { - self::$reseted = true; - - self::_controllerName ($url['c']); - self::_actionName ($url['a']); - self::_params (array_merge ( + self::_controllerName($url['c']); + self::_actionName($url['a']); + self::_params(array_merge( self::$params, $url['params'] )); + Minz_Dispatcher::reset(); } } + + /** + * Wrappers good notifications + redirection + * @param $msg notification content + * @param $url url array to where we should be forwarded + */ + public static function good($msg, $url = array()) { + Minz_Session::_param('notification', array( + 'type' => 'good', + 'content' => $msg + )); + + Minz_Request::forward($url, true); + } + + public static function bad($msg, $url = array()) { + Minz_Session::_param('notification', array( + 'type' => 'bad', + 'content' => $msg + )); + + Minz_Request::forward($url, true); + } + + /** * Permet de récupérer une variable de type $_GET * @param $param nom de la variable @@ -156,10 +178,10 @@ class Minz_Request { * $_GET si $param = false * $default si $_GET[$param] n'existe pas */ - public static function fetchGET ($param = false, $default = false) { + public static function fetchGET($param = false, $default = false) { if ($param === false) { return $_GET; - } elseif (isset ($_GET[$param])) { + } elseif (isset($_GET[$param])) { return $_GET[$param]; } else { return $default; @@ -174,10 +196,10 @@ class Minz_Request { * $_POST si $param = false * $default si $_POST[$param] n'existe pas */ - public static function fetchPOST ($param = false, $default = false) { + public static function fetchPOST($param = false, $default = false) { if ($param === false) { return $_POST; - } elseif (isset ($_POST[$param])) { + } elseif (isset($_POST[$param])) { return $_POST[$param]; } else { return $default; @@ -190,15 +212,16 @@ class Minz_Request { * $_POST * $_COOKIE */ - private static function magicQuotesOff () { - if (get_magic_quotes_gpc ()) { - $_GET = Minz_Helper::stripslashes_r ($_GET); - $_POST = Minz_Helper::stripslashes_r ($_POST); - $_COOKIE = Minz_Helper::stripslashes_r ($_COOKIE); + private static function magicQuotesOff() { + if (get_magic_quotes_gpc()) { + $_GET = Minz_Helper::stripslashes_r($_GET); + $_POST = Minz_Helper::stripslashes_r($_POST); + $_COOKIE = Minz_Helper::stripslashes_r($_COOKIE); } } - public static function isPost () { - return $_SERVER['REQUEST_METHOD'] === 'POST'; + public static function isPost() { + return isset($_SERVER['REQUEST_METHOD']) && + $_SERVER['REQUEST_METHOD'] === 'POST'; } } diff --git a/lib/Minz/Response.php b/lib/Minz/Response.php deleted file mode 100644 index f8ea3d946..000000000 --- a/lib/Minz/Response.php +++ /dev/null @@ -1,60 +0,0 @@ -<?php -/** - * MINZ - Copyright 2011 Marien Fressinaud - * Sous licence AGPL3 <http://www.gnu.org/licenses/> -*/ - -/** - * Response représente la requête http renvoyée à l'utilisateur - */ -class Minz_Response { - private static $header = 'HTTP/1.0 200 OK'; - private static $body = ''; - - /** - * Mets à jour le body de la Response - * @param $text le texte à incorporer dans le body - */ - public static function setBody ($text) { - self::$body = $text; - } - - /** - * Mets à jour le header de la Response - * @param $code le code HTTP, valeurs possibles - * - 200 (OK) - * - 403 (Forbidden) - * - 404 (Forbidden) - * - 500 (Forbidden) -> par défaut si $code erroné - * - 503 (Forbidden) - */ - public static function setHeader ($code) { - switch ($code) { - case 200 : - self::$header = 'HTTP/1.0 200 OK'; - break; - case 403 : - self::$header = 'HTTP/1.0 403 Forbidden'; - break; - case 404 : - self::$header = 'HTTP/1.0 404 Not Found'; - break; - case 500 : - self::$header = 'HTTP/1.0 500 Internal Server Error'; - break; - case 503 : - self::$header = 'HTTP/1.0 503 Service Unavailable'; - break; - default : - self::$header = 'HTTP/1.0 500 Internal Server Error'; - } - } - - /** - * Envoie la Response à l'utilisateur - */ - public static function send () { - header (self::$header); - echo self::$body; - } -} diff --git a/lib/Minz/RouteNotFoundException.php b/lib/Minz/RouteNotFoundException.php deleted file mode 100644 index dc4f6fbad..000000000 --- a/lib/Minz/RouteNotFoundException.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php -class Minz_RouteNotFoundException extends Minz_Exception { - private $route; - - public function __construct ($route, $code = self::ERROR) { - $this->route = $route; - - $message = 'Route `' . $route . '` not found'; - - parent::__construct ($message, $code); - } - - public function route () { - return $this->route; - } -} diff --git a/lib/Minz/Router.php b/lib/Minz/Router.php deleted file mode 100644 index 1ccd72597..000000000 --- a/lib/Minz/Router.php +++ /dev/null @@ -1,209 +0,0 @@ -<?php -/** - * MINZ - Copyright 2011 Marien Fressinaud - * Sous licence AGPL3 <http://www.gnu.org/licenses/> -*/ - -/** - * La classe Router gère le routage de l'application - * Les routes sont définies dans APP_PATH.'/configuration/routes.php' - */ -class Minz_Router { - const ROUTES_PATH_NAME = '/configuration/routes.php'; - - private $routes = array (); - - /** - * Constructeur - * @exception FileNotExistException si ROUTES_PATH_NAME n'existe pas - * et que l'on utilise l'url rewriting - */ - public function __construct () { - if (Minz_Configuration::useUrlRewriting ()) { - if (file_exists (APP_PATH . self::ROUTES_PATH_NAME)) { - $routes = include ( - APP_PATH . self::ROUTES_PATH_NAME - ); - - if (!is_array ($routes)) { - $routes = array (); - } - - $this->routes = array_map ( - array ('Url', 'checkUrl'), - $routes - ); - } else { - throw new Minz_FileNotExistException ( - self::ROUTES_PATH_NAME, - Minz_Exception::ERROR - ); - } - } - } - - /** - * Initialise le Router en déterminant le couple Controller / Action - * Mets à jour la Request - * @exception RouteNotFoundException si l'uri n'est pas présente dans - * > la table de routage - */ - public function init () { - $url = array (); - - if (Minz_Configuration::useUrlRewriting ()) { - try { - $url = $this->buildWithRewriting (); - } catch (Minz_RouteNotFoundException $e) { - throw $e; - } - } else { - $url = $this->buildWithoutRewriting (); - } - - $url['params'] = array_merge ( - $url['params'], - Minz_Request::fetchPOST () - ); - - Minz_Request::forward ($url); - } - - /** - * Retourne un tableau représentant l'url passée par la barre d'adresses - * Ne se base PAS sur la table de routage - * @return tableau représentant l'url - */ - public function buildWithoutRewriting () { - $url = array (); - - $url['c'] = Minz_Request::fetchGET ( - 'c', - Minz_Request::defaultControllerName () - ); - $url['a'] = Minz_Request::fetchGET ( - 'a', - Minz_Request::defaultActionName () - ); - $url['params'] = Minz_Request::fetchGET (); - - // post-traitement - unset ($url['params']['c']); - unset ($url['params']['a']); - - return $url; - } - - /** - * Retourne un tableau représentant l'url passée par la barre d'adresses - * Se base sur la table de routage - * @return tableau représentant l'url - * @exception RouteNotFoundException si l'uri n'est pas présente dans - * > la table de routage - */ - public function buildWithRewriting () { - $url = array (); - $uri = Minz_Request::getURI (); - $find = false; - - foreach ($this->routes as $route) { - $regex = '*^' . $route['route'] . '$*'; - if (preg_match ($regex, $uri, $matches)) { - $url['c'] = $route['controller']; - $url['a'] = $route['action']; - $url['params'] = $this->getParams ( - $route['params'], - $matches - ); - $find = true; - break; - } - } - - if (!$find && $uri != '/') { - throw new Minz_RouteNotFoundException ( - $uri, - Minz_Exception::ERROR - ); - } - - // post-traitement - $url = Minz_Url::checkUrl ($url); - - return $url; - } - - /** - * Retourne l'uri d'une url en se basant sur la table de routage - * @param l'url sous forme de tableau - * @return l'uri formatée (string) selon une route trouvée - */ - public function printUriRewrited ($url) { - $route = $this->searchRoute ($url); - - if ($route !== false) { - return $this->replaceParams ($route, $url['params']); - } - - return ''; - } - - /** - * Recherche la route correspondante à une url - * @param l'url sous forme de tableau - * @return la route telle que spécifiée dans la table de routage, - * false si pas trouvée - */ - public function searchRoute ($url) { - foreach ($this->routes as $route) { - if ($route['controller'] == $url['c'] - && $route['action'] == $url['a']) { - // calcule la différence des tableaux de params - $params = array_flip ($route['params']); - $difference_params = array_diff_key ( - $params, - $url['params'] - ); - - // vérifie que pas de différence - // et le cas où $params est vide et pas $url['params'] - if (empty ($difference_params) - && (!empty ($params) || empty ($url['params']))) { - return $route; - } - } - } - - return false; - } - - /** - * Récupère un tableau dont - * - les clés sont définies dans $params_route - * - les valeurs sont situées dans $matches - * Le tableau $matches est décalé de +1 par rapport à $params_route - */ - private function getParams($params_route, $matches) { - $params = array (); - - for ($i = 0; $i < count ($params_route); $i++) { - $param = $params_route[$i]; - $params[$param] = $matches[$i + 1]; - } - - return $params; - } - - /** - * Remplace les éléments de la route par les valeurs contenues dans $params - */ - private function replaceParams ($route, $params_replace) { - $uri = $route['route']; - $params = array(); - foreach($route['params'] as $param) { - $uri = preg_replace('#\((.+)\)#U', $params_replace[$param], $uri, 1); - } - - return stripslashes($uri); - } -} diff --git a/lib/Minz/Session.php b/lib/Minz/Session.php index ddabc4658..af4de75bb 100644 --- a/lib/Minz/Session.php +++ b/lib/Minz/Session.php @@ -2,28 +2,20 @@ /** * La classe Session gère la session utilisateur - * C'est un singleton */ class Minz_Session { /** - * $session stocke les variables de session - */ - private static $session = array (); //TODO: Try to avoid having another local copy - - /** * Initialise la session, avec un nom - * Le nom de session est utilisé comme nom pour les cookies et les URLs (i.e. PHPSESSID). + * Le nom de session est utilisé comme nom pour les cookies et les URLs(i.e. PHPSESSID). * Il ne doit contenir que des caractères alphanumériques ; il doit être court et descriptif */ - public static function init ($name) { - // démarre la session - session_name ($name); - session_set_cookie_params (0, dirname(empty($_SERVER['REQUEST_URI']) ? '/' : dirname($_SERVER['REQUEST_URI'])), null, false, true); - session_start (); + public static function init($name) { + $cookie = session_get_cookie_params(); + self::keepCookie($cookie['lifetime']); - if (isset ($_SESSION)) { - self::$session = $_SESSION; - } + // démarre la session + session_name($name); + session_start(); } @@ -32,8 +24,8 @@ class Minz_Session { * @param $p le paramètre à récupérer * @return la valeur de la variable de session, false si n'existe pas */ - public static function param ($p, $default = false) { - return isset(self::$session[$p]) ? self::$session[$p] : $default; + public static function param($p, $default = false) { + return isset($_SESSION[$p]) ? $_SESSION[$p] : $default; } @@ -42,13 +34,11 @@ class Minz_Session { * @param $p le paramètre à créer ou modifier * @param $v la valeur à attribuer, false pour supprimer */ - public static function _param ($p, $v = false) { + public static function _param($p, $v = false) { if ($v === false) { - unset ($_SESSION[$p]); - unset (self::$session[$p]); + unset($_SESSION[$p]); } else { $_SESSION[$p] = $v; - self::$session[$p] = $v; } } @@ -57,15 +47,47 @@ class Minz_Session { * Permet d'effacer une session * @param $force si à false, n'efface pas le paramètre de langue */ - public static function unset_session ($force = false) { - $language = self::param ('language'); + public static function unset_session($force = false) { + $language = self::param('language'); session_destroy(); - self::$session = array (); + $_SESSION = array(); if (!$force) { - self::_param ('language', $language); - Minz_Translate::reset (); + self::_param('language', $language); + Minz_Translate::reset(); } } + + + /** + * Spécifie la durée de vie des cookies + * @param $l la durée de vie + */ + public static function keepCookie($l) { + $cookie_dir = empty($_SERVER['REQUEST_URI']) ? '' : $_SERVER['REQUEST_URI']; + session_set_cookie_params($l, $cookie_dir, '', false, true); + } + + + /** + * Régénère un id de session. + * Utile pour appeler session_set_cookie_params après session_start() + */ + public static function regenerateID() { + session_regenerate_id(true); + } + + public static function deleteLongTermCookie($name) { + setcookie($name, '', 1, '', '', false, true); + } + + public static function setLongTermCookie($name, $value, $expire) { + setcookie($name, $value, $expire, '', '', false, true); + } + + public static function getLongTermCookie($name) { + return isset($_COOKIE[$name]) ? $_COOKIE[$name] : null; + } + } diff --git a/lib/Minz/Translate.php b/lib/Minz/Translate.php index e14f783f7..8c2f90041 100644 --- a/lib/Minz/Translate.php +++ b/lib/Minz/Translate.php @@ -18,28 +18,28 @@ class Minz_Translate { * $translates est le tableau de correspondance * $key => $traduction */ - private static $translates = array (); + private static $translates = array(); /** * Inclus le fichier de langue qui va bien * l'enregistre dans $translates */ - public static function init () { - $l = Minz_Configuration::language (); - self::$language = Minz_Session::param ('language', $l); + public static function init() { + $l = Minz_Configuration::language(); + self::$language = Minz_Session::param('language', $l); $l_path = APP_PATH . '/i18n/' . self::$language . '.php'; - if (file_exists ($l_path)) { - self::$translates = include ($l_path); + if (file_exists($l_path)) { + self::$translates = include($l_path); } } /** * Alias de init */ - public static function reset () { - self::init (); + public static function reset() { + self::init(); } /** @@ -48,24 +48,32 @@ class Minz_Translate { * @return la valeur correspondante à la clé * > si non présente dans le tableau, on retourne la clé elle-même */ - public static function t ($key) { + public static function t($key) { $translate = $key; - if (isset (self::$translates[$key])) { + if (isset(self::$translates[$key])) { $translate = self::$translates[$key]; } - $args = func_get_args (); + $args = func_get_args(); unset($args[0]); - return vsprintf ($translate, $args); + return vsprintf($translate, $args); } /** * Retourne la langue utilisée actuellement * @return la langue */ - public static function language () { + public static function language() { return self::$language; } } + +function _t($key) { + $args = func_get_args(); + unset($args[0]); + array_unshift($args, $key); + + return call_user_func_array('Minz_Translate::t', $args); +} diff --git a/lib/Minz/Url.php b/lib/Minz/Url.php index 17f1ddece..e9f9a69ba 100644 --- a/lib/Minz/Url.php +++ b/lib/Minz/Url.php @@ -5,8 +5,7 @@ */ class Minz_Url { /** - * Affiche une Url formatée selon que l'on utilise l'url_rewriting ou non - * si oui, on cherche dans la table de routage la correspondance pour formater + * Affiche une Url formatée * @param $url l'url à formater définie comme un tableau : * $url['c'] = controller * $url['a'] = action @@ -39,13 +38,7 @@ class Minz_Url { } if ($isArray) { - $router = new Minz_Router (); - - if (Minz_Configuration::useUrlRewriting ()) { - $url_string .= $router->printUriRewrited ($url); - } else { - $url_string .= self::printUri ($url, $encodage); - } + $url_string .= self::printUri ($url, $encodage); } else { $url_string .= $url; } @@ -54,14 +47,14 @@ class Minz_Url { } /** - * Construit l'URI d'une URL sans url rewriting + * Construit l'URI d'une URL * @param l'url sous forme de tableau * @param $encodage pour indiquer comment encoder les & (& ou & pour html) * @return l'uri sous la forme ?key=value&key2=value2 */ private static function printUri ($url, $encodage) { $uri = ''; - $separator = '/?'; + $separator = '?'; if($encodage == 'html') { $and = '&'; diff --git a/lib/Minz/View.php b/lib/Minz/View.php index e170bd406..a0dec1824 100644 --- a/lib/Minz/View.php +++ b/lib/Minz/View.php @@ -26,12 +26,19 @@ class Minz_View { * Détermine si on utilise un layout ou non */ public function __construct () { + $this->change_view(Minz_Request::controllerName(), + Minz_Request::actionName()); + self::$title = Minz_Configuration::title (); + } + + /** + * Change le fichier de vue en fonction d'un controller / action + */ + public function change_view($controller_name, $action_name) { $this->view_filename = APP_PATH . self::VIEWS_PATH_NAME . '/' - . Minz_Request::controllerName () . '/' - . Minz_Request::actionName () . '.phtml'; - - self::$title = Minz_Configuration::title (); + . $controller_name . '/' + . $action_name . '.phtml'; } /** @@ -103,6 +110,16 @@ class Minz_View { } /** + * Retourne renderHelper() dans une chaîne + * @param $helper l'élément à traîter + */ + public function helperToString($helper) { + ob_start(); + $this->renderHelper($helper); + return ob_get_clean(); + } + + /** * Permet de choisir si on souhaite utiliser le layout * @param $use true si on souhaite utiliser le layout, false sinon */ diff --git a/lib/SimplePie/SimplePie.php b/lib/SimplePie/SimplePie.php index d7aaeb0c5..06c100f59 100644 --- a/lib/SimplePie/SimplePie.php +++ b/lib/SimplePie/SimplePie.php @@ -446,6 +446,13 @@ class SimplePie public $feed_url; /** + * @var string Original feed URL, or new feed URL iff HTTP 301 Moved Permanently + * @see SimplePie::subscribe_url() + * @access private + */ + public $permanent_url = null; //FreshRSS + + /** * @var object Instance of SimplePie_File to use as a feed * @see SimplePie::set_file() * @access private @@ -735,6 +742,7 @@ class SimplePie else { $this->feed_url = $this->registry->call('Misc', 'fix_protocol', array($url, 1)); + $this->permanent_url = $this->feed_url; //FreshRSS } } @@ -749,6 +757,7 @@ class SimplePie if ($file instanceof SimplePie_File) { $this->feed_url = $file->url; + $this->permanent_url = $this->feed_url; //FreshRSS $this->file =& $file; return true; } @@ -1331,7 +1340,7 @@ class SimplePie // First check to see if input has been overridden. if ($this->input_encoding !== false) { - $encodings[] = strtoupper($this->input_encoding); //FreshRSS + $encodings[] = strtoupper($this->input_encoding); } $application_types = array('application/xml', 'application/xml-dtd', 'application/xml-external-parsed-entity'); @@ -1355,7 +1364,7 @@ class SimplePie { if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset)) { - $encodings[] = strtoupper($charset[1]); //FreshRSS + $encodings[] = strtoupper($charset[1]); } else { @@ -1602,7 +1611,7 @@ class SimplePie } $this->raw_data = $file->body; - + $this->permanent_url = $file->permanent_url; //FreshRSS $headers = $file->headers; $sniffer = $this->registry->create('Content_Type_Sniffer', array(&$file)); $sniffed = $sniffer->get_type(); @@ -1788,26 +1797,39 @@ class SimplePie /** * Get the URL for the feed + * + * When the 'permanent' mode is enabled, returns the original feed URL, + * except in the case of an `HTTP 301 Moved Permanently` status response, + * in which case the location of the first redirection is returned. * - * May or may not be different from the URL passed to {@see set_feed_url()}, + * When the 'permanent' mode is disabled (default), + * may or may not be different from the URL passed to {@see set_feed_url()}, * depending on whether auto-discovery was used. * * @since Preview Release (previously called `get_feed_url()` since SimplePie 0.8.) - * @todo If we have a perm redirect we should return the new URL - * @todo When we make the above change, let's support <itunes:new-feed-url> as well + * @todo Support <itunes:new-feed-url> * @todo Also, |atom:link|@rel=self + * @param bool $permanent Permanent mode to return only the original URL or the first redirection + * iff it is a 301 redirection * @return string|null */ - public function subscribe_url() + public function subscribe_url($permanent = false) { - if ($this->feed_url !== null) + if ($permanent) //FreshRSS { - return $this->sanitize($this->feed_url, SIMPLEPIE_CONSTRUCT_IRI); + if ($this->permanent_url !== null) + { + return $this->sanitize($this->permanent_url, SIMPLEPIE_CONSTRUCT_IRI); + } } else { - return null; + if ($this->feed_url !== null) + { + return $this->sanitize($this->feed_url, SIMPLEPIE_CONSTRUCT_IRI); + } } + return null; } /** diff --git a/lib/SimplePie/SimplePie/File.php b/lib/SimplePie/SimplePie/File.php index faf5dd1f1..b1bbe4420 100644 --- a/lib/SimplePie/SimplePie/File.php +++ b/lib/SimplePie/SimplePie/File.php @@ -64,6 +64,7 @@ class SimplePie_File var $redirects = 0; var $error; var $method = SIMPLEPIE_FILE_SOURCE_NONE; + var $permanent_url; //FreshRSS public function __construct($url, $timeout = 10, $redirects = 5, $headers = null, $useragent = null, $force_fsockopen = false) { @@ -74,6 +75,7 @@ class SimplePie_File $url = SimplePie_Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']); } $this->url = $url; + $this->permanent_url = $url; //FreshRSS $this->useragent = $useragent; if (preg_match('/^http(s)?:\/\//i', $url)) { @@ -142,7 +144,10 @@ class SimplePie_File { $this->redirects++; $location = SimplePie_Misc::absolutize_url($this->headers['location'], $url); - return $this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen); + $previousStatusCode = $this->status_code; + $this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen); + $this->permanent_url = ($previousStatusCode == 301) ? $location : $url; //FreshRSS + return; } } } @@ -224,7 +229,10 @@ class SimplePie_File { $this->redirects++; $location = SimplePie_Misc::absolutize_url($this->headers['location'], $url); - return $this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen); + $previousStatusCode = $this->status_code; + $this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen); + $this->permanent_url = ($previousStatusCode == 301) ? $location : $url; //FreshRSS + return; } if (isset($this->headers['content-encoding'])) { diff --git a/lib/SimplePie/SimplePie/Parser.php b/lib/SimplePie/SimplePie/Parser.php index 9300b4ba9..7fb7bd9be 100644 --- a/lib/SimplePie/SimplePie/Parser.php +++ b/lib/SimplePie/SimplePie/Parser.php @@ -142,7 +142,7 @@ class SimplePie_Parser $dom = new DOMDocument(); $dom->recover = true; $dom->strictErrorChecking = false; - $dom->loadXML($data); + @$dom->loadXML($data); $this->encoding = $encoding = $dom->encoding = 'UTF-8'; $data2 = $dom->saveXML(); if (function_exists('mb_convert_encoding')) diff --git a/lib/lib_date.php b/lib/lib_date.php new file mode 100644 index 000000000..9533711e3 --- /dev/null +++ b/lib/lib_date.php @@ -0,0 +1,131 @@ +<?php +/** + * Author: Alexandre Alapetite http://alexandre.alapetite.fr + * 2014-06-01 + * License: GNU AGPLv3 http://www.gnu.org/licenses/agpl-3.0.html + * + * Parser of ISO 8601 time intervals http://en.wikipedia.org/wiki/ISO_8601#Time_intervals + * Examples: "2014-02/2014-04", "2014-02/04", "2014-06", "P1M" + */ + +/* +example('2014-03'); +example('201403'); +example('2014-03-30'); +example('2014-05-30T13'); +example('2014-05-30T13:30'); +example('2014-02/2014-04'); +example('2014-02--2014-04'); +example('2014-02/04'); +example('2014-02-03/05'); +example('2014-02-03T22:00/22:15'); +example('2014-02-03T22:00/15'); +example('2014-03/'); +example('/2014-03'); +example('2014-03/P1W'); +example('P1W/2014-05-25T23:59:59'); +example('P1Y/'); +example('P1Y'); +example('P2M/'); +example('P3W/'); +example('P4D/'); +example('PT5H/'); +example('PT6M/'); +example('PT7S/'); +example('P1DT1H/'); + +function example($dateInterval) { + $dateIntervalArray = parseDateInterval($dateInterval); + echo $dateInterval, "\t=>\t", + $dateIntervalArray[0] == null ? 'null' : @date('c', $dateIntervalArray[0]), '/', + $dateIntervalArray[1] == null ? 'null' : @date('c', $dateIntervalArray[1]), "\n"; +} +*/ + +function _dateFloor($isoDate) { + $x = explode('T', $isoDate, 2); + $t = isset($x[1]) ? str_pad($x[1], 6, '0') : '000000'; + return str_pad($x[0], 8, '01') . 'T' . $t; +} + +function _dateCeiling($isoDate) { + $x = explode('T', $isoDate, 2); + $t = isset($x[1]) && strlen($x[1]) > 1 ? str_pad($x[1], 6, '59') : '235959'; + switch (strlen($x[0])) { + case 4: + return $x[0] . '1231T' . $t; + case 6: + $d = @strtotime($x[0] . '01'); + return $x[0] . date('t', $d) . 'T' . $t; + default: + return $x[0] . 'T' . $t; + } +} + +function _noDelimit($isoDate) { + return $isoDate === null || $isoDate === '' ? null : + str_replace(array('-', ':'), '', $isoDate); //FIXME: Bug with negative time zone +} + +function _dateRelative($d1, $d2) { + if ($d2 === null) { + return $d1 !== null && $d1[0] !== 'P' ? $d1 : null; + } elseif ($d2 !== '' && $d2[0] != 'P' && $d1 !== null && $d1[0] !== 'P') { + $y2 = substr($d2, 0, 4); + if (strlen($y2) < 4 || !ctype_digit($y2)) { //Does not start by a year + $d2 = _noDelimit($d2); + return substr($d1, 0, -strlen($d2)) . $d2; //Add prefix from $d1 + } + } + return _noDelimit($d2); +} + +/** + * Parameter $dateInterval is a string containing an ISO 8601 time interval. + * Returns an array with the minimum and maximum Unix timestamp of this interval, + * or null if open interval, or false if error. + */ +function parseDateInterval($dateInterval) { + $dateInterval = trim($dateInterval); + $dateInterval = str_replace('--', '/', $dateInterval); + $dateInterval = strtoupper($dateInterval); + $min = null; + $max = null; + $x = explode('/', $dateInterval, 2); + $d1 = _noDelimit($x[0]); + $d2 = _dateRelative($d1, count($x) > 1 ? $x[1] : null); + if ($d1 !== null && $d1[0] !== 'P') { + $min = @strtotime(_dateFloor($d1)); + } + if ($d2 !== null) { + if ($d2[0] === 'P') { + try { + $di2 = new DateInterval($d2); + $dt1 = @date_create(); //new DateTime() would create an Exception if the default time zone is not defined + if ($min !== null && $min !== false) { + $dt1->setTimestamp($min); + } + $max = $dt1->add($di2)->getTimestamp() - 1; + } catch (Exception $e) { + $max = false; + } + } elseif ($d1 === null || $d1[0] !== 'P') { + $max = @strtotime(_dateCeiling($d2)); + } else { + $max = @strtotime($d2); + } + } + if ($d1 !== null && $d1[0] === 'P') { + try { + $di1 = new DateInterval($d1); + $dt2 = @date_create(); + if ($max !== null && $max !== false) { + $dt2->setTimestamp($max); + } + $min = $dt2->sub($di1)->getTimestamp() + 1; + } catch (Exception $e) { + $min = false; + } + } + return array($min, $max); +} diff --git a/lib/lib_opml.php b/lib/lib_opml.php index 9feb12ae0..16a9921ea 100644 --- a/lib/lib_opml.php +++ b/lib/lib_opml.php @@ -1,23 +1,86 @@ <?php -function opml_export ($cats) { - $txt = ''; - foreach ($cats as $cat) { - $txt .= '<outline text="' . $cat['name'] . '">' . "\n"; - - foreach ($cat['feeds'] as $feed) { - $txt .= "\t" . '<outline text="' . $feed->name () . '" type="rss" xmlUrl="' . $feed->url () . '" htmlUrl="' . $feed->website () . '" description="' . htmlspecialchars($feed->description(), ENT_COMPAT, 'UTF-8') . '" />' . "\n"; +/* * + * lib_opml is a free library to manage OPML format in PHP. + * It takes in consideration only version 2.0 (http://dev.opml.org/spec2.html). + * Basically it means "text" attribute for outline elements is required. + * + * lib_opml requires SimpleXML (http://php.net/manual/en/book.simplexml.php) + * + * Usages: + * > include('lib_opml.php'); + * > $filename = 'my_opml_file.xml'; + * > $opml_array = libopml_parse_file($filename); + * > print_r($opml_array); + * + * > $opml_string = [...]; + * > $opml_array = libopml_parse_string($opml_string); + * > print_r($opml_array); + * + * > $opml_array = [...]; + * > $opml_string = libopml_render($opml_array); + * > $opml_object = libopml_render($opml_array, true); + * > echo $opml_string; + * > print_r($opml_object); + * + * If parsing fails for any reason (e.g. not an XML string, does not match with + * the specifications), a LibOPML_Exception is raised. + * + * Author: Marien Fressinaud <dev@marienfressinaud.fr> + * Url: https://github.com/marienfressinaud/lib_opml + * Version: 0.1 + * Date: 2014-03-29 + * License: public domain + * + * */ + +class LibOPML_Exception extends Exception {} + + +// These elements are optional +define('HEAD_ELEMENTS', serialize(array( + 'title', 'dateCreated', 'dateModified', 'ownerName', 'ownerEmail', + 'ownerId', 'docs', 'expansionState', 'vertScrollState', 'windowTop', + 'windowLeft', 'windowBottom', 'windowRight' +))); + + +function libopml_parse_outline($outline_xml) { + $outline = array(); + + // An outline may contain any kind of attributes but "text" attribute is + // required ! + $text_is_present = false; + foreach ($outline_xml->attributes() as $key => $value) { + $outline[$key] = (string)$value; + + if ($key === 'text') { + $text_is_present = true; } + } - $txt .= '</outline>' . "\n"; + if (!$text_is_present) { + throw new LibOPML_Exception( + 'Outline does not contain any text attribute' + ); } - return $txt; + foreach ($outline_xml->children() as $key => $value) { + // An outline may contain any number of outline children + if ($key === 'outline') { + $outline['@outlines'][] = libopml_parse_outline($value); + } else { + throw new LibOPML_Exception( + 'Body can contain only outline elements' + ); + } + } + + return $outline; } -function opml_import ($xml) { - $xml = html_only_entity_decode($xml); //!\ Assume UTF-8 +function libopml_parse_string($xml) { $dom = new DOMDocument(); $dom->recover = true; $dom->strictErrorChecking = false; @@ -27,94 +90,142 @@ function opml_import ($xml) { $opml = simplexml_import_dom($dom); if (!$opml) { - throw new FreshRSS_Opml_Exception (); + throw new LibOPML_Exception(); } - $catDAO = new FreshRSS_CategoryDAO(); - $catDAO->checkDefault(); - $defCat = $catDAO->getDefault(); + $array = array( + 'version' => (string)$opml['version'], + 'head' => array(), + 'body' => array() + ); + + // First, we get all "head" elements. Head is required but its sub-elements + // are optional. + foreach ($opml->head->children() as $key => $value) { + if (in_array($key, unserialize(HEAD_ELEMENTS), true)) { + $array['head'][$key] = (string)$value; + } else { + throw new LibOPML_Exception( + $key . 'is not part of OPML format' + ); + } + } - $categories = array (); - $feeds = array (); + // Then, we get body oulines. Body must contain at least one outline + // element. + $at_least_one_outline = false; + foreach ($opml->body->children() as $key => $value) { + if ($key === 'outline') { + $at_least_one_outline = true; + $array['body'][] = libopml_parse_outline($value); + } else { + throw new LibOPML_Exception( + 'Body can contain only outline elements' + ); + } + } + + if (!$at_least_one_outline) { + throw new LibOPML_Exception( + 'Body must contain at least one outline element' + ); + } - foreach ($opml->body->outline as $outline) { - if (!isset ($outline['xmlUrl'])) { - // Catégorie - $title = ''; + return $array; +} - if (isset ($outline['text'])) { - $title = (string) $outline['text']; - } elseif (isset ($outline['title'])) { - $title = (string) $outline['title']; - } - if ($title) { - // Permet d'éviter les soucis au niveau des id : - // ceux-ci sont générés en fonction de la date, - // un flux pourrait être dans une catégorie X avec l'id Y - // alors qu'il existe déjà la catégorie X mais avec l'id Z - // Y ne sera pas ajouté et le flux non plus vu que l'id - // de sa catégorie n'exisera pas - $title = htmlspecialchars($title, ENT_COMPAT, 'UTF-8'); - $catDAO = new FreshRSS_CategoryDAO (); - $cat = $catDAO->searchByName ($title); - if ($cat === false) { - $cat = new FreshRSS_Category ($title); - $values = array ( - 'name' => $cat->name () - ); - $cat->_id ($catDAO->addCategory ($values)); - } - - $feeds = array_merge ($feeds, getFeedsOutline ($outline, $cat->id ())); +function libopml_parse_file($filename) { + $file_content = file_get_contents($filename); + + if ($file_content === false) { + throw new LibOPML_Exception( + $filename . ' cannot be found' + ); + } + + return libopml_parse_string($file_content); +} + + +function libopml_render_outline($parent_elt, $outline) { + // Outline MUST be an array! + if (!is_array($outline)) { + throw new LibOPML_Exception( + 'Outline element must be defined as array' + ); + } + + $outline_elt = $parent_elt->addChild('outline'); + $text_is_present = false; + foreach ($outline as $key => $value) { + // Only outlines can be an array and so we consider children are also + // outline elements. + if ($key === '@outlines' && is_array($value)) { + foreach ($value as $outline_child) { + libopml_render_outline($outline_elt, $outline_child); } + } elseif (is_array($value)) { + throw new LibOPML_Exception( + 'Type of outline elements cannot be array: ' . $key + ); } else { - // Flux rss sans catégorie, on récupère l'ajoute dans la catégorie par défaut - $feeds[] = getFeed ($outline, $defCat->id()); + // Detect text attribute is present, that's good :) + if ($key === 'text') { + $text_is_present = true; + } + + $outline_elt->addAttribute($key, $value); } } - return array ($categories, $feeds); + if (!$text_is_present) { + throw new LibOPML_Exception( + 'You must define at least a text element for all outlines' + ); + } } -/** - * import all feeds of a given outline tag - */ -function getFeedsOutline ($outline, $cat_id) { - $feeds = array (); - foreach ($outline->children () as $child) { - if (isset ($child['xmlUrl'])) { - $feeds[] = getFeed ($child, $cat_id); - } else { - $feeds = array_merge( - $feeds, - getFeedsOutline ($child, $cat_id) - ); +function libopml_render($array, $as_xml_object = false) { + $opml = new SimpleXMLElement('<opml version="2.0"></opml>'); + + // Create head element. $array['head'] is optional but head element will + // exist in the final XML object. + $head = $opml->addChild('head'); + if (isset($array['head'])) { + foreach ($array['head'] as $key => $value) { + if (in_array($key, unserialize(HEAD_ELEMENTS), true)) { + $head->addChild($key, $value); + } } } - return $feeds; -} + // Check body is set and contains at least one element + if (!isset($array['body'])) { + throw new LibOPML_Exception( + '$array must contain a body element' + ); + } + if (count($array['body']) <= 0) { + throw new LibOPML_Exception( + 'Body element must contain at least one element (array)' + ); + } -function getFeed ($outline, $cat_id) { - $url = (string) $outline['xmlUrl']; - $url = htmlspecialchars($url, ENT_COMPAT, 'UTF-8'); - $title = ''; - if (isset ($outline['text'])) { - $title = (string) $outline['text']; - } elseif (isset ($outline['title'])) { - $title = (string) $outline['title']; - } - $title = htmlspecialchars($title, ENT_COMPAT, 'UTF-8'); - $feed = new FreshRSS_Feed ($url); - $feed->_category ($cat_id); - $feed->_name ($title); - if (isset($outline['htmlUrl'])) { - $feed->_website(htmlspecialchars((string)$outline['htmlUrl'], ENT_COMPAT, 'UTF-8')); - } - if (isset($outline['description'])) { - $feed->_description(sanitizeHTML((string)$outline['description'])); - } - return $feed; + // Create outline elements + $body = $opml->addChild('body'); + foreach ($array['body'] as $outline) { + libopml_render_outline($body, $outline); + } + + // And return the final result + if ($as_xml_object) { + return $opml; + } else { + $dom = dom_import_simplexml($opml)->ownerDocument; + $dom->formatOutput = true; + $dom->encoding = 'UTF-8'; + return $dom->saveXML(); + } } diff --git a/lib/lib_rss.php b/lib/lib_rss.php index a13d9e951..31c9cdbc1 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -27,7 +27,7 @@ function classAutoloader($class) { include(APP_PATH . '/Models/' . $components[1] . '.php'); return; case 3: //Controllers, Exceptions - include(APP_PATH . '/' . $components[2] . 's/' . $components[1] . $components[2] . '.php'); + @include(APP_PATH . '/' . $components[2] . 's/' . $components[1] . $components[2] . '.php'); return; } } elseif (strpos($class, 'Minz') === 0) { @@ -56,12 +56,6 @@ function checkUrl($url) { } } -// tiré de Shaarli de Seb Sauvage //Format RFC 4648 base64url -function small_hash ($txt) { - $t = rtrim (base64_encode (hash ('crc32', $txt, true)), '='); - return strtr ($t, '+/', '-_'); -} - function formatNumber($n, $precision = 0) { return str_replace(' ', ' ', //Espace insécable //TODO: remplacer par une espace _fine_ insécable number_format($n, $precision, '.', ' ')); //number_format does not seem to be Unicode-compatible @@ -115,7 +109,7 @@ function customSimplePie() { $simplePie = new SimplePie(); $simplePie->set_useragent(Minz_Translate::t('freshrss') . '/' . FRESHRSS_VERSION . ' (' . PHP_OS . '; ' . FRESHRSS_WEBSITE . ') ' . SIMPLEPIE_NAME . '/' . SIMPLEPIE_VERSION); $simplePie->set_cache_location(CACHE_PATH); - $simplePie->set_cache_duration(1500); + $simplePie->set_cache_duration(800); $simplePie->strip_htmltags(array( 'base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', @@ -127,10 +121,10 @@ function customSimplePie() { 'onmouseover', 'onmousemove', 'onmouseout', 'onfocus', 'onblur', 'onkeypress', 'onkeydown', 'onkeyup', 'onselect', 'onchange', 'seamless'))); $simplePie->add_attributes(array( - 'img' => array('lazyload' => ''), //http://www.w3.org/TR/resource-priorities/ - 'audio' => array('preload' => 'none'), - 'iframe' => array('postpone' => '', 'sandbox' => 'allow-scripts allow-same-origin'), - 'video' => array('postpone' => '', 'preload' => 'none'), + 'img' => array('lazyload' => '', 'postpone' => ''), //http://www.w3.org/TR/resource-priorities/ + 'audio' => array('lazyload' => '', 'postpone' => '', 'preload' => 'none'), + 'iframe' => array('lazyload' => '', 'postpone' => '', 'sandbox' => 'allow-scripts allow-same-origin'), + 'video' => array('lazyload' => '', 'postpone' => '', 'preload' => 'none'), )); $simplePie->set_url_replacements(array( 'a' => 'href', @@ -189,16 +183,8 @@ function get_content_by_parsing ($url, $path) { */ function lazyimg($content) { return preg_replace( - '/<img([^>]+?)src=[\'"]([^"\']+)[\'"]([^>]*)>/i', - '<img$1src="' . Minz_Url::display('/themes/icons/grey.gif') . '" data-original="$2"$3>', - $content - ); -} - -function lazyIframe($content) { - return preg_replace( - '/<iframe([^>]+?)src=[\'"]([^"\']+)[\'"]([^>]*)>/i', - '<iframe$1src="about:blank" data-original="$2"$3>', + '/<((?:img|iframe)[^>]+?)src=[\'"]([^"\']+)[\'"]([^>]*)>/i', + '<$1src="' . Minz_Url::display('/themes/icons/grey.gif') . '" data-original="$2"$3>', $content ); } @@ -219,7 +205,7 @@ function invalidateHttpCache() { } function usernameFromPath($userPath) { - if (preg_match('%/([a-z0-9]{1,16})_user\.php$%', $userPath, $matches)) { + if (preg_match('%/([A-Za-z0-9]{1,16})_user\.php$%', $userPath, $matches)) { return $matches[1]; } else { return ''; @@ -233,3 +219,28 @@ function listUsers() { function httpAuthUser() { return isset($_SERVER['REMOTE_USER']) ? $_SERVER['REMOTE_USER'] : ''; } + +function cryptAvailable() { + if (version_compare(PHP_VERSION, '5.3.3', '>=')) { + try { + $hash = '$2y$04$usesomesillystringfore7hnbRJHxXVLeakoG8K30oukPsA.ztMG'; + return $hash === @crypt('password', $hash); + } catch (Exception $e) { + } + } + return false; +} + +function is_referer_from_same_domain() { + if (empty($_SERVER['HTTP_REFERER'])) { + return false; + } + $host = parse_url(((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') ? 'https://' : 'http://') . + (empty($_SERVER['HTTP_HOST']) ? $_SERVER['SERVER_NAME'] : $_SERVER['HTTP_HOST'])); + $referer = parse_url($_SERVER['HTTP_REFERER']); + if (empty($host['scheme']) || empty($referer['scheme']) || $host['scheme'] !== $referer['scheme'] || + empty($host['host']) || empty($referer['host']) || $host['host'] !== $referer['host']) { + return false; + } + return (isset($host['port']) ? $host['port'] : 0) === (isset($referer['port']) ? $referer['port'] : 0); +} |
