Jdlrobson has uploaded a new change for review.
https://gerrit.wikimedia.org/r/169730
Change subject: Creative destruction: Wikidata Infoboxes in alpha
......................................................................
Creative destruction: Wikidata Infoboxes in alpha
This replaces infoboxes in alpha of the site with a Wikidata generated one.
The idea is we can use this to highlight missing data and use it as a basis
for wikidata games.
Change-Id: I9c206b4c87b9aec0e8e59f484a24d26fd7570fdf
---
M includes/Resources.php
A javascripts/modules/infobox/Infobox.js
A javascripts/modules/infobox/init.js
M javascripts/modules/wikigrok/WikiDataApi.js
A templates/modules/wikigrok/Infobox.hogan
5 files changed, 294 insertions(+), 25 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/MobileFrontend
refs/changes/30/169730/1
diff --git a/includes/Resources.php b/includes/Resources.php
index bf4ac15..24958c0 100644
--- a/includes/Resources.php
+++ b/includes/Resources.php
@@ -546,9 +546,11 @@
'mobile.alpha' => $wgMFMobileResourceBoilerplate + array(
'dependencies' => array(
'mobile.beta',
+ 'mobile.infobox',
),
'scripts' => array(
'javascripts/modules/mf-translator.js',
+ 'javascripts/modules/infobox/init.js',
),
),
@@ -918,18 +920,26 @@
),
),
+ 'mobile.wikigrok.api' => $wgMFMobileResourceBoilerplate + array(
+ 'dependencies' => array(
+ 'mobile.startup',
+ ),
+ 'scripts' => array(
+ 'javascripts/modules/wikigrok/WikiDataApi.js',
+ 'javascripts/modules/wikigrok/WikiGrokApi.js',
+ ),
+ ),
+
// See https://www.mediawiki.org/wiki/Extension:MobileFrontend/WikiGrok
'mobile.wikigrok.dialog' => $wgMFMobileResourceBoilerplate + array(
'dependencies' => array(
- 'mobile.alpha',
+ 'mobile.wikigrok.api',
),
'templates' => array(
'modules/wikigrok/WikiGrokDialog.hogan',
'modules/wikigrok/WikiGrokMoreInfo.hogan',
),
'scripts' => array(
- 'javascripts/modules/wikigrok/WikiDataApi.js',
- 'javascripts/modules/wikigrok/WikiGrokApi.js',
'javascripts/modules/wikigrok/WikiGrokDialog.js',
'javascripts/modules/wikigrok/WikiGrokMoreInfo.js',
),
@@ -938,6 +948,18 @@
),
),
+ 'mobile.infobox' => $wgMFMobileResourceBoilerplate + array(
+ 'dependencies' => array(
+ 'mobile.wikigrok.api',
+ ),
+ 'templates' => array(
+ 'modules/wikigrok/Infobox.hogan',
+ ),
+ 'scripts' => array(
+ 'javascripts/modules/infobox/Infobox.js',
+ ),
+ ),
+
'mobile.site' => array(
'dependencies' => array( 'mobile.startup' ),
'class' => 'MobileSiteModule',
diff --git a/javascripts/modules/infobox/Infobox.js
b/javascripts/modules/infobox/Infobox.js
new file mode 100644
index 0000000..023780c
--- /dev/null
+++ b/javascripts/modules/infobox/Infobox.js
@@ -0,0 +1,209 @@
+( function ( M, $ ) {
+ var Infobox,
+ WikiDataApi = M.require( 'modules/wikigrok/WikiDataApi' ),
+ View = M.require( 'View' );
+ /**
+ * A Wikidata generated infobox.
+ * FIXME: This currently requires 2 hits to the Wikidata API on every
page load.
+ * @class Infobox
+ * @extends View
+ */
+ Infobox = View.extend( {
+ template: M.template.get( 'modules/wikigrok/Infobox.hogan' ),
+
+ className: 'wikidata-infobox',
+ defaults: {
+ description: undefined,
+ rows: []
+ },
+ typeDefaults: {
+ human: {
+ description: undefined,
+ rows: [ {
+ id: 'P18'
+ },
+ // description
+ // details about employment
+ {
+ id: 'P569',
+ label: 'Born'
+ }, {
+ id: 'P19',
+ label: 'Birthplace'
+ },
+ // nationality
+ // political party
+ {
+ id: 'P26',
+ label: 'Spouse(s)'
+ }, {
+ id: 'P25',
+ label: 'Mother(s)'
+ }, {
+ id: 'P22',
+ label: 'Father(s)'
+ },
+ //{ id: 'P43', label: 'Stepfather(s)' },
+ {
+ id: 'P9',
+ label: 'Sister(s)'
+ }, {
+ id: 'P7',
+ label: 'Brother(s)'
+ }, {
+ id: 'P40',
+ label: 'Child(ren)'
+ },
+ // residence
+ {
+ id: 'P69',
+ label: 'Alma mater'
+ }, {
+ id: 'P106',
+ label: 'Occupation'
+ }, {
+ id: 'P108',
+ label: 'Employer(s)'
+ }, {
+ id: 'P140',
+ label: 'Religion'
+ },
+ // awards
+ {
+ id: 'P109',
+ label: 'Signature'
+ }, {
+ id: 'P856',
+ label: 'Official website'
+ }
+ ]
+ },
+ default: {
+ description: undefined,
+ rows: [ {
+ id: 'P856',
+ label: 'Official website'
+ } ]
+ }
+ },
+ /**
+ * Parses a list of claims
+ *
+ * @private
+ * @param {Object} claims as returned by WikiData#getClaims
+ * @return {Array} List of values matching that claim
+ */
+ _getValues: function ( claims ) {
+ var values = [];
+
+ $.each( claims, function ( i, claim ) {
+ var snak = claim.mainsnak,
+ value = snak.datavalue;
+ if ( snak.datatype === 'commonsMedia' ) {
+ values.push( {
+ url: mw.util.getUrl( 'File:' +
value.value ),
+ // FIXME: Map this to the image
src
+ value: value.value
+ } );
+ } else if ( value.type === 'string' ) {
+ values.push( {
+ value: value.value
+ } );
+ } else if ( value.type === 'time' ) {
+ // FIXME: Make human readable
+ values.push( {
+ value: value.value.time
+ } );
+ } else if ( value.type === 'wikibase-entityid'
) {
+ values.push( {
+ id: 'Q' + value.value[
'numeric-id' ],
+ isLink: true
+ } );
+ }
+ } );
+ return values;
+ },
+ /**
+ * Translates IDs in the current row value to human readable
text
+ *
+ * @private
+ * @param {Array} rows with id and label
+ * @return {Array} rows with human readable values
+ */
+ _mapLabels: function ( rows ) {
+ var labelIds = [];
+
+ // collect all the label ids
+ $.each( rows, function ( i, row ) {
+ $.each( row.values, function ( i, value ) {
+ if ( value.id ) {
+ labelIds.push( value.id );
+ }
+ } );
+ } );
+
+ // work out what they all mean
+ return this.api.getLabels( labelIds ).then( function (
labels ) {
+ // map the property id to the actual label.
+ $.each( rows, function ( i, row ) {
+ $.each( row.values, function ( j, value
) {
+ if ( labels[ value.id ] ) {
+ value.value = labels[
value.id ];
+ value.url =
mw.util.getUrl( value.value );
+ }
+ } );
+ } );
+ return rows;
+ } );
+ },
+ /**
+ * Decides based on the type of item what infobox to render
+ *
+ * @private
+ * @param {Object} claims as returned by WikiData#getClaims
+ * @return {Object} default option values
+ */
+ getDefaultsFromClaims: function ( claims ) {
+ if ( claims.isHuman ) {
+ return this.typeDefaults.human;
+ } else {
+ return this.typeDefaults.default;
+ }
+ },
+ /**
+ * @inheritdoc
+ */
+ initialize: function ( options ) {
+ var self = this,
+ _super = View.prototype.render;
+
+ this.api = new WikiDataApi( {
+ itemId: options.itemId
+ } );
+
+ this.api.getClaims().done( function ( claims ) {
+ var rows;
+ options = $.extend( options,
self.getDefaultsFromClaims( claims ) );
+ options.description = claims.description;
+ rows = options.rows;
+ $.each( rows, function ( i, row ) {
+ if ( claims.entities[ row.id ] ) {
+ row.values = self._getValues(
claims.entities[ row.id ] );
+ } else {
+ row.values = [];
+ }
+ row.isEmpty = !( row.values &&
row.values.length );
+ } );
+
+ self._mapLabels( rows ).done( function ( rows )
{
+ options.rows = rows;
+ _super.call( self, options );
+ } );
+ } );
+ View.prototype.initialize.apply( this, arguments );
+ }
+ } );
+
+ M.define( 'modules/wikigrok/Infobox', Infobox );
+
+}( mw.mobileFrontend, jQuery ) );
diff --git a/javascripts/modules/infobox/init.js
b/javascripts/modules/infobox/init.js
new file mode 100644
index 0000000..67be01c
--- /dev/null
+++ b/javascripts/modules/infobox/init.js
@@ -0,0 +1,14 @@
+( function ( M ) {
+ M.assertMode( [ 'alpha' ] );
+ var infobox,
+ wikidataID = mw.config.get( 'wgWikibaseItemId' ),
+ Infobox = M.require( 'modules/wikigrok/Infobox' );
+
+ // upset people
+ $( '.infobox' ).hide();
+ // build the future
+ infobox = new Infobox( {
+ itemId: wikidataID
+ } );
+ infobox.insertBefore( M.getLeadSection() );
+}( mw.mobileFrontend ) );
diff --git a/javascripts/modules/wikigrok/WikiDataApi.js
b/javascripts/modules/wikigrok/WikiDataApi.js
index c0d3fe0..97b2d3e 100644
--- a/javascripts/modules/wikigrok/WikiDataApi.js
+++ b/javascripts/modules/wikigrok/WikiDataApi.js
@@ -1,5 +1,6 @@
( function ( M, $ ) {
- var Api = M.require( 'api' ).Api, WikiDataApi;
+ var Api = M.require( 'api' ).Api,
+ WikiDataApi;
/**
* @class WikiDataApi
* @extends Api
@@ -7,35 +8,46 @@
WikiDataApi = Api.extend( {
apiUrl: 'https://www.wikidata.org/w/api.php',
useJsonp: true,
- language: 'en',
+ language: mw.config.get( 'wgUserLanguage' ),
initialize: function ( options ) {
this.subjectId = options.itemId;
Api.prototype.initialize.apply( this, arguments );
},
getClaims: function () {
- var id = this.subjectId;
+ var self = this,
+ id = this.subjectId;
+
return this.ajax( {
action: 'wbgetentities',
ids: id,
- props: 'claims',
+ languages: this.language,
+ props: [ 'descriptions', 'claims' ],
format: 'json'
} ).then( function ( data ) {
- var instanceClaims, entityClaims,
+ var description, instanceClaims, entityClaims,
claims = {};
// See if the page has any 'instance of' claims.
- if ( data.entities !== undefined &&
data.entities[id].claims.P31 !== undefined ) {
- entityClaims = data.entities[id].claims;
+ if ( data.entities !== undefined &&
data.entities[ id ].claims.P31 !== undefined ) {
+ entityClaims = data.entities[ id
].claims;
instanceClaims = entityClaims.P31;
// Examine claims closely
$.each( instanceClaims, function ( i,
claim ) {
- claims.isHuman =
claim.mainsnak.datavalue.value['numeric-id'] === 5 ? true : false;
+ claims.isHuman =
claim.mainsnak.datavalue.value[ 'numeric-id' ] === 5 ? true : false;
claims.hasOccupation =
entityClaims.P106 ? true : false;
claims.hasCountryOfCitizenship
= entityClaims.P27 ? true : false;
claims.hasDateOfBirth =
entityClaims.P569 ? true : false;
claims.hasDateOfDeath =
entityClaims.P570 ? true : false;
} );
+ claims.entities = entityClaims;
+ description = data.entities[ id ];
+
+ if ( description ) {
+ if ( description.descriptions[
self.language ] ) {
+ claims.description =
description.descriptions[ self.language ].value;
+ }
+ }
return claims;
} else {
return false;
@@ -52,21 +64,23 @@
getLabels: function ( itemIds ) {
var lang = this.language;
return this.ajax( {
- action: 'wbgetentities',
- props: 'labels',
- languages: lang,
- ids: itemIds
- } ).then( function ( data ) {
- var map = {};
- $.each( itemIds, function ( i, itemId )
{
- if (
data.entities[itemId].labels[lang].value !== undefined ) {
- map[itemId] =
data.entities[itemId].labels[lang].value;
- } else {
- map[itemId] = null;
- }
- } );
- return map;
+ action: 'wbgetentities',
+ props: 'labels',
+ languages: lang,
+ ids: itemIds
+ } ).then( function ( data ) {
+ var map = {};
+ $.each( itemIds, function ( i, itemId ) {
+ if ( data.entities[ itemId ].labels &&
+ data.entities[ itemId ].labels[
lang ] !== undefined
+ ) {
+ map[ itemId ] = data.entities[
itemId ].labels[ lang ].value;
+ } else {
+ map[ itemId ] = null;
+ }
} );
+ return map;
+ } );
}
} );
diff --git a/templates/modules/wikigrok/Infobox.hogan
b/templates/modules/wikigrok/Infobox.hogan
new file mode 100644
index 0000000..e72be6e
--- /dev/null
+++ b/templates/modules/wikigrok/Infobox.hogan
@@ -0,0 +1,10 @@
+<em>{{description}}</em>
+{{#rows}}
+<div>
+ {{^isEmpty}}{{#label}}<strong>{{label}}</strong>: {{/label}}{{/isEmpty}}
+ {{#values}}
+ {{#url}}<a href="{{url}}">{{value}}</a>{{/url}}
+ {{^url}}{{value}}{{/url}}
+ {{/values}}
+</div>
+{{/rows}}
--
To view, visit https://gerrit.wikimedia.org/r/169730
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I9c206b4c87b9aec0e8e59f484a24d26fd7570fdf
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/MobileFrontend
Gerrit-Branch: master
Gerrit-Owner: Jdlrobson <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits