Thiemo Mättig (WMDE) has submitted this change and it was merged.

Change subject: (bug 64308) Localize error parameters.
......................................................................


(bug 64308) Localize error parameters.

This patch generalizes the localization of parameters found
in Error objects. This releves the code that generates the
error from worrying about how to format/localize the parameters.
See the change to SiteLinkUniquenessValidator in particular.

Change-Id: I2240d6e0ced47fc2ab1eb5795073c185e6ad336f
---
M lib/includes/Validators/ValidatorErrorLocalizer.php
M lib/includes/formatters/FormattingException.php
A lib/includes/i18n/MessageParameterFormatter.php
M lib/includes/i18n/WikibaseExceptionLocalizer.php
M lib/includes/store/EntityTitleLookup.php
M lib/tests/phpunit/Validators/ValidatorErrorLocalizerTest.php
A lib/tests/phpunit/i18n/MessageParameterFormatterTest.php
M lib/tests/phpunit/i18n/WikibaseExceptionLocalizerTest.php
M repo/Wikibase.hooks.php
M repo/i18n/en.json
M repo/i18n/qqq.json
M repo/includes/LabelDescriptionDuplicateDetector.php
M repo/includes/WikibaseRepo.php
M repo/includes/content/SiteLinkUniquenessValidator.php
M repo/tests/phpunit/includes/LabelDescriptionDuplicateDetectorTest.php
M repo/tests/phpunit/includes/PreSaveChecksTest.php
M repo/tests/phpunit/includes/api/ClaimModificationHelperTest.php
M repo/tests/phpunit/includes/content/SiteLinkUniquenessValidatorTest.php
18 files changed, 504 insertions(+), 116 deletions(-)

Approvals:
  WikidataJenkins: Verified
  Siebrand: Looks good to me, but someone else must approve
  Thiemo Mättig (WMDE): Looks good to me, approved
  jenkins-bot: Checked



diff --git a/lib/includes/Validators/ValidatorErrorLocalizer.php 
b/lib/includes/Validators/ValidatorErrorLocalizer.php
index bd1587f..b278dc8 100644
--- a/lib/includes/Validators/ValidatorErrorLocalizer.php
+++ b/lib/includes/Validators/ValidatorErrorLocalizer.php
@@ -4,6 +4,8 @@
 
 use Message;
 use Status;
+use ValueFormatters\FormattingException;
+use ValueFormatters\ValueFormatter;
 use ValueValidators\Error;
 use ValueValidators\Result;
 
