Page MenuHomePhabricator
Paste P7368

WIP wikidata gadget
ActivePublic

Authored by Mvolz on Jul 17 2018, 10:02 AM.
Tags
Referenced Files
F25172958: WIP wikidata gadget
Aug 20 2018, 12:00 PM
F23900449: WIP wikidata gadget
Jul 19 2018, 1:07 PM
F23900358: WIP wikidata gadget
Jul 19 2018, 12:29 PM
F23843732: WIP wikidata gadget
Jul 17 2018, 10:02 AM
Subscribers
None
var citoidQidTypeMap = {
"artwork": "Q838948",
"audioRecording": "Q5057302",
"bill": "Q686822",
"blogPost": "Q17928402",
"book": "Q571",
"bookSection": "Q1980247",
"case": "Q2334719",
"computerProgram": "Q5057302",
"conferencePaper": "Q23927052",
"dictionaryEntry": "Q4423781",
"document": "Q49848",
"email": "Q9158",
"encyclopediaArticle": "Q17329259",
"film": "Q11424",
"forumPost": "Q7216866",
"hearing": "Q545861",
"instantMessage": "Q30070565",
"interview": "Q178651",
"journalArticle": "Q191067",
"letter": "Q133492",
"magazineArticle": "Q191067",
"manuscript": "Q87167",
"map": "Q4006",
"newspaperArticle": "Q191067",
"patent": "Q253623",
"podcast": "Q5057302",
"presentation": "Q604733",
"radioBroadcast": "Q1555508",
"report": "Q10870555",
"statute": "Q820655",
"thesis": "Q1266946",
"tvBroadcast": "Q21191270",
"videoRecording": "Q30070675",
"webpage": "Q36774"
};
var instanceOf = "P31"; // Property to use for "instance of" or "type" of the item; corresponds to field 'itemType' in citoid
var statedIn = "P149"; // Property to use to point to items in references
var currentField;
( function ( $, mw ) {
'use strict';
function ReferenceItem( data ) {
this.data = data; // Raw data from citoid
this.display = {}; // Elements for displaying the item
}
function CitoidDialog( config ) {
CitoidDialog.super.call( this, config );
}
OO.inheritClass( CitoidDialog, OO.ui.Dialog );
CitoidDialog.static.name = 'citoidDialog';
CitoidDialog.static.title = 'Create new reference item';
CitoidDialog.static.citoidFormat = 'mediawiki-basefields';
CitoidDialog.prototype.initialize = function () {
CitoidDialog.super.prototype.initialize.call( this );
this.items = []; // List of ReferenceItem objects
this.content = new OO.ui.PanelLayout( { padded: true, expanded: false } );
this.content.$element.append( '<p>Press \'Esc\' to close. </p>' );
this.$body.append( this.content.$element );
this.lookupInput = new OO.ui.TextInputWidget( {
placeholder: 'URL, DOI, PMC or Title'
} );
this.lookupButton = new OO.ui.ButtonWidget( {
label: 'Search'
} );
this.lookupActionFieldLayout = new OO.ui.ActionFieldLayout( this.lookupInput, this.lookupButton, {
align: 'top',
label: 'Search'
} );
// Error label
this.$noticeLabel = $( '<div>' ).addClass( 'oo-ui-element-hidden' ).text( 'Unable to retrieve data from input; try changing your search?' );
this.content.$element.append( this.lookupActionFieldLayout.$element, this.$noticeLabel );
// Restbase URL
this.fullRestbaseUrl = 'https://en.wikipedia.org/api/';
// API config for citoid service if using citoid behind Restbase
this.serviceUrl = this.fullRestbaseUrl + 'rest_v1/data/citation/' + CitoidDialog.static.citoidFormat;
this.serviceConfig = {
ajax: {
// Request content in user language from citoid service. Possible we should do English as well to get both in case they are different.
headers: { 'accept-language': mw.config.get( 'wgUserLanguage' ) },
crossDomain: true,
timeout: 30 * 1000, // 30 seconds
type: 'GET'
}
};
this.lookupButton.connect( this, { click: 'onLookupButtonClick' } );
};
// Override the getBodyHeight() method to specify a custom height (or don't to use the automatically generated height)
CitoidDialog.prototype.getBodyHeight = function () {
return this.content.$element.outerHeight( true );
};
CitoidDialog.prototype.getSetupProcess = function ( data ) {
data = data || {};
return CitoidDialog.super.prototype.getSetupProcess.call( this, data )
.next( function () {
// Set up contents based on data
this.lookupInput.setValue( data.search );
}, this );
};
CitoidDialog.prototype.displayNewItem = function ( data ) {
this.item = new ReferenceItem( data[0] ); // Hardcode to only use first citation for the time being
this.items.push( this.item ); // Add to list of items in dialog
/* Display Elements */
// Item label
this.item.display.label = new OO.ui.TextInputWidget( {
value: this.item.data.title,
label: 'label'
} );
// Item description
this.item.display.description = new OO.ui.TextInputWidget( {
value: this.item.data.itemType,
label: 'description'
} );
// Button
this.item.display.createButton = new OO.ui.ButtonWidget( {
label: 'Create new item'
} );
// Item creation failure message
this.item.display.$failureLabel = $( '<div>' )
.addClass( 'oo-ui-element-hidden' )
.text( 'Unable to create new item; item with same label and description already exists. Try changing the description?' );
/* Actions */
this.item.display.createButton.connect( this, { click: 'onCreateButtonClick' } );
/* Layout */
this.item.display.itemLayout = new OO.ui.FieldsetLayout( {
label: 'New Item',
classes: ["container"]
} );
this.item.display.itemLayout.addItems( [
new OO.ui.FieldLayout( this.item.display.label, {
label: this.item.display.label.label,
align: 'left'
} ),
new OO.ui.FieldLayout( this.item.display.description, {
label: this.item.display.description.label,
align: 'left'
} )
]);
this.content.$element.append( this.item.display.itemLayout.$element,
this.item.display.createButton.$element,
this.item.display.$failureLabel
);
this.updateSize();
};
/**
* Handle click events from the lookup button, perform lookup
*/
CitoidDialog.prototype.onLookupButtonClick = function () {
this.executeAction( 'lookup' );
};
/**
* Handle click events from the lookup button, perform lookup
*/
CitoidDialog.prototype.onCreateButtonClick = function () {
this.executeAction( 'create' );
};
CitoidDialog.prototype.getActionProcess = function ( action ) {
var dialog = this;
if ( action === 'lookup' ) {
return new OO.ui.Process( function () {
return this.performLookup().then( function ( data ) {
dialog.displayNewItem( data );
},
// Failure
function () {
dialog.lookupFailed();
});
}, this );
}
if ( action === 'create' ) {
return new OO.ui.Process( function () {
dialog.createNewItem();
}, this );
}
// Fallback to parent handler
return CitoidDialog.super.prototype.getActionProcess.call( this, action );
};
CitoidDialog.prototype.createNewItem = function ( ) {
var dialog = this;
var data = {
labels:[ {
language:"en",
value: this.item.display.label.value
} ],
descriptions:[ {
language:"en",
value: this.item.display.description.value
} ]
};
var api = new mw.Api();
// Set as pending
this.item.display.createButton.setDisabled( true );
this.item.display.label.setDisabled( true ).pushPending();
this.item.display.description.setDisabled( true ).pushPending();
api.postWithEditToken( {
assert: 'user',
action: 'wbeditentity',
new: 'item',
data: JSON.stringify(data),
format: 'json'
} ).then( function ( results ) {
if ( results.success && results.success === 1 ) {
dialog.insertItem( results );
} else {
dialog.creationFailed();
}
// Failure
}, function ( ) {
dialog.creationFailed();
}
);
}
var addReferenceItem = function( statement, qid ) {
console.log( 'addRefItem Function' );
var api = new mw.Api();
var entity = mw.config.get( 'wbEntityId' );
var numericId = Number( qid.substr( 1 ) ); // Remove the Q and cast to Number to get the numeric ID
var snaks = {};
snaks[statedIn] = [{
"snaktype":"value",
"property": statedIn,
"datavalue":{
"entity-type":"item",
"numeric-id": numericId
}
}]
//TODO: revision id maybe needed
api.postWithEditToken( {
assert: 'user',
action: 'wbsetreference',
format: 'json',
statement: statement,
snaks: snaks
} ).then( function ( results ) {
if ( results.success && results.success === 1 ) {
console.log('Inserted item as reference');
} else {
console.log('Unabled to insert created item');
}
// Failure
}, function ( ) {
console.log('Unable to insert created item');
}
);
};
CitoidDialog.prototype.insertItem = function ( results ) {
this.close( results.entity.id );
// Remove pre-creation display
//this.item.display.itemLayout.$element.remove();
//this.item.display.createButton.$element.remove();
// Update display
//this.content.$element.append( '<p>Created new item ' + results.entity.id + '</p>' );
//this.updateSize();
// TODO insert item into field
};
/**
* Send a request to the citoid service
*
* @return {jQuery.Promise} Lookup promise
*/
CitoidDialog.prototype.performLookup = function () {
var xhr,
search,
dialog = this;
// Clear prior results
this.clearResults();
// TODO: Add caching for requested urls
if ( this.lookupPromise ) {
// Abort existing lookup
this.lookupPromise.abort();
this.lookupPromise = null;
this.lookupInput.popPending();
}
// Set as pending
this.lookupButton.setDisabled( true );
this.lookupInput.setDisabled( true ).pushPending();
search = this.lookupInput.getValue();
search = decodeURIComponent( search );
// We have to first set up a get response so we can have
// a proper xhr object with "abort" method, so we can
// hand off this abort method to the jquery promise
if ( this.fullRestbaseUrl ) {
// Use restbase endpoint
this.serviceConfig.ajax.url = this.serviceUrl + '/' + encodeURIComponent( search );
xhr = new mw.Api( this.serviceConfig ).get();
} else {
// Use standalone citoid service
xhr = this.service
.get( {
search: search,
format: ve.ui.CiteFromIdInspector.static.citoidFormat
} );
}
this.lookupPromise = xhr
.always( function () {
dialog
.lookupInput
.setDisabled( false )
// restore focus to the input field
.focus()
.popPending();
dialog.lookupButton.setDisabled( false );
} )
.promise( { abort: xhr.abort } );
return this.lookupPromise;
};
CitoidDialog.prototype.lookupFailed = function () {
// Enable the input and lookup button
this.$noticeLabel.removeClass( 'oo-ui-element-hidden' );
this.lookupInput.once( 'change', function () {
this.$noticeLabel.addClass( 'oo-ui-element-hidden' );
this.updateSize();
}.bind( this ) ).setValidityFlag( false );
this.updateSize();
};
CitoidDialog.prototype.creationFailed = function () {
// Enable the input and lookup button
this.item.display.$failureLabel.removeClass( 'oo-ui-element-hidden' );
this.item.display.label.setDisabled( false ).popPending();
this.item.display.description.setDisabled( false ).popPending();
this.item.display.createButton.setDisabled( false );
this.updateSize();
};
/**
* Clear the search results
*/
CitoidDialog.prototype.clearResults = function () {
var i;
for (i = 0; i < this.items.length; i++) {
this.item.display.itemLayout.$element.remove();
this.item.display.createButton.$element.remove();
this.item.display.$failureLabel.remove();
}
this.items = [];
this.updateSize();
};
// Make the window.
var citoidDialog = new CitoidDialog( {
size: 'medium'
} );
// Create and append a window manager, which will open and close the window.
var windowManager = new OO.ui.WindowManager();
$( 'body' ).append( windowManager.$element );
// Add the window to the window manager using the addWindows() method.
windowManager.addWindows( [ citoidDialog ] );
function init() {
currentField = $( 'input' );
var willEntitySelectorListUpdate = false;
$( document ).on( 'input propertychange paste', '.wikibase-snakview-value-container:has(.ui-suggester-input)', function () {
currentField = $( this ).find( '.ui-suggester-input' ).first();
willEntitySelectorListUpdate = true;
} );
$( document ).on( 'DOMSubtreeModified', '.ui-entityselector-list', function () {
if ( willEntitySelectorListUpdate ) {
var firstLi = $( this ).find( 'li' ).first();
var isNotFound = firstLi.hasClass( 'ui-entityselector-notfound' );
if ( isNotFound ) {
willEntitySelectorListUpdate = false;
var $innerA = firstLi.find( 'a' ).first();
$innerA.on( 'click', function () {
firstLi.remove();
var citoidWindow = windowManager.openWindow( citoidDialog, { search: currentField.val() } );
citoidWindow.opened.then( function () {
citoidDialog.executeAction( 'lookup' );
} );
citoidWindow.closed.then( function( qid ) {
if ( qid ) {
//find closet
var $statement = currentField.closest( '.wikibase-statementview' );
var entity = mw.config.get( 'wbEntityId' );
var statementHash = $statement[0].id;
addReferenceItem( statementHash, qid );
//currentField.val( qid );
//currentField.addClass( 'ui-entityselector-input-recognized' ); // Make field recognised
//currentField.removeClass( 'ui-entityselector-input-unrecognized' );
//var $publishLink = $statement.find( 'span.wikibase-toolbar-button-save' ); // Get publish link
// Enable publish link
//$publishLink.attr('aria-disabled', 'false');
//$publishLink.removeClass( 'wikibase-toolbarbutton-disabled' );
//$publishLink.removeClass( 'ui-state-disabled' );
}
} );
return false;
} );
$innerA.text( "No item with that title found; Create new reference item?" );
}
}
} );
}
$( function () {
mw.hook( 'wikibase.entityPage.entityLoaded' ).add( init );
} );
}( jQuery, mediaWiki ) );