Bläddra i källkod

Problème de casse renommage répertoire

Alexandre Alapetite 12 år sedan
förälder
incheckning
4ee4f16ffe

+ 85 - 0
app/Models/Category.php

@@ -0,0 +1,85 @@
+<?php
+
+class FreshRSS_Category extends Minz_Model {
+	private $id = 0;
+	private $name;
+	private $color;
+	private $nbFeed = -1;
+	private $nbNotRead = -1;
+	private $feeds = null;
+
+	public function __construct ($name = '', $color = '#0062BE', $feeds = null) {
+		$this->_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;
+	}
+}

+ 7 - 0
app/Models/Days.php

@@ -0,0 +1,7 @@
+<?php
+
+class FreshRSS_Days {
+	const TODAY = 0;
+	const YESTERDAY = 1;
+	const BEFORE_YESTERDAY = 2;
+}

+ 193 - 0
app/Models/Entry.php

@@ -0,0 +1,193 @@
+<?php
+class FreshRSS_Entry extends Minz_Model {
+
+	private $id = 0;
+	private $guid;
+	private $title;
+	private $author;
+	private $content;
+	private $link;
+	private $date;
+	private $is_read;
+	private $is_favorite;
+	private $feed;
+	private $tags;
+
+	public function __construct ($feed = '', $guid = '', $title = '', $author = '', $content = '',
+	                             $link = '', $pubdate = 0, $is_read = false, $is_favorite = false, $tags = '') {
+		$this->_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),
+		);
+	}
+}

+ 319 - 0
app/Models/Feed.php

@@ -0,0 +1,319 @@
+<?php
+class FreshRSS_Feed extends Minz_Model {
+	private $id = 0;
+	private $url;
+	private $category = 1;
+	private $nbEntries = -1;
+	private $nbNotRead = -1;
+	private $entries = null;
+	private $name = '';
+	private $website = '';
+	private $description = '';
+	private $lastUpdate = 0;
+	private $priority = 10;
+	private $pathEntries = '';
+	private $httpAuth = '';
+	private $error = false;
+	private $keep_history = false;
+
+	public function __construct ($url, $validate=true) {
+		if ($validate) {
+			$this->_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 .= '<br /><img src="' . $elink . '" alt="" />';
+				}
+			}
+
+			$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;
+	}
+}

+ 42 - 0
lib/Minz/ActionController.php

@@ -0,0 +1,42 @@
+<?php
+/** 
+ * MINZ - Copyright 2011 Marien Fressinaud
+ * Sous licence AGPL3 <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * 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 () { }
+}
+
+

+ 262 - 0
lib/Minz/Configuration.php

@@ -0,0 +1,262 @@
+<?php
+/**
+ * MINZ - Copyright 2011 Marien Fressinaud
+ * Sous licence AGPL3 <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * 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);
+		}
+	}
+}

+ 138 - 0
lib/Minz/Dispatcher.php

@@ -0,0 +1,138 @@
+<?php
+/** 
+ * MINZ - Copyright 2011 Marien Fressinaud
+ * Sous licence AGPL3 <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * 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
+			));
+		}
+	}
+}

+ 94 - 0
lib/Minz/Error.php

@@ -0,0 +1,94 @@
+<?php
+/**
+ * MINZ - Copyright 2011 Marien Fressinaud
+ * Sous licence AGPL3 <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * 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 = '<h1>An error occured</h1>'."\n";
+
+			if (!empty ($logs)) {
+				$text .= '<ul>'."\n";
+				foreach ($logs as $log) {
+					$text .= '<li>' . $log . '</li>'."\n";
+				}
+				$text .= '</ul>'."\n";
+			}
+
+			Minz_Response::setHeader ($code);
+			Minz_Response::setBody ($text);
+			Minz_Response::send ();
+			exit ();
+		}
+	}
+
+	/**
+	 * Permet de retourner les logs de façon à n'avoir que
+	 * ceux que l'on veut réellement
+	 * @param $logs les logs rangés par catégories (error, warning, notice)
+	 * @return la liste des logs, sans catégorie,
+	 *       > en fonction de l'environment
+	 */
+	private static function processLogs ($logs) {
+		$env = Minz_Configuration::environment ();
+		$logs_ok = array ();
+		$error = array ();
+		$warning = array ();
+		$notice = array ();
+
+		if (isset ($logs['error'])) {
+			$error = $logs['error'];
+		}
+		if (isset ($logs['warning'])) {
+			$warning = $logs['warning'];
+		}
+		if (isset ($logs['notice'])) {
+			$notice = $logs['notice'];
+		}
+
+		if ($env == Minz_Configuration::PRODUCTION) {
+			$logs_ok = $error;
+		}
+		if ($env == Minz_Configuration::DEVELOPMENT) {
+			$logs_ok = array_merge ($error, $warning, $notice);
+		}
+
+		return $logs_ok;
+	}
+}