@@ -14,6 +16,20 @@
  * @author Daniel Kinzler
  */
 class ValidatorErrorLocalizer {
+
+       /**
+        * @var ValueFormatter Formatter for generating wikitext for message 
parameters.
+        */
+       protected $paramFormatter;
+
+       /**
+        * @param ValueFormatter $paramFormatter A formatter for formatting 
message parameters.
+        *        MUST return wikitext. This is typically some kind of 
dispatcher. If not provided,
+        *        naive formatting will be used, which will fail on 
non-primitive parameters.
+        */
+       function __construct( ValueFormatter $paramFormatter = null ) {
+               $this->paramFormatter = $paramFormatter;
+       }
 
        /**
         * Returns a Status representing the given validation result.
@@ -53,8 +69,30 @@
                $key = 'wikibase-validator-' . $error->getCode();
                $params = $error->getParameters();
 
-               //TODO: look for non-string in $params and run them through an 
appropriate formatter
+               foreach ( $params as &$param ) {
+                       if ( !is_string( $param ) ) {
+                               $param = $this->paramToString( $param );
+                       }
+               }
 
-               return wfMessage( $key, $params );
+               return wfMessage( $key )->params( $params );
+       }
+
+       /**
+        * @param mixed $param
+        *
+        * @return string wikitext
+        */
+       private function paramToString( $param ) {
+               if ( $this->paramFormatter ) {
+                       try {
+                               return $this->paramFormatter->format( $param );
+                       } catch ( FormattingException $e ) {
+                               // ok, never mind, use naive version below.
+                               wfWarn( __METHOD__ . ': Formatting of message 
parameter fialed: ' . $e->getMessage() );
+                       }
+               }
+
+               return wfEscapeWikiText( strval( $param ) );
        }
 }
\ No newline at end of file
diff --git a/lib/includes/formatters/FormattingException.php 
b/lib/includes/formatters/FormattingException.php
index a1641cb..69c5b7e 100644
--- a/lib/includes/formatters/FormattingException.php
+++ b/lib/includes/formatters/FormattingException.php
@@ -1,13 +1,14 @@
 <?php
 namespace Wikibase\Lib;
-use RuntimeException;
 
 /**
  * FormattingException
  *
+ * @deprecated use \ValueFormatters\FormattingException
+ *
  * @license GPL 2+
  * @author Daniel Kinzler
  */
-class FormattingException extends RuntimeException {
+class FormattingException extends \ValueFormatters\FormattingException {
 
 }
\ No newline at end of file
diff --git a/lib/includes/i18n/MessageParameterFormatter.php 
b/lib/includes/i18n/MessageParameterFormatter.php
new file mode 100644
index 0000000..bdff3df
--- /dev/null
+++ b/lib/includes/i18n/MessageParameterFormatter.php
@@ -0,0 +1,155 @@
+<?php
+
+namespace Wikibase\i18n;
+
+use DataValues\DataValue;
+use Language;
+use SiteStore;
+use ValueFormatters\FormattingException;
+use ValueFormatters\Localizer;
+use ValueFormatters\ValueFormatter;
+use Wikibase\DataModel\Entity\EntityId;
+use Wikibase\DataModel\SiteLink;
+use Wikibase\EntityTitleLookup;
+use Wikibase\Lib\MediaWikiNumberLocalizer;
+
+/**
+ * ValueFormatter for formatting objects that may be encountered in
+ * parameters of ValueValidators\Error objects as wikitext.
+ *
+ * @license GPL 2+
+ * @author Daniel Kinzler
+ */
+class MessageParameterFormatter implements ValueFormatter {
+
+       /**
+        * @var ValueFormatter
+        */
+       private $dataValueFormatter;
+
+       /**
+        * @var EntityTitleLookup
+        */
+       private $entityTitleLookup;
+
+       /**
+        * @var SiteStore
+        */
+       private $sites;
+
+       /**
+        * @var Language
+        */
+       private $language;
+
+       /**
+        * @var Localizer
+        */
+       private $valueLocalizer;
+
+       /**
+        * @param ValueFormatter $dataValueFormatter A formatter for turning 
DataValues into wikitext.
+        * @param EntityTitleLookup $entityTitleLookup
+        * @param SiteStore $sites
+        * @param Language $language
+        */
+       function __construct(
+               ValueFormatter $dataValueFormatter,
+               EntityTitleLookup $entityTitleLookup,
+               SiteStore $sites,
+               Language $language
+       ) {
+               $this->dataValueFormatter = $dataValueFormatter;
+               $this->entityTitleLookup = $entityTitleLookup;
+               $this->sites = $sites;
+               $this->language = $language;
+
+               $this->valueLocalizer = new MediaWikiNumberLocalizer( $language 
);
+       }
+
+       /**
+        * Formats a value.
+        *
+        * @since 0.1
+        *
+        * @param mixed $value The value to format
+        *
+        * @return string The formatted value (as wikitext).
+        * @throws FormattingException
+        */
+       public function format( $value ) {
+               if ( is_int( $value ) || is_float( $value ) ) {
+                       return $this->valueLocalizer->localizeNumber( $value );
+               } elseif ( $value instanceof DataValue ) {
+                       return $this->dataValueFormatter->format( $value );
+               } elseif ( is_object( $value ) ) {
+                       return $this->formatObject( $value );
+               } elseif ( is_array( $value ) ) {
+                       return $this->formatValueList( $value );
+               }
+
+               return wfEscapeWikiText( strval( $value ) );
+       }
+
+       /**
+        * @param array $values
+        *
+        * @return string[]
+        */
+       private function formatValueList( $values ) {
+               $formatted = array();
+
+               foreach ( $values as $key => $value ) {
+                       $formatted[$key] = $this->format( $value );
+               }
+
+               //XXX: commaList should really be in the Localizer interface.
+               return $this->language->commaList( $formatted );
+       }
+
+       /**
+        * @param object $value
+        *
+        * @return string The formatted value (as wikitext).
+        */
+       private function formatObject( $value ) {
+               if ( $value instanceof EntityId ) {
+                       return $this->formatEntityId( $value );
+               } elseif ( $value instanceof SiteLink ) {
+                       return $this->formatSiteLink( $value );
+               }
+
+               // hope we can interpolate, and just fail if we can't
+               return wfEscapeWikiText( strval( $value ) );
+       }
+
+       /**
+        * @param EntityId $entityId
+        *
+        * @return string The formatted ID (as a wikitext link).
+        */
+       private function formatEntityId( EntityId $entityId ) {
+               // @todo: this should use TitleValue + 
MediaWikiPageLinkRenderer!
+               $title = $this->entityTitleLookup->getTitleForId( $entityId );
+
+               $target = $title->getFullText();
+               $text = wfEscapeWikiText( $entityId->getSerialization() );
+
+               return "[[$target|$text]]";
+       }
+
+       /**
+        * @param SiteLink $link
+        *
+        * @return string The formatted link (as a wikitext link).
+        */
+       private function formatSiteLink( SiteLink $link ) {
+               $siteId = $link->getSiteId();
+               $page = $link->getPageName();
+
+               $site = $this->sites->getSite( $link->getSiteId() );
+               $url = $site->getPageUrl( $link->getPageName() );
+
+               return "[$url $siteId:$page]";
+       }
+}
diff --git a/lib/includes/i18n/WikibaseExceptionLocalizer.php 
b/lib/includes/i18n/WikibaseExceptionLocalizer.php
index 412dd42..b55944e 100644
--- a/lib/includes/i18n/WikibaseExceptionLocalizer.php
+++ b/lib/includes/i18n/WikibaseExceptionLocalizer.php
@@ -5,6 +5,7 @@
 use Exception;
 use Message;
 use MessageException;
+use ValueFormatters\ValueFormatter;
 use ValueParsers\ParseException;
 use Wikibase\ChangeOp\ChangeOpValidationException;
 use Wikibase\Validators\ValidatorErrorLocalizer;
@@ -29,8 +30,12 @@
         */
        protected $validatorErrorLocalizer;
 
-       public function __construct() {
-               $this->validatorErrorLocalizer = new ValidatorErrorLocalizer();
+       /**
+        * @param ValueFormatter $paramFormatter A formatter for formatting 
message parameters
+        *        as wikitext. Typically some kind of dispatcher.
+        */
+       public function __construct( ValueFormatter $paramFormatter ) {
+               $this->validatorErrorLocalizer = new ValidatorErrorLocalizer( 
$paramFormatter );
        }
 
        /**
diff --git a/lib/includes/store/EntityTitleLookup.php 
b/lib/includes/store/EntityTitleLookup.php
index bdfd0d3..9991531 100644
--- a/lib/includes/store/EntityTitleLookup.php
+++ b/lib/includes/store/EntityTitleLookup.php
@@ -22,6 +22,8 @@
         * If the entity does not exist, this method will return either null,
         * or a Title object referring to a page that does not exist.
         *
+        * @todo change this to return a TitleValue
+        *
         * @since 0.4
         *
         * @param EntityId $id
diff --git a/lib/tests/phpunit/Validators/ValidatorErrorLocalizerTest.php 
b/lib/tests/phpunit/Validators/ValidatorErrorLocalizerTest.php
index fb0ad32..0b817d6 100644
--- a/lib/tests/phpunit/Validators/ValidatorErrorLocalizerTest.php
+++ b/lib/tests/phpunit/Validators/ValidatorErrorLocalizerTest.php
@@ -3,6 +3,7 @@
 namespace Wikibase\Test\Validators;
 
 use Status;
+use ValueFormatters\ValueFormatter;
 use ValueValidators\Error;
 use ValueValidators\Result;
 use Wikibase\Validators\ValidatorErrorLocalizer;
@@ -20,22 +21,56 @@
  */
 class ValidatorErrorLocalizerTest extends \PHPUnit_Framework_TestCase {
 
+       /**
+        * @return ValueFormatter
+        */
+       private function getMockFormatter() {
+               $mock = $this->getMock( 'ValueFormatters\ValueFormatter' );
+               $mock->expects( $this->any() )
+                       ->method( 'format' )
+                       ->will( $this->returnCallback(
+                               function ( $param ) {
+                                       if ( is_array( $param ) ) {
+                                               $param = implode( '|', $param );
+                                       } else {
+                                               $param = "$param";
+                                       }
+
+                                       return $param;
+                               }
+                       ) );
+
+               return $mock;
+       }
+
        public static function provideGetErrorMessage() {
                return array(
-                       array( Error::newError( 'Bla bla' ) ),
-                       array( Error::newError( 'Bla bla', null, 'test', array( 
'thingy' ) ) ),
+                       'simple' => array(
+                               Error::newError( 'Bla bla' ),
+                               array()
+                       ),
+                       'with params' => array(
+                               Error::newError(
+                                       'Bla bla',
+                                       null,
+                                       'test',
+                                       array( 'thingy', array( 'a', 'b', 'c' ) 
)
+                               ),
+                               array( 'thingy', 'a|b|c' )
+                       ),
                );
        }
 
        /**
         * @dataProvider provideGetErrorMessage()
         */
-       public function testGetErrorMessage( $error ) {
-               $localizer = new ValidatorErrorLocalizer();
+       public function testGetErrorMessage( $error, $params ) {
+               $localizer = new ValidatorErrorLocalizer( 
$this->getMockFormatter() );
                $message = $localizer->getErrorMessage( $error );
 
                //TODO: check that messages for actual error codes exist
                $this->assertInstanceOf( 'Message', $message );
+               $this->assertEquals( $params, $message->getParams() );
        }
 
        public static function provideGetResultStatus() {
@@ -54,7 +89,7 @@
         * @dataProvider provideGetResultStatus()
         */
        public function testGetResultStatus( Result $result ) {
-               $localizer = new ValidatorErrorLocalizer();
+               $localizer = new ValidatorErrorLocalizer( 
$this->getMockFormatter() );
                $status = $localizer->getResultStatus( $result );
 
                $this->assertInstanceOf( 'Status', $status );
diff --git a/lib/tests/phpunit/i18n/MessageParameterFormatterTest.php 
b/lib/tests/phpunit/i18n/MessageParameterFormatterTest.php
new file mode 100644
index 0000000..e783338
--- /dev/null
+++ b/lib/tests/phpunit/i18n/MessageParameterFormatterTest.php
@@ -0,0 +1,114 @@
+<?php
+
+namespace Wikibase\Test;
+use DataValues\DataValue;
+use DataValues\DecimalValue;
+use Language;
+use Site;
+use SiteStore;
+use Title;
+use ValueFormatters\ValueFormatter;
+use Wikibase\DataModel\Entity\ItemId;
+use Wikibase\DataModel\SiteLink;
+use Wikibase\EntityId;
+use Wikibase\EntityTitleLookup;
+use Wikibase\i18n\MessageParameterFormatter;
+
+/**
+ * @property mixed getMockValueFormatter
+ * @covers Wikibase\i18n\MessageParameterFormatter
+ *
+ * @group Wikibase
+ * @group WikibaseLib
+ *
+ * @licence GNU GPL v2+
+ * @author Daniel Kinzler
+ */
+class MessageParameterFormatterTest extends \PHPUnit_Framework_TestCase {
+
+       public function formatProvider() {
+               $decimal = new DecimalValue( '+123.456' );
+               $entityId = new ItemId( 'Q123' );
+               $siteLink = new SiteLink( 'acme', 'Foo' );
+
+               return array(
+                       'string' => array( 'Hello', 'en', 'Hello' ),
+                       'int' => array( 456, 'en', '456' ),
+                       'float en' => array( 123.456, 'en', '123.456' ),
+                       'float de' => array( 123.456, 'de', '123,456' ),
+                       'DecimalValue en' => array( $decimal, 'en', 
'DataValues\DecimalValue:+123.456' ),
+                       'EntityId' => array( $entityId, 'en', '[[Q123|Q123]]' ),
+                       'SiteLink' => array( $siteLink, 'en', 
'[http://acme.com/Foo acme:Foo]' ),
+                       'list of floats' => array( array( 1.2, 0.5 ), 'en', 
'1.2, 0.5' ),
+               );
+       }
+
+       /**
+        * @dataProvider formatProvider
+        */
+       public function testFormat( $param, $lang, $expected ) {
+               $formatter = new MessageParameterFormatter(
+                       $this->getMockValueFormatter(),
+                       $this->getMockTitleLookup(),
+                       $this->getMockSitesTable(),
+                       Language::factory( $lang )
+               );
+
+               $actual = $formatter->format( $param );
+               $this->assertEquals( $expected, $actual );
+       }
+
+       /**
+        * @return ValueFormatter
+        */
+       private function getMockValueFormatter() {
+               $mock = $this->getMock( 'ValueFormatters\ValueFormatter' );
+               $mock->expects( $this->any() )
+                       ->method( 'format' )
+                       ->will( $this->returnCallback(
+                               function ( DataValue $param ) {
+                                       $class = get_class( $param );
+                                       $value = $param->getArrayValue();
+
+                                       return "$class:$value";
+                               }
+                       ) );
+
+               return $mock;
+       }
+
+       /**
+        * @return EntityTitleLookup
+        */
+       private function getMockTitleLookup() {
+               $mock = $this->getMock( 'Wikibase\EntityTitleLookup' );
+               $mock->expects( $this->any() )
+                       ->method( 'getTitleForId' )
+                       ->will( $this->returnCallback(
+                               function ( EntityId $id ) {
+                                       return Title::makeTitle( NS_MAIN, 
$id->getSerialization() );
+                               }
+                       ) );
+
+               return $mock;
+       }
+
+       /**
+        * @return SiteStore
+        */
+       private function getMockSitesTable() {
+               $mock = $this->getMock( 'SiteStore' );
+               $mock->expects( $this->any() )
+                       ->method( 'getSite' )
+                       ->will( $this->returnCallback(
+                               function ( $siteId ) {
+                                       $site = new Site();
+                                       $site->setGlobalId( $siteId );
+                                       $site->setLinkPath( 
"http://$siteId.com/$1"; );
+                                       return $site;
+                               }
+                       ) );
+
+               return $mock;
+       }
+}
diff --git a/lib/tests/phpunit/i18n/WikibaseExceptionLocalizerTest.php 
b/lib/tests/phpunit/i18n/WikibaseExceptionLocalizerTest.php
index 560bf4c..46d455d 100644
--- a/lib/tests/phpunit/i18n/WikibaseExceptionLocalizerTest.php
+++ b/lib/tests/phpunit/i18n/WikibaseExceptionLocalizerTest.php
@@ -3,10 +3,12 @@
 namespace Wikibase\Test;
 use Exception;
 use RuntimeException;
+use ValueFormatters\ValueFormatter;
 use ValueParsers\ParseException;
 use ValueValidators\Error;
 use ValueValidators\Result;
 use Wikibase\ChangeOp\ChangeOpValidationException;
+use Wikibase\DataModel\SiteLink;
 use Wikibase\i18n\WikibaseExceptionLocalizer;
 
 /**
@@ -26,8 +28,8 @@
                        Error::newError( 'Eeek!', null, 'too-long', array( 8 ) 
),
                ) );
                $result2 = Result::newError( array(
-                       Error::newError( 'Eeek!', null, 'too-long', array( 8 ) 
),
-                       Error::newError( 'Foo!', null, 'too-short', array( 8 ) 
),
+                       Error::newError( 'Eeek!', null, 'too-long', array( 
array( 'eekwiki', 'Eek' ) ) ),
+                       Error::newError( 'Foo!', null, 'too-short', array( 
array( 'foowiki', 'Foo' ) ) ),
                ) );
 
                return array(
@@ -35,16 +37,38 @@
                        'ParseException' => array( new ParseException( 'Blarg!' 
), 'wikibase-parse-error', array() ),
 
                        'ChangeOpValidationException(0)' => array( new 
ChangeOpValidationException( $result0 ), 'wikibase-validator-invalid', array() 
),
-                       'ChangeOpValidationException(1)' => array( new 
ChangeOpValidationException( $result1 ), 'wikibase-validator-too-long', array( 
8 ) ),
-                       'ChangeOpValidationException(2)' => array( new 
ChangeOpValidationException( $result2 ), 'wikibase-validator-too-long', array( 
8 ) ),
+                       'ChangeOpValidationException(1)' => array( new 
ChangeOpValidationException( $result1 ), 'wikibase-validator-too-long', array( 
'8' ) ),
+                       'ChangeOpValidationException(2)' => array( new 
ChangeOpValidationException( $result2 ), 'wikibase-validator-too-long', array( 
'eekwiki|Eek' ) ),
                );
+       }
+
+       /**
+        * @return ValueFormatter
+        */
+       private function getMockFormatter() {
+               $mock = $this->getMock( 'ValueFormatters\ValueFormatter' );
+               $mock->expects( $this->any() )
+                       ->method( 'format' )
+                       ->will( $this->returnCallback(
+                               function ( $param ) {
+                                       if ( is_array( $param ) ) {
+                                               $param = implode( '|', $param );
+                                       } else {
+                                               $param = "$param";
+                                       }
+
+                                       return $param;
+                               }
+                       ) );
+
+               return $mock;
        }
 
        /**
         * @dataProvider provideGetExceptionMessage
         */
        public function testGetExceptionMessage( Exception $ex, $expectedKey, 
$expectedParams ) {
-               $localizer = new WikibaseExceptionLocalizer();
+               $localizer = new WikibaseExceptionLocalizer( 
$this->getMockFormatter() );
 
                $this->assertTrue( $localizer->hasExceptionMessage( $ex ) );
 
diff --git a/repo/Wikibase.hooks.php b/repo/Wikibase.hooks.php
index 220490a..ce4b1cd 100644
--- a/repo/Wikibase.hooks.php
+++ b/repo/Wikibase.hooks.php
@@ -1256,9 +1256,7 @@
                //       So, check the item's site links, but don't check 
label/description uniqueness.
                $validators = array(
                        new SiteLinkUniquenessValidator(
-                               $repo->getEntityTitleLookup(),
-                               $repo->getStore()->newSiteLinkCache(),
-                               SiteSQLStore::newInstance()
+                               $repo->getStore()->newSiteLinkCache()
                        )
                );
 
diff --git a/repo/i18n/en.json b/repo/i18n/en.json
index d39f184..420f89e 100644
--- a/repo/i18n/en.json
+++ b/repo/i18n/en.json
@@ -96,9 +96,9 @@
     "wikibase-error-constraint-violation-label": "There {{PLURAL:$1|is a 
constraint violation|are constraint violations}} for the 
{{PLURAL:$1|label|labels}} \"$3\" in {{PLURAL:$1|language code|language codes}} 
\"$2\".",
     "wikibase-error-constraint-violation-description": "There is {{PLURAL:$1|a 
constraint|constraints}} violation for {{PLURAL:$1|description|descriptions}} 
\"$3\" for {{PLURAL:$1|language code|language codes}} \"$2\".",
     "wikibase-error-constraint-violation-aliases": "There is {{PLURAL:$1|a 
constraint|constraints}} violation for {{PLURAL:$1|alias|aliases}} \"$3\" for 
{{PLURAL:$1|language code|language codes}} \"$2\".",
-    "wikibase-validator-sitelink-already-used": "Site link [$1 $2] is already 
included in another item, [[$3]].",
-    "wikibase-validator-label-conflict": "Another property ($3) already has 
label \"$1\" associated with language code $2.",
-    "wikibase-validator-label-with-description-conflict": "Another item ($3) 
already has label \"$1\" and description \"$4\" associated with language code 
$2.",
+    "wikibase-validator-sitelink-conflict": "Site link $1 is already used by 
item $2.",
+    "wikibase-validator-label-conflict": "Property $3 already has label \"$1\" 
associated with language code $2.",
+    "wikibase-validator-label-with-description-conflict": "Item $3 already has 
label \"$1\" associated with language code $2, using the same description 
text.",
     "wikibase-validator-label-no-entityid": "The label must not be a valid 
entity id.",
     "wikibase-itemlink": "$1 $2",
     "wikibase-itemlink-id-wrapper": "($1)",
diff --git a/repo/i18n/qqq.json b/repo/i18n/qqq.json
index 8e8f77d..0d50319 100644
--- a/repo/i18n/qqq.json
+++ b/repo/i18n/qqq.json
@@ -115,7 +115,7 @@
        "wikibase-warning-constraint-violation-length": "A warning message that 
a length constraint is triggered. This will usually come together with a fatal 
error message.\n\nParameters:\n* $1 - the language code",
        "wikibase-error-constraint-violation-label": "Error message that is 
shown when a user tries to save a multilanguage label that has some constraint 
violation, typically a string length violation.\n* $1 is the count of violating 
languages\n* $2 is the violating languages\n* $3 is the violating string on a 
shortened form, but this is usually not very useful as the message is usually 
given in an edit window",
        "wikibase-error-constraint-violation-description": "Error message that 
is shown when a user tries to save a multilanguage description that has some 
constraint violation, typically a string length violation.\n* $1 - the count of 
violating languages\n* $2 - the violating languages\n* $3 - the violating 
string on a shortened form, but this is usually not very useful as the message 
is usually given in an edit 
window","wikibase-error-constraint-violation-aliases": "Error message that is 
shown when a user tries to save an alias that has some constraint violation, 
typically a string length violation.\n* $1 - the count of violating 
languages\n* $2 - the violating languages\n* $3 - the violating string on a 
shortened form, but this is usually not very useful as the message is usually 
given in an edit window",
-       "wikibase-validator-sitelink-already-used": "Error message shown when 
an item can't be saved because it contains a site link already used by another 
item.\n\nParameters:\n* $1 - the URL to the remote client page\n* $2 - the 
title on the remote client site\n* $3 - the item (e.g. Q60) on the repo with 
the conflicting site link",
+       "wikibase-validator-sitelink-conflict": "Error message shown when an 
item contains a site link already used by another item.\n\nParameters:\n* $1 - 
an external wiki link to the sitelink's target\n* $2 - a local wiki link to the 
conflicting item.",
        "wikibase-validator-label-conflict": "Error message shown when a user 
tries to save a property that has a non-unique label.\n* $1 is label text\n* $2 
is the labels language code\n* $3 is the id of the property that already has 
the label",
        "wikibase-validator-label-with-description-conflict": "Error message 
shown when a user tries to save an item that has a non-unique label+description 
pair. Parameters:\n* $1 is label text\n* $2 is the labels language code\n* $3 
is the id of the query that already has the label\n* $4 is description text",
        "wikibase-validator-label-no-entityid": "Error message shown when a 
user tries to save an item that has a valid entity id as label. Parameters:\n* 
$1 is the label.",
diff --git a/repo/includes/LabelDescriptionDuplicateDetector.php 
b/repo/includes/LabelDescriptionDuplicateDetector.php
index 371dad1..e2f7dd8 100644
--- a/repo/includes/LabelDescriptionDuplicateDetector.php
+++ b/repo/includes/LabelDescriptionDuplicateDetector.php
@@ -229,10 +229,9 @@
                                $term->getType(),
                                $errorCode,
                                array(
-                                       $term->getType(),
-                                       $term->getLanguage(),
                                        $term->getText(),
-                                       $term->getEntityId()->getSerialization()
+                                       $term->getLanguage(),
+                                       $term->getEntityId()
                                ) );
                }
 
