jenkins-bot has submitted this change and it was merged.

Change subject: Define external-id datatype
......................................................................


Define external-id datatype

DEPLOY: Make sure we don't start using the new datatype in the repo
until all client wikis also have the new code (or at least I3074ac0f).

Bug: T95682
Change-Id: I00c38a51cd22966778f47fa0f1c296c7eac33f0f
---
M client/includes/WikibaseClient.php
M lib/WikibaseLib.datatypes.php
M lib/config/WikibaseLib.default.php
M lib/i18n/en.json
M lib/i18n/qqq.json
M lib/includes/formatters/DispatchingSnakFormatter.php
M lib/includes/formatters/PropertyValueSnakFormatter.php
A lib/includes/formatters/WikibaseSnakFormatterBuilders.php
M lib/tests/phpunit/formatters/DispatchingSnakFormatterTest.php
M lib/tests/phpunit/formatters/PropertyValueSnakFormatterTest.php
A lib/tests/phpunit/formatters/WikibaseSnakFormatterBuildersTest.php
M repo/WikibaseRepo.datatypes.php
M repo/i18n/en.json
M repo/i18n/qqq.json
M repo/includes/WikibaseRepo.php
M repo/includes/specials/SpecialListDatatypes.php
16 files changed, 439 insertions(+), 15 deletions(-)

Approvals:
  Thiemo Mättig (WMDE): Looks good to me, approved
  jenkins-bot: Verified



diff --git a/client/includes/WikibaseClient.php 
b/client/includes/WikibaseClient.php
index b92ca27..12f5f9c 100644
--- a/client/includes/WikibaseClient.php
+++ b/client/includes/WikibaseClient.php
@@ -60,6 +60,7 @@
 use Wikibase\Lib\PropertyInfoDataTypeLookup;
 use Wikibase\Lib\Store\EntityContentDataCodec;
 use Wikibase\Lib\MediaWikiContentLanguages;
+use Wikibase\Lib\WikibaseSnakFormatterBuilders;
 use Wikibase\Lib\WikibaseValueFormatterBuilders;
 use Wikibase\Lib\Interactors\TermIndexSearchInteractor;
 use Wikibase\NamespaceChecker;
