Add Message/MessageValue user group member parameter type

* Added ParamType::OBJECT, which allows Stringable objects to be passed into MessageValue

Bug: T278482
Change-Id: Ib4990f87d4ad70b7525d7aa05c8b97e90c121674
This commit is contained in:
TChin 2021-10-15 13:39:56 -04:00
parent 80d3ece54a
commit 349819dc5a
11 changed files with 211 additions and 11 deletions

View file

@ -0,0 +1,52 @@
<?php
/**
* Represents a Message/MessageValue parameter user group membership to be used with ParamType::OBJECT.
*
* 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
*/
namespace MediaWiki\Message;
use MediaWiki\User\UserIdentity;
use Stringable;
class UserGroupMembershipParam implements Stringable {
/** @var string */
private $group;
/** @var UserIdentity */
private $member;
public function __construct( string $group, UserIdentity $member ) {
$this->group = $group;
$this->member = $member;
}
public function getGroup(): string {
return $this->group;
}
public function getMember(): UserIdentity {
return $this->member;
}
public function __toString() {
return $this->group . ':' . $this->member->getName();
}
}

View file

@ -899,10 +899,11 @@ class Language {
* Get message object in this language. Only for use inside this class.
*
* @param string $msg Message name
* @param mixed ...$params Message parameters
* @return Message
*/
protected function msg( $msg ) {
return wfMessage( $msg )->inLanguage( $this );
protected function msg( $msg, ...$params ) {
return wfMessage( $msg, ...$params )->inLanguage( $this );
}
/**
@ -2615,6 +2616,22 @@ class Language {
return $msg->isBlank() ? $group : $msg->text();
}
/**
* Gets the localized name for a member of a group, if it exists. For example,
* "administrator" or "bureaucrat"
*
* @param string $group Internal group name
* @param string|UserIdentity $member
* @return string Localized name for group member
*/
public function getGroupMemberName( string $group, $member ) {
if ( $member instanceof UserIdentity ) {
$member = $member->getName();
}
$msg = $this->msg( "group-$group-member", $member );
return $msg->isBlank() ? $group : $msg->text();
}
/**
* @param string $key
* @return string|null

View file

@ -21,6 +21,7 @@
use MediaWiki\Logger\LoggerFactory;
use MediaWiki\MediaWikiServices;
use MediaWiki\Message\UserGroupMembershipParam;
use MediaWiki\Page\PageReference;
use MediaWiki\Page\PageReferenceValue;
@ -649,6 +650,26 @@ class Message implements MessageSpecifier, Serializable {
return $this;
}
/**
* Add parameters that represent stringable objects
*
* @since 1.38
*
* @param Stringable|Stringable[] ...$params stringable parameters,
* or a single argument that is an array of stringable parameters.
*
* @return Message $this
*/
public function objectParams( ...$params ) {
if ( isset( $params[0] ) && is_array( $params[0] ) ) {
$params = $params[0];
}
foreach ( $params as $param ) {
$this->parameters[] = self::objectParam( $param );
}
return $this;
}
/**
* Add parameters that are times and will be passed through
* Language::time before substitution
@ -1173,6 +1194,17 @@ class Message implements MessageSpecifier, Serializable {
return [ 'group' => $userGroup ];
}
/**
* @since 1.38
*
* @param Stringable $object
*
* @return Stringable[] Array with a single "object" key.
*/
public static function objectParam( Stringable $object ) {
return [ 'object' => $object ];
}
/**
* @since 1.22
*
@ -1311,6 +1343,16 @@ class Message implements MessageSpecifier, Serializable {
return [ 'after', $this->formatPlaintext( $param['plaintext'], $format ) ];
} elseif ( isset( $param['list'] ) ) {
return $this->formatListParam( $param['list'], $param['type'], $format );
} elseif ( isset( $param['object'] ) ) {
$obj = $param['object'];
if ( $obj instanceof UserGroupMembershipParam ) {
return [
'before',
$this->getLanguage()->getGroupMemberName( $obj->getGroup(), $obj->getMember() )
];
} else {
return [ 'before', $obj->__toString() ];
}
} else {
LoggerFactory::getInstance( 'Bug58676' )->warning(
'Invalid parameter for message "{msgkey}": {param}',

View file

@ -2,6 +2,8 @@
namespace Wikimedia\Message;
use Stringable;
/**
* Value object representing a message for i18n.
*
@ -92,6 +94,19 @@ class MessageValue {
return $this;
}
/**
* Chainable mutator which adds object parameters
*
* @param Stringable ...$values stringable object values
* @return $this
*/
public function objectParams( ...$values ) {
foreach ( $values as $value ) {
$this->params[] = new ScalarParam( ParamType::OBJECT, $value );
}
return $this;
}
/**
* Chainable mutator which adds list parameters with a common type
*

View file

@ -60,12 +60,17 @@ class ParamType {
public const TIME = 'time';
/**
* @since 1.38
*
* User Group
* @since 1.38
*/
public const GROUP = 'group';
/**
* For arbitrary stringable objects
* @since 1.38
*/
public const OBJECT = 'object';
/** A number of bytes. The output will be rounded to an appropriate magnitude. */
public const SIZE = 'size';

View file

@ -2,6 +2,8 @@
namespace Wikimedia\Message;
use Stringable;
/**
* Value object representing a message parameter holding a single value.
*
@ -16,7 +18,7 @@ class ScalarParam extends MessageParam {
* @stable to call.
*
* @param string $type One of the ParamType constants.
* @param string|int|float|MessageValue $value
* @param string|int|float|MessageValue|Stringable $value
*/
public function __construct( $type, $value ) {
if ( $type === ParamType::LIST ) {
@ -24,7 +26,8 @@ class ScalarParam extends MessageParam {
'ParamType::LIST cannot be used with ScalarParam; use ListParam instead'
);
}
if ( !is_string( $value ) && !is_numeric( $value ) && !$value instanceof MessageValue ) {
if ( !is_string( $value ) && !is_numeric( $value ) &&
!$value instanceof MessageValue && !$value instanceof Stringable ) {
$type = is_object( $value ) ? get_class( $value ) : gettype( $value );
throw new \InvalidArgumentException(
"Scalar parameter must be a string, number, or MessageValue; got $type"
@ -39,7 +42,7 @@ class ScalarParam extends MessageParam {
if ( $this->value instanceof MessageValue ) {
$contents = $this->value->dump();
} else {
$contents = htmlspecialchars( $this->value );
$contents = htmlspecialchars( (string)$this->value );
}
return "<{$this->type}>" . $contents . "</{$this->type}>";
}

View file

@ -21,6 +21,7 @@
*/
use MediaWiki\MediaWikiServices;
use MediaWiki\User\UserIdentity;
/**
* Represents a "user group membership" -- a specific instance of a user belonging
@ -175,12 +176,11 @@ class UserGroupMembership {
* "administrator" or "bureaucrat"
*
* @param string $group Internal group name
* @param string $username Username for gender
* @param string|UserIdentity $member Username or UserIdentity of member for gender
* @return string Localized name for group member
*/
public static function getGroupMemberName( $group, $username ) {
$msg = wfMessage( "group-$group-member", $username );
return $msg->isBlank() ? $group : $msg->text();
public static function getGroupMemberName( $group, $member ) {
return RequestContext::getMain()->getLanguage()->getGroupMemberName( $group, $member );
}
/**

View file

@ -4,6 +4,8 @@ namespace MediaWiki\Tests\Message;
use MediaWiki\Message\Converter;
use MediaWiki\Message\TextFormatter;
use MediaWiki\Message\UserGroupMembershipParam;
use MediaWiki\User\UserIdentityValue;
use MediaWikiIntegrationTestCase;
use Message;
use Wikimedia\Message\MessageValue;
@ -92,6 +94,13 @@ class TextFormatterTest extends MediaWikiIntegrationTestCase {
->userGroupParams( 'bot' ),
'test (group-bot) $2'
];
yield [ ( new MessageValue( 'test' ) )
->objectParams(
new UserGroupMembershipParam( 'bot', new UserIdentityValue( 1, 'user' ) )
),
'test (group-bot-member: user) $2'
];
}
/**

View file

@ -1,8 +1,10 @@
<?php
use MediaWiki\MediaWikiServices;
use MediaWiki\Message\UserGroupMembershipParam;
use MediaWiki\Page\PageReference;
use MediaWiki\Page\PageReferenceValue;
use MediaWiki\User\UserIdentityValue;
use Wikimedia\TestingAccessWrapper;
/**
@ -551,6 +553,23 @@ class MessageTest extends MediaWikiLangTestCase {
);
}
/**
* @covers Message::objectParam
* @covers Message::objectParams
*/
public function testUserGroupMemberParams() {
$lang = MediaWikiServices::getInstance()->getLanguageFactory()->getLanguage( 'qqx' );
$msg = new RawMessage( '$1' );
$this->setUserLang( $lang );
$this->assertSame(
'(group-bot-member: user)',
$msg->objectParams(
new UserGroupMembershipParam( 'bot', new UserIdentityValue( 1, 'user' ) )
)->plain(),
'user group member is handled correctly'
);
}
/**
* @covers Message::timeperiodParam
* @covers Message::timeperiodParams

View file

@ -6,6 +6,8 @@ use MediaWiki\Languages\LanguageConverterFactory;
use MediaWiki\Languages\LanguageFallback;
use MediaWiki\Languages\LanguageNameUtils;
use MediaWiki\MediaWikiServices;
use MediaWiki\User\UserIdentityValue;
use Wikimedia\TestingAccessWrapper;
/**
* @group Language
@ -2200,4 +2202,26 @@ class LanguageIntegrationTest extends LanguageClassesTestCase {
$this->assertSame( 'Bots', $groupName );
}
/**
* @covers Language::getGroupMemberName
*/
public function testGetGroupMemberName() {
$lang = $this->getLang();
$user = new UserIdentityValue( 1, 'user' );
$groupMemberName = $lang->getGroupMemberName( 'bot', $user );
$this->assertSame( 'bot', $groupMemberName );
$lang = $this->getServiceContainer()->getLanguageFactory()->getLanguage( 'qqx' );
$groupMemberName = $lang->getGroupMemberName( 'bot', $user );
$this->assertSame( '(group-bot-member: user)', $groupMemberName );
}
/**
* @covers Language::msg
*/
public function testMsg() {
$lang = TestingAccessWrapper::newFromObject( $this->getLang() );
$this->assertSame( 'December 1', $lang->msg( 'december-date', '1' )->text() );
}
}

View file

@ -2,6 +2,8 @@
namespace Wikimedia\Tests\Message;
use MediaWiki\Message\UserGroupMembershipParam;
use MediaWiki\User\UserIdentityValue;
use Wikimedia\Message\ListType;
use Wikimedia\Message\MessageValue;
use Wikimedia\Message\ParamType;
@ -183,6 +185,18 @@ class MessageValueTest extends \PHPUnit\Framework\TestCase {
$this->assertSame( $mv, $mv2 );
}
public function testUserGroupMemberParams() {
$mv = new MessageValue( 'key' );
$mv2 = $mv->objectParams(
new UserGroupMembershipParam( 'bot', new UserIdentityValue( 1, 'user' ) )
);
$this->assertSame( '<message key="key">' .
'<object>bot:user</object>' .
'</message>',
$mv->dump() );
$this->assertSame( $mv, $mv2 );
}
public function testSizeParams() {
$mv = new MessageValue( 'key' );
$mv2 = $mv->sizeParams( 1, 2 );