Show "and X more authors" when a file has multiple info templates

Bug: T74082
Change-Id: If53e3e364d07ef35a93b3b24430441d390aaf66e
This commit is contained in:
Gergő Tisza 2014-11-27 07:19:24 +00:00
parent 9c6d50e55a
commit 485944ba9d
9 changed files with 63 additions and 16 deletions

View file

@ -576,6 +576,7 @@ $wgResourceModules += array(
'mmv.ui.truncatableTextField', 'mmv.ui.truncatableTextField',
'oojs', 'oojs',
'jquery.tipsy', 'jquery.tipsy',
'mediawiki.jqueryMsg',
), ),
'messages' => array( 'messages' => array(
@ -583,6 +584,7 @@ $wgResourceModules += array(
'multimediaviewer-credit', 'multimediaviewer-credit',
'multimediaviewer-credit-fallback', 'multimediaviewer-credit-fallback',
'multimediaviewer-multiple-authors',
'multimediaviewer-userpage-link', 'multimediaviewer-userpage-link',

View file

@ -15,6 +15,7 @@
"multimediaviewer-userpage-link": "{{GENDER:$2|Uploaded}} by $1", "multimediaviewer-userpage-link": "{{GENDER:$2|Uploaded}} by $1",
"multimediaviewer-credit": "$1 - $2", "multimediaviewer-credit": "$1 - $2",
"multimediaviewer-credit-fallback": "View author information", "multimediaviewer-credit-fallback": "View author information",
"multimediaviewer-multiple-authors": "and {{PLURAL:$1|one more author|$1 more authors}}",
"multimediaviewer-metadata-error": "Error: Could not load image data. $1", "multimediaviewer-metadata-error": "Error: Could not load image data. $1",
"multimediaviewer-thumbnail-error": "Error: Could not load thumbnail data. $1", "multimediaviewer-thumbnail-error": "Error: Could not load thumbnail data. $1",
"multimediaviewer-license-cc-by-1.0": "CC BY 1.0", "multimediaviewer-license-cc-by-1.0": "CC BY 1.0",

View file

@ -21,6 +21,7 @@
"multimediaviewer-userpage-link": "Link to the user page for the uploader of the image.\n\nUsed in JavaScript code.\n\nParameters:\n* $1 - the username of the uploader\n* $2 - their gender", "multimediaviewer-userpage-link": "Link to the user page for the uploader of the image.\n\nUsed in JavaScript code.\n\nParameters:\n* $1 - the username of the uploader\n* $2 - their gender",
"multimediaviewer-credit": "Credit line for images. Parameters:\n* $1 - HTML describing the author\n* $2 - HTML describing the source\n\nNeither parameters are usernames, so GENDER is useless. Both come directly from the API, the extended metadata imageinfo prop in particular.\n\nThey will usually be derived from the HTML output from wikitext on a file description page - however, no complicated HTML, only links, will be allowed.\n\nSee also {{msg-mw|multimediaviewer-credit-fallback}}", "multimediaviewer-credit": "Credit line for images. Parameters:\n* $1 - HTML describing the author\n* $2 - HTML describing the source\n\nNeither parameters are usernames, so GENDER is useless. Both come directly from the API, the extended metadata imageinfo prop in particular.\n\nThey will usually be derived from the HTML output from wikitext on a file description page - however, no complicated HTML, only links, will be allowed.\n\nSee also {{msg-mw|multimediaviewer-credit-fallback}}",
"multimediaviewer-credit-fallback": "Text shown in place of the credit line ({{msg-mw|multimediaviewer-credit}}) when neither author nor source information is available.", "multimediaviewer-credit-fallback": "Text shown in place of the credit line ({{msg-mw|multimediaviewer-credit}}) when neither author nor source information is available.",
"multimediaviewer-multiple-authors": "Text shown after the author name when there are multiple authors. The text will link to the file description page.\n* $1 - number of additional authors.",
"multimediaviewer-metadata-error": "Text shown when the information on the metadata panel could not be loaded.\n\nParameters:\n* $1 - the error message (not localized)\nSee also:\n* {{msg-mw|Multimediaviewer-thumbnail-error}}", "multimediaviewer-metadata-error": "Text shown when the information on the metadata panel could not be loaded.\n\nParameters:\n* $1 - the error message (not localized)\nSee also:\n* {{msg-mw|Multimediaviewer-thumbnail-error}}",
"multimediaviewer-thumbnail-error": "Text shown when the image could not be loaded. Parameters:\n* $1 - the error message (not localized)\nSee also:\n* {{msg-mw|Multimediaviewer-metadata-error}}", "multimediaviewer-thumbnail-error": "Text shown when the image could not be loaded. Parameters:\n* $1 - the error message (not localized)\nSee also:\n* {{msg-mw|Multimediaviewer-metadata-error}}",
"multimediaviewer-license-cc-by-1.0": "Very short label for the Creative Commons Attribution license, version 1.0, used in a link to the file information page that has more licensing information.\n{{Identical|CC BY}}", "multimediaviewer-license-cc-by-1.0": "Very short label for the Creative Commons Attribution license, version 1.0, used in a link to the file information page that has more licensing information.\n{{Identical|CC BY}}",

View file

@ -37,6 +37,7 @@
* @param {string} description * @param {string} description
* @param {string} source * @param {string} source
* @param {string} author * @param {string} author
* @param {number} authorCount
* @param {mw.mmv.model.License} license * @param {mw.mmv.model.License} license
* @param {string} permission * @param {string} permission
* @param {number} latitude * @param {number} latitude
@ -58,6 +59,7 @@
description, description,
source, source,
author, author,
authorCount,
license, license,
permission, permission,
latitude, latitude,
@ -108,6 +110,10 @@
/** @property {string} author The author of the image - unsafe HTML sometimes goes here */ /** @property {string} author The author of the image - unsafe HTML sometimes goes here */
this.author = author; this.author = author;
/** @property {number} authorCount The number of different authors of the image. This is guessed by the
* number of templates with author fields, so might be less than the number of actual authors. */
this.authorCount = authorCount;
/** @property {mw.mmv.model.License} license The license under which the image is distributed */ /** @property {mw.mmv.model.License} license The license under which the image is distributed */
this.license = license; this.license = license;
@ -139,7 +145,7 @@
*/ */
Image.newFromImageInfo = function ( title, imageInfo ) { Image.newFromImageInfo = function ( title, imageInfo ) {
var name, uploadDateTime, creationDateTime, imageData, var name, uploadDateTime, creationDateTime, imageData,
description, source, author, license, permission, description, source, author, authorCount, license, permission,
latitude, longitude, latitude, longitude,
innerInfo = imageInfo.imageinfo[0], innerInfo = imageInfo.imageinfo[0],
extmeta = innerInfo.extmetadata; extmeta = innerInfo.extmetadata;
@ -152,6 +158,8 @@
description = this.parseExtmeta( extmeta.ImageDescription, 'string' ); description = this.parseExtmeta( extmeta.ImageDescription, 'string' );
source = this.parseExtmeta( extmeta.Credit, 'string' ); source = this.parseExtmeta( extmeta.Credit, 'string' );
author = this.parseExtmeta( extmeta.Artist, 'string' ); author = this.parseExtmeta( extmeta.Artist, 'string' );
authorCount = this.parseExtmeta( extmeta.AuthorCount, 'integer' );
license = this.newLicenseFromImageInfo( extmeta ); license = this.newLicenseFromImageInfo( extmeta );
permission = this.parseExtmeta( extmeta.Permission, 'string' ); permission = this.parseExtmeta( extmeta.Permission, 'string' );
@ -180,6 +188,7 @@
description, description,
source, source,
author, author,
authorCount,
license, license,
permission, permission,
latitude, latitude,
@ -234,6 +243,8 @@
return value.toString().replace( /<.*?>/g, '' ); return value.toString().replace( /<.*?>/g, '' );
} else if ( type === 'string' ) { } else if ( type === 'string' ) {
return value.toString(); return value.toString();
} else if ( type === 'integer' ) {
return parseInt( value, 10 );
} else if ( type === 'float' ) { } else if ( type === 'float' ) {
return parseFloat( value ); return parseFloat( value );
} else if ( type === 'boolean' ) { } else if ( type === 'boolean' ) {

View file

@ -67,6 +67,7 @@
'LicenseUrl', 'LicenseUrl',
'Credit', 'Credit',
'Artist', 'Artist',
'AuthorCount',
'GPSLatitude', 'GPSLatitude',
'GPSLongitude', 'GPSLongitude',
'Permission', 'Permission',

View file

@ -456,20 +456,21 @@
* Set source and author. * Set source and author.
* @param {string} source With unsafe HTML * @param {string} source With unsafe HTML
* @param {string} author With unsafe HTML * @param {string} author With unsafe HTML
* @param {number} authorCount
* @param {string} filepageUrl URL of the file page (used when other data is not available) * @param {string} filepageUrl URL of the file page (used when other data is not available)
*/ */
MPP.setCredit = function ( source, author, filepageUrl ) { MPP.setCredit = function ( source, author, authorCount, filepageUrl ) {
// sanitization will be done by TruncatableTextField.set() // sanitization will be done by TruncatableTextField.set()
if ( author && source ) { if ( author && source ) {
this.creditField.set( this.creditField.set(
mw.message( mw.message(
'multimediaviewer-credit', 'multimediaviewer-credit',
this.wrapAuthor( author ), this.wrapAuthor( author, authorCount, filepageUrl ),
this.wrapSource( source ) this.wrapSource( source )
).plain() ).plain()
); );
} else if ( author ) { } else if ( author ) {
this.creditField.set( this.wrapAuthor( author ) ); this.creditField.set( this.wrapAuthor( author, authorCount, filepageUrl ) );
} else if ( source ) { } else if ( source ) {
this.creditField.set( this.wrapSource( source ) ); this.creditField.set( this.wrapSource( source ) );
} else { } else {
@ -499,13 +500,27 @@
/** /**
* Wraps an author string with MediaViewer styles * Wraps an author string with MediaViewer styles
* @param {string} author Warning - unsafe HTML sometimes goes here * @param {string} author Warning - unsafe HTML sometimes goes here
* @param {number} authorCount
* @param {string} filepageUrl URL of the file page (used when some author data is not available)
* @return {string} unsafe HTML * @return {string} unsafe HTML
*/ */
MPP.wrapAuthor = function ( author ) { MPP.wrapAuthor = function ( author, authorCount, filepageUrl ) {
return $( '<span>' ) var $wrapper = $( '<span>' );
$wrapper
.addClass( 'mw-mmv-author' ) .addClass( 'mw-mmv-author' )
.append( $.parseHTML( author ) ) .append( $.parseHTML( author ) );
.get( 0 ).outerHTML;
if ( authorCount > 1 ) {
$wrapper.append( ' ',
$( '<a>' )
.addClass( 'mw-mmv-more-authors' )
.text( mw.message( 'multimediaviewer-multiple-authors', authorCount - 1 ).text() )
.attr( 'href', filepageUrl )
);
}
return $wrapper.get( 0 ).outerHTML;
}; };
/** /**
@ -655,7 +670,7 @@
// these handle text truncation and should be called when everything that can push text down // these handle text truncation and should be called when everything that can push text down
// (e.g. floated buttons) has already been laid out // (e.g. floated buttons) has already been laid out
this.setTitle( image, imageData ); this.setTitle( image, imageData );
this.setCredit( imageData.source, imageData.author, imageData.descriptionUrl ); this.setCredit( imageData.source, imageData.author, imageData.authorCount, imageData.descriptionUrl );
if ( imageData.permission ) { if ( imageData.permission ) {
this.setPermission( imageData.permission ); this.setPermission( imageData.permission );

View file

@ -6,7 +6,7 @@
options.licenseInternalName, options.licenseLongName, options.licenseUrl ) : undefined, options.licenseInternalName, options.licenseLongName, options.licenseUrl ) : undefined,
imageInfo = new mw.mmv.model.Image( options.title, options.title.getNameText(), undefined, imageInfo = new mw.mmv.model.Image( options.title, options.title.getNameText(), undefined,
undefined, undefined, undefined, options.imgUrl, options.filePageUrl, 'repo', undefined, undefined, undefined, undefined, options.imgUrl, options.filePageUrl, 'repo', undefined,
undefined, undefined, undefined, options.source, options.author, license ), undefined, undefined, undefined, options.source, options.author, options.authorCount, license ),
repoInfo = { displayName: options.siteName, getSiteLink: repoInfo = { displayName: options.siteName, getSiteLink:
function () { return options.siteUrl; } }; function () { return options.siteUrl; } };

View file

@ -18,7 +18,7 @@
( function( mw ) { ( function( mw ) {
QUnit.module( 'mmv.model.Image', QUnit.newMwEnvironment() ); QUnit.module( 'mmv.model.Image', QUnit.newMwEnvironment() );
QUnit.test( 'Image model constructor sanity check', 20, function ( assert ) { QUnit.test( 'Image model constructor sanity check', 21, function ( assert ) {
var var
title = mw.Title.newFromText( 'File:Foobar.jpg' ), title = mw.Title.newFromText( 'File:Foobar.jpg' ),
name = 'Foo bar', name = 'Foo bar',
@ -35,6 +35,7 @@
description = 'This is a test file.', description = 'This is a test file.',
source = 'WMF', source = 'WMF',
author = 'Ryan Kaldari', author = 'Ryan Kaldari',
authorCount = 1,
permission = 'only use for good, not evil', permission = 'only use for good, not evil',
license = new mw.mmv.model.License( 'cc0' ), license = new mw.mmv.model.License( 'cc0' ),
latitude = 39.12381283, latitude = 39.12381283,
@ -42,7 +43,7 @@
imageData = new mw.mmv.model.Image( imageData = new mw.mmv.model.Image(
title, name, size, width, height, mime, url, title, name, size, width, height, mime, url,
descurl, repo, user, datetime, origdatetime, descurl, repo, user, datetime, origdatetime,
description, source, author, license, permission, description, source, author, authorCount, license, permission,
latitude, longitude ); latitude, longitude );
assert.strictEqual( imageData.title, title, 'Title is set correctly' ); assert.strictEqual( imageData.title, title, 'Title is set correctly' );
@ -60,6 +61,7 @@
assert.strictEqual( imageData.description, description, 'Description is set correctly' ); assert.strictEqual( imageData.description, description, 'Description is set correctly' );
assert.strictEqual( imageData.source, source, 'Source is set correctly' ); assert.strictEqual( imageData.source, source, 'Source is set correctly' );
assert.strictEqual( imageData.author, author, 'Author is set correctly' ); assert.strictEqual( imageData.author, author, 'Author is set correctly' );
assert.strictEqual( imageData.authorCount, authorCount, 'Author is set correctly' );
assert.strictEqual( imageData.license, license, 'License is set correctly' ); assert.strictEqual( imageData.license, license, 'License is set correctly' );
assert.strictEqual( imageData.permission, permission, 'Permission is set correctly' ); assert.strictEqual( imageData.permission, permission, 'Permission is set correctly' );
assert.strictEqual( imageData.latitude, latitude, 'Latitude is set correctly' ); assert.strictEqual( imageData.latitude, latitude, 'Latitude is set correctly' );
@ -73,13 +75,13 @@
mw.Title.newFromText( 'File:Foobar.pdf.jpg' ), 'Foo bar', mw.Title.newFromText( 'File:Foobar.pdf.jpg' ), 'Foo bar',
10, 10, 10, 'image/jpeg', 'http://example.org', 'http://example.com', 10, 10, 10, 'image/jpeg', 'http://example.org', 'http://example.com',
'example', 'tester', '2013-11-10', '2013-11-09', 'Blah blah blah', 'example', 'tester', '2013-11-10', '2013-11-09', 'Blah blah blah',
'A person', 'Another person', 'CC-BY-SA-3.0', 'Permitted' 'A person', 'Another person', 1, 'CC-BY-SA-3.0', 'Permitted'
), ),
secondImageData = new mw.mmv.model.Image( secondImageData = new mw.mmv.model.Image(
mw.Title.newFromText( 'File:Foobar.pdf.jpg' ), 'Foo bar', mw.Title.newFromText( 'File:Foobar.pdf.jpg' ), 'Foo bar',
10, 10, 10, 'image/jpeg', 'http://example.org', 'http://example.com', 10, 10, 10, 'image/jpeg', 'http://example.org', 'http://example.com',
'example', 'tester', '2013-11-10', '2013-11-09', 'Blah blah blah', 'example', 'tester', '2013-11-10', '2013-11-09', 'Blah blah blah',
'A person', 'Another person', 'CC-BY-SA-3.0', 'Permitted', 'A person', 'Another person', 1, 'CC-BY-SA-3.0', 'Permitted',
'39.91820938', '78.09812938' '39.91820938', '78.09812938'
); );
@ -87,10 +89,13 @@
assert.strictEqual( secondImageData.hasCoords(), true, 'Coordinates present means hasCoords returns true.' ); assert.strictEqual( secondImageData.hasCoords(), true, 'Coordinates present means hasCoords returns true.' );
} ); } );
QUnit.test( 'parseExtmeta()', 11, function ( assert ) { QUnit.test( 'parseExtmeta()', 14, function ( assert ) {
var Image = mw.mmv.model.Image, var Image = mw.mmv.model.Image,
stringData = { value: 'foo' }, stringData = { value: 'foo' },
plaintextData = { value: 'fo<b>o</b>' }, plaintextData = { value: 'fo<b>o</b>' },
integerData = { value: 3 },
integerStringData = { value: '3' },
zeroPrefixedIntegerStringData = { value: '03' },
floatData = { value: 1.23 }, floatData = { value: 1.23 },
floatStringData = { value: '1.23' }, floatStringData = { value: '1.23' },
booleanData = { value: 'yes' }, booleanData = { value: 'yes' },
@ -112,6 +117,12 @@
'Extmeta boolean string parsed correctly.' ); 'Extmeta boolean string parsed correctly.' );
assert.strictEqual( Image.parseExtmeta( wrongBooleanData, 'boolean' ), undefined, assert.strictEqual( Image.parseExtmeta( wrongBooleanData, 'boolean' ), undefined,
'Extmeta boolean string with error ignored.' ); 'Extmeta boolean string with error ignored.' );
assert.strictEqual( Image.parseExtmeta( integerData, 'integer' ), 3,
'Extmeta integer parsed correctly.' );
assert.strictEqual( Image.parseExtmeta( integerStringData, 'integer' ), 3,
'Extmeta integer string parsed correctly.' );
assert.strictEqual( Image.parseExtmeta( zeroPrefixedIntegerStringData, 'integer' ), 3,
'Extmeta zero-prefixed integer string parsed correctly.' );
assert.deepEqual( Image.parseExtmeta( listDataEmpty, 'list' ), [], assert.deepEqual( Image.parseExtmeta( listDataEmpty, 'list' ), [],
'Extmeta empty list parsed correctly.' ); 'Extmeta empty list parsed correctly.' );
assert.deepEqual( Image.parseExtmeta( listDataSingle, 'list' ), ['foo'], assert.deepEqual( Image.parseExtmeta( listDataSingle, 'list' ), ['foo'],

View file

@ -25,7 +25,7 @@
assert.ok( imageInfoProvider ); assert.ok( imageInfoProvider );
} ); } );
QUnit.asyncTest( 'ImageInfo get test', 25, function ( assert ) { QUnit.asyncTest( 'ImageInfo get test', 26, function ( assert ) {
var apiCallCount = 0, var apiCallCount = 0,
api = { get: function() { api = { get: function() {
apiCallCount++; apiCallCount++;
@ -101,6 +101,10 @@
value: 'Wikimeda', value: 'Wikimeda',
source: 'commons-desc-page' source: 'commons-desc-page'
}, },
AuthorCount: {
value: '2',
source: 'commons-desc-page'
},
Permission: { Permission: {
value: 'Do not use. Ever.', value: 'Do not use. Ever.',
source: 'commons-desc-page' source: 'commons-desc-page'
@ -142,6 +146,7 @@
assert.strictEqual( image.description, 'Wikis stuff', 'description is set correctly' ); assert.strictEqual( image.description, 'Wikis stuff', 'description is set correctly' );
assert.strictEqual( image.source, 'Wikipedia', 'source is set correctly' ); assert.strictEqual( image.source, 'Wikipedia', 'source is set correctly' );
assert.strictEqual( image.author, 'Wikimeda', 'author is set correctly' ); assert.strictEqual( image.author, 'Wikimeda', 'author is set correctly' );
assert.strictEqual( image.authorCount, 2, 'author count is set correctly' );
assert.strictEqual( image.license.shortName, 'CC0', 'license short name is set correctly' ); assert.strictEqual( image.license.shortName, 'CC0', 'license short name is set correctly' );
assert.strictEqual( image.license.internalName, 'cc0', 'license internal name is set correctly' ); assert.strictEqual( image.license.internalName, 'cc0', 'license internal name is set correctly' );
assert.strictEqual( image.license.longName, 'Creative Commons Public Domain Dedication', 'license long name is set correctly' ); assert.strictEqual( image.license.longName, 'Creative Commons Public Domain Dedication', 'license long name is set correctly' );