@@ -213,6 +214,48 @@
        }
 
        /**
+        * Returns the default WikibaseSnakFormatterBuilders instance.
+        * @warning This is for use with bootstrap code in 
WikibaseClient.datatypes.php only!
+        * Program logic should use WikibaseClient::getSnakFormatterFactory() 
instead!
+        *
+        * @since 0.5
+        *
+        * @param string $reset Flag: Pass "reset" to reset the default instance
+        *
+        * @return WikibaseSnakFormatterBuilders
+        */
+       public static function getDefaultSnakFormatterBuilders( $reset = 
'noreset' ) {
+               static $builders;
+
+               if ( $builders === null || $reset === 'reset' ) {
+                       $builders = 
self::getDefaultInstance()->newWikibaseSnakFormatterBuilders(
+                               self::getDefaultFormatterBuilders()
+                       );
+               }
+
+               return $builders;
+       }
+
+       /**
+        * Returns a low level factory object for creating formatters for well 
known data types.
+        *
+        * @warning This is for use with getDefaultFormatterBuilders() during 
bootstrap only!
+        * Program logic should use WikibaseClient::getSnakFormatterFactory() 
instead!
+        *
+        * @param WikibaseValueFormatterBuilders $valueFormatterBuilders
+        *
+        * @return WikibaseSnakFormatterBuilders
+        */
+       private function newWikibaseSnakFormatterBuilders( 
WikibaseValueFormatterBuilders $valueFormatterBuilders ) {
+               return new WikibaseSnakFormatterBuilders(
+                       $valueFormatterBuilders,
+                       $this->getStore()->getPropertyInfoStore(),
+                       $this->getPropertyDataTypeLookup(),
+                       $this->getDataTypeFactory()
+               );
+       }
+
+       /**
         * @param SettingsArray $settings
         * @param Language $contentLanguage
         * @param DataTypeDefinitions $dataTypeDefinitions
diff --git a/lib/WikibaseLib.datatypes.php b/lib/WikibaseLib.datatypes.php
index 642bee0..b879d8b 100644
--- a/lib/WikibaseLib.datatypes.php
+++ b/lib/WikibaseLib.datatypes.php
@@ -22,6 +22,7 @@
        'PT:string'            => array( 'value-type' => 'string' ),
        'PT:time'              => array( 'value-type' => 'time' ),
        'PT:url'               => array( 'value-type' => 'string' ),
+       'PT:external-id'       => array( 'value-type' => 'string' ),
        'PT:wikibase-item'     => array( 'value-type' => 'wikibase-entityid' ),
        'PT:wikibase-property' => array( 'value-type' => 'wikibase-entityid' ),
 );
diff --git a/lib/config/WikibaseLib.default.php 
b/lib/config/WikibaseLib.default.php
index 1e2ce32..de19c74 100644
--- a/lib/config/WikibaseLib.default.php
+++ b/lib/config/WikibaseLib.default.php
@@ -68,6 +68,7 @@
                'string',
                'time',
                'url',
+               'external-id',
                'wikibase-item',
                'wikibase-property',
        ),
diff --git a/lib/i18n/en.json b/lib/i18n/en.json
index f869813..1eeff22 100644
--- a/lib/i18n/en.json
+++ b/lib/i18n/en.json
@@ -71,6 +71,7 @@
        "datatypes-type-wikibase-item": "Item",
        "datatypes-type-wikibase-property": "Property",
        "datatypes-type-commonsMedia": "Commons media file",
+       "datatypes-type-external-id": "External identifier",
        "version-wikibase": "Wikibase",
        "wikibase-time-precision-Gannum": "$1 billion years CE",
        "wikibase-time-precision-Mannum": "$1 million years CE",
diff --git a/lib/i18n/qqq.json b/lib/i18n/qqq.json
index b4112ac..6a01306 100644
--- a/lib/i18n/qqq.json
+++ b/lib/i18n/qqq.json
@@ -81,6 +81,7 @@
        "datatypes-type-wikibase-item": "The name of a data type for items in 
Wikibase.\n{{Identical|Item}}",
        "datatypes-type-wikibase-property": "The name of a data type for 
properties in Wikibase.\n{{Identical|Property}}",
        "datatypes-type-commonsMedia": "The name of a data type for media files 
on Wikimedia Commons (proper name, capitalised in English; first letter 
capitalised anyway in this message and 
relatives).\n\n{{related|Datatypes-type}}",
+       "datatypes-type-external-id": "The name of a data type for external 
identifiers in Wikibase.",
        "version-wikibase": "Name of the Wikibase extension collection, used on 
[[Special:Version]]",
        "wikibase-time-precision-Gannum": "Used to present a point in time with 
the precession of 1 billion of years. $1 is the point in time in billion years, 
rounded to billion years.\n{{Related|Wikibase-time-precision}}",
        "wikibase-time-precision-Mannum": "Used to present a point in time with 
the precession of 1 million of years. $1 is the point in time in million years, 
rounded to million years.\n{{Related|Wikibase-time-precision}}",
diff --git a/lib/includes/formatters/DispatchingSnakFormatter.php 
b/lib/includes/formatters/DispatchingSnakFormatter.php
index 081b1a1..fd7467f 100644
--- a/lib/includes/formatters/DispatchingSnakFormatter.php
+++ b/lib/includes/formatters/DispatchingSnakFormatter.php
@@ -39,8 +39,11 @@
        private $formattersBySnakType;
 
        /**
-        * @param string $format The output format generated by this formatter. 
All formatters
-        *  provided to the constructor must produce the same output format.
+        * @param string $format The output format generated by this formatter. 
All SnakFormatters
+        *  provided via $formattersBySnakType and $formattersByDataType must 
be safe for this
+        *  output format. This is checked by comparing the $format with what 
each SnakFormatter
+        *  returns from getFormat(). MIME parameters are ignored for this 
check, so FORMAT_HTML
+        *  is considered compatible with FORMAT_HTML_DIFF, etc.
         * @param PropertyDataTypeLookup $dataTypeLookup
         * @param SnakFormatter[] $formattersBySnakType An associative array 
mapping snak types
         *  to SnakFormatter objects. If no formatter is defined for the a 
given snak type,
@@ -49,7 +52,9 @@
         *  to SnakFormatter objects. If no formatter is defined for the a 
given data type,
         *  the "*" key in this array is checked for a default formatter.
         *
-        * @throws InvalidArgumentException
+        * @throws InvalidArgumentException If any of the given formatters is 
incompatible
+        *         with $format. Formats are assumed to be represented by MIME 
types,
+        *         MIME parameters are ignored.
         */
        public function __construct(
                $format,
@@ -78,7 +83,10 @@
                                throw new InvalidArgumentException( 'formatter 
array must contain instances of SnakFormatter.' );
                        }
 
-                       if ( $formatter->getFormat() !== $format ) {
+                       // Ignore MIME parameters when checking output format. 
We only care that the base format
+                       // is the same, so we can assume that all formatters 
apply the correct escaping and are
+                       // safe to use.
+                       if ( $this->getBaseFormat( $formatter->getFormat() ) 
!== $this->getBaseFormat( $format ) ) {
                                throw new InvalidArgumentException( 'The 
formatter supplied for ' . $type
                                        . ' produces ' . 
$formatter->getFormat() . ', but we expect ' . $format . '.' );
                        }
@@ -86,6 +94,15 @@
        }
 
        /**
+        * @param string $format MIME type
+        *
+        * @return string MIME type with parameters stripped.
+        */
+       private function getBaseFormat( $format ) {
+               return preg_replace( '/ *;.*$/', '', $format );
+       }
+
+       /**
         * @param Snak $snak
         *
         * @throws PropertyDataTypeLookupException
diff --git a/lib/includes/formatters/PropertyValueSnakFormatter.php 
b/lib/includes/formatters/PropertyValueSnakFormatter.php
index d3e5a66..a00d008 100644
--- a/lib/includes/formatters/PropertyValueSnakFormatter.php
+++ b/lib/includes/formatters/PropertyValueSnakFormatter.php
@@ -10,6 +10,7 @@
 use ValueFormatters\Exceptions\MismatchingDataValueTypeException;
 use ValueFormatters\FormatterOptions;
 use ValueFormatters\FormattingException;
+use ValueFormatters\ValueFormatter;
 use Wikibase\DataModel\Services\Lookup\PropertyDataTypeLookup;
 use Wikibase\DataModel\Services\Lookup\PropertyDataTypeLookupException;
 use Wikibase\DataModel\Snak\PropertyValueSnak;
@@ -35,7 +36,7 @@
        private $options;
 
        /**
-        * @var TypedValueFormatter
+        * @var ValueFormatter
         */
        private $valueFormatter;
 
@@ -53,7 +54,7 @@
         * @param string $format The name of this formatter's output format.
         *        Use the FORMAT_XXX constants defined in SnakFormatter.
         * @param FormatterOptions|null $options
-        * @param TypedValueFormatter $valueFormatter
+        * @param ValueFormatter $valueFormatter
         * @param PropertyDataTypeLookup $typeLookup
         * @param DataTypeFactory $dataTypeFactory
         *
@@ -62,7 +63,7 @@
        public function __construct(
                $format,
                FormatterOptions $options = null,
-               TypedValueFormatter $valueFormatter,
+               ValueFormatter $valueFormatter,
                PropertyDataTypeLookup $typeLookup,
                DataTypeFactory $dataTypeFactory
        ) {
@@ -171,7 +172,11 @@
         */
        private function formatValue( DataValue $value, $dataTypeId = null ) {
                if ( !$this->isUnDeserializableValue( $value ) ) {
-                       $text = $this->valueFormatter->formatValue( $value, 
$dataTypeId );
+                       if ( $this->valueFormatter instanceof 
TypedValueFormatter ) {
+                               $text = $this->valueFormatter->formatValue( 
$value, $dataTypeId );
+                       } else {
+                               $text = $this->valueFormatter->format( $value );
+                       }
                } else {
                        $text = '';
                }
diff --git a/lib/includes/formatters/WikibaseSnakFormatterBuilders.php 
b/lib/includes/formatters/WikibaseSnakFormatterBuilders.php
new file mode 100644
index 0000000..7293e2d
--- /dev/null
+++ b/lib/includes/formatters/WikibaseSnakFormatterBuilders.php
@@ -0,0 +1,148 @@
+<?php
+
+namespace Wikibase\Lib;
+
+use DataTypes\DataTypeFactory;
+use InvalidArgumentException;
+use ValueFormatters\FormatterOptions;
+use Wikibase\DataModel\Services\Lookup\PropertyDataTypeLookup;
+use Wikibase\Lib\Formatters\HtmlExternalIdentifierFormatter;
+use Wikibase\Lib\Formatters\WikitextExternalIdentifierFormatter;
+use Wikibase\PropertyInfoStore;
+
+/**
+ * Low level factory for SnakFormatters for well known data types.
+ *
+ * @warning: This is a low level factory for use by boostrap code only!
+ * Program logic should use an instance of OutputFormatValueFormatterFactory
+ * resp. OutputFormatSnakFormatterFactory.
+ *
+ * @since 0.5
+ *
+ * @licence GNU GPL v2+
+ * @author Daniel Kinzler
+ */
+class WikibaseSnakFormatterBuilders {
+
+       /**
+        * @var WikibaseValueFormatterBuilders
+        */
+       private $valueFormatterBuilders;
+
+       /**
+        * @var PropertyInfoStore
+        */
+       private $propertyInfoStore;
+
+       /**
+        * @var PropertyDataTypeLookup
+        */
+       private $dataTypeLookup;
+
+       /**
+        * @var DataTypeFactory
+        */
+       private $dataTypeFactory;
+
+       /**
+        * @param WikibaseValueFormatterBuilders $valueFormatterBuilders
+        */
+       public function __construct(
+               WikibaseValueFormatterBuilders $valueFormatterBuilders,
+               PropertyInfoStore $propertyInfoStore,
+               PropertyDataTypeLookup $dataTypeLookup,
+               DataTypeFactory $dataTypeFactory
+       ) {
+               $this->valueFormatterBuilders = $valueFormatterBuilders;
+               $this->propertyInfoStore = $propertyInfoStore;
+               $this->dataTypeLookup = $dataTypeLookup;
+               $this->dataTypeFactory = $dataTypeFactory;
+       }
+
+       /**
+        * @param string $format One of the SnakFormatter::FORMAT_... constants.
+        *
+        * @throws InvalidArgumentException
+        * @return string Either SnakFormatter::FORMAT_HTML, ...WIKI or 
...PLAIN.
+        */
+       private function getBaseFormat( $format ) {
+               switch ( $format ) {
+                       case SnakFormatter::FORMAT_HTML:
+                       case SnakFormatter::FORMAT_HTML_DIFF:
+                       case SnakFormatter::FORMAT_HTML_WIDGET:
+                               return SnakFormatter::FORMAT_HTML;
+                       case SnakFormatter::FORMAT_WIKI:
+                       case SnakFormatter::FORMAT_PLAIN:
+                               return $format;
+               }
+
+               throw new InvalidArgumentException( 'Unsupported output format: 
' . $format );
+       }
+
+       /**
+        * @param string $format The desired target format, see 
SnakFormatter::FORMAT_XXX
+        *
+        * @throws InvalidArgumentException
+        * @return bool True if $format is one of the 
SnakFormatter::FORMAT_HTML_XXX formats.
+        */
+       private function isHtmlFormat( $format ) {
+               return $this->getBaseFormat( $format ) === 
SnakFormatter::FORMAT_HTML;
+       }
+
+       /**
+        * Wraps the given formatter in an EscapingSnakFormatter if necessary.
+        *
+        * @param string $format The desired target format, see 
SnakFormatter::FORMAT_XXX
+        * @param SnakFormatter $formatter The plain text formatter to wrap.
+        *
+        * @throws InvalidArgumentException
+        * @return SnakFormatter
+        */
+       private function escapeSnakFormatter( $format, SnakFormatter $formatter 
) {
+               switch ( $this->getBaseFormat( $format ) ) {
+                       case SnakFormatter::FORMAT_HTML:
+                               return new EscapingSnakFormatter( $format, 
$formatter, 'htmlspecialchars' );
+                       case SnakFormatter::FORMAT_WIKI:
+                               return new EscapingSnakFormatter( $format, 
$formatter, 'wfEscapeWikiText' );
+                       case SnakFormatter::FORMAT_PLAIN:
+                               return $formatter;
+               }
+
+               throw new InvalidArgumentException( 'Unsupported output format: 
' . $format );
+       }
+
+       /**
+        * @param string $format The desired target format, see 
SnakFormatter::FORMAT_XXX
+        * @param FormatterOptions $options
+        *
+        * @throws InvalidArgumentException
+        * @return SnakFormatter
+        */
+       public function newExternalIdentifierFormatter( $format, 
FormatterOptions $options ) {
+               if ( $format === SnakFormatter::FORMAT_PLAIN ) {
+                       return new PropertyValueSnakFormatter(
+                               $format,
+                               $options,
+                               
$this->valueFormatterBuilders->newStringFormatter( $format, $options ),
+                               $this->dataTypeLookup,
+                               $this->dataTypeFactory
+                       );
+               }
+
+               $urlProvider = new FieldPropertyInfoProvider(
+                       $this->propertyInfoStore,
+                       PropertyInfoStore::KEY_FORMATTER_URL
+               );
+
+               $urlExpander = new PropertyInfoSnakUrlExpander( $urlProvider );
+
+               if ( $format === SnakFormatter::FORMAT_WIKI ) {
+                       return new WikitextExternalIdentifierFormatter( 
$urlExpander );
+               } elseif ( $this->isHtmlFormat( $format ) ) {
+                       return new HtmlExternalIdentifierFormatter( 
$urlExpander );
+               }
+
+               throw new InvalidArgumentException( 'Unsupported output format: 
' . $format );
+       }
+
+}
diff --git a/lib/tests/phpunit/formatters/DispatchingSnakFormatterTest.php 
b/lib/tests/phpunit/formatters/DispatchingSnakFormatterTest.php
index 76c92cc..10de96d 100644
--- a/lib/tests/phpunit/formatters/DispatchingSnakFormatterTest.php
+++ b/lib/tests/phpunit/formatters/DispatchingSnakFormatterTest.php
@@ -62,6 +62,44 @@
        }
 
        /**
+        * @dataProvider constructorProvider
+        */
+       public function testConstructor( $format, array $formattersBySnakType, 
array $formattersByDataType ) {
+               $dataTypeLookup = $this->getDataTypeLookup();
+
+               new DispatchingSnakFormatter(
+                       $format,
+                       $dataTypeLookup,
+                       $formattersBySnakType,
+                       $formattersByDataType
+               );
+
+               // we are just checking that the constructor did not throw an 
exception
+               $this->assertTrue( true );
+       }
+
+       public function constructorProvider() {
+               $formatter = new MessageSnakFormatter(
+                       'novalue',
+                       wfMessage( 'wikibase-snakview-snaktypeselector-novalue' 
),
+                       SnakFormatter::FORMAT_HTML_DIFF
+               );
+
+               return array(
+                       'plain constructor call' => array(
+                               SnakFormatter::FORMAT_HTML_DIFF,
+                               array( 'novalue' => $formatter ),
+                               array( 'string' => $formatter ),
+                       ),
+                       'constructor call with formatters for base format ID' 
=> array(
+                               SnakFormatter::FORMAT_HTML,
+                               array( 'novalue' => $formatter ),
+                               array( 'string' => $formatter ),
+                       ),
+               );
+       }
+
+       /**
         * @dataProvider constructorErrorsProvider
         */
        public function testConstructorErrors( $format, array 
$formattersBySnakType, array $formattersByDataType ) {
diff --git a/lib/tests/phpunit/formatters/PropertyValueSnakFormatterTest.php 
b/lib/tests/phpunit/formatters/PropertyValueSnakFormatterTest.php
index 22675a2..4ac73f0 100644
--- a/lib/tests/phpunit/formatters/PropertyValueSnakFormatterTest.php
+++ b/lib/tests/phpunit/formatters/PropertyValueSnakFormatterTest.php
@@ -7,6 +7,8 @@
 use DataValues\StringValue;
 use DataValues\UnDeserializableValue;
 use ValueFormatters\FormatterOptions;
+use ValueFormatters\StringFormatter;
+use ValueFormatters\ValueFormatter;
 use Wikibase\DataModel\Entity\PropertyId;
 use Wikibase\DataModel\Services\Lookup\PropertyDataTypeLookup;
 use Wikibase\DataModel\Services\Lookup\PropertyDataTypeLookupException;
@@ -91,7 +93,7 @@
         * @dataProvider formatSnakProvider
         */
        public function testFormatSnak(
-               $snak, $dataType, $valueType, $targetFormat, $formatters,
+               $snak, $dataType, $valueType, $targetFormat, ValueFormatter 
$formatter,
                $expected, $expectedException = null
        ) {
                if ( $expectedException !== null ) {
@@ -108,7 +110,7 @@
                $formatter = new PropertyValueSnakFormatter(
                        $targetFormat,
                        $options,
-                       new DispatchingValueFormatter( $formatters ),
+                       $formatter,
                        $typeLookup,
                        $typeFactory
                );
@@ -134,13 +136,15 @@
                        'PT:commonsMedia' => $this->getMockFormatter( 
'PT:commonsMedia' )
                );
 
+               $dispatchingFormatter = new DispatchingValueFormatter( 
$formatters );
+
                return array(
                        'match PT' => array(
                                new PropertyValueSnak( 17, new StringValue( 
'Foo.jpg' ) ),
                                'commonsMedia',
                                'string',
                                SnakFormatter::FORMAT_PLAIN,
-                               $formatters,
+                               $dispatchingFormatter,
                                '/^PT:commonsMedia$/'
                        ),
 
@@ -149,8 +153,17 @@
                                'someStuff',
                                'string',
                                SnakFormatter::FORMAT_WIKI,
-                               $formatters,
+                               $dispatchingFormatter,
                                '/^VT:string$/'
+                       ),
+
+                       'use plain value formatter' => array(
+                               new PropertyValueSnak( 33, new StringValue( 
'something' ) ),
+                               'url',
+                               'string',
+                               SnakFormatter::FORMAT_WIKI,
+                               new StringFormatter(),
+                               '/^something$/'
                        ),
 
                        'UnDeserializableValue, fail' => array(
@@ -160,7 +173,7 @@
                                'globe-coordinate',
                                'globecoordinate',
                                SnakFormatter::FORMAT_HTML,
-                               $formatters,
+                               $dispatchingFormatter,
                                null,
                                
'ValueFormatters\Exceptions\MismatchingDataValueTypeException'
                        ),
@@ -170,7 +183,7 @@
                                'url',
                                'iri', // url expects an iri, but will get a 
string
                                SnakFormatter::FORMAT_WIKI,
-                               $formatters,
+                               $dispatchingFormatter,
                                null,
                                
'ValueFormatters\Exceptions\MismatchingDataValueTypeException'
                        ),
@@ -180,7 +193,7 @@
                                '', // triggers an exception from the mock 
PropertyDataTypeLookup
                                'xxx', // should not be used
                                SnakFormatter::FORMAT_HTML,
-                               $formatters,
+                               $dispatchingFormatter,
                                null,
                                
'Wikibase\DataModel\Services\Lookup\PropertyDataTypeLookupException'
                        ),
diff --git a/lib/tests/phpunit/formatters/WikibaseSnakFormatterBuildersTest.php 
b/lib/tests/phpunit/formatters/WikibaseSnakFormatterBuildersTest.php
new file mode 100644
index 0000000..719bc69
--- /dev/null
+++ b/lib/tests/phpunit/formatters/WikibaseSnakFormatterBuildersTest.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace Wikibase\Lib\Test;
+
+use DataTypes\DataTypeFactory;
+use DataValues\StringValue;
+use ValueFormatters\FormatterOptions;
+use ValueFormatters\StringFormatter;
+use Wikibase\DataModel\Entity\PropertyId;
+use Wikibase\DataModel\Services\Lookup\InMemoryDataTypeLookup;
+use Wikibase\DataModel\Snak\PropertyValueSnak;
+use Wikibase\DataModel\Snak\Snak;
+use Wikibase\Lib\SnakFormatter;
+use Wikibase\Lib\WikibaseSnakFormatterBuilders;
+use Wikibase\PropertyInfoStore;
+use Wikibase\Test\MockPropertyInfoStore;
+
+/**
+ * @covers Wikibase\Lib\WikibaseSnakFormatterBuilders
+ *
+ * @group SnakFormatters
+ * @group DataValueExtensions
+ * @group WikibaseLib
+ * @group Wikibase
+ *
+ * @licence GNU GPL v2+
+ * @author Daniel Kinzler
+ */
+class WikibaseSnakFormatterBuildersTest extends \PHPUnit_Framework_TestCase {
+
+       /**
+        * @return WikibaseSnakFormatterBuilders
+        */
+       private function getWikibaseSnakFormatterBuilders() {
+               $p1 = new PropertyId( 'P1' );
+
+               $valueFormatterBuilders = $this->getMockBuilder( 
'Wikibase\Lib\WikibaseValueFormatterBuilders' )
+                       ->disableOriginalConstructor()
+                       ->getMock();
+
+               $valueFormatterBuilders->expects( $this->any() )
+                       ->method( 'newStringFormatter' )
+                       ->will( $this->returnValue( new StringFormatter() ) );
+
+               $propertyInfoStore = new MockPropertyInfoStore();
+               $propertyInfoStore->setPropertyInfo( $p1, array(
+                       PropertyInfoStore::KEY_DATA_TYPE => 'external-id',
+                       PropertyInfoStore::KEY_FORMATTER_URL => 
'http://acme.com/vocab/$1',
+               ) );
+
+               $dataTypeLookup = new InMemoryDataTypeLookup();
+               $dataTypeLookup->setDataTypeForProperty( $p1, 'external-id' );
+
+               $dataTypeFactory = new DataTypeFactory( array( 'external-id' => 
'string' ) );
+
+               return new WikibaseSnakFormatterBuilders(
+                       $valueFormatterBuilders,
+                       $propertyInfoStore,
+                       $dataTypeLookup,
+                       $dataTypeFactory
+               );
+       }
+
+       public function provideNewExternalIdentifierFormatter() {
+               $p1 = new PropertyId( 'P1' );
+               $snak = new PropertyValueSnak( $p1, new StringValue( 'AB123' ) 
);
+
+               return array(
+                       array( $snak, SnakFormatter::FORMAT_PLAIN, 'AB123' ),
+                       array( $snak, SnakFormatter::FORMAT_WIKI, 
'[http://acme.com/vocab/AB123 AB123]' ),
+                       array( $snak, SnakFormatter::FORMAT_HTML, '<a 
class="wb-external-id" href="http://acme.com/vocab/AB123";>AB123</a>' ),
+               );
+       }
+
+       /**
+        * @dataProvider provideNewExternalIdentifierFormatter
+        */
+       public function testNewExternalIdentifierFormatter( Snak $snak, 
$format, $expected ) {
+               $options = new FormatterOptions();
+               $builders = $this->getWikibaseSnakFormatterBuilders();
+               $formatter = $builders->newExternalIdentifierFormatter( 
$format, $options );
+               $actual = $formatter->formatSnak( $snak );
+               $this->assertSame( $expected, $actual );
+       }
+
+       public function testNewExternalIdentifierFormatter_bad_format() {
+               $options = new FormatterOptions();
+               $builders = $this->getWikibaseSnakFormatterBuilders();
+
+               $this->setExpectedException( 'InvalidArgumentException' );
+               $builders->newExternalIdentifierFormatter( 'unknown', $options 
);
+       }
+
+}
diff --git a/repo/WikibaseRepo.datatypes.php b/repo/WikibaseRepo.datatypes.php
index 40e2318..174bd25 100644
--- a/repo/WikibaseRepo.datatypes.php
+++ b/repo/WikibaseRepo.datatypes.php
@@ -239,6 +239,19 @@
                                return new ObjectUriRdfBuilder();
                        },
                ),
+               'PT:id' => array(
+                       'validator-factory-callback' => function() {
+                               $factory = 
WikibaseRepo::getDefaultValidatorBuilders();
+                               return $factory->buildStringValidators();
+                       },
+                       'parser-factory-callback' => $newStringParser,
+                       // NOTE: for 'formatter-factory-callback', we fall back 
to plain text formatting
+                       'snak-formatter-factory-callback' => function( $format, 
FormatterOptions $options ) {
+                               $factory = 
WikibaseRepo::getDefaultSnakFormatterBuilders();
+                               return 
$factory->newExternalIdentifierFormatter( $format, $options );
+                       },
+                       // TODO: RDF mapping using canonical URI patterns
+               ),
                'VT:wikibase-entityid' => array(
                        'validator-factory-callback' => function() {
                                $factory = 
WikibaseRepo::getDefaultValidatorBuilders();
diff --git a/repo/i18n/en.json b/repo/i18n/en.json
index 5fd946d..2d63dac 100644
--- a/repo/i18n/en.json
+++ b/repo/i18n/en.json
@@ -360,6 +360,8 @@
        "wikibase-listdatatypes-time-body": "Literal data field for a point in 
time. Given as a date and time with some precision and boundaries. The time is 
saved internally in the specified calendar model.\n* time – explicit value for 
point in time, represented as a timestamp resembling ISO 8601, e.g. 
+2013-01-01T00:00:00Z. The year is always signed and padded to have between 4 
and 16 digits.\n* timezone – explicit value as a signed integer. Timezone 
information as an offset from UTC in minutes.\n* before – explicit integer 
value for how many units after the given time it could be. The unit is given by 
the precision.\n* after – explicit integer value for how many units before the 
given time it could be. The unit is given by the precision.\n* precision – 
explicit value encoded in a shortint. The numbers have the following meaning: 0 
- billion years, 1 - hundred million years, ..., 6 - millennium, 7 - century, 8 
- decade, 9 - year, 10 - month, 11 - day, 12 - hour, 13 - minute, 14 - 
second.\n* calendarmodel – explicit value given as a URI. It identifies the 
calendar model of the timestamp.",
        "wikibase-listdatatypes-url-head": "URL",
        "wikibase-listdatatypes-url-body": "Literal data field for a URL. URLs 
are restricted to the protocols also supported for external links in wikitext.",
+       "wikibase-listdatatypes-external-id-head": "External identifier",
+       "wikibase-listdatatypes-external-id-body": "Literal data field for an 
external identifier. External identifiers may automatically be linked to an 
authoritative resource for display.",
        "wikibase-concept-uri": "Concept URI",
        "wikibase-concept-uri-tooltip": "URI that identifies the concept 
described by this item",
        "wikibase-add-badges": "Add badges",
diff --git a/repo/i18n/qqq.json b/repo/i18n/qqq.json
index bc1b925..a6ebc52 100644
--- a/repo/i18n/qqq.json
+++ b/repo/i18n/qqq.json
@@ -389,6 +389,8 @@
        "wikibase-listdatatypes-time-body": "{{Wikibase-datatype-body|Time}}",
        "wikibase-listdatatypes-url-head": 
"{{Wikibase-datatype-head|Url}}\n{{Identical|URL}}",
        "wikibase-listdatatypes-url-body": "{{Wikibase-datatype-body|Url}}",
+       "wikibase-listdatatypes-external-id-head": 
"{{Wikibase-datatype-head|External identifier|external-id}}",
+       "wikibase-listdatatypes-external-id-body": 
"{{Wikibase-datatype-body|External identifier}}",
        "wikibase-concept-uri": "Label for a link in the toolbox on entity 
pages. Target of the link is the one and specific URI for the entity's concept 
(\"canonical concept link\").",
        "wikibase-concept-uri-tooltip": "Text for the canonical concept link in 
toolbox on entity pages.",
        "wikibase-add-badges": "Title for an empty (dummy) badge sign that asks 
the user to add badges to a sitelink.",
diff --git a/repo/includes/WikibaseRepo.php b/repo/includes/WikibaseRepo.php
index 4bca1f1..34f38b1 100644
--- a/repo/includes/WikibaseRepo.php
+++ b/repo/includes/WikibaseRepo.php
@@ -37,6 +37,7 @@
 use Wikibase\DataModel\Services\Statement\StatementGuidValidator;
 use Wikibase\EditEntityFactory;
 use Wikibase\EntityFactory;
+use Wikibase\Lib\WikibaseSnakFormatterBuilders;
 use Wikibase\Repo\ParserOutput\EntityParserOutputGeneratorFactory;
 use Wikibase\InternalSerialization\DeserializerFactory as 
InternalDeserializerFactory;
 use Wikibase\InternalSerialization\SerializerFactory as 
InternalSerializerFactory;
@@ -322,6 +323,48 @@
        }
 
        /**
+        * Returns the default WikibaseSnakFormatterBuilders instance.
+        * @warning This is for use with bootstrap code in 
WikibaseRepo.datatypes.php only!
+        * Program logic should use WikibaseRepo::getSnakFormatterFactory() 
instead!
+        *
+        * @since 0.5
+        *
+        * @param string $reset Flag: Pass "reset" to reset the default instance
+        *
+        * @return WikibaseSnakFormatterBuilders
+        */
+       public static function getDefaultSnakFormatterBuilders( $reset = 
'noreset' ) {
+               static $builders;
+
+               if ( $builders === null || $reset === 'reset' ) {
+                       $builders = 
self::getDefaultInstance()->newWikibaseSnakFormatterBuilders(
+                               self::getDefaultFormatterBuilders()
+                       );
+               }
+
+               return $builders;
+       }
+
+       /**
+        * Returns a low level factory object for creating formatters for well 
known data types.
+        *
+        * @warning This is for use with getDefaultFormatterBuilders() during 
bootstrap only!
+        * Program logic should use WikibaseRepo::getSnakFormatterFactory() 
instead!
+        *
+        * @param WikibaseValueFormatterBuilders $valueFormatterBuilders
+        *
+        * @return WikibaseSnakFormatterBuilders
+        */
+       private function newWikibaseSnakFormatterBuilders( 
WikibaseValueFormatterBuilders $valueFormatterBuilders ) {
+               return new WikibaseSnakFormatterBuilders(
+                       $valueFormatterBuilders,
+                       $this->getStore()->getPropertyInfoStore(),
+                       $this->getPropertyDataTypeLookup(),
+                       $this->getDataTypeFactory()
+               );
+       }
+
+       /**
         * @since 0.4
         *
         * @param SettingsArray $settings
diff --git a/repo/includes/specials/SpecialListDatatypes.php 
b/repo/includes/specials/SpecialListDatatypes.php
index b40abca..86b36b0 100644
--- a/repo/includes/specials/SpecialListDatatypes.php
+++ b/repo/includes/specials/SpecialListDatatypes.php
@@ -56,6 +56,8 @@
                // 'wikibase-listdatatypes-string-body'
                // 'wikibase-listdatatypes-url-head'
                // 'wikibase-listdatatypes-url-body'
+               // 'wikibase-listdatatypes-external-id-head'
+               // 'wikibase-listdatatypes-external-id-body'
 
                foreach ( $this->getDataTypeIds() as $dataTypeId ) {
                        $this->getOutput()->addHTML( 
$this->getHtmlForDataTypeId( $dataTypeId ) );

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

Gerrit-MessageType: merged
Gerrit-Change-Id: I00c38a51cd22966778f47fa0f1c296c7eac33f0f
Gerrit-PatchSet: 16
Gerrit-Project: mediawiki/extensions/Wikibase
Gerrit-Branch: master
Gerrit-Owner: Daniel Kinzler <[email protected]>
Gerrit-Reviewer: Aude <[email protected]>
Gerrit-Reviewer: Daniel Kinzler <[email protected]>
Gerrit-Reviewer: Hashar <[email protected]>
Gerrit-Reviewer: Hoo man <[email protected]>
Gerrit-Reviewer: Jonas Kress (WMDE) <[email protected]>
Gerrit-Reviewer: Thiemo Mättig (WMDE) <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to