diff options
| author | 2015-01-31 14:45:37 +0100 | |
|---|---|---|
| committer | 2015-01-31 14:45:37 +0100 | |
| commit | a97bbd9bd54c5fa56d54b3c214cf4e8af96af8b2 (patch) | |
| tree | 6e83890bc1b3814a12c3b7bedc0d5944f30f507b /lib/Minz | |
| parent | 42fd539a1b14f883077048a35864b4294b6efe94 (diff) | |
| parent | e91b72b63cd11ae3c4f59e48439e93955242c673 (diff) | |
Merge branch 'dev'
Conflicts:
CHANGELOG
README.fr.md
README.md
app/Controllers/feedController.php
app/Controllers/indexController.php
app/i18n/en.php
app/i18n/fr.php
app/views/helpers/view/normal_view.phtml
app/views/stats/index.phtml
app/views/stats/repartition.phtml
constants.php
p/scripts/main.js
Diffstat (limited to 'lib/Minz')
| -rw-r--r-- | lib/Minz/BadConfigurationException.php | 9 | ||||
| -rw-r--r-- | lib/Minz/Configuration.php | 498 | ||||
| -rw-r--r-- | lib/Minz/ConfigurationException.php | 8 | ||||
| -rw-r--r-- | lib/Minz/ConfigurationNamespaceException.php | 4 | ||||
| -rw-r--r-- | lib/Minz/ConfigurationParamException.php | 4 | ||||
| -rw-r--r-- | lib/Minz/Dispatcher.php | 53 | ||||
| -rw-r--r-- | lib/Minz/Error.php | 48 | ||||
| -rw-r--r-- | lib/Minz/Extension.php | 208 | ||||
| -rw-r--r-- | lib/Minz/ExtensionException.php | 15 | ||||
| -rw-r--r-- | lib/Minz/ExtensionManager.php | 301 | ||||
| -rw-r--r-- | lib/Minz/FrontController.php | 34 | ||||
| -rw-r--r-- | lib/Minz/Log.php | 17 | ||||
| -rw-r--r-- | lib/Minz/ModelPdo.php | 17 | ||||
| -rw-r--r-- | lib/Minz/Request.php | 10 | ||||
| -rw-r--r-- | lib/Minz/Session.php | 6 | ||||
| -rw-r--r-- | lib/Minz/Translate.php | 225 | ||||
| -rw-r--r-- | lib/Minz/Url.php | 34 | ||||
| -rw-r--r-- | lib/Minz/View.php | 80 |
18 files changed, 1088 insertions, 483 deletions
diff --git a/lib/Minz/BadConfigurationException.php b/lib/Minz/BadConfigurationException.php deleted file mode 100644 index a7b77d687..000000000 --- a/lib/Minz/BadConfigurationException.php +++ /dev/null @@ -1,9 +0,0 @@ -<?php -class Minz_BadConfigurationException extends Minz_Exception { - public function __construct ($part_missing, $code = self::ERROR) { - $message = '`' . $part_missing - . '` in the configuration file is missing or is misconfigured'; - - parent::__construct ($message, $code); - } -} diff --git a/lib/Minz/Configuration.php b/lib/Minz/Configuration.php index 4e9da58b4..ab5bb4fc2 100644 --- a/lib/Minz/Configuration.php +++ b/lib/Minz/Configuration.php @@ -1,375 +1,221 @@ <?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 + * Manage configuration for the application. */ class Minz_Configuration { - const CONF_PATH_NAME = '/config.php'; - /** - * VERSION est la version actuelle de MINZ + * The list of configurations. */ - const VERSION = '1.3.1.freshrss'; // version spéciale FreshRSS + private static $config_list = array(); /** - * 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 + * Add a new configuration to the list of configuration. + * + * @param $namespace the name of the current configuration + * @param $config_filename the filename of the configuration + * @param $default_filename a filename containing default values for the configuration + * @param $configuration_setter an optional helper to set values in configuration */ - const SILENT = 0; - const PRODUCTION = 1; - const DEVELOPMENT = 2; + public static function register($namespace, $config_filename, $default_filename = null, + $configuration_setter = null) { + self::$config_list[$namespace] = new Minz_Configuration( + $namespace, $config_filename, $default_filename, $configuration_setter + ); + } /** - * définition des variables de configuration - * $salt une chaîne de caractères aléatoires (obligatoire) - * $environment gère le niveau d'affichage pour log et erreurs - * $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 - * $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 + * Parse a file and return its data. + * + * If the file does not contain a valid PHP code returning an array, an + * empty array is returned anyway. + * + * @param $filename the name of the file to parse. + * @return an array of values + * @throws Minz_FileNotExistException if the file does not exist. */ - private static $salt = ''; - private static $environment = Minz_Configuration::PRODUCTION; - private static $base_url = ''; - private static $title = ''; - private static $language = 'en'; - private static $default_user = ''; - private static $allow_anonymous = false; - private static $allow_anonymous_refresh = false; - private static $auth_type = 'none'; - private static $api_enabled = false; - private static $unsafe_autologin_enabled = false; - - private static $db = array ( - 'type' => 'mysql', - 'host' => '', - 'user' => '', - 'password' => '', - 'base' => '', - 'prefix' => '', - ); + public static function load($filename) { + if (!file_exists($filename)) { + throw new Minz_FileNotExistException($filename); + } - /* - * Getteurs - */ - public static function salt () { - return self::$salt; + $data = @include($filename); + if (is_array($data)) { + return $data; + } else { + return array(); + } } - public static function environment ($str = false) { - $env = self::$environment; - if ($str) { - switch (self::$environment) { - case self::SILENT: - $env = 'silent'; - break; - case self::DEVELOPMENT: - $env = 'development'; - break; - case self::PRODUCTION: - default: - $env = 'production'; - } + /** + * Return the configuration related to a given namespace. + * + * @param $namespace the name of the configuration to get. + * @return a Minz_Configuration object + * @throws Minz_ConfigurationNamespaceException if the namespace does not exist. + */ + public static function get($namespace) { + if (!isset(self::$config_list[$namespace])) { + throw new Minz_ConfigurationNamespaceException( + $namespace . ' namespace does not exist' + ); } - return $env; - } - public static function baseUrl () { - return self::$base_url; - } - public static function title () { - return self::$title; - } - public static function language () { - return self::$language; - } - public static function dataBase () { - return self::$db; - } - public static function defaultUser () { - return self::$default_user; - } - public static function isAdmin($currentUser) { - return $currentUser === self::$default_user; - } - public static function allowAnonymous() { - return self::$allow_anonymous; - } - public static function allowAnonymousRefresh() { - return self::$allow_anonymous_refresh; - } - public static function authType() { - return self::$auth_type; - } - public static function needsLogin() { - return self::$auth_type !== 'none'; - } - public static function canLogIn() { - return self::$auth_type === 'form' || self::$auth_type === 'persona'; - } - public static function apiEnabled() { - return self::$api_enabled; - } - public static function unsafeAutologinEnabled() { - return self::$unsafe_autologin_enabled; + return self::$config_list[$namespace]; } - public static function _allowAnonymous($allow = false) { - self::$allow_anonymous = ((bool)$allow) && self::canLogIn(); - } - public static function _allowAnonymousRefresh($allow = false) { - self::$allow_anonymous_refresh = ((bool)$allow) && self::allowAnonymous(); - } - public static function _authType($value) { - $value = strtolower($value); - switch ($value) { - case 'form': - case 'http_auth': - case 'persona': - case 'none': - self::$auth_type = $value; - break; - } - self::_allowAnonymous(self::$allow_anonymous); - } + /** + * The namespace of the current configuration. + */ + private $namespace = ''; + + /** + * The filename for the current configuration. + */ + private $config_filename = ''; - public static function _enableApi($value = false) { - self::$api_enabled = (bool)$value; + /** + * The filename for the current default values, null by default. + */ + private $default_filename = null; + + /** + * The configuration values, an empty array by default. + */ + private $data = array(); + + /** + * The default values, an empty array by default. + */ + private $data_default = array(); + + /** + * An object which help to set good values in configuration. + */ + private $configuration_setter = null; + + public function removeExtension($ext_name) { + self::$extensions_enabled = array_diff( + self::$extensions_enabled, + array($ext_name) + ); } - public static function _enableAutologin($value = false) { - self::$unsafe_autologin_enabled = (bool)$value; + public function addExtension($ext_name) { + $found = array_search($ext_name, self::$extensions_enabled) !== false; + if (!$found) { + self::$extensions_enabled[] = $ext_name; + } } /** - * 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é + * Create a new Minz_Configuration object. + * + * @param $namespace the name of the current configuration. + * @param $config_filename the file containing configuration values. + * @param $default_filename the file containing default values, null by default. + * @param $configuration_setter an optional helper to set values in configuration */ - public static function init () { + private function __construct($namespace, $config_filename, $default_filename = null, + $configuration_setter = null) { + $this->namespace = $namespace; + $this->config_filename = $config_filename; + try { - self::parseFile (); - self::setReporting (); + $this->data = self::load($this->config_filename); } catch (Minz_FileNotExistException $e) { - throw $e; - } catch (Minz_BadConfigurationException $e) { - throw $e; + if (is_null($default_filename)) { + throw $e; + } } - } - public static function writeFile() { - $ini_array = array( - 'general' => array( - 'environment' => self::environment(true), - 'salt' => self::$salt, - 'base_url' => self::$base_url, - 'title' => self::$title, - 'default_user' => self::$default_user, - 'allow_anonymous' => self::$allow_anonymous, - 'allow_anonymous_refresh' => self::$allow_anonymous_refresh, - 'auth_type' => self::$auth_type, - 'api_enabled' => self::$api_enabled, - 'unsafe_autologin_enabled' => self::$unsafe_autologin_enabled, - ), - 'db' => self::$db, - ); - @rename(DATA_PATH . self::CONF_PATH_NAME, DATA_PATH . self::CONF_PATH_NAME . '.bak.php'); - $result = file_put_contents(DATA_PATH . self::CONF_PATH_NAME, "<?php\n return " . var_export($ini_array, true) . ';'); - if (function_exists('opcache_invalidate')) { - opcache_invalidate(DATA_PATH . self::CONF_PATH_NAME); //Clear PHP 5.5+ cache for include + $this->default_filename = $default_filename; + if (!is_null($this->default_filename)) { + $this->data_default = self::load($this->default_filename); } - return (bool)$result; + + $this->_configurationSetter($configuration_setter); } /** - * Parse un fichier de configuration - * @exception Minz_PermissionDeniedException si le CONF_PATH_NAME n'est pas accessible - * @exception Minz_BadConfigurationException si CONF_PATH_NAME mal formaté + * Set a configuration setter for the current configuration. + * @param $configuration_setter the setter to call when modifying data. It + * must implement an handle($key, $value) method. */ - private static function parseFile () { - $ini_array = include(DATA_PATH . self::CONF_PATH_NAME); - - if (!is_array($ini_array)) { - throw new Minz_PermissionDeniedException ( - DATA_PATH . self::CONF_PATH_NAME, - Minz_Exception::ERROR - ); + public function _configurationSetter($configuration_setter) { + if (is_callable(array($configuration_setter, 'handle'))) { + $this->configuration_setter = $configuration_setter; } + } - // [general] est obligatoire - if (!isset ($ini_array['general'])) { - throw new Minz_BadConfigurationException ( - '[general]', - Minz_Exception::ERROR - ); + /** + * Return the value of the given param. + * + * @param $key the name of the param. + * @param $default default value to return if key does not exist. + * @return the value corresponding to the key. + * @throws Minz_ConfigurationParamException if the param does not exist + */ + public function param($key, $default = null) { + if (isset($this->data[$key])) { + return $this->data[$key]; + } elseif (!is_null($default)) { + return $default; + } elseif (isset($this->data_default[$key])) { + return $this->data_default[$key]; + } else { + Minz_Log::warning($key . ' does not exist in configuration'); + return null; } - $general = $ini_array['general']; + } - // salt est obligatoire - if (!isset ($general['salt'])) { - if (isset($general['sel_application'])) { //v0.6 - $general['salt'] = $general['sel_application']; - } else { - throw new Minz_BadConfigurationException ( - 'salt', - Minz_Exception::ERROR - ); - } + /** + * A wrapper for param(). + */ + public function __get($key) { + return $this->param($key); + } + + /** + * Set or remove a param. + * + * @param $key the param name to set. + * @param $value the value to set. If null, the key is removed from the configuration. + */ + public function _param($key, $value = null) { + if (!is_null($this->configuration_setter) && $this->configuration_setter->support($key)) { + $this->configuration_setter->handle($this->data, $key, $value); + } elseif (isset($this->data[$key]) && is_null($value)) { + unset($this->data[$key]); + } elseif (!is_null($value)) { + $this->data[$key] = $value; } - self::$salt = $general['salt']; + } - 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: - if ($general['environment'] >= 0 && - $general['environment'] <= 2) { - // fallback 0.7-beta - self::$environment = $general['environment']; - } else { - throw new Minz_BadConfigurationException ( - 'environment', - Minz_Exception::ERROR - ); - } - } + /** + * A wrapper for _param(). + */ + public function __set($key, $value) { + $this->_param($key, $value); + } - } - if (isset ($general['base_url'])) { - self::$base_url = $general['base_url']; - } + /** + * Save the current configuration in the configuration file. + */ + public function save() { + $back_filename = $this->config_filename . '.bak.php'; + @rename($this->config_filename, $back_filename); - if (isset ($general['title'])) { - self::$title = $general['title']; - } - if (isset ($general['language'])) { - self::$language = $general['language']; - } - if (isset ($general['default_user'])) { - self::$default_user = $general['default_user']; - } - if (isset ($general['auth_type'])) { - self::_authType($general['auth_type']); - } - if (isset ($general['allow_anonymous'])) { - self::$allow_anonymous = ( - ((bool)($general['allow_anonymous'])) && - ($general['allow_anonymous'] !== 'no') - ); - } - if (isset ($general['allow_anonymous_refresh'])) { - self::$allow_anonymous_refresh = ( - ((bool)($general['allow_anonymous_refresh'])) && - ($general['allow_anonymous_refresh'] !== 'no') - ); - } - if (isset ($general['api_enabled'])) { - self::$api_enabled = ( - ((bool)($general['api_enabled'])) && - ($general['api_enabled'] !== 'no') - ); - } - if (isset ($general['unsafe_autologin_enabled'])) { - self::$unsafe_autologin_enabled = ( - ((bool)($general['unsafe_autologin_enabled'])) && - ($general['unsafe_autologin_enabled'] !== 'no') - ); + if (file_put_contents($this->config_filename, + "<?php\nreturn " . var_export($this->data, true) . ';', + LOCK_EX) === false) { + return false; } - // Base de données - if (isset ($ini_array['db'])) { - $db = $ini_array['db']; - if (empty($db['type'])) { - throw new Minz_BadConfigurationException ( - 'type', - Minz_Exception::ERROR - ); - } - switch ($db['type']) { - case 'mysql': - if (empty($db['host'])) { - throw new Minz_BadConfigurationException ( - 'host', - Minz_Exception::ERROR - ); - } - if (empty($db['user'])) { - throw new Minz_BadConfigurationException ( - 'user', - Minz_Exception::ERROR - ); - } - if (!isset($db['password'])) { - throw new Minz_BadConfigurationException ( - 'password', - Minz_Exception::ERROR - ); - } - if (empty($db['base'])) { - throw new Minz_BadConfigurationException ( - 'base', - Minz_Exception::ERROR - ); - } - self::$db['host'] = $db['host']; - self::$db['user'] = $db['user']; - self::$db['password'] = $db['password']; - self::$db['base'] = $db['base']; - if (isset($db['prefix'])) { - self::$db['prefix'] = $db['prefix']; - } - break; - case 'sqlite': - self::$db['host'] = ''; - self::$db['user'] = ''; - self::$db['password'] = ''; - self::$db['base'] = ''; - self::$db['prefix'] = ''; - break; - default: - throw new Minz_BadConfigurationException ( - 'type', - Minz_Exception::ERROR - ); - break; - } - self::$db['type'] = $db['type']; + // Clear PHP 5.5+ cache for include + if (function_exists('opcache_invalidate')) { + opcache_invalidate($this->config_filename); } - } - private static function setReporting() { - switch (self::$environment) { - case self::PRODUCTION: - error_reporting(E_ALL); - ini_set('display_errors','Off'); - ini_set('log_errors', 'On'); - break; - case self::DEVELOPMENT: - error_reporting(E_ALL); - ini_set('display_errors','On'); - ini_set('log_errors', 'On'); - break; - case self::SILENT: - error_reporting(0); - break; - } + return true; } } diff --git a/lib/Minz/ConfigurationException.php b/lib/Minz/ConfigurationException.php new file mode 100644 index 000000000..f294c3341 --- /dev/null +++ b/lib/Minz/ConfigurationException.php @@ -0,0 +1,8 @@ +<?php + +class Minz_ConfigurationException extends Minz_Exception { + public function __construct($error, $code = self::ERROR) { + $message = 'Configuration error: ' . $error; + parent::__construct($message, $code); + } +} diff --git a/lib/Minz/ConfigurationNamespaceException.php b/lib/Minz/ConfigurationNamespaceException.php new file mode 100644 index 000000000..f4278c5d6 --- /dev/null +++ b/lib/Minz/ConfigurationNamespaceException.php @@ -0,0 +1,4 @@ +<?php + +class Minz_ConfigurationNamespaceException extends Minz_ConfigurationException { +} diff --git a/lib/Minz/ConfigurationParamException.php b/lib/Minz/ConfigurationParamException.php new file mode 100644 index 000000000..eac977935 --- /dev/null +++ b/lib/Minz/ConfigurationParamException.php @@ -0,0 +1,4 @@ +<?php + +class Minz_ConfigurationParamException extends Minz_ConfigurationException { +} diff --git a/lib/Minz/Dispatcher.php b/lib/Minz/Dispatcher.php index f62a92911..125ce5757 100644 --- a/lib/Minz/Dispatcher.php +++ b/lib/Minz/Dispatcher.php @@ -15,6 +15,7 @@ class Minz_Dispatcher { /* singleton */ private static $instance = null; private static $needsReset; + private static $registrations = array(); private $controller; @@ -38,7 +39,7 @@ class Minz_Dispatcher { self::$needsReset = false; try { - $this->createController ('FreshRSS_' . Minz_Request::controllerName () . '_Controller'); + $this->createController (Minz_Request::controllerName ()); $this->controller->init (); $this->controller->firstAction (); if (!self::$needsReset) { @@ -67,14 +68,18 @@ class Minz_Dispatcher { /** * Instancie le Controller - * @param $controller_name le nom du controller à instancier + * @param $base_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'; + private function createController ($base_name) { + if (self::isRegistered($base_name)) { + self::loadController($base_name); + $controller_name = 'FreshExtension_' . $base_name . '_Controller'; + } else { + $controller_name = 'FreshRSS_' . $base_name . '_Controller'; + } if (!class_exists ($controller_name)) { throw new Minz_ControllerNotExistException ( @@ -114,4 +119,42 @@ class Minz_Dispatcher { $action_name )); } + + /** + * Register a controller file. + * + * @param $base_name the base name of the controller (i.e. ./?c=<base_name>) + * @param $base_path the base path where we should look into to find info. + */ + public static function registerController($base_name, $base_path) { + if (!self::isRegistered($base_name)) { + self::$registrations[$base_name] = $base_path; + } + } + + /** + * Return if a controller is registered. + * + * @param $base_name the base name of the controller. + * @return true if the controller has been registered, false else. + */ + public static function isRegistered($base_name) { + return isset(self::$registrations[$base_name]); + } + + /** + * Load a controller file (include). + * + * @param $base_name the base name of the controller. + */ + private static function loadController($base_name) { + $base_path = self::$registrations[$base_name]; + $controller_filename = $base_path . '/controllers/' . $base_name . 'Controller.php'; + include_once $controller_filename; + } + + private static function setViewPath($controller, $base_name) { + $base_path = self::$registrations[$base_name]; + $controller->view()->setBasePathname($base_path); + } } diff --git a/lib/Minz/Error.php b/lib/Minz/Error.php index c8222a430..3e4a3e8f3 100644 --- a/lib/Minz/Error.php +++ b/lib/Minz/Error.php @@ -19,46 +19,17 @@ class Minz_Error { * > $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) { + public static function error ($code = 404, $logs = array (), $redirect = true) { $logs = self::processLogs ($logs); $error_filename = APP_PATH . '/Controllers/errorController.php'; - switch ($code) { - case 200 : - header('HTTP/1.1 200 OK'); - break; - case 403 : - header('HTTP/1.1 403 Forbidden'); - break; - case 404 : - header('HTTP/1.1 404 Not Found'); - break; - case 500 : - header('HTTP/1.1 500 Internal Server Error'); - break; - case 503 : - header('HTTP/1.1 503 Service Unavailable'); - break; - default : - header('HTTP/1.1 500 Internal Server Error'); - } - if (file_exists ($error_filename)) { - $params = array ( - 'code' => $code, - 'logs' => $logs - ); + Minz_Session::_param('error_code', $code); + Minz_Session::_param('error_logs', $logs); - if ($redirect) { - Minz_Request::forward (array ( - 'c' => 'error' - ), true); - } else { - Minz_Request::forward (array ( - 'c' => 'error', - 'params' => $params - ), false); - } + Minz_Request::forward (array ( + 'c' => 'error' + ), $redirect); } else { echo '<h1>An error occured</h1>' . "\n"; @@ -82,7 +53,8 @@ class Minz_Error { * > en fonction de l'environment */ private static function processLogs ($logs) { - $env = Minz_Configuration::environment (); + $conf = Minz_Configuration::get('system'); + $env = $conf->environment; $logs_ok = array (); $error = array (); $warning = array (); @@ -98,10 +70,10 @@ class Minz_Error { $notice = $logs['notice']; } - if ($env == Minz_Configuration::PRODUCTION) { + if ($env == 'production') { $logs_ok = $error; } - if ($env == Minz_Configuration::DEVELOPMENT) { + if ($env == 'development') { $logs_ok = array_merge ($error, $warning, $notice); } diff --git a/lib/Minz/Extension.php b/lib/Minz/Extension.php new file mode 100644 index 000000000..d7ee8fe81 --- /dev/null +++ b/lib/Minz/Extension.php @@ -0,0 +1,208 @@ +<?php + +/** + * The extension base class. + */ +class Minz_Extension { + private $name; + private $entrypoint; + private $path; + private $author; + private $description; + private $version; + private $type; + + public static $authorized_types = array( + 'system', + 'user', + ); + + private $is_enabled; + + /** + * The constructor to assign specific information to the extension. + * + * Available fields are: + * - name: the name of the extension (required). + * - entrypoint: the extension class name (required). + * - path: the pathname to the extension files (required). + * - author: the name and / or email address of the extension author. + * - description: a short description to describe the extension role. + * - version: a version for the current extension. + * - type: "system" or "user" (default). + * + * It must not be redefined by child classes. + * + * @param $meta_info contains information about the extension. + */ + public function __construct($meta_info) { + $this->name = $meta_info['name']; + $this->entrypoint = $meta_info['entrypoint']; + $this->path = $meta_info['path']; + $this->author = isset($meta_info['author']) ? $meta_info['author'] : ''; + $this->description = isset($meta_info['description']) ? $meta_info['description'] : ''; + $this->version = isset($meta_info['version']) ? $meta_info['version'] : '0.1'; + $this->setType(isset($meta_info['type']) ? $meta_info['type'] : 'user'); + + $this->is_enabled = false; + } + + /** + * Used when installing an extension (e.g. update the database scheme). + * + * It must be redefined by child classes. + * + * @return true if the extension has been installed or a string explaining + * the problem. + */ + public function install() { + return true; + } + + /** + * Used when uninstalling an extension (e.g. revert the database scheme to + * cancel changes from install). + * + * It must be redefined by child classes. + * + * @return true if the extension has been uninstalled or a string explaining + * the problem. + */ + public function uninstall() { + return true; + } + + /** + * Call at the initialization of the extension (i.e. when the extension is + * enabled by the extension manager). + * + * It must be redefined by child classes. + */ + public function init() {} + + /** + * Set the current extension to enable. + */ + public function enable() { + $this->is_enabled = true; + } + + /** + * Return if the extension is currently enabled. + * + * @return true if extension is enabled, false else. + */ + public function isEnabled() { + return $this->is_enabled; + } + + /** + * Return the content of the configure view for the current extension. + * + * @return the html content from ext_dir/configure.phtml, false if it does + * not exist. + */ + public function getConfigureView() { + $filename = $this->path . '/configure.phtml'; + if (!file_exists($filename)) { + return false; + } + + ob_start(); + include($filename); + return ob_get_clean(); + } + + /** + * Handle the configure action. + * + * It must be redefined by child classes. + */ + public function handleConfigureAction() {} + + /** + * Getters and setters. + */ + public function getName() { + return $this->name; + } + public function getEntrypoint() { + return $this->entrypoint; + } + public function getPath() { + return $this->path; + } + public function getAuthor() { + return $this->author; + } + public function getDescription() { + return $this->description; + } + public function getVersion() { + return $this->version; + } + public function getType() { + return $this->type; + } + private function setType($type) { + if (!in_array($type, self::$authorized_types)) { + throw new Minz_ExtensionException('invalid `type` info', $this->name); + } + $this->type = $type; + } + + /** + * Return the url for a given file. + * + * @param $filename name of the file to serve. + * @param $type the type (js or css) of the file to serve. + * @return the url corresponding to the file. + */ + public function getFileUrl($filename, $type) { + $dir = substr(strrchr($this->path, '/'), 1); + $file_name_url = urlencode($dir . '/static/' . $filename); + + $absolute_path = $this->path . '/static/' . $filename; + $mtime = @filemtime($absolute_path); + + $url = '/ext.php?f=' . $file_name_url . + '&t=' . $type . + '&' . $mtime; + return Minz_Url::display($url); + } + + /** + * Register a controller in the Dispatcher. + * + * @param @base_name the base name of the controller. Final name will be: + * FreshExtension_<base_name>_Controller. + */ + public function registerController($base_name) { + Minz_Dispatcher::registerController($base_name, $this->path); + } + + /** + * Register the views in order to be accessible by the application. + */ + public function registerViews() { + Minz_View::addBasePathname($this->path); + } + + /** + * Register i18n files from ext_dir/i18n/ + */ + public function registerTranslates() { + $i18n_dir = $this->path . '/i18n'; + Minz_Translate::registerPath($i18n_dir); + } + + /** + * Register a new hook. + * + * @param $hook_name the hook name (must exist). + * @param $hook_function the function name to call (must be callable). + */ + public function registerHook($hook_name, $hook_function) { + Minz_ExtensionManager::addHook($hook_name, $hook_function, $this); + } +} diff --git a/lib/Minz/ExtensionException.php b/lib/Minz/ExtensionException.php new file mode 100644 index 000000000..647f1a9b9 --- /dev/null +++ b/lib/Minz/ExtensionException.php @@ -0,0 +1,15 @@ +<?php + +class Minz_ExtensionException extends Minz_Exception { + public function __construct ($message, $extension_name = false, $code = self::ERROR) { + if ($extension_name) { + $message = 'An error occured in `' . $extension_name + . '` extension with the message: ' . $message; + } else { + $message = 'An error occured in an unnamed ' + . 'extension with the message: ' . $message; + } + + parent::__construct($message, $code); + } +} diff --git a/lib/Minz/ExtensionManager.php b/lib/Minz/ExtensionManager.php new file mode 100644 index 000000000..c5c68a8d4 --- /dev/null +++ b/lib/Minz/ExtensionManager.php @@ -0,0 +1,301 @@ +<?php + +/** + * An extension manager to load extensions present in EXTENSIONS_PATH. + * + * @todo see coding style for methods!! + */ +class Minz_ExtensionManager { + private static $ext_metaname = 'metadata.json'; + private static $ext_entry_point = 'extension.php'; + private static $ext_list = array(); + private static $ext_list_enabled = array(); + + private static $ext_auto_enabled = array(); + + // List of available hooks. Please keep this list sorted. + private static $hook_list = array( + 'entry_before_display' => array( // function($entry) -> Entry | null + 'list' => array(), + 'signature' => 'OneToOne', + ), + 'entry_before_insert' => array( // function($entry) -> Entry | null + 'list' => array(), + 'signature' => 'OneToOne', + ), + 'feed_before_insert' => array( // function($feed) -> Feed | null + 'list' => array(), + 'signature' => 'OneToOne', + ), + 'post_update' => array( // function(none) -> none + 'list' => array(), + 'signature' => 'NoneToNone', + ), + ); + private static $ext_to_hooks = array(); + + /** + * Initialize the extension manager by loading extensions in EXTENSIONS_PATH. + * + * A valid extension is a directory containing metadata.json and + * extension.php files. + * metadata.json is a JSON structure where the only required fields are + * `name` and `entry_point`. + * extension.php should contain at least a class named <name>Extension where + * <name> must match with the entry point in metadata.json. This class must + * inherit from Minz_Extension class. + */ + public static function init() { + $list_potential_extensions = array_values(array_diff( + scandir(EXTENSIONS_PATH), + array('..', '.') + )); + + $system_conf = Minz_Configuration::get('system'); + self::$ext_auto_enabled = $system_conf->extensions_enabled; + + foreach ($list_potential_extensions as $ext_dir) { + $ext_pathname = EXTENSIONS_PATH . '/' . $ext_dir; + if (!is_dir($ext_pathname)) { + continue; + } + $metadata_filename = $ext_pathname . '/' . self::$ext_metaname; + + // Try to load metadata file. + if (!file_exists($metadata_filename)) { + // No metadata file? Invalid! + continue; + } + $meta_raw_content = file_get_contents($metadata_filename); + $meta_json = json_decode($meta_raw_content, true); + if (!$meta_json || !self::isValidMetadata($meta_json)) { + // metadata.json is not a json file? Invalid! + // or metadata.json is invalid (no required information), invalid! + Minz_Log::warning('`' . $metadata_filename . '` is not a valid metadata file'); + continue; + } + + $meta_json['path'] = $ext_pathname; + + // Try to load extension itself + $extension = self::load($meta_json); + if (!is_null($extension)) { + self::register($extension); + } + } + } + + /** + * Indicates if the given parameter is a valid metadata array. + * + * Required fields are: + * - `name`: the name of the extension + * - `entry_point`: a class name to load the extension source code + * If the extension class name is `TestExtension`, entry point will be `Test`. + * `entry_point` must be composed of alphanumeric characters. + * + * @param $meta is an array of values. + * @return true if the array is valid, false else. + */ + public static function isValidMetadata($meta) { + $valid_chars = array('_'); + return !(empty($meta['name']) || + empty($meta['entrypoint']) || + !ctype_alnum(str_replace($valid_chars, '', $meta['entrypoint']))); + } + + /** + * Load the extension source code based on info metadata. + * + * @param $info an array containing information about extension. + * @return an extension inheriting from Minz_Extension. + */ + public static function load($info) { + $entry_point_filename = $info['path'] . '/' . self::$ext_entry_point; + $ext_class_name = $info['entrypoint'] . 'Extension'; + + include_once($entry_point_filename); + + // Test if the given extension class exists. + if (!class_exists($ext_class_name)) { + Minz_Log::warning('`' . $ext_class_name . + '` cannot be found in `' . $entry_point_filename . '`'); + return null; + } + + // Try to load the class. + $extension = null; + try { + $extension = new $ext_class_name($info); + } catch (Minz_ExtensionException $e) { + // We cannot load the extension? Invalid! + Minz_Log::warning('In `' . $metadata_filename . '`: ' . $e->getMessage()); + return null; + } + + // Test if class is correct. + if (!($extension instanceof Minz_Extension)) { + Minz_Log::warning('`' . $ext_class_name . + '` is not an instance of `Minz_Extension`'); + return null; + } + + return $extension; + } + + /** + * Add the extension to the list of the known extensions ($ext_list). + * + * If the extension is present in $ext_auto_enabled and if its type is "system", + * it will be enabled in the same time. + * + * @param $ext a valid extension. + */ + public static function register($ext) { + $name = $ext->getName(); + self::$ext_list[$name] = $ext; + + if ($ext->getType() === 'system' && + in_array($name, self::$ext_auto_enabled)) { + self::enable($ext->getName()); + } + + self::$ext_to_hooks[$name] = array(); + } + + /** + * Enable an extension so it will be called when necessary. + * + * The extension init() method will be called. + * + * @param $ext_name is the name of a valid extension present in $ext_list. + */ + public static function enable($ext_name) { + if (isset(self::$ext_list[$ext_name])) { + $ext = self::$ext_list[$ext_name]; + self::$ext_list_enabled[$ext_name] = $ext; + $ext->enable(); + $ext->init(); + } + } + + /** + * Enable a list of extensions. + * + * @param $ext_list the names of extensions we want to load. + */ + public static function enableByList($ext_list) { + foreach ($ext_list as $ext_name) { + self::enable($ext_name); + } + } + + /** + * Return a list of extensions. + * + * @param $only_enabled if true returns only the enabled extensions (false by default). + * @return an array of extensions. + */ + public static function listExtensions($only_enabled = false) { + if ($only_enabled) { + return self::$ext_list_enabled; + } else { + return self::$ext_list; + } + } + + /** + * Return an extension by its name. + * + * @param $ext_name the name of the extension. + * @return the corresponding extension or null if it doesn't exist. + */ + public static function findExtension($ext_name) { + if (!isset(self::$ext_list[$ext_name])) { + return null; + } + + return self::$ext_list[$ext_name]; + } + + /** + * Add a hook function to a given hook. + * + * The hook name must be a valid one. For the valid list, see self::$hook_list + * array keys. + * + * @param $hook_name the hook name (must exist). + * @param $hook_function the function name to call (must be callable). + * @param $ext the extension which register the hook. + */ + public static function addHook($hook_name, $hook_function, $ext) { + if (isset(self::$hook_list[$hook_name]) && is_callable($hook_function)) { + self::$hook_list[$hook_name]['list'][] = $hook_function; + self::$ext_to_hooks[$ext->getName()][] = $hook_name; + } + } + + /** + * Call functions related to a given hook. + * + * The hook name must be a valid one. For the valid list, see self::$hook_list + * array keys. + * + * @param $hook_name the hook to call. + * @param additionnal parameters (for signature, please see self::$hook_list). + * @return the final result of the called hook. + */ + public static function callHook($hook_name) { + if (!isset(self::$hook_list[$hook_name])) { + return; + } + + $signature = self::$hook_list[$hook_name]['signature']; + $signature = 'self::call' . $signature; + $args = func_get_args(); + + return call_user_func_array($signature, $args); + } + + /** + * Call a hook which takes one argument and return a result. + * + * The result is chained between the extension, for instance, first extension + * hook will receive the initial argument and return a result which will be + * passed as an argument to the next extension hook and so on. + * + * If a hook return a null value, the method is stopped and return null. + * + * @param $hook_name is the hook to call. + * @param $arg is the argument to pass to the first extension hook. + * @return the final chained result of the hooks. If nothing is changed, + * the initial argument is returned. + */ + private static function callOneToOne($hook_name, $arg) { + $result = $arg; + foreach (self::$hook_list[$hook_name]['list'] as $function) { + $result = call_user_func($function, $arg); + + if (is_null($result)) { + break; + } + + $arg = $result; + } + return $result; + } + + /** + * Call a hook which takes no argument and returns nothing. + * + * This case is simpler than callOneToOne because hooks are called one by + * one, without any consideration of argument nor result. + * + * @param $hook_name is the hook to call. + */ + private static function callNoneToNone($hook_name) { + foreach (self::$hook_list[$hook_name]['list'] as $function) { + call_user_func($function); + } + } +} diff --git a/lib/Minz/FrontController.php b/lib/Minz/FrontController.php index f13882801..f9eff3db6 100644 --- a/lib/Minz/FrontController.php +++ b/lib/Minz/FrontController.php @@ -30,14 +30,13 @@ class Minz_FrontController { * Initialise le dispatcher, met à jour la Request */ public function __construct () { - if (LOG_PATH === false) { - $this->killApp ('Path not found: LOG_PATH'); - } - try { - Minz_Configuration::init (); + Minz_Configuration::register('system', + DATA_PATH . '/config.php', + DATA_PATH . '/config.default.php'); + $this->setReporting(); - Minz_Request::init (); + Minz_Request::init(); $url = $this->buildUrl(); $url['params'] = array_merge ( @@ -46,7 +45,7 @@ class Minz_FrontController { ); Minz_Request::forward ($url); } catch (Minz_Exception $e) { - Minz_Log::record ($e->getMessage (), Minz_Log::ERROR); + Minz_Log::error($e->getMessage()); $this->killApp ($e->getMessage ()); } @@ -85,7 +84,7 @@ class Minz_FrontController { $this->dispatcher->run(); } catch (Minz_Exception $e) { try { - Minz_Log::record ($e->getMessage (), Minz_Log::ERROR); + Minz_Log::error($e->getMessage()); } catch (Minz_PermissionDeniedException $e) { $this->killApp ($e->getMessage ()); } @@ -114,4 +113,23 @@ class Minz_FrontController { } exit ('### Application problem ###<br />'."\n".$txt); } + + private function setReporting() { + $conf = Minz_Configuration::get('system'); + switch($conf->environment) { + case 'production': + error_reporting(E_ALL); + ini_set('display_errors','Off'); + ini_set('log_errors', 'On'); + break; + case 'development': + error_reporting(E_ALL); + ini_set('display_errors','On'); + ini_set('log_errors', 'On'); + break; + case 'silent': + error_reporting(0); + break; + } + } } diff --git a/lib/Minz/Log.php b/lib/Minz/Log.php index d3eaec2ae..2a9e10993 100644 --- a/lib/Minz/Log.php +++ b/lib/Minz/Log.php @@ -28,16 +28,21 @@ class Minz_Log { * - level = NOTICE et environment = PRODUCTION * @param $information message d'erreur / information à enregistrer * @param $level niveau d'erreur - * @param $file_name fichier de log, par défaut LOG_PATH/application.log + * @param $file_name fichier de log */ public static function record ($information, $level, $file_name = null) { - $env = Minz_Configuration::environment (); + try { + $conf = Minz_Configuration::get('system'); + $env = $conf->environment; + } catch (Minz_ConfigurationException $e) { + $env = 'production'; + } - if (! ($env === Minz_Configuration::SILENT - || ($env === Minz_Configuration::PRODUCTION + if (! ($env === 'silent' + || ($env === 'production' && ($level >= Minz_Log::NOTICE)))) { if ($file_name === null) { - $file_name = LOG_PATH . '/' . Minz_Session::param('currentUser', '_') . '.log'; + $file_name = join_path(USERS_PATH, Minz_Session::param('currentUser', '_'), 'log.txt'); } switch ($level) { @@ -71,7 +76,7 @@ class Minz_Log { * Automatise le log des variables globales $_GET et $_POST * Fait appel à la fonction record(...) * Ne fonctionne qu'en environnement "development" - * @param $file_name fichier de log, par défaut LOG_PATH/application.log + * @param $file_name fichier de log */ public static function recordRequest($file_name = null) { $msg_get = str_replace("\n", '', '$_GET content : ' . print_r($_GET, true)); diff --git a/lib/Minz/ModelPdo.php b/lib/Minz/ModelPdo.php index b4bfca746..ac7a1bed7 100644 --- a/lib/Minz/ModelPdo.php +++ b/lib/Minz/ModelPdo.php @@ -16,6 +16,8 @@ class Minz_ModelPdo { public static $useSharedBd = true; private static $sharedBd = null; private static $sharedPrefix; + private static $has_transaction = false; + private static $sharedCurrentUser; protected static $sharedDbType; /** @@ -23,6 +25,7 @@ class Minz_ModelPdo { */ protected $bd; + protected $current_user; protected $prefix; public function dbType() { @@ -37,14 +40,18 @@ class Minz_ModelPdo { if (self::$useSharedBd && self::$sharedBd != null && $currentUser === null) { $this->bd = self::$sharedBd; $this->prefix = self::$sharedPrefix; + $this->current_user = self::$sharedCurrentUser; return; } - $db = Minz_Configuration::dataBase(); + $conf = Minz_Configuration::get('system'); + $db = $conf->db; if ($currentUser === null) { $currentUser = Minz_Session::param('currentUser', '_'); } + $this->current_user = $currentUser; + self::$sharedCurrentUser = $currentUser; try { $type = $db['type']; @@ -57,7 +64,7 @@ class Minz_ModelPdo { ); $this->prefix = $db['prefix'] . $currentUser . '_'; } elseif ($type === 'sqlite') { - $string = 'sqlite:' . DATA_PATH . '/' . $currentUser . '.sqlite'; + $string = 'sqlite:' . join_path(DATA_PATH, 'users', $currentUser, 'db.sqlite'); $driver_options = array( //PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, ); @@ -91,12 +98,18 @@ class Minz_ModelPdo { public function beginTransaction() { $this->bd->beginTransaction(); + self::$has_transaction = true; + } + public function hasTransaction() { + return self::$has_transaction; } public function commit() { $this->bd->commit(); + self::$has_transaction = false; } public function rollBack() { $this->bd->rollBack(); + self::$has_transaction = false; } public static function clean() { diff --git a/lib/Minz/Request.php b/lib/Minz/Request.php index f7a24c026..6db2e9c7a 100644 --- a/lib/Minz/Request.php +++ b/lib/Minz/Request.php @@ -45,6 +45,13 @@ class Minz_Request { public static function defaultActionName() { return self::$default_action_name; } + public static function currentRequest() { + return array( + 'c' => self::$controller_name, + 'a' => self::$action_name, + 'params' => self::$params, + ); + } /** * Setteurs @@ -89,7 +96,8 @@ class Minz_Request { * @return la base de l'url */ public static function getBaseUrl() { - $defaultBaseUrl = Minz_Configuration::baseUrl(); + $conf = Minz_Configuration::get('system'); + $defaultBaseUrl = $conf->base_url; if (!empty($defaultBaseUrl)) { return $defaultBaseUrl; } elseif (isset($_SERVER['REQUEST_URI'])) { diff --git a/lib/Minz/Session.php b/lib/Minz/Session.php index af4de75bb..058685ada 100644 --- a/lib/Minz/Session.php +++ b/lib/Minz/Session.php @@ -55,7 +55,7 @@ class Minz_Session { if (!$force) { self::_param('language', $language); - Minz_Translate::reset(); + Minz_Translate::reset($language); } } @@ -65,7 +65,9 @@ class Minz_Session { * @param $l la durée de vie */ public static function keepCookie($l) { - $cookie_dir = empty($_SERVER['REQUEST_URI']) ? '' : $_SERVER['REQUEST_URI']; + // Get the script_name (e.g. /p/i/index.php) and keep only the path. + $cookie_dir = empty($_SERVER['SCRIPT_NAME']) ? '' : $_SERVER['SCRIPT_NAME']; + $cookie_dir = dirname($cookie_dir); session_set_cookie_params($l, $cookie_dir, '', false, true); } diff --git a/lib/Minz/Translate.php b/lib/Minz/Translate.php index 8c2f90041..baddcb424 100644 --- a/lib/Minz/Translate.php +++ b/lib/Minz/Translate.php @@ -5,71 +5,222 @@ */ /** - * La classe Translate se charge de la traduction - * Utilise les fichiers du répertoire /app/i18n/ + * This class is used for the internationalization. + * It uses files in `./app/i18n/` */ class Minz_Translate { /** - * $language est la langue à afficher + * $path_list is the list of registered base path to search translations. */ - private static $language; - + private static $path_list = array(); + + /** + * $lang_name is the name of the current language to use. + */ + private static $lang_name; + + /** + * $lang_files is a list of registered i18n files. + */ + private static $lang_files = array(); + /** - * $translates est le tableau de correspondance - * $key => $traduction + * $translates is a cache for i18n translation. */ private static $translates = array(); - + + /** + * Init the translation object. + * @param $lang_name the lang to show. + */ + public static function init($lang_name = null) { + self::$lang_name = $lang_name; + self::$lang_files = array(); + self::$translates = array(); + self::registerPath(APP_PATH . '/i18n'); + foreach (self::$path_list as $path) { + self::loadLang($path); + } + } + + /** + * Reset the translation object with a new language. + * @param $lang_name the new language to use + */ + public static function reset($lang_name) { + self::$lang_name = $lang_name; + self::$lang_files = array(); + self::$translates = array(); + foreach (self::$path_list as $path) { + self::loadLang($path); + } + } + + /** + * Return the list of available languages. + * @return an array containing langs found in different registered paths. + */ + public static function availableLanguages() { + $list_langs = array(); + + foreach (self::$path_list as $path) { + $path_langs = array_values(array_diff( + scandir($path), + array('..', '.') + )); + + $list_langs = array_merge($list_langs, $path_langs); + } + + return array_unique($list_langs); + } + + /** + * Register a new path. + * @param $path a path containing i18n directories (e.g. ./en/, ./fr/). + */ + public static function registerPath($path) { + if (in_array($path, self::$path_list)) { + return; + } + + self::$path_list[] = $path; + self::loadLang($path); + } + /** - * Inclus le fichier de langue qui va bien - * l'enregistre dans $translates + * Load translations of the current language from the given path. + * @param $path the path containing i18n directories. */ - 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); + private static function loadLang($path) { + $lang_path = $path . '/' . self::$lang_name; + if (!file_exists($lang_path) || is_null(self::$lang_name)) { + // The lang path does not exist, nothing more to do. + return; + } + + $list_i18n_files = array_values(array_diff( + scandir($lang_path), + array('..', '.') + )); + + // Each file basename correspond to a top-level i18n key. For each of + // these keys we store the file pathname and mark translations must be + // reloaded (by setting $translates[$i18n_key] to null). + foreach ($list_i18n_files as $i18n_filename) { + $i18n_key = basename($i18n_filename, '.php'); + if (!isset(self::$lang_files[$i18n_key])) { + self::$lang_files[$i18n_key] = array(); + } + self::$lang_files[$i18n_key][] = $lang_path . '/' . $i18n_filename; + self::$translates[$i18n_key] = null; } } - + /** - * Alias de init + * Load the files associated to $key into $translates. + * @param $key the top level i18n key we want to load. */ - public static function reset() { - self::init(); + private static function loadKey($key) { + // The top level key is not in $lang_files, it means it does not exist! + if (!isset(self::$lang_files[$key])) { + Minz_Log::debug($key . ' is not a valid top level key'); + return false; + } + + self::$translates[$key] = array(); + + foreach (self::$lang_files[$key] as $lang_pathname) { + $i18n_array = include($lang_pathname); + if (!is_array($i18n_array)) { + Minz_Log::warning('`' . $lang_pathname . '` does not contain a PHP array'); + continue; + } + + // We must avoid to erase previous data so we just override them if + // needed. + self::$translates[$key] = array_replace_recursive( + self::$translates[$key], $i18n_array + ); + } + + return true; } - + /** - * 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 + * Translate a key into its corresponding value based on selected language. + * @param $key the key to translate. + * @param additional parameters for variable keys. + * @return the value corresponding to the key. + * If no value is found, return the key itself. */ public static function t($key) { - $translate = $key; - - if (isset(self::$translates[$key])) { - $translate = self::$translates[$key]; + $group = explode('.', $key); + + if (count($group) < 2) { + Minz_Log::debug($key . ' is not in a valid format'); + $top_level = 'gen'; + } else { + $top_level = array_shift($group); + } + + // If $translates[$top_level] is null it means we have to load the + // corresponding files. + if (!isset(self::$translates[$top_level]) || + is_null(self::$translates[$top_level])) { + $res = self::loadKey($top_level); + if (!$res) { + return $key; + } + } + + // Go through the i18n keys to get the correct translation value. + $translates = self::$translates[$top_level]; + $size_group = count($group); + $level_processed = 0; + $translation_value = $key; + foreach ($group as $i18n_level) { + $level_processed++; + if (!isset($translates[$i18n_level])) { + Minz_Log::debug($key . ' is not a valid key'); + return $key; + } + + if ($level_processed < $size_group) { + $translates = $translates[$i18n_level]; + } else { + $translation_value = $translates[$i18n_level]; + } } + if (is_array($translation_value)) { + if (isset($translation_value['_'])) { + $translation_value = $translation_value['_']; + } else { + Minz_Log::debug($key . ' is not a valid key'); + return $key; + } + } + + // Get the facultative arguments to replace i18n variables. $args = func_get_args(); unset($args[0]); - - return vsprintf($translate, $args); + + return vsprintf($translation_value, $args); } - + /** - * Retourne la langue utilisée actuellement - * @return la langue + * Return the current language. */ public static function language() { - return self::$language; + return self::$lang_name; } } + +/** + * Alias for Minz_Translate::t() + */ function _t($key) { $args = func_get_args(); unset($args[0]); diff --git a/lib/Minz/Url.php b/lib/Minz/Url.php index e9f9a69ba..af555a277 100644 --- a/lib/Minz/Url.php +++ b/lib/Minz/Url.php @@ -45,45 +45,45 @@ class Minz_Url { return $url_string; } - + /** * Construit l'URI d'une URL * @param l'url sous forme de tableau * @param $encodage pour indiquer comment encoder les & (& ou & pour html) * @return l'uri sous la forme ?key=value&key2=value2 */ - private static function printUri ($url, $encodage) { + private static function printUri($url, $encodage) { $uri = ''; $separator = '?'; - - if($encodage == 'html') { + + if ($encodage === 'html') { $and = '&'; } else { $and = '&'; } - - if (isset ($url['c']) - && $url['c'] != Minz_Request::defaultControllerName ()) { + + 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 ()) { + + if (isset($url['a']) + && $url['a'] != Minz_Request::defaultActionName()) { $uri .= $separator . 'a=' . $url['a']; $separator = $and; } - - if (isset ($url['params'])) { + + if (isset($url['params'])) { foreach ($url['params'] as $key => $param) { - $uri .= $separator . $key . '=' . $param; + $uri .= $separator . urlencode($key) . '=' . urlencode($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) @@ -91,7 +91,7 @@ class Minz_Url { */ public static function checkUrl ($url) { $url_checked = $url; - + if (is_array ($url)) { if (!isset ($url['c'])) { $url_checked['c'] = Minz_Request::defaultControllerName (); @@ -103,7 +103,7 @@ class Minz_Url { $url_checked['params'] = array (); } } - + return $url_checked; } } diff --git a/lib/Minz/View.php b/lib/Minz/View.php index a0dec1824..ff5cce4a5 100644 --- a/lib/Minz/View.php +++ b/lib/Minz/View.php @@ -13,8 +13,9 @@ class Minz_View { const LAYOUT_FILENAME = '/layout.phtml'; private $view_filename = ''; - private $use_layout = null; + private $use_layout = true; + private static $base_pathnames = array(APP_PATH); private static $title = ''; private static $styles = array (); private static $scripts = array (); @@ -28,26 +29,35 @@ class Minz_View { public function __construct () { $this->change_view(Minz_Request::controllerName(), Minz_Request::actionName()); - self::$title = Minz_Configuration::title (); + + $conf = Minz_Configuration::get('system'); + self::$title = $conf->title; } /** * Change le fichier de vue en fonction d'un controller / action */ public function change_view($controller_name, $action_name) { - $this->view_filename = APP_PATH - . self::VIEWS_PATH_NAME . '/' + $this->view_filename = self::VIEWS_PATH_NAME . '/' . $controller_name . '/' . $action_name . '.phtml'; } /** + * Add a base pathname to search views. + * + * New pathnames will be added at the beginning of the list. + * + * @param $base_pathname the new base pathname. + */ + public static function addBasePathname($base_pathname) { + array_unshift(self::$base_pathnames, $base_pathname); + } + + /** * Construit la vue */ public function build () { - if ($this->use_layout === null) { //TODO: avoid file_exists and require views to be explicit - $this->use_layout = file_exists (APP_PATH . self::LAYOUT_PATH_NAME . self::LAYOUT_FILENAME); - } if ($this->use_layout) { $this->buildLayout (); } else { @@ -56,24 +66,40 @@ class Minz_View { } /** + * Include a view file. + * + * The file is searched inside list of $base_pathnames. + * + * @param $filename the name of the file to include. + * @return true if the file has been included, false else. + */ + private function includeFile($filename) { + // We search the filename in the list of base pathnames. Only the first view + // found is considered. + foreach (self::$base_pathnames as $base) { + $absolute_filename = $base . $filename; + if (file_exists($absolute_filename)) { + include $absolute_filename; + return true; + } + } + + return false; + } + + /** * Construit le layout */ public function buildLayout () { - include ( - APP_PATH - . self::LAYOUT_PATH_NAME - . self::LAYOUT_FILENAME - ); + $this->includeFile(self::LAYOUT_PATH_NAME . self::LAYOUT_FILENAME); } /** * Affiche la Vue en elle-même */ public function render () { - if ((include($this->view_filename)) === false) { - Minz_Log::record ('File not found: `' - . $this->view_filename . '`', - Minz_Log::NOTICE); + if (!$this->includeFile($this->view_filename)) { + Minz_Log::notice('File not found: `' . $this->view_filename . '`'); } } @@ -82,14 +108,9 @@ class Minz_View { * @param $part l'élément partial à ajouter */ public function partial ($part) { - $fic_partial = APP_PATH - . self::LAYOUT_PATH_NAME . '/' - . $part . '.phtml'; - - if ((include($fic_partial)) === false) { - Minz_Log::record ('File not found: `' - . $fic_partial . '`', - Minz_Log::WARNING); + $fic_partial = self::LAYOUT_PATH_NAME . '/' . $part . '.phtml'; + if (!$this->includeFile($fic_partial)) { + Minz_Log::warning('File not found: `' . $fic_partial . '`'); } } @@ -98,14 +119,9 @@ class Minz_View { * @param $helper l'élément à afficher */ public function renderHelper ($helper) { - $fic_helper = APP_PATH - . '/views/helpers/' - . $helper . '.phtml'; - - if ((include($fic_helper)) === false) {; - Minz_Log::record ('File not found: `' - . $fic_helper . '`', - Minz_Log::WARNING); + $fic_helper = '/views/helpers/' . $helper . '.phtml'; + if (!$this->includeFile($fic_helper)) { + Minz_Log::warning('File not found: `' . $fic_helper . '`'); } } |