+ 97 - 0
lib/Minz/FrontController.php

@@ -0,0 +1,97 @@
+<?php
+# ***** BEGIN LICENSE BLOCK *****
+# MINZ - a free PHP Framework like Zend Framework
+# Copyright (C) 2011 Marien Fressinaud
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+# ***** END LICENSE BLOCK *****
+
+/**
+ * La classe FrontController est le Dispatcher du framework, elle lance l'application
+ * Elle est appelée en général dans le fichier index.php à la racine du serveur
+ */
+class Minz_FrontController {
+	protected $dispatcher;
+	protected $router;
+
+	/**
+	 * Constructeur
+	 * Initialise le router et le dispatcher
+	 */
+	public function __construct () {
+		if (LOG_PATH === false) {
+			$this->killApp ('Path doesn\'t exist : LOG_PATH');
+		}
+
+		try {
+			Minz_Configuration::init ();
+
+			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 ()))
+			);
+		} catch (Minz_Exception $e) {
+			Minz_Log::record ($e->getMessage (), Minz_Log::ERROR);
+			$this->killApp ($e->getMessage ());
+		}
+
+		$this->dispatcher = Minz_Dispatcher::getInstance ($this->router);
+	}
+
+	/**
+	 * Démarre l'application (lance le dispatcher et renvoie la réponse
+	 */
+	public function run () {
+		try {
+			$this->dispatcher->run ();
+			Minz_Response::send ();
+		} catch (Minz_Exception $e) {
+			try {
+				Minz_Log::record ($e->getMessage (), Minz_Log::ERROR);
+			} catch (Minz_PermissionDeniedException $e) {
+				$this->killApp ($e->getMessage ());
+			}
+
+			if ($e instanceof Minz_FileNotExistException ||
+					$e instanceof Minz_ControllerNotExistException ||
+					$e instanceof Minz_ControllerNotActionControllerException ||
+					$e instanceof Minz_ActionException) {
+				Minz_Error::error (
+					404,
+					array ('error' => array ($e->getMessage ())),
+					true
+				);
+			} else {
+				$this->killApp ();
+			}
+		}
+	}
+
+	/**
+	* Permet d'arrêter le programme en urgence
+	*/
+	private function killApp ($txt = '') {
+		if ($txt == '') {
+			$txt = 'See logs files';
+		}
+		exit ('### Application problem ###<br />'."\n".$txt);
+	}
+}

+ 22 - 0
lib/Minz/Helper.php

@@ -0,0 +1,22 @@
+<?php
+/** 
+ * MINZ - Copyright 2011 Marien Fressinaud
+ * Sous licence AGPL3 <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * La classe Helper représente une aide pour des tâches récurrentes
+ */
+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);
+		} else {
+			return stripslashes($var);
+		}
+	}
+}

+ 12 - 0
lib/Minz/Model.php

@@ -0,0 +1,12 @@
+<?php
+/** 
+ * MINZ - Copyright 2011 Marien Fressinaud
+ * Sous licence AGPL3 <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * La classe Model représente un modèle de l'application (représentation MVC)
+ */
+class Minz_Model {
+
+}

