X-Git-Url: https://git.cyclocoop.org/%7B%7B%20url_for%28%27votes%27%2C%20votes=%27waiting%27%29%20%7D%7D?a=blobdiff_plain;f=includes%2FMessage.php;h=578e5f10e0fe77ec065284ed5f3b6918040f71ee;hb=81be80e6accedc49dcc17638b57096c43eed9158;hp=859b9d400d3dbb0745ff853f976beef557b0c89d;hpb=e5941506f9c3ac72f8ca8a5187ac74499ee8c271;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/Message.php b/includes/Message.php index 859b9d400d..578e5f10e0 100644 --- a/includes/Message.php +++ b/includes/Message.php @@ -1,8 +1,5 @@ text() ); + * * Messages can have parameters: - * Message::key( 'welcome-to' )->param( $wgSitename ).text(); // {{GRAMMAR}} and friends work correctly - * Message::key( 'are-friends' )->params( $user, $friend ); - * Message::key( 'bad-message' )->rawParam( '' ).escaped() + * 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. - * Message::key( 'file-log' )->params( $user, $filename )->inContentLanguage()->text() + * wfMessage( 'file-log', $user, $filename )->inContentLanguage()->text() + * * Checking if message exists: - * Message::key( 'mysterious-message' )->exists() + * wfMessage( 'mysterious-message' )->exists() + * * If you want to use a different language: - * Message::key( 'email-header' )->language( $user->getOption( 'language' ) )->plain() + * wfMessage( 'email-header' )->inLanguage( $user->getOption( 'language' ) )->plain() + * Note that you cannot parse the text except in the content or interface + * languages + * * * * Comparison with old wfMsg* functions: * * Use full parsing. - * Would correspond to wfMsgExt( 'key', array( 'parseinline' ), 'apple' ); - * $parsed = Message::key( 'key' )->param( 'apple' )->parse(); + * wfMsgExt( 'key', array( 'parseinline' ), 'apple' ); + * === wfMessage( 'key', 'apple' )->parse(); + * * 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. - * Would correspond to wfMsgExt( 'key', array( 'parsemag' ), 'apple', 'pear' ); - * $plain = Message::key( 'key' )->params( 'apple', 'pear' )->text(); + * wfMsgExt( 'key', array( 'parsemag' ), 'apple', 'pear' ); + * === wfMessage( 'key', 'apple', 'pear' )->text(); + * * * Shortcut for escaping the message too, similar to wfMsgHTML, but * parameters are not replaced after escaping by default. - * $escaped = Message::key( 'key' )->rawParam( 'apple' )->escaped(); + * $escaped = wfMessage( 'key' )->rawParams( 'apple' )->escaped(); + * * - * TODO: - * * test, can we have tests? - * * sort out the details marked with fixme - * * should we have _m() or similar global wrapper? + * @todo + * - test, can we have tests? * * @since 1.17 + * @author Niklas Laxström */ class Message { /** @@ -53,11 +60,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. */ @@ -68,67 +79,141 @@ class Message { */ protected $parameters = array(); + /** + * Format for the message. + * Supported formats are: + * * text (transform) + * * escaped (transform+htmlspecialchars) + * * block-parse + * * parse (default) + * * plain + */ + protected $format = 'parse'; + + /** + * Whether database can be used. + */ + protected $useDatabase = true; + + /** + * Title object to use as context + */ + protected $title = null; + + /** + * @var string + */ + protected $message; + /** * Constructor. - * @param $key String: message key + * @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 */ - public function __construct( $key ) { + public function __construct( $key, $params = array() ) { + global $wgLang; $this->key = $key; + $this->parameters = array_values( $params ); + $this->language = $wgLang; } /** * 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. - * //FIXME: key or get or something else? * @param $key String: message key + * @param Varargs: parameters as Strings * @return Message: $this */ - public function key( $key ) { - return new Message( $key ); + public static function newFromKey( $key /*...*/ ) { + $params = func_get_args(); + array_shift( $params ); + return new self( $key, $params ); } /** - * Adds parameters to the parameter list of this message. - * @param $value String: parameter + * 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. + * @param Varargs: message keys * @return Message: $this */ - public function param( $value ) { - $this->parameters[] = $value; - return $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. - * @params Vargars: parameters + * @param Varargs: parameters as Strings * @return Message: $this */ public function params( /*...*/ ) { - $this->paramList( 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; } /** - * Adds a list of parameters to the parameter list of this message. - * @param $value Array: list of parameters, array keys will be ignored. + * Add parameters that are substituted after parsing or escaping. + * 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 * @return Message: $this */ - public function paramList( array $values ) { - $this->parameters += array_values( $values ); + 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; } /** - * Adds a parameters that is substituted after parsing or escaping. - * In other words the parsing process cannot access the contents - * of this type parameter, and you need to make sure it is - * sanitized beforehand. - * @param $value String: raw parameter + * Add parameters that are numeric and will be passed through + * Language::formatNum before substitution + * @param Varargs: numeric parameters * @return Message: $this */ - public function rawParam( $value ) { - $this->parameters[] = array( 'raw' => $value ); + 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 + * + * @param $context IContextSource + * @return Message: $this + */ + public function setContext( IContextSource $context ) { + $this->inLanguage( $context->getLang() ); + $this->title( $context->getTitle() ); + return $this; } @@ -136,26 +221,62 @@ class Message { * Request the message in any language that is supported. * As a side effect interface message status is unconditionally * turned off. - * @param $lang Mixed: langauge code or language object. + * @param $lang Mixed: language code or Language object. * @return Message: $this */ - public function language( Language $lang ) { - if ( is_string( $lang ) ) { - $this->language = Language::factory( $lang ); - } else { + public function inLanguage( $lang ) { + if ( $lang instanceof Language || $lang instanceof StubUserLang ) { $this->language = $lang; + } elseif ( is_string( $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" + ); } $this->interface = false; return $this; } /** - * 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. + * @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 = null; + $this->language = $wgContLang; + return $this; + } + + /** + * Enable or disable database use. + * @param $value Boolean + * @return Message: $this + */ + public function useDatabase( $value ) { + $this->useDatabase = (bool) $value; + return $this; + } + + /** + * Set the Title object to use as context when transforming the message + * + * @param $title Title object + * @return Message: $this + */ + public function title( $title ) { + $this->title = $title; return $this; } @@ -163,25 +284,60 @@ class Message { * Returns the message parsed from wikitext to HTML. * @return String: HTML */ - public function parse() { - $string = $this->parseAsBlock( $string ); - $m = array(); - if( preg_match( '/^
(.*)\n?<\/p>\n?$/sU', $string, $m ) ) { - $string = $m[1]; + public function toString() { + $string = $this->getMessageText(); + + # Replace parameters before text parsing + $string = $this->replaceParameters( $string, 'before' ); + + # Maybe transform using the full parser + if( $this->format === 'parse' ) { + $string = $this->parseText( $string ); + $m = array(); + if( preg_match( '/^
(.*)\n?<\/p>\n?$/sU', $string, $m ) ) { + $string = $m[1]; + } + } elseif( $this->format === 'block-parse' ){ + $string = $this->parseText( $string ); + } elseif( $this->format === 'text' ){ + $string = $this->transformText( $string ); + } elseif( $this->format === 'escaped' ){ + $string = $this->transformText( $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"; + * @return String + */ + public function __toString() { + return $this->toString(); + } + + /** + * Fully parse the text from wikitext to HTML + * @return String parsed HTML + */ + public function parse() { + $this->format = 'parse'; + return $this->toString(); + } + /** * Returns the message text. {{-transformation is done. * @return String: Unescaped message text. */ public function text() { - $string = $this->getMessageText(); - $string = $this->replaceParameters( 'before' ); - $string = $this->transformText( $string ); - $string = $this->replaceParameters( 'after' ); - return $string; + $this->format = 'text'; + return $this->toString(); } /** @@ -189,10 +345,8 @@ class Message { * @return String: Unescaped untransformed message text. */ public function plain() { - $string = $this->getMessageText(); - $string = $this->replaceParameters( 'before' ); - $string = $this->replaceParameters( 'after' ); - return $string; + $this->format = 'plain'; + return $this->toString(); } /** @@ -200,25 +354,18 @@ class Message { * @return String: HTML */ public function parseAsBlock() { - $string = $this->getMessageText(); - $string = $this->replaceParameters( 'before' ); - $string = $this->parseText( $string ); - $string = $this->replaceParameters( 'after' ); - return $string; + $this->format = 'block-parse'; + return $this->toString(); } /** * Returns the message text. {{-transformation is done and the result - * is excaped excluding any raw parameters. + * is escaped excluding any raw parameters. * @return String: Escaped message text. */ public function escaped() { - $string = $this->getMessageText(); - $string = $this->replaceParameters( 'before' ); - $string = $this->transformText( $string ); - $string = htmlspecialchars( $string ); - $string = $this->replaceParameters( 'after' ); - return $string; + $this->format = 'escaped'; + return $this->toString(); } /** @@ -226,58 +373,134 @@ class Message { * @return Bool: true if it is and false if not. */ public function exists() { - return !wfEmptyMsg( $this->key, $this->getMessageText() ); + return $this->fetchMessage() !== false; + } + + /** + * Check whether a message does not exist, or is an empty string + * @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 "-" + * @return Bool: true if is is and false if not + */ + public function isDisabled() { + $message = $this->fetchMessage(); + return $message === false || $message === '' || $message === '-'; + } + + /** + * @param $value + * @return array + */ + public static function rawParam( $value ) { + return array( 'raw' => $value ); + } + + /** + * @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 * @param $type String: either before or after * @return String */ - protected function replaceParameters( $type = 'before' ) { + protected function replaceParameters( $message, $type = 'before' ) { $replacementKeys = array(); - foreach( $args as $n => $param ) { - if ( $type === 'before' && !isset( $param['raw'] ) ) { - $replacementKeys['$' . ($n + 1)] = $param; - } elseif ( $type === 'after' && isset( $param['raw'] ) ) { - $replacementKeys['$' . ($n + 1)] = $param['raw']; + foreach( $this->parameters as $n => $param ) { + list( $paramType, $value ) = $this->extractParam( $param ); + if ( $type === $paramType ) { + $replacementKeys['$' . ($n + 1)] = $value; } } $message = strtr( $message, $replacementKeys ); return $message; } + /** + * Extracts the parameter type and preprocessed the value if needed. + * @param $param String|Array: Parameter as defined in this class. + * @return Tuple(type, value) + * @throws MWException + */ + 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" ); + } + } + /** * Wrapper for what ever method we use to parse wikitext. * @param $string String: Wikitext message contents - * @return Wikitext parsed into HTML + * @return string Wikitext parsed into HTML */ protected function parseText( $string ) { - global $wgOut; - if ( $this->language !== null ) { - // FIXME: remove this limitation - throw new MWException( 'Can only parse in interface or content language' ); - } - return $wgOut->parse( $string, /*linestart*/true, $this->interface() ); + return MessageCache::singleton()->parse( $string, $this->title, /*linestart*/true, $this->interface, $this->language )->getText(); } /** * Wrapper for what ever method we use to {{-transform wikitext. * @param $string String: Wikitext message contents - * @return Wikitext with {{-constructs replaced with their values. + * @return string Wikitext with {{-constructs replaced with their values. */ protected function transformText( $string ) { - global $wgMessageCache; - return $wgMessageCache->transform( $string, $this->interface, $this->language ); + return MessageCache::singleton()->transform( $string, $this->interface, $this->language, $this->title ); } /** - * Wrapper for what ever method we use to get message contents - * @return Unmodified message contents + * Returns the textual value for the message. + * @return Message contents or placeholder */ protected function getMessageText() { - global $wgMessageCache; - return $wgMessageCache->get( $this->key, /*DB*/true, $this->language ); + $message = $this->fetchMessage(); + if ( $message === false ) { + return '<' . htmlspecialchars( is_array($this->key) ? $this->key[0] : $this->key ) . '>'; + } else { + return $message; + } + } + + /** + * Wrapper for what ever method we use to get message contents + * + * @return string + */ + protected function fetchMessage() { + if ( !isset( $this->message ) ) { + $cache = MessageCache::singleton(); + if ( is_array($this->key) ) { + 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 +}