User:DannyS712/PropertyCreator.js
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
// <nowiki>
/* jshint maxerr: 999 */
// Script to simplify creation of new properties.
// After creating, when marking proposal as done, for easy of copying
// I created this in part using a new script to simplify property creation, please let me know if something went wrong --~~~~
// notes:
// convert "instance of" to dropdown? Always use Q19847637? ("Wikidata property for an identifier")
// regex to convert examples from the page: `Example.*?\((.*?)\) → ` -> `$1|`
$(() => {
const PropertyCreator = {};
window.PropertyCreator = PropertyCreator;
PropertyCreator.config = {
version: '1.4',
author: 'DannyS712', // [[User:RPI2026F1]] also helped
createSummary: 'Creating new property',
attribution: ' using [[User:DannyS712/PropertyCreator.js]]',
// show a warning to users that the script is currently being worked on and
// may not be stable
currentDev: false,
// CONFIG for api calls
// "property proposal discussion", P3254 in prod, P94264 in test
propertyProposalDiscussionProp: 'P3254',
// "Wikidata property example", P1855 in prod, P48077 in test
propertyExampleProp: 'P1855',
// Expected completeness. New properties are almost always created
// as "always incomplete" or "eventually complete"
// "expected completeness", P2429 in prod, P95238 in test
// "always incomplete", Q21873886 in prod, Q211837 in test
// "eventually complete", Q21873974 in prod, Q213515 in test
completenessProp: "P2429",
completenessAlwaysIncomplete: "Q21873886",
completenessEventuallyComplete: "Q21873974",
// Stability of property values
// "stability of property value", P2668 in prod
// "never changes", Q23611288 in prod
// "sometimes changes", Q24025284 in prod
// "values can be added", Q23611840 in prod
// "continuously changes", Q23611587 in prod
stabilityProp: "P2668",
stabilityNeverChanges: "Q23611288",
stabilitySometimesChanges: "Q24025284",
stabilityValuesCanBeAdded: "Q23611840",
stabilityContinuouslyChanges: "Q23611587",
// "subject item of this property", P1629 in prod, P94574 in test
subjectItemProp: 'P1629',
// Inverse, used to link back
// "Wikidata property", P1687 in prod, P678 in test
wikidataPropertyProp: 'P1687',
// "formatter URL", P1630 in prod, P368 in test
formatterURLProp: 'P1630',
// "format as a regular expression", P1793 in prod, P51065 in test
regexFormatProperty: 'P1793',
// CONSTRAINTS
// Overall property, "property constraint", P2302 in prod, P51064 in test
constraintPropertyProp: 'P2302',
// regex "format constraint", Q21502404 in prod, Q100086 in test
regexFormatConstraint: 'Q21502404',
// "Wikimedia import URL", P4656 in prod, P77057 in test
wikimediaImportURLProp: 'P4656',
// For now only works with main scope
// "property scope constraint", Q53869507 in prod, Q187953 in test
// qualifier for the constraint, "property scope", P5314 in prod, P84130 in test
// scope: main value, "as main value", Q54828448 in prod, Q187967 in test
propertyScopeConstraint: 'Q53869507',
propertyScopeConstraintProp: 'P5314',
propertyScopeAsMain: 'Q54828448',
// For now only works with items
// "allowed entity types constraint", Q52004125 in prod, Q187951 in test
// "item of property constraint", P2305 in prod, P76946 in test
// "Wikibase item", Q29934200 in prod, Q187962 in test
// "class" (for classes in property constraints), P2308 in prod
// "relation" (for classes in property constraints), P2309 in prod
// currently only instance of is used:
// "instance of" (for relations), Q21503252 in prod
allowedEntityTypesConstraint: 'Q52004125',
itemOfPropertyConstraint: 'P2305',
entityTypeItem: 'Q29934200',
classForPropertyConstraint: 'P2308',
relationOfPropertyConstraint: 'P2309',
instanceOfRelation: 'Q21503252',
// "instance of", P31 in prod, P82 in test
instanceOfProp: 'P31',
// "distinct values constraint", Q21502410 in prod, ??? in test
distinctValuesConstraint: 'Q21502410',
// "single value constraint", Q19474404 in prod, ??? in test
singleValueConstraint: 'Q19474404',
// "source website for the property", P1896 in prod, P94562 in test
sourceWebsiteProp: 'P1896',
// "subject type constraint", Q21503250 in prod
subjectTypeConstraint: 'Q21503250',
// "language of work or name", P407 in prod
languageOfWorkProp: 'P407'
};
PropertyCreator.config.attribution += (" (version " + PropertyCreator.config.version + ")");
PropertyCreator.config.createSummary += PropertyCreator.config.attribution;
PropertyCreator.checkDomain = function () {
// Use different configuration on test.wikidata.org
if ( mw.config.get( 'wgDBname') !== 'testwikidatawiki' ) {
return;
}
var testWikidataConfig = {
propertyProposalDiscussionProp: 'P94264',
propertyExampleProp: 'P48077',
completenessProp: "P95238",
completenessAlwaysIncomplete: "Q211837",
completenessEventuallyComplete: "Q213515",
subjectItemProp: 'P94574',
wikidataPropertyProp: 'P678',
formatterURLProp: 'P368',
regexFormatProperty: 'P51065',
constraintPropertyProp: 'P51064',
regexFormatConstraint: 'Q100086',
wikimediaImportURLProp: 'P77057',
propertyScopeConstraint: 'Q187953',
propertyScopeConstraintProp: 'P84130',
propertyScopeAsMain: 'Q187967',
allowedEntityTypesConstraint: 'Q187951',
itemOfPropertyConstraint: 'P76946',
entityTypeItem: 'Q187962',
instanceOfProp: 'P82',
sourceWebsiteProp: 'P94562',
stabilityProp: 'P97210',
stabilityNeverChanges: 'Q227415',
stabilitySometimesChanges: 'Q227416',
stabilityValuesCanBeAdded: 'Q227417',
stabilityContinuouslyChanges: 'Q227418',
relationOfPropertyConstraint: 'P97211',
instanceOfRelation: 'Q227419',
distinctValuesConstraint: 'Q227420',
singleValueConstraint: 'Q227421',
subjectTypeConstraint: 'Q227422',
languageOfWorkProp: 'P97212'
};
$.extend( PropertyCreator.config, testWikidataConfig );
var e = PropertyCreator.elements;
// Only for testing/development; label needs to be changed (eg add a number at the end)
e.pLabel.setValue( 'ProperlyLabel' );
e.pDescription.setValue( 'PropertyDescription' );
e.instanceOf.setValue( 'Q213516|Q227413' );
e.proposalDiscussion.setValue( 'https://www.wikidata.org/wiki/Wikidata:Property_proposal/Supreme_Court_docket_number' );
e.subjectItem.setValue( 'Q34734' );
e.subjectType.setValue( 'Q227413|Q227414' );
e.sourceWebsite.setValue( 'https://www.wikidata.org/wiki/Property:P7063' );
e.sourceWebsiteLang.setValue( 'Q227413' );
e.examplesText.setValue( "Q199546|foo\nQ57403|bar" );
e.formatterURL.setValue( 'https://caselaw.findlaw.com/search.html?search_type=docket&court=us-supreme-court&text=$1' );
e.formatterURLLang.setValue( 'Q227413' );
e.valueRegex.setValue( '\\d+' );
};
PropertyCreator.onErrHandler = function () {
// Shared error handler
alert( 'Something went wrong' );
console.log( arguments );
};
PropertyCreator.init = function () {
window.document.title = 'PropertyCreator script';
PropertyCreator.createElements();
PropertyCreator.checkDomain();
PropertyCreator.maybeAddDevWarning();
var preloadFieldSet = new OO.ui.FieldsetLayout( {
label: 'Form preloader'
} );
var e = PropertyCreator.elements;
preloadFieldSet.addItems( [
e.proposalDiscussionPageLayout,
e.preloadButtonLayout
] );
var preloadTitle = mw.util.getParamValue( 'forDiscussion' );
if ( preloadTitle !== null ) {
e.proposalDiscussionPage.setValue( preloadTitle );
}
var formFieldSet = new OO.ui.FieldsetLayout( {
label: 'Helper to easily create new properties'
} );
formFieldSet.addItems( [
e.pLabelLayout,
e.pDescriptionLayout,
e.pDataTypesLayout,
e.proposalDiscussionLayout,
e.instanceOfLayout,
e.allowedEntityTypeLayout,
e.subjectItemLayout,
e.subjectTypeLayout,
e.sourceWebsiteLayout,
e.sourceWebsiteLangLayout,
e.propertyScopeLayout,
e.stabilityLayout,
e.expectedCompletenessLayout,
e.examplesTextLayout,
e.formatterURLLayout,
e.formatterURLLangLayout,
e.valueRegexLayout,
e.distinctValuesConstraintLayout,
e.distinctValuesConstraintRefLayout,
e.singleValueConstraintLayout,
e.singleValueConstraintRefLayout,
e.submitButtonLayout
] );
$('#mw-content-text').empty().append(
preloadFieldSet.$element,
$( '<hr>' ),
formFieldSet.$element
);
e.preloadButton.on(
'click',
PropertyCreator.onPreload
);
e.submitButton.on(
'click',
PropertyCreator.onSubmit
);
};
PropertyCreator.maybeAddDevWarning = function () {
if ( !PropertyCreator.config.currentDev ) {
return;
}
OO.ui.alert(
'PropertyCreator is currently undergoing active development and is not stable!'
);
};
PropertyCreator.done = function ( propId ) {
console.log( 'DONE' );
console.log( 'Property created at: ' + propId );
};
PropertyCreator.onPreload = function () {
console.log( 'Preloading!' );
var e = PropertyCreator.elements;
e.submitButton.setDisabled( true );
var subpageTitle = e.proposalDiscussionPage.getValue();
PropertyCreator.preloadFrom( subpageTitle ).then(
function () {
console.log( 'Preloaded!' );
e.submitButton.setDisabled( false );
},
PropertyCreator.onErrHandler
);
};
PropertyCreator.preloadFrom = function ( subpageTitle ) {
var fullTitle = 'Wikidata:Property proposal/' + subpageTitle;
return new Promise( function ( resolve ) {
new mw.Api().get( {
action: 'parse',
page: fullTitle,
prop: 'wikitext',
format: 'json',
formatversion: 2
} ).then(
function ( response ) {
console.log( response ); // Good result
var wikitext = response.parse.wikitext;
console.log( wikitext );
var extracted = PropertyCreator.extractFromDiscussionPage( fullTitle, wikitext );
console.log( extracted );
PropertyCreator.applyPreloads( extracted );
resolve();
},
PropertyCreator.onErrHandler
);
} );
};
PropertyCreator.extractFromDiscussionPage = function ( fullTitle, wikitext ) {
let minHeading = '==';
if ( wikitext.startsWith( '===' ) ) {
minHeading = '===';
}
var fields = {};
fields.proposalDiscussion = 'https://www.wikidata.org/wiki/' + ( fullTitle.replaceAll( ' ', '_' ) );
// strip comments
wikitext = wikitext.replace( /<!--[\s\S]*?-->/g, '' );
// strip motivation and discussion, everything after first ====
wikitext = wikitext.slice( 0, wikitext.indexOf( minHeading + '=' ) );
// only keep English from TranslateThis templates
wikitext = wikitext.replace( /{{TranslateThis[\s\S]*?\|\s*en\s*=\s*([\s\S]*?)(?:\|[\s\S]*?}}|}})/g, '$1' );
console.log( wikitext );
// Split - get the heading and the content
var splits = wikitext.split( minHeading );
var sectionHeading = splits[ 1 ];
wikitext = splits[ 2 ];
console.log( sectionHeading, wikitext );
fields.pLabel = sectionHeading.trim();
wikitext = wikitext.replace( /\s*?{{Property proposal\s*/, '' );
wikitext = wikitext.replace( /\n?}}\s*$/, '' );
wikitext = wikitext.replace( /[ \t]+/g, ' ' );
wikitext = wikitext.replace( /\n\n/g, '\n' );
var params = wikitext.split( /(?:^|[^Q])\|/g );
console.log( params );
var rawFields = {};
params.forEach(
function ( paramLine ) {
var match = paramLine.match( /\s*(\S[^=]+?)\s*=\s*(.*)/ );
if ( match !== null ) {
var matchValue = ( match[ 2 ] ).trim();
matchValue = matchValue.replace( /{{\s*[qQ]\s*\|\s*[qQ]?(\d+)\s*}}/g, 'Q$1' );
if ( matchValue !== '' ) {
rawFields[ match[1] ] = matchValue;
}
}
}
);
console.log( rawFields );
fields[ 'pDescription' ] = rawFields[ 'description' ];
fields.subjectItem = rawFields[ 'subject item' ];
fields.subjectType = rawFields[ 'domain' ];
fields.sourceWebsite = rawFields[ 'source' ];
fields.formatterURL = rawFields[ 'formatter URL' ];
fields.valueRegex = rawFields[ 'allowed values' ];
fields.distinctValuesConstraint = rawFields[ 'distinct values constraint' ] === 'yes';
fields.distinctValuesConstraintRef = rawFields[ 'distinct values constraint' ] === 'yes';
fields.singleValueConstraint = rawFields[ 'single value constraint' ] === 'yes';
fields.singleValueConstraintRef = rawFields[ 'single value constraint' ] === 'yes';
// expected completeness
var cfg = PropertyCreator.config;
if ( rawFields[ 'expected completeness' ] === cfg.completenessAlwaysIncomplete ) {
fields.expectedCompleteness = 'alwaysIncomplete';
} else if ( rawFields[ 'expected completeness' ] === cfg.completenessEventuallyComplete ) {
fields.expectedCompleteness = 'eventuallyComplete';
}
// examples
fields.examplesText = '';
Object.keys( rawFields ).filter(
function ( key ) { return key.startsWith( 'example' ); }
)
.forEach(
function ( key ) {
var valMatch = rawFields[ key ].match( /^(Q\d+)[^\[]+\[\S+\s+(.*?)\]/ );
if ( valMatch !== null ) {
console.log( valMatch );
fields.examplesText += valMatch [ 1 ] + '|' + valMatch[ 2 ] + '\n';
}
}
);
fields.examplesText = fields.examplesText.trim();
return fields;
};
PropertyCreator.applyPreloads = function ( fields ) {
var e = PropertyCreator.elements;
Object.keys( fields ).forEach(
function ( key ) {
if ( fields[ key ] !== undefined && e[ key ] !== undefined ) {
if ( typeof ( fields[ key ] ) === 'string' ) {
e[ key ].setValue( fields[ key ] );
} else {
// must be boolean
e[ key ].setSelected( fields[ key ] );
}
}
}
);
};
PropertyCreator.onSubmit = function () {
console.log( 'Submitted!' );
var e = PropertyCreator.elements;
e.submitButton.setDisabled( true );
PropertyCreator.createProperty(
e.pLabel.getValue(),
e.pDescription.getValue(),
e.pDataTypes.getValue()
);
};
PropertyCreator.util = {};
PropertyCreator.util.uuidv4 = function () {
// Generates a random UUID v4
// Source: https://stackoverflow.com/a/2117523/12248328
// *Should* be compatible with IE11
return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
);
};
PropertyCreator.util.makeSnak = function ( property, value ) {
const snak = {
"snaktype": "value",
"property": property,
};
if (value.match(/^[QP][1-9]\d*$/i)){
// This is an entity ID
snak.datavalue = {
"type": "wikibase-entityid",
"value": {
"id": value
}
};
} else {
snak.datavalue = {
"type": "string",
"value": value
};
}
return snak;
};
PropertyCreator.util.serializeClaim = function ( claimInfo ) {
// Serializes a claim for batch editing.
const claim = {
"type": "statement",
"mainsnak": {
"snaktype": "value",
},
"rank": "normal"
};
claim.mainsnak = PropertyCreator.util.makeSnak(claimInfo.claimProperty, claimInfo.claimValue);
if (claimInfo.claimEntity){
claim.id = claimInfo.claimEntity + "$" + PropertyCreator.util.uuidv4();
}
let qualifiers = [];
if ( claimInfo.claimQualifier && claimInfo.claimQualifier !== false ){
qualifiers = [ claimInfo.claimQualifier ];
} else if (claimInfo.claimQualifiers && claimInfo.claimQualifiers !== false) {
qualifiers = claimInfo.claimQualifiers;
}
if (qualifiers.length > 0){
claim.qualifiers = {};
claim["qualifiers-order"] = [];
qualifiers.forEach((qualifier) => {
if (!claim.qualifiers.hasOwnProperty(qualifier.prop)){
claim.qualifiers[qualifier.prop] = [];
}
if (!claim["qualifiers-order"].includes(qualifier.prop)){
claim["qualifiers-order"].push(qualifier.prop);
}
claim.qualifiers[qualifier.prop].push(PropertyCreator.util.makeSnak(qualifier.prop, qualifier.value));
});
}
const proposalDiscussion = PropertyCreator.elements.proposalDiscussion.getValue();
if (claimInfo.addRef && proposalDiscussion){
const refProp = PropertyCreator.config.wikimediaImportURLProp;
claim.references = [{
"snaks": {
},
"snaks-order": [refProp]
}];
claim.references[0].snaks[refProp] = [PropertyCreator.util.makeSnak(refProp, proposalDiscussion)];
}
return claim;
};
PropertyCreator.util.addClaim = function ( claimInfo ) {
// claimInfo is an object with `claimEntity`, `claimProperty`, and `claimValue` all set
// as well as `addRef` for whether the discussion should be reference
// claimQualifier can be false, or an object with a `prop` and `value`,
// or claimQualifiers can be an array of those objects
console.log( 'Converting claim value to snak, started as ' + claimInfo.claimValue );
console.log(
'Adding claim to ' + claimInfo.claimEntity +
': property ' + claimInfo.claimProperty +
' has value ' + claimInfo.claimValue
);
return new Promise( function ( resolve ) {
new mw.Api().postWithEditToken( {
action: 'wbsetclaim',
claim: JSON.stringify(PropertyCreator.util.serializeClaim(claimInfo)),
assert: 'user',
assertuser: mw.config.get( 'wgUserName' )
} ).then(
function ( response ) {
console.log( response ); // Good result
resolve();
},
PropertyCreator.onErrHandler
);
} );
};
PropertyCreator.createProperty = function ( labelValue, descriptionValue, dataType ) {
var propertyData = {
'labels': { 'en': { 'language': 'en', 'value': labelValue } },
'descriptions': { 'en': { 'language': 'en', 'value': descriptionValue } },
'datatype': dataType,
'claims': [
...PropertyCreator.getInstanceOfData(),
PropertyCreator.documentSubjectItem(),
PropertyCreator.documentDiscussion(),
PropertyCreator.documentSourceWebsite(),
PropertyCreator.documentExpectedCompleteness(),
PropertyCreator.documentStability(),
PropertyCreator.documentFormatterURL(),
PropertyCreator.addRegex(),
PropertyCreator.addRegexConstraint(),
PropertyCreator.addScopeConstraint(),
PropertyCreator.addEntityTypeConstraint(),
PropertyCreator.maybeAddDistinctValuesConstraint(),
PropertyCreator.maybeAddSingleValueConstraint(),
PropertyCreator.maybeAddSubjectTypeConstraint()
].filter(item => item !== null && item !== undefined)
};
var propertyDataStr = JSON.stringify( propertyData );
console.log( propertyData, propertyDataStr );
new mw.Api().postWithEditToken( {
'action': 'wbeditentity',
'new': 'property',
'summary': PropertyCreator.config.createSummary,
'data': propertyDataStr,
'assert': 'user',
'assertuser': mw.config.get( 'wgUserName' )
} ).then(
function ( response ) {
console.log( response ); // Good result
var propertyId = response.entity.id;
// No need to wait for this to be done
PropertyCreator.createPropertyTalk( propertyId );
PropertyCreator.addPropertyExamplesInit( propertyId );
},
PropertyCreator.onErrHandler
);
};
PropertyCreator.createPropertyTalk = function ( propId ) {
new mw.Api().postWithEditToken( {
action: 'edit',
title: 'Property talk:' + propId,
text: '{{Property documentation}}',
summary: 'Create with {{Property documentation}}' + PropertyCreator.config.attribution,
assert: 'user',
assertuser: mw.config.get( 'wgUserName' )
} ).then(
function ( response ) {
console.log( response ); // Good result
},
PropertyCreator.onErrHandler
);
};
PropertyCreator.getInstanceOfData = function () {
var instanceOfValues = PropertyCreator.elements.instanceOf.getValue().split("|");
return instanceOfValues.map( instanceOfValue => PropertyCreator.util.serializeClaim({
claimProperty: PropertyCreator.config.instanceOfProp,
claimValue: instanceOfValue,
addRef: true
}));
};
PropertyCreator.addPropertyExamplesInit = function ( propId ) {
console.log( 'Adding property examples for new property with id ' + propId );
var examplesStr = PropertyCreator.elements.examplesText.getValue();
var examples = examplesStr.split("\n");
PropertyCreator.addPropertyExamplesRecursive( propId, examples );
};
PropertyCreator.addPropertyExamplesRecursive = function ( propertyId, examples ) {
var currentExample = examples.pop();
var exampleParams = currentExample.split('|');
var exampleItem = exampleParams[0];
var exampleValue = exampleParams[1];
PropertyCreator.addPropertyExample( exampleItem, propertyId, exampleValue ).then( function () {
PropertyCreator.documentPropertyExample( exampleItem, propertyId, exampleValue ).then( function () {
if ( examples.length !== 0 ) {
PropertyCreator.addPropertyExamplesRecursive( propertyId, examples );
} else {
// Continue to the next step
PropertyCreator.documentSubjectProperty( propertyId );
}
} );
} );
};
PropertyCreator.addPropertyExample = function ( item, propId, value ) {
console.log( 'Setting examples: item ' + item + ' has property value ' + value );
// THIS ONLY WORKS FOR EXTERNAL LINKS FOR NOW (probably)
return PropertyCreator.util.addClaim( {
claimEntity: item,
claimProperty: propId,
claimValue: value,
addRef: true
} );
};
PropertyCreator.documentPropertyExample = function ( item, propId, value ) {
console.log( 'Document examples: item ' + item + ' has property value ' + value );
// THIS ONLY WORKS FOR EXTERNAL LINKS FOR NOW (probably)
return PropertyCreator.util.addClaim( {
claimEntity: propId,
claimProperty: PropertyCreator.config.propertyExampleProp,
claimValue: item,
claimQualifier: {
prop: propId,
value: value
},
addRef: true
} );
};
PropertyCreator.documentSubjectItem = function () {
// Link property to subject item
subjectItemValue = PropertyCreator.elements.subjectItem.getValue();
return PropertyCreator.util.serializeClaim( {
claimProperty: PropertyCreator.config.subjectItemProp,
claimValue: subjectItemValue,
addRef: true
} );
};
PropertyCreator.documentSubjectProperty = function ( propId ) {
// Link subject item back to property
subjectItemValue = PropertyCreator.elements.subjectItem.getValue();
PropertyCreator.util.addClaim( {
claimEntity: subjectItemValue,
claimProperty: PropertyCreator.config.wikidataPropertyProp,
claimValue: propId,
addRef: true
} ).then(
() => PropertyCreator.done(propId),
PropertyCreator.onErrHandler
);
};
PropertyCreator.documentDiscussion = function () {
return PropertyCreator.util.serializeClaim( {
claimProperty: PropertyCreator.config.propertyProposalDiscussionProp,
claimValue: PropertyCreator.elements.proposalDiscussion.getValue(),
addRef: false
} );
};
PropertyCreator.documentSourceWebsite = function () {
var sourceWebsite = PropertyCreator.elements.sourceWebsite.getValue();
if ( sourceWebsite === '' ) {
// No source website set, move on
console.log( 'Skipping source website handling, no source website set set' );
return;
}
var sourceWebsiteLang = PropertyCreator.elements.sourceWebsiteLang.getValue();
var langQualifier = {
prop: PropertyCreator.config.languageOfWorkProp,
value: sourceWebsiteLang
};
// empty value = omit qualifier, util.addClaim() ignores false value
if ( sourceWebsiteLang === "" ) {
langQualifier = false;
}
return PropertyCreator.util.serializeClaim( {
claimProperty: PropertyCreator.config.sourceWebsiteProp,
claimValue: sourceWebsite,
claimQualifier: langQualifier,
addRef: true
} );
};
PropertyCreator.documentExpectedCompleteness = function () {
if ( PropertyCreator.elements.expectedCompleteness.getValue() === 'unknown' ) {
console.log( 'Skipping expected completeness, not listed' );
return;
}
var expectedCompletenessValue;
if ( PropertyCreator.elements.expectedCompleteness.getValue() === 'alwaysIncomplete' ) {
expectedCompletenessValue = PropertyCreator.config.completenessAlwaysIncomplete;
} else {
expectedCompletenessValue = PropertyCreator.config.completenessEventuallyComplete;
}
return PropertyCreator.util.serializeClaim( {
claimProperty: PropertyCreator.config.completenessProp,
claimValue: expectedCompletenessValue,
addRef: true
} );
};
PropertyCreator.getStabilityValue = function ( input ) {
switch ( input ) {
case 'neverChanges':
return PropertyCreator.config.stabilityNeverChanges;
case 'sometimesChanges':
return PropertyCreator.config.stabilitySometimesChanges;
case 'valuesCanBeAdded':
return PropertyCreator.config.stabilityValuesCanBeAdded;
case 'continuouslyChanges':
return PropertyCreator.config.stabilityContinuouslyChanges;
case 'unknown':
return false;
default:
console.log( 'Invalid expected stability: ' + input );
return false;
}
};
PropertyCreator.documentStability = function () {
var stabilityValue = PropertyCreator.getStabilityValue(
PropertyCreator.elements.stability.getValue()
);
if ( stabilityValue === false ) {
console.log( 'Skipping value stability, not listed' );
return;
}
return PropertyCreator.util.serializeClaim( {
claimProperty: PropertyCreator.config.stabilityProp,
claimValue: stabilityValue,
addRef: true
} );
};
PropertyCreator.documentFormatterURL = function () {
var formatterURL = PropertyCreator.elements.formatterURL.getValue();
var formatterURLLang = PropertyCreator.elements.formatterURLLang.getValue();
var langQualifier = {
prop: PropertyCreator.config.languageOfWorkProp,
value: formatterURLLang
};
// empty value = omit qualifier, util.addClaim() ignores false value
if ( formatterURLLang === "" ) {
langQualifier = false;
}
return PropertyCreator.util.serializeClaim( {
claimProperty: PropertyCreator.config.formatterURLProp,
claimValue: formatterURL,
claimQualifier: langQualifier,
addRef: true
} );
};
PropertyCreator.addRegex = function () {
// Adds "format as a regular expression" and calls addRegexConstraint
var propertyRegex = PropertyCreator.elements.valueRegex.getValue();
if ( propertyRegex === '' ) {
// No regex set, move on
console.log( 'Skipping regex handling, no regex set' );
return;
}
console.log( 'Adding regex: ' + propertyRegex );
return PropertyCreator.util.serializeClaim( {
claimProperty: PropertyCreator.config.regexFormatProperty,
claimValue: propertyRegex,
addRef: true
} );
};
PropertyCreator.addRegexConstraint = function () {
var propertyRegex = PropertyCreator.elements.valueRegex.getValue();
if ( propertyRegex === '' ) {
// No regex set, move on
return;
}
var cfg = PropertyCreator.config;
return PropertyCreator.util.serializeClaim( {
claimProperty: cfg.constraintPropertyProp,
claimValue: cfg.regexFormatConstraint,
claimQualifier: {
prop: cfg.regexFormatProperty,
value: propertyRegex
},
addRef: true
} );
};
PropertyCreator.addScopeConstraint = function ( ) {
var cfg = PropertyCreator.config;
var scopeConstraintValue;
if ( PropertyCreator.elements.propertyScope.getValue() === 'asMain' ) {
// Only option for now
scopeConstraintValue = cfg.propertyScopeAsMain;
}
return PropertyCreator.util.serializeClaim( {
claimProperty: cfg.constraintPropertyProp,
claimValue: cfg.propertyScopeConstraint,
claimQualifier: {
prop: cfg.propertyScopeConstraintProp,
value: scopeConstraintValue
},
addRef: true
} );
};
PropertyCreator.addEntityTypeConstraint = function () {
var cfg = PropertyCreator.config;
var typeConstraintValue;
if ( PropertyCreator.elements.allowedEntityType.getValue() === 'typeItem' ) {
// Only option for now
typeConstraintValue = cfg.entityTypeItem;
}
return PropertyCreator.util.serializeClaim( {
claimProperty: cfg.constraintPropertyProp,
claimValue: cfg.allowedEntityTypesConstraint,
claimQualifier: {
prop: cfg.itemOfPropertyConstraint,
value: typeConstraintValue
},
addRef: true
} );
};
PropertyCreator.maybeAddDistinctValuesConstraint = function () {
if ( PropertyCreator.elements.distinctValuesConstraint.isSelected() === false ) {
// Skip
return;
}
var cfg = PropertyCreator.config;
return PropertyCreator.util.serializeClaim( {
claimProperty: cfg.constraintPropertyProp,
claimValue: cfg.distinctValuesConstraint,
addRef: PropertyCreator.elements.distinctValuesConstraintRef.isSelected()
} );
};
PropertyCreator.maybeAddSingleValueConstraint = function () {
if ( PropertyCreator.elements.singleValueConstraint.isSelected() === false ) {
// Skip
return;
}
var cfg = PropertyCreator.config;
return PropertyCreator.util.serializeClaim( {
claimProperty: cfg.constraintPropertyProp,
claimValue: cfg.singleValueConstraint,
addRef: PropertyCreator.elements.singleValueConstraintRef.isSelected()
} );
// TODO "domain" / "value type constraint"
};
PropertyCreator.maybeAddSubjectTypeConstraint = function () {
var subjectTypeString = PropertyCreator.elements.subjectType.getValue();
if ( subjectTypeString === '' ) {
// No subject type set, move on
console.log( 'Skipping subject type handling, no subject set' );
return;
}
var subjectTypes = subjectTypeString.split("|");
var cfg = PropertyCreator.config;
return PropertyCreator.util.serializeClaim( {
claimProperty: cfg.constraintPropertyProp,
claimValue: cfg.subjectTypeConstraint,
claimQualifiers: [
...subjectTypes.map((subjectType) => { return { prop: cfg.classForPropertyConstraint, value: subjectType } }),
// instance of:
{ prop: cfg.relationOfPropertyConstraint, value: cfg.instanceOfRelation }
],
addRef: true
} );
};
PropertyCreator.addDiscussionReference = function ( claimNeedingReference ) {
console.log( 'Documenting the discussion as the reference for a claim' );
var reference = {
referenceProp: [ {
'snaktype': 'value',
'property': PropertyCreator.config.wikimediaImportURLProp,
'datavalue': {
'type': 'string',
'value': PropertyCreator.elements.proposalDiscussion.getValue()
}
} ]
};
var referenceStr = JSON.stringify( reference );
console.log( 'Documenting the discussion as a reference for the claim', claimNeedingReference, reference, referenceStr );
return new mw.Api().postWithEditToken( {
action: 'wbsetreference',
statement: claimNeedingReference,
snaks: referenceStr,
assert: 'user',
assertuser: mw.config.get( 'wgUserName' )
} );
};
PropertyCreator.elements = {};
PropertyCreator.createElements = function () {
var e = {};
// WIP shortcut: loading from the wikitext of a proposal discussion
e.proposalDiscussionPage = new OO.ui.TextInputWidget();
e.proposalDiscussionPageLayout = new OO.ui.FieldLayout(
e.proposalDiscussionPage,
{ label: 'Proposal discussion page (just the subpage title)' }
);
e.preloadButton = new OO.ui.ButtonInputWidget( {
label: 'Preload',
flags: [
'primary',
'progressive'
]
} );
e.preloadButtonLayout = new OO.ui.FieldLayout( e.preloadButton );
e.pLabel = new OO.ui.TextInputWidget();
e.pLabelLayout = new OO.ui.FieldLayout( e.pLabel, { label: 'Property label' } );
e.pDescription = new OO.ui.TextInputWidget();
e.pDescriptionLayout = new OO.ui.FieldLayout( e.pDescription, { label: 'Property description' } );
var validDataTypes = [ 'external-id' ];
var dataTypeOptions = validDataTypes.map(
function ( type ) {
return { data: type, label: type };
}
);
e.pDataTypes = new OO.ui.DropdownInputWidget( { options: dataTypeOptions } );
e.pDataTypesLayout = new OO.ui.FieldLayout( e.pDataTypes, { label: 'Data type' } );
e.proposalDiscussion = new OO.ui.TextInputWidget();
e.proposalDiscussionLayout = new OO.ui.FieldLayout( e.proposalDiscussion, { label: 'Proposal discussion' } );
e.instanceOf = new OO.ui.TextInputWidget();
e.instanceOfLayout = new OO.ui.FieldLayout( e.instanceOf, { label: 'Instance of (Q19847637 = Wikidata property for an identifier)' } );
e.allowedEntityType = new OO.ui.RadioSelectInputWidget( {
options: [
{ data: 'typeItem', label: 'Wikibase item' }
]
} );
e.allowedEntityTypeLayout = new OO.ui.FieldLayout( e.allowedEntityType, { label: 'Allowed entity type' } );
e.subjectItem = new OO.ui.TextInputWidget();
e.subjectItemLayout = new OO.ui.FieldLayout( e.subjectItem, { label: 'Subject item for this property' } );
e.subjectType = new OO.ui.TextInputWidget();
e.subjectTypeLayout = new OO.ui.FieldLayout( e.subjectType, { label: 'Subject type (domain) of this property' } );
e.sourceWebsite = new OO.ui.TextInputWidget();
e.sourceWebsiteLayout = new OO.ui.FieldLayout( e.sourceWebsite, { label: 'Source website for this property' } );
e.sourceWebsiteLang = new OO.ui.TextInputWidget();
e.sourceWebsiteLangLayout = new OO.ui.FieldLayout( e.sourceWebsiteLang, { label: 'Source website language (Q1860 = English, omit = skip)' } );
e.propertyScope = new OO.ui.RadioSelectInputWidget( {
options: [
{ data: 'asMain', label: 'As main value' }
]
} );
e.propertyScopeLayout = new OO.ui.FieldLayout( e.propertyScope, { label: 'Property scope' } );
e.expectedCompleteness = new OO.ui.RadioSelectInputWidget( {
options: [
{ data: 'alwaysIncomplete', label: 'Always incomplete' },
{ data: 'eventuallyComplete', label: 'Eventually complete' },
{ data: 'unknown', label: 'Unknown (skip)' }
]
} );
e.expectedCompletenessLayout = new OO.ui.FieldLayout( e.expectedCompleteness, { label: 'Expected completeness' } );
e.stability = new OO.ui.RadioSelectInputWidget( {
options: [
{ data: 'neverChanges', label: 'Never changes' },
{ data: 'sometimesChanges', label: 'Sometimes changes' },
{ data: 'valuesCanBeAdded', label: 'Values can be added' },
{ data: 'continuouslyChanges', label: 'Continuously changes' },
{ data: 'unknown', label: 'Unknown (skip)' }
]
} );
// default to unknown
e.stability.setValue( 'unknown' );
e.stabilityLayout = new OO.ui.FieldLayout( e.stability, { label: 'Stability of property values' } );
e.examplesText = new OO.ui.MultilineTextInputWidget( { rows: 7 } );
e.examplesTextLayout = new OO.ui.FieldLayout(
e.examplesText,
{ label: 'Examples (one per line, separate item and value with |)' }
);
e.formatterURL = new OO.ui.TextInputWidget();
e.formatterURLLayout = new OO.ui.FieldLayout( e.formatterURL, { label: 'Formatter URL' } );
e.formatterURLLang = new OO.ui.TextInputWidget();
e.formatterURLLangLayout = new OO.ui.FieldLayout( e.formatterURLLang, { label: 'Formatter URL language (Q1860 = English, omit = skip)' } );
e.valueRegex = new OO.ui.TextInputWidget();
e.valueRegexLayout = new OO.ui.FieldLayout( e.valueRegex, { label: 'Regex for values' } );
e.distinctValuesConstraint = new OO.ui.CheckboxInputWidget();
e.distinctValuesConstraintLayout = new OO.ui.FieldLayout( e.distinctValuesConstraint, { label: 'Distinct values constraint' } );
e.distinctValuesConstraintRef = new OO.ui.CheckboxInputWidget();
e.distinctValuesConstraintRefLayout = new OO.ui.FieldLayout( e.distinctValuesConstraintRef, { label: 'Distinct values constraint - reference' } );
e.singleValueConstraint = new OO.ui.CheckboxInputWidget();
e.singleValueConstraintLayout = new OO.ui.FieldLayout( e.singleValueConstraint, { label: 'Single value constraint' } );
e.singleValueConstraintRef = new OO.ui.CheckboxInputWidget();
e.singleValueConstraintRefLayout = new OO.ui.FieldLayout( e.singleValueConstraintRef, { label: 'Single value constraint - reference' } );
e.submitButton = new OO.ui.ButtonInputWidget( {
label: 'Create',
flags: [
'primary',
'progressive'
]
} );
e.submitButtonLayout = new OO.ui.FieldLayout( e.submitButton );
PropertyCreator.elements = e;
};
});
mw.loader.using(
[ 'mediawiki.util', 'mediawiki.api', 'oojs-ui-widgets', 'oojs-ui-windows' ],
function () {
$( document ).ready( () => {
if (
mw.config.get( 'wgNamespaceNumber' ) === -1 &&
mw.config.get( 'wgTitle' ) === 'BlankPage/PropertyCreator'
) {
window.PropertyCreator.init();
} else {
var urlParams = '';
if ( mw.config.get( 'wgNamespaceNumber' ) === 4 &&
mw.config.get( 'wgTitle').startsWith( 'Property proposal/' )
) {
urlParams += '?forDiscussion=';
urlParams += mw.util.rawurlencode(
mw.config.get( 'wgTitle').replace( 'Property proposal/', '' )
);
}
mw.util.addPortletLink(
'p-tb',
'/wiki/Special:BlankPage/PropertyCreator' + urlParams,
'PropertyCreator helper form'
);
}
} );
}
);
// </nowiki>