+ 196 - 0
lib/Minz/Paginator.php

@@ -0,0 +1,196 @@
+<?php
+/** 
+ * MINZ - Copyright 2011 Marien Fressinaud
+ * Sous licence AGPL3 <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * La classe Paginator permet de gérer la pagination de l'application facilement
+ */
+class Minz_Paginator {
+	/**
+	 * $items tableau des éléments à afficher/gérer
+	 */
+	private $items = array ();
+
+	/**
+	 * $nbItemsPerPage le nombre d'éléments par page
+	 */
+	private $nbItemsPerPage = 10;
+
+	/**
+	 * $currentPage page actuelle à gérer
+	 */
+	private $currentPage = 1;
+
+	/**
+	 * $nbPage le nombre de pages de pagination
+	 */
+	private $nbPage = 1;
+
+	/**
+	 * $nbItems le nombre d'éléments
+	 */
+	private $nbItems = 0;
+
+	/**
+	 * Constructeur
+	 * @param $items les éléments à gérer
+	 */
+	public function __construct ($items) {
+		$this->_items ($items);
+		$this->_nbItems (count ($this->items (true)));
+		$this->_nbItemsPerPage ($this->nbItemsPerPage);
+		$this->_currentPage ($this->currentPage);
+	}
+
+	/**
+	 * Permet d'afficher la pagination
+	 * @param $view nom du fichier de vue situé dans /app/views/helpers/
+	 * @param $getteur variable de type $_GET[] permettant de retrouver la page
+	 */
+	public function render ($view, $getteur) {
+		$view = APP_PATH . '/views/helpers/'.$view;
+		
+		if (file_exists ($view)) {
+			include ($view);
+		}
+	}
+
+	/**
+	 * Permet de retrouver la page d'un élément donné
+	 * @param $item l'élément à retrouver
+	 * @return la page à laquelle se trouve l'élément (false si non trouvé)
+	 */
+	public function pageByItem ($item) {
+		$page = false;
+		$i = 0;
+
+		do {
+			if ($item == $this->items[$i]) {
+				$page = ceil (($i + 1) / $this->nbItemsPerPage);
+			}
+
+			$i++;
+		} while (!$page && $i < $this->nbItems ());
+
+		return $page;
+	}
+
+	/**
+	 * Permet de retrouver la position d'un élément donné (à partir de 0)
+	 * @param $item l'élément à retrouver
+	 * @return la position à laquelle se trouve l'élément (false si non trouvé)
+	 */
+	public function positionByItem ($item) {
+		$find = false;
+		$i = 0;
+
+		do {
+			if ($item == $this->items[$i]) {
+				$find = true;
+			} else {
+				$i++;
+			}
+		} while (!$find && $i < $this->nbItems ());
+
+		return $i;
+	}
+
+	/**
+	 * Permet de récupérer un item par sa position
+	 * @param $pos la position de l'élément
+	 * @return l'item situé à $pos (dernier item si $pos<0, 1er si $pos>=count($items))
+	 */
+	public function itemByPosition ($pos) {
+		if ($pos < 0) {
+			$pos = $this->nbItems () - 1;
+		}
+		if ($pos >= count($this->items)) {
+			$pos = 0;
+		}
+
+		return $this->items[$pos];
+	}
+
+	/**
+	 * GETTEURS
+	 */
+	/**
+	 * @param $all si à true, retourne tous les éléments sans prendre en compte la pagination
+	 */
+	public function items ($all = false) {
+		$array = array ();
+		$nbItems = $this->nbItems ();
+
+		if ($nbItems <= $this->nbItemsPerPage || $all) {
+			$array = $this->items;
+		} else {
+			$begin = ($this->currentPage - 1) * $this->nbItemsPerPage;
+			$counter = 0;
+			$i = 0;
+			
+			foreach ($this->items as $key => $item) {
+				if ($i >= $begin) {
+					$array[$key] = $item;
+					$counter++;
+				}
+				if ($counter >= $this->nbItemsPerPage) {
+					break;
+				}
+				$i++;
+			}
+		}
+
+		return $array;
+	}
+	public function nbItemsPerPage  () {
+		return $this->nbItemsPerPage;
+	}
+	public function currentPage () {
+		return $this->currentPage;
+	}
+	public function nbPage () {
+		return $this->nbPage;
+	}
+	public function nbItems () {
+		return $this->nbItems;
+	}
+
+	/**
+	 * SETTEURS
+	 */
+	public function _items ($items) {
+		if (is_array ($items)) {
+			$this->items = $items;
+		}
+		
+		$this->_nbPage ();
+	}
+	public function _nbItemsPerPage ($nbItemsPerPage) {
+		if ($nbItemsPerPage > $this->nbItems ()) {
+			$nbItemsPerPage = $this->nbItems ();
+		}
+		if ($nbItemsPerPage < 0) {
+			$nbItemsPerPage = 0;
+		}
+
+		$this->nbItemsPerPage = $nbItemsPerPage;
+		$this->_nbPage ();
+	}
+	public function _currentPage ($page) {
+		if($page < 1 || ($page > $this->nbPage && $this->nbPage > 0)) {
+			throw new CurrentPagePaginationException ($page);
+		}
+
+		$this->currentPage = $page;
+	}
+	private function _nbPage () {
+		if ($this->nbItemsPerPage > 0) {
+			$this->nbPage = ceil ($this->nbItems () / $this->nbItemsPerPage);
+		}
+	}
+	public function _nbItems ($value) {
+		$this->nbItems = $value;
+	}
+}