diff --git a/repo/includes/WikibaseRepo.php b/repo/includes/WikibaseRepo.php
index e03a699..927121a 100644
--- a/repo/includes/WikibaseRepo.php
+++ b/repo/includes/WikibaseRepo.php
@@ -4,7 +4,9 @@
 
 use DataTypes\DataTypeFactory;
 use DataValues\DataValueFactory;
+use SiteSQLStore;
 use ValueFormatters\FormatterOptions;
+use ValueFormatters\ValueFormatter;
 use Wikibase\ChangeOp\ChangeOpFactory;
 use Wikibase\DataModel\Claim\ClaimGuidParser;
 use Wikibase\DataModel\Entity\BasicEntityIdParser;
@@ -13,10 +15,12 @@
 use Wikibase\EntityContentFactory;
 use Wikibase\EntityLookup;
 use Wikibase\i18n\ExceptionLocalizer;
+use Wikibase\i18n\MessageParameterFormatter;
 use Wikibase\i18n\WikibaseExceptionLocalizer;
 use Wikibase\LanguageFallbackChainFactory;
 use Wikibase\Lib\ClaimGuidGenerator;
 use Wikibase\Lib\ClaimGuidValidator;
+use Wikibase\Lib\DispatchingValueFormatter;
 use Wikibase\Lib\EntityIdLinkFormatter;
 use Wikibase\Lib\EntityRetrievingDataTypeLookup;
 use Wikibase\Lib\OutputFormatSnakFormatterFactory;
