MediaWiki:Gadget-WmfProjectStatusHelper.js
Versie door Ahenket (overleg | bijdragen) op 19 jul 2013 om 18:14 (Nieuwe pagina aangemaakt met '→Project Status Helper rmoen@wikimedia.org psh.js v1.0 TODO: Conflict handling: Edit, Replace, Cleanup: var testTxt = ''; ( function( mw, $ ) { /* Pr...')
Opmerking: nadat u de wijzigingen hebt opgeslagen is het wellicht nodig uw browsercache te legen.
- Firefox / Safari: houd Shift ingedrukt terwijl u op Vernieuwen klikt of druk op Ctrl-F5 of Ctrl-R (⌘-Shift-R op een Mac)
- Google Chrome: druk op Ctrl-Shift-R (⌘-Shift-R op een Mac)
- Internet Explorer: houd Ctrl ingedrukt terwijl u op Vernieuwen klikt of druk op Ctrl-F5
- Opera: ga naar Menu → Instellingen (Opera → Voorkeuren op een Mac) en daarna naar Privacy & beveiliging → Browsegegevens wissen... → Tijdelijk opgeslagen afbeeldingen en bestanden.
/*
Project Status Helper
rmoen@wikimedia.org
psh.js v1.0
TODO: Conflict handling:
Edit, Replace, Cleanup
*/
var testTxt = '';
( function( mw, $ ) {
/* Project status helper class */
Psh = function( project ) {
this.project = {
date: '',
name: '',
desc: ''
};
if( project !== undefined && project.date !== undefined ) {
this.project.date = project.date;
} else {
this.project.date = ( new Date() ).toJSON().substring( 0, 10 );
}
if( project !== undefined && project.name !== undefined ) {
this.project.name = project.name;
} else {
this.project.name = mw.config.get( 'wgTitle' ).replace( '/status', '' );
}
var _this = this,
todayFormatted = this.project.date.replace( 'monthly', '28' ),
isMonthly = ( this.project.date.search( 'monthly' ) > -1 ),
$modalElements =
$( '<form>' )
.attr( 'id', 'projectStatusHelperForm' )
.append(
$( '<div>' )
.attr( 'class', 'mw-ajax-loader' )
.css({
position: 'absolute',
width: '100%',
height: '100%',
top: '0px',
left: '0px'
})
.hide()
).append(
$( '<div>' ).attr( 'id', 'projectStatusHelperInput' )
.append(
$( '<div>' )
.append(
$( '<label>' )
.attr( 'for', 'projectStatusName' )
.text( 'Project Name:' )
).append(
$( '<br>' ).css( 'clear', 'both' )
).append(
$( '<select>' )
.attr({
name: 'projectStatusName',
id: 'projectStatusName'
})
.bind( 'change', function() {
_this.updateStatusLink();
}).append(
$( '<option>' )
.attr( 'value', '' )
.text( '- Please Select Project -' )
)
)
).append(
$( '<div>' )
.append(
$( '<span>' )
.text( 'Project Link: ' )
).append(
$( '<a>' )
.attr({
id: 'projectLink',
target: '_blank'
}).css( 'text-decoration', 'underline' )
)
).append(
$( '<div>' )
.append(
$( '<label>' )
.attr( 'for', 'projectStatusDate' )
.text( 'Status Date:' )
).append(
$( '<br>' ).css({'clear': 'both'})
).append(
$( '<input>' )
.attr({
type: 'text',
name: 'projectStatusDate',
id: 'projectStatusDate'
})
.val( todayFormatted )
.bind( 'change', function() {
_this.fillDescriptionField();
})
).append(
$( '<small>' ).text( 'eg. YYYY-MM-DD' )
)
).append(
$( '<div>' )
.append(
$( '<label>' )
.attr( 'for', 'projectStatusMonthlyFlag' )
.text( 'Include in Monthly report?' )
).append(
$( '<input>' )
.attr({
type: 'checkbox',
name: 'projectStatusMonthlyFlag',
id: 'projectStatusMonthlyFlag'
})
.prop( 'checked', isMonthly )
.bind( 'change', function() {
_this.fillDescriptionField();
})
)
).append(
$( '<div>' )
.append(
$( '<label>' )
.attr( 'for', 'projectStatusUpdateDescription' )
.text( 'Description:' )
).append(
$( '<textarea>' )
.attr({
name: 'projectStatusUpdateDescription',
id: 'projectStatusUpdateDescription',
style: 'width:98.5% !important'
}).css({
padding: '5px',
fontSize: '12px',
height: '130px'
// TODO: remove this after testing
}).html( testTxt )
)
)
).append(
$( '<div>' )
.attr( 'id', 'projectStatusPreview' )
.css({
backgroundColor: '#FDFFE7',
border: '1px solid #FCEB92',
padding: '5px',
height: '310px',
overflowY: 'scroll',
marginBottom: '5px'
}).hide()
)/*
.append(
$('<button>')
.attr({'id': 'projectStatusBackBtn'})
.text( 'Back' )
.click( function( e ) {
e.preventDefault();
_this.back();
}).hide()
).append(
$( '<button>' )
.attr({'id': 'projectStatusPreviewBtn'})
.text( 'Preview' )
.click( function( e ) {
e.preventDefault();
_this.preview();
})
).append(
$( '<button>' )
.attr({'id': ''})
.text( 'Publish' )
.click( function( e ) {
e.preventDefault();
_this.publish();
})
)*/;
this.uneditedDescription = '';
var projectPages = [
'Wikimedia_Features_engineering',
'Wikimedia_Platform_Engineering',
'Wikimedia_Mobile_engineering',
'Wikimedia_Language_engineering'
];
//{{Wikimedia project index line|TimedMediaHandler}}
var lookFor = '{{Wikimedia project index line|';
// Testing getPages
this.getPages( projectPages, function( pages ) {
for ( var page in pages ) {
_this.scrubPage(
pages[page],
lookFor,
buildProjectsObject
);
}
_this.updateStatusLink();
_this.fillDescriptionField();
} );
function buildProjectsObject( title, names ) {
var $projectSel = $modalElements.find( '#projectStatusName' );
var $projectGroup = $( '<optgroup>' ).attr( 'label', title );
var name;
for ( var i = 0; i < names.length; i++ ) {
name = names[i]
.replace( lookFor, '' )
.replace( '}}', '' );
$projectGroup
.append(
$('<option>')
.attr( 'value', name )
.prop( 'selected', name === _this.project.name )
.text( name )
);
}
$projectSel.append( $projectGroup );
}
$( '#psh-dialog' ).remove();
// add to the DOM
$( 'body' ).append(
$( '<div>' ).attr({
id: 'psh-dialog',
title: 'Project Status Helper'
})
.append(
$modalElements
)
.hide()
);
mw.loader.using( ['jquery.ui.dialog', 'jquery.ui.datepicker'], function() {
$( '#psh-dialog' ).dialog({
height: 'auto',
width: 800,
modal: true,
buttons: [
{
id: 'projectStatusBackBtn',
text: 'Back',
click: function() {
_this.back();
}
},
{
id: 'projectStatusPreviewBtn',
text: 'Preview',
click: function() {
_this.preview();
}
},
{
id: 'projectStatusPublishBtn',
text: 'Publish',
click: function() {
_this.publish();
}
}
]
});
// hide preview back button
$( '#projectStatusBackBtn' ).hide();
// datepicker that
$( '#projectStatusDate' )
.datepicker({'dateFormat': 'yy-mm-dd'});
});
this.$modal = $( '#psh-dialog' );
return this;
};
Psh.prototype.scrubPage = function( page, lookFor, callback ) {
var lines, i;
var found = [];
var content = page.revisions[0]['*'];
var title = page.title;
if ( content.length > 0 ) {
lines = content.split( /\n/ );
// Loop through lines
for ( i = 0; i < lines.length; i++ ) {
if ( lines[i].indexOf( lookFor ) !== -1 ) {
found.push(
lines[i]
);
}
}
callback( title, found );
}
};
Psh.prototype.addStatus = function() {
var _this = this,
summary = 'new status update';
// Send the wikitext to the parser for rendering
$.ajax({
url: mw.util.wikiScript( 'api' ),
data: {
action: 'edit',
// Project page name...
title: this.pageName,
//appendtext: this.wikitext,
text: this.wikitext,
token: mw.user.tokens.get( 'editToken' ),
format: 'json',
summary: summary,
notminor: true
},
dataType: 'json',
type: 'POST',
success: function( data ) {
if ( data && data.edit && data.edit.result === 'Success' ) {
_this.hideSpinner();
window.location.reload( true );
}
},
error: function() {}
});
};
Psh.prototype.getPages = function( titles, callback ) {
var pagesQuery = $.isArray( titles ) ? titles.join( '|' ) : titles;
$.ajax({
url: mw.util.wikiScript( 'api' ),
data: {
action: 'query',
format: 'json',
titles: pagesQuery,
prop: 'revisions',
rvprop: 'content'
},
dataType: 'json',
type: 'GET',
cache: 'false',
success: function( data ) {
if ( data && data.query && data.query.pages ) {
// return pages to callback
if( typeof callback === 'function' ) {
callback( data.query.pages );
}
}
},
error: function() {
if( typeof callback === 'function' ) {
callback( {} );
}
}
});
};
/* quick and dirty validation form */
Psh.prototype.validate = function() {
for ( var prop in this.project ) {
if ( this.project[prop] === '' ) {
return false;
}
}
return true;
};
Psh.prototype.fillDescriptionField = function() {
var _this = this;
var $descField = _this.$modal.find( '#projectStatusUpdateDescription' );
var currentdesc = $descField.val();
if ( this.uneditedDescription != currentdesc && currentdesc != '' ) {
if ( !confirm( 'Replace existing entry with content from date (if available)?' ) ) {
return false;
}
}
this.setupProject();
if ( this.project.name == '' ) {
_this.pageContent = '';
return false;
}
this.getPages( this.pageName, function( pages ) {
_this.fillStatusPageContent( pages );
var splitEntry = _this.getStatusPageSplitAtDate( _this.project.date );
var desc = _this.stripEntry( splitEntry['middle'] );
$descField.val( desc );
_this.uneditedDescription = desc;
});
};
Psh.prototype.stripEntry = function( content ) {
// strip the title
content = content.replace( /^== *([^= ]*) *==\n\n?/m, '' );
// ...and the begin and end tags
content = content.replace( /^\s*<section[\s]+begin[^>]*\/>\s*\n?/mg, '' );
content = content.replace( /<section[\s]+end[^>]*\/>\s*\n?$/mg, '' );
return content;
};
Psh.prototype.fillStatusPageContent = function( pages ) {
// use only first page in pages object.
for ( var page in pages ) {
// if page exists the title will be in the returned object
if ( pages[page].hasOwnProperty( 'revisions' ) ) {
// save wikitext, if page empty pageContent will be ''
this.pageContent = pages[page].revisions[0]['*'];
} else {
// page does not exist
this.pageContent = '';
}
break;
}
};
/* query for page */
Psh.prototype.publish = function() {
var _this = this;
this.showSpinner();
this.setupProject();
//console.log( this.project );
if ( this.validate() === false ) {
alert( 'Project name, date, and description are required.' );
this.hideSpinner();
return ;
}
this.getPages( this.pageName, function( pages ) {
_this.fillStatusPageContent( pages );
_this.prepareContentAndUpdate();
});
};
Psh.prototype.setupProject = function() {
var $helperForm = this.$modal.find( '#projectStatusHelperForm' );
this.project = {
date: $helperForm.find( '#projectStatusDate' ).val(),
name: $helperForm.find( '#projectStatusName' ).val(),
monthly: $helperForm.find( '#projectStatusMonthlyFlag' ).prop( 'checked' ),
desc: $helperForm.find( '#projectStatusUpdateDescription' ).val()
};
// if monthly status update
if ( this.project.monthly ) {
this.project.date = this.project.date.substring( 0, 8 ) + 'monthly';
}
this.project.latestUpdate = 'Last update on: <section begin="latest"/>' +
this.project.date + '<section end="latest"/>';
this.wikitext = '';
};
Psh.prototype.getStatusPageSplitAtDate = function( splitDate ) {
var statusLines = [],
lookFor = '';
// for replacement, gather text before and after entry
var splitEntry = {
'firstline': '',
'before': '',
'middle': '',
'after': ''
};
if ( this.pageContent.length > 0 ) {
statusLines = this.pageContent.split( /\n/ );
splitEntry['firstline'] = statusLines[0] + '\n';
var datematch = null;
var splitState = 'before';
// skip the first line, populate 'before', 'middle', and 'after'
for ( var i = 1; i < statusLines.length; i++ ) {
datematch = statusLines[i].match( /^== *([^= ]*) *==$/ );
if ( datematch != null ) {
if ( datematch[1] == splitDate ) {
splitState = 'middle';
} else if ( splitState == 'middle' ) {
// we must be starting a new section
splitState = 'after';
}
}
splitEntry[splitState] += statusLines[i] + '\n';
}
}
return splitEntry;
};
/**
* replace this.wikitext with new version containing latest update,
* then push it to the wiki.
*/
Psh.prototype.prepareContentAndUpdate = function() {
var splitEntry = this.getStatusPageSplitAtDate( this.project.date );
if( splitEntry['middle'] != '' ) {
if ( confirm( 'Replace existing entry?' ) ) {
splitstate = 'middle';
} else {
//console.log( 'abort' );
this.hideSpinner();
return false;
}
}
this.wikitext = this.project.latestUpdate + '\n';
this.wikitext += splitEntry['before'];
if( splitEntry['middle'] == '' ) {
this.wikitext = this.wikitext.replace( /\n*$/m, '\n\n' );
}
this.wikitext += this.buildWikitext();
this.wikitext += splitEntry['after'];
this.addStatus();
return true;
};
Psh.prototype.buildWikitext = function() {
var retval = '';
retval = '== ' + this.project.date + ' ==\n\n';
retval += '<section begin="'+ this.project.date + '"/>' +
this.project.desc + '<section end="' + this.project.date + '"/>';
retval += '\n\n';
return retval;
};
Psh.prototype.preview = function() {
var _this = this;
$( '#projectStatusPreviewBtn' ).hide();
$( '#projectStatusBackBtn' ).show();
this.showSpinner();
this.setupProject();
wikitext = this.buildWikitext();
// Send the wikitext to the parser for rendering
$.ajax({
url: mw.util.wikiScript( 'api' ),
data: {
'action': 'parse',
'title': this.pageName,
'format': 'json',
'text': wikitext,
'prop': 'text',
'pst': true
},
dataType: 'json',
type: 'POST',
success: function ( data ) {
_this.$modal.find( '#projectStatusPreview' )
.html( data.parse.text['*'] )
.show()
.prev()
.hide();
_this.$modal.find( '.editsection' ).remove();
_this.hideSpinner();
},
error: function() {}
});
};
Psh.prototype.back = function() {
$( '#projectStatusHelperInput, #projectStatusPreviewBtn' ).show();
$( '#projectStatusPreview, #projectStatusBackBtn' ).hide();
};
Psh.prototype.showSpinner = function() {
this.$modal
.find( '.mw-ajax-loader' )
.show();
};
Psh.prototype.hideSpinner = function() {
this.$modal
.find( '.mw-ajax-loader' )
.hide();
};
Psh.prototype.updateStatusLink = function() {
var path = mw.config.get( 'wgServer' ) + mw.config.get( 'wgArticlePath' ),
$inputElement =
this.$modal
.find( '#projectStatusName' );
var selectedProject = $inputElement.val();
if( selectedProject != '' ) {
this.pageName = $inputElement.val() + '/status';
this.pageURL = path.replace( '$1', this.pageName );
$( '#projectLink' )
.attr( 'href', this.pageURL )
.text( this.pageName );
}
};
$( document ).ready( function() {
// Add a link to the toolbox
var link = mw.util.addPortletLink(
'p-tb',
'#',
'Project Status',
't-prettylinkwidget',
'Dialog to help you submit your project updates',
null,
'#t-projectstatushelper'
);
// Setup link click event
$( link ).click( function( e ) {
e.preventDefault();
var psh = new Psh();
$( '#projectStatusHelperInput div' ).css({
'margin-top': '10px'
});
});
// override edit links on project pages
$( '.mw-statushelper-editlink > a' ).each( function( i ) {
$( this ).click( function( event ) {
event.preventDefault();
var projectObj = {};
projectObj.name = $( '.mw-statushelper-editlink' )[i].getAttribute( 'data-statuspage' ).replace( '/status', '' );
projectObj.date = $( '.mw-statushelper-editlink' )[i].getAttribute( 'data-entrydate' );
var psh = new Psh( projectObj );
$( '#projectStatusHelperInput div' ).css({
'margin-top': '10px'
});
});
});
// override add links on project pages
$( '.mw-statushelper-addlink > a' ).each( function( i ) {
$( this ).click( function( event ) {
event.preventDefault();
var projectObj = {};
projectObj.name = $( '.mw-statushelper-addlink' )[i].getAttribute( 'data-statuspage' ).replace( '/status', '' );
var psh = new Psh( projectObj );
$( '#projectStatusHelperInput div' ).css({
'margin-top': '10px'
});
});
});
});
})( mediaWiki, jQuery );