+ 197 - 0
lib/Minz/Request.php

@@ -0,0 +1,197 @@
+<?php
+/**
+ * MINZ - Copyright 2011 Marien Fressinaud
+ * Sous licence AGPL3 <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * Request représente la requête http
+ */
+class Minz_Request {
+	private static $controller_name = '';
+	private static $action_name = '';
+	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 () {
+		return self::$controller_name;
+	}
+	public static function actionName () {
+		return self::$action_name;
+	}
+	public static function params () {
+		return self::$params;
+	}
+	static function htmlspecialchars_utf8 ($p) {
+		return htmlspecialchars($p, ENT_QUOTES, 'UTF-8');
+	}
+	public static function param ($key, $default = false, $specialchars = false) {
+		if (isset (self::$params[$key])) {
+			$p = self::$params[$key];
+			if(is_object($p) || $specialchars) {
+				return $p;
+			} elseif(is_array($p)) {
+				return array_map('self::htmlspecialchars_utf8', $p);
+			} else {
+				return self::htmlspecialchars_utf8($p);
+			}
+		} else {
+			return $default;
+		}
+	}
+	public static function defaultControllerName () {
+		return self::$default_controller_name;
+	}
+	public static function defaultActionName () {
+		return self::$default_action_name;
+	}
+
+	/**
+	 * Setteurs
+	 */
+	public static function _controllerName ($controller_name) {
+		self::$controller_name = $controller_name;
+	}
+	public static function _actionName ($action_name) {
+		self::$action_name = $action_name;
+	}
+	public static function _params ($params) {
+		if (!is_array($params)) {
+			$params = array ($params);
+		}
+
+		self::$params = $params;
+	}
+	public static function _param ($key, $value = false) {
+		if ($value === false) {
+			unset (self::$params[$key]);
+		} else {
+			self::$params[$key] = $value;
+		}
+	}
+
+	/**
+	 * Initialise la Request
+	 */
+	public static function init () {
+		self::magicQuotesOff ();
+	}
+
+	/**
+	 * Retourn le nom de domaine du site
+	 */
+	public static function getDomainName () {
+		return $_SERVER['HTTP_HOST'];
+	}
+
+	/**
+	 * Détermine la base de l'url
+	 * @return la base de l'url
+	 */
+	public static function getBaseUrl () {
+		return Minz_Configuration::baseUrl ();
+	}
+
+	/**
+	 * 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 ();
+			$uri = $_SERVER['REQUEST_URI'];
+
+			$len_base_url = strlen ($base_url);
+			$real_uri = substr ($uri, $len_base_url);
+		} else {
+			$real_uri = '';
+		}
+
+		return $real_uri;
+	}
+
+	/**
+	 * Relance une requête
+	 * @param $url l'url vers laquelle est relancée la requête
+	 * @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);
+
+		if ($redirect) {
+			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::$params,
+				$url['params']
+			));
+		}
+	}
+
+	/**
+	 * Permet de récupérer une variable de type $_GET
+	 * @param $param nom de la variable
+	 * @param $default valeur par défaut à attribuer à la variable
+	 * @return $_GET[$param]
+	 *         $_GET si $param = false
+	 *         $default si $_GET[$param] n'existe pas
+	 */
+	public static function fetchGET ($param = false, $default = false) {
+		if ($param === false) {
+			return $_GET;
+		} elseif (isset ($_GET[$param])) {
+			return $_GET[$param];
+		} else {
+			return $default;
+		}
+	}
+
+	/**
+	 * Permet de récupérer une variable de type $_POST
+	 * @param $param nom de la variable
+	 * @param $default valeur par défaut à attribuer à la variable
+	 * @return $_POST[$param]
+	 *         $_POST si $param = false
+	 *         $default si $_POST[$param] n'existe pas
+	 */
+	public static function fetchPOST ($param = false, $default = false) {
+		if ($param === false) {
+			return $_POST;
+		} elseif (isset ($_POST[$param])) {
+			return $_POST[$param];
+		} else {
+			return $default;
+		}
+	}
+
+	/**
+	 * Méthode désactivant les magic_quotes pour les variables
+	 *   $_GET
+	 *   $_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);
+		}
+	}
+
+	public static function isPost () {
+		return !empty ($_POST) || !empty ($_FILES);
+	}
+}

+ 60 - 0
lib/Minz/Response.php

@@ -0,0 +1,60 @@
+<?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;
+	}
+}

+ 209 - 0
lib/Minz/Router.php

@@ -0,0 +1,209 @@
+<?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);
+	 }
+}

+ 78 - 0
lib/Minz/Session.php

@@ -0,0 +1,78 @@
+<?php
+
+/**
+ * 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 ();
+	
+	/**
+	 * Initialise la session
+	 */
+	public static function init () {
+		// démarre la session
+		session_name (md5 (Minz_Configuration::selApplication ()));
+		session_start ();
+		
+		if (isset ($_SESSION)) {
+			self::$session = $_SESSION;
+		}
+	}
+	
+	
+	/**
+	 * Permet de récupérer une variable de 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) {
+		if (isset (self::$session[$p])) {
+			$return = self::$session[$p];
+		} else {
+			$return = $default;
+		}
+		
+		return $return;
+	}
+	
+	
+	/**
+	 * Permet de créer ou mettre à jour une variable de 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) {
+		if ($v === false) {
+			unset ($_SESSION[$p]);
+			unset (self::$session[$p]);
+		} else {
+			$_SESSION[$p] = $v;
+			self::$session[$p] = $v;
+
+			if($p == 'language') {
+				// reset pour remettre à jour le fichier de langue à utiliser
+				Minz_Translate::reset ();
+			}
+		}
+	}
+	
+	
+	/**
+	 * 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');
+		
+		session_unset ();
+		self::$session = array ();
+		
+		if (!$force) {
+			self::_param ('language', $language);
+		}
+	}
+}

+ 71 - 0
lib/Minz/Translate.php

@@ -0,0 +1,71 @@
+<?php
+/** 
+ * MINZ - Copyright 2011 Marien Fressinaud
+ * Sous licence AGPL3 <http://www.gnu.org/licenses/>
+ */
+
+/**
+ * La classe Translate se charge de la traduction
+ * Utilise les fichiers du répertoire /app/i18n/
+ */
+class Minz_Translate {
+	/**
+	 * $language est la langue à afficher
+	 */
+	private static $language;
+	
+	/**
+	 * $translates est le tableau de correspondance
+	 * 	$key => $traduction
+	 */
+	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);
+		
+		$l_path = APP_PATH . '/i18n/' . self::$language . '.php';
+		
+		if (file_exists ($l_path)) {
+			self::$translates = include ($l_path);
+		}
+	}
+	
+	/**
+	 * Alias de init
+	 */
+	public static function reset () {
+		self::init ();
+	}
+	
+	/**
+	 * Traduit une clé en sa valeur du tableau $translates
+	 * @param $key la clé à traduire
+	 * @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) {
+		$translate = $key;
+		
+		if (isset (self::$translates[$key])) {
+			$translate = self::$translates[$key];
+		}
+
+		$args = func_get_args ();
+		unset($args[0]);
+		
+		return vsprintf ($translate, $args);
+	}
+	
+	/**
+	 * Retourne la langue utilisée actuellement
+	 * @return la langue
+	 */
+	public static function language () {
+		return self::$language;
+	}
+}

+ 129 - 0
lib/Minz/Url.php

@@ -0,0 +1,129 @@
+<?php
+
+/**
+ * La classe Url permet de gérer les URL à travers MINZ
+ */
+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
+	 * @param $url l'url à formater définie comme un tableau :
+	 *                    $url['c'] = controller
+	 *                    $url['a'] = action
+	 *                    $url['params'] = tableau des paramètres supplémentaires
+	 *                    $url['protocol'] = protocole à utiliser (http par défaut)
+	 *             ou comme une chaîne de caractère
+	 * @param $encodage pour indiquer comment encoder les & (& ou &amp; pour html)
+	 * @return l'url formatée
+	 */
+	public static function display ($url = array (), $encodage = 'html', $absolute = false) {
+		$url = self::checkUrl ($url);
+
+		$url_string = '';
+
+		if ($absolute) {
+			if (is_array ($url) && isset ($url['protocol'])) {
+				$protocol = $url['protocol'];
+			} elseif (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
+				$protocol = 'https:';
+			} else {
+				$protocol = 'http:';
+			}
+			$url_string = $protocol . '//' . Minz_Request::getDomainName () . Minz_Request::getBaseUrl ();
+		}
+		else {
+			$url_string = '.';
+		}
+
+		if (is_array ($url)) {
+			$router = new Minz_Router ();
+
+			if (Minz_Configuration::useUrlRewriting ()) {
+				$url_string .= $router->printUriRewrited ($url);
+			} else {
+				$url_string .= self::printUri ($url, $encodage);
+			}
+		} else {
+			$url_string .= $url;
+		}
+
+		return $url_string;
+	}
+	
+	/**
+	 * Construit l'URI d'une URL sans url rewriting
+	 * @param l'url sous forme de tableau
+	 * @param $encodage pour indiquer comment encoder les & (& ou &amp; pour html)
+	 * @return l'uri sous la forme ?key=value&key2=value2
+	 */
+	private static function printUri ($url, $encodage) {
+		$uri = '';
+		$separator = '/?';
+		
+		if($encodage == 'html') {
+			$and = '&amp;';
+		} else {
+			$and = '&';
+		}
+		
+		if (isset ($url['c'])
+		 && $url['c'] != Minz_Request::defaultControllerName ()) {
+			$uri .= $separator . 'c=' . $url['c'];
+			$separator = $and;
+		}
+		
+		if (isset ($url['a'])
+		 && $url['a'] != Minz_Request::defaultActionName ()) {
+			$uri .= $separator . 'a=' . $url['a'];
+			$separator = $and;
+		}
+		
+		if (isset ($url['params'])) {
+			foreach ($url['params'] as $key => $param) {
+				$uri .= $separator . $key . '=' . $param;
+				$separator = $and;
+			}
+		}
+		
+		return $uri;
+	}
+	
+	/**
+	 * Vérifie que les éléments du tableau représentant une url soit ok
+	 * @param l'url sous forme de tableau (sinon renverra directement $url)
+	 * @return l'url vérifié
+	 */
+	public static function checkUrl ($url) {
+		$url_checked = $url;
+		
+		if (is_array ($url)) {
+			if (!isset ($url['c'])) {
+				$url_checked['c'] = Minz_Request::defaultControllerName ();
+			}
+			if (!isset ($url['a'])) {
+				$url_checked['a'] = Minz_Request::defaultActionName ();
+			}
+			if (!isset ($url['params'])) {
+				$url_checked['params'] = array ();
+			}
+		}
+		
+		return $url_checked;
+	}
+}
+
+function _url ($controller, $action) {
+	$nb_args = func_num_args ();
+
+	if($nb_args < 2 || $nb_args % 2 != 0) {
+		return false;
+	}
+
+	$args = func_get_args ();
+	$params = array ();
+	for($i = 2; $i < $nb_args; $i = $i + 2) {
+		$params[$args[$i]] = $args[$i + 1];
+	}
+
+	return Minz_Url::display (array ('c' => $controller, 'a' => $action, 'params' => $params));
+}

