X-Git-Url: http://git.cyclocoop.org/%7B%24www_url%7Dadmin/compta/exercices/modifier.php?a=blobdiff_plain;f=includes%2FMessage.php;h=78b9ec2044384dd28f656e06ff1ff98ce983b1d3;hb=c546fae8ed67279d21daf0652349975fa034f7d1;hp=44c72ad24f9791b8b88bbac07ae209a2bda2ae18;hpb=9eebeaa3d1cc628ffb0b235a677709ebba17b98b;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/Message.php b/includes/Message.php index 44c72ad24f..787ea421c5 100644 --- a/includes/Message.php +++ b/includes/Message.php @@ -1,59 +1,160 @@ text() ); - * - * Messages can have parameters: - * wfMessage( 'welcome-to' )->params( $wgSitename )->text(); - * {{GRAMMAR}} and friends work correctly - * wfMessage( 'are-friends', $user, $friend ); - * wfMessage( 'bad-message' )->rawParams( '' )->escaped(); - * - * Sometimes the message text ends up in the database, so content language is needed. - * wfMessage( 'file-log', $user, $filename )->inContentLanguage()->text() - * - * Checking if message exists: + * Fetching and processing of interface messages. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @author Niklas Laxström + */ + +/** + * The Message class provides methods which fullfil two basic services: + * - fetching interface messages + * - processing messages into a variety of formats + * + * First implemented with MediaWiki 1.17, the Message class is intented to + * replace the old wfMsg* functions that over time grew unusable. + * @see https://www.mediawiki.org/wiki/Manual:Messages_API for equivalences + * between old and new functions. + * + * You should use the wfMessage() global function which acts as a wrapper for + * the Message class. The wrapper let you pass parameters as arguments. + * + * The most basic usage cases would be: + * + * @code + * // Initialize a Message object using the 'some_key' message key + * $message = wfMessage( 'some_key' ); + * + * // Using two parameters those values are strings 'value1' and 'value2': + * $message = wfMessage( 'some_key', + * 'value1', 'value2' + * ); + * @endcode + * + * @section message_global_fn Global function wrapper: + * + * Since wfMessage() returns a Message instance, you can chain its call with + * a method. Some of them return a Message instance too so you can chain them. + * You will find below several examples of wfMessage() usage. + * + * Fetching a message text for interface message: + * + * @code + * $button = Xml::button( + * wfMessage( 'submit' )->text() + * ); + * @endcode + * + * A Message instance can be passed parameters after it has been constructed, + * use the params() method to do so: + * + * @code + * wfMessage( 'welcome-to' ) + * ->params( $wgSitename ) + * ->text(); + * @endcode + * + * {{GRAMMAR}} and friends work correctly: + * + * @code + * wfMessage( 'are-friends', + * $user, $friend + * ); + * wfMessage( 'bad-message' ) + * ->rawParams( '' ) + * ->escaped(); + * @endcode + * + * @section message_language Changing language: + * + * Messages can be requested in a different language or in whatever current + * content language is being used. The methods are: + * - Message->inContentLanguage() + * - Message->inLanguage() + * + * Sometimes the message text ends up in the database, so content language is + * needed: + * + * @code + * wfMessage( 'file-log', + * $user, $filename + * )->inContentLanguage()->text(); + * @endcode + * + * Checking whether a message exists: + * + * @code * wfMessage( 'mysterious-message' )->exists() - * + * // returns a boolean whether the 'mysterious-message' key exist. + * @endcode + * * If you want to use a different language: - * wfMessage( 'email-header' )->inLanguage( $user->getOption( 'language' ) )->plain() - * Note that you cannot parse the text except in the content or interface - * languages - * * + * @code + * $userLanguage = $user->getOption( 'language' ); + * wfMessage( 'email-header' ) + * ->inLanguage( $userLanguage ) + * ->plain(); + * @endcode + * + * @note You can parse the text only in the content or interface languages + * + * @section message_compare_old Comparison with old wfMsg* functions: * - * Comparison with old wfMsg* functions: + * Use full parsing: * - * Use full parsing. + * @code + * // old style: * wfMsgExt( 'key', array( 'parseinline' ), 'apple' ); - * === wfMessage( 'key', 'apple' )->parse(); - * - * Parseinline is used because it is more useful when pre-building html. + * // new style: + * wfMessage( 'key', 'apple' )->parse(); + * @endcode + * + * Parseinline is used because it is more useful when pre-building HTML. * In normal use it is better to use OutputPage::(add|wrap)WikiMsg. * - * Places where html cannot be used. {{-transformation is done. + * Places where HTML cannot be used. {{-transformation is done. + * @code + * // old style: * wfMsgExt( 'key', array( 'parsemag' ), 'apple', 'pear' ); - * === wfMessage( 'key', 'apple', 'pear' )->text(); - * + * // new style: + * wfMessage( 'key', 'apple', 'pear' )->text(); + * @endcode * - * Shortcut for escaping the message too, similar to wfMsgHTML, but + * Shortcut for escaping the message too, similar to wfMsgHTML(), but * parameters are not replaced after escaping by default. - * $escaped = wfMessage( 'key' )->rawParams( 'apple' )->escaped(); - * + * @code + * $escaped = wfMessage( 'key' ) + * ->rawParams( 'apple' ) + * ->escaped(); + * @endcode + * + * @section message_appendix Appendix: * - * TODO: + * @todo * - test, can we have tests? - * - sort out the details marked with fixme + * - this documentation needs to be extended + * + * @see https://www.mediawiki.org/wiki/WfMessage() + * @see https://www.mediawiki.org/wiki/New_messages_API + * @see https://www.mediawiki.org/wiki/Localisation * * @since 1.17 - * @author Niklas Laxström */ class Message { /** @@ -61,13 +162,15 @@ class Message { * means the current interface language, false content language. */ protected $interface = true; - + /** * In which language to get this message. Overrides the $interface * variable. + * + * @var Language */ protected $language = null; - + /** * The message key. */ @@ -94,9 +197,25 @@ class Message { */ protected $useDatabase = true; + /** + * Title object to use as context + */ + protected $title = null; + + /** + * Content object representing the message + */ + protected $content = null; + + /** + * @var string + */ + protected $message; + /** * Constructor. - * @param $key String: message key + * @since 1.17 + * @param $key: message key, or array of message keys to try and use the first non-empty message for * @param $params Array message parameters * @return Message: $this */ @@ -111,6 +230,7 @@ class Message { * Factory function that is just wrapper for the real constructor. It is * intented to be used instead of the real constructor, because it allows * chaining method calls, while new objects don't. + * @since 1.17 * @param $key String: message key * @param Varargs: parameters as Strings * @return Message: $this @@ -121,13 +241,41 @@ class Message { return new self( $key, $params ); } + /** + * Factory function accepting multiple message keys and returning a message instance + * for the first message which is non-empty. If all messages are empty then an + * instance of the first message key is returned. + * @since 1.18 + * @param Varargs: message keys (or first arg as an array of all the message keys) + * @return Message: $this + */ + public static function newFallbackSequence( /*...*/ ) { + $keys = func_get_args(); + if ( func_num_args() == 1 ) { + if ( is_array($keys[0]) ) { + // Allow an array to be passed as the first argument instead + $keys = array_values($keys[0]); + } else { + // Optimize a single string to not need special fallback handling + $keys = $keys[0]; + } + } + return new self( $keys ); + } + /** * Adds parameters to the parameter list of this message. - * @param Varargs: parameters as Strings + * @since 1.17 + * @param Varargs: parameters as Strings, or a single argument that is an array of Strings * @return Message: $this */ public function params( /*...*/ ) { - $this->parameters = array_merge( $this->parameters, array_values( func_get_args() ) ); + $args = func_get_args(); + if ( isset( $args[0] ) && is_array( $args[0] ) ) { + $args = $args[0]; + } + $args_values = array_values( $args ); + $this->parameters = array_merge( $this->parameters, $args_values ); return $this; } @@ -136,33 +284,72 @@ class Message { * In other words the parsing process cannot access the contents * of this type of parameter, and you need to make sure it is * sanitized beforehand. The parser will see "$n", instead. - * @param Varargs: raw parameters as Strings + * @since 1.17 + * @param Varargs: raw parameters as Strings (or single argument that is an array of raw parameters) * @return Message: $this */ public function rawParams( /*...*/ ) { $params = func_get_args(); + if ( isset( $params[0] ) && is_array( $params[0] ) ) { + $params = $params[0]; + } foreach( $params as $param ) { $this->parameters[] = self::rawParam( $param ); } return $this; } - + + /** + * Add parameters that are numeric and will be passed through + * Language::formatNum before substitution + * @since 1.18 + * @param Varargs: numeric parameters (or single argument that is array of numeric parameters) + * @return Message: $this + */ + public function numParams( /*...*/ ) { + $params = func_get_args(); + if ( isset( $params[0] ) && is_array( $params[0] ) ) { + $params = $params[0]; + } + foreach( $params as $param ) { + $this->parameters[] = self::numParam( $param ); + } + return $this; + } + + /** + * Set the language and the title from a context object + * @since 1.19 + * @param $context IContextSource + * @return Message: $this + */ + public function setContext( IContextSource $context ) { + $this->inLanguage( $context->getLanguage() ); + $this->title( $context->getTitle() ); + $this->interface = true; + + return $this; + } + /** * Request the message in any language that is supported. * As a side effect interface message status is unconditionally * turned off. + * @since 1.17 * @param $lang Mixed: language code or Language object. * @return Message: $this */ public function inLanguage( $lang ) { - if( $lang instanceof Language ){ + if ( $lang instanceof Language || $lang instanceof StubUserLang ) { $this->language = $lang; } elseif ( is_string( $lang ) ) { - $this->language = Language::factory( $lang ); + if( $this->language->getCode() != $lang ) { + $this->language = Language::factory( $lang ); + } } else { $type = gettype( $lang ); throw new MWException( __METHOD__ . " must be " - . "passed a String or Language object; $type given" + . "passed a String or Language object; $type given" ); } $this->interface = false; @@ -170,18 +357,39 @@ class Message { } /** - * Request the message in the wiki's content language. + * Request the message in the wiki's content language, + * unless it is disabled for this message. + * @since 1.17 + * @see $wgForceUIMsgAsContentMsg * @return Message: $this */ public function inContentLanguage() { + global $wgForceUIMsgAsContentMsg; + if ( in_array( $this->key, (array)$wgForceUIMsgAsContentMsg ) ) { + return $this; + } + global $wgContLang; $this->interface = false; $this->language = $wgContLang; return $this; } + /** + * Allows manipulating the interface message flag directly. + * Can be used to restore the flag after setting a language. + * @param $value bool + * @return Message: $this + * @since 1.20 + */ + public function setInterfaceMessageFlag( $value ) { + $this->interface = (bool) $value; + return $this; + } + /** * Enable or disable database use. + * @since 1.17 * @param $value Boolean * @return Message: $this */ @@ -190,22 +398,48 @@ class Message { return $this; } + /** + * Set the Title object to use as context when transforming the message + * @since 1.18 + * @param $title Title object + * @return Message: $this + */ + public function title( $title ) { + $this->title = $title; + return $this; + } + + /** + * Returns the message as a Content object. + * @return Content + */ + public function content() { + if ( !$this->content ) { + $this->content = new MessageContent( $this->key ); + } + + return $this->content; + } + /** * Returns the message parsed from wikitext to HTML. - * TODO: in PHP >= 5.2.0, we can make this a magic method, - * and then we can do, eg: - * $foo = Message::get($key); - * $string = "$foo"; - * But we shouldn't implement that while MediaWiki still supports - * PHP < 5.2; or people will start using it... + * @since 1.17 * @return String: HTML */ public function toString() { - $string = $this->getMessageText(); - + $string = $this->fetchMessage(); + + if ( $string === false ) { + $key = htmlspecialchars( is_array( $this->key ) ? $this->key[0] : $this->key ); + if ( $this->format === 'plain' ) { + return '<' . $key . '>'; + } + return '<' . $key . '>'; + } + # Replace parameters before text parsing $string = $this->replaceParameters( $string, 'before' ); - + # Maybe transform using the full parser if( $this->format === 'parse' ) { $string = $this->parseText( $string ); @@ -218,19 +452,30 @@ class Message { } elseif( $this->format === 'text' ){ $string = $this->transformText( $string ); } elseif( $this->format === 'escaped' ){ - # FIXME: Sanitizer method here? $string = $this->transformText( $string ); - $string = htmlspecialchars( $string ); + $string = htmlspecialchars( $string, ENT_QUOTES, 'UTF-8', false ); } - + # Raw parameter replacement $string = $this->replaceParameters( $string, 'after' ); - + return $string; } - + + /** + * Magic method implementation of the above (for PHP >= 5.2.0), so we can do, eg: + * $foo = Message::get($key); + * $string = "$foo"; + * @since 1.18 + * @return String + */ + public function __toString() { + return $this->toString(); + } + /** * Fully parse the text from wikitext to HTML + * @since 1.17 * @return String parsed HTML */ public function parse() { @@ -240,6 +485,7 @@ class Message { /** * Returns the message text. {{-transformation is done. + * @since 1.17 * @return String: Unescaped message text. */ public function text() { @@ -249,6 +495,7 @@ class Message { /** * Returns the message text as-is, only parameters are subsituted. + * @since 1.17 * @return String: Unescaped untransformed message text. */ public function plain() { @@ -258,6 +505,7 @@ class Message { /** * Returns the parsed message text which is always surrounded by a block element. + * @since 1.17 * @return String: HTML */ public function parseAsBlock() { @@ -268,6 +516,7 @@ class Message { /** * Returns the message text. {{-transformation is done and the result * is escaped excluding any raw parameters. + * @since 1.17 * @return String: Escaped message text. */ public function escaped() { @@ -277,29 +526,65 @@ class Message { /** * Check whether a message key has been defined currently. + * @since 1.17 * @return Bool: true if it is and false if not. */ public function exists() { return $this->fetchMessage() !== false; } + /** + * Check whether a message does not exist, or is an empty string + * @since 1.18 + * @return Bool: true if is is and false if not + * @todo FIXME: Merge with isDisabled()? + */ + public function isBlank() { + $message = $this->fetchMessage(); + return $message === false || $message === ''; + } + + /** + * Check whether a message does not exist, is an empty string, or is "-" + * @since 1.18 + * @return Bool: true if it is and false if not + */ + public function isDisabled() { + $message = $this->fetchMessage(); + return $message === false || $message === '' || $message === '-'; + } + + /** + * @since 1.17 + * @param $value + * @return array + */ public static function rawParam( $value ) { return array( 'raw' => $value ); } + /** + * @since 1.18 + * @param $value + * @return array + */ + public static function numParam( $value ) { + return array( 'num' => $value ); + } + /** * Substitutes any paramaters into the message text. - * @param $message String, the message text + * @since 1.17 + * @param $message String: the message text * @param $type String: either before or after * @return String */ protected function replaceParameters( $message, $type = 'before' ) { $replacementKeys = array(); foreach( $this->parameters as $n => $param ) { - if ( $type === 'before' && !is_array( $param ) ) { - $replacementKeys['$' . ($n + 1)] = $param; - } elseif ( $type === 'after' && isset( $param['raw'] ) ) { - $replacementKeys['$' . ($n + 1)] = $param['raw']; + list( $paramType, $value ) = $this->extractParam( $param ); + if ( $type === $paramType ) { + $replacementKeys['$' . ($n + 1)] = $value; } } $message = strtr( $message, $replacementKeys ); @@ -307,51 +592,70 @@ class Message { } /** - * Wrapper for what ever method we use to parse wikitext. - * @param $string String: Wikitext message contents - * @return Wikitext parsed into HTML + * Extracts the parameter type and preprocessed the value if needed. + * @since 1.18 + * @param $param String|Array: Parameter as defined in this class. + * @return Tuple(type, value) + * @throws MWException */ - protected function parseText( $string ) { - global $wgOut, $wgLang, $wgContLang; - if ( $this->language !== $wgLang && $this->language !== $wgContLang ) { - # FIXME: remove this limitation - throw new MWException( 'Can only parse in interface or content language' ); + protected function extractParam( $param ) { + if ( is_array( $param ) && isset( $param['raw'] ) ) { + return array( 'after', $param['raw'] ); + } elseif ( is_array( $param ) && isset( $param['num'] ) ) { + // Replace number params always in before step for now. + // No support for combined raw and num params + return array( 'before', $this->language->formatNum( $param['num'] ) ); + } elseif ( !is_array( $param ) ) { + return array( 'before', $param ); + } else { + throw new MWException( "Invalid message parameter" ); } - return $wgOut->parse( $string, /*linestart*/true, $this->interface ); } /** - * Wrapper for what ever method we use to {{-transform wikitext. + * Wrapper for what ever method we use to parse wikitext. + * @since 1.17 * @param $string String: Wikitext message contents - * @return Wikitext with {{-constructs replaced with their values. + * @return string Wikitext parsed into HTML */ - protected function transformText( $string ) { - global $wgMessageCache; - return $wgMessageCache->transform( $string, $this->interface, $this->language ); + protected function parseText( $string ) { + return MessageCache::singleton()->parse( $string, $this->title, /*linestart*/true, $this->interface, $this->language )->getText(); } /** - * Returns the textual value for the message. - * @return Message contents or placeholder + * Wrapper for what ever method we use to {{-transform wikitext. + * @since 1.17 + * @param $string String: Wikitext message contents + * @return string Wikitext with {{-constructs replaced with their values. */ - protected function getMessageText() { - $message = $this->fetchMessage(); - if ( $message === false ) { - return '<' . htmlspecialchars( $this->key ) . '>'; - } else { - return $message; - } + protected function transformText( $string ) { + return MessageCache::singleton()->transform( $string, $this->interface, $this->language, $this->title ); } /** * Wrapper for what ever method we use to get message contents + * @since 1.17 + * @return string */ protected function fetchMessage() { if ( !isset( $this->message ) ) { - global $wgMessageCache; - $this->message = $wgMessageCache->get( $this->key, $this->useDatabase, $this->language ); + $cache = MessageCache::singleton(); + if ( is_array( $this->key ) ) { + if ( !count( $this->key ) ) { + throw new MWException( "Given empty message key array." ); + } + foreach ( $this->key as $key ) { + $message = $cache->get( $key, $this->useDatabase, $this->language ); + if ( $message !== false && $message !== '' ) { + break; + } + } + $this->message = $message; + } else { + $this->message = $cache->get( $this->key, $this->useDatabase, $this->language ); + } } return $this->message; } -} \ No newline at end of file +}