diff options
| author | 2013-12-15 11:24:14 +0100 | |
|---|---|---|
| committer | 2013-12-15 11:24:14 +0100 | |
| commit | 4ee4f16ffe06e247d2cb79a2054ab5d5315d82b2 (patch) | |
| tree | 1210892f3fd3da163bcdf8ae3a8024b29607f293 | |
| parent | a29dad13761208bfdf024e7f6703023d85428695 (diff) | |
Problème de casse renommage répertoire
| -rw-r--r-- | app/Models/Category.php | 85 | ||||
| -rw-r--r-- | app/Models/Days.php | 7 | ||||
| -rw-r--r-- | app/Models/Entry.php | 193 | ||||
| -rw-r--r-- | app/Models/Feed.php | 319 | ||||
| -rw-r--r-- | lib/Minz/ActionController.php | 42 | ||||
| -rw-r--r-- | lib/Minz/Configuration.php | 262 | ||||
| -rw-r--r-- | lib/Minz/Dispatcher.php | 138 | ||||
| -rw-r--r-- | lib/Minz/Error.php | 94 | ||||
| -rw-r--r-- | lib/Minz/FrontController.php | 97 | ||||
| -rw-r--r-- | lib/Minz/Helper.php | 22 | ||||
| -rw-r--r-- | lib/Minz/Model.php | 12 | ||||
| -rw-r--r-- | lib/Minz/Paginator.php | 196 | ||||
| -rw-r--r-- | lib/Minz/Request.php | 197 | ||||
| -rw-r--r-- | lib/Minz/Response.php | 60 | ||||
| -rw-r--r-- | lib/Minz/Router.php | 209 | ||||
| -rw-r--r-- | lib/Minz/Session.php | 78 | ||||
| -rw-r--r-- | lib/Minz/Translate.php | 71 | ||||
| -rw-r--r-- | lib/Minz/Url.php | 129 | ||||
| -rw-r--r-- | lib/Minz/View.php | 241 |
19 files changed, 2452 insertions, 0 deletions
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 @@ +<?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; + } +} 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 @@ +<?php + +class FreshRSS_Days { + const TODAY = 0; + const YESTERDAY = 1; + const BEFORE_YESTERDAY = 2; +} diff --git a/app/Models/Entry.php b/app/Models/Entry.php new file mode 100644 index 000000000..ba0fb48f4 --- /dev/null +++ b/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), + ); + } +} 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 @@ +<?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; + } +} 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 @@ +<?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 () { } +} + + 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 @@ +<?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); + } + } +} 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 @@ +<?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 + )); + } + } +} 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 @@ +<?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; + } +} diff --git a/lib/Minz/FrontController.php b/lib/Minz/FrontController.php new file mode 100644 index 000000000..eb9835fe5 --- /dev/null +++ b/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); + } +} diff --git a/lib/Minz/Helper.php b/lib/Minz/Helper.php new file mode 100644 index 000000000..b058211d3 --- /dev/null +++ b/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); + } + } +} diff --git a/lib/Minz/Model.php b/lib/Minz/Model.php new file mode 100644 index 000000000..adbaba942 --- /dev/null +++ b/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 { + +} diff --git a/lib/Minz/Paginator.php b/lib/Minz/Paginator.php new file mode 100644 index 000000000..5858e76a5 --- /dev/null +++ b/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; + } +} diff --git a/lib/Minz/Request.php b/lib/Minz/Request.php new file mode 100644 index 000000000..c8ffa4a42 --- /dev/null +++ b/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); + } +} diff --git a/lib/Minz/Response.php b/lib/Minz/Response.php new file mode 100644 index 000000000..f8ea3d946 --- /dev/null +++ b/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; + } +} diff --git a/lib/Minz/Router.php b/lib/Minz/Router.php new file mode 100644 index 000000000..1ccd72597 --- /dev/null +++ b/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); + } +} diff --git a/lib/Minz/Session.php b/lib/Minz/Session.php new file mode 100644 index 000000000..878caa556 --- /dev/null +++ b/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); + } + } +} diff --git a/lib/Minz/Translate.php b/lib/Minz/Translate.php new file mode 100644 index 000000000..e14f783f7 --- /dev/null +++ b/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; + } +} diff --git a/lib/Minz/Url.php b/lib/Minz/Url.php new file mode 100644 index 000000000..30f7f6231 --- /dev/null +++ b/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 & 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 & pour html) + * @return l'uri sous la forme ?key=value&key2=value2 + */ + private static function printUri ($url, $encodage) { + $uri = ''; + $separator = '/?'; + + if($encodage == 'html') { + $and = '&'; + } 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)); +} diff --git a/lib/Minz/View.php b/lib/Minz/View.php new file mode 100644 index 000000000..c8d0aefed --- /dev/null +++ b/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; + } + } +} + + |
