Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/azatoth/twinkle into vi
Browse files Browse the repository at this point in the history
Conflicts:
	modules/friendlytag.js
	modules/twinklebatchdelete.js
	modules/twinklebatchundelete.js
	modules/twinkleimage.js
	modules/twinklespeedy.js
	modules/twinklewarn.js
	modules/twinklexfd.js
  • Loading branch information
1ec5 committed Jul 22, 2014
2 parents 2ad87f2 + ad45b14 commit e334e95
Show file tree
Hide file tree
Showing 14 changed files with 594 additions and 293 deletions.
41 changes: 28 additions & 13 deletions README.md
Expand Up @@ -9,26 +9,38 @@ See [Wikipedia:Twinkle][] on the English Wikipedia for more information.


[AzaToth][] is the original author and maintainer of the tool, as well as the `morebits.js` library. [AzaToth][] is the original author and maintainer of the tool, as well as the `morebits.js` library.


Layout of this repository
-------------------------

* `moment.js`: A copy of the [moment.js][] JavaScript library, version 2.4.0. As of July 2014, it is only used by the ARV module.
* `morebits.js`: The central library used by Twinkle and many other scripts. Contains code to interact with the MediaWiki API, display forms and dialogs, generate status logs, and do various other useful things. The vast majority of code in here is not Twinkle-specific.
* `morebits.css`: Styling to accompany `morebits.js`. The portlet styles relating to the Modern skin are Twinkle-specific and should arguably be in a `twinkle.css` file.
* `sync.pl`: A Perl script to update on-wiki gadgets, or update the repository based on on-wiki changes. See below for full documentation.
* `twinkle.js`: General Twinkle-specific code, mostly related to preferences and exposing Twinkle in the UI. Significantly, it contains the default set of preferences of Twinkle.
* `modules`: Contains the individual Twinkle modules. Descriptions for these can be found in header comments or in the [Twinkle documentation][]. The module `twinkleconfig.js` powers the [Twinkle preferences panel][WP:TWPREFS].

Other files not mentioned here are probably obsolete.

Updating scripts on Wikipedia Updating scripts on Wikipedia
----------------------------- -----------------------------


To generate the concatenated Twinkle script, use the following `bash` command: There are two ways to upload Twinkle scripts to Wikipedia or another destination.