@@ -426,19 +430,24 @@
        }
 
        /**
-        * @return OutputFormatSnakFormatterFactory
+        * @return WikibaseValueFormatterBuilders
         */
-       protected function newSnakFormatterFactory() {
+       protected function getValueFormatterBuilders() {
                global $wgContLang;
 
-               $valueFormatterBuilders = new WikibaseValueFormatterBuilders(
+               return new WikibaseValueFormatterBuilders(
                        $this->getEntityLookup(),
                        $wgContLang,
                        $this->getEntityTitleLookup()
                );
+       }
 
+       /**
+        * @return OutputFormatSnakFormatterFactory
+        */
+       protected function newSnakFormatterFactory() {
                $builders = new WikibaseSnakFormatterBuilders(
-                       $valueFormatterBuilders,
+                       $this->getValueFormatterBuilders(),
                        $this->getPropertyDataTypeLookup()
                );
 
@@ -464,13 +473,7 @@
         * @return OutputFormatValueFormatterFactory
         */
        protected function newValueFormatterFactory() {
-               global $wgContLang;
-
-               $builders = new WikibaseValueFormatterBuilders(
-                       $this->getEntityLookup(),
-                       $wgContLang,
-                       $this->getEntityTitleLookup()
-               );
+               $builders = $this->getValueFormatterBuilders();
 
                $factory = new OutputFormatValueFormatterFactory( 
$builders->getValueFormatterBuildersForFormats() );
                return $factory;
@@ -481,7 +484,9 @@
         */
        public function getExceptionLocalizer() {
                if ( !$this->exceptionLocalizer ) {
-                       $this->exceptionLocalizer = new 
WikibaseExceptionLocalizer();
+                       $this->exceptionLocalizer = new 
WikibaseExceptionLocalizer(
+                               $this->getMessageParameterFormatter()
+                       );
                }
 
                return $this->exceptionLocalizer;
@@ -509,11 +514,7 @@
                $options = new FormatterOptions();
                $idFormatter = new EntityIdLinkFormatter( $options, 
$this->getEntityContentFactory() );
 
-               $valueFormatterBuilders = new WikibaseValueFormatterBuilders(
-                       $this->getEntityLookup(),
-                       $wgContLang,
-                       $this->getEntityTitleLookup()
-               );
+               $valueFormatterBuilders = $this->getValueFormatterBuilders();
 
                $snakFormatterBuilders = new WikibaseSnakFormatterBuilders(
                        $valueFormatterBuilders,
@@ -601,7 +602,7 @@
         * @return ValidatorErrorLocalizer
         */
        public function getValidatorErrorLocalizer() {
-               return new ValidatorErrorLocalizer();
+               return new ValidatorErrorLocalizer( 
$this->getMessageParameterFormatter() );
        }
 
        /**
@@ -610,4 +611,33 @@
        public function getLabelDescriptionDuplicateDetector() {
                return new LabelDescriptionDuplicateDetector( 
$this->getStore()->getTermIndex() );
        }
+
+       /**
+        * @return SiteSQLStore
+        */
+       protected function getSitesTable() {
+               return SiteSQLStore::newInstance();
+       }
+
+       /**
+        * Returns a ValueFormatter suitable for converting message parameters 
to wikitext.
+        * The formatter is most likely implemented to dispatch to different 
formatters internally,
+        * based on the type of the parameter.
+        *
+        * @return ValueFormatter
+        */
+       protected function getMessageParameterFormatter() {
+               global $wgLang;
+
+               $formatterOptions = new FormatterOptions();
+               $valueFormatteBuilders = $this->getValueFormatterBuilders();
+               $valueFormatters = 
$valueFormatteBuilders->getWikiTextFormatters( $formatterOptions );
+
+               return new MessageParameterFormatter(
+                       new DispatchingValueFormatter( $valueFormatters ),
+                       $this->getEntityTitleLookup(),
+                       $this->getSitesTable(),
+                       $wgLang
+               );
+       }
 }
diff --git a/repo/includes/content/SiteLinkUniquenessValidator.php 
b/repo/includes/content/SiteLinkUniquenessValidator.php
index 80a65e1..684c52d 100644
--- a/repo/includes/content/SiteLinkUniquenessValidator.php
+++ b/repo/includes/content/SiteLinkUniquenessValidator.php
@@ -2,14 +2,11 @@
 
 namespace Wikibase\content;
 
-use Message;
-use SiteStore;
-use Status;
 use ValueValidators\Error;
 use ValueValidators\Result;
 use Wikibase\DataModel\Entity\Entity;
 use Wikibase\DataModel\Entity\ItemId;
-use Wikibase\EntityTitleLookup;
+use Wikibase\DataModel\SiteLink;
 use Wikibase\SiteLinkLookup;
 
 /**
@@ -23,29 +20,15 @@
 class SiteLinkUniquenessValidator implements EntityValidator {
 
        /**
-        * @var SiteStore
-        */
-       protected $siteStore;
-
-       /**
         * @var SiteLinkLookup
         */
-       protected $siteLinkLookup;
+       private $siteLinkLookup;
 
        /**
-        * @var EntityTitleLookup
-        */
-       protected $entityTitleLookup;
-
-       /**
-        * @param EntityTitleLookup $entityTitleLookup
         * @param SiteLinkLookup $siteLinkLookup
-        * @param SiteStore $siteStore
         */
-       function __construct( EntityTitleLookup $entityTitleLookup, 
SiteLinkLookup $siteLinkLookup, SiteStore $siteStore ) {
-               $this->entityTitleLookup = $entityTitleLookup;
+       function __construct( SiteLinkLookup $siteLinkLookup ) {
                $this->siteLinkLookup = $siteLinkLookup;
-               $this->siteStore = $siteStore;
        }
 
        /**
@@ -84,20 +67,14 @@
         */
        protected function getConflictError( array $conflict ) {
                $entityId = ItemId::newFromNumber( $conflict['itemId'] );
-               $conflictingTitle = $this->entityTitleLookup->getTitleForId( 
$entityId );
-
-               $site = $this->siteStore->getSite( $conflict['siteId'] );
-               $pageUrl = $site->getPageUrl( $conflict['sitePage'] );
 
                return Error::newError(
                        'SiteLink conflict',
                        'sitelink',
-                       'sitelink-already-used',
+                       'sitelink-conflict',
                        array(
-                               $pageUrl,
-                               $conflict['sitePage'],
-                               $conflictingTitle->getFullText(),
-                               $conflict['siteId'],
+                               new SiteLink( $conflict['siteId'], 
$conflict['sitePage'] ),
+                               $entityId,
                        )
                );
        }
diff --git 
a/repo/tests/phpunit/includes/LabelDescriptionDuplicateDetectorTest.php 
b/repo/tests/phpunit/includes/LabelDescriptionDuplicateDetectorTest.php
index a9f0130..35b0d3f 100644
--- a/repo/tests/phpunit/includes/LabelDescriptionDuplicateDetectorTest.php
+++ b/repo/tests/phpunit/includes/LabelDescriptionDuplicateDetectorTest.php
@@ -68,10 +68,9 @@
                        'label',
                        'label-conflict',
                        array(
-                               'label',
-                               'en',
                                'item label',
-                               'Q42'
+                               'en',
+                               new ItemId( 'Q42' )
                        )
                );
 
@@ -175,10 +174,9 @@
                        'label',
                        'label-with-description-conflict',
                        array(
-                               'label',
-                               'en',
                                'item label',
-                               'Q42'
+                               'en',
+                               new ItemId( 'Q42' )
                        )
                );
 
diff --git a/repo/tests/phpunit/includes/PreSaveChecksTest.php 
b/repo/tests/phpunit/includes/PreSaveChecksTest.php
index a93f1a3..f5e0cbb 100644
--- a/repo/tests/phpunit/includes/PreSaveChecksTest.php
+++ b/repo/tests/phpunit/includes/PreSaveChecksTest.php
@@ -3,6 +3,7 @@
 namespace Wikibase\Test;
 
 use Status;
+use ValueFormatters\ValueFormatter;
 use ValueValidators\Result;
 use Wikibase\DataModel\Entity\BasicEntityIdParser;
 use Wikibase\DataModel\Entity\Entity;
@@ -137,6 +138,26 @@
        }
 
        /**
+        * @return ValueFormatter
+        */
+       private function getMockFormatter() {
+               $mock = $this->getMock( 'ValueFormatters\ValueFormatter' );
+               $mock->expects( $this->any() )
+                       ->method( 'format' )
+                       ->will( $this->returnCallback(
+                               function ( $param ) {
+                                       if ( is_object( $param ) ) {
+                                               $param = get_class( $param );
+                                       }
+
+                                       return wfEscapeWikiText( strval( $param 
) );
+                               }
+                       ) );
+
+               return $mock;
+       }
+
+       /**
         * @dataProvider providePreSaveChecks
         *
         * @param array $oldData
@@ -152,7 +173,7 @@
                $languages = array( 'en', 'de' );
 
                $validatorFactory = new TermValidatorFactory( $maxLength, 
$languages, $idParser, $dupeDetector );
-               $errorLocalizer = new ValidatorErrorLocalizer();
+               $errorLocalizer = new ValidatorErrorLocalizer( 
$this->getMockFormatter() );
 
                $checks = new PreSaveChecks(
                        $validatorFactory,
diff --git a/repo/tests/phpunit/includes/api/ClaimModificationHelperTest.php 
b/repo/tests/phpunit/includes/api/ClaimModificationHelperTest.php
index b93a6da..e6a100c 100644
--- a/repo/tests/phpunit/includes/api/ClaimModificationHelperTest.php
+++ b/repo/tests/phpunit/includes/api/ClaimModificationHelperTest.php
@@ -4,6 +4,7 @@
 
 use DataValues\StringValue;
 use UsageException;
+use ValueFormatters\ValueFormatter;
 use Wikibase\Api\CreateClaim;
 use ApiMain;
 use Wikibase\Api\ClaimModificationHelper;
@@ -13,7 +14,6 @@
 use Wikibase\DataModel\Snak\PropertyValueSnak;
 use Wikibase\i18n\WikibaseExceptionLocalizer;
 use Wikibase\Repo\WikibaseRepo;
-use Wikibase\Validators\ValidatorErrorLocalizer;
 
 /**
  * @covers Wikibase\Api\ClaimModificationHelper
@@ -82,12 +82,34 @@
                $claimModificationHelper->getClaimFromEntity( 
'q42$D8404CDA-25E4-4334-AF13-A3290BCD9C0N', $entity );
        }
 
+       /**
+        * @return ValueFormatter
+        */
+       private function getMockFormatter() {
+               $mock = $this->getMock( 'ValueFormatters\ValueFormatter' );
+               $mock->expects( $this->any() )
+                       ->method( 'format' )
+                       ->will( $this->returnCallback(
+                               function ( $param ) {
+                                       if ( is_object( $param ) ) {
+                                               $param = get_class( $param );
+                                       } else {
+                                               $param = "$param";
+                                       }
+
+                                       return $param;
+                               }
+                       ) );
+
+               return $mock;
+       }
+
        private function getNewInstance() {
                $api = new ApiMain();
 
                $errorReporter = new ApiErrorReporter(
                        $api,
-                       new WikibaseExceptionLocalizer(),
+                       new WikibaseExceptionLocalizer( 
$this->getMockFormatter() ),
                        $api->getLanguage()
                );
 
diff --git 
a/repo/tests/phpunit/includes/content/SiteLinkUniquenessValidatorTest.php 
b/repo/tests/phpunit/includes/content/SiteLinkUniquenessValidatorTest.php
index aba13c7..ffb05fb 100644
--- a/repo/tests/phpunit/includes/content/SiteLinkUniquenessValidatorTest.php
+++ b/repo/tests/phpunit/includes/content/SiteLinkUniquenessValidatorTest.php
@@ -10,7 +10,6 @@
 use Wikibase\DataModel\Entity\Item;
 use Wikibase\DataModel\Entity\ItemId;
 use Wikibase\DataModel\SiteLink;
-use Wikibase\EntityTitleLookup;
 use Wikibase\SiteLinkLookup;
 
 /**
@@ -54,19 +53,6 @@
        }
 
        /**
-        * @return EntityTitleLookup
-        */
-       private function getMockTitleLookup() {
-               $termIndex = $this->getMock( 'Wikibase\EntityTitleLookup' );
-
-               $termIndex->expects( $this->any() )
-                       ->method( 'getTitleForId' )
-                       ->will( $this->returnCallback( array( $this, 
'getTitleForId' ) ) );
-
-               return $termIndex;
-       }
-
-       /**
         * @return SiteLinkLookup
         */
        private function getMockSiteLinkLookup() {
@@ -75,19 +61,6 @@
                $termIndex->expects( $this->any() )
                        ->method( 'getConflictsForItem' )
                        ->will( $this->returnCallback( array( $this, 
'getConflictsForItem' ) ) );
-
-               return $termIndex;
-       }
-
-       /**
-        * @return SiteLinkLookup
-        */
-       private function getMockSiteStore() {
-               $termIndex = $this->getMock( '\SiteStore' );
-
-               $termIndex->expects( $this->any() )
-                       ->method( 'getSite' )
-                       ->will( $this->returnCallback( array( $this, 'getSite' 
) ) );
 
                return $termIndex;
        }
@@ -108,7 +81,7 @@
                $badEntity->addSiteLink( new SiteLink( 'testwiki', 'DUPE' ) );
 
                return array(
-                       array( $badEntity, 'sitelink-already-used' ),
+                       array( $badEntity, 'sitelink-conflict' ),
                );
        }
 
@@ -118,11 +91,9 @@
         * @param Entity $entity
         */
        public function testValidateEntity( Entity $entity ) {
-               $titleLookup = $this->getMockTitleLookup();
                $siteLinkLookup = $this->getMockSiteLinkLookup();
-               $siteStore = $this->getMockSiteStore();
 
-               $validator = new SiteLinkUniquenessValidator( $titleLookup, 
$siteLinkLookup, $siteStore );
+               $validator = new SiteLinkUniquenessValidator( $siteLinkLookup );
 
                $result = $validator->validateEntity( $entity );
 
@@ -136,11 +107,9 @@
         * @param string $error
         */
        public function testValidateEntity_failure( Entity $entity, $error ) {
-               $titleLookup = $this->getMockTitleLookup();
                $siteLinkLookup = $this->getMockSiteLinkLookup();
-               $siteStore = $this->getMockSiteStore();
 
-               $validator = new SiteLinkUniquenessValidator( $titleLookup, 
$siteLinkLookup, $siteStore );
+               $validator = new SiteLinkUniquenessValidator( $siteLinkLookup );
 
                $result = $validator->validateEntity( $entity );
 

-- 
To view, visit https://gerrit.wikimedia.org/r/128938
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I2240d6e0ced47fc2ab1eb5795073c185e6ad336f
Gerrit-PatchSet: 7
Gerrit-Project: mediawiki/extensions/Wikibase
Gerrit-Branch: master
Gerrit-Owner: Daniel Kinzler <daniel.kinz...@wikimedia.de>
Gerrit-Reviewer: Addshore <addshorew...@gmail.com>
Gerrit-Reviewer: Adrian Lang <adrian.l...@wikimedia.de>
Gerrit-Reviewer: Aude <aude.w...@gmail.com>
Gerrit-Reviewer: Daniel Kinzler <daniel.kinz...@wikimedia.de>
Gerrit-Reviewer: Hoo man <h...@online.de>
Gerrit-Reviewer: Jeroen De Dauw <jeroended...@gmail.com>
Gerrit-Reviewer: Siebrand <siebr...@kitano.nl>
Gerrit-Reviewer: Thiemo Mättig (WMDE) <thiemo.maet...@wikimedia.de>
Gerrit-Reviewer: WikidataJenkins <wikidata-servi...@wikimedia.de>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to