+ 241 - 0
lib/Minz/View.php

@@ -0,0 +1,241 @@
+<?php
+/**
+ * MINZ - Copyright 2011 Marien Fressinaud
+ * Sous licence AGPL3 <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * La classe View représente la vue de l'application
+ */
+class Minz_View {
+	const VIEWS_PATH_NAME = '/views';
+	const LAYOUT_PATH_NAME = '/layout';
+	const LAYOUT_FILENAME = '/layout.phtml';
+
+	private $view_filename = '';
+	private $use_layout = false;
+
+	private static $title = '';
+	private static $styles = array ();
+	private static $scripts = array ();
+
+	private static $params = array ();
+
+	/**
+	 * Constructeur
+	 * Détermine si on utilise un layout ou non
+	 */
+	public function __construct () {
+		$this->view_filename = APP_PATH
+		                     . self::VIEWS_PATH_NAME . '/'
+		                     . Minz_Request::controllerName () . '/'
+		                     . Minz_Request::actionName () . '.phtml';
+
+		if (file_exists (APP_PATH
+		               . self::LAYOUT_PATH_NAME
+		               . self::LAYOUT_FILENAME)) {
+			$this->use_layout = true;
+		}
+
+		self::$title = Minz_Configuration::title ();
+	}
+
+	/**
+	 * Construit la vue
+	 */
+	public function build () {
+		if ($this->use_layout) {
+			$this->buildLayout ();
+		} else {
+			$this->render ();
+		}
+	}
+
+	/**
+	 * Construit le layout
+	 */
+	public function buildLayout () {
+		include (
+			APP_PATH
+			. self::LAYOUT_PATH_NAME
+			. self::LAYOUT_FILENAME
+		);
+	}
+
+	/**
+	 * Affiche la Vue en elle-même
+	 */
+	public function render () {
+		if (file_exists ($this->view_filename)) {
+			include ($this->view_filename);
+		} else {
+			Minz_Log::record ('File doesn\'t exist : `'
+			            . $this->view_filename . '`',
+			            Minz_Log::NOTICE);
+		}
+	}
+
+	/**
+	 * Ajoute un élément du layout
+	 * @param $part l'élément partial à ajouter
+	 */
+	public function partial ($part) {
+		$fic_partial = APP_PATH
+		             . self::LAYOUT_PATH_NAME . '/'
+		             . $part . '.phtml';
+
+		if (file_exists ($fic_partial)) {
+			include ($fic_partial);
+		} else {
+			Minz_Log::record ('File doesn\'t exist : `'
+			            . $fic_partial . '`',
+			            Minz_Log::WARNING);
+		}
+	}
+
+	/**
+	 * Affiche un élément graphique situé dans APP./views/helpers/
+	 * @param $helper l'élément à afficher
+	 */
+	public function renderHelper ($helper) {
+		$fic_helper = APP_PATH
+		            . '/views/helpers/'
+		            . $helper . '.phtml';
+
+		if (file_exists ($fic_helper)) {
+			include ($fic_helper);
+		} else {
+			Minz_Log::record ('File doesn\'t exist : `'
+			            . $fic_helper . '`',
+			            Minz_Log::WARNING);
+		}
+	}
+
+	/**
+	 * Permet de choisir si on souhaite utiliser le layout
+	 * @param $use true si on souhaite utiliser le layout, false sinon
+	 */
+	public function _useLayout ($use) {
+		$this->use_layout = $use;
+	}
+
+	/**
+	 * Gestion du titre
+	 */
+	public static function title () {
+		return self::$title;
+	}
+	public static function headTitle () {
+		return '<title>' . self::$title . '</title>' . "\n";
+	}
+	public static function _title ($title) {
+		self::$title = $title;
+	}
+	public static function prependTitle ($title) {
+		self::$title = $title . self::$title;
+	}
+	public static function appendTitle ($title) {
+		self::$title = self::$title . $title;
+	}
+
+	/**
+	 * Gestion des feuilles de style
+	 */
+	public static function headStyle () {
+		$styles = '';
+
+		foreach(self::$styles as $style) {
+			$cond = $style['cond'];
+			if ($cond) {
+				$styles .= '<!--[if ' . $cond . ']>';
+			}
+
+			$styles .= '<link rel="stylesheet" ' .
+				($style['media'] === 'all' ? '' : 'media="' . $style['media'] . '" ') .
+				'href="' . $style['url'] . '" />';
+
+			if ($cond) {
+				$styles .= '<![endif]-->';
+			}
+
+			$styles .= "\n";
+		}
+
+		return $styles;
+	}
+	public static function prependStyle ($url, $media = 'all', $cond = false) {
+		array_unshift (self::$styles, array (
+			'url' => $url,
+			'media' => $media,
+			'cond' => $cond
+		));
+	}
+	public static function appendStyle ($url, $media = 'all', $cond = false) {
+		self::$styles[] = array (
+			'url' => $url,
+			'media' => $media,
+			'cond' => $cond
+		);
+	}
+
+	/**
+	 * Gestion des scripts JS
+	 */
+	public static function headScript () {
+		$scripts = '';
+
+		foreach (self::$scripts as $script) {
+			$cond = $script['cond'];
+			if ($cond) {
+				$scripts .= '<!--[if ' . $cond . ']>';
+			}
+
+			$scripts .= '<script src="' . $script['url'] . '"';
+			if ($script['defer']) {
+				$scripts .= ' defer="defer"';
+			}
+			if ($script['async']) {
+				$scripts .= ' async="async"';
+			}
+			$scripts .= '></script>';
+
+			if ($cond) {
+				$scripts .= '<![endif]-->';
+			}
+
+			$scripts .= "\n";
+		}
+
+		return $scripts;
+	}
+	public static function prependScript ($url, $cond = false, $defer = true, $async = true) {
+		array_unshift(self::$scripts, array (
+			'url' => $url,
+			'cond' => $cond,
+			'defer' => $defer,
+			'async' => $async,
+		));
+	}
+	public static function appendScript ($url, $cond = false, $defer = true, $async = true) {
+		self::$scripts[] = array (
+			'url' => $url,
+			'cond' => $cond,
+			'defer' => $defer,
+			'async' => $async,
+		);
+	}
+
+	/**
+	 * Gestion des paramètres ajoutés à la vue
+	 */
+	public static function _param ($key, $value) {
+		self::$params[$key] = $value;
+	}
+	public function attributeParams () {
+		foreach (Minz_View::$params as $key => $value) {
+			$this->$key = $value;
+		}
+	}
+}
+
+