User:Novusistic/Guidedtour-tour-tut3.js

/** * Guidedtour-tour-tut3.js * * It creates a guided tour for The Userscript Tour: Mission 3 (Strengths of the Action API). * It makes the users familiar with MediaWiki Action API, how to test the API with the API Sandbox, * and how to write userscripts using the API. * * The following is the entire license notice for the JavaScript code in this Guided Tour. * * Copyright (C) 2021 Devyansh Chawla  and contributors * * The JavaScript/Gadget code in this page is free software: you can * redistribute it and/or modify it under the terms of the GNU * General Public License (GNU GPL) as published by the Free Software * Foundation, either version 3 of the License, or (at your option) * any later version. The code is distributed WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU GPL for more details. * * As additional permission under GNU GPL version 3 section 7, you * may distribute non-source (e.g., minimized or compacted) forms of * that code without the copy of the GNU GPL normally required by * section 4, provided you include this license notice and a URL * through which recipients can access the Corresponding Source. * * The above is the entire license notice for the JavaScript code in this Guided Tour. */ ( function ( window, document, $, mw, gt ) {

// Defined the guided tour. var tour = new gt.TourBuilder( {			name: 'tut3',			shouldLog: true		} ), postEditButtons = [];

/**	 * Show the message dialog box. *	 * It displays a message dialog box (alert box) with relevant message if the user * is not logged-in or request to the Action API for sending yourself messages fails. *	 * @param {string} title Represents the title of the error. * @param {string} message Represents the description of the error. */	function showAlert( title, message ) { var messageDialog = new OO.ui.MessageDialog, windowManager = new OO.ui.WindowManager;

// Sets the z-index of the message dialog box to be greater than that of the 'guider' // so that the dialog box appears on top of everything. messageDialog.$element.css( { zIndex: '100000010' } );

$( 'body' ).append( windowManager.$element );

windowManager.addWindows( [ messageDialog ] );

windowManager.openWindow( messageDialog, {			title: title,			message: message,			actions: [ {				action: 'accept',				label: 'OK',				flags: 'primary'			} ]		} ); }

/**	 * Sends the message to the personal wiki page. *	 * It sends the message to the user's wiki page with API:Edit. For example, Welcome to the Tour, * Badge earned, etc.	 * * @param {string} targetPage Represents the location of the personal wiki page to send * the message to. * @param {string} msgPage Represents the location of the wiki page containing the message * to be sent. * @param {string} linkTo Represents the location of the wiki page to direct the user to, * after the message has been sent. */	function sendMessage( targetPage, msgPage, linkTo ) { var api = new mw.Api;

// Sends a GET request to the wiki page containing the message. The response contains the // message and the edit token if the Promise is resolved. api .get( {				action: 'query',				titles: msgPage,				prop: 'revisions',				rvprop: 'content',				rvslots: '*',				indexpageids: 1,				meta: 'tokens'			} ) .done( function ( result ) {				result = result.query;				var csrfToken = result.tokens.csrftoken,					page = result.pages[ result.pageids[ 0 ] ],					// Checks whether the wiki page containing the message exists or not.					text = page.revisions !== undefined ?						page.revisions[ 0 ].slots.main[ '*' ] :						'Page unavailable';

// If the page containing the message doesn't exist, simply direct the user // to the specified location. if ( text === 'Page unavailable' ) { window.location.href = linkTo; return; }

// Sends the extracted message to the personal wiki page. api .post( {						action: 'edit',						title: targetPage,						appendtext: '\n' + text,						summary: 'A badge sent as part of The Userscript Tour.',						token: csrfToken					} ) .done( function {						window.location.href = linkTo;					} ) // Should the GET request fail, a message dialog box is displayed to the user. .fail( function {						showAlert( 'Error', 'An error occured. Please try again.' );					} ); } )			// Should the GET request fail, a message dialog box is displayed to the user.			.fail( function { showAlert( 'Error', 'An error occured. Please try again.' ); } );	}

// Loads the OOUI dependencies required to display the message dialog box. mw.loader.load( [ 'oojs-ui-core', 'oojs-ui-windows' ] );

// Gracefully handles the situation when the user saves a file without editing it. // Asks the user to go back and make an edit. if ( mw.config.get( 'wgAction' ) === 'view' && !gt.isPostEdit ) { postEditButtons.push( {			name: 'Click here to go back and make an edit',			onclick: function {				window.location.href = window.location.href + '?action=edit';			}		} ); }

// Step 1 tour .firstStep( {			name: '1',			title: 'Welcome back!',			description: '  Hey, good to see you again. You seem all pumped up to embark on this journey of MediaWiki Action API.  Ready to make this journey an insightful one?  ',			onShow: gt.parseDescription,			overlay: true,			closeOnClickOutside: false,			buttons: [ {				name: 'Let\'s begin',				action: 'next'			} ],			allowAutomaticOkay: false		} ) .next( '2' );

// Step 2 tour .step( {			name: '2',			title: 'MediaWiki Action API',			description: '  The MediaWiki Action API is a web service that allows access to wiki-features like page operations (create and edit a page, get the contents of a page, etc.) and search.  It\'s a medium to perform actions on a wiki programmatically.  ',			onShow: gt.parseDescription,			overlay: true,			closeOnClickOutside: false,			buttons: [ {				name: ' ← ',				action: 'externalLink',				url: mw.util.getUrl( 'TUT/3/Start' ) + '?tour=tut3&step=1'			}, {				name: 'Okay!',				action: 'next'			} ],			allowAutomaticOkay: false		} ) .next( '3' );

// Step 3 tour .step( {			name: '3',			title: 'What can it be used for?',			description: '  The MediaWiki Action API can be used to: 1) access wiki features 2) interact with a wiki 3) obtain meta-information about wikis and public users  The wiki operations that can be performed via the user-interface can also be performed via the MediaWiki Action API. ',			onShow: gt.parseDescription, overlay: true, closeOnClickOutside: false, buttons: [ { name: ' ← ', action: 'externalLink', url: mw.util.getUrl( 'TUT/3/Start' ) + '?tour=tut3&step=2' }, {				name: 'Sounds good', action: 'next' } ],			allowAutomaticOkay: false } )		.next( '4' );

// Step 4 tour .step( {			name: '4',			title: 'Interesting use case',			description: '  A widely used application of MediaWiki Action API is HotCat.  HotCat facilitates addition, removal and changing of categories on wiki pages.  ',			onShow: gt.parseDescription,			overlay: true,			closeOnClickOutside: false,			buttons: [ {				name: ' ← ',				action: 'externalLink',				url: mw.util.getUrl( 'TUT/3/Start' ) + '?tour=tut3&step=3'			}, {				name: 'Explore HotCat',				type: 'neutral',				onclick: function  {					window.open( 'https://commons.wikimedia.org/wiki/Help:Gadget-HotCat', '_blank' );				}			}, {				name: 'Move ahead',				action: 'next'			} ],			allowAutomaticOkay: false		} ) .next( '5' );

// Step 5 tour .step( {			name: '5',			title: 'Hands-on',			description: '  Let\'s leverage the MediaWiki Action API.  To begin with, we\'ll fetch the basic information about the wiki pages.  ',			onShow: gt.parseDescription,			overlay: false,			closeOnClickOutside: false,			buttons: [ {				name: ' ← ',				action: 'externalLink',				url: mw.util.getUrl( 'TUT/3/Start' ) + '?tour=tut3&step=4'			}, {				name: 'Alright',				type: 'progressive',				action: 'externalLink',				url: mw.util.getUrl( 'Special:MyPage/basicPageInfo.js' ) + '?tour=tut3&step=6'			} ],			allowAutomaticOkay: false		} );

// Step 6 tour .step( {			name: '6',			title: 'Your subpage',			description: ' This user script will display the length of characters (or the number of bytes) on every page. The name of our subpage would be User:' + mw.config.get( 'wgUserName' ) + '/basicPageInfo.js  ',			onShow: gt.parseDescription,			overlay: true,			closeOnClickOutside: false,			buttons: [ {				name: ' ← ',				action: 'externalLink',				url: mw.util.getUrl( 'TUT/3/Start' ) + '?tour=tut3&step=5'			}, {				name: 'Sure',				type: 'progressive',				action: 'externalLink',				url: mw.util.getUrl( 'Special:MyPage/basicPageInfo.js' ) + '?tour=tut3&step=7&action=edit&preload=User:Novusistic/TUT_basicPageInfo.js&summary=Created a userscript to display number of bytes on every page.'			} ],			allowAutomaticOkay: false		} );

// Step 7 tour .step( {			name: '7',			title: 'Sneak Peek',			description: ' Go through the comments in the above user script. The concepts are explained well. Once done, publish the changes.  ',			onShow: gt.parseDescription,			attachTo: '#wpSave',			position: 'bottomRight',			overlay: false,			closeOnClickOutside: false,			buttons:				postEditButtons.length === 0 ?					[ {						name: ' ← ',						action: 'externalLink',						url: mw.util.getUrl( 'Special:MyPage/basicPageInfo.js' ) + '?tour=tut3&step=6'					} ] :					postEditButtons,			allowAutomaticOkay: false		} ) .transition( function {			if ( gt.isPostEdit ) {				return '8';			}		} );

// Step 8 tour .step( {			name: '8',			title: 'The Rationale',			description: '  LOGIC:  1) The mediawiki.api module is initially loaded, which makes the mw.Api constructor available. 2) Once the module is loaded, a GET request is made to fetch the basic information of the current page. 3) The desired information is shown on the top of the page using the prepend method of jQuery. ',			onShow: gt.parseDescription, overlay: false, attachTo: '#bodyContent', position: 'bottom', closeOnClickOutside: false, buttons: [ { name: ' ← ', action: 'externalLink', url: mw.util.getUrl( 'Special:MyPage/basicPageInfo.js' ) + '?tour=tut3&step=7&action=edit' }, {				name: 'Let\'s get to common.js', type: 'progressive', action: 'externalLink', url: mw.util.getUrl( 'Special:MyPage/common.js' ) + '?tour=tut3&step=9' } ],			allowAutomaticOkay: false } );

// Step 9 tour .step( {			name: '9',			title: 'Edit common.js',			description: ' By now, you would have memorized all the steps of loading a user script. Click CREATE SOURCE or EDIT SOURCE above.  ',			onShow: gt.parseDescription,			attachTo: '#ca-edit',			position: 'bottom',			overlay: false,			closeOnClickOutside: false,			buttons: [ {				name: ' ← ',				action: 'externalLink',				url: mw.util.getUrl( 'Special:MyPage/basicPageInfo.js' ) + '?tour=tut3&step=8'			} ],			allowAutomaticOkay: false		} ) .transition( function {			if ( gt.hasQuery( { action: 'edit' } ) ) {				return '10';			}		} );

// Step 10 tour .step( {			name: '10',			title: 'Load the userscript',			description: '  In case the page looks cluttered, you may consider clearing it a bit or entirely.  Now copy and paste the following at the end of common.js: mw.loader.load( \' https:' + mw.config.get( 'wgServer' ) + '/w/index.php?title=User:' + mw.config.get( 'wgUserName' ) + '/basicPageInfo.js&action=raw&ctype=text/javascript \' );  ',			onShow: gt.parseDescription,			attachTo: '.wikiEditor-ui-text',			position: 'bottomRight',			overlay: false,			closeOnClickOutside: false,			buttons: [ {				name: ' ← ',				action: 'externalLink',				url: mw.util.getUrl( 'Special:MyPage/common.js' ) + '?tour=tut3&step=9'			}, {				name: 'Copied and Pasted!',				action: 'next'			} ],			allowAutomaticOkay: false		} ) .next( '11' );

// Step 11 tour .step( {			name: '11',			title: 'Edit summary and Publish Changes',			description: ' Nice! Before you click Publish Changes, leave a brief note about the changes you made. Click PUBLISH CHANGES when you\'re ready.  ',			onShow: gt.parseDescription,			attachTo: '#wpSave',			position: 'bottomRight',			overlay: false,			closeOnClickOutside: false,			buttons:				postEditButtons.length === 0 ?					[ {						name: ' ← ',						action: 'externalLink',						url: mw.util.getUrl( 'Special:MyPage/common.js' ) + '?tour=tut3&step=10&action=edit'					} ] :					postEditButtons,			allowAutomaticOkay: false		} ) .transition( function {			if ( gt.isPostEdit ) {				return '12';			}		} );

// Step 12 tour .step( {			name: '12',			title: 'Number of bytes',			description: ' Do you see the byte count. If you don’t, try bypassing your cache (by holding the Shift key and clicking the Reload button). You would now see the number of bytes on every page.  ',			onShow: gt.parseDescription,			attachTo: '#byte-count',			position: 'bottom',			overlay: false,			closeOnClickOutside: false,			buttons: [ {				name: ' ← ',				action: 'externalLink',				url: mw.util.getUrl( 'Special:MyPage/common.js' ) + '?tour=tut3&step=11&action=edit'			}, {				name: 'Right',				type: 'progressive',				action: 'externalLink',				url: mw.util.getUrl( 'TUT/3/Start' ) + '?tour=tut3&step=13'			} ],			allowAutomaticOkay: false		} );

// Step 13 tour .step( {			name: '13',			title: 'Endpoints',			description: '  All Wikimedia wikis have endpoints that follow this pattern:  https://www.example.org/w/api.php . For example:  MediaWiki API -   English Wikipedia API -   and so on.  ',			onShow: gt.parseDescription,			overlay: true,			closeOnClickOutside: false,			buttons: [ {				name: ' ← ',				action: 'externalLink',				url: mw.util.getUrl( 'Special:MyPage/common.js' ) + '?tour=tut3&step=12'			}, {				name: 'Okay',				action: 'next'			} ],			allowAutomaticOkay: false		} ) .next( '14' );

// Step 14 tour .step( {			name: '14',			title: 'Module, submodule, parameter',			description: '  Consider the following request: api.php ? action=query & prop=info & titles=API:Main_page & format=json  here:  1)   is a parameter of the main module. 2)  is another module called the query module. 3)   and   are parameters of the query module. 4)  is a parameter of the main module.  ',			onShow: gt.parseDescription,			overlay: true,			closeOnClickOutside: false,			buttons: [ {				name: ' ← ',				action: 'externalLink',				url: mw.util.getUrl( 'TUT/3/Start' ) + '?tour=tut3&step=13'			}, {				name: 'Alright',				action: 'next'			} ],			allowAutomaticOkay: false		} ) .next( '15' );

// Step 15 tour .step( {			name: '15',			title: 'Where to go for reference?',			description: '  The MediaWiki Action API is big. To work out your API request:  In the sidebar of the API:Main_page, look for the feature you wish to implement and follow the link for information about which modules to call.  Check out API:Main_page  ',			onShow: gt.parseDescription,			overlay: true,			closeOnClickOutside: false,			buttons: [ {				name: ' ← ',				action: 'externalLink',				url: mw.util.getUrl( 'TUT/3/Start' ) + '?tour=tut3&step=14'			}, {				name: 'What\'s next',				action: 'next'			} ],			allowAutomaticOkay: false		} ) .next( '17' );

// Step 16 tour .step( {			name: '16',			title: 'Lots of use cases!',			description: '  You may look for the feature you wish to implement using the API from the pool of features in the sidebar.  ',			onShow: gt.parseDescription,			attachTo: '.vertical-navbox',			position: 'leftTop',			overlay: false,			closeOnClickOutside: false,			buttons: [ {				name: ' ← ',				action: 'externalLink',				url: mw.util.getUrl( 'TUT/3/Start' ) + '?tour=tut3&step=15'			}, {				name: 'Continue on the tour',				action: 'next'			} ],			allowAutomaticOkay: false		} ) .next( '17' );

// Step 17 tour .step( {			name: '17',			title: 'API Sandbox',			description: '  API Sandbox is used to test and experiment with the MediaWiki Action API. Note that, although this is a sandbox, actions you carry out on this page may modify the wiki.  ',			onShow: gt.parseDescription,			overlay: true,			closeOnClickOutside: false,			buttons: [ {				name: ' ← ',				action: 'externalLink',				url: mw.util.getUrl( 'TUT/3/Start' ) + '?tour=tut3&step=15'			}, {				name: 'Play with the sandbox',				type: 'neutral',				onclick: function  {					window.open( 'https://en.wikipedia.org/wiki/Special:ApiSandbox', '_blank' );				}			}, {				name: 'Next',				type: 'progressive',				action: 'externalLink',				url: mw.util.getUrl( 'Special:MyPage/quickChangeLog.js' ) + '?tour=tut3&step=18'			} ],			allowAutomaticOkay: false		} );

// Step 18 tour .step( {			name: '18',			title: 'Your subpage',			description: ' You’ll now put everything you have learned so far into action in the subpage - User:' + mw.config.get( 'wgUserName' ) + '/quickChangeLog.js. The next user script shows a dialog with up to 25 recent edits on the entire wiki!  ',			onShow: gt.parseDescription,			overlay: true,			closeOnClickOutside: false,			buttons: [ {				name: ' ← ',				action: 'externalLink',				url: mw.util.getUrl( 'TUT/3/Start' ) + '?tour=tut3&step=17'			}, {				name: 'Okay',				type: 'progressive',				action: 'externalLink',				url: mw.util.getUrl( 'Special:MyPage/quickChangeLog.js' ) + '?tour=tut3&step=19&action=edit&preload=User:Novusistic/TUT_quickChangeLog.js&summary=Created a userscript to show atmost 25 recent changes.'			} ],			allowAutomaticOkay: false		} );

// Step 19 tour .step( {			name: '19',			title: 'Check it out',			description: ' The above user script combines the crux of the ongoing as well as previous missions. Have a good look at this script and when done, publish the changes.  ',			onShow: gt.parseDescription,			attachTo: '#wpSave',			position: 'bottomRight',			overlay: false,			closeOnClickOutside: false,			buttons:				postEditButtons.length === 0 ?					[ {						name: ' ← ',						action: 'externalLink',						url: mw.util.getUrl( 'Special:MyPage/quickChangeLog.js' ) + '?tour=tut3&step=18'					} ] :					postEditButtons,			allowAutomaticOkay: false		} ) .transition( function {			if ( gt.isPostEdit ) {				return '20';			}		} );

// Step 20 tour .step( {			name: '20',			title: 'The Rationale',			description: '  LOGIC:  1. The required modules are called. 2. When the page is fully loaded, a new link is added to the Portlet area. 3. quickRC fetches the recent changes using the Action API and passes the data to renderQuickRCDialog. 4. renderQuickRCDialog pops up a dialog when the QUICK CHANGELOG link in the toolbox is clicked. The dialog contains the recent changes.  ',			onShow: gt.parseDescription,			overlay: false,			attachTo: '#bodyContent',			position: 'bottom',			closeOnClickOutside: false,			buttons: [ {				name: ' ← ',				action: 'externalLink',				url: mw.util.getUrl( 'Special:MyPage/quickChangeLog.js' ) + '?tour=tut3&step=19&action=edit'			}, {				name: 'Let\'s head to common.js',				type: 'progressive',				action: 'externalLink',				url: mw.util.getUrl( 'Special:MyPage/common.js' ) + '?tour=tut3&step=21&action=edit' } ],			allowAutomaticOkay: false } );

// Step 21 tour .step( {			name: '21',			title: 'Load Quick ChangeLog',			description: '  Feel free to clear common.js first, if it looks cluttered.  Now copy and paste at the very end: mw.loader.load( \' https:' + mw.config.get( 'wgServer' ) + '/w/index.php?title=User:' + mw.config.get( 'wgUserName' ) + '/quickChangeLog.js&action=raw&ctype=text/javascript \' );  ',			onShow: gt.parseDescription,			attachTo: '.wikiEditor-ui-text',			position: 'bottomRight',			overlay: false,			closeOnClickOutside: false,			buttons: [ {				name: ' ← ',				action: 'externalLink',				url: mw.util.getUrl( 'Special:MyPage/quickChangeLog.js' ) + '?tour=tut3&step=20'			}, {				name: 'Copied and Pasted!',				action: 'next'			} ],			allowAutomaticOkay: false		} ) .next( '22' );

// Step 22 tour .step( {			name: '22',			title: 'Edit summary and Publish Changes',			description: ' Before you click Save Changes, leave a brief note about the changes you made. Click Publish Changes when you\'re ready.  ',			onShow: gt.parseDescription,			attachTo: '#wpSave',			position: 'bottomRight',			overlay: false,			closeOnClickOutside: false,			buttons:				postEditButtons.length === 0 ?					[ {						name: ' ← ',						action: 'externalLink',						url: mw.util.getUrl( 'Special:MyPage/common.js' ) + '?tour=tut3&step=21&action=edit'					} ] :					postEditButtons,			allowAutomaticOkay: false		} ) .transition( function {			if ( gt.isPostEdit ) {				return '23';			}		} );

// Step 23 tour .step( {			name: '23',			title: 'Play around',			description: ' Click the QUICK CHANGELOG link in the toolbox. Bypass your cache if the changes are not visible to you.  ',			onShow: gt.parseDescription,			attachTo: '#t-prettylinkwidget',			position: 'right',			overlay: false,			closeOnClickOutside: false,			buttons: [ {				name: ' ← ',				action: 'externalLink',				url: mw.util.getUrl( 'Special:MyPage/common.js' ) + '?tour=tut3&step=22&action=edit'			}, {				name: 'Legit',				type: 'progressive',				action: 'externalLink',				url: mw.util.getUrl( 'Special:MyPage/common.js' ) + '?tour=tut3&step=24'			} ],			allowAutomaticOkay: false		} );

// Step 24 tour .step( {			name: '24',			title: 'Congrats!',			description: 'New badge earned: ONE FRONTIER TO GO  Brilliant work! Take your time to pat on your shoulder for coming this far. ',			onShow: gt.parseDescription,			overlay: true,			closeOnClickOutside: false,			buttons: [ {				name: ' ← ',				action: 'externalLink',				url: mw.util.getUrl( 'Special:MyPage/common.js' ) + '?tour=tut3&step=23'			}, {				name: 'Thanks',				action: 'next'			} ],			allowAutomaticOkay: false		} ) .next( '25' );

// Step 25 tour .step( {			name: '25',			title: 'Save the badge',			description: '  Do you want to save this badge in your subpage?  ',			onShow: gt.parseDescription,			overlay: true,			closeOnClickOutside: false,			buttons: [ {				name: ' ← ',				action: 'externalLink',				url: mw.util.getUrl( 'Special:MyPage/common.js' ) + '?tour=tut3&step=24'			}, {				name: 'Don\'t save',				action: 'externalLink',				url: mw.util.getUrl( 'TUT/3/End' ) + '?tour=tut3&step=26'			}, {				name: 'Save the badge',				onclick: function  {					if ( !mw.config.get( 'wgUserName' ) ) {						showAlert( 'Please login', 'Please login to continue on the tour.' );						return;					}					sendMessage( 'User:' + mw.config.get( 'wgUserName' ) + '/theUserscriptTourBadges', 'TUT/Badge/3template1', mw.util.getUrl( 'TUT/3/End' ) + '?tour=tut3&step=26' );				}			} ],			allowAutomaticOkay: false		} );

// Step 26 tour .step( {			name: '26',			title: 'Mission 3 complete!',			description: ' This concludes Mission 3. You\'re all set for your journey on Mission 4: Novelty of OOUI  ',			onShow: gt.parseDescription,			overlay: true,			closeOnClickOutside: false,			buttons: [ {				name: 'Congrats me!',				action: 'end'			} ],			allowAutomaticOkay: false		} );

}( window, document, jQuery, mediaWiki, mediaWiki.guidedTour ) );