diff --git a/app/Models/Category.php b/app/Models/Category.php
new file mode 100644
index 000000000..e70d1303f
--- /dev/null
+++ b/app/Models/Category.php
@@ -0,0 +1,85 @@
+_name ($name);
+ $this->_color ($color);
+ if (isset ($feeds)) {
+ $this->_feeds ($feeds);
+ $this->nbFeed = 0;
+ $this->nbNotRead = 0;
+ foreach ($feeds as $feed) {
+ $this->nbFeed++;
+ $this->nbNotRead += $feed->nbNotRead ();
+ }
+ }
+ }
+
+ public function id () {
+ return $this->id;
+ }
+ public function name () {
+ return $this->name;
+ }
+ public function color () {
+ return $this->color;
+ }
+ public function nbFeed () {
+ if ($this->nbFeed < 0) {
+ $catDAO = new FreshRSS_CategoryDAO ();
+ $this->nbFeed = $catDAO->countFeed ($this->id ());
+ }
+
+ return $this->nbFeed;
+ }
+ public function nbNotRead () {
+ if ($this->nbNotRead < 0) {
+ $catDAO = new FreshRSS_CategoryDAO ();
+ $this->nbNotRead = $catDAO->countNotRead ($this->id ());
+ }
+
+ return $this->nbNotRead;
+ }
+ public function feeds () {
+ if (is_null ($this->feeds)) {
+ $feedDAO = new FreshRSS_FeedDAO ();
+ $this->feeds = $feedDAO->listByCategory ($this->id ());
+ $this->nbFeed = 0;
+ $this->nbNotRead = 0;
+ foreach ($this->feeds as $feed) {
+ $this->nbFeed++;
+ $this->nbNotRead += $feed->nbNotRead ();
+ }
+ }
+
+ return $this->feeds;
+ }
+
+ public function _id ($value) {
+ $this->id = $value;
+ }
+ public function _name ($value) {
+ $this->name = $value;
+ }
+ public function _color ($value) {
+ if (preg_match ('/^#([0-9a-f]{3}|[0-9a-f]{6})$/i', $value)) {
+ $this->color = $value;
+ } else {
+ $this->color = '#0062BE';
+ }
+ }
+ public function _feeds ($values) {
+ if (!is_array ($values)) {
+ $values = array ($values);
+ }
+
+ $this->feeds = $values;
+ }
+}
diff --git a/app/Models/Days.php b/app/Models/Days.php
new file mode 100644
index 000000000..2d770c30b
--- /dev/null
+++ b/app/Models/Days.php
@@ -0,0 +1,7 @@
+_guid ($guid);
+ $this->_title ($title);
+ $this->_author ($author);
+ $this->_content ($content);
+ $this->_link ($link);
+ $this->_date ($pubdate);
+ $this->_isRead ($is_read);
+ $this->_isFavorite ($is_favorite);
+ $this->_feed ($feed);
+ $this->_tags (preg_split('/[\s#]/', $tags));
+ }
+
+ public function id () {
+ return $this->id;
+ }
+ public function guid () {
+ return $this->guid;
+ }
+ public function title () {
+ return $this->title;
+ }
+ public function author () {
+ if (is_null ($this->author)) {
+ return '';
+ } else {
+ return $this->author;
+ }
+ }
+ public function content () {
+ return $this->content;
+ }
+ public function link () {
+ return $this->link;
+ }
+ public function date ($raw = false) {
+ if ($raw) {
+ return $this->date;
+ } else {
+ return timestamptodate ($this->date);
+ }
+ }
+ public function dateAdded ($raw = false) {
+ $date = intval(substr($this->id, 0, -6));
+ if ($raw) {
+ return $date;
+ } else {
+ return timestamptodate ($date);
+ }
+ }
+ public function isRead () {
+ return $this->is_read;
+ }
+ public function isFavorite () {
+ return $this->is_favorite;
+ }
+ public function feed ($object = false) {
+ if ($object) {
+ $feedDAO = new FreshRSS_FeedDAO ();
+ return $feedDAO->searchById ($this->feed);
+ } else {
+ return $this->feed;
+ }
+ }
+ public function tags ($inString = false) {
+ if ($inString) {
+ return empty ($this->tags) ? '' : '#' . implode(' #', $this->tags);
+ } else {
+ return $this->tags;
+ }
+ }
+
+ public function _id ($value) {
+ $this->id = $value;
+ }
+ public function _guid ($value) {
+ $this->guid = $value;
+ }
+ public function _title ($value) {
+ $this->title = $value;
+ }
+ public function _author ($value) {
+ $this->author = $value;
+ }
+ public function _content ($value) {
+ $this->content = $value;
+ }
+ public function _link ($value) {
+ $this->link = $value;
+ }
+ public function _date ($value) {
+ if (ctype_digit ($value)) {
+ $this->date = intval ($value);
+ } else {
+ $this->date = time ();
+ }
+ }
+ public function _isRead ($value) {
+ $this->is_read = $value;
+ }
+ public function _isFavorite ($value) {
+ $this->is_favorite = $value;
+ }
+ public function _feed ($value) {
+ $this->feed = $value;
+ }
+ public function _tags ($value) {
+ if (!is_array ($value)) {
+ $value = array ($value);
+ }
+
+ foreach ($value as $key => $t) {
+ if (!$t) {
+ unset ($value[$key]);
+ }
+ }
+
+ $this->tags = $value;
+ }
+
+ public function isDay ($day) {
+ $date = $this->dateAdded(true);
+ $today = @strtotime('today');
+ $yesterday = $today - 86400;
+
+ if ($day === FreshRSS_Days::TODAY &&
+ $date >= $today && $date < $today + 86400) {
+ return true;
+ } elseif ($day === FreshRSS_Days::YESTERDAY &&
+ $date >= $yesterday && $date < $yesterday + 86400) {
+ return true;
+ } elseif ($day === FreshRSS_Days::BEFORE_YESTERDAY && $date < $yesterday) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public function loadCompleteContent($pathEntries) {
+ // Gestion du contenu
+ // On cherche à récupérer les articles en entier... même si le flux ne le propose pas
+ if ($pathEntries) {
+ $entryDAO = new FreshRSS_EntryDAO();
+ $entry = $entryDAO->searchByGuid($this->feed, $this->guid);
+
+ if($entry) {
+ // l'article existe déjà en BDD, en se contente de recharger ce contenu
+ $this->content = $entry->content();
+ } else {
+ try {
+ // l'article n'est pas en BDD, on va le chercher sur le site
+ $this->content = get_content_by_parsing(
+ $this->link(), $pathEntries
+ );
+ } catch (Exception $e) {
+ // rien à faire, on garde l'ancien contenu (requête a échoué)
+ }
+ }
+ }
+ }
+
+ public function toArray () {
+ return array (
+ 'id' => $this->id (),
+ 'guid' => $this->guid (),
+ 'title' => $this->title (),
+ 'author' => $this->author (),
+ 'content' => $this->content (),
+ 'link' => $this->link (),
+ 'date' => $this->date (true),
+ 'is_read' => $this->isRead (),
+ 'is_favorite' => $this->isFavorite (),
+ 'id_feed' => $this->feed (),
+ 'tags' => $this->tags (true),
+ );
+ }
+}
diff --git a/app/Models/Feed.php b/app/Models/Feed.php
new file mode 100644
index 000000000..e63ac8c7a
--- /dev/null
+++ b/app/Models/Feed.php
@@ -0,0 +1,319 @@
+_url ($url);
+ } else {
+ $this->url = $url;
+ }
+ }
+
+ public function id () {
+ return $this->id;
+ }
+ public function url () {
+ return $this->url;
+ }
+ public function category () {
+ return $this->category;
+ }
+ public function entries () {
+ if (!is_null ($this->entries)) {
+ return $this->entries;
+ } else {
+ return array ();
+ }
+ }
+ public function name () {
+ return $this->name;
+ }
+ public function website () {
+ return $this->website;
+ }
+ public function description () {
+ return $this->description;
+ }
+ public function lastUpdate () {
+ return $this->lastUpdate;
+ }
+ public function priority () {
+ return $this->priority;
+ }
+ public function pathEntries () {
+ return $this->pathEntries;
+ }
+ public function httpAuth ($raw = true) {
+ if ($raw) {
+ return $this->httpAuth;
+ } else {
+ $pos_colon = strpos ($this->httpAuth, ':');
+ $user = substr ($this->httpAuth, 0, $pos_colon);
+ $pass = substr ($this->httpAuth, $pos_colon + 1);
+
+ return array (
+ 'username' => $user,
+ 'password' => $pass
+ );
+ }
+ }
+ public function inError () {
+ return $this->error;
+ }
+ public function keepHistory () {
+ return $this->keep_history;
+ }
+ public function nbEntries () {
+ if ($this->nbEntries < 0) {
+ $feedDAO = new FreshRSS_FeedDAO ();
+ $this->nbEntries = $feedDAO->countEntries ($this->id ());
+ }
+
+ return $this->nbEntries;
+ }
+ public function nbNotRead () {
+ if ($this->nbNotRead < 0) {
+ $feedDAO = new FreshRSS_FeedDAO ();
+ $this->nbNotRead = $feedDAO->countNotRead ($this->id ());
+ }
+
+ return $this->nbNotRead;
+ }
+ public function faviconPrepare() {
+ $file = DATA_PATH . '/favicons/' . $this->id () . '.txt';
+ if (!file_exists ($file)) {
+ $t = $this->website;
+ if (empty($t)) {
+ $t = $this->url;
+ }
+ file_put_contents($file, $t);
+ }
+ }
+ public static function faviconDelete($id) {
+ $path = DATA_PATH . '/favicons/' . $id;
+ @unlink($path . '.ico');
+ @unlink($path . '.txt');
+ }
+ public function favicon () {
+ return Minz_Url::display ('/f.php?' . $this->id ());
+ }
+
+ public function _id ($value) {
+ $this->id = $value;
+ }
+ public function _url ($value, $validate=true) {
+ if ($validate) {
+ $value = checkUrl($value);
+ }
+ if (empty ($value)) {
+ throw new FreshRSS_BadUrl_Exception ($value);
+ }
+ $this->url = $value;
+ }
+ public function _category ($value) {
+ $this->category = $value;
+ }
+ public function _name ($value) {
+ if (is_null ($value)) {
+ $value = '';
+ }
+ $this->name = $value;
+ }
+ public function _website ($value, $validate=true) {
+ if ($validate) {
+ $value = checkUrl($value);
+ }
+ if (empty ($value)) {
+ $value = '';
+ }
+ $this->website = $value;
+ }
+ public function _description ($value) {
+ if (is_null ($value)) {
+ $value = '';
+ }
+ $this->description = $value;
+ }
+ public function _lastUpdate ($value) {
+ $this->lastUpdate = $value;
+ }
+ public function _priority ($value) {
+ $this->priority = ctype_digit ($value) ? intval ($value) : 10;
+ }
+ public function _pathEntries ($value) {
+ $this->pathEntries = $value;
+ }
+ public function _httpAuth ($value) {
+ $this->httpAuth = $value;
+ }
+ public function _error ($value) {
+ if ($value) {
+ $value = true;
+ } else {
+ $value = false;
+ }
+ $this->error = $value;
+ }
+ public function _keepHistory ($value) {
+ if ($value) {
+ $value = true;
+ } else {
+ $value = false;
+ }
+ $this->keep_history = $value;
+ }
+ public function _nbNotRead ($value) {
+ $this->nbNotRead = ctype_digit ($value) ? intval ($value) : -1;
+ }
+ public function _nbEntries ($value) {
+ $this->nbEntries = ctype_digit ($value) ? intval ($value) : -1;
+ }
+
+ public function load () {
+ if (!is_null ($this->url)) {
+ if (CACHE_PATH === false) {
+ throw new Minz_FileNotExistException (
+ 'CACHE_PATH',
+ Minz_Exception::ERROR
+ );
+ } else {
+ $feed = new SimplePie ();
+ $feed->set_useragent(Minz_Translate::t ('freshrss') . '/' . FRESHRSS_VERSION . ' (' . PHP_OS . '; ' . FRESHRSS_WEBSITE . ') ' . SIMPLEPIE_NAME . '/' . SIMPLEPIE_VERSION);
+ $url = htmlspecialchars_decode ($this->url, ENT_QUOTES);
+ if ($this->httpAuth != '') {
+ $url = preg_replace ('#((.+)://)(.+)#', '${1}' . $this->httpAuth . '@${3}', $url);
+ }
+
+ $feed->set_feed_url ($url);
+ $feed->set_cache_location (CACHE_PATH);
+ $feed->set_cache_duration(1500);
+ $feed->strip_htmltags (array (
+ 'base', 'blink', 'body', 'doctype', 'embed',
+ 'font', 'form', 'frame', 'frameset', 'html',
+ 'input', 'marquee', 'meta', 'noscript',
+ 'object', 'param', 'plaintext', 'script', 'style',
+ ));
+ $feed->strip_attributes(array_merge($feed->strip_attributes, array(
+ 'autoplay', 'onload', 'onunload', 'onclick', 'ondblclick', 'onmousedown', 'onmouseup',
+ 'onmouseover', 'onmousemove', 'onmouseout', 'onfocus', 'onblur',
+ 'onkeypress', 'onkeydown', 'onkeyup', 'onselect', 'onchange', 'seamless')));
+ $feed->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'),
+ ));
+ $feed->set_url_replacements(array(
+ 'a' => 'href',
+ 'area' => 'href',
+ 'audio' => 'src',
+ 'blockquote' => 'cite',
+ 'del' => 'cite',
+ 'form' => 'action',
+ 'iframe' => 'src',
+ 'img' => array(
+ 'longdesc',
+ 'src'
+ ),
+ 'input' => 'src',
+ 'ins' => 'cite',
+ 'q' => 'cite',
+ 'source' => 'src',
+ 'track' => 'src',
+ 'video' => array(
+ 'poster',
+ 'src',
+ ),
+ ));
+ $feed->init ();
+
+ if ($feed->error ()) {
+ throw new FreshRSS_Feed_Exception ($feed->error . ' [' . $url . ']');
+ }
+
+ // si on a utilisé l'auto-discover, notre url va avoir changé
+ $subscribe_url = $feed->subscribe_url ();
+ if (!is_null ($subscribe_url) && $subscribe_url != $this->url) {
+ if ($this->httpAuth != '') {
+ // on enlève les id si authentification HTTP
+ $subscribe_url = preg_replace ('#((.+)://)((.+)@)(.+)#', '${1}${5}', $subscribe_url);
+ }
+ $this->_url ($subscribe_url);
+ }
+
+ $title = $feed->get_title ();
+ $this->_name (!is_null ($title) ? $title : $this->url);
+
+ $this->_website ($feed->get_link ());
+ $this->_description ($feed->get_description ());
+
+ // et on charge les articles du flux
+ $this->loadEntries ($feed);
+ }
+ }
+ }
+ private function loadEntries ($feed) {
+ $entries = array ();
+
+ foreach ($feed->get_items () as $item) {
+ $title = html_only_entity_decode (strip_tags ($item->get_title ()));
+ $author = $item->get_author ();
+ $link = $item->get_permalink ();
+ $date = @strtotime ($item->get_date ());
+
+ // gestion des tags (catégorie == tag)
+ $tags_tmp = $item->get_categories ();
+ $tags = array ();
+ if (!is_null ($tags_tmp)) {
+ foreach ($tags_tmp as $tag) {
+ $tags[] = html_only_entity_decode ($tag->get_label ());
+ }
+ }
+
+ $content = html_only_entity_decode ($item->get_content ());
+
+ $elinks = array();
+ foreach ($item->get_enclosures() as $enclosure) {
+ $elink = $enclosure->get_link();
+ if (array_key_exists($elink, $elinks)) continue;
+ $elinks[$elink] = '1';
+ $mime = strtolower($enclosure->get_type());
+ if (strpos($mime, 'image/') === 0) {
+ $content .= '';
+ }
+ }
+
+ $entry = new FreshRSS_Entry (
+ $this->id (),
+ $item->get_id (),
+ !is_null ($title) ? $title : '',
+ !is_null ($author) ? html_only_entity_decode ($author->name) : '',
+ !is_null ($content) ? $content : '',
+ !is_null ($link) ? $link : '',
+ $date ? $date : time ()
+ );
+ $entry->_tags ($tags);
+ // permet de récupérer le contenu des flux tronqués
+ $entry->loadCompleteContent($this->pathEntries());
+
+ $entries[] = $entry;
+ }
+
+ $this->entries = $entries;
+ }
+}
diff --git a/lib/Minz/ActionController.php b/lib/Minz/ActionController.php
new file mode 100644
index 000000000..409d9611f
--- /dev/null
+++ b/lib/Minz/ActionController.php
@@ -0,0 +1,42 @@
+
+*/
+
+/**
+ * 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;
+ $this->view = new Minz_View ();
+ $this->view->attributeParams ();
+ }
+
+ /**
+ * Getteur
+ */
+ public function view () {
+ return $this->view;
+ }
+
+ /**
+ * Méthodes à redéfinir (ou non) par héritage
+ * firstAction est la première méthode exécutée par le Dispatcher
+ * lastAction est la dernière
+ */
+ public function init () { }
+ public function firstAction () { }
+ public function lastAction () { }
+}
+
+
diff --git a/lib/Minz/Configuration.php b/lib/Minz/Configuration.php
new file mode 100644
index 000000000..9fc913964
--- /dev/null
+++ b/lib/Minz/Configuration.php
@@ -0,0 +1,262 @@
+
+*/
+
+/**
+ * La classe Configuration permet de gérer la configuration de l'application
+ */
+class Minz_Configuration {
+ const CONF_PATH_NAME = '/application.ini';
+
+ /**
+ * VERSION est la version actuelle de MINZ
+ */
+ const VERSION = '1.3.1.freshrss'; // version spéciale FreshRSS
+
+ /**
+ * valeurs possibles pour l'"environment"
+ * SILENT rend l'application muette (pas de log)
+ * PRODUCTION est recommandée pour une appli en production
+ * (log les erreurs critiques)
+ * DEVELOPMENT log toutes les erreurs
+ */
+ const SILENT = 0;
+ const PRODUCTION = 1;
+ const DEVELOPMENT = 2;
+
+ /**
+ * définition des variables de configuration
+ * $sel_application 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
+ * - password mot de passe de l'utilisateur
+ * - base le nom de la base de données
+ */
+ private static $sel_application = '';
+ 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 $current_user = '';
+
+ private static $db = array (
+ 'host' => false,
+ 'user' => false,
+ 'password' => false,
+ 'base' => false
+ );
+
+ /*
+ * Getteurs
+ */
+ public static function selApplication () {
+ return self::$sel_application;
+ }
+ public static function environment () {
+ return self::$environment;
+ }
+ public static function baseUrl () {
+ return self::$base_url;
+ }
+ public static function useUrlRewriting () {
+ return self::$use_url_rewriting;
+ }
+ public static function title () {
+ return stripslashes(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;
+ }
+ public static function defaultUser () {
+ return self::$default_user;
+ }
+ public static function currentUser () {
+ return self::$current_user;
+ }
+
+ /**
+ * Initialise les variables de configuration
+ * @exception Minz_FileNotExistException si le CONF_PATH_NAME n'existe pas
+ * @exception Minz_BadConfigurationException si CONF_PATH_NAME mal formaté
+ */
+ public static function init () {
+ try {
+ self::parseFile ();
+ self::setReporting ();
+ } catch (Minz_FileNotExistException $e) {
+ throw $e;
+ } catch (Minz_BadConfigurationException $e) {
+ throw $e;
+ }
+ }
+
+ /**
+ * Parse un fichier de configuration de type ".ini"
+ * @exception Minz_FileNotExistException si le CONF_PATH_NAME n'existe pas
+ * @exception Minz_BadConfigurationException si CONF_PATH_NAME mal formaté
+ */
+ private static function parseFile () {
+ if (!file_exists (DATA_PATH . self::CONF_PATH_NAME)) {
+ throw new Minz_FileNotExistException (
+ DATA_PATH . self::CONF_PATH_NAME,
+ Minz_Exception::ERROR
+ );
+ }
+
+ $ini_array = parse_ini_file (
+ DATA_PATH . self::CONF_PATH_NAME,
+ true
+ );
+
+ if (!$ini_array) {
+ throw new Minz_PermissionDeniedException (
+ DATA_PATH . self::CONF_PATH_NAME,
+ Minz_Exception::ERROR
+ );
+ }
+
+ // [general] est obligatoire
+ if (!isset ($ini_array['general'])) {
+ throw new Minz_BadConfigurationException (
+ '[general]',
+ Minz_Exception::ERROR
+ );
+ }
+ $general = $ini_array['general'];
+
+
+ // sel_application est obligatoire
+ if (!isset ($general['sel_application'])) {
+ throw new Minz_BadConfigurationException (
+ 'sel_application',
+ Minz_Exception::ERROR
+ );
+ }
+ self::$sel_application = $general['sel_application'];
+
+ if (isset ($general['environment'])) {
+ switch ($general['environment']) {
+ case 'silent':
+ self::$environment = Minz_Configuration::SILENT;
+ break;
+ case 'development':
+ self::$environment = Minz_Configuration::DEVELOPMENT;
+ break;
+ case 'production':
+ self::$environment = Minz_Configuration::PRODUCTION;
+ break;
+ default:
+ throw new Minz_BadConfigurationException (
+ 'environment',
+ Minz_Exception::ERROR
+ );
+ }
+
+ }
+ 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'];
+ }
+ 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 = $general['delay_cache'];
+ }
+ if (isset ($general['default_user'])) {
+ self::$default_user = $general['default_user'];
+ self::$current_user = self::$default_user;
+ }
+
+ // Base de données
+ $db = false;
+ if (isset ($ini_array['db'])) {
+ $db = $ini_array['db'];
+ }
+ if ($db) {
+ if (!isset ($db['host'])) {
+ throw new Minz_BadConfigurationException (
+ 'host',
+ Minz_Exception::ERROR
+ );
+ }
+ if (!isset ($db['user'])) {
+ throw new Minz_BadConfigurationException (
+ 'user',
+ Minz_Exception::ERROR
+ );
+ }
+ if (!isset ($db['password'])) {
+ throw new Minz_BadConfigurationException (
+ 'password',
+ Minz_Exception::ERROR
+ );
+ }
+ if (!isset ($db['base'])) {
+ throw new Minz_BadConfigurationException (
+ 'base',
+ Minz_Exception::ERROR
+ );
+ }
+
+ self::$db['type'] = isset ($db['type']) ? $db['type'] : 'mysql';
+ self::$db['host'] = $db['host'];
+ self::$db['user'] = $db['user'];
+ self::$db['password'] = $db['password'];
+ self::$db['base'] = $db['base'];
+ self::$db['prefix'] = isset ($db['prefix']) ? $db['prefix'] : '';
+ }
+ }
+
+ private static function setReporting () {
+ if (self::environment () == self::DEVELOPMENT) {
+ error_reporting (E_ALL);
+ ini_set ('display_errors','On');
+ ini_set('log_errors', 'On');
+ } elseif (self::environment () == self::PRODUCTION) {
+ error_reporting(E_ALL);
+ ini_set('display_errors','Off');
+ ini_set('log_errors', 'On');
+ } else {
+ error_reporting(0);
+ }
+ }
+}
diff --git a/lib/Minz/Dispatcher.php b/lib/Minz/Dispatcher.php
new file mode 100644
index 000000000..2898b5f00
--- /dev/null
+++ b/lib/Minz/Dispatcher.php
@@ -0,0 +1,138 @@
+
+*/
+
+/**
+ * Le Dispatcher s'occupe d'initialiser le Controller et d'executer l'action
+ * déterminée dans la Request
+ * C'est un singleton
+ */
+class Minz_Dispatcher {
+ const CONTROLLERS_PATH_NAME = '/Controllers';
+
+ /* singleton */
+ private static $instance = null;
+
+ private $router;
+ private $controller;
+
+ /**
+ * Récupère l'instance du Dispatcher
+ */
+ public static function getInstance ($router) {
+ if (is_null (self::$instance)) {
+ self::$instance = new Minz_Dispatcher ($router);
+ }
+ 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 () {
+ $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 :(
+ ob_start ('ob_gzhandler');
+
+ if (Minz_Cache::isEnabled () && !$cache->expired ()) {
+ ob_start ();
+ $cache->render ();
+ $text = ob_get_clean();
+ } else {
+ while (Minz_Request::$reseted) {
+ Minz_Request::$reseted = false;
+
+ try {
+ $this->createController ('FreshRSS_' . Minz_Request::controllerName () . '_Controller');
+ $this->controller->init ();
+ $this->controller->firstAction ();
+ $this->launchAction (
+ Minz_Request::actionName ()
+ . 'Action'
+ );
+ $this->controller->lastAction ();
+
+ if (!Minz_Request::$reseted) {
+ ob_start ();
+ $this->controller->view ()->build ();
+ $text = ob_get_clean();
+ }
+ } catch (Minz_Exception $e) {
+ throw $e;
+ }
+ }
+
+ if (Minz_Cache::isEnabled ()) {
+ $cache->cache ($text);
+ }
+ }
+
+ Minz_Response::setBody ($text);
+ }
+
+ /**
+ * Instancie le Controller
+ * @param $controller_name le nom du controller à instancier
+ * @exception ControllerNotExistException le controller n'existe pas
+ * @exception ControllerNotActionControllerException controller n'est
+ * > pas une instance de ActionController
+ */
+ private function createController ($controller_name) {
+ $filename = APP_PATH . self::CONTROLLERS_PATH_NAME . '/'
+ . $controller_name . '.php';
+
+ if (!class_exists ($controller_name)) {
+ throw new Minz_ControllerNotExistException (
+ $controller_name,
+ Minz_Exception::ERROR
+ );
+ }
+ $this->controller = new $controller_name ($this->router);
+
+ if (! ($this->controller instanceof Minz_ActionController)) {
+ throw new Minz_ControllerNotActionControllerException (
+ $controller_name,
+ Minz_Exception::ERROR
+ );
+ }
+ }
+
+ /**
+ * Lance l'action sur le controller du dispatcher
+ * @param $action_name le nom de l'action
+ * @exception ActionException si on ne peut pas exécuter l'action sur
+ * 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
+ ));
+ }
+ }
+}
diff --git a/lib/Minz/Error.php b/lib/Minz/Error.php
new file mode 100644
index 000000000..1ad0d313c
--- /dev/null
+++ b/lib/Minz/Error.php
@@ -0,0 +1,94 @@
+
+*/
+
+/**
+ * La classe Error permet de lancer des erreurs HTTP
+ */
+class Minz_Error {
+ public function __construct () { }
+
+ /**
+ * Permet de lancer une erreur
+ * @param $code le type de l'erreur, par défaut 404 (page not found)
+ * @param $logs logs d'erreurs découpés de la forme
+ * > $logs['error']
+ * > $logs['warning']
+ * > $logs['notice']
+ * @param $redirect indique s'il faut forcer la redirection (les logs ne seront pas transmis)
+ */
+ public static function error ($code = 404, $logs = array (), $redirect = false) {
+ $logs = self::processLogs ($logs);
+ $error_filename = APP_PATH . '/Controllers/ErrorController.php';
+
+ if (file_exists ($error_filename)) {
+ $params = array (
+ 'code' => $code,
+ 'logs' => $logs
+ );
+
+ Minz_Response::setHeader ($code);
+ if ($redirect) {
+ Minz_Request::forward (array (
+ 'c' => 'error'
+ ), true);
+ } else {
+ Minz_Request::forward (array (
+ 'c' => 'error',
+ 'params' => $params
+ ), false);
+ }
+ } else {
+ $text = '