From 485944ba9d1a347777b4d0abbbd55b403c58953b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Tisza?= Date: Thu, 27 Nov 2014 07:19:24 +0000 Subject: [PATCH] Show "and X more authors" when a file has multiple info templates Bug: T74082 Change-Id: If53e3e364d07ef35a93b3b24430441d390aaf66e --- MultimediaViewer.php | 2 ++ i18n/en.json | 1 + i18n/qqq.json | 1 + resources/mmv/model/mmv.model.Image.js | 13 +++++++- .../mmv/provider/mmv.provider.ImageInfo.js | 1 + resources/mmv/ui/mmv.ui.metadataPanel.js | 31 ++++++++++++++----- .../qunit/mmv/mmv.EmbedFileFormatter.test.js | 2 +- tests/qunit/mmv/model/mmv.model.Image.test.js | 21 ++++++++++--- .../provider/mmv.provider.ImageInfo.test.js | 7 ++++- 9 files changed, 63 insertions(+), 16 deletions(-) diff --git a/MultimediaViewer.php b/MultimediaViewer.php index 2ca3ad073..f69e4aebb 100644 --- a/MultimediaViewer.php +++ b/MultimediaViewer.php @@ -576,6 +576,7 @@ $wgResourceModules += array( 'mmv.ui.truncatableTextField', 'oojs', 'jquery.tipsy', + 'mediawiki.jqueryMsg', ), 'messages' => array( @@ -583,6 +584,7 @@ $wgResourceModules += array( 'multimediaviewer-credit', 'multimediaviewer-credit-fallback', + 'multimediaviewer-multiple-authors', 'multimediaviewer-userpage-link', diff --git a/i18n/en.json b/i18n/en.json index 4df2ba8f6..74d3c4ee3 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -15,6 +15,7 @@ "multimediaviewer-userpage-link": "{{GENDER:$2|Uploaded}} by $1", "multimediaviewer-credit": "$1 - $2", "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-thumbnail-error": "Error: Could not load thumbnail data. $1", "multimediaviewer-license-cc-by-1.0": "CC BY 1.0", diff --git a/i18n/qqq.json b/i18n/qqq.json index fbd520071..1c14b7f96 100644 --- a/i18n/qqq.json +++ b/i18n/qqq.json @@ -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-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-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-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}}", diff --git a/resources/mmv/model/mmv.model.Image.js b/resources/mmv/model/mmv.model.Image.js index 304702c83..59382ed92 100644 --- a/resources/mmv/model/mmv.model.Image.js +++ b/resources/mmv/model/mmv.model.Image.js @@ -37,6 +37,7 @@ * @param {string} description * @param {string} source * @param {string} author + * @param {number} authorCount * @param {mw.mmv.model.License} license * @param {string} permission * @param {number} latitude @@ -58,6 +59,7 @@ description, source, author, + authorCount, license, permission, latitude, @@ -108,6 +110,10 @@ /** @property {string} author The author of the image - unsafe HTML sometimes goes here */ 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 */ this.license = license; @@ -139,7 +145,7 @@ */ Image.newFromImageInfo = function ( title, imageInfo ) { var name, uploadDateTime, creationDateTime, imageData, - description, source, author, license, permission, + description, source, author, authorCount, license, permission, latitude, longitude, innerInfo = imageInfo.imageinfo[0], extmeta = innerInfo.extmetadata; @@ -152,6 +158,8 @@ description = this.parseExtmeta( extmeta.ImageDescription, 'string' ); source = this.parseExtmeta( extmeta.Credit, 'string' ); author = this.parseExtmeta( extmeta.Artist, 'string' ); + authorCount = this.parseExtmeta( extmeta.AuthorCount, 'integer' ); + license = this.newLicenseFromImageInfo( extmeta ); permission = this.parseExtmeta( extmeta.Permission, 'string' ); @@ -180,6 +188,7 @@ description, source, author, + authorCount, license, permission, latitude, @@ -234,6 +243,8 @@ return value.toString().replace( /<.*?>/g, '' ); } else if ( type === 'string' ) { return value.toString(); + } else if ( type === 'integer' ) { + return parseInt( value, 10 ); } else if ( type === 'float' ) { return parseFloat( value ); } else if ( type === 'boolean' ) { diff --git a/resources/mmv/provider/mmv.provider.ImageInfo.js b/resources/mmv/provider/mmv.provider.ImageInfo.js index fc05b2191..44f6a5866 100644 --- a/resources/mmv/provider/mmv.provider.ImageInfo.js +++ b/resources/mmv/provider/mmv.provider.ImageInfo.js @@ -67,6 +67,7 @@ 'LicenseUrl', 'Credit', 'Artist', + 'AuthorCount', 'GPSLatitude', 'GPSLongitude', 'Permission', diff --git a/resources/mmv/ui/mmv.ui.metadataPanel.js b/resources/mmv/ui/mmv.ui.metadataPanel.js index 5c3bc34eb..be6b403a3 100644 --- a/resources/mmv/ui/mmv.ui.metadataPanel.js +++ b/resources/mmv/ui/mmv.ui.metadataPanel.js @@ -456,20 +456,21 @@ * Set source and author. * @param {string} source 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) */ - MPP.setCredit = function ( source, author, filepageUrl ) { + MPP.setCredit = function ( source, author, authorCount, filepageUrl ) { // sanitization will be done by TruncatableTextField.set() if ( author && source ) { this.creditField.set( mw.message( 'multimediaviewer-credit', - this.wrapAuthor( author ), + this.wrapAuthor( author, authorCount, filepageUrl ), this.wrapSource( source ) ).plain() ); } else if ( author ) { - this.creditField.set( this.wrapAuthor( author ) ); + this.creditField.set( this.wrapAuthor( author, authorCount, filepageUrl ) ); } else if ( source ) { this.creditField.set( this.wrapSource( source ) ); } else { @@ -499,13 +500,27 @@ /** * Wraps an author string with MediaViewer styles * @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 */ - MPP.wrapAuthor = function ( author ) { - return $( '' ) + MPP.wrapAuthor = function ( author, authorCount, filepageUrl ) { + var $wrapper = $( '' ); + + $wrapper .addClass( 'mw-mmv-author' ) - .append( $.parseHTML( author ) ) - .get( 0 ).outerHTML; + .append( $.parseHTML( author ) ); + + if ( authorCount > 1 ) { + $wrapper.append( ' ', + $( '' ) + .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 // (e.g. floated buttons) has already been laid out 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 ) { this.setPermission( imageData.permission ); diff --git a/tests/qunit/mmv/mmv.EmbedFileFormatter.test.js b/tests/qunit/mmv/mmv.EmbedFileFormatter.test.js index 61e342e2a..c73f57f85 100644 --- a/tests/qunit/mmv/mmv.EmbedFileFormatter.test.js +++ b/tests/qunit/mmv/mmv.EmbedFileFormatter.test.js @@ -6,7 +6,7 @@ options.licenseInternalName, options.licenseLongName, options.licenseUrl ) : 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.source, options.author, license ), + undefined, undefined, undefined, options.source, options.author, options.authorCount, license ), repoInfo = { displayName: options.siteName, getSiteLink: function () { return options.siteUrl; } }; diff --git a/tests/qunit/mmv/model/mmv.model.Image.test.js b/tests/qunit/mmv/model/mmv.model.Image.test.js index e9a4f1ec4..33fda9054 100644 --- a/tests/qunit/mmv/model/mmv.model.Image.test.js +++ b/tests/qunit/mmv/model/mmv.model.Image.test.js @@ -18,7 +18,7 @@ ( function( mw ) { 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 title = mw.Title.newFromText( 'File:Foobar.jpg' ), name = 'Foo bar', @@ -35,6 +35,7 @@ description = 'This is a test file.', source = 'WMF', author = 'Ryan Kaldari', + authorCount = 1, permission = 'only use for good, not evil', license = new mw.mmv.model.License( 'cc0' ), latitude = 39.12381283, @@ -42,7 +43,7 @@ imageData = new mw.mmv.model.Image( title, name, size, width, height, mime, url, descurl, repo, user, datetime, origdatetime, - description, source, author, license, permission, + description, source, author, authorCount, license, permission, latitude, longitude ); 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.source, source, 'Source 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.permission, permission, 'Permission 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', 10, 10, 10, 'image/jpeg', 'http://example.org', 'http://example.com', '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( mw.Title.newFromText( 'File:Foobar.pdf.jpg' ), 'Foo bar', 10, 10, 10, 'image/jpeg', 'http://example.org', 'http://example.com', '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' ); @@ -87,10 +89,13 @@ 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, stringData = { value: 'foo' }, plaintextData = { value: 'foo' }, + integerData = { value: 3 }, + integerStringData = { value: '3' }, + zeroPrefixedIntegerStringData = { value: '03' }, floatData = { value: 1.23 }, floatStringData = { value: '1.23' }, booleanData = { value: 'yes' }, @@ -112,6 +117,12 @@ 'Extmeta boolean string parsed correctly.' ); assert.strictEqual( Image.parseExtmeta( wrongBooleanData, 'boolean' ), undefined, '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' ), [], 'Extmeta empty list parsed correctly.' ); assert.deepEqual( Image.parseExtmeta( listDataSingle, 'list' ), ['foo'], diff --git a/tests/qunit/mmv/provider/mmv.provider.ImageInfo.test.js b/tests/qunit/mmv/provider/mmv.provider.ImageInfo.test.js index dfb8e167c..e9062b190 100644 --- a/tests/qunit/mmv/provider/mmv.provider.ImageInfo.test.js +++ b/tests/qunit/mmv/provider/mmv.provider.ImageInfo.test.js @@ -25,7 +25,7 @@ assert.ok( imageInfoProvider ); } ); - QUnit.asyncTest( 'ImageInfo get test', 25, function ( assert ) { + QUnit.asyncTest( 'ImageInfo get test', 26, function ( assert ) { var apiCallCount = 0, api = { get: function() { apiCallCount++; @@ -101,6 +101,10 @@ value: 'Wikimeda', source: 'commons-desc-page' }, + AuthorCount: { + value: '2', + source: 'commons-desc-page' + }, Permission: { value: 'Do not use. Ever.', source: 'commons-desc-page' @@ -142,6 +146,7 @@ assert.strictEqual( image.description, 'Wikis stuff', 'description is set correctly' ); assert.strictEqual( image.source, 'Wikipedia', 'source 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.internalName, 'cc0', 'license internal name is set correctly' ); assert.strictEqual( image.license.longName, 'Creative Commons Public Domain Dedication', 'license long name is set correctly' );