awk 'FNR==1{print ""}{print}' twinkle.header.js modules/*.js twinkle.footer.js > alltwinkle.js ### Manual concatenation


Then you will be able to upload `alltwinkle.js` to [MediaWiki:Gadget-Twinkle.js][]. This does not include `morebits.js` and `morebits.css`; these have to be uploaded separately. To generate a concatenated Twinkle script, use the following `bash` command:


If `morebits.js` and/or `morebits.css` need to be updated, they should be synched to these places: awk 'FNR==1{print ""}{print}' twinkle.js modules/*.js > alltwinkle.js


* _morebits.js_ at [MediaWiki:Gadget-morebits.js][] and [User:AzaToth/morebits.js][] Then you will be able to upload `alltwinkle.js` to [MediaWiki:Gadget-Twinkle.js][]. The concatenation does not include `morebits.js` and `morebits.css`; these have to be uploaded separately.
* _morebits.css_ at [MediaWiki:Gadget-morebits.css][] and [User:AzaToth/morebits.css][]


[MediaWiki:Gadgets-definition][] should contain the following line: If `morebits.js` and/or `morebits.css` need to be updated, they should be synched to [MediaWiki:Gadget-morebits.js][] and [MediaWiki:Gadget-morebits.css][].

[MediaWiki:Gadgets-definition][] would then contain the following line:


* Twinkle[ResourceLoader|dependencies=jquery.ui.dialog,jquery.tipsy]|morebits.js|morebits.css|Twinkle.js * Twinkle[ResourceLoader|dependencies=jquery.ui.dialog,jquery.tipsy]|morebits.js|morebits.css|Twinkle.js


Synchronization (for developers) ### Synchronization using `sync.pl`
--------------------------------


There is a synchronization script called `sync.pl`, which can be used to pull and push files to Wikipedia. There is a synchronization script called `sync.pl`, which can be used to pull and push files to Wikipedia.


Expand All @@ -46,15 +58,15 @@ where `base` is the wiki path to prefix the files for `pull` and `push`.


Notice that your working directory **must** be clean; if not, either `stash` or `commit` your changes. Notice that your working directory **must** be clean; if not, either `stash` or `commit` your changes.


To `pull` user Foobar's changes, do: To `pull` user Foobar's changes (i.e. `User:Foobar/morebits.js`), do:


./sync.pl --base User:Foobar --pull morebits.js ./sync.pl --base User:Foobar --pull morebits.js


To `push` your changes to Foobar's wiki page, do: To `push` your changes to Foobar's wiki page, do:


./sync.pl --base User:Foobar --push morebits.js ./sync.pl --base User:Foobar --push morebits.js


There is also an `deploy` command to deploy the new files live. There is also a `deploy` command to deploy all Twinkle files live.


./sync.pl --deploy twinkle.js ./sync.pl --deploy twinkle.js
make deploy make deploy
Expand All @@ -68,14 +80,17 @@ While old legacy code has many different and incoherent styles, it has been deci


The [jQuery Core Style Guideline][jq_style] is what we will hereafter use as our style guideline. The [jQuery Core Style Guideline][jq_style] is what we will hereafter use as our style guideline.


Needless to say, there are exceptions. The main sticking point is spacing around parentheses. Older Twinkle code looks like `if ( condition ) {`, but newer code tends to use `if (condition) {`. The best convention here is to follow the style of surrounding code.

[Wikipedia:Twinkle]: https://en.wikipedia.org/wiki/Wikipedia:Twinkle [Wikipedia:Twinkle]: https://en.wikipedia.org/wiki/Wikipedia:Twinkle
[AzaToth]: https://en.wikipedia.org/wiki/User:AzaToth [AzaToth]: https://en.wikipedia.org/wiki/User:AzaToth
[moment.js]: http://momentjs.com/
[Twinkle documentation]: https://en.wikipedia.org/wiki/Wikipedia:Twinkle/doc
[WP:TWPREFS]: https://en.wikipedia.org/wiki/WP:TWPREFS
[MediaWiki:Gadget-Twinkle.js]: https://en.wikipedia.org/wiki/MediaWiki:Gadget-Twinkle.js [MediaWiki:Gadget-Twinkle.js]: https://en.wikipedia.org/wiki/MediaWiki:Gadget-Twinkle.js
[User:AzaToth/twinkle.js]: https://en.wikipedia.org/wiki/User:AzaToth/twinkle.js [User:AzaToth/twinkle.js]: https://en.wikipedia.org/wiki/User:AzaToth/twinkle.js
[MediaWiki:Gadget-morebits.js]: https://en.wikipedia.org/wiki/MediaWiki:Gadget-morebits.js [MediaWiki:Gadget-morebits.js]: https://en.wikipedia.org/wiki/MediaWiki:Gadget-morebits.js
[User:AzaToth/morebits.js]: https://en.wikipedia.org/wiki/User:AzaToth/morebits.js
[MediaWiki:Gadget-morebits.css]: https://en.wikipedia.org/wiki/MediaWiki:Gadget-morebits.css [MediaWiki:Gadget-morebits.css]: https://en.wikipedia.org/wiki/MediaWiki:Gadget-morebits.css
[User:AzaToth/morebits.css]: https://en.wikipedia.org/wiki/User:AzaToth/morebits.css
[MediaWiki:Gadgets-definition]: https://en.wikipedia.org/wiki/MediaWiki:Gadgets-definition [MediaWiki:Gadgets-definition]: https://en.wikipedia.org/wiki/MediaWiki:Gadgets-definition
[Git::Repository]: http://search.cpan.org/perldoc?Git%3A%3ARepository [Git::Repository]: http://search.cpan.org/perldoc?Git%3A%3ARepository
[MediaWiki::Bot]: http://search.cpan.org/perldoc?MediaWiki%3A%3ABot [MediaWiki::Bot]: http://search.cpan.org/perldoc?MediaWiki%3A%3ABot
Expand Down
8 changes: 8 additions & 0 deletions modules/friendlytag.js
Expand Up @@ -328,6 +328,7 @@ Twinkle.tag.updateSortOrder = function(e) {
{ label: "{{notability|Companies}}: notability guidelines for companies and organizations", value: "Companies" }, { label: "{{notability|Companies}}: notability guidelines for companies and organizations", value: "Companies" },
{ label: "{{notability|Events}}: notability guideline for events", value: "Events" }, { label: "{{notability|Events}}: notability guideline for events", value: "Events" },
{ label: "{{notability|Films}}: notability guideline for films", value: "Films" }, { label: "{{notability|Films}}: notability guideline for films", value: "Films" },
{ label: "{{notability|Places}}: notability guideline for places", value: "Places" },
{ label: "{{notability|Music}}: notability guideline for music", value: "Music" }, { label: "{{notability|Music}}: notability guideline for music", value: "Music" },
{ label: "{{notability|Neologisms}}: notability guideline for neologisms", value: "Neologisms" }, { label: "{{notability|Neologisms}}: notability guideline for neologisms", value: "Neologisms" },
{ label: "{{notability|Numbers}}: notability guideline for numbers", value: "Numbers" }, { label: "{{notability|Numbers}}: notability guideline for numbers", value: "Numbers" },
Expand Down Expand Up @@ -1132,6 +1133,13 @@ Twinkle.tag.callbacks = {
if (params.translationNotify) { if (params.translationNotify) {
pageobj.lookupCreator(function(innerPageobj) { pageobj.lookupCreator(function(innerPageobj) {
var initialContrib = innerPageobj.getCreator(); var initialContrib = innerPageobj.getCreator();

// Disallow warning yourself
if (initialContrib === mw.config.get('wgUserName')) {
innerPageobj.getStatusElement().warn("Bạn (" + initialContrib + ") đã tạo trang này; bỏ qua thông báo");
return;
}

var userTalkPage = new Morebits.wiki.page('Thảo luận Thành viên:' + initialContrib, var userTalkPage = new Morebits.wiki.page('Thảo luận Thành viên:' + initialContrib,
'Notifying initial contributor (' + initialContrib + ')'); 'Notifying initial contributor (' + initialContrib + ')');
var notifytext = "\n\n== Your article [[" + Morebits.pageNameNorm + "]]==\n" + var notifytext = "\n\n== Your article [[" + Morebits.pageNameNorm + "]]==\n" +
Expand Down
202 changes: 115 additions & 87 deletions modules/twinklebatchdelete.js
Expand Up @@ -22,7 +22,7 @@ Twinkle.batchdelete = function twinklebatchdelete() {


Twinkle.batchdelete.unlinkCache = {}; Twinkle.batchdelete.unlinkCache = {};
Twinkle.batchdelete.callback = function twinklebatchdeleteCallback() { Twinkle.batchdelete.callback = function twinklebatchdeleteCallback() {
var Window = new Morebits.simpleWindow( 800, 400 ); var Window = new Morebits.simpleWindow( 600, 400 );
Window.setTitle( "Xóa hàng loạt" ); Window.setTitle( "Xóa hàng loạt" );
Window.setScriptName( "Twinkle" ); Window.setScriptName( "Twinkle" );
Window.addFooterLink( "Trợ giúp Twinkle", "WP:TW/DOC#batchdelete" ); Window.addFooterLink( "Trợ giúp Twinkle", "WP:TW/DOC#batchdelete" );
Expand Down Expand Up @@ -52,9 +52,10 @@ Twinkle.batchdelete.callback = function twinklebatchdeleteCallback() {
] ]
} ); } );
form.append( { form.append( {
type: 'textarea', type: 'input',
name: 'reason', name: 'reason',
label: 'Reason: ' label: 'Reason: ',
size: 60
} ); } );


var query; var query;
Expand Down Expand Up @@ -103,17 +104,19 @@ Twinkle.batchdelete.callback = function twinklebatchdeleteCallback() {
'gapnamespace': gapnamespace , 'gapnamespace': gapnamespace ,
'gapprefix': gapprefix, 'gapprefix': gapprefix,
'gaplimit' : Twinkle.getPref('batchMax'), // the max for sysops 'gaplimit' : Twinkle.getPref('batchMax'), // the max for sysops
'prop' : ['categories', 'revisions' ], 'prop' : 'revisions|info',
'rvprop': [ 'size' ] 'inprop': 'protection',
'rvprop': 'size'
}; };
} else { } else {
query = { query = {
'action': 'query', 'action': 'query',
'generator': 'links', 'generator': 'links',
'titles': mw.config.get( 'wgPageName' ), 'titles': mw.config.get( 'wgPageName' ),
'gpllimit' : Twinkle.getPref('batchMax'), // the max for sysops 'gpllimit' : Twinkle.getPref('batchMax'), // the max for sysops
'prop': [ 'categories', 'revisions' ], 'prop': 'revisions|info',
'rvprop': [ 'size' ] 'inprop': 'protection',
'rvprop': 'size'
}; };
} }


Expand All @@ -124,27 +127,61 @@ Twinkle.batchdelete.callback = function twinklebatchdeleteCallback() {
Window.display(); Window.display();


var statelem = new Morebits.status("Grabbing list of pages"); var statelem = new Morebits.status("Grabbing list of pages");
var wikipedia_api = new Morebits.wiki.api( 'loading...', query, function( self ) { var wikipedia_api = new Morebits.wiki.api( 'loading...', query, function( apiobj ) {
var xmlDoc = self.responseXML; var xml = apiobj.responseXML;
var snapshot = xmlDoc.evaluate('//page[@ns != "6" and not(@missing)]', xmlDoc, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null ); // 6 = File: namespace var $pages = $(xml).find('page').filter(':not([missing])');
var list = []; var list = [];
for ( var i = 0; i < snapshot.snapshotLength; ++i ) { $pages.each(function(index, page) {
var object = snapshot.snapshotItem(i); var $page = $(page);
var page = xmlDoc.evaluate( '@title', object, null, XPathResult.STRING_TYPE, null ).stringValue; var title = $page.attr('title');
var size = xmlDoc.evaluate( 'revisions/rev/@size', object, null, XPathResult.NUMBER_TYPE, null ).numberValue; var isRedir = $page.attr('redirect') === "";

var $editprot = $page.find('pr[type="edit"][level="sysop"]');
var disputed = xmlDoc.evaluate( 'boolean(categories/cl[@title="Category:Contested candidates for speedy deletion"])', object, null, XPathResult.BOOLEAN_TYPE, null ).booleanValue; var protected = $editprot.length > 0;
list.push( {label:page + ' (' + size + ' bytes)' + ( disputed ? ' (DISPUTED CSD)' : '' ), value:page, checked:!disputed }); var size = $page.find('rev').attr('size');
}
self.params.form.append( { var metadata = [];
if (isRedir) {
metadata.push("redirect");
}
if (protected) {
metadata.push("fully protected" +
($editprot.attr('expiry') === 'infinity' ? ' indefinitely' : (', expires ' + $editprot.attr('expiry'))));
}
metadata.push(size + " bytes");
list.push({
label: title + (metadata.length ? (' (' + metadata.join('; ') + ')') : ''),
value: title,
checked: true,
style: (protected ? 'color:red' : '')
});
});

apiobj.params.form.append({ type: 'header', label: 'Pages to delete' });
apiobj.params.form.append({
type: 'button',
label: "Select All",
event: function(e) {
$(Morebits.quickForm.getElements(e.target.form, "pages")).prop('checked', true);
}
});
apiobj.params.form.append({
type: 'button',
label: "Deselect All",
event: function(e) {
$(Morebits.quickForm.getElements(e.target.form, "pages")).prop('checked', false);
}
});
apiobj.params.form.append( {
type: 'checkbox', type: 'checkbox',
name: 'pages', name: 'pages',
list: list list: list
} ); } );
self.params.form.append( { type:'submit' } ); apiobj.params.form.append( { type:'submit' } );

var result = apiobj.params.form.render();
apiobj.params.Window.setContent( result );


var result = self.params.form.render(); Morebits.checkboxShiftClickSupport(Morebits.quickForm.getElements(result, 'pages'));
self.params.Window.setContent( result );
}, statelem ); }, statelem );


wikipedia_api.params = { form:form, Window:Window }; wikipedia_api.params = { form:form, Window:Window };
Expand All @@ -158,12 +195,20 @@ Twinkle.batchdelete.callback.evaluate = function twinklebatchdeleteCallbackEvalu
Morebits.wiki.actionCompleted.notice = 'Status'; Morebits.wiki.actionCompleted.notice = 'Status';
Morebits.wiki.actionCompleted.postfix = 'batch deletion is now complete'; Morebits.wiki.actionCompleted.postfix = 'batch deletion is now complete';


var numProtected = $(Morebits.quickForm.getElements(event.target, 'pages')).filter(function(index, element) {
return element.checked && element.nextElementSibling.style.color === 'red';
}).length;
if (numProtected > 0 && !confirm("You are about to delete " + numProtected + " fully protected page(s). Are you sure?")) {
return;
}

var pages = event.target.getChecked( 'pages' ); var pages = event.target.getChecked( 'pages' );
var reason = event.target.reason.value; var reason = event.target.reason.value;
var delete_page = event.target.delete_page.checked; var delete_page = event.target.delete_page.checked;
var unlink_page = event.target.unlink_page.checked; var unlink_page = event.target.unlink_page.checked;
var delete_redirects = event.target.delete_redirects.checked; var delete_redirects = event.target.delete_redirects.checked;
if( ! reason ) { if( ! reason ) {
alert("You need to give a reason, you cabal crony!");
return; return;
} }
Morebits.simpleWindow.setButtonsEnabled( false ); Morebits.simpleWindow.setButtonsEnabled( false );
Expand All @@ -185,13 +230,54 @@ Twinkle.batchdelete.callback.evaluate = function twinklebatchdeleteCallbackEvalu
Twinkle.batchdelete.currentUnlinkCounter += pages.length; Twinkle.batchdelete.currentUnlinkCounter += pages.length;
for( var i = 0; i < pages.length; ++i ) { for( var i = 0; i < pages.length; ++i ) {
var page = pages[i]; var page = pages[i];
var query = { var params = { page:page, reason:reason };
'action': 'query',
'titles': page var query, wikipedia_api;
}; if( unlink_page ) {
var wikipedia_api = new Morebits.wiki.api( 'Checking if page ' + page + ' exists', query, Twinkle.batchdelete.callbacks.main ); query = {
wikipedia_api.params = { page:page, reason:reason, unlink_page:unlink_page, delete_page:delete_page, delete_redirects:delete_redirects }; 'action': 'query',
wikipedia_api.post(); 'list': 'backlinks',
'blfilterredir': 'nonredirects',
'blnamespace': [0, 100], // main space and portal space only
'bltitle': page,
'bllimit': Morebits.userIsInGroup( 'sysop' ) ? 5000 : 500 // 500 is max for normal users, 5000 for bots and sysops
};
wikipedia_api = new Morebits.wiki.api( 'Grabbing backlinks', query, Twinkle.batchdelete.callbacks.unlinkBacklinksMain );
wikipedia_api.params = params;
wikipedia_api.post();
} else {
--Twinkle.batchdelete.currentUnlinkCounter;
}
if( delete_page ) {
if (delete_redirects)
{
query = {
'action': 'query',
'list': 'backlinks',
'blfilterredir': 'redirects',
'bltitle': page,
'bllimit': Morebits.userIsInGroup( 'sysop' ) ? 5000 : 500 // 500 is max for normal users, 5000 for bots and sysops
};
wikipedia_api = new Morebits.wiki.api( 'Grabbing redirects', query, Twinkle.batchdelete.callbacks.deleteRedirectsMain );
wikipedia_api.params = params;
wikipedia_api.post();
}

var wikipedia_page = new Morebits.wiki.page( page, 'Deleting page ' + page );
wikipedia_page.setEditSummary(reason + Twinkle.getPref('deletionSummaryAd'));
wikipedia_page.suppressProtectWarning();
wikipedia_page.deletePage(function( apiobj ) {
--Twinkle.batchdelete.currentDeleteCounter;
var link = document.createElement( 'a' );
var innerPage = apiobj.parent.getPageName();
link.setAttribute( 'href', mw.util.getUrl( innerPage ) );
link.setAttribute( 'title', innerPage );
link.appendChild( document.createTextNode( innerPage ) );
apiobj.getStatusElement().info( [ 'completed (' , link , ')' ] );
} );
} else {
--Twinkle.batchdelete.currentDeleteCounter;
}
} }
} }
} }
Expand All @@ -201,64 +287,6 @@ Twinkle.batchdelete.callback.evaluate = function twinklebatchdeleteCallbackEvalu
}; };


Twinkle.batchdelete.callbacks = { Twinkle.batchdelete.callbacks = {
main: function( self ) {
var xmlDoc = self.responseXML;
var normal = xmlDoc.evaluate( '//normalized/n/@to', xmlDoc, null, XPathResult.STRING_TYPE, null ).stringValue;
if( normal ) {
self.params.page = normal;
}
var exists = xmlDoc.evaluate( 'boolean(//pages/page[not(@missing)])', xmlDoc, null, XPathResult.BOOLEAN_TYPE, null ).booleanValue;

if( ! exists ) {
self.statelem.error( "It seems that the page doesn't exist, perhaps it has already been deleted" );
return;
}

var query, wikipedia_api;
if( self.params.unlink_page ) {
query = {
'action': 'query',
'list': 'backlinks',
'blfilterredir': 'nonredirects',
'blnamespace': [0, 100], // main space and portal space only
'bltitle': self.params.page,
'bllimit': Morebits.userIsInGroup( 'sysop' ) ? 5000 : 500 // 500 is max for normal users, 5000 for bots and sysops
};
wikipedia_api = new Morebits.wiki.api( 'Grabbing backlinks', query, Twinkle.batchdelete.callbacks.unlinkBacklinksMain );
wikipedia_api.params = self.params;
wikipedia_api.post();
} else {
--Twinkle.batchdelete.currentUnlinkCounter;
}
if( self.params.delete_page ) {
if (self.params.delete_redirects)
{
query = {
'action': 'query',
'list': 'backlinks',
'blfilterredir': 'redirects',
'bltitle': self.params.page,
'bllimit': Morebits.userIsInGroup( 'sysop' ) ? 5000 : 500 // 500 is max for normal users, 5000 for bots and sysops
};
wikipedia_api = new Morebits.wiki.api( 'Grabbing redirects', query, Twinkle.batchdelete.callbacks.deleteRedirectsMain );
wikipedia_api.params = self.params;
wikipedia_api.post();
}

var wikipedia_page = new Morebits.wiki.page( self.params.page, 'Đang xóa trang ' + self.params.page );
wikipedia_page.setEditSummary(self.params.reason + Twinkle.getPref('deletionSummaryAd'));
wikipedia_page.deletePage(function( apiobj ) {
--Twinkle.batchdelete.currentDeleteCounter;
var link = document.createElement( 'a' );
link.setAttribute( 'href', mw.util.getUrl(self.params.page) );
link.setAttribute( 'title', self.params.page );
link.appendChild( document.createTextNode( self.params.page ) );
apiobj.statelem.info( [ 'completed (' , link , ')' ] );
} );
} else {
--Twinkle.batchdelete.currentDeleteCounter;
}
},
deleteRedirectsMain: function( self ) { deleteRedirectsMain: function( self ) {
var xmlDoc = self.responseXML; var xmlDoc = self.responseXML;
var snapshot = xmlDoc.evaluate('//backlinks/bl/@title', xmlDoc, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null ); var snapshot = xmlDoc.evaluate('//backlinks/bl/@title', xmlDoc, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null );
Expand Down

0 comments on commit e334e95

Please sign